summaryrefslogtreecommitdiff
path: root/tools/perf/scripts/python
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-02-22 11:05:56 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2023-02-22 11:05:56 -0800
commitd5176cdbf64ce7d4eebf339205f17c23118e9f72 (patch)
tree188bbda7ab47c4503d101eafb98264b4a13165bc /tools/perf/scripts/python
parent17bbc46fc9d5128756dc9f36063836eaede06b0b (diff)
parent099f37a539e616f762241ab999495fb8aa2f5971 (diff)
Merge tag 'pinctrl-v6.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl
Pull pin control updates from Linus Walleij: "Nothing special, notably a lot of new Qualcomm hardware is supported, a RISC-V reference SoC and then some cleanups both in code and device tree bindings. Core changes: - Add PINCTRL_PINFUNCTION() macro and use it in several drivers New drivers: - New driver for the StarFive JH7110 SoC "sys" and "aon" (always-on) pin controllers. (RISC-V.) - New subdriver for the Qualcomm QDU1000/QRU1000 SoC pin controller - New subdrivers for the Qualcomm SM8550 SoC and LPASS pin controllers - New subdriver for the Qualcomm SA8775P SoC pin controller - New subdriver for the Qualcomm IPQ5332 SoC pin controller - New (trivial) support for Qualcomm PM8550 and PMR735D PMIC pin control - New subdriver for the Mediatek MT7981 SoC pin controller Improvements: - Several cleanups and refactorings to the Intel drivers - Add 4KOhm bias support to the Intel driver - Use the NOIRQ_SYSTEM_SLEEP_PM_OPS for the AT91 driver - Support general purpose clocks in the Qualcomm MSM8226 SoC - Several conversions to use the new I2C .probe_new() call - Massive clean-up of the Qualcomm Device Tree YAML schemas - Add VIN[45] pins, groups and functions to the Renesas r8a77950 SoC driver" * tag 'pinctrl-v6.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl: (118 commits) pinctrl: qcom: Add support for i2c specific pull feature pinctrl: starfive: Add StarFive JH7110 aon controller driver pinctrl: starfive: Add StarFive JH7110 sys controller driver dt-bindings: pinctrl: Add StarFive JH7110 aon pinctrl dt-bindings: pinctrl: Add StarFive JH7110 sys pinctrl pinctrl: add mt7981 pinctrl driver dt-bindings: pinctrl: add bindings for MT7981 SoC dt-bindings: pinctrl: rockchip,pinctrl: mark gpio sub nodes of pinctrl as deprecated pinctrl: qcom: Introduce IPQ5332 TLMM driver dt-bindings: pinctrl: qcom: add IPQ5332 pinctrl dt-bindings: pinctrl: qcom: lpass-lpi: correct GPIO name pattern pinctrl: qcom: pinctrl-sm8550-lpass-lpi: add SM8550 LPASS dt-bindings: pinctrl: qcom,sm8550-lpass-lpi-pinctrl: add SM8550 LPASS pinctrl: at91: use devm_kasprintf() to avoid potential leaks dt-bindings: pinctrl: qcom: correct gpio-ranges in examples dt-bindings: pinctrl: qcom,msm8994: correct number of GPIOs dt-bindings: pinctrl: qcom,sdx55: correct GPIO name pattern dt-bindings: pinctrl: qcom,msm8953: correct GPIO name pattern dt-bindings: pinctrl: qcom,sm6375: correct GPIO name pattern and example dt-bindings: pinctrl: qcom,msm8909: correct GPIO name pattern and example ...
Diffstat (limited to 'tools/perf/scripts/python')
0 files changed, 0 insertions, 0 deletions
/cec-adap.c2364
-rw-r--r--drivers/media/cec/core/cec-api.c701
-rw-r--r--drivers/media/cec/core/cec-core.c445
-rw-r--r--drivers/media/cec/core/cec-notifier.c256
-rw-r--r--drivers/media/cec/core/cec-pin-error-inj.c403
-rw-r--r--drivers/media/cec/core/cec-pin-priv.h260
-rw-r--r--drivers/media/cec/core/cec-pin.c1397
-rw-r--r--drivers/media/cec/core/cec-priv.h60
-rw-r--r--drivers/media/cec/i2c/Kconfig25
-rw-r--r--drivers/media/cec/i2c/Makefile6
-rw-r--r--drivers/media/cec/i2c/ch7322.c602
-rw-r--r--drivers/media/cec/i2c/tda9950.c507
-rw-r--r--drivers/media/cec/platform/Kconfig120
-rw-r--r--drivers/media/cec/platform/Makefile15
-rw-r--r--drivers/media/cec/platform/cec-gpio/Makefile2
-rw-r--r--drivers/media/cec/platform/cec-gpio/cec-gpio.c296
-rw-r--r--drivers/media/cec/platform/cros-ec/Makefile2
-rw-r--r--drivers/media/cec/platform/cros-ec/cros-ec-cec.c602
-rw-r--r--drivers/media/cec/platform/meson/Makefile3
-rw-r--r--drivers/media/cec/platform/meson/ao-cec-g12a.c792
-rw-r--r--drivers/media/cec/platform/meson/ao-cec.c728
-rw-r--r--drivers/media/cec/platform/s5p/Makefile3
-rw-r--r--drivers/media/cec/platform/s5p/exynos_hdmi_cec.h34
-rw-r--r--drivers/media/cec/platform/s5p/exynos_hdmi_cecctrl.c206
-rw-r--r--drivers/media/cec/platform/s5p/regs-cec.h93
-rw-r--r--drivers/media/cec/platform/s5p/s5p_cec.c309
-rw-r--r--drivers/media/cec/platform/s5p/s5p_cec.h76
-rw-r--r--drivers/media/cec/platform/seco/Makefile2
-rw-r--r--drivers/media/cec/platform/seco/seco-cec.c788
-rw-r--r--drivers/media/cec/platform/seco/seco-cec.h141
-rw-r--r--drivers/media/cec/platform/sti/Makefile2
-rw-r--r--drivers/media/cec/platform/sti/stih-cec.c397
-rw-r--r--drivers/media/cec/platform/stm32/Makefile2
-rw-r--r--drivers/media/cec/platform/stm32/stm32-cec.c375
-rw-r--r--drivers/media/cec/platform/tegra/Makefile2
-rw-r--r--drivers/media/cec/platform/tegra/tegra_cec.c481
-rw-r--r--drivers/media/cec/platform/tegra/tegra_cec.h116
-rw-r--r--drivers/media/cec/usb/Kconfig9
-rw-r--r--drivers/media/cec/usb/Makefile7
-rw-r--r--drivers/media/cec/usb/extron-da-hd-4k-plus/Kconfig14
-rw-r--r--drivers/media/cec/usb/extron-da-hd-4k-plus/Makefile2
-rw-r--r--drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.c657
-rw-r--r--drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.h51
-rw-r--r--drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c1836
-rw-r--r--drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.h118
-rw-r--r--drivers/media/cec/usb/pulse8/Kconfig13
-rw-r--r--drivers/media/cec/usb/pulse8/Makefile2
-rw-r--r--drivers/media/cec/usb/pulse8/pulse8-cec.c924
-rw-r--r--drivers/media/cec/usb/rainshadow/Kconfig13
-rw-r--r--drivers/media/cec/usb/rainshadow/Makefile2
-rw-r--r--drivers/media/cec/usb/rainshadow/rainshadow-cec.c381
-rw-r--r--drivers/media/common/Kconfig34
-rw-r--r--drivers/media/common/Makefile14
-rw-r--r--drivers/media/common/b2c2/Kconfig25
-rw-r--r--drivers/media/common/b2c2/Makefile8
-rw-r--r--drivers/media/common/b2c2/flexcop-common.h (renamed from drivers/media/dvb/b2c2/flexcop-common.h)15
-rw-r--r--drivers/media/common/b2c2/flexcop-eeprom.c (renamed from drivers/media/dvb/b2c2/flexcop-eeprom.c)4
-rw-r--r--drivers/media/common/b2c2/flexcop-fe-tuner.c (renamed from drivers/media/dvb/b2c2/flexcop-fe-tuner.c)81
-rw-r--r--drivers/media/common/b2c2/flexcop-hw-filter.c (renamed from drivers/media/dvb/b2c2/flexcop-hw-filter.c)19
-rw-r--r--drivers/media/common/b2c2/flexcop-i2c.c (renamed from drivers/media/dvb/b2c2/flexcop-i2c.c)66
-rw-r--r--drivers/media/common/b2c2/flexcop-misc.c (renamed from drivers/media/dvb/b2c2/flexcop-misc.c)24
-rw-r--r--drivers/media/common/b2c2/flexcop-reg.h (renamed from drivers/media/dvb/b2c2/flexcop-reg.h)2
-rw-r--r--drivers/media/common/b2c2/flexcop-sram.c (renamed from drivers/media/dvb/b2c2/flexcop-sram.c)9
-rw-r--r--drivers/media/common/b2c2/flexcop.c (renamed from drivers/media/dvb/b2c2/flexcop.c)45
-rw-r--r--drivers/media/common/b2c2/flexcop.h31
-rw-r--r--drivers/media/common/b2c2/flexcop_ibi_value_be.h (renamed from drivers/media/dvb/b2c2/flexcop_ibi_value_be.h)1
-rw-r--r--drivers/media/common/b2c2/flexcop_ibi_value_le.h (renamed from drivers/media/dvb/b2c2/flexcop_ibi_value_le.h)1
-rw-r--r--drivers/media/common/cx2341x.c (renamed from drivers/media/video/cx2341x.c)218
-rw-r--r--drivers/media/common/cypress_firmware.c131
-rw-r--r--drivers/media/common/cypress_firmware.h29
-rw-r--r--drivers/media/common/saa7146/Kconfig10
-rw-r--r--drivers/media/common/saa7146/Makefile6
-rw-r--r--drivers/media/common/saa7146/saa7146_core.c (renamed from drivers/media/common/saa7146_core.c)124
-rw-r--r--drivers/media/common/saa7146/saa7146_fops.c435
-rw-r--r--drivers/media/common/saa7146/saa7146_hlp.c771
-rw-r--r--drivers/media/common/saa7146/saa7146_i2c.c (renamed from drivers/media/common/saa7146_i2c.c)14
-rw-r--r--drivers/media/common/saa7146/saa7146_vbi.c445
-rw-r--r--drivers/media/common/saa7146/saa7146_video.c723
-rw-r--r--drivers/media/common/saa7146_fops.c641
-rw-r--r--drivers/media/common/saa7146_hlp.c1048
-rw-r--r--drivers/media/common/saa7146_vbi.c498
-rw-r--r--drivers/media/common/saa7146_video.c1332
-rw-r--r--drivers/media/common/siano/Kconfig33
-rw-r--r--drivers/media/common/siano/Makefile13
-rw-r--r--drivers/media/common/siano/sms-cards.c348
-rw-r--r--drivers/media/common/siano/sms-cards.h (renamed from drivers/media/dvb/siano/sms-cards.h)32
-rw-r--r--drivers/media/common/siano/smscoreapi.c2178
-rw-r--r--drivers/media/common/siano/smscoreapi.h1144
-rw-r--r--drivers/media/common/siano/smsdvb-debugfs.c420
-rw-r--r--drivers/media/common/siano/smsdvb-main.c1273
-rw-r--r--drivers/media/common/siano/smsdvb.h115
-rw-r--r--drivers/media/common/siano/smsendian.c94
-rw-r--r--drivers/media/common/siano/smsendian.h21
-rw-r--r--drivers/media/common/siano/smsir.c97
-rw-r--r--drivers/media/common/siano/smsir.h48
-rw-r--r--drivers/media/common/ttpci-eeprom.c (renamed from drivers/media/dvb/ttpci/ttpci-eeprom.c)35
-rw-r--r--drivers/media/common/ttpci-eeprom.h22
-rw-r--r--drivers/media/common/tuners/Kconfig241
-rw-r--r--drivers/media/common/tuners/Makefile37
-rw-r--r--drivers/media/common/tuners/fc0012-priv.h43
-rw-r--r--drivers/media/common/tuners/fc0012.h44
-rw-r--r--drivers/media/common/tuners/fc0013-priv.h44
-rw-r--r--drivers/media/common/tuners/fc0013.h57
-rw-r--r--drivers/media/common/tuners/fc001x-common.h39
-rw-r--r--drivers/media/common/tuners/max2165.h48
-rw-r--r--drivers/media/common/tuners/max2165_priv.h60
-rw-r--r--drivers/media/common/tuners/mc44s803.h46
-rw-r--r--drivers/media/common/tuners/mt2060.c403
-rw-r--r--drivers/media/common/tuners/mt2060.h43
-rw-r--r--drivers/media/common/tuners/mt2063.h32
-rw-r--r--drivers/media/common/tuners/mt20xx.h37
-rw-r--r--drivers/media/common/tuners/mt2131.h54
-rw-r--r--drivers/media/common/tuners/mt2131_priv.h48
-rw-r--r--drivers/media/common/tuners/mt2266.h37
-rw-r--r--drivers/media/common/tuners/mxl5007t.h104
-rw-r--r--drivers/media/common/tuners/qt1010.h53
-rw-r--r--drivers/media/common/tuners/qt1010_priv.h104
-rw-r--r--drivers/media/common/tuners/tda18212.c326
-rw-r--r--drivers/media/common/tuners/tda18212.h52
-rw-r--r--drivers/media/common/tuners/tda18218.h45
-rw-r--r--drivers/media/common/tuners/tda18218_priv.h108
-rw-r--r--drivers/media/common/tuners/tda18271.h134
-rw-r--r--drivers/media/common/tuners/tda827x.h68
-rw-r--r--drivers/media/common/tuners/tda8290.h56
-rw-r--r--drivers/media/common/tuners/tda9887.h38
-rw-r--r--drivers/media/common/tuners/tea5761.h47
-rw-r--r--drivers/media/common/tuners/tea5767.h66
-rw-r--r--drivers/media/common/tuners/tua9001.c215
-rw-r--r--drivers/media/common/tuners/tua9001.h46
-rw-r--r--drivers/media/common/tuners/tua9001_priv.h34
-rw-r--r--drivers/media/common/tuners/tuner-simple.h39
-rw-r--r--drivers/media/common/tuners/tuner-xc2028-types.h141
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.c1377
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.h72
-rw-r--r--drivers/media/common/tuners/xc4000.h67
-rw-r--r--drivers/media/common/tuners/xc5000.c1255
-rw-r--r--drivers/media/common/tuners/xc5000.h74
-rw-r--r--drivers/media/common/tveeprom.c759
-rw-r--r--drivers/media/common/uvc.c192
-rw-r--r--drivers/media/common/v4l2-tpg/Kconfig3
-rw-r--r--drivers/media/common/v4l2-tpg/Makefile4
-rw-r--r--drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c1409
-rw-r--r--drivers/media/common/v4l2-tpg/v4l2-tpg-core.c2732
-rw-r--r--drivers/media/common/videobuf2/Kconfig32
-rw-r--r--drivers/media/common/videobuf2/Makefile17
-rw-r--r--drivers/media/common/videobuf2/frame_vector.c197
-rw-r--r--drivers/media/common/videobuf2/vb2-trace.c10
-rw-r--r--drivers/media/common/videobuf2/videobuf2-core.c3337
-rw-r--r--drivers/media/common/videobuf2/videobuf2-dma-contig.c866
-rw-r--r--drivers/media/common/videobuf2/videobuf2-dma-sg.c679
-rw-r--r--drivers/media/common/videobuf2/videobuf2-dvb.c342
-rw-r--r--drivers/media/common/videobuf2/videobuf2-memops.c131
-rw-r--r--drivers/media/common/videobuf2/videobuf2-v4l2.c1372
-rw-r--r--drivers/media/common/videobuf2/videobuf2-vmalloc.c445
-rw-r--r--drivers/media/dvb-core/Kconfig80
-rw-r--r--drivers/media/dvb-core/Makefile13
-rw-r--r--drivers/media/dvb-core/dmxdev.c1478
-rw-r--r--drivers/media/dvb-core/dvb_ca_en50221.c1983
-rw-r--r--drivers/media/dvb-core/dvb_demux.c (renamed from drivers/media/dvb/dvb-core/dvb_demux.c)361
-rw-r--r--drivers/media/dvb-core/dvb_frontend.c3107
-rw-r--r--drivers/media/dvb-core/dvb_net.c1664
-rw-r--r--drivers/media/dvb-core/dvb_ringbuffer.c377
-rw-r--r--drivers/media/dvb-core/dvb_vb2.c454
-rw-r--r--drivers/media/dvb-core/dvbdev.c1140
-rw-r--r--drivers/media/dvb-frontends/Kconfig957
-rw-r--r--drivers/media/dvb-frontends/Makefile138
-rw-r--r--drivers/media/dvb-frontends/a8293.c278
-rw-r--r--drivers/media/dvb-frontends/a8293.h29
-rw-r--r--drivers/media/dvb-frontends/af9013.c1576
-rw-r--r--drivers/media/dvb-frontends/af9013.h87
-rw-r--r--drivers/media/dvb-frontends/af9013_priv.h851
-rw-r--r--drivers/media/dvb-frontends/af9033.c1195
-rw-r--r--drivers/media/dvb-frontends/af9033.h91
-rw-r--r--drivers/media/dvb-frontends/af9033_priv.h1991
-rw-r--r--drivers/media/dvb-frontends/as102_fe.c471
-rw-r--r--drivers/media/dvb-frontends/as102_fe.h20
-rw-r--r--drivers/media/dvb-frontends/as102_fe_types.h179
-rw-r--r--drivers/media/dvb-frontends/ascot2e.c540
-rw-r--r--drivers/media/dvb-frontends/ascot2e.h57
-rw-r--r--drivers/media/dvb-frontends/atbm8830.c (renamed from drivers/media/dvb/frontends/atbm8830.c)36
-rw-r--r--drivers/media/dvb-frontends/atbm8830.h62
-rw-r--r--drivers/media/dvb-frontends/atbm8830_priv.h62
-rw-r--r--drivers/media/dvb-frontends/au8522.h79
-rw-r--r--drivers/media/dvb-frontends/au8522_common.c (renamed from drivers/media/dvb/frontends/au8522_common.c)53
-rw-r--r--drivers/media/dvb-frontends/au8522_decoder.c785
-rw-r--r--drivers/media/dvb-frontends/au8522_dig.c918
-rw-r--r--drivers/media/dvb-frontends/au8522_priv.h446
-rw-r--r--drivers/media/dvb-frontends/bcm3510.c (renamed from drivers/media/dvb/frontends/bcm3510.c)57
-rw-r--r--drivers/media/dvb-frontends/bcm3510.h36
-rw-r--r--drivers/media/dvb-frontends/bcm3510_priv.h (renamed from drivers/media/dvb/frontends/bcm3510_priv.h)23
-rw-r--r--drivers/media/dvb-frontends/bsbe1-d01a.h129
-rw-r--r--drivers/media/dvb-frontends/bsbe1.h89
-rw-r--r--drivers/media/dvb-frontends/bsru6.h (renamed from drivers/media/dvb/frontends/bsru6.h)21
-rw-r--r--drivers/media/dvb-frontends/cx22700.c (renamed from drivers/media/dvb/frontends/cx22700.c)48
-rw-r--r--drivers/media/dvb-frontends/cx22700.h34
-rw-r--r--drivers/media/dvb-frontends/cx22702.c (renamed from drivers/media/dvb/frontends/cx22702.c)30
-rw-r--r--drivers/media/dvb-frontends/cx22702.h45
-rw-r--r--drivers/media/dvb-frontends/cx24110.c (renamed from drivers/media/dvb/frontends/cx24110.c)118
-rw-r--r--drivers/media/dvb-frontends/cx24110.h48
-rw-r--r--drivers/media/dvb-frontends/cx24113.c (renamed from drivers/media/dvb/frontends/cx24113.c)47
-rw-r--r--drivers/media/dvb-frontends/cx24113.h38
-rw-r--r--drivers/media/dvb-frontends/cx24116.c (renamed from drivers/media/dvb/frontends/cx24116.c)117
-rw-r--r--drivers/media/dvb-frontends/cx24116.h45
-rw-r--r--drivers/media/dvb-frontends/cx24117.c1654
-rw-r--r--drivers/media/dvb-frontends/cx24117.h34
-rw-r--r--drivers/media/dvb-frontends/cx24120.c1591
-rw-r--r--drivers/media/dvb-frontends/cx24120.h48
-rw-r--r--drivers/media/dvb-frontends/cx24123.c (renamed from drivers/media/dvb/frontends/cx24123.c)102
-rw-r--r--drivers/media/dvb-frontends/cx24123.h48
-rw-r--r--drivers/media/dvb-frontends/cxd2099.c693
-rw-r--r--drivers/media/dvb-frontends/cxd2099.h24
-rw-r--r--drivers/media/dvb-frontends/cxd2820r.h128
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_c.c331
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_core.c745
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_priv.h124
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_t.c425
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_t2.c420
-rw-r--r--drivers/media/dvb-frontends/cxd2841er.c4006
-rw-r--r--drivers/media/dvb-frontends/cxd2841er.h63
-rw-r--r--drivers/media/dvb-frontends/cxd2841er_priv.h38
-rw-r--r--drivers/media/dvb-frontends/cxd2880/Kconfig8
-rw-r--r--drivers/media/dvb-frontends/cxd2880/Makefile16
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880.h29
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_common.c21
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_common.h20
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c129
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h23
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h29
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h74
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h385
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c72
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h27
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_io.c66
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_io.h54
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h34
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c113
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h26
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c3519
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h365
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h12
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c919
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h45
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c1217
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h65
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c1878
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h135
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c775
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h77
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c150
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h29
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_top.c1957
-rw-r--r--drivers/media/dvb-frontends/dib0070.c769
-rw-r--r--drivers/media/dvb-frontends/dib0070.h (renamed from drivers/media/dvb/frontends/dib0070.h)7
-rw-r--r--drivers/media/dvb-frontends/dib0090.c (renamed from drivers/media/dvb/frontends/dib0090.c)692
-rw-r--r--drivers/media/dvb-frontends/dib0090.h (renamed from drivers/media/dvb/frontends/dib0090.h)7
-rw-r--r--drivers/media/dvb-frontends/dib3000.h52
-rw-r--r--drivers/media/dvb-frontends/dib3000mb.c (renamed from drivers/media/dvb/frontends/dib3000mb.c)189
-rw-r--r--drivers/media/dvb-frontends/dib3000mb_priv.h (renamed from drivers/media/dvb/frontends/dib3000mb_priv.h)23
-rw-r--r--drivers/media/dvb-frontends/dib3000mc.c (renamed from drivers/media/dvb/frontends/dib3000mc.c)109
-rw-r--r--drivers/media/dvb-frontends/dib3000mc.h (renamed from drivers/media/dvb/frontends/dib3000mc.h)10
-rw-r--r--drivers/media/dvb-frontends/dib7000m.c (renamed from drivers/media/dvb/frontends/dib7000m.c)111
-rw-r--r--drivers/media/dvb-frontends/dib7000m.h (renamed from drivers/media/dvb/frontends/dib7000m.h)4
-rw-r--r--drivers/media/dvb-frontends/dib7000p.c (renamed from drivers/media/dvb/frontends/dib7000p.c)674
-rw-r--r--drivers/media/dvb-frontends/dib7000p.h78
-rw-r--r--drivers/media/dvb-frontends/dib8000.c4538
-rw-r--r--drivers/media/dvb-frontends/dib8000.h76
-rw-r--r--drivers/media/dvb-frontends/dib9000.c (renamed from drivers/media/dvb/frontends/dib9000.c)272
-rw-r--r--drivers/media/dvb-frontends/dib9000.h (renamed from drivers/media/dvb/frontends/dib9000.h)10
-rw-r--r--drivers/media/dvb-frontends/dibx000_common.c (renamed from drivers/media/dvb/frontends/dibx000_common.c)69
-rw-r--r--drivers/media/dvb-frontends/dibx000_common.h (renamed from drivers/media/dvb/frontends/dibx000_common.h)8
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/Kconfig8
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/Makefile6
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drx39xxj.h31
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h254
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drx_driver.h2326
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drx_driver_version.h72
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj.c12391
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj.h651
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj_map.h15055
-rw-r--r--drivers/media/dvb-frontends/drxd.h55
-rw-r--r--drivers/media/dvb-frontends/drxd_firm.c (renamed from drivers/media/dvb/frontends/drxd_firm.c)20
-rw-r--r--drivers/media/dvb-frontends/drxd_firm.h (renamed from drivers/media/dvb/frontends/drxd_firm.h)18
-rw-r--r--drivers/media/dvb-frontends/drxd_hard.c (renamed from drivers/media/dvb/frontends/drxd_hard.c)198
-rw-r--r--drivers/media/dvb-frontends/drxd_map_firm.h (renamed from drivers/media/dvb/frontends/drxd_map_firm.h)18
-rw-r--r--drivers/media/dvb-frontends/drxk.h75
-rw-r--r--drivers/media/dvb-frontends/drxk_hard.c6820
-rw-r--r--drivers/media/dvb-frontends/drxk_hard.h368
-rw-r--r--drivers/media/dvb-frontends/drxk_map.h (renamed from drivers/media/dvb/frontends/drxk_map.h)4
-rw-r--r--drivers/media/dvb-frontends/ds3000.c1130
-rw-r--r--drivers/media/dvb-frontends/ds3000.h37
-rw-r--r--drivers/media/dvb-frontends/dvb-pll.c954
-rw-r--r--drivers/media/dvb-frontends/dvb-pll.h65
-rw-r--r--drivers/media/dvb-frontends/dvb_dummy_fe.c286
-rw-r--r--drivers/media/dvb-frontends/dvb_dummy_fe.h36
-rw-r--r--drivers/media/dvb-frontends/ec100.c (renamed from drivers/media/dvb/frontends/ec100.c)86
-rw-r--r--drivers/media/dvb-frontends/ec100.h31
-rw-r--r--drivers/media/dvb-frontends/eds1547.h (renamed from drivers/media/dvb/frontends/eds1547.h)7
-rw-r--r--drivers/media/dvb-frontends/gp8psk-fe.c397
-rw-r--r--drivers/media/dvb-frontends/gp8psk-fe.h73
-rw-r--r--drivers/media/dvb-frontends/helene.c1120
-rw-r--r--drivers/media/dvb-frontends/helene.h88
-rw-r--r--drivers/media/dvb-frontends/horus3a.c402
-rw-r--r--drivers/media/dvb-frontends/horus3a.h57
-rw-r--r--drivers/media/dvb-frontends/isl6405.c (renamed from drivers/media/dvb/frontends/isl6405.c)30
-rw-r--r--drivers/media/dvb-frontends/isl6405.h57
-rw-r--r--drivers/media/dvb-frontends/isl6421.c220
-rw-r--r--drivers/media/dvb-frontends/isl6421.h38
-rw-r--r--drivers/media/dvb-frontends/isl6423.c (renamed from drivers/media/dvb/frontends/isl6423.c)18
-rw-r--r--drivers/media/dvb-frontends/isl6423.h51
-rw-r--r--drivers/media/dvb-frontends/itd1000.c (renamed from drivers/media/dvb/frontends/itd1000.c)51
-rw-r--r--drivers/media/dvb-frontends/itd1000.h28
-rw-r--r--drivers/media/dvb-frontends/itd1000_priv.h74
-rw-r--r--drivers/media/dvb-frontends/ix2505v.c (renamed from drivers/media/dvb/frontends/ix2505v.c)38
-rw-r--r--drivers/media/dvb-frontends/ix2505v.h53
-rw-r--r--drivers/media/dvb-frontends/l64781.c (renamed from drivers/media/dvb/frontends/l64781.c)45
-rw-r--r--drivers/media/dvb-frontends/l64781.h34
-rw-r--r--drivers/media/dvb-frontends/lg2160.c (renamed from drivers/media/dvb/frontends/lg2160.c)122
-rw-r--r--drivers/media/dvb-frontends/lg2160.h69
-rw-r--r--drivers/media/dvb-frontends/lgdt3305.c (renamed from drivers/media/dvb/frontends/lgdt3305.c)72
-rw-r--r--drivers/media/dvb-frontends/lgdt3305.h82
-rw-r--r--drivers/media/dvb-frontends/lgdt3306a.c2267
-rw-r--r--drivers/media/dvb-frontends/lgdt3306a.h69
-rw-r--r--drivers/media/dvb-frontends/lgdt330x.c1006
-rw-r--r--drivers/media/dvb-frontends/lgdt330x.h60
-rw-r--r--drivers/media/dvb-frontends/lgdt330x_priv.h57
-rw-r--r--drivers/media/dvb-frontends/lgs8gl5.c (renamed from drivers/media/dvb/frontends/lgs8gl5.c)36
-rw-r--r--drivers/media/dvb-frontends/lgs8gl5.h32
-rw-r--r--drivers/media/dvb-frontends/lgs8gxx.c (renamed from drivers/media/dvb/frontends/lgs8gxx.c)51
-rw-r--r--drivers/media/dvb-frontends/lgs8gxx.h80
-rw-r--r--drivers/media/dvb-frontends/lgs8gxx_priv.h56
-rw-r--r--drivers/media/dvb-frontends/lnbh24.h40
-rw-r--r--drivers/media/dvb-frontends/lnbh25.c180
-rw-r--r--drivers/media/dvb-frontends/lnbh25.h46
-rw-r--r--drivers/media/dvb-frontends/lnbh29.c168
-rw-r--r--drivers/media/dvb-frontends/lnbh29.h36
-rw-r--r--drivers/media/dvb-frontends/lnbp21.c (renamed from drivers/media/dvb/frontends/lnbp21.c)35
-rw-r--r--drivers/media/dvb-frontends/lnbp21.h57
-rw-r--r--drivers/media/dvb-frontends/lnbp22.c132
-rw-r--r--drivers/media/dvb-frontends/lnbp22.h39
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.c1965
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.h158
-rw-r--r--drivers/media/dvb-frontends/m88ds3103_priv.h402
-rw-r--r--drivers/media/dvb-frontends/m88rs2000.c817
-rw-r--r--drivers/media/dvb-frontends/m88rs2000.h49
-rw-r--r--drivers/media/dvb-frontends/mb86a16.c (renamed from drivers/media/dvb/frontends/mb86a16.c)113
-rw-r--r--drivers/media/dvb-frontends/mb86a16.h41
-rw-r--r--drivers/media/dvb-frontends/mb86a16_priv.h (renamed from drivers/media/dvb/frontends/mb86a16_priv.h)14
-rw-r--r--drivers/media/dvb-frontends/mb86a20s.c2112
-rw-r--r--drivers/media/dvb-frontends/mb86a20s.h48
-rw-r--r--drivers/media/dvb-frontends/mn88443x.c811
-rw-r--r--drivers/media/dvb-frontends/mn88443x.h27
-rw-r--r--drivers/media/dvb-frontends/mn88472.c731
-rw-r--r--drivers/media/dvb-frontends/mn88472.h50
-rw-r--r--drivers/media/dvb-frontends/mn88472_priv.h30
-rw-r--r--drivers/media/dvb-frontends/mn88473.c766
-rw-r--r--drivers/media/dvb-frontends/mn88473.h35
-rw-r--r--drivers/media/dvb-frontends/mn88473_priv.h29
-rw-r--r--drivers/media/dvb-frontends/mt312.c (renamed from drivers/media/dvb/frontends/mt312.c)102
-rw-r--r--drivers/media/dvb-frontends/mt312.h38
-rw-r--r--drivers/media/dvb-frontends/mt312_priv.h (renamed from drivers/media/dvb/frontends/mt312_priv.h)15
-rw-r--r--drivers/media/dvb-frontends/mt352.c (renamed from drivers/media/dvb/frontends/mt352.c)38
-rw-r--r--drivers/media/dvb-frontends/mt352.h59
-rw-r--r--drivers/media/dvb-frontends/mt352_priv.h (renamed from drivers/media/dvb/frontends/mt352_priv.h)16
-rw-r--r--drivers/media/dvb-frontends/mxl5xx.c1885
-rw-r--r--drivers/media/dvb-frontends/mxl5xx.h54
-rw-r--r--drivers/media/dvb-frontends/mxl5xx_defs.h728
-rw-r--r--drivers/media/dvb-frontends/mxl5xx_regs.h358
-rw-r--r--drivers/media/dvb-frontends/mxl692.c1368
-rw-r--r--drivers/media/dvb-frontends/mxl692.h29
-rw-r--r--drivers/media/dvb-frontends/mxl692_defs.h539
-rw-r--r--drivers/media/dvb-frontends/nxt200x.c (renamed from drivers/media/dvb/frontends/nxt200x.c)132
-rw-r--r--drivers/media/dvb-frontends/nxt200x.h43
-rw-r--r--drivers/media/dvb-frontends/nxt6000.c (renamed from drivers/media/dvb/frontends/nxt6000.c)176
-rw-r--r--drivers/media/dvb-frontends/nxt6000.h36
-rw-r--r--drivers/media/dvb-frontends/nxt6000_priv.h (renamed from drivers/media/dvb/frontends/nxt6000_priv.h)1
-rw-r--r--drivers/media/dvb-frontends/or51132.c (renamed from drivers/media/dvb/frontends/or51132.c)61
-rw-r--r--drivers/media/dvb-frontends/or51132.h35
-rw-r--r--drivers/media/dvb-frontends/or51211.c555
-rw-r--r--drivers/media/dvb-frontends/or51211.h39
-rw-r--r--drivers/media/dvb-frontends/rtl2830.c898
-rw-r--r--drivers/media/dvb-frontends/rtl2830.h38
-rw-r--r--drivers/media/dvb-frontends/rtl2830_priv.h39
-rw-r--r--drivers/media/dvb-frontends/rtl2832.c1148
-rw-r--r--drivers/media/dvb-frontends/rtl2832.h49
-rw-r--r--drivers/media/dvb-frontends/rtl2832_priv.h415
-rw-r--r--drivers/media/dvb-frontends/rtl2832_sdr.c1494
-rw-r--r--drivers/media/dvb-frontends/rtl2832_sdr.h47
-rw-r--r--drivers/media/dvb-frontends/s5h1409.c (renamed from drivers/media/dvb/frontends/s5h1409.c)58
-rw-r--r--drivers/media/dvb-frontends/s5h1409.h70
-rw-r--r--drivers/media/dvb-frontends/s5h1411.c (renamed from drivers/media/dvb/frontends/s5h1411.c)61
-rw-r--r--drivers/media/dvb-frontends/s5h1411.h72
-rw-r--r--drivers/media/dvb-frontends/s5h1420.c (renamed from drivers/media/dvb/frontends/s5h1420.c)114
-rw-r--r--drivers/media/dvb-frontends/s5h1420.h47
-rw-r--r--drivers/media/dvb-frontends/s5h1420_priv.h88
-rw-r--r--drivers/media/dvb-frontends/s5h1432.c (renamed from drivers/media/dvb/frontends/s5h1432.c)45
-rw-r--r--drivers/media/dvb-frontends/s5h1432.h76
-rw-r--r--drivers/media/dvb-frontends/s921.c (renamed from drivers/media/dvb/frontends/s921.c)56
-rw-r--r--drivers/media/dvb-frontends/s921.h38
-rw-r--r--drivers/media/dvb-frontends/si2165.c1303
-rw-r--r--drivers/media/dvb-frontends/si2165.h44
-rw-r--r--drivers/media/dvb-frontends/si2165_priv.h110
-rw-r--r--drivers/media/dvb-frontends/si2168.c814
-rw-r--r--drivers/media/dvb-frontends/si2168.h51
-rw-r--r--drivers/media/dvb-frontends/si2168_priv.h53
-rw-r--r--drivers/media/dvb-frontends/si21xx.c (renamed from drivers/media/dvb/frontends/si21xx.c)56
-rw-r--r--drivers/media/dvb-frontends/si21xx.h (renamed from drivers/media/dvb/frontends/si21xx.h)6
-rw-r--r--drivers/media/dvb-frontends/sp2.c428
-rw-r--r--drivers/media/dvb-frontends/sp2.h43
-rw-r--r--drivers/media/dvb-frontends/sp2_priv.h41
-rw-r--r--drivers/media/dvb-frontends/sp887x.c (renamed from drivers/media/dvb/frontends/sp887x.c)44
-rw-r--r--drivers/media/dvb-frontends/sp887x.h (renamed from drivers/media/dvb/frontends/sp887x.h)3
-rw-r--r--drivers/media/dvb-frontends/stb0899_algo.c (renamed from drivers/media/dvb/frontends/stb0899_algo.c)129
-rw-r--r--drivers/media/dvb-frontends/stb0899_cfg.h (renamed from drivers/media/dvb/frontends/stb0899_cfg.h)14
-rw-r--r--drivers/media/dvb-frontends/stb0899_drv.c (renamed from drivers/media/dvb/frontends/stb0899_drv.c)136
-rw-r--r--drivers/media/dvb-frontends/stb0899_drv.h (renamed from drivers/media/dvb/frontends/stb0899_drv.h)25
-rw-r--r--drivers/media/dvb-frontends/stb0899_priv.h (renamed from drivers/media/dvb/frontends/stb0899_priv.h)18
-rw-r--r--drivers/media/dvb-frontends/stb0899_reg.h (renamed from drivers/media/dvb/frontends/stb0899_reg.h)22
-rw-r--r--drivers/media/dvb-frontends/stb6000.c (renamed from drivers/media/dvb/frontends/stb6000.c)26
-rw-r--r--drivers/media/dvb-frontends/stb6000.h38
-rw-r--r--drivers/media/dvb-frontends/stb6100.c (renamed from drivers/media/dvb/frontends/stb6100.c)136
-rw-r--r--drivers/media/dvb-frontends/stb6100.h102
-rw-r--r--drivers/media/dvb-frontends/stb6100_cfg.h87
-rw-r--r--drivers/media/dvb-frontends/stb6100_proc.h115
-rw-r--r--drivers/media/dvb-frontends/stv0288.c (renamed from drivers/media/dvb/frontends/stv0288.c)89
-rw-r--r--drivers/media/dvb-frontends/stv0288.h54
-rw-r--r--drivers/media/dvb-frontends/stv0297.c (renamed from drivers/media/dvb/frontends/stv0297.c)55
-rw-r--r--drivers/media/dvb-frontends/stv0297.h45
-rw-r--r--drivers/media/dvb-frontends/stv0299.c (renamed from drivers/media/dvb/frontends/stv0299.c)91
-rw-r--r--drivers/media/dvb-frontends/stv0299.h106
-rw-r--r--drivers/media/dvb-frontends/stv0367.c3332
-rw-r--r--drivers/media/dvb-frontends/stv0367.h64
-rw-r--r--drivers/media/dvb-frontends/stv0367_defs.h1291
-rw-r--r--drivers/media/dvb-frontends/stv0367_priv.h (renamed from drivers/media/dvb/frontends/stv0367_priv.h)22
-rw-r--r--drivers/media/dvb-frontends/stv0367_regs.h (renamed from drivers/media/dvb/frontends/stv0367_regs.h)20
-rw-r--r--drivers/media/dvb-frontends/stv0900.h59
-rw-r--r--drivers/media/dvb-frontends/stv0900_core.c (renamed from drivers/media/dvb/frontends/stv0900_core.c)105
-rw-r--r--drivers/media/dvb-frontends/stv0900_init.h (renamed from drivers/media/dvb/frontends/stv0900_init.h)50
-rw-r--r--drivers/media/dvb-frontends/stv0900_priv.h (renamed from drivers/media/dvb/frontends/stv0900_priv.h)19
-rw-r--r--drivers/media/dvb-frontends/stv0900_reg.h (renamed from drivers/media/dvb/frontends/stv0900_reg.h)19
-rw-r--r--drivers/media/dvb-frontends/stv0900_sw.c (renamed from drivers/media/dvb/frontends/stv0900_sw.c)43
-rw-r--r--drivers/media/dvb-frontends/stv090x.c (renamed from drivers/media/dvb/frontends/stv090x.c)715
-rw-r--r--drivers/media/dvb-frontends/stv090x.h119
-rw-r--r--drivers/media/dvb-frontends/stv090x_priv.h (renamed from drivers/media/dvb/frontends/stv090x_priv.h)20
-rw-r--r--drivers/media/dvb-frontends/stv090x_reg.h (renamed from drivers/media/dvb/frontends/stv090x_reg.h)14
-rw-r--r--drivers/media/dvb-frontends/stv0910.c1829
-rw-r--r--drivers/media/dvb-frontends/stv0910.h42
-rw-r--r--drivers/media/dvb-frontends/stv0910_regs.h4761
-rw-r--r--drivers/media/dvb-frontends/stv6110.c (renamed from drivers/media/dvb/frontends/stv6110.c)54
-rw-r--r--drivers/media/dvb-frontends/stv6110.h48
-rw-r--r--drivers/media/dvb-frontends/stv6110x.c (renamed from drivers/media/dvb/frontends/stv6110x.c)181
-rw-r--r--drivers/media/dvb-frontends/stv6110x.h64
-rw-r--r--drivers/media/dvb-frontends/stv6110x_priv.h65
-rw-r--r--drivers/media/dvb-frontends/stv6110x_reg.h (renamed from drivers/media/dvb/frontends/stv6110x_reg.h)14
-rw-r--r--drivers/media/dvb-frontends/stv6111.c681
-rw-r--r--drivers/media/dvb-frontends/stv6111.h28
-rw-r--r--drivers/media/dvb-frontends/tc90522.c852
-rw-r--r--drivers/media/dvb-frontends/tc90522.h36
-rw-r--r--drivers/media/dvb-frontends/tda10021.c (renamed from drivers/media/dvb/frontends/tda10021.c)88
-rw-r--r--drivers/media/dvb-frontends/tda10023.c (renamed from drivers/media/dvb/frontends/tda10023.c)45
-rw-r--r--drivers/media/dvb-frontends/tda1002x.h74
-rw-r--r--drivers/media/dvb-frontends/tda10048.c (renamed from drivers/media/dvb/frontends/tda10048.c)69
-rw-r--r--drivers/media/dvb-frontends/tda10048.h77
-rw-r--r--drivers/media/dvb-frontends/tda1004x.c (renamed from drivers/media/dvb/frontends/tda1004x.c)67
-rw-r--r--drivers/media/dvb-frontends/tda1004x.h (renamed from drivers/media/dvb/frontends/tda1004x.h)19
-rw-r--r--drivers/media/dvb-frontends/tda10071.c1253
-rw-r--r--drivers/media/dvb-frontends/tda10071.h41
-rw-r--r--drivers/media/dvb-frontends/tda10071_priv.h106
-rw-r--r--drivers/media/dvb-frontends/tda10086.c (renamed from drivers/media/dvb/frontends/tda10086.c)68
-rw-r--r--drivers/media/dvb-frontends/tda10086.h48
-rw-r--r--drivers/media/dvb-frontends/tda18271c2dd.c (renamed from drivers/media/dvb/frontends/tda18271c2dd.c)105
-rw-r--r--drivers/media/dvb-frontends/tda18271c2dd.h17
-rw-r--r--drivers/media/dvb-frontends/tda18271c2dd_maps.h (renamed from drivers/media/dvb/frontends/tda18271c2dd_maps.h)27
-rw-r--r--drivers/media/dvb-frontends/tda665x.c234
-rw-r--r--drivers/media/dvb-frontends/tda665x.h40
-rw-r--r--drivers/media/dvb-frontends/tda8083.c (renamed from drivers/media/dvb/frontends/tda8083.c)81
-rw-r--r--drivers/media/dvb-frontends/tda8083.h38
-rw-r--r--drivers/media/dvb-frontends/tda8261.c195
-rw-r--r--drivers/media/dvb-frontends/tda8261.h43
-rw-r--r--drivers/media/dvb-frontends/tda8261_cfg.h49
-rw-r--r--drivers/media/dvb-frontends/tda826x.c (renamed from drivers/media/dvb/frontends/tda826x.c)26
-rw-r--r--drivers/media/dvb-frontends/tda826x.h41
-rw-r--r--drivers/media/dvb-frontends/tdhd1.h58
-rw-r--r--drivers/media/dvb-frontends/ts2020.c738
-rw-r--r--drivers/media/dvb-frontends/ts2020.h74
-rw-r--r--drivers/media/dvb-frontends/tua6100.c (renamed from drivers/media/dvb/frontends/tua6100.c)51
-rw-r--r--drivers/media/dvb-frontends/tua6100.h35
-rw-r--r--drivers/media/dvb-frontends/ves1820.c (renamed from drivers/media/dvb/frontends/ves1820.c)48
-rw-r--r--drivers/media/dvb-frontends/ves1820.h44
-rw-r--r--drivers/media/dvb-frontends/ves1x93.c (renamed from drivers/media/dvb/frontends/ves1x93.c)50
-rw-r--r--drivers/media/dvb-frontends/ves1x93.h42
-rw-r--r--drivers/media/dvb-frontends/z0194a.h (renamed from drivers/media/dvb/frontends/z0194a.h)7
-rw-r--r--drivers/media/dvb-frontends/zd1301_demod.c540
-rw-r--r--drivers/media/dvb-frontends/zd1301_demod.h61
-rw-r--r--drivers/media/dvb-frontends/zl10036.c (renamed from drivers/media/dvb/frontends/zl10036.c)46
-rw-r--r--drivers/media/dvb-frontends/zl10036.h40
-rw-r--r--drivers/media/dvb-frontends/zl10039.c (renamed from drivers/media/dvb/frontends/zl10039.c)49
-rw-r--r--drivers/media/dvb-frontends/zl10039.h26
-rw-r--r--drivers/media/dvb-frontends/zl10353.c (renamed from drivers/media/dvb/frontends/zl10353.c)56
-rw-r--r--drivers/media/dvb-frontends/zl10353.h48
-rw-r--r--drivers/media/dvb-frontends/zl10353_priv.h65
-rw-r--r--drivers/media/dvb/Kconfig91
-rw-r--r--drivers/media/dvb/Makefile21
-rw-r--r--drivers/media/dvb/b2c2/Kconfig45
-rw-r--r--drivers/media/dvb/b2c2/Makefile16
-rw-r--r--drivers/media/dvb/b2c2/flexcop-dma.c172
-rw-r--r--drivers/media/dvb/b2c2/flexcop.h29
-rw-r--r--drivers/media/dvb/bt8xx/Kconfig22
-rw-r--r--drivers/media/dvb/bt8xx/Makefile6
-rw-r--r--drivers/media/dvb/bt8xx/dst_ca.h58
-rw-r--r--drivers/media/dvb/bt8xx/dvb-bt8xx.h63
-rw-r--r--drivers/media/dvb/ddbridge/Kconfig18
-rw-r--r--drivers/media/dvb/ddbridge/Makefile14
-rw-r--r--drivers/media/dvb/ddbridge/ddbridge-core.c1722
-rw-r--r--drivers/media/dvb/ddbridge/ddbridge-regs.h151
-rw-r--r--drivers/media/dvb/ddbridge/ddbridge.h185
-rw-r--r--drivers/media/dvb/dm1105/Kconfig20
-rw-r--r--drivers/media/dvb/dm1105/Makefile3
-rw-r--r--drivers/media/dvb/dvb-core/Makefile11
-rw-r--r--drivers/media/dvb/dvb-core/demux.h280
-rw-r--r--drivers/media/dvb/dvb-core/dmxdev.c1277
-rw-r--r--drivers/media/dvb/dvb-core/dmxdev.h118
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ca_en50221.c1753
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ca_en50221.h136
-rw-r--r--drivers/media/dvb/dvb-core/dvb_demux.h151
-rw-r--r--drivers/media/dvb/dvb-core/dvb_filter.c603
-rw-r--r--drivers/media/dvb/dvb-core/dvb_filter.h246
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c2563
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h422
-rw-r--r--drivers/media/dvb/dvb-core/dvb_math.c145
-rw-r--r--drivers/media/dvb/dvb-core/dvb_math.h58
-rw-r--r--drivers/media/dvb/dvb-core/dvb_net.c1516
-rw-r--r--drivers/media/dvb/dvb-core/dvb_net.h66
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ringbuffer.c299
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ringbuffer.h186
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.c507
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.h172
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig437
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile121
-rw-r--r--drivers/media/dvb/dvb-usb/a800.c191
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.c1952
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.h132
-rw-r--r--drivers/media/dvb/dvb-usb/af9035.c1242
-rw-r--r--drivers/media/dvb/dvb-usb/af9035.h113
-rw-r--r--drivers/media/dvb/dvb-usb/anysee.c1406
-rw-r--r--drivers/media/dvb/dvb-usb/au6610.c252
-rw-r--r--drivers/media/dvb/dvb-usb/au6610.h39
-rw-r--r--drivers/media/dvb/dvb-usb/az6007.c957
-rw-r--r--drivers/media/dvb/dvb-usb/ce6230.c324
-rw-r--r--drivers/media/dvb/dvb-usb/ce6230.h69
-rw-r--r--drivers/media/dvb/dvb-usb/cinergyT2-core.c254
-rw-r--r--drivers/media/dvb/dvb-usb/cinergyT2-fe.c356
-rw-r--r--drivers/media/dvb/dvb-usb/cxusb.c2043
-rw-r--r--drivers/media/dvb/dvb-usb/cxusb.h35
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700.h75
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_devices.c4813
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-common.c479
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-mb.c471
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-mc.c149
-rw-r--r--drivers/media/dvb/dvb-usb/dtt200u-fe.c210
-rw-r--r--drivers/media/dvb/dvb-usb/dtt200u.c368
-rw-r--r--drivers/media/dvb/dvb-usb/dtv5100.h51
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-firmware.c146
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-i2c.c43
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h358
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-init.c304
-rw-r--r--drivers/media/dvb/dvb-usb/dw2102.c1951
-rw-r--r--drivers/media/dvb/dvb-usb/ec168.c435
-rw-r--r--drivers/media/dvb/dvb-usb/ec168.h73
-rw-r--r--drivers/media/dvb/dvb-usb/friio-fe.c473
-rw-r--r--drivers/media/dvb/dvb-usb/friio.c522
-rw-r--r--drivers/media/dvb/dvb-usb/friio.h99
-rw-r--r--drivers/media/dvb/dvb-usb/gl861.c217
-rw-r--r--drivers/media/dvb/dvb-usb/gl861.h15
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk-fe.c369
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk.c328
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk.h100
-rw-r--r--drivers/media/dvb/dvb-usb/it913x.c931
-rw-r--r--drivers/media/dvb/dvb-usb/lmedm04.c1407
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-demod.h55
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-gpio.h56
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-i2c.h35
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-phy.h53
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-tuner.h89
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf.c1835
-rw-r--r--drivers/media/dvb/dvb-usb/rtl28xxu.c1010
-rw-r--r--drivers/media/dvb/dvb-usb/vp7045.c302
-rw-r--r--drivers/media/dvb/firewire/Kconfig19
-rw-r--r--drivers/media/dvb/firewire/Makefile6
-rw-r--r--drivers/media/dvb/frontends/Kconfig748
-rw-r--r--drivers/media/dvb/frontends/Makefile104
-rw-r--r--drivers/media/dvb/frontends/a8293.c184
-rw-r--r--drivers/media/dvb/frontends/a8293.h41
-rw-r--r--drivers/media/dvb/frontends/af9013.c1524
-rw-r--r--drivers/media/dvb/frontends/af9013.h118
-rw-r--r--drivers/media/dvb/frontends/af9013_priv.h922
-rw-r--r--drivers/media/dvb/frontends/af9033.c980
-rw-r--r--drivers/media/dvb/frontends/af9033.h75
-rw-r--r--drivers/media/dvb/frontends/af9033_priv.h470
-rw-r--r--drivers/media/dvb/frontends/atbm8830.h76
-rw-r--r--drivers/media/dvb/frontends/atbm8830_priv.h75
-rw-r--r--drivers/media/dvb/frontends/au8522.h98
-rw-r--r--drivers/media/dvb/frontends/au8522_decoder.c842
-rw-r--r--drivers/media/dvb/frontends/au8522_dig.c826
-rw-r--r--drivers/media/dvb/frontends/au8522_priv.h423
-rw-r--r--drivers/media/dvb/frontends/bcm3510.h49
-rw-r--r--drivers/media/dvb/frontends/bsbe1-d01a.h146
-rw-r--r--drivers/media/dvb/frontends/bsbe1.h106
-rw-r--r--drivers/media/dvb/frontends/cx22700.h46
-rw-r--r--drivers/media/dvb/frontends/cx22702.h58
-rw-r--r--drivers/media/dvb/frontends/cx24110.h61
-rw-r--r--drivers/media/dvb/frontends/cx24113.h53
-rw-r--r--drivers/media/dvb/frontends/cx24116.h58
-rw-r--r--drivers/media/dvb/frontends/cx24123.h61
-rw-r--r--drivers/media/dvb/frontends/cxd2820r.h94
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_c.c346
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_core.c646
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_priv.h156
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_t.c453
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_t2.c426
-rw-r--r--drivers/media/dvb/frontends/dib0070.c780
-rw-r--r--drivers/media/dvb/frontends/dib3000.h56
-rw-r--r--drivers/media/dvb/frontends/dib7000p.h158
-rw-r--r--drivers/media/dvb/frontends/dib8000.c3558
-rw-r--r--drivers/media/dvb/frontends/dib8000.h174
-rw-r--r--drivers/media/dvb/frontends/drxd.h73
-rw-r--r--drivers/media/dvb/frontends/drxk.h57
-rw-r--r--drivers/media/dvb/frontends/drxk_hard.c6457
-rw-r--r--drivers/media/dvb/frontends/drxk_hard.h349
-rw-r--r--drivers/media/dvb/frontends/ds3000.c1312
-rw-r--r--drivers/media/dvb/frontends/ds3000.h48
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.c794
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.h56
-rw-r--r--drivers/media/dvb/frontends/dvb_dummy_fe.c276
-rw-r--r--drivers/media/dvb/frontends/dvb_dummy_fe.h51
-rw-r--r--drivers/media/dvb/frontends/ec100.h46
-rw-r--r--drivers/media/dvb/frontends/ec100_priv.h39
-rw-r--r--drivers/media/dvb/frontends/hd29l2.c861
-rw-r--r--drivers/media/dvb/frontends/hd29l2.h66
-rw-r--r--drivers/media/dvb/frontends/hd29l2_priv.h314
-rw-r--r--drivers/media/dvb/frontends/isl6405.h74
-rw-r--r--drivers/media/dvb/frontends/isl6421.c141
-rw-r--r--drivers/media/dvb/frontends/isl6421.h55
-rw-r--r--drivers/media/dvb/frontends/isl6423.h63
-rw-r--r--drivers/media/dvb/frontends/it913x-fe-priv.h1051
-rw-r--r--drivers/media/dvb/frontends/it913x-fe.c1045
-rw-r--r--drivers/media/dvb/frontends/it913x-fe.h237
-rw-r--r--drivers/media/dvb/frontends/itd1000.h42
-rw-r--r--drivers/media/dvb/frontends/itd1000_priv.h88
-rw-r--r--drivers/media/dvb/frontends/ix2505v.h64
-rw-r--r--drivers/media/dvb/frontends/l64781.h46
-rw-r--r--drivers/media/dvb/frontends/lg2160.h84
-rw-r--r--drivers/media/dvb/frontends/lgdt3305.h91
-rw-r--r--drivers/media/dvb/frontends/lgdt330x.c831
-rw-r--r--drivers/media/dvb/frontends/lgdt330x.h73
-rw-r--r--drivers/media/dvb/frontends/lgdt330x_priv.h77
-rw-r--r--drivers/media/dvb/frontends/lgs8gl5.h45
-rw-r--r--drivers/media/dvb/frontends/lgs8gxx.h95
-rw-r--r--drivers/media/dvb/frontends/lgs8gxx_priv.h70
-rw-r--r--drivers/media/dvb/frontends/lnbh24.h55
-rw-r--r--drivers/media/dvb/frontends/lnbp21.h75
-rw-r--r--drivers/media/dvb/frontends/lnbp22.c148
-rw-r--r--drivers/media/dvb/frontends/lnbp22.h57
-rw-r--r--drivers/media/dvb/frontends/m88rs2000.c919
-rw-r--r--drivers/media/dvb/frontends/m88rs2000.h66
-rw-r--r--drivers/media/dvb/frontends/mb86a16.h52
-rw-r--r--drivers/media/dvb/frontends/mb86a20s.c701
-rw-r--r--drivers/media/dvb/frontends/mb86a20s.h52
-rw-r--r--drivers/media/dvb/frontends/mt312.h51
-rw-r--r--drivers/media/dvb/frontends/mt352.h73
-rw-r--r--drivers/media/dvb/frontends/nxt200x.h63
-rw-r--r--drivers/media/dvb/frontends/nxt6000.h48
-rw-r--r--drivers/media/dvb/frontends/or51132.h55
-rw-r--r--drivers/media/dvb/frontends/or51211.c581
-rw-r--r--drivers/media/dvb/frontends/or51211.h53
-rw-r--r--drivers/media/dvb/frontends/rtl2830.c757
-rw-r--r--drivers/media/dvb/frontends/rtl2830.h97
-rw-r--r--drivers/media/dvb/frontends/rtl2830_priv.h58
-rw-r--r--drivers/media/dvb/frontends/s5h1409.h88
-rw-r--r--drivers/media/dvb/frontends/s5h1411.h90
-rw-r--r--drivers/media/dvb/frontends/s5h1420.h61
-rw-r--r--drivers/media/dvb/frontends/s5h1420_priv.h102
-rw-r--r--drivers/media/dvb/frontends/s5h1432.h91
-rw-r--r--drivers/media/dvb/frontends/s921.h47
-rw-r--r--drivers/media/dvb/frontends/sp8870.c620
-rw-r--r--drivers/media/dvb/frontends/sp8870.h50
-rw-r--r--drivers/media/dvb/frontends/stb6000.h51
-rw-r--r--drivers/media/dvb/frontends/stb6100.h115
-rw-r--r--drivers/media/dvb/frontends/stb6100_cfg.h104
-rw-r--r--drivers/media/dvb/frontends/stb6100_proc.h138
-rw-r--r--drivers/media/dvb/frontends/stv0288.h67
-rw-r--r--drivers/media/dvb/frontends/stv0297.h57
-rw-r--r--drivers/media/dvb/frontends/stv0299.h118
-rw-r--r--drivers/media/dvb/frontends/stv0367.c3453
-rw-r--r--drivers/media/dvb/frontends/stv0367.h66
-rw-r--r--drivers/media/dvb/frontends/stv0900.h74
-rw-r--r--drivers/media/dvb/frontends/stv090x.h134
-rw-r--r--drivers/media/dvb/frontends/stv6110.h63
-rw-r--r--drivers/media/dvb/frontends/stv6110x.h73
-rw-r--r--drivers/media/dvb/frontends/stv6110x_priv.h76
-rw-r--r--drivers/media/dvb/frontends/tda1002x.h87
-rw-r--r--drivers/media/dvb/frontends/tda10048.h90
-rw-r--r--drivers/media/dvb/frontends/tda10071.c1267
-rw-r--r--drivers/media/dvb/frontends/tda10071.h81
-rw-r--r--drivers/media/dvb/frontends/tda10071_priv.h122
-rw-r--r--drivers/media/dvb/frontends/tda10086.h61
-rw-r--r--drivers/media/dvb/frontends/tda18271c2dd.h16
-rw-r--r--drivers/media/dvb/frontends/tda665x.c258
-rw-r--r--drivers/media/dvb/frontends/tda665x.h52
-rw-r--r--drivers/media/dvb/frontends/tda8083.h50
-rw-r--r--drivers/media/dvb/frontends/tda8261.c230
-rw-r--r--drivers/media/dvb/frontends/tda8261.h55
-rw-r--r--drivers/media/dvb/frontends/tda8261_cfg.h84
-rw-r--r--drivers/media/dvb/frontends/tda826x.h53
-rw-r--r--drivers/media/dvb/frontends/tdhd1.h74
-rw-r--r--drivers/media/dvb/frontends/tua6100.h47
-rw-r--r--drivers/media/dvb/frontends/ves1820.h56
-rw-r--r--drivers/media/dvb/frontends/ves1x93.h55
-rw-r--r--drivers/media/dvb/frontends/zl10036.h53
-rw-r--r--drivers/media/dvb/frontends/zl10039.h40
-rw-r--r--drivers/media/dvb/frontends/zl10353.h62
-rw-r--r--drivers/media/dvb/frontends/zl10353_priv.h79
-rw-r--r--drivers/media/dvb/mantis/Kconfig38
-rw-r--r--drivers/media/dvb/mantis/Makefile28
-rw-r--r--drivers/media/dvb/mantis/hopper_vp3028.c88
-rw-r--r--drivers/media/dvb/mantis/hopper_vp3028.h30
-rw-r--r--drivers/media/dvb/mantis/mantis_ca.h27
-rw-r--r--drivers/media/dvb/mantis/mantis_cards.c307
-rw-r--r--drivers/media/dvb/mantis/mantis_core.c235
-rw-r--r--drivers/media/dvb/mantis/mantis_core.h57
-rw-r--r--drivers/media/dvb/mantis/mantis_dma.h30
-rw-r--r--drivers/media/dvb/mantis/mantis_dvb.h35
-rw-r--r--drivers/media/dvb/mantis/mantis_hif.h29
-rw-r--r--drivers/media/dvb/mantis/mantis_i2c.h30
-rw-r--r--drivers/media/dvb/mantis/mantis_input.c159
-rw-r--r--drivers/media/dvb/mantis/mantis_ioc.h51
-rw-r--r--drivers/media/dvb/mantis/mantis_link.h83
-rw-r--r--drivers/media/dvb/mantis/mantis_pci.h27
-rw-r--r--drivers/media/dvb/mantis/mantis_pcmcia.c121
-rw-r--r--drivers/media/dvb/mantis/mantis_reg.h197
-rw-r--r--drivers/media/dvb/mantis/mantis_uart.c188
-rw-r--r--drivers/media/dvb/mantis/mantis_uart.h58
-rw-r--r--drivers/media/dvb/mantis/mantis_vp1033.h30
-rw-r--r--drivers/media/dvb/mantis/mantis_vp1034.c120
-rw-r--r--drivers/media/dvb/mantis/mantis_vp1034.h33
-rw-r--r--drivers/media/dvb/mantis/mantis_vp1041.c357
-rw-r--r--drivers/media/dvb/mantis/mantis_vp1041.h33
-rw-r--r--drivers/media/dvb/mantis/mantis_vp2033.h30
-rw-r--r--drivers/media/dvb/mantis/mantis_vp2040.h32
-rw-r--r--drivers/media/dvb/mantis/mantis_vp3028.c38
-rw-r--r--drivers/media/dvb/mantis/mantis_vp3028.h33
-rw-r--r--drivers/media/dvb/mantis/mantis_vp3030.c105
-rw-r--r--drivers/media/dvb/mantis/mantis_vp3030.h30
-rw-r--r--drivers/media/dvb/ngene/Kconfig13
-rw-r--r--drivers/media/dvb/ngene/Makefile14
-rw-r--r--drivers/media/dvb/ngene/ngene-cards.c559
-rw-r--r--drivers/media/dvb/ngene/ngene-dvb.c261
-rw-r--r--drivers/media/dvb/pluto2/Kconfig15
-rw-r--r--drivers/media/dvb/pluto2/Makefile3
-rw-r--r--drivers/media/dvb/pt1/Kconfig12
-rw-r--r--drivers/media/dvb/pt1/Makefile5
-rw-r--r--drivers/media/dvb/pt1/pt1.c1246
-rw-r--r--drivers/media/dvb/pt1/va1j5jf8007s.c735
-rw-r--r--drivers/media/dvb/pt1/va1j5jf8007s.h46
-rw-r--r--drivers/media/dvb/pt1/va1j5jf8007t.c536
-rw-r--r--drivers/media/dvb/pt1/va1j5jf8007t.h46
-rw-r--r--drivers/media/dvb/siano/Kconfig34
-rw-r--r--drivers/media/dvb/siano/Makefile11
-rw-r--r--drivers/media/dvb/siano/sms-cards.c311
-rw-r--r--drivers/media/dvb/siano/smscoreapi.c1644
-rw-r--r--drivers/media/dvb/siano/smscoreapi.h775
-rw-r--r--drivers/media/dvb/siano/smsdvb.c1078
-rw-r--r--drivers/media/dvb/siano/smsendian.c103
-rw-r--r--drivers/media/dvb/siano/smsendian.h32
-rw-r--r--drivers/media/dvb/siano/smsir.c114
-rw-r--r--drivers/media/dvb/siano/smsir.h55
-rw-r--r--drivers/media/dvb/siano/smsusb.c568
-rw-r--r--drivers/media/dvb/ttpci/Kconfig159
-rw-r--r--drivers/media/dvb/ttpci/Makefile21
-rw-r--r--drivers/media/dvb/ttpci/av7110.c2939
-rw-r--r--drivers/media/dvb/ttpci/av7110.h314
-rw-r--r--drivers/media/dvb/ttpci/av7110_av.c1626
-rw-r--r--drivers/media/dvb/ttpci/av7110_av.h30
-rw-r--r--drivers/media/dvb/ttpci/av7110_ca.c387
-rw-r--r--drivers/media/dvb/ttpci/av7110_ca.h14
-rw-r--r--drivers/media/dvb/ttpci/av7110_hw.c1208
-rw-r--r--drivers/media/dvb/ttpci/av7110_hw.h495
-rw-r--r--drivers/media/dvb/ttpci/av7110_ipack.c403
-rw-r--r--drivers/media/dvb/ttpci/av7110_ipack.h12
-rw-r--r--drivers/media/dvb/ttpci/av7110_ir.c415
-rw-r--r--drivers/media/dvb/ttpci/av7110_v4l.c966
-rw-r--r--drivers/media/dvb/ttpci/budget-av.c1640
-rw-r--r--drivers/media/dvb/ttpci/budget-ci.c1591
-rw-r--r--drivers/media/dvb/ttpci/budget-patch.c680
-rw-r--r--drivers/media/dvb/ttpci/budget.c811
-rw-r--r--drivers/media/dvb/ttpci/ttpci-eeprom.h34
-rw-r--r--drivers/media/dvb/ttusb-budget/Kconfig18
-rw-r--r--drivers/media/dvb/ttusb-budget/Makefile3
-rw-r--r--drivers/media/dvb/ttusb-dec/Kconfig21
-rw-r--r--drivers/media/dvb/ttusb-dec/Makefile3
-rw-r--r--drivers/media/dvb/ttusb-dec/ttusbdecfe.h38
-rw-r--r--drivers/media/firewire/Kconfig23
-rw-r--r--drivers/media/firewire/Makefile5
-rw-r--r--drivers/media/firewire/firedtv-avc.c (renamed from drivers/media/dvb/firewire/firedtv-avc.c)46
-rw-r--r--drivers/media/firewire/firedtv-ci.c (renamed from drivers/media/dvb/firewire/firedtv-ci.c)21
-rw-r--r--drivers/media/firewire/firedtv-dvb.c (renamed from drivers/media/dvb/firewire/firedtv-dvb.c)28
-rw-r--r--drivers/media/firewire/firedtv-fe.c (renamed from drivers/media/dvb/firewire/firedtv-fe.c)50
-rw-r--r--drivers/media/firewire/firedtv-fw.c (renamed from drivers/media/dvb/firewire/firedtv-fw.c)27
-rw-r--r--drivers/media/firewire/firedtv-rc.c (renamed from drivers/media/dvb/firewire/firedtv-rc.c)11
-rw-r--r--drivers/media/firewire/firedtv.h (renamed from drivers/media/dvb/firewire/firedtv.h)31
-rw-r--r--drivers/media/i2c/Kconfig1733
-rw-r--r--drivers/media/i2c/Makefile168
-rw-r--r--drivers/media/i2c/ad5820.c380
-rw-r--r--drivers/media/i2c/adp1653.c (renamed from drivers/media/video/adp1653.c)160
-rw-r--r--drivers/media/i2c/adv7170.c (renamed from drivers/media/video/adv7170.c)101
-rw-r--r--drivers/media/i2c/adv7175.c (renamed from drivers/media/video/adv7175.c)98
-rw-r--r--drivers/media/i2c/adv7180.c1636
-rw-r--r--drivers/media/i2c/adv7183.c (renamed from drivers/media/video/adv7183.c)222
-rw-r--r--drivers/media/i2c/adv7183_regs.h (renamed from drivers/media/video/adv7183_regs.h)22
-rw-r--r--drivers/media/i2c/adv7343.c (renamed from drivers/media/video/adv7343.c)139
-rw-r--r--drivers/media/i2c/adv7343_regs.h (renamed from drivers/media/video/adv7343_regs.h)12
-rw-r--r--drivers/media/i2c/adv7393.c462
-rw-r--r--drivers/media/i2c/adv7393_regs.h180
-rw-r--r--drivers/media/i2c/adv748x/Makefile8
-rw-r--r--drivers/media/i2c/adv748x/adv748x-afe.c539
-rw-r--r--drivers/media/i2c/adv748x/adv748x-core.c858
-rw-r--r--drivers/media/i2c/adv748x/adv748x-csi2.c393
-rw-r--r--drivers/media/i2c/adv748x/adv748x-hdmi.c761
-rw-r--r--drivers/media/i2c/adv748x/adv748x.h443
-rw-r--r--drivers/media/i2c/adv7511-v4l2.c2025
-rw-r--r--drivers/media/i2c/adv7604.c3754
-rw-r--r--drivers/media/i2c/adv7842.c3694
-rw-r--r--drivers/media/i2c/ak7375.c357
-rw-r--r--drivers/media/i2c/ak881x.c326
-rw-r--r--drivers/media/i2c/alvium-csi2.c2546
-rw-r--r--drivers/media/i2c/alvium-csi2.h476
-rw-r--r--drivers/media/i2c/aptina-pll.c (renamed from drivers/media/video/aptina-pll.c)16
-rw-r--r--drivers/media/i2c/aptina-pll.h43
-rw-r--r--drivers/media/i2c/ar0521.c1189
-rw-r--r--drivers/media/i2c/bt819.c (renamed from drivers/media/video/bt819.c)75
-rw-r--r--drivers/media/i2c/bt856.c (renamed from drivers/media/video/bt856.c)38
-rw-r--r--drivers/media/i2c/bt866.c (renamed from drivers/media/video/bt866.c)41
-rw-r--r--drivers/media/i2c/ccs-pll.c911
-rw-r--r--drivers/media/i2c/ccs-pll.h235
-rw-r--r--drivers/media/i2c/ccs/Kconfig9
-rw-r--r--drivers/media/i2c/ccs/Makefile6
-rw-r--r--drivers/media/i2c/ccs/ccs-core.c3674
-rw-r--r--drivers/media/i2c/ccs/ccs-data-defs.h221
-rw-r--r--drivers/media/i2c/ccs/ccs-data.c982
-rw-r--r--drivers/media/i2c/ccs/ccs-data.h230
-rw-r--r--drivers/media/i2c/ccs/ccs-limits.c243
-rw-r--r--drivers/media/i2c/ccs/ccs-limits.h263
-rw-r--r--drivers/media/i2c/ccs/ccs-quirk.c217
-rw-r--r--drivers/media/i2c/ccs/ccs-quirk.h79
-rw-r--r--drivers/media/i2c/ccs/ccs-reg-access.c268
-rw-r--r--drivers/media/i2c/ccs/ccs-reg-access.h39
-rw-r--r--drivers/media/i2c/ccs/ccs-regs.h958
-rw-r--r--drivers/media/i2c/ccs/ccs.h279
-rw-r--r--drivers/media/i2c/ccs/smiapp-reg-defs.h585
-rw-r--r--drivers/media/i2c/cs3308.c126
-rw-r--r--drivers/media/i2c/cs5345.c206
-rw-r--r--drivers/media/i2c/cs53l32a.c (renamed from drivers/media/video/cs53l32a.c)50
-rw-r--r--drivers/media/i2c/cx25840/Kconfig9
-rw-r--r--drivers/media/i2c/cx25840/Makefile5
-rw-r--r--drivers/media/i2c/cx25840/cx25840-audio.c (renamed from drivers/media/video/cx25840/cx25840-audio.c)17
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.c3981
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.h194
-rw-r--r--drivers/media/i2c/cx25840/cx25840-firmware.c161
-rw-r--r--drivers/media/i2c/cx25840/cx25840-ir.c (renamed from drivers/media/video/cx25840/cx25840-ir.c)80
-rw-r--r--drivers/media/i2c/cx25840/cx25840-vbi.c (renamed from drivers/media/video/cx25840/cx25840-vbi.c)56
-rw-r--r--drivers/media/i2c/ds90ub913.c958
-rw-r--r--drivers/media/i2c/ds90ub953.c1437
-rw-r--r--drivers/media/i2c/ds90ub953.h104
-rw-r--r--drivers/media/i2c/ds90ub960.c5215
-rw-r--r--drivers/media/i2c/dw9714.c345
-rw-r--r--drivers/media/i2c/dw9719.c467
-rw-r--r--drivers/media/i2c/dw9768.c556
-rw-r--r--drivers/media/i2c/dw9807-vcm.c321
-rw-r--r--drivers/media/i2c/et8ek8/Kconfig6
-rw-r--r--drivers/media/i2c/et8ek8/Makefile3
-rw-r--r--drivers/media/i2c/et8ek8/et8ek8_driver.c1508
-rw-r--r--drivers/media/i2c/et8ek8/et8ek8_mode.c570
-rw-r--r--drivers/media/i2c/et8ek8/et8ek8_reg.h87
-rw-r--r--drivers/media/i2c/gc0308.c1444
-rw-r--r--drivers/media/i2c/gc0310.c783
-rw-r--r--drivers/media/i2c/gc05a2.c1347
-rw-r--r--drivers/media/i2c/gc08a3.c1327
-rw-r--r--drivers/media/i2c/gc2145.c1484
-rw-r--r--drivers/media/i2c/hi556.c1455
-rw-r--r--drivers/media/i2c/hi846.c2176
-rw-r--r--drivers/media/i2c/hi847.c2956
-rw-r--r--drivers/media/i2c/imx111.c1610
-rw-r--r--drivers/media/i2c/imx208.c1069
-rw-r--r--drivers/media/i2c/imx214.c1535
-rw-r--r--drivers/media/i2c/imx219.c1355
-rw-r--r--drivers/media/i2c/imx258.c1556
-rw-r--r--drivers/media/i2c/imx274.c2179
-rw-r--r--drivers/media/i2c/imx283.c1620
-rw-r--r--drivers/media/i2c/imx290.c1728
-rw-r--r--drivers/media/i2c/imx296.c1160
-rw-r--r--drivers/media/i2c/imx319.c2505
-rw-r--r--drivers/media/i2c/imx334.c1360
-rw-r--r--drivers/media/i2c/imx335.c1578
-rw-r--r--drivers/media/i2c/imx355.c1797
-rw-r--r--drivers/media/i2c/imx412.c1301
-rw-r--r--drivers/media/i2c/imx415.c1482
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c1002
-rw-r--r--drivers/media/i2c/isl7998x.c1626
-rw-r--r--drivers/media/i2c/ks0127.c (renamed from drivers/media/video/ks0127.c)92
-rw-r--r--drivers/media/i2c/ks0127.h38
-rw-r--r--drivers/media/i2c/lm3560.c480
-rw-r--r--drivers/media/i2c/lm3646.c409
-rw-r--r--drivers/media/i2c/lt6911uxe.c707
-rw-r--r--drivers/media/i2c/m52790.c180
-rw-r--r--drivers/media/i2c/max2175.c1441
-rw-r--r--drivers/media/i2c/max2175.h101
-rw-r--r--drivers/media/i2c/max9271.c374
-rw-r--r--drivers/media/i2c/max9271.h238
-rw-r--r--drivers/media/i2c/max9286.c1684
-rw-r--r--drivers/media/i2c/max96714.c1017
-rw-r--r--drivers/media/i2c/max96717.c1104
-rw-r--r--drivers/media/i2c/ml86v7667.c445
-rw-r--r--drivers/media/i2c/msp3400-driver.c (renamed from drivers/media/video/msp3400-driver.c)239
-rw-r--r--drivers/media/i2c/msp3400-driver.h (renamed from drivers/media/video/msp3400-driver.h)16
-rw-r--r--drivers/media/i2c/msp3400-kthreads.c (renamed from drivers/media/video/msp3400-kthreads.c)150
-rw-r--r--drivers/media/i2c/mt9m001.c888
-rw-r--r--drivers/media/i2c/mt9m111.c1406
-rw-r--r--drivers/media/i2c/mt9m114.c2567
-rw-r--r--drivers/media/i2c/mt9p031.c1243
-rw-r--r--drivers/media/i2c/mt9t112.c1129
-rw-r--r--drivers/media/i2c/mt9v011.c599
-rw-r--r--drivers/media/i2c/mt9v032.c1295
-rw-r--r--drivers/media/i2c/mt9v111.c1282
-rw-r--r--drivers/media/i2c/og01a1b.c1217
-rw-r--r--drivers/media/i2c/og0ve1b.c816
-rw-r--r--drivers/media/i2c/ov01a10.c961
-rw-r--r--drivers/media/i2c/ov02a10.c1003
-rw-r--r--drivers/media/i2c/ov02c10.c1022
-rw-r--r--drivers/media/i2c/ov02e10.c956
-rw-r--r--drivers/media/i2c/ov08d10.c1482
-rw-r--r--drivers/media/i2c/ov08x40.c2405
-rw-r--r--drivers/media/i2c/ov13858.c1781
-rw-r--r--drivers/media/i2c/ov13b10.c1718
-rw-r--r--drivers/media/i2c/ov2640.c1299
-rw-r--r--drivers/media/i2c/ov2659.c1584
-rw-r--r--drivers/media/i2c/ov2680.c1324
-rw-r--r--drivers/media/i2c/ov2685.c890
-rw-r--r--drivers/media/i2c/ov2735.c1109
-rw-r--r--drivers/media/i2c/ov2740.c1486
-rw-r--r--drivers/media/i2c/ov4689.c1045
-rw-r--r--drivers/media/i2c/ov5640.c4027
-rw-r--r--drivers/media/i2c/ov5645.c1252
-rw-r--r--drivers/media/i2c/ov5647.c1517
-rw-r--r--drivers/media/i2c/ov5648.c2635
-rw-r--r--drivers/media/i2c/ov5670.c2829
-rw-r--r--drivers/media/i2c/ov5675.c1386
-rw-r--r--drivers/media/i2c/ov5693.c1422
-rw-r--r--drivers/media/i2c/ov5695.c1373
-rw-r--r--drivers/media/i2c/ov6211.c793
-rw-r--r--drivers/media/i2c/ov64a40.c3677
-rw-r--r--drivers/media/i2c/ov7251.c1824
-rw-r--r--drivers/media/i2c/ov7640.c93
-rw-r--r--drivers/media/i2c/ov7670.c2022
-rw-r--r--drivers/media/i2c/ov772x.c1575
-rw-r--r--drivers/media/i2c/ov7740.c1181
-rw-r--r--drivers/media/i2c/ov8856.c2479
-rw-r--r--drivers/media/i2c/ov8858.c1997
-rw-r--r--drivers/media/i2c/ov8865.c3144
-rw-r--r--drivers/media/i2c/ov9282.c1530
-rw-r--r--drivers/media/i2c/ov9640.c773
-rw-r--r--drivers/media/i2c/ov9640.h (renamed from drivers/media/video/ov9640.h)8
-rw-r--r--drivers/media/i2c/ov9650.c1599
-rw-r--r--drivers/media/i2c/ov9734.c994
-rw-r--r--drivers/media/i2c/rdacm20.c672
-rw-r--r--drivers/media/i2c/rdacm21.c631
-rw-r--r--drivers/media/i2c/rj54n1cb0c.c (renamed from drivers/media/video/rj54n1cb0c.c)404
-rw-r--r--drivers/media/i2c/s5c73m3/Makefile3
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c1758
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c553
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-spi.c147
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3.h448
-rw-r--r--drivers/media/i2c/s5k5baf.c2035
-rw-r--r--drivers/media/i2c/s5k6a3.c373
-rw-r--r--drivers/media/i2c/saa6588.c (renamed from drivers/media/video/saa6588.c)111
-rw-r--r--drivers/media/i2c/saa6752hs.c787
-rw-r--r--drivers/media/i2c/saa7110.c (renamed from drivers/media/video/saa7110.c)56
-rw-r--r--drivers/media/i2c/saa7115.c (renamed from drivers/media/video/saa7115.c)640
-rw-r--r--drivers/media/i2c/saa711x_regs.h (renamed from drivers/media/video/saa711x_regs.h)35
-rw-r--r--drivers/media/i2c/saa7127.c (renamed from drivers/media/video/saa7127.c)259
-rw-r--r--drivers/media/i2c/saa717x.c (renamed from drivers/media/video/saa717x.c)91
-rw-r--r--drivers/media/i2c/saa7185.c (renamed from drivers/media/video/saa7185.c)38
-rw-r--r--drivers/media/i2c/sony-btf-mpx.c382
-rw-r--r--drivers/media/i2c/st-mipid02.c967
-rw-r--r--drivers/media/i2c/tc358743.c2385
-rw-r--r--drivers/media/i2c/tc358743_regs.h782
-rw-r--r--drivers/media/i2c/tc358746.c1717
-rw-r--r--drivers/media/i2c/tda1997x.c2848
-rw-r--r--drivers/media/i2c/tda1997x_regs.h644
-rw-r--r--drivers/media/i2c/tda7432.c417
-rw-r--r--drivers/media/i2c/tda9840.c199
-rw-r--r--drivers/media/i2c/tea6415c.c158
-rw-r--r--drivers/media/i2c/tea6415c.h (renamed from drivers/media/video/tea6415c.h)1
-rw-r--r--drivers/media/i2c/tea6420.c140
-rw-r--r--drivers/media/i2c/tea6420.h (renamed from drivers/media/video/tea6420.h)1
-rw-r--r--drivers/media/i2c/thp7312.c2249
-rw-r--r--drivers/media/i2c/ths7303.c388
-rw-r--r--drivers/media/i2c/ths8200.c513
-rw-r--r--drivers/media/i2c/ths8200_regs.h161
-rw-r--r--drivers/media/i2c/tlv320aic23b.c (renamed from drivers/media/video/tlv320aic23b.c)39
-rw-r--r--drivers/media/i2c/tvaudio.c (renamed from drivers/media/video/tvaudio.c)682
-rw-r--r--drivers/media/i2c/tvp514x.c (renamed from drivers/media/video/tvp514x.c)514
-rw-r--r--drivers/media/i2c/tvp514x_regs.h (renamed from drivers/media/video/tvp514x_regs.h)23
-rw-r--r--drivers/media/i2c/tvp5150.c2292
-rw-r--r--drivers/media/i2c/tvp5150_reg.h (renamed from drivers/media/video/tvp5150_reg.h)21
-rw-r--r--drivers/media/i2c/tvp7002.c1097
-rw-r--r--drivers/media/i2c/tvp7002_reg.h (renamed from drivers/media/video/tvp7002_reg.h)23
-rw-r--r--drivers/media/i2c/tw2804.c435
-rw-r--r--drivers/media/i2c/tw9900.c781
-rw-r--r--drivers/media/i2c/tw9903.c261
-rw-r--r--drivers/media/i2c/tw9906.c229
-rw-r--r--drivers/media/i2c/tw9910.c1017
-rw-r--r--drivers/media/i2c/uda1342.c99
-rw-r--r--drivers/media/i2c/upd64031a.c (renamed from drivers/media/video/upd64031a.c)56
-rw-r--r--drivers/media/i2c/upd64083.c207
-rw-r--r--drivers/media/i2c/vd55g1.c2061
-rw-r--r--drivers/media/i2c/vd56g3.c1582
-rw-r--r--drivers/media/i2c/vgxy61.c1911
-rw-r--r--drivers/media/i2c/video-i2c.c952
-rw-r--r--drivers/media/i2c/vp27smpx.c189
-rw-r--r--drivers/media/i2c/vpx3220.c (renamed from drivers/media/video/vpx3220.c)73
-rw-r--r--drivers/media/i2c/wm8739.c (renamed from drivers/media/video/wm8739.c)48
-rw-r--r--drivers/media/i2c/wm8775.c (renamed from drivers/media/video/wm8775.c)56
-rw-r--r--drivers/media/mc/Kconfig13
-rw-r--r--drivers/media/mc/Makefile10
-rw-r--r--drivers/media/mc/mc-dev-allocator.c135
-rw-r--r--drivers/media/mc/mc-device.c885
-rw-r--r--drivers/media/mc/mc-devnode.c321
-rw-r--r--drivers/media/mc/mc-entity.c1675
-rw-r--r--drivers/media/mc/mc-request.c491
-rw-r--r--drivers/media/media-device.c382
-rw-r--r--drivers/media/media-devnode.c319
-rw-r--r--drivers/media/media-entity.c593
-rw-r--r--drivers/media/mmc/Kconfig3
-rw-r--r--drivers/media/mmc/Makefile2
-rw-r--r--drivers/media/mmc/siano/Kconfig16
-rw-r--r--drivers/media/mmc/siano/Makefile4
-rw-r--r--drivers/media/mmc/siano/smssdio.c (renamed from drivers/media/dvb/siano/smssdio.c)80
-rw-r--r--drivers/media/pci/Kconfig79
-rw-r--r--drivers/media/pci/Makefile40
-rw-r--r--drivers/media/pci/b2c2/Kconfig16
-rw-r--r--drivers/media/pci/b2c2/Makefile9
-rw-r--r--drivers/media/pci/b2c2/flexcop-dma.c158
-rw-r--r--drivers/media/pci/b2c2/flexcop-pci.c (renamed from drivers/media/dvb/b2c2/flexcop-pci.c)40
-rw-r--r--drivers/media/pci/bt8xx/Kconfig46
-rw-r--r--drivers/media/pci/bt8xx/Makefile10
-rw-r--r--drivers/media/pci/bt8xx/bt848.h (renamed from drivers/media/video/bt8xx/bt848.h)22
-rw-r--r--drivers/media/pci/bt8xx/bt878.c (renamed from drivers/media/dvb/bt8xx/bt878.c)92
-rw-r--r--drivers/media/pci/bt8xx/bt878.h (renamed from drivers/media/dvb/bt8xx/bt878.h)28
-rw-r--r--drivers/media/pci/bt8xx/btcx-risc.c77
-rw-r--r--drivers/media/pci/bt8xx/btcx-risc.h18
-rw-r--r--drivers/media/pci/bt8xx/bttv-audio-hook.c481
-rw-r--r--drivers/media/pci/bt8xx/bttv-audio-hook.h (renamed from drivers/media/video/bt8xx/bttv-audio-hook.h)6
-rw-r--r--drivers/media/pci/bt8xx/bttv-cards.c (renamed from drivers/media/video/bt8xx/bttv-cards.c)919
-rw-r--r--drivers/media/pci/bt8xx/bttv-driver.c3625
-rw-r--r--drivers/media/pci/bt8xx/bttv-gpio.c (renamed from drivers/media/video/bt8xx/bttv-gpio.c)31
-rw-r--r--drivers/media/pci/bt8xx/bttv-i2c.c (renamed from drivers/media/video/bt8xx/bttv-i2c.c)47
-rw-r--r--drivers/media/pci/bt8xx/bttv-if.c103
-rw-r--r--drivers/media/pci/bt8xx/bttv-input.c579
-rw-r--r--drivers/media/pci/bt8xx/bttv-risc.c804
-rw-r--r--drivers/media/pci/bt8xx/bttv-vbi.c377
-rw-r--r--drivers/media/pci/bt8xx/bttv.h (renamed from drivers/media/video/bt8xx/bttv.h)27
-rw-r--r--drivers/media/pci/bt8xx/bttvp.h (renamed from drivers/media/video/bt8xx/bttvp.h)195
-rw-r--r--drivers/media/pci/bt8xx/dst.c (renamed from drivers/media/dvb/bt8xx/dst.c)398
-rw-r--r--drivers/media/pci/bt8xx/dst_ca.c (renamed from drivers/media/dvb/bt8xx/dst_ca.c)252
-rw-r--r--drivers/media/pci/bt8xx/dst_ca.h46
-rw-r--r--drivers/media/pci/bt8xx/dst_common.h (renamed from drivers/media/dvb/bt8xx/dst_common.h)28
-rw-r--r--drivers/media/pci/bt8xx/dst_priv.h (renamed from drivers/media/dvb/bt8xx/dst_priv.h)1
-rw-r--r--drivers/media/pci/bt8xx/dvb-bt8xx.c (renamed from drivers/media/dvb/bt8xx/dvb-bt8xx.c)105
-rw-r--r--drivers/media/pci/bt8xx/dvb-bt8xx.h49
-rw-r--r--drivers/media/pci/cobalt/Kconfig25
-rw-r--r--drivers/media/pci/cobalt/Makefile6
-rw-r--r--drivers/media/pci/cobalt/cobalt-alsa-main.c150
-rw-r--r--drivers/media/pci/cobalt/cobalt-alsa-pcm.c530
-rw-r--r--drivers/media/pci/cobalt/cobalt-alsa-pcm.h10
-rw-r--r--drivers/media/pci/cobalt/cobalt-alsa.h29
-rw-r--r--drivers/media/pci/cobalt/cobalt-cpld.c324
-rw-r--r--drivers/media/pci/cobalt/cobalt-cpld.h17
-rw-r--r--drivers/media/pci/cobalt/cobalt-driver.c798
-rw-r--r--drivers/media/pci/cobalt/cobalt-driver.h373
-rw-r--r--drivers/media/pci/cobalt/cobalt-flash.c116
-rw-r--r--drivers/media/pci/cobalt/cobalt-flash.h17
-rw-r--r--drivers/media/pci/cobalt/cobalt-i2c.c384
-rw-r--r--drivers/media/pci/cobalt/cobalt-i2c.h13
-rw-r--r--drivers/media/pci/cobalt/cobalt-irq.c247
-rw-r--r--drivers/media/pci/cobalt/cobalt-irq.h13
-rw-r--r--drivers/media/pci/cobalt/cobalt-omnitek.c329
-rw-r--r--drivers/media/pci/cobalt/cobalt-omnitek.h50
-rw-r--r--drivers/media/pci/cobalt/cobalt-v4l2.c1317
-rw-r--r--drivers/media/pci/cobalt/cobalt-v4l2.h10
-rw-r--r--drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h103
-rw-r--r--drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h32
-rw-r--r--drivers/media/pci/cobalt/m00389_cvi_memmap_package.h47
-rw-r--r--drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h32
-rw-r--r--drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h45
-rw-r--r--drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h41
-rw-r--r--drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h76
-rw-r--r--drivers/media/pci/cx18/Kconfig36
-rw-r--r--drivers/media/pci/cx18/Makefile13
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-main.c (renamed from drivers/media/video/cx18/cx18-alsa-main.c)47
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-pcm.c272
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-pcm.h13
-rw-r--r--drivers/media/pci/cx18/cx18-alsa.h60
-rw-r--r--drivers/media/pci/cx18/cx18-audio.c78
-rw-r--r--drivers/media/pci/cx18/cx18-audio.h10
-rw-r--r--drivers/media/pci/cx18/cx18-av-audio.c (renamed from drivers/media/video/cx18/cx18-av-audio.c)24
-rw-r--r--drivers/media/pci/cx18/cx18-av-core.c (renamed from drivers/media/video/cx18/cx18-av-core.c)139
-rw-r--r--drivers/media/pci/cx18/cx18-av-core.h (renamed from drivers/media/video/cx18/cx18-av-core.h)21
-rw-r--r--drivers/media/pci/cx18/cx18-av-firmware.c (renamed from drivers/media/video/cx18/cx18-av-firmware.c)23
-rw-r--r--drivers/media/pci/cx18/cx18-av-vbi.c (renamed from drivers/media/video/cx18/cx18-av-vbi.c)36
-rw-r--r--drivers/media/pci/cx18/cx18-cards.c (renamed from drivers/media/video/cx18/cx18-cards.c)46
-rw-r--r--drivers/media/pci/cx18/cx18-cards.h139
-rw-r--r--drivers/media/pci/cx18/cx18-controls.c117
-rw-r--r--drivers/media/pci/cx18/cx18-controls.h11
-rw-r--r--drivers/media/pci/cx18/cx18-driver.c (renamed from drivers/media/video/cx18/cx18-driver.c)149
-rw-r--r--drivers/media/pci/cx18/cx18-driver.h (renamed from drivers/media/video/cx18/cx18-driver.h)143
-rw-r--r--drivers/media/pci/cx18/cx18-dvb.c (renamed from drivers/media/video/cx18/cx18-dvb.c)44
-rw-r--r--drivers/media/pci/cx18/cx18-dvb.h11
-rw-r--r--drivers/media/pci/cx18/cx18-fileops.c (renamed from drivers/media/video/cx18/cx18-fileops.c)151
-rw-r--r--drivers/media/pci/cx18/cx18-fileops.h28
-rw-r--r--drivers/media/pci/cx18/cx18-firmware.c (renamed from drivers/media/video/cx18/cx18-firmware.c)134
-rw-r--r--drivers/media/pci/cx18/cx18-firmware.h11
-rw-r--r--drivers/media/pci/cx18/cx18-gpio.c (renamed from drivers/media/video/cx18/cx18-gpio.c)41
-rw-r--r--drivers/media/pci/cx18/cx18-gpio.h20
-rw-r--r--drivers/media/pci/cx18/cx18-i2c.c (renamed from drivers/media/video/cx18/cx18-i2c.c)51
-rw-r--r--drivers/media/pci/cx18/cx18-i2c.h15
-rw-r--r--drivers/media/pci/cx18/cx18-io.c83
-rw-r--r--drivers/media/pci/cx18/cx18-io.h (renamed from drivers/media/video/cx18/cx18-io.h)20
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.c1057
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.h19
-rw-r--r--drivers/media/pci/cx18/cx18-irq.c67
-rw-r--r--drivers/media/pci/cx18/cx18-irq.h21
-rw-r--r--drivers/media/pci/cx18/cx18-mailbox.c (renamed from drivers/media/video/cx18/cx18-mailbox.c)96
-rw-r--r--drivers/media/pci/cx18/cx18-mailbox.h (renamed from drivers/media/video/cx18/cx18-mailbox.h)18
-rw-r--r--drivers/media/pci/cx18/cx18-queue.c (renamed from drivers/media/video/cx18/cx18-queue.c)48
-rw-r--r--drivers/media/pci/cx18/cx18-queue.h84
-rw-r--r--drivers/media/pci/cx18/cx18-scb.c (renamed from drivers/media/video/cx18/cx18-scb.c)18
-rw-r--r--drivers/media/pci/cx18/cx18-scb.h (renamed from drivers/media/video/cx18/cx18-scb.h)20
-rw-r--r--drivers/media/pci/cx18/cx18-streams.c1051
-rw-r--r--drivers/media/pci/cx18/cx18-streams.h48
-rw-r--r--drivers/media/pci/cx18/cx18-vbi.c (renamed from drivers/media/video/cx18/cx18-vbi.c)30
-rw-r--r--drivers/media/pci/cx18/cx18-vbi.h12
-rw-r--r--drivers/media/pci/cx18/cx18-version.h14
-rw-r--r--drivers/media/pci/cx18/cx18-video.c18
-rw-r--r--drivers/media/pci/cx18/cx18-video.h8
-rw-r--r--drivers/media/pci/cx18/cx23418.h (renamed from drivers/media/video/cx18/cx23418.h)110
-rw-r--r--drivers/media/pci/cx23885/Kconfig62
-rw-r--r--drivers/media/pci/cx23885/Makefile14
-rw-r--r--drivers/media/pci/cx23885/altera-ci.c (renamed from drivers/media/video/cx23885/altera-ci.c)103
-rw-r--r--drivers/media/pci/cx23885/altera-ci.h85
-rw-r--r--drivers/media/pci/cx23885/cimax2.c (renamed from drivers/media/video/cx23885/cimax2.c)63
-rw-r--r--drivers/media/pci/cx23885/cimax2.h33
-rw-r--r--drivers/media/pci/cx23885/cx23885-417.c1564
-rw-r--r--drivers/media/pci/cx23885/cx23885-alsa.c (renamed from drivers/media/video/cx23885/cx23885-alsa.c)173
-rw-r--r--drivers/media/pci/cx23885/cx23885-av.c35
-rw-r--r--drivers/media/pci/cx23885/cx23885-av.h13
-rw-r--r--drivers/media/pci/cx23885/cx23885-cards.c2523
-rw-r--r--drivers/media/pci/cx23885/cx23885-core.c (renamed from drivers/media/video/cx23885/cx23885-core.c)814
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c2739
-rw-r--r--drivers/media/pci/cx23885/cx23885-f300.c (renamed from drivers/media/video/cx23885/cx23885-f300.c)21
-rw-r--r--drivers/media/pci/cx23885/cx23885-f300.h3
-rw-r--r--drivers/media/pci/cx23885/cx23885-i2c.c (renamed from drivers/media/video/cx23885/cx23885-i2c.c)82
-rw-r--r--drivers/media/pci/cx23885/cx23885-input.c (renamed from drivers/media/video/cx23885/cx23885-input.c)121
-rw-r--r--drivers/media/pci/cx23885/cx23885-input.h16
-rw-r--r--drivers/media/pci/cx23885/cx23885-ioctl.c98
-rw-r--r--drivers/media/pci/cx23885/cx23885-ioctl.h25
-rw-r--r--drivers/media/pci/cx23885/cx23885-ir.c (renamed from drivers/media/video/cx23885/cx23885-ir.c)21
-rw-r--r--drivers/media/pci/cx23885/cx23885-ir.h17
-rw-r--r--drivers/media/pci/cx23885/cx23885-reg.h (renamed from drivers/media/video/cx23885/cx23885-reg.h)30
-rw-r--r--drivers/media/pci/cx23885/cx23885-vbi.c254
-rw-r--r--drivers/media/pci/cx23885/cx23885-video.c1424
-rw-r--r--drivers/media/pci/cx23885/cx23885-video.h12
-rw-r--r--drivers/media/pci/cx23885/cx23885.h (renamed from drivers/media/video/cx23885/cx23885.h)200
-rw-r--r--drivers/media/pci/cx23885/cx23888-ir.c (renamed from drivers/media/video/cx23885/cx23888-ir.c)114
-rw-r--r--drivers/media/pci/cx23885/cx23888-ir.h14
-rw-r--r--drivers/media/pci/cx23885/netup-eeprom.c64
-rw-r--r--drivers/media/pci/cx23885/netup-eeprom.h27
-rw-r--r--drivers/media/pci/cx23885/netup-init.c112
-rw-r--r--drivers/media/pci/cx23885/netup-init.h11
-rw-r--r--drivers/media/pci/cx25821/Kconfig29
-rw-r--r--drivers/media/pci/cx25821/Makefile7
-rw-r--r--drivers/media/pci/cx25821/cx25821-alsa.c (renamed from drivers/media/video/cx25821/cx25821-alsa.c)243
-rw-r--r--drivers/media/pci/cx25821/cx25821-audio.h48
-rw-r--r--drivers/media/pci/cx25821/cx25821-biffuncs.h31
-rw-r--r--drivers/media/pci/cx25821/cx25821-cards.c33
-rw-r--r--drivers/media/pci/cx25821/cx25821-core.c (renamed from drivers/media/video/cx25821/cx25821-core.c)310
-rw-r--r--drivers/media/pci/cx25821/cx25821-gpio.c85
-rw-r--r--drivers/media/pci/cx25821/cx25821-i2c.c (renamed from drivers/media/video/cx25821/cx25821-i2c.c)41
-rw-r--r--drivers/media/pci/cx25821/cx25821-medusa-defines.h28
-rw-r--r--drivers/media/pci/cx25821/cx25821-medusa-reg.h (renamed from drivers/media/video/cx25821/cx25821-medusa-reg.h)22
-rw-r--r--drivers/media/pci/cx25821/cx25821-medusa-video.c (renamed from drivers/media/video/cx25821/cx25821-medusa-video.c)82
-rw-r--r--drivers/media/pci/cx25821/cx25821-medusa-video.h29
-rw-r--r--drivers/media/pci/cx25821/cx25821-reg.h (renamed from drivers/media/video/cx25821/cx25821-reg.h)16
-rw-r--r--drivers/media/pci/cx25821/cx25821-sram.h (renamed from drivers/media/video/cx25821/cx25821-sram.h)18
-rw-r--r--drivers/media/pci/cx25821/cx25821-video.c774
-rw-r--r--drivers/media/pci/cx25821/cx25821-video.h48
-rw-r--r--drivers/media/pci/cx25821/cx25821.h426
-rw-r--r--drivers/media/pci/cx88/Kconfig93
-rw-r--r--drivers/media/pci/cx88/Makefile14
-rw-r--r--drivers/media/pci/cx88/cx88-alsa.c1007
-rw-r--r--drivers/media/pci/cx88/cx88-blackbird.c1251
-rw-r--r--drivers/media/pci/cx88/cx88-cards.c (renamed from drivers/media/video/cx88/cx88-cards.c)1289
-rw-r--r--drivers/media/pci/cx88/cx88-core.c1099
-rw-r--r--drivers/media/pci/cx88/cx88-dsp.c323
-rw-r--r--drivers/media/pci/cx88/cx88-dvb.c (renamed from drivers/media/video/cx88/cx88-dvb.c)576
-rw-r--r--drivers/media/pci/cx88/cx88-i2c.c173
-rw-r--r--drivers/media/pci/cx88/cx88-input.c (renamed from drivers/media/video/cx88/cx88-input.c)153
-rw-r--r--drivers/media/pci/cx88/cx88-mpeg.c807
-rw-r--r--drivers/media/pci/cx88/cx88-reg.h (renamed from drivers/media/video/cx88/cx88-reg.h)114
-rw-r--r--drivers/media/pci/cx88/cx88-tvaudio.c (renamed from drivers/media/video/cx88/cx88-tvaudio.c)171
-rw-r--r--drivers/media/pci/cx88/cx88-vbi.c233
-rw-r--r--drivers/media/pci/cx88/cx88-video.c1633
-rw-r--r--drivers/media/pci/cx88/cx88-vp3054-i2c.c141
-rw-r--r--drivers/media/pci/cx88/cx88-vp3054-i2c.h26
-rw-r--r--drivers/media/pci/cx88/cx88.h731
-rw-r--r--drivers/media/pci/ddbridge/Kconfig46
-rw-r--r--drivers/media/pci/ddbridge/Makefile13
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-ci.c377
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-ci.h20
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-core.c3438
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-dummy-fe.c153
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-dummy-fe.h16
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-hw.c379
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-hw.h34
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-i2c.c225
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-i2c.h103
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-io.h62
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-main.c313
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-max.c495
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-max.h21
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-mci.c169
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-mci.h253
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-regs.h140
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-sx8.c477
-rw-r--r--drivers/media/pci/ddbridge/ddbridge.h373
-rw-r--r--drivers/media/pci/dm1105/Kconfig22
-rw-r--r--drivers/media/pci/dm1105/Makefile4
-rw-r--r--drivers/media/pci/dm1105/dm1105.c (renamed from drivers/media/dvb/dm1105/dm1105.c)128
-rw-r--r--drivers/media/pci/dt3155/Kconfig12
-rw-r--r--drivers/media/pci/dt3155/Makefile2
-rw-r--r--drivers/media/pci/dt3155/dt3155.c597
-rw-r--r--drivers/media/pci/dt3155/dt3155.h186
-rw-r--r--drivers/media/pci/intel/Kconfig21
-rw-r--r--drivers/media/pci/intel/Makefile8
-rw-r--r--drivers/media/pci/intel/ipu-bridge.c880
-rw-r--r--drivers/media/pci/intel/ipu3/Kconfig20
-rw-r--r--drivers/media/pci/intel/ipu3/Makefile2
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.c1989
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.h456
-rw-r--r--drivers/media/pci/intel/ipu6/Kconfig18
-rw-r--r--drivers/media/pci/intel/ipu6/Makefile23
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-bus.c159
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-bus.h55
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-buttress.c910
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-buttress.h85
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-cpd.c362
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-cpd.h105
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-dma.c459
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-dma.h43
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-fw-com.c413
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-fw-com.h47
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-fw-isys.c487
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-fw-isys.h596
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c647
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h78
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c536
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c242
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c720
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-queue.c850
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-queue.h71
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c425
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h55
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-video.c1404
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-video.h135
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys.c1380
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys.h202
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-mmu.c806
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-mmu.h73
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h224
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h172
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-platform-regs.h179
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6.c846
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6.h342
-rw-r--r--drivers/media/pci/intel/ivsc/Kconfig22
-rw-r--r--drivers/media/pci/intel/ivsc/Makefile9
-rw-r--r--drivers/media/pci/intel/ivsc/mei_ace.c582
-rw-r--r--drivers/media/pci/intel/ivsc/mei_csi.c791
-rw-r--r--drivers/media/pci/ivtv/Kconfig78
-rw-r--r--drivers/media/pci/ivtv/Makefile15
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-main.c274
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-pcm.c274
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-pcm.h9
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa.h61
-rw-r--r--drivers/media/pci/ivtv/ivtv-cards.c (renamed from drivers/media/video/ivtv/ivtv-cards.c)42
-rw-r--r--drivers/media/pci/ivtv/ivtv-cards.h314
-rw-r--r--drivers/media/pci/ivtv/ivtv-controls.c (renamed from drivers/media/video/ivtv/ivtv-controls.c)34
-rw-r--r--drivers/media/pci/ivtv/ivtv-controls.h16
-rw-r--r--drivers/media/pci/ivtv/ivtv-driver.c (renamed from drivers/media/video/ivtv/ivtv-driver.c)269
-rw-r--r--drivers/media/pci/ivtv/ivtv-driver.h (renamed from drivers/media/video/ivtv/ivtv-driver.h)166
-rw-r--r--drivers/media/pci/ivtv/ivtv-fileops.c (renamed from drivers/media/video/ivtv/ivtv-fileops.c)213
-rw-r--r--drivers/media/pci/ivtv/ivtv-fileops.h32
-rw-r--r--drivers/media/pci/ivtv/ivtv-firmware.c (renamed from drivers/media/video/ivtv/ivtv-firmware.c)74
-rw-r--r--drivers/media/pci/ivtv/ivtv-firmware.h19
-rw-r--r--drivers/media/pci/ivtv/ivtv-gpio.c (renamed from drivers/media/video/ivtv/ivtv-gpio.c)27
-rw-r--r--drivers/media/pci/ivtv/ivtv-gpio.h17
-rw-r--r--drivers/media/pci/ivtv/ivtv-i2c.c (renamed from drivers/media/video/ivtv/ivtv-i2c.c)133
-rw-r--r--drivers/media/pci/ivtv/ivtv-i2c.h20
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.c1755
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.h25
-rw-r--r--drivers/media/pci/ivtv/ivtv-irq.c (renamed from drivers/media/video/ivtv/ivtv-irq.c)126
-rw-r--r--drivers/media/pci/ivtv/ivtv-irq.h41
-rw-r--r--drivers/media/pci/ivtv/ivtv-mailbox.c373
-rw-r--r--drivers/media/pci/ivtv/ivtv-mailbox.h23
-rw-r--r--drivers/media/pci/ivtv/ivtv-queue.c (renamed from drivers/media/video/ivtv/ivtv-queue.c)36
-rw-r--r--drivers/media/pci/ivtv/ivtv-queue.h87
-rw-r--r--drivers/media/pci/ivtv/ivtv-routing.c (renamed from drivers/media/video/ivtv/ivtv-routing.c)24
-rw-r--r--drivers/media/pci/ivtv/ivtv-routing.h15
-rw-r--r--drivers/media/pci/ivtv/ivtv-streams.c (renamed from drivers/media/video/ivtv/ivtv-streams.c)196
-rw-r--r--drivers/media/pci/ivtv/ivtv-streams.h25
-rw-r--r--drivers/media/pci/ivtv/ivtv-udma.c226
-rw-r--r--drivers/media/pci/ivtv/ivtv-udma.h36
-rw-r--r--drivers/media/pci/ivtv/ivtv-vbi.c (renamed from drivers/media/video/ivtv/ivtv-vbi.c)22
-rw-r--r--drivers/media/pci/ivtv/ivtv-vbi.h22
-rw-r--r--drivers/media/pci/ivtv/ivtv-version.h14
-rw-r--r--drivers/media/pci/ivtv/ivtv-yuv.c (renamed from drivers/media/video/ivtv/ivtv-yuv.c)75
-rw-r--r--drivers/media/pci/ivtv/ivtv-yuv.h32
-rw-r--r--drivers/media/pci/ivtv/ivtvfb.c (renamed from drivers/media/video/ivtv/ivtvfb.c)175
-rw-r--r--drivers/media/pci/mantis/Kconfig39
-rw-r--r--drivers/media/pci/mantis/Makefile29
-rw-r--r--drivers/media/pci/mantis/hopper_cards.c (renamed from drivers/media/dvb/mantis/hopper_cards.c)71
-rw-r--r--drivers/media/pci/mantis/hopper_vp3028.c76
-rw-r--r--drivers/media/pci/mantis/hopper_vp3028.h18
-rw-r--r--drivers/media/pci/mantis/mantis_ca.c (renamed from drivers/media/dvb/mantis/mantis_ca.c)30
-rw-r--r--drivers/media/pci/mantis/mantis_ca.h15
-rw-r--r--drivers/media/pci/mantis/mantis_cards.c306
-rw-r--r--drivers/media/pci/mantis/mantis_common.h (renamed from drivers/media/dvb/mantis/mantis_common.h)53
-rw-r--r--drivers/media/pci/mantis/mantis_dma.c (renamed from drivers/media/dvb/mantis/mantis_dma.c)62
-rw-r--r--drivers/media/pci/mantis/mantis_dma.h18
-rw-r--r--drivers/media/pci/mantis/mantis_dvb.c (renamed from drivers/media/dvb/mantis/mantis_dvb.c)53
-rw-r--r--drivers/media/pci/mantis/mantis_dvb.h23
-rw-r--r--drivers/media/pci/mantis/mantis_evm.c (renamed from drivers/media/dvb/mantis/mantis_evm.c)26
-rw-r--r--drivers/media/pci/mantis/mantis_hif.c (renamed from drivers/media/dvb/mantis/mantis_hif.c)24
-rw-r--r--drivers/media/pci/mantis/mantis_hif.h17
-rw-r--r--drivers/media/pci/mantis/mantis_i2c.c (renamed from drivers/media/dvb/mantis/mantis_i2c.c)46
-rw-r--r--drivers/media/pci/mantis/mantis_i2c.h18
-rw-r--r--drivers/media/pci/mantis/mantis_input.c76
-rw-r--r--drivers/media/pci/mantis/mantis_input.h16
-rw-r--r--drivers/media/pci/mantis/mantis_ioc.c (renamed from drivers/media/dvb/mantis/mantis_ioc.c)24
-rw-r--r--drivers/media/pci/mantis/mantis_ioc.h39
-rw-r--r--drivers/media/pci/mantis/mantis_link.h71
-rw-r--r--drivers/media/pci/mantis/mantis_pci.c (renamed from drivers/media/dvb/mantis/mantis_pci.c)30
-rw-r--r--drivers/media/pci/mantis/mantis_pci.h15
-rw-r--r--drivers/media/pci/mantis/mantis_pcmcia.c109
-rw-r--r--drivers/media/pci/mantis/mantis_reg.h185
-rw-r--r--drivers/media/pci/mantis/mantis_uart.c186
-rw-r--r--drivers/media/pci/mantis/mantis_uart.h46
-rw-r--r--drivers/media/pci/mantis/mantis_vp1033.c (renamed from drivers/media/dvb/mantis/mantis_vp1033.c)34
-rw-r--r--drivers/media/pci/mantis/mantis_vp1033.h18
-rw-r--r--drivers/media/pci/mantis/mantis_vp1034.c108
-rw-r--r--drivers/media/pci/mantis/mantis_vp1034.h22
-rw-r--r--drivers/media/pci/mantis/mantis_vp1041.c345
-rw-r--r--drivers/media/pci/mantis/mantis_vp1041.h21
-rw-r--r--drivers/media/pci/mantis/mantis_vp2033.c (renamed from drivers/media/dvb/mantis/mantis_vp2033.c)28
-rw-r--r--drivers/media/pci/mantis/mantis_vp2033.h18
-rw-r--r--drivers/media/pci/mantis/mantis_vp2040.c (renamed from drivers/media/dvb/mantis/mantis_vp2040.c)28
-rw-r--r--drivers/media/pci/mantis/mantis_vp2040.h20
-rw-r--r--drivers/media/pci/mantis/mantis_vp3030.c93
-rw-r--r--drivers/media/pci/mantis/mantis_vp3030.h18
-rw-r--r--drivers/media/pci/mgb4/Kconfig18
-rw-r--r--drivers/media/pci/mgb4/Makefile6
-rw-r--r--drivers/media/pci/mgb4/mgb4_cmt.c248
-rw-r--r--drivers/media/pci/mgb4/mgb4_cmt.h17
-rw-r--r--drivers/media/pci/mgb4/mgb4_core.c700
-rw-r--r--drivers/media/pci/mgb4/mgb4_core.h75
-rw-r--r--drivers/media/pci/mgb4/mgb4_dma.c123
-rw-r--r--drivers/media/pci/mgb4/mgb4_dma.h18
-rw-r--r--drivers/media/pci/mgb4/mgb4_i2c.c140
-rw-r--r--drivers/media/pci/mgb4/mgb4_i2c.h35
-rw-r--r--drivers/media/pci/mgb4/mgb4_io.h54
-rw-r--r--drivers/media/pci/mgb4/mgb4_regs.c31
-rw-r--r--drivers/media/pci/mgb4/mgb4_regs.h35
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs.h18
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs_in.c772
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs_out.c741
-rw-r--r--drivers/media/pci/mgb4/mgb4_sysfs_pci.c71
-rw-r--r--drivers/media/pci/mgb4/mgb4_trigger.c209
-rw-r--r--drivers/media/pci/mgb4/mgb4_trigger.h8
-rw-r--r--drivers/media/pci/mgb4/mgb4_vin.c1037
-rw-r--r--drivers/media/pci/mgb4/mgb4_vin.h69
-rw-r--r--drivers/media/pci/mgb4/mgb4_vout.c847
-rw-r--r--drivers/media/pci/mgb4/mgb4_vout.h65
-rw-r--r--drivers/media/pci/netup_unidvb/Kconfig18
-rw-r--r--drivers/media/pci/netup_unidvb/Makefile9
-rw-r--r--drivers/media/pci/netup_unidvb/netup_unidvb.h134
-rw-r--r--drivers/media/pci/netup_unidvb/netup_unidvb_ci.c239
-rw-r--r--drivers/media/pci/netup_unidvb/netup_unidvb_core.c1028
-rw-r--r--drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c364
-rw-r--r--drivers/media/pci/netup_unidvb/netup_unidvb_spi.c240
-rw-r--r--drivers/media/pci/ngene/Kconfig21
-rw-r--r--drivers/media/pci/ngene/Makefile11
-rw-r--r--drivers/media/pci/ngene/ngene-cards.c1239
-rw-r--r--drivers/media/pci/ngene/ngene-core.c (renamed from drivers/media/dvb/ngene/ngene-core.c)312
-rw-r--r--drivers/media/pci/ngene/ngene-dvb.c340
-rw-r--r--drivers/media/pci/ngene/ngene-i2c.c (renamed from drivers/media/dvb/ngene/ngene-i2c.c)31
-rw-r--r--drivers/media/pci/ngene/ngene.h (renamed from drivers/media/dvb/ngene/ngene.h)170
-rw-r--r--drivers/media/pci/pluto2/Kconfig16
-rw-r--r--drivers/media/pci/pluto2/Makefile4
-rw-r--r--drivers/media/pci/pluto2/pluto2.c (renamed from drivers/media/dvb/pluto2/pluto2.c)98
-rw-r--r--drivers/media/pci/pt1/Kconfig16
-rw-r--r--drivers/media/pci/pt1/Makefile7
-rw-r--r--drivers/media/pci/pt1/pt1.c1482
-rw-r--r--drivers/media/pci/pt3/Kconfig11
-rw-r--r--drivers/media/pci/pt3/Makefile8
-rw-r--r--drivers/media/pci/pt3/pt3.c807
-rw-r--r--drivers/media/pci/pt3/pt3.h177
-rw-r--r--drivers/media/pci/pt3/pt3_dma.c216
-rw-r--r--drivers/media/pci/pt3/pt3_i2c.c230
-rw-r--r--drivers/media/pci/saa7134/Kconfig74
-rw-r--r--drivers/media/pci/saa7134/Makefile17
-rw-r--r--drivers/media/pci/saa7134/saa7134-alsa.c (renamed from drivers/media/video/saa7134/saa7134-alsa.c)218
-rw-r--r--drivers/media/pci/saa7134/saa7134-cards.c (renamed from drivers/media/video/saa7134/saa7134-cards.c)2310
-rw-r--r--drivers/media/pci/saa7134/saa7134-core.c1524
-rw-r--r--drivers/media/pci/saa7134/saa7134-dvb.c (renamed from drivers/media/video/saa7134/saa7134-dvb.c)393
-rw-r--r--drivers/media/pci/saa7134/saa7134-empress.c346
-rw-r--r--drivers/media/pci/saa7134/saa7134-go7007.c520
-rw-r--r--drivers/media/pci/saa7134/saa7134-i2c.c (renamed from drivers/media/video/saa7134/saa7134-i2c.c)153
-rw-r--r--drivers/media/pci/saa7134/saa7134-input.c (renamed from drivers/media/video/saa7134/saa7134-input.c)387
-rw-r--r--drivers/media/pci/saa7134/saa7134-reg.h (renamed from drivers/media/video/saa7134/saa7134-reg.h)13
-rw-r--r--drivers/media/pci/saa7134/saa7134-ts.c326
-rw-r--r--drivers/media/pci/saa7134/saa7134-tvaudio.c (renamed from drivers/media/video/saa7134/saa7134-tvaudio.c)227
-rw-r--r--drivers/media/pci/saa7134/saa7134-vbi.c208
-rw-r--r--drivers/media/pci/saa7134/saa7134-video.c1862
-rw-r--r--drivers/media/pci/saa7134/saa7134.h (renamed from drivers/media/video/saa7134/saa7134.h)265
-rw-r--r--drivers/media/pci/saa7146/Kconfig39
-rw-r--r--drivers/media/pci/saa7146/Makefile6
-rw-r--r--drivers/media/pci/saa7146/hexium_gemini.c (renamed from drivers/media/video/hexium_gemini.c)92
-rw-r--r--drivers/media/pci/saa7146/hexium_orion.c (renamed from drivers/media/video/hexium_orion.c)90
-rw-r--r--drivers/media/pci/saa7146/mxb.c (renamed from drivers/media/video/mxb.c)196
-rw-r--r--drivers/media/pci/saa7164/Kconfig18
-rw-r--r--drivers/media/pci/saa7164/Makefile9
-rw-r--r--drivers/media/pci/saa7164/saa7164-api.c (renamed from drivers/media/video/saa7164/saa7164-api.c)91
-rw-r--r--drivers/media/pci/saa7164/saa7164-buffer.c (renamed from drivers/media/video/saa7164/saa7164-buffer.c)77
-rw-r--r--drivers/media/pci/saa7164/saa7164-bus.c (renamed from drivers/media/video/saa7164/saa7164-bus.c)124
-rw-r--r--drivers/media/pci/saa7164/saa7164-cards.c (renamed from drivers/media/video/saa7164/saa7164-cards.c)214
-rw-r--r--drivers/media/pci/saa7164/saa7164-cmd.c (renamed from drivers/media/video/saa7164/saa7164-cmd.c)85
-rw-r--r--drivers/media/pci/saa7164/saa7164-core.c (renamed from drivers/media/video/saa7164/saa7164-core.c)465
-rw-r--r--drivers/media/pci/saa7164/saa7164-dvb.c740
-rw-r--r--drivers/media/pci/saa7164/saa7164-encoder.c1144
-rw-r--r--drivers/media/pci/saa7164/saa7164-fw.c (renamed from drivers/media/video/saa7164/saa7164-fw.c)50
-rw-r--r--drivers/media/pci/saa7164/saa7164-i2c.c112
-rw-r--r--drivers/media/pci/saa7164/saa7164-reg.h (renamed from drivers/media/video/saa7164/saa7164-reg.h)18
-rw-r--r--drivers/media/pci/saa7164/saa7164-types.h (renamed from drivers/media/video/saa7164/saa7164-types.h)26
-rw-r--r--drivers/media/pci/saa7164/saa7164-vbi.c765
-rw-r--r--drivers/media/pci/saa7164/saa7164.h (renamed from drivers/media/video/saa7164/saa7164.h)92
-rw-r--r--drivers/media/pci/smipcie/Kconfig19
-rw-r--r--drivers/media/pci/smipcie/Makefile8
-rw-r--r--drivers/media/pci/smipcie/smipcie-ir.c185
-rw-r--r--drivers/media/pci/smipcie/smipcie-main.c1124
-rw-r--r--drivers/media/pci/smipcie/smipcie.h310
-rw-r--r--drivers/media/pci/solo6x10/Kconfig20
-rw-r--r--drivers/media/pci/solo6x10/Makefile6
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-core.c671
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-disp.c313
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-eeprom.c141
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-enc.c331
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-g723.c393
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-gpio.c183
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-i2c.c323
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-jpeg.h180
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-offsets.h74
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-p2m.c317
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-regs.h628
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-tw28.c863
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-tw28.h56
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c1392
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-v4l2.c717
-rw-r--r--drivers/media/pci/solo6x10/solo6x10.h380
-rw-r--r--drivers/media/pci/ttpci/Kconfig86
-rw-r--r--drivers/media/pci/ttpci/Makefile13
-rw-r--r--drivers/media/pci/ttpci/budget-av.c1625
-rw-r--r--drivers/media/pci/ttpci/budget-ci.c1574
-rw-r--r--drivers/media/pci/ttpci/budget-core.c (renamed from drivers/media/dvb/ttpci/budget-core.c)194
-rw-r--r--drivers/media/pci/ttpci/budget.c896
-rw-r--r--drivers/media/pci/ttpci/budget.h (renamed from drivers/media/dvb/ttpci/budget.h)47
-rw-r--r--drivers/media/pci/tw5864/Kconfig12
-rw-r--r--drivers/media/pci/tw5864/Makefile4
-rw-r--r--drivers/media/pci/tw5864/tw5864-core.c336
-rw-r--r--drivers/media/pci/tw5864/tw5864-h264.c250
-rw-r--r--drivers/media/pci/tw5864/tw5864-reg.h2132
-rw-r--r--drivers/media/pci/tw5864/tw5864-util.c38
-rw-r--r--drivers/media/pci/tw5864/tw5864-video.c1513
-rw-r--r--drivers/media/pci/tw5864/tw5864.h197
-rw-r--r--drivers/media/pci/tw68/Kconfig10
-rw-r--r--drivers/media/pci/tw68/Makefile4
-rw-r--r--drivers/media/pci/tw68/tw68-core.c418
-rw-r--r--drivers/media/pci/tw68/tw68-reg.h186
-rw-r--r--drivers/media/pci/tw68/tw68-risc.c223
-rw-r--r--drivers/media/pci/tw68/tw68-video.c1017
-rw-r--r--drivers/media/pci/tw68/tw68.h205
-rw-r--r--drivers/media/pci/tw686x/Kconfig20
-rw-r--r--drivers/media/pci/tw686x/Makefile4
-rw-r--r--drivers/media/pci/tw686x/tw686x-audio.c413
-rw-r--r--drivers/media/pci/tw686x/tw686x-core.c452
-rw-r--r--drivers/media/pci/tw686x/tw686x-regs.h132
-rw-r--r--drivers/media/pci/tw686x/tw686x-video.c1307
-rw-r--r--drivers/media/pci/tw686x/tw686x.h179
-rw-r--r--drivers/media/pci/zoran/Kconfig74
-rw-r--r--drivers/media/pci/zoran/Makefile7
-rw-r--r--drivers/media/pci/zoran/videocodec.c278
-rw-r--r--drivers/media/pci/zoran/videocodec.h325
-rw-r--r--drivers/media/pci/zoran/zoran.h322
-rw-r--r--drivers/media/pci/zoran/zoran_card.c1436
-rw-r--r--drivers/media/pci/zoran/zoran_card.h27
-rw-r--r--drivers/media/pci/zoran/zoran_device.c956
-rw-r--r--drivers/media/pci/zoran/zoran_device.h56
-rw-r--r--drivers/media/pci/zoran/zoran_driver.c983
-rw-r--r--drivers/media/pci/zoran/zr36016.c406
-rw-r--r--drivers/media/pci/zoran/zr36016.h94
-rw-r--r--drivers/media/pci/zoran/zr36050.c817
-rw-r--r--drivers/media/pci/zoran/zr36050.h (renamed from drivers/media/video/zoran/zr36050.h)33
-rw-r--r--drivers/media/pci/zoran/zr36057.h154
-rw-r--r--drivers/media/pci/zoran/zr36060.c870
-rw-r--r--drivers/media/pci/zoran/zr36060.h203
-rw-r--r--drivers/media/platform/Kconfig95
-rw-r--r--drivers/media/platform/Makefile42
-rw-r--r--drivers/media/platform/allegro-dvt/Kconfig19
-rw-r--r--drivers/media/platform/allegro-dvt/Makefile6
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-core.c4086
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-mail.c549
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-mail.h297
-rw-r--r--drivers/media/platform/allegro-dvt/nal-h264.c605
-rw-r--r--drivers/media/platform/allegro-dvt/nal-h264.h404
-rw-r--r--drivers/media/platform/allegro-dvt/nal-hevc.c884
-rw-r--r--drivers/media/platform/allegro-dvt/nal-hevc.h520
-rw-r--r--drivers/media/platform/allegro-dvt/nal-rbsp.c310
-rw-r--r--drivers/media/platform/allegro-dvt/nal-rbsp.h61
-rw-r--r--drivers/media/platform/amlogic/Kconfig6
-rw-r--r--drivers/media/platform/amlogic/Makefile4
-rw-r--r--drivers/media/platform/amlogic/c3/Kconfig5
-rw-r--r--drivers/media/platform/amlogic/c3/Makefile5
-rw-r--r--drivers/media/platform/amlogic/c3/isp/Kconfig19
-rw-r--r--drivers/media/platform/amlogic/c3/isp/Makefile10
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c804
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-common.h340
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-core.c641
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c421
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-params.c930
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-regs.h618
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c892
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-stats.c326
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig16
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-adapter/Makefile3
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c842
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig16
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-csi2/Makefile3
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c827
-rw-r--r--drivers/media/platform/amlogic/meson-ge2d/Kconfig14
-rw-r--r--drivers/media/platform/amlogic/meson-ge2d/Makefile3
-rw-r--r--drivers/media/platform/amlogic/meson-ge2d/ge2d-regs.h360
-rw-r--r--drivers/media/platform/amlogic/meson-ge2d/ge2d.c1055
-rw-r--r--drivers/media/platform/amphion/Kconfig22
-rw-r--r--drivers/media/platform/amphion/Makefile20
-rw-r--r--drivers/media/platform/amphion/vdec.c1967
-rw-r--r--drivers/media/platform/amphion/venc.c1357
-rw-r--r--drivers/media/platform/amphion/vpu.h372
-rw-r--r--drivers/media/platform/amphion/vpu_cmds.c453
-rw-r--r--drivers/media/platform/amphion/vpu_cmds.h25
-rw-r--r--drivers/media/platform/amphion/vpu_codec.h69
-rw-r--r--drivers/media/platform/amphion/vpu_color.c110
-rw-r--r--drivers/media/platform/amphion/vpu_core.c864
-rw-r--r--drivers/media/platform/amphion/vpu_core.h16
-rw-r--r--drivers/media/platform/amphion/vpu_dbg.c521
-rw-r--r--drivers/media/platform/amphion/vpu_defs.h201
-rw-r--r--drivers/media/platform/amphion/vpu_drv.c237
-rw-r--r--drivers/media/platform/amphion/vpu_helpers.c634
-rw-r--r--drivers/media/platform/amphion/vpu_helpers.h75
-rw-r--r--drivers/media/platform/amphion/vpu_imx8q.c271
-rw-r--r--drivers/media/platform/amphion/vpu_imx8q.h115
-rw-r--r--drivers/media/platform/amphion/vpu_malone.c1734
-rw-r--r--drivers/media/platform/amphion/vpu_malone.h46
-rw-r--r--drivers/media/platform/amphion/vpu_mbox.c111
-rw-r--r--drivers/media/platform/amphion/vpu_mbox.h15
-rw-r--r--drivers/media/platform/amphion/vpu_msgs.c421
-rw-r--r--drivers/media/platform/amphion/vpu_msgs.h14
-rw-r--r--drivers/media/platform/amphion/vpu_rpc.c257
-rw-r--r--drivers/media/platform/amphion/vpu_rpc.h466
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.c887
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.h42
-rw-r--r--drivers/media/platform/amphion/vpu_windsor.c1180
-rw-r--r--drivers/media/platform/amphion/vpu_windsor.h37
-rw-r--r--drivers/media/platform/arm/Kconfig5
-rw-r--r--drivers/media/platform/arm/Makefile2
-rw-r--r--drivers/media/platform/arm/mali-c55/Kconfig18
-rw-r--r--drivers/media/platform/arm/mali-c55/Makefile11
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-capture.c959
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-common.h310
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-core.c917
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-isp.c665
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-params.c819
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-registers.h449
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-resizer.c1156
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-stats.c323
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-tpg.c437
-rw-r--r--drivers/media/platform/aspeed/Kconfig14
-rw-r--r--drivers/media/platform/aspeed/Makefile2
-rw-r--r--drivers/media/platform/aspeed/aspeed-video.c2380
-rw-r--r--drivers/media/platform/atmel/Kconfig15
-rw-r--r--drivers/media/platform/atmel/Makefile3
-rw-r--r--drivers/media/platform/atmel/atmel-isi.c1371
-rw-r--r--drivers/media/platform/atmel/atmel-isi.h135
-rw-r--r--drivers/media/platform/broadcom/Kconfig23
-rw-r--r--drivers/media/platform/broadcom/Makefile3
-rw-r--r--drivers/media/platform/broadcom/bcm2835-unicam-regs.h246
-rw-r--r--drivers/media/platform/broadcom/bcm2835-unicam.c2757
-rw-r--r--drivers/media/platform/cadence/Kconfig29
-rw-r--r--drivers/media/platform/cadence/Makefile4
-rw-r--r--drivers/media/platform/cadence/cdns-csi2rx.c936
-rw-r--r--drivers/media/platform/cadence/cdns-csi2tx.c657
-rw-r--r--drivers/media/platform/chips-media/Kconfig6
-rw-r--r--drivers/media/platform/chips-media/Makefile4
-rw-r--r--drivers/media/platform/chips-media/coda/Kconfig18
-rw-r--r--drivers/media/platform/chips-media/coda/Makefile6
-rw-r--r--drivers/media/platform/chips-media/coda/coda-bit.c2666
-rw-r--r--drivers/media/platform/chips-media/coda/coda-common.c3360
-rw-r--r--drivers/media/platform/chips-media/coda/coda-gdi.c146
-rw-r--r--drivers/media/platform/chips-media/coda/coda-h264.c429
-rw-r--r--drivers/media/platform/chips-media/coda/coda-jpeg.c1547
-rw-r--r--drivers/media/platform/chips-media/coda/coda-mpeg2.c87
-rw-r--r--drivers/media/platform/chips-media/coda/coda-mpeg4.c87
-rw-r--r--drivers/media/platform/chips-media/coda/coda.h403
-rw-r--r--drivers/media/platform/chips-media/coda/coda_regs.h563
-rw-r--r--drivers/media/platform/chips-media/coda/imx-vdoa.c346
-rw-r--r--drivers/media/platform/chips-media/coda/imx-vdoa.h50
-rw-r--r--drivers/media/platform/chips-media/coda/trace.h175
-rw-r--r--drivers/media/platform/chips-media/wave5/Kconfig15
-rw-r--r--drivers/media/platform/chips-media/wave5/Makefile10
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-helper.c243
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-helper.h36
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-hw.c2762
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-regdefine.h737
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vdi.c210
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vdi.h35
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c1911
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c1840
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu.c397
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu.h85
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuapi.c997
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuapi.h878
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h102
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuerror.h292
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5.h123
-rw-r--r--drivers/media/platform/imagination/Kconfig14
-rw-r--r--drivers/media/platform/imagination/Makefile3
-rw-r--r--drivers/media/platform/imagination/e5010-core-regs.h585
-rw-r--r--drivers/media/platform/imagination/e5010-jpeg-enc-hw.c267
-rw-r--r--drivers/media/platform/imagination/e5010-jpeg-enc-hw.h42
-rw-r--r--drivers/media/platform/imagination/e5010-jpeg-enc.c1630
-rw-r--r--drivers/media/platform/imagination/e5010-jpeg-enc.h173
-rw-r--r--drivers/media/platform/imagination/e5010-mmu-regs.h311
-rw-r--r--drivers/media/platform/intel/Kconfig14
-rw-r--r--drivers/media/platform/intel/Makefile2
-rw-r--r--drivers/media/platform/intel/pxa_camera.c2470
-rw-r--r--drivers/media/platform/m2m-deinterlace.c997
-rw-r--r--drivers/media/platform/marvell/Kconfig37
-rw-r--r--drivers/media/platform/marvell/Makefile6
-rw-r--r--drivers/media/platform/marvell/cafe-driver.c (renamed from drivers/media/video/marvell-ccic/cafe-driver.c)163
-rw-r--r--drivers/media/platform/marvell/mcam-core.c1987
-rw-r--r--drivers/media/platform/marvell/mcam-core.h (renamed from drivers/media/video/marvell-ccic/mcam-core.h)105
-rw-r--r--drivers/media/platform/marvell/mmp-driver.c383
-rw-r--r--drivers/media/platform/mediatek/Kconfig9
-rw-r--r--drivers/media/platform/mediatek/Makefile6
-rw-r--r--drivers/media/platform/mediatek/jpeg/Kconfig16
-rw-r--r--drivers/media/platform/mediatek/jpeg/Makefile10
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c1965
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h309
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c685
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h82
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_parse.c147
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_parse.h17
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h58
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c407
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h96
-rw-r--r--drivers/media/platform/mediatek/mdp/Kconfig17
-rw-r--r--drivers/media/platform/mediatek/mdp/Makefile10
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_comp.c74
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_comp.h46
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_core.c313
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_core.h256
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h120
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c1226
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.h14
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_regs.c148
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_regs.h23
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_vpu.c138
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_vpu.h33
-rw-r--r--drivers/media/platform/mediatek/mdp3/Kconfig19
-rw-r--r--drivers/media/platform/mediatek/mdp3/Makefile6
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c1418
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_aal.h25
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_ccorr.h19
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_color.h31
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_fg.h23
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_hdr.h31
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_merge.h25
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_ovl.h25
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_pad.h21
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_rdma.h89
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_rsz.h41
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_tdshp.h34
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_wdma.h47
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_reg_wrot.h63
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_sm_mt8183.h144
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_sm_mt8195.h283
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h154
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h23
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c724
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h43
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c2020
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h264
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c425
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h153
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c738
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.h47
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c514
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.h378
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-type.h53
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c288
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.h63
-rw-r--r--drivers/media/platform/mediatek/vcodec/Kconfig36
-rw-r--r--drivers/media/platform/mediatek/vcodec/Makefile5
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/Makefile21
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_cmn_drv.h147
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c231
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.h74
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c78
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h44
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h52
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c92
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c132
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_intr.c68
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_intr.h19
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c174
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.h75
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/Makefile25
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c1039
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.h102
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c605
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h347
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.c201
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_hw.h59
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.c262
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.h17
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c621
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c918
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c2215
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c494
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.c322
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h280
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c449
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c1323
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c1093
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c608
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c436
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c1018
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c2209
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_base.h46
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_if.c128
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_drv_if.h103
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h153
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.c372
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h191
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c320
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h119
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/Makefile11
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c1428
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.h51
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c488
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h250
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c109
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h18
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c819
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc/venc_vp8_if.c441
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_drv_base.h53
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c99
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h169
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_ipi_msg.h244
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c382
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.h50
-rw-r--r--drivers/media/platform/mediatek/vpu/Kconfig15
-rw-r--r--drivers/media/platform/mediatek/vpu/Makefile4
-rw-r--r--drivers/media/platform/mediatek/vpu/mtk_vpu.c1055
-rw-r--r--drivers/media/platform/mediatek/vpu/mtk_vpu.h188
-rw-r--r--drivers/media/platform/microchip/Kconfig61
-rw-r--r--drivers/media/platform/microchip/Makefile9
-rw-r--r--drivers/media/platform/microchip/microchip-csi2dc.c797
-rw-r--r--drivers/media/platform/microchip/microchip-isc-base.c1970
-rw-r--r--drivers/media/platform/microchip/microchip-isc-clk.c311
-rw-r--r--drivers/media/platform/microchip/microchip-isc-regs.h413
-rw-r--r--drivers/media/platform/microchip/microchip-isc-scaler.c271
-rw-r--r--drivers/media/platform/microchip/microchip-isc.h400
-rw-r--r--drivers/media/platform/microchip/microchip-sama5d2-isc.c673
-rw-r--r--drivers/media/platform/microchip/microchip-sama7g5-isc.c636
-rw-r--r--drivers/media/platform/nuvoton/Kconfig15
-rw-r--r--drivers/media/platform/nuvoton/Makefile2
-rw-r--r--drivers/media/platform/nuvoton/npcm-regs.h152
-rw-r--r--drivers/media/platform/nuvoton/npcm-video.c1826
-rw-r--r--drivers/media/platform/nvidia/Kconfig5
-rw-r--r--drivers/media/platform/nvidia/Makefile3
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/Kconfig16
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/Makefile3
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c229
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/h264.c943
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/iommu.c158
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/trace.h95
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/v4l2.c1015
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/vde.c550
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/vde.h241
-rw-r--r--drivers/media/platform/nxp/Kconfig69
-rw-r--r--drivers/media/platform/nxp/Makefile11
-rw-r--r--drivers/media/platform/nxp/dw100/Kconfig16
-rw-r--r--drivers/media/platform/nxp/dw100/Makefile3
-rw-r--r--drivers/media/platform/nxp/dw100/dw100.c1693
-rw-r--r--drivers/media/platform/nxp/dw100/dw100_regs.h117
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/Kconfig12
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/Makefile3
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c191
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h132
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c3062
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h202
-rw-r--r--drivers/media/platform/nxp/imx-mipi-csis.c1643
-rw-r--r--drivers/media/platform/nxp/imx-pxp.c1947
-rw-r--r--drivers/media/platform/nxp/imx-pxp.h1690
-rw-r--r--drivers/media/platform/nxp/imx7-media-csi.c2292
-rw-r--r--drivers/media/platform/nxp/imx8-isi/Kconfig22
-rw-r--r--drivers/media/platform/nxp/imx8-isi/Makefile8
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c581
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h416
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c507
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-debug.c130
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-gasket.c97
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c649
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c856
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c870
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-regs.h418
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c1469
-rw-r--r--drivers/media/platform/nxp/imx8mq-mipi-csi2.c1116
-rw-r--r--drivers/media/platform/nxp/mx2_emmaprp.c905
-rw-r--r--drivers/media/platform/qcom/Kconfig7
-rw-r--r--drivers/media/platform/qcom/Makefile4
-rw-r--r--drivers/media/platform/qcom/camss/Kconfig9
-rw-r--r--drivers/media/platform/qcom/camss/Makefile31
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-340.c190
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-4-1.c187
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-4-7.c212
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-680.c422
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen1.h27
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen2.c432
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen2.h39
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen3.c351
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen3.h25
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.c1441
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.h250
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c196
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c1143
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.c839
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.h134
-rw-r--r--drivers/media/platform/qcom/camss/camss-format.c91
-rw-r--r--drivers/media/platform/qcom/camss/camss-format.h62
-rw-r--r--drivers/media/platform/qcom/camss/camss-ispif.c1461
-rw-r--r--drivers/media/platform/qcom/camss/camss-ispif.h79
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-17x.c595
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-340.c320
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-1.c1020
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-7.c1160
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-8.c1150
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-480.c297
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-680.c244
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-gen1.c743
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-gen1.h117
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-gen3.c193
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-vbif.c31
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-vbif.h19
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.c2167
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.h317
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.c756
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.h58
-rw-r--r--drivers/media/platform/qcom/camss/camss.c5024
-rw-r--r--drivers/media/platform/qcom/camss/camss.h176
-rw-r--r--drivers/media/platform/qcom/iris/Kconfig13
-rw-r--r--drivers/media/platform/qcom/iris/Makefile32
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.c795
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.h123
-rw-r--r--drivers/media/platform/qcom/iris/iris_common.c235
-rw-r--r--drivers/media/platform/qcom/iris/iris_common.h18
-rw-r--r--drivers/media/platform/qcom/iris/iris_core.c98
-rw-r--r--drivers/media/platform/qcom/iris/iris_core.h125
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.c917
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.h37
-rw-r--r--drivers/media/platform/qcom/iris/iris_firmware.c109
-rw-r--r--drivers/media/platform/qcom/iris/iris_firmware.h15
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_common.c176
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_common.h156
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1.h16
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c1089
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h551
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c709
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2.h41
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c1216
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h197
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c292
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h125
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c984
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_queue.c317
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_queue.h182
-rw-r--r--drivers/media/platform/qcom/iris/iris_instance.h112
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_common.h266
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_gen1.c417
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_gen2.c1080
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_qcs8300.h23
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sc7280.h26
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sm8650.h13
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sm8750.h22
-rw-r--r--drivers/media/platform/qcom/iris/iris_power.c140
-rw-r--r--drivers/media/platform/qcom/iris/iris_power.h13
-rw-r--r--drivers/media/platform/qcom/iris/iris_probe.c397
-rw-r--r--drivers/media/platform/qcom/iris/iris_resources.c131
-rw-r--r--drivers/media/platform/qcom/iris/iris_resources.h18
-rw-r--r--drivers/media/platform/qcom/iris/iris_state.c277
-rw-r--r--drivers/media/platform/qcom/iris/iris_state.h146
-rw-r--r--drivers/media/platform/qcom/iris/iris_utils.c127
-rw-r--r--drivers/media/platform/qcom/iris/iris_utils.h55
-rw-r--r--drivers/media/platform/qcom/iris/iris_vb2.c343
-rw-r--r--drivers/media/platform/qcom/iris/iris_vb2.h19
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.c515
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.h25
-rw-r--r--drivers/media/platform/qcom/iris/iris_venc.c616
-rw-r--r--drivers/media/platform/qcom/iris/iris_venc.h27
-rw-r--r--drivers/media/platform/qcom/iris/iris_vidc.c718
-rw-r--r--drivers/media/platform/qcom/iris/iris_vidc.h15
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu2.c47
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu3x.c473
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.c1555
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.h153
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.c389
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.h37
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_register_defines.h17
-rw-r--r--drivers/media/platform/qcom/venus/Kconfig15
-rw-r--r--drivers/media/platform/qcom/venus/Makefile15
-rw-r--r--drivers/media/platform/qcom/venus/core.c1150
-rw-r--r--drivers/media/platform/qcom/venus/core.h585
-rw-r--r--drivers/media/platform/qcom/venus/dbgfs.c28
-rw-r--r--drivers/media/platform/qcom/venus/dbgfs.h25
-rw-r--r--drivers/media/platform/qcom/venus/firmware.c384
-rw-r--r--drivers/media/platform/qcom/venus/firmware.h28
-rw-r--r--drivers/media/platform/qcom/venus/helpers.c1862
-rw-r--r--drivers/media/platform/qcom/venus/helpers.h74
-rw-r--r--drivers/media/platform/qcom/venus/hfi.c569
-rw-r--r--drivers/media/platform/qcom/venus/hfi.h176
-rw-r--r--drivers/media/platform/qcom/venus/hfi_cmds.c1378
-rw-r--r--drivers/media/platform/qcom/venus/hfi_cmds.h296
-rw-r--r--drivers/media/platform/qcom/venus/hfi_helper.h1302
-rw-r--r--drivers/media/platform/qcom/venus/hfi_msgs.c862
-rw-r--r--drivers/media/platform/qcom/venus/hfi_msgs.h276
-rw-r--r--drivers/media/platform/qcom/venus/hfi_parser.c390
-rw-r--r--drivers/media/platform/qcom/venus/hfi_parser.h120
-rw-r--r--drivers/media/platform/qcom/venus/hfi_plat_bufs.h41
-rw-r--r--drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c1334
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform.c97
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform.h79
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform_v4.c481
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform_v6.c347
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus.c1778
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus.h15
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus_io.h164
-rw-r--r--drivers/media/platform/qcom/venus/pm_helpers.c1196
-rw-r--r--drivers/media/platform/qcom/venus/pm_helpers.h66
-rw-r--r--drivers/media/platform/qcom/venus/vdec.c1881
-rw-r--r--drivers/media/platform/qcom/venus/vdec.h13
-rw-r--r--drivers/media/platform/qcom/venus/vdec_ctrls.c189
-rw-r--r--drivers/media/platform/qcom/venus/venc.c1663
-rw-r--r--drivers/media/platform/qcom/venus/venc.h13
-rw-r--r--drivers/media/platform/qcom/venus/venc_ctrls.c740
-rw-r--r--drivers/media/platform/raspberrypi/Kconfig6
-rw-r--r--drivers/media/platform/raspberrypi/Makefile4
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/Kconfig14
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/Makefile6
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/pisp_be.c1796
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h519
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/Kconfig15
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/Makefile6
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h332
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h202
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe.c2506
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe.h43
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/csi2.c586
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/csi2.h89
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/dphy.c181
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/dphy.h27
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c605
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h53
-rw-r--r--drivers/media/platform/renesas/Kconfig123
-rw-r--r--drivers/media/platform/renesas/Makefile18
-rw-r--r--drivers/media/platform/renesas/rcar-csi2.c2659
-rw-r--r--drivers/media/platform/renesas/rcar-fcp.c216
-rw-r--r--drivers/media/platform/renesas/rcar-isp/Kconfig18
-rw-r--r--drivers/media/platform/renesas/rcar-isp/Makefile4
-rw-r--r--drivers/media/platform/renesas/rcar-isp/csisp.c593
-rw-r--r--drivers/media/platform/renesas/rcar-vin/Kconfig16
-rw-r--r--drivers/media/platform/renesas/rcar-vin/Makefile4
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-core.c1296
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-dma.c1585
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c733
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-vin.h281
-rw-r--r--drivers/media/platform/renesas/rcar_drif.c1485
-rw-r--r--drivers/media/platform/renesas/rcar_fdp1.c2451
-rw-r--r--drivers/media/platform/renesas/rcar_jpu.c1737
-rw-r--r--drivers/media/platform/renesas/renesas-ceu.c1729
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/Kconfig33
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/Makefile6
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c442
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h111
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h205
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c1049
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c379
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c1204
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/Kconfig18
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/Makefile5
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c251
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c376
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c531
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h130
-rw-r--r--drivers/media/platform/renesas/sh_vou.c1372
-rw-r--r--drivers/media/platform/renesas/vsp1/Makefile11
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1.h132
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_brx.c450
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_brx.h44
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_clu.c284
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_clu.h45
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_dl.c1199
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_dl.h79
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.c1022
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.h80
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drv.c1054
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.c680
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.h197
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgo.c221
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgo.h41
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgt.c213
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgt.h38
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_histo.c561
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_histo.h77
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hsit.c185
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hsit.h34
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_iif.c121
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_iif.h29
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lif.c177
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lif.h33
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lut.c242
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lut.h42
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_pipe.c707
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_pipe.h189
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_regs.h895
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rpf.c456
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.c321
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.h88
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_sru.c395
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_sru.h38
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uds.c421
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uds.h37
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uif.c262
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uif.h32
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_video.c1315
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_video.h61
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_vspx.c634
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_vspx.h16
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_wpf.c610
-rw-r--r--drivers/media/platform/rockchip/Kconfig8
-rw-r--r--drivers/media/platform/rockchip/Makefile5
-rw-r--r--drivers/media/platform/rockchip/rga/Kconfig14
-rw-r--r--drivers/media/platform/rockchip/rga/Makefile4
-rw-r--r--drivers/media/platform/rockchip/rga/rga-buf.c230
-rw-r--r--drivers/media/platform/rockchip/rga/rga-hw.c454
-rw-r--r--drivers/media/platform/rockchip/rga/rga-hw.h434
-rw-r--r--drivers/media/platform/rockchip/rga/rga.c981
-rw-r--r--drivers/media/platform/rockchip/rga/rga.h150
-rw-r--r--drivers/media/platform/rockchip/rkcif/Kconfig18
-rw-r--r--drivers/media/platform/rockchip/rkcif/Makefile8
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.c865
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.h25
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c777
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h23
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-common.h250
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-dev.c303
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-interface.c442
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-interface.h31
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-regs.h153
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-stream.c636
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-stream.h32
-rw-r--r--drivers/media/platform/rockchip/rkisp1/Kconfig20
-rw-r--r--drivers/media/platform/rockchip/rkisp1/Makefile14
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c1647
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-common.c194
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-common.h700
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c518
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-csi.h28
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c249
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c828
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c1173
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-params.c2888
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h1367
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c766
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c471
-rw-r--r--drivers/media/platform/rockchip/rkvdec/Kconfig16
-rw-r--r--drivers/media/platform/rockchip/rkvdec/Makefile3
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c1212
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-data.c1848
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c820
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h227
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c1076
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec.c1411
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec.h161
-rw-r--r--drivers/media/platform/samsung/Kconfig10
-rw-r--r--drivers/media/platform/samsung/Makefile7
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/Kconfig10
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/Makefile4
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-core.c1324
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-core.h524
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c786
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-regs.c426
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-regs.h169
-rw-r--r--drivers/media/platform/samsung/exynos4-is/Kconfig84
-rw-r--r--drivers/media/platform/samsung/exynos4-is/Makefile18
-rw-r--r--drivers/media/platform/samsung/exynos4-is/common.c48
-rw-r--r--drivers/media/platform/samsung/exynos4-is/common.h12
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-capture.c1895
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-core.c1176
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-core.h (renamed from drivers/media/video/s5p-fimc/fimc-core.h)221
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-command.h134
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c138
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h244
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c156
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h10
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-param.c884
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-param.h1021
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c231
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-regs.h161
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-sensor.c31
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-sensor.h53
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is.c1002
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is.h356
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c657
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-isp-video.h41
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-isp.c787
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-isp.h197
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite-reg.c347
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite-reg.h153
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite.c1664
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite.h221
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-m2m.c771
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-reg.c (renamed from drivers/media/video/s5p-fimc/fimc-reg.c)271
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-reg.h338
-rw-r--r--drivers/media/platform/samsung/exynos4-is/media-dev.c1593
-rw-r--r--drivers/media/platform/samsung/exynos4-is/media-dev.h201
-rw-r--r--drivers/media/platform/samsung/exynos4-is/mipi-csis.c1041
-rw-r--r--drivers/media/platform/samsung/exynos4-is/mipi-csis.h23
-rw-r--r--drivers/media/platform/samsung/s3c-camif/Kconfig15
-rw-r--r--drivers/media/platform/samsung/s3c-camif/Makefile6
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-capture.c1649
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-core.c647
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-core.h397
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-regs.c603
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-regs.h268
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/Kconfig11
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/Makefile4
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/g2d-hw.c (renamed from drivers/media/video/s5p-g2d/g2d-hw.c)22
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/g2d-regs.h (renamed from drivers/media/video/s5p-g2d/g2d-regs.h)13
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/g2d.c787
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/g2d.h (renamed from drivers/media/video/s5p-g2d/g2d.h)22
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/Kconfig12
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/Makefile3
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c3171
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-core.h257
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c481
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h56
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c302
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h40
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.c305
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.h57
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-regs.h646
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/Kconfig9
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/Makefile7
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v10.h87
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v12.h52
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h409
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h58
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h126
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc.h (renamed from drivers/media/video/s5p-mfc/regs-mfc.h)49
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c1753
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.c21
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.h31
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.c163
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.h16
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.c160
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.h16
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h811
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c490
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.h29
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_debug.h51
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c1256
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.h19
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c2757
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.h19
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_intr.c (renamed from drivers/media/video/s5p-mfc/s5p_mfc_intr.c)18
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_intr.h23
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_iommu.h28
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.c121
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h341
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c1639
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.h82
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c2713
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h56
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.c105
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.h20
-rw-r--r--drivers/media/platform/st/Kconfig6
-rw-r--r--drivers/media/platform/st/Makefile6
-rw-r--r--drivers/media/platform/st/sti/Kconfig4
-rw-r--r--drivers/media/platform/st/sti/Makefile5
-rw-r--r--drivers/media/platform/st/sti/bdisp/Kconfig10
-rw-r--r--drivers/media/platform/st/sti/bdisp/Makefile4
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-debug.c658
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-filter.h42
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-hw.c1118
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-reg.h235
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c1426
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp.h214
-rw-r--r--drivers/media/platform/st/sti/delta/Kconfig36
-rw-r--r--drivers/media/platform/st/sti/delta/Makefile7
-rw-r--r--drivers/media/platform/st/sti/delta/delta-cfg.h64
-rw-r--r--drivers/media/platform/st/sti/delta/delta-debug.c72
-rw-r--r--drivers/media/platform/st/sti/delta/delta-debug.h18
-rw-r--r--drivers/media/platform/st/sti/delta/delta-ipc.c591
-rw-r--r--drivers/media/platform/st/sti/delta/delta-ipc.h76
-rw-r--r--drivers/media/platform/st/sti/delta/delta-mem.c51
-rw-r--r--drivers/media/platform/st/sti/delta/delta-mem.h14
-rw-r--r--drivers/media/platform/st/sti/delta/delta-mjpeg-dec.c457
-rw-r--r--drivers/media/platform/st/sti/delta/delta-mjpeg-fw.h225
-rw-r--r--drivers/media/platform/st/sti/delta/delta-mjpeg-hdr.c149
-rw-r--r--drivers/media/platform/st/sti/delta/delta-mjpeg.h35
-rw-r--r--drivers/media/platform/st/sti/delta/delta-v4l2.c1967
-rw-r--r--drivers/media/platform/st/sti/delta/delta.h566
-rw-r--r--drivers/media/platform/st/sti/hva/Kconfig26
-rw-r--r--drivers/media/platform/st/sti/hva/Makefile4
-rw-r--r--drivers/media/platform/st/sti/hva/hva-debugfs.c396
-rw-r--r--drivers/media/platform/st/sti/hva/hva-h264.c1063
-rw-r--r--drivers/media/platform/st/sti/hva/hva-hw.c584
-rw-r--r--drivers/media/platform/st/sti/hva/hva-hw.h45
-rw-r--r--drivers/media/platform/st/sti/hva/hva-mem.c62
-rw-r--r--drivers/media/platform/st/sti/hva/hva-mem.h34
-rw-r--r--drivers/media/platform/st/sti/hva/hva-v4l2.c1471
-rw-r--r--drivers/media/platform/st/sti/hva/hva.h407
-rw-r--r--drivers/media/platform/st/stm32/Kconfig61
-rw-r--r--drivers/media/platform/st/stm32/Makefile6
-rw-r--r--drivers/media/platform/st/stm32/dma2d/dma2d-hw.c133
-rw-r--r--drivers/media/platform/st/stm32/dma2d/dma2d-regs.h113
-rw-r--r--drivers/media/platform/st/stm32/dma2d/dma2d.c727
-rw-r--r--drivers/media/platform/st/stm32/dma2d/dma2d.h133
-rw-r--r--drivers/media/platform/st/stm32/stm32-csi.c1141
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmi.c2159
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/Makefile4
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c964
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c594
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.c111
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h217
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c672
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c540
-rw-r--r--drivers/media/platform/sunxi/Kconfig10
-rw-r--r--drivers/media/platform/sunxi/Makefile8
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/Kconfig16
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/Makefile7
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c354
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h162
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c456
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c381
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/Kconfig15
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/Makefile4
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c439
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h63
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c872
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h69
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c1100
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h89
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h184
-rw-r--r--drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig15
-rw-r--r--drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile4
-rw-r--r--drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c771
-rw-r--r--drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h52
-rw-r--r--drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2_reg.h76
-rw-r--r--drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig14
-rw-r--r--drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Makefile4
-rw-r--r--drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.c72
-rw-r--r--drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.h39
-rw-r--r--drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c838
-rw-r--r--drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h55
-rw-r--r--drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2_reg.h151
-rw-r--r--drivers/media/platform/sunxi/sun8i-di/Kconfig14
-rw-r--r--drivers/media/platform/sunxi/sun8i-di/Makefile2
-rw-r--r--drivers/media/platform/sunxi/sun8i-di/sun8i-di.c1011
-rw-r--r--drivers/media/platform/sunxi/sun8i-di/sun8i-di.h237
-rw-r--r--drivers/media/platform/sunxi/sun8i-rotate/Kconfig14
-rw-r--r--drivers/media/platform/sunxi/sun8i-rotate/Makefile5
-rw-r--r--drivers/media/platform/sunxi/sun8i-rotate/sun8i-formats.h25
-rw-r--r--drivers/media/platform/sunxi/sun8i-rotate/sun8i-rotate.h135
-rw-r--r--drivers/media/platform/sunxi/sun8i-rotate/sun8i_formats.c273
-rw-r--r--drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c915
-rw-r--r--drivers/media/platform/synopsys/Kconfig3
-rw-r--r--drivers/media/platform/synopsys/Makefile2
-rw-r--r--drivers/media/platform/synopsys/hdmirx/Kconfig36
-rw-r--r--drivers/media/platform/synopsys/hdmirx/Makefile4
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c2746
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h396
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c275
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h43
-rw-r--r--drivers/media/platform/ti/Kconfig82
-rw-r--r--drivers/media/platform/ti/Makefile8
-rw-r--r--drivers/media/platform/ti/am437x/Kconfig16
-rw-r--r--drivers/media/platform/ti/am437x/Makefile4
-rw-r--r--drivers/media/platform/ti/am437x/am437x-vpfe.c2631
-rw-r--r--drivers/media/platform/ti/am437x/am437x-vpfe.h288
-rw-r--r--drivers/media/platform/ti/am437x/am437x-vpfe_regs.h132
-rw-r--r--drivers/media/platform/ti/cal/Makefile3
-rw-r--r--drivers/media/platform/ti/cal/cal-camerarx.c1046
-rw-r--r--drivers/media/platform/ti/cal/cal-video.c1112
-rw-r--r--drivers/media/platform/ti/cal/cal.c1363
-rw-r--r--drivers/media/platform/ti/cal/cal.h341
-rw-r--r--drivers/media/platform/ti/cal/cal_regs.h463
-rw-r--r--drivers/media/platform/ti/davinci/Kconfig33
-rw-r--r--drivers/media/platform/ti/davinci/Makefile9
-rw-r--r--drivers/media/platform/ti/davinci/vpif.c609
-rw-r--r--drivers/media/platform/ti/davinci/vpif.h (renamed from drivers/media/video/davinci/vpif.h)126
-rw-r--r--drivers/media/platform/ti/davinci/vpif_capture.c1819
-rw-r--r--drivers/media/platform/ti/davinci/vpif_capture.h107
-rw-r--r--drivers/media/platform/ti/davinci/vpif_display.c1402
-rw-r--r--drivers/media/platform/ti/davinci/vpif_display.h116
-rw-r--r--drivers/media/platform/ti/j721e-csi2rx/Makefile2
-rw-r--r--drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c1210
-rw-r--r--drivers/media/platform/ti/omap/Kconfig17
-rw-r--r--drivers/media/platform/ti/omap/Makefile9
-rw-r--r--drivers/media/platform/ti/omap/omap_vout.c1740
-rw-r--r--drivers/media/platform/ti/omap/omap_vout_vrfb.c419
-rw-r--r--drivers/media/platform/ti/omap/omap_vout_vrfb.h40
-rw-r--r--drivers/media/platform/ti/omap/omap_voutdef.h (renamed from drivers/media/video/omap/omap_voutdef.h)66
-rw-r--r--drivers/media/platform/ti/omap/omap_voutlib.c (renamed from drivers/media/video/omap/omap_voutlib.c)50
-rw-r--r--drivers/media/platform/ti/omap/omap_voutlib.h (renamed from drivers/media/video/omap/omap_voutlib.h)3
-rw-r--r--drivers/media/platform/ti/omap3isp/Kconfig21
-rw-r--r--drivers/media/platform/ti/omap3isp/Makefile12
-rw-r--r--drivers/media/platform/ti/omap3isp/cfa_coef_table.h48
-rw-r--r--drivers/media/platform/ti/omap3isp/gamma_table.h (renamed from drivers/media/video/omap3isp/gamma_table.h)15
-rw-r--r--drivers/media/platform/ti/omap3isp/isp.c2455
-rw-r--r--drivers/media/platform/ti/omap3isp/isp.h (renamed from drivers/media/video/omap3isp/isp.h)142
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccdc.c2763
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccdc.h174
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccp2.c (renamed from drivers/media/video/omap3isp/ispccp2.c)201
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccp2.h85
-rw-r--r--drivers/media/platform/ti/omap3isp/ispcsi2.c (renamed from drivers/media/video/omap3isp/ispcsi2.c)202
-rw-r--r--drivers/media/platform/ti/omap3isp/ispcsi2.h (renamed from drivers/media/video/omap3isp/ispcsi2.h)15
-rw-r--r--drivers/media/platform/ti/omap3isp/ispcsiphy.c365
-rw-r--r--drivers/media/platform/ti/omap3isp/ispcsiphy.h43
-rw-r--r--drivers/media/platform/ti/omap3isp/isph3a.h (renamed from drivers/media/video/omap3isp/isph3a.h)15
-rw-r--r--drivers/media/platform/ti/omap3isp/isph3a_aewb.c (renamed from drivers/media/video/omap3isp/isph3a_aewb.c)62
-rw-r--r--drivers/media/platform/ti/omap3isp/isph3a_af.c (renamed from drivers/media/video/omap3isp/isph3a_af.c)64
-rw-r--r--drivers/media/platform/ti/omap3isp/isphist.c (renamed from drivers/media/video/omap3isp/isphist.c)178
-rw-r--r--drivers/media/platform/ti/omap3isp/isphist.h27
-rw-r--r--drivers/media/platform/ti/omap3isp/isppreview.c (renamed from drivers/media/video/omap3isp/isppreview.c)1000
-rw-r--r--drivers/media/platform/ti/omap3isp/isppreview.h (renamed from drivers/media/video/omap3isp/isppreview.h)16
-rw-r--r--drivers/media/platform/ti/omap3isp/ispreg.h1518
-rw-r--r--drivers/media/platform/ti/omap3isp/ispresizer.c (renamed from drivers/media/video/omap3isp/ispresizer.c)260
-rw-r--r--drivers/media/platform/ti/omap3isp/ispresizer.h (renamed from drivers/media/video/omap3isp/ispresizer.h)22
-rw-r--r--drivers/media/platform/ti/omap3isp/ispstat.c (renamed from drivers/media/video/omap3isp/ispstat.c)316
-rw-r--r--drivers/media/platform/ti/omap3isp/ispstat.h (renamed from drivers/media/video/omap3isp/ispstat.h)30
-rw-r--r--drivers/media/platform/ti/omap3isp/ispvideo.c1492
-rw-r--r--drivers/media/platform/ti/omap3isp/ispvideo.h (renamed from drivers/media/video/omap3isp/ispvideo.h)84
-rw-r--r--drivers/media/platform/ti/omap3isp/luma_enhance_table.h29
-rw-r--r--drivers/media/platform/ti/omap3isp/noise_filter_table.h17
-rw-r--r--drivers/media/platform/ti/omap3isp/omap3isp.h132
-rw-r--r--drivers/media/platform/ti/vpe/Makefile12
-rw-r--r--drivers/media/platform/ti/vpe/csc.c279
-rw-r--r--drivers/media/platform/ti/vpe/csc.h65
-rw-r--r--drivers/media/platform/ti/vpe/sc.c306
-rw-r--r--drivers/media/platform/ti/vpe/sc.h208
-rw-r--r--drivers/media/platform/ti/vpe/sc_coeff.h1339
-rw-r--r--drivers/media/platform/ti/vpe/vpdma.c1145
-rw-r--r--drivers/media/platform/ti/vpe/vpdma.h281
-rw-r--r--drivers/media/platform/ti/vpe/vpdma_priv.h639
-rw-r--r--drivers/media/platform/ti/vpe/vpe.c2657
-rw-r--r--drivers/media/platform/ti/vpe/vpe_regs.h306
-rw-r--r--drivers/media/platform/verisilicon/Kconfig70
-rw-r--r--drivers/media/platform/verisilicon/Makefile44
-rw-r--r--drivers/media/platform/verisilicon/hantro.h508
-rw-r--r--drivers/media/platform/verisilicon/hantro_drv.c1292
-rw-r--r--drivers/media/platform/verisilicon/hantro_g1.c39
-rw-r--r--drivers/media/platform/verisilicon/hantro_g1_h264_dec.c284
-rw-r--r--drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c240
-rw-r--r--drivers/media/platform/verisilicon/hantro_g1_regs.h356
-rw-r--r--drivers/media/platform/verisilicon/hantro_g1_vp8_dec.c511
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2.c137
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c637
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2_regs.h342
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c996
-rw-r--r--drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c166
-rw-r--r--drivers/media/platform/verisilicon/hantro_h1_regs.h154
-rw-r--r--drivers/media/platform/verisilicon/hantro_h264.c521
-rw-r--r--drivers/media/platform/verisilicon/hantro_hevc.c287
-rw-r--r--drivers/media/platform/verisilicon/hantro_hw.h589
-rw-r--r--drivers/media/platform/verisilicon/hantro_jpeg.c245
-rw-r--r--drivers/media/platform/verisilicon/hantro_jpeg.h15
-rw-r--r--drivers/media/platform/verisilicon/hantro_mpeg2.c61
-rw-r--r--drivers/media/platform/verisilicon/hantro_postproc.c344
-rw-r--r--drivers/media/platform/verisilicon/hantro_v4l2.c1027
-rw-r--r--drivers/media/platform/verisilicon/hantro_v4l2.h34
-rw-r--r--drivers/media/platform/verisilicon/hantro_vp8.c201
-rw-r--r--drivers/media/platform/verisilicon/hantro_vp9.c240
-rw-r--r--drivers/media/platform/verisilicon/hantro_vp9.h102
-rw-r--r--drivers/media/platform/verisilicon/imx8m_vpu_hw.c386
-rw-r--r--drivers/media/platform/verisilicon/rockchip_av1_entropymode.c4424
-rw-r--r--drivers/media/platform/verisilicon/rockchip_av1_entropymode.h272
-rw-r--r--drivers/media/platform/verisilicon/rockchip_av1_filmgrain.c401
-rw-r--r--drivers/media/platform/verisilicon/rockchip_av1_filmgrain.h36
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu2_hw_h264_dec.c491
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c197
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c248
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu2_hw_vp8_dec.c600
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu2_regs.h600
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c2237
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu981_regs.h477
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu_hw.c818
-rw-r--r--drivers/media/platform/verisilicon/sama5d4_vdec_hw.c128
-rw-r--r--drivers/media/platform/verisilicon/stm32mp25_vpu_hw.c186
-rw-r--r--drivers/media/platform/verisilicon/sunxi_vpu_hw.c129
-rw-r--r--drivers/media/platform/via/Kconfig14
-rw-r--r--drivers/media/platform/via/Makefile2
-rw-r--r--drivers/media/platform/via/via-camera.c1311
-rw-r--r--drivers/media/platform/via/via-camera.h (renamed from drivers/media/video/via-camera.h)3
-rw-r--r--drivers/media/platform/video-mux.c501
-rw-r--r--drivers/media/platform/xilinx/Kconfig35
-rw-r--r--drivers/media/platform/xilinx/Makefile8
-rw-r--r--drivers/media/platform/xilinx/xilinx-csi2rxss.c1038
-rw-r--r--drivers/media/platform/xilinx/xilinx-dma.c741
-rw-r--r--drivers/media/platform/xilinx/xilinx-dma.h109
-rw-r--r--drivers/media/platform/xilinx/xilinx-tpg.c920
-rw-r--r--drivers/media/platform/xilinx/xilinx-vip.c320
-rw-r--r--drivers/media/platform/xilinx/xilinx-vip.h234
-rw-r--r--drivers/media/platform/xilinx/xilinx-vipp.c625
-rw-r--r--drivers/media/platform/xilinx/xilinx-vipp.h42
-rw-r--r--drivers/media/platform/xilinx/xilinx-vtc.c375
-rw-r--r--drivers/media/platform/xilinx/xilinx-vtc.h39
-rw-r--r--drivers/media/radio/Kconfig425
-rw-r--r--drivers/media/radio/Makefile45
-rw-r--r--drivers/media/radio/dsbr100.c34
-rw-r--r--drivers/media/radio/lm7000.h44
-rw-r--r--drivers/media/radio/radio-aimslab.c77
-rw-r--r--drivers/media/radio/radio-aztech.c89
-rw-r--r--drivers/media/radio/radio-cadet.c429
-rw-r--r--drivers/media/radio/radio-gemtek.c27
-rw-r--r--drivers/media/radio/radio-isa.c60
-rw-r--r--drivers/media/radio/radio-isa.h19
-rw-r--r--drivers/media/radio/radio-keene.c74
-rw-r--r--drivers/media/radio/radio-ma901.c456
-rw-r--r--drivers/media/radio/radio-maxiradio.c38
-rw-r--r--drivers/media/radio/radio-miropcm20.c437
-rw-r--r--drivers/media/radio/radio-mr800.c83
-rw-r--r--drivers/media/radio/radio-raremono.c389
-rw-r--r--drivers/media/radio/radio-rtrack2.c12
-rw-r--r--drivers/media/radio/radio-sf16fmi.c213
-rw-r--r--drivers/media/radio/radio-sf16fmr2.c43
-rw-r--r--drivers/media/radio/radio-shark.c429
-rw-r--r--drivers/media/radio/radio-shark2.c395
-rw-r--r--drivers/media/radio/radio-si4713.c358
-rw-r--r--drivers/media/radio/radio-si476x.c1522
-rw-r--r--drivers/media/radio/radio-tea5764.c255
-rw-r--r--drivers/media/radio/radio-tea5777.c587
-rw-r--r--drivers/media/radio/radio-tea5777.h76
-rw-r--r--drivers/media/radio/radio-terratec.c14
-rw-r--r--drivers/media/radio/radio-timb.c125
-rw-r--r--drivers/media/radio/radio-trust.c4
-rw-r--r--drivers/media/radio/radio-typhoon.c3
-rw-r--r--drivers/media/radio/radio-wl1273.c2165
-rw-r--r--drivers/media/radio/radio-zoltrix.c5
-rw-r--r--drivers/media/radio/saa7706h.c106
-rw-r--r--drivers/media/radio/si470x/Kconfig23
-rw-r--r--drivers/media/radio/si470x/Makefile9
-rw-r--r--drivers/media/radio/si470x/radio-si470x-common.c444
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c197
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c228
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h57
-rw-r--r--drivers/media/radio/si4713-i2c.c2109
-rw-r--r--drivers/media/radio/si4713-i2c.h240
-rw-r--r--drivers/media/radio/si4713/Kconfig41
-rw-r--r--drivers/media/radio/si4713/Makefile8
-rw-r--r--drivers/media/radio/si4713/radio-platform-si4713.c211
-rw-r--r--drivers/media/radio/si4713/radio-usb-si4713.c522
-rw-r--r--drivers/media/radio/si4713/si4713.c1665
-rw-r--r--drivers/media/radio/si4713/si4713.h254
-rw-r--r--drivers/media/radio/tea575x.c577
-rw-r--r--drivers/media/radio/tef6862.c77
-rw-r--r--drivers/media/radio/wl128x/Kconfig17
-rw-r--r--drivers/media/radio/wl128x/Makefile6
-rw-r--r--drivers/media/radio/wl128x/fmdrv.h239
-rw-r--r--drivers/media/radio/wl128x/fmdrv_common.c1685
-rw-r--r--drivers/media/radio/wl128x/fmdrv_common.h402
-rw-r--r--drivers/media/radio/wl128x/fmdrv_rx.c849
-rw-r--r--drivers/media/radio/wl128x/fmdrv_rx.h59
-rw-r--r--drivers/media/radio/wl128x/fmdrv_tx.c426
-rw-r--r--drivers/media/radio/wl128x/fmdrv_tx.h37
-rw-r--r--drivers/media/radio/wl128x/fmdrv_v4l2.c588
-rw-r--r--drivers/media/radio/wl128x/fmdrv_v4l2.h33
-rw-r--r--drivers/media/rc/Kconfig474
-rw-r--r--drivers/media/rc/Makefile51
-rw-r--r--drivers/media/rc/ati_remote.c431
-rw-r--r--drivers/media/rc/bpf-lirc.c342
-rw-r--r--drivers/media/rc/ene_ir.c169
-rw-r--r--drivers/media/rc/ene_ir.h20
-rw-r--r--drivers/media/rc/fintek-cir.c140
-rw-r--r--drivers/media/rc/fintek-cir.h22
-rw-r--r--drivers/media/rc/gpio-ir-recv.c245
-rw-r--r--drivers/media/rc/gpio-ir-tx.c205
-rw-r--r--drivers/media/rc/igorplugusb.c269
-rw-r--r--drivers/media/rc/iguanair.c567
-rw-r--r--drivers/media/rc/img-ir/Kconfig79
-rw-r--r--drivers/media/rc/img-ir/Makefile14
-rw-r--r--drivers/media/rc/img-ir/img-ir-core.c191
-rw-r--r--drivers/media/rc/img-ir/img-ir-hw.c1148
-rw-r--r--drivers/media/rc/img-ir/img-ir-hw.h297
-rw-r--r--drivers/media/rc/img-ir/img-ir-jvc.c84
-rw-r--r--drivers/media/rc/img-ir/img-ir-nec.c173
-rw-r--r--drivers/media/rc/img-ir/img-ir-raw.c151
-rw-r--r--drivers/media/rc/img-ir/img-ir-raw.h61
-rw-r--r--drivers/media/rc/img-ir/img-ir-rc5.c84
-rw-r--r--drivers/media/rc/img-ir/img-ir-rc6.c113
-rw-r--r--drivers/media/rc/img-ir/img-ir-sanyo.c125
-rw-r--r--drivers/media/rc/img-ir/img-ir-sharp.c102
-rw-r--r--drivers/media/rc/img-ir/img-ir-sony.c161
-rw-r--r--drivers/media/rc/img-ir/img-ir.h169
-rw-r--r--drivers/media/rc/imon.c963
-rw-r--r--drivers/media/rc/imon_raw.c217
-rw-r--r--drivers/media/rc/ir-hix5hd2.c404
-rw-r--r--drivers/media/rc/ir-imon-decoder.c267
-rw-r--r--drivers/media/rc/ir-jvc-decoder.c78
-rw-r--r--drivers/media/rc/ir-lirc-codec.c411
-rw-r--r--drivers/media/rc/ir-mce_kbd-decoder.c245
-rw-r--r--drivers/media/rc/ir-nec-decoder.c191
-rw-r--r--drivers/media/rc/ir-raw.c374
-rw-r--r--drivers/media/rc/ir-rc5-decoder.c218
-rw-r--r--drivers/media/rc/ir-rc5-sz-decoder.c154
-rw-r--r--drivers/media/rc/ir-rc6-decoder.c201
-rw-r--r--drivers/media/rc/ir-rcmm-decoder.c255
-rw-r--r--drivers/media/rc/ir-sanyo-decoder.c126
-rw-r--r--drivers/media/rc/ir-sharp-decoder.c244
-rw-r--r--drivers/media/rc/ir-sony-decoder.c108
-rw-r--r--drivers/media/rc/ir-spi.c195
-rw-r--r--drivers/media/rc/ir-xmp-decoder.c217
-rw-r--r--drivers/media/rc/ir_toy.c562
-rw-r--r--drivers/media/rc/ite-cir.c444
-rw-r--r--drivers/media/rc/ite-cir.h80
-rw-r--r--drivers/media/rc/keymaps/Kconfig5
-rw-r--r--drivers/media/rc/keymaps/Makefile69
-rw-r--r--drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c48
-rw-r--r--drivers/media/rc/keymaps/rc-alink-dtu-m.c44
-rw-r--r--drivers/media/rc/keymaps/rc-anysee.c44
-rw-r--r--drivers/media/rc/keymaps/rc-apac-viewcomp.c48
-rw-r--r--drivers/media/rc/keymaps/rc-astrometa-t2hybrid.c65
-rw-r--r--drivers/media/rc/keymaps/rc-asus-pc39.c48
-rw-r--r--drivers/media/rc/keymaps/rc-asus-ps3-100.c48
-rw-r--r--drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c28
-rw-r--r--drivers/media/rc/keymaps/rc-ati-x10.c126
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-a16d.c48
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-cardbus.c48
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-dvbt.c48
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-m135a.c71
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c37
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-rm-ks.c80
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia.c48
-rw-r--r--drivers/media/rc/keymaps/rc-avertv-303.c48
-rw-r--r--drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c44
-rw-r--r--drivers/media/rc/keymaps/rc-beelink-gs1.c85
-rw-r--r--drivers/media/rc/keymaps/rc-beelink-mxiii.c58
-rw-r--r--drivers/media/rc/keymaps/rc-behold-columbus.c58
-rw-r--r--drivers/media/rc/keymaps/rc-behold.c98
-rw-r--r--drivers/media/rc/keymaps/rc-budget-ci-old.c48
-rw-r--r--drivers/media/rc/keymaps/rc-cec.c172
-rw-r--r--drivers/media/rc/keymaps/rc-cinergy-1400.c48
-rw-r--r--drivers/media/rc/keymaps/rc-cinergy.c48
-rw-r--r--drivers/media/rc/keymaps/rc-ct-90405.c87
-rw-r--r--drivers/media/rc/keymaps/rc-d680-dmb.c73
-rw-r--r--drivers/media/rc/keymaps/rc-delock-61959.c79
-rw-r--r--drivers/media/rc/keymaps/rc-dib0700-nec.c78
-rw-r--r--drivers/media/rc/keymaps/rc-dib0700-rc5.c138
-rw-r--r--drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c44
-rw-r--r--drivers/media/rc/keymaps/rc-digittrade.c44
-rw-r--r--drivers/media/rc/keymaps/rc-dm1105-nec.c48
-rw-r--r--drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c48
-rw-r--r--drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c48
-rw-r--r--drivers/media/rc/keymaps/rc-dreambox.c152
-rw-r--r--drivers/media/rc/keymaps/rc-dtt200u.c56
-rw-r--r--drivers/media/rc/keymaps/rc-dvbsky.c74
-rw-r--r--drivers/media/rc/keymaps/rc-dvico-mce.c83
-rw-r--r--drivers/media/rc/keymaps/rc-dvico-portable.c74
-rw-r--r--drivers/media/rc/keymaps/rc-em-terratec.c48
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv-fm53.c50
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv.c48
-rw-r--r--drivers/media/rc/keymaps/rc-encore-enltv2.c50
-rw-r--r--drivers/media/rc/keymaps/rc-evga-indtube.c28
-rw-r--r--drivers/media/rc/keymaps/rc-eztv.c48
-rw-r--r--drivers/media/rc/keymaps/rc-flydvb.c48
-rw-r--r--drivers/media/rc/keymaps/rc-flyvideo.c48
-rw-r--r--drivers/media/rc/keymaps/rc-fusionhdtv-mce.c48
-rw-r--r--drivers/media/rc/keymaps/rc-gadmei-rm008z.c48
-rw-r--r--drivers/media/rc/keymaps/rc-geekbox.c50
-rw-r--r--drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c48
-rw-r--r--drivers/media/rc/keymaps/rc-gotview7135.c48
-rw-r--r--drivers/media/rc/keymaps/rc-hauppauge.c183
-rw-r--r--drivers/media/rc/keymaps/rc-hisi-poplar.c66
-rw-r--r--drivers/media/rc/keymaps/rc-hisi-tv-demo.c78
-rw-r--r--drivers/media/rc/keymaps/rc-imon-mce.c15
-rw-r--r--drivers/media/rc/keymaps/rc-imon-pad.c16
-rw-r--r--drivers/media/rc/keymaps/rc-imon-rsc.c83
-rw-r--r--drivers/media/rc/keymaps/rc-iodata-bctv7e.c48
-rw-r--r--drivers/media/rc/keymaps/rc-it913x-v1.c55
-rw-r--r--drivers/media/rc/keymaps/rc-it913x-v2.c55
-rw-r--r--drivers/media/rc/keymaps/rc-kaiomy.c50
-rw-r--r--drivers/media/rc/keymaps/rc-khadas.c55
-rw-r--r--drivers/media/rc/keymaps/rc-khamsin.c76
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-315u.c48
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-pc150u.c37
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c54
-rw-r--r--drivers/media/rc/keymaps/rc-leadtek-y04g0051.c44
-rw-r--r--drivers/media/rc/keymaps/rc-lirc.c42
-rw-r--r--drivers/media/rc/keymaps/rc-lme2510.c148
-rw-r--r--drivers/media/rc/keymaps/rc-manli.c50
-rw-r--r--drivers/media/rc/keymaps/rc-mecool-kii-pro.c92
-rw-r--r--drivers/media/rc/keymaps/rc-mecool-kiii-pro.c89
-rw-r--r--drivers/media/rc/keymaps/rc-medion-x10-digitainer.c43
-rw-r--r--drivers/media/rc/keymaps/rc-medion-x10-or2x.c43
-rw-r--r--drivers/media/rc/keymaps/rc-medion-x10.c44
-rw-r--r--drivers/media/rc/keymaps/rc-minix-neo.c56
-rw-r--r--drivers/media/rc/keymaps/rc-msi-digivox-ii.c60
-rw-r--r--drivers/media/rc/keymaps/rc-msi-digivox-iii.c44
-rw-r--r--drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c48
-rw-r--r--drivers/media/rc/keymaps/rc-msi-tvanywhere.c48
-rw-r--r--drivers/media/rc/keymaps/rc-mygica-utv3.c69
-rw-r--r--drivers/media/rc/keymaps/rc-nebula.c138
-rw-r--r--drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c70
-rw-r--r--drivers/media/rc/keymaps/rc-norwood.c48
-rw-r--r--drivers/media/rc/keymaps/rc-npgtech.c48
-rw-r--r--drivers/media/rc/keymaps/rc-odroid.c55
-rw-r--r--drivers/media/rc/keymaps/rc-pctv-sedna.c48
-rw-r--r--drivers/media/rc/keymaps/rc-pine64.c66
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-color.c48
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-grey.c48
-rw-r--r--drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c48
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-002t.c48
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-mk12.c48
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview-new.c50
-rw-r--r--drivers/media/rc/keymaps/rc-pixelview.c48
-rw-r--r--drivers/media/rc/keymaps/rc-powercolor-real-angel.c50
-rw-r--r--drivers/media/rc/keymaps/rc-proteus-2309.c48
-rw-r--r--drivers/media/rc/keymaps/rc-purpletv.c48
-rw-r--r--drivers/media/rc/keymaps/rc-pv951.c48
-rw-r--r--drivers/media/rc/keymaps/rc-rc6-mce.c16
-rw-r--r--drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c48
-rw-r--r--drivers/media/rc/keymaps/rc-reddo.c74
-rw-r--r--drivers/media/rc/keymaps/rc-siemens-gigaset-rc20.c71
-rw-r--r--drivers/media/rc/keymaps/rc-snapstream-firefly.c44
-rw-r--r--drivers/media/rc/keymaps/rc-streamzap.c19
-rw-r--r--drivers/media/rc/keymaps/rc-su3000.c72
-rw-r--r--drivers/media/rc/keymaps/rc-tanix-tx3mini.c78
-rw-r--r--drivers/media/rc/keymaps/rc-tanix-tx5max.c69
-rw-r--r--drivers/media/rc/keymaps/rc-tbs-nec.c48
-rw-r--r--drivers/media/rc/keymaps/rc-technisat-ts35.c73
-rw-r--r--drivers/media/rc/keymaps/rc-technisat-usb2.c33
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-cinergy-c-pci.c85
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-cinergy-s2-hd.c83
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c48
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-slim-2.c44
-rw-r--r--drivers/media/rc/keymaps/rc-terratec-slim.c44
-rw-r--r--drivers/media/rc/keymaps/rc-tevii-nec.c48
-rw-r--r--drivers/media/rc/keymaps/rc-tivo.c101
-rw-r--r--drivers/media/rc/keymaps/rc-total-media-in-hand-02.c74
-rw-r--r--drivers/media/rc/keymaps/rc-total-media-in-hand.c44
-rw-r--r--drivers/media/rc/keymaps/rc-trekstor.c44
-rw-r--r--drivers/media/rc/keymaps/rc-tt-1500.c48
-rw-r--r--drivers/media/rc/keymaps/rc-twinhan-dtv-cab-ci.c95
-rw-r--r--drivers/media/rc/keymaps/rc-twinhan1027.c30
-rw-r--r--drivers/media/rc/keymaps/rc-vega-s9x.c55
-rw-r--r--drivers/media/rc/keymaps/rc-videomate-m1f.c35
-rw-r--r--drivers/media/rc/keymaps/rc-videomate-s350.c48
-rw-r--r--drivers/media/rc/keymaps/rc-videomate-tv-pvr.c48
-rw-r--r--drivers/media/rc/keymaps/rc-videostrong-kii-pro.c84
-rw-r--r--drivers/media/rc/keymaps/rc-wetek-hub.c54
-rw-r--r--drivers/media/rc/keymaps/rc-wetek-play2.c94
-rw-r--r--drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c50
-rw-r--r--drivers/media/rc/keymaps/rc-winfast.c48
-rw-r--r--drivers/media/rc/keymaps/rc-x96max.c84
-rw-r--r--drivers/media/rc/keymaps/rc-xbox-360.c84
-rw-r--r--drivers/media/rc/keymaps/rc-xbox-dvd.c64
-rw-r--r--drivers/media/rc/keymaps/rc-zx-irdec.c77
-rw-r--r--drivers/media/rc/lirc_dev.c1313
-rw-r--r--drivers/media/rc/mceusb.c1309
-rw-r--r--drivers/media/rc/meson-ir-tx.c388
-rw-r--r--drivers/media/rc/meson-ir.c644
-rw-r--r--drivers/media/rc/mtk-cir.c454
-rw-r--r--drivers/media/rc/nuvoton-cir.c1089
-rw-r--r--drivers/media/rc/nuvoton-cir.h116
-rw-r--r--drivers/media/rc/pwm-ir-tx.c200
-rw-r--r--drivers/media/rc/rc-core-priv.h322
-rw-r--r--drivers/media/rc/rc-ir-raw.c718
-rw-r--r--drivers/media/rc/rc-loopback.c165
-rw-r--r--drivers/media/rc/rc-main.c1623
-rw-r--r--drivers/media/rc/redrat3.c1140
-rw-r--r--drivers/media/rc/serial_ir.c845
-rw-r--r--drivers/media/rc/st_rc.c418
-rw-r--r--drivers/media/rc/streamzap.c274
-rw-r--r--drivers/media/rc/sunxi-cir.c429
-rw-r--r--drivers/media/rc/ttusbir.c434
-rw-r--r--drivers/media/rc/winbond-cir.c520
-rw-r--r--drivers/media/rc/xbox_remote.c304
-rw-r--r--drivers/media/spi/Kconfig26
-rw-r--r--drivers/media/spi/Makefile8
-rw-r--r--drivers/media/spi/cxd2880-spi.c667
-rw-r--r--drivers/media/spi/gs1662.c483
-rw-r--r--drivers/media/test-drivers/Kconfig40
-rw-r--r--drivers/media/test-drivers/Makefile15
-rw-r--r--drivers/media/test-drivers/vicodec/Kconfig14
-rw-r--r--drivers/media/test-drivers/vicodec/Makefile4
-rw-r--r--drivers/media/test-drivers/vicodec/codec-fwht.c959
-rw-r--r--drivers/media/test-drivers/vicodec/codec-fwht.h118
-rw-r--r--drivers/media/test-drivers/vicodec/codec-v4l2-fwht.c367
-rw-r--r--drivers/media/test-drivers/vicodec/codec-v4l2-fwht.h64
-rw-r--r--drivers/media/test-drivers/vicodec/vicodec-core.c2239
-rw-r--r--drivers/media/test-drivers/vidtv/Kconfig10
-rw-r--r--drivers/media/test-drivers/vidtv/Makefile9
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_bridge.c612
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_bridge.h72
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_channel.c549
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_channel.h81
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_common.c89
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_common.h32
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_demod.c461
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_demod.h68
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_encoder.h165
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_mux.c551
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_mux.h182
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_pes.c426
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_pes.h193
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_psi.c2053
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_psi.h809
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_s302m.c524
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_s302m.h97
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_ts.c136
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_ts.h106
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_tuner.c436
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_tuner.h44
-rw-r--r--drivers/media/test-drivers/vim2m.c1615
-rw-r--r--drivers/media/test-drivers/vimc/Kconfig20
-rw-r--r--drivers/media/test-drivers/vimc/Makefile6
-rw-r--r--drivers/media/test-drivers/vimc/vimc-capture.c493
-rw-r--r--drivers/media/test-drivers/vimc/vimc-common.c423
-rw-r--r--drivers/media/test-drivers/vimc/vimc-common.h243
-rw-r--r--drivers/media/test-drivers/vimc/vimc-core.c451
-rw-r--r--drivers/media/test-drivers/vimc/vimc-debayer.c612
-rw-r--r--drivers/media/test-drivers/vimc/vimc-lens.c103
-rw-r--r--drivers/media/test-drivers/vimc/vimc-scaler.c422
-rw-r--r--drivers/media/test-drivers/vimc/vimc-sensor.c445
-rw-r--r--drivers/media/test-drivers/vimc/vimc-streamer.c244
-rw-r--r--drivers/media/test-drivers/vimc/vimc-streamer.h45
-rw-r--r--drivers/media/test-drivers/visl/Kconfig28
-rw-r--r--drivers/media/test-drivers/visl/Makefile8
-rw-r--r--drivers/media/test-drivers/visl/visl-core.c576
-rw-r--r--drivers/media/test-drivers/visl/visl-debugfs.c112
-rw-r--r--drivers/media/test-drivers/visl/visl-debugfs.h40
-rw-r--r--drivers/media/test-drivers/visl/visl-dec.c648
-rw-r--r--drivers/media/test-drivers/visl/visl-dec.h75
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-av1.h314
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-fwht.h66
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-h264.h349
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-hevc.h405
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-mpeg2.h99
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-points.c11
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-vp8.h156
-rw-r--r--drivers/media/test-drivers/visl/visl-trace-vp9.h292
-rw-r--r--drivers/media/test-drivers/visl/visl-video.c803
-rw-r--r--drivers/media/test-drivers/visl/visl-video.h28
-rw-r--r--drivers/media/test-drivers/visl/visl.h173
-rw-r--r--drivers/media/test-drivers/vivid/Kconfig49
-rw-r--r--drivers/media/test-drivers/vivid/Makefile15
-rw-r--r--drivers/media/test-drivers/vivid/vivid-cec.c415
-rw-r--r--drivers/media/test-drivers/vivid/vivid-cec.h13
-rw-r--r--drivers/media/test-drivers/vivid/vivid-core.c2338
-rw-r--r--drivers/media/test-drivers/vivid/vivid-core.h706
-rw-r--r--drivers/media/test-drivers/vivid/vivid-ctrls.c2107
-rw-r--r--drivers/media/test-drivers/vivid/vivid-ctrls.h22
-rw-r--r--drivers/media/test-drivers/vivid/vivid-kthread-cap.c915
-rw-r--r--drivers/media/test-drivers/vivid/vivid-kthread-cap.h14
-rw-r--r--drivers/media/test-drivers/vivid/vivid-kthread-out.c362
-rw-r--r--drivers/media/test-drivers/vivid/vivid-kthread-out.h14
-rw-r--r--drivers/media/test-drivers/vivid/vivid-kthread-touch.c196
-rw-r--r--drivers/media/test-drivers/vivid/vivid-kthread-touch.h13
-rw-r--r--drivers/media/test-drivers/vivid/vivid-meta-cap.c196
-rw-r--r--drivers/media/test-drivers/vivid/vivid-meta-cap.h29
-rw-r--r--drivers/media/test-drivers/vivid/vivid-meta-out.c170
-rw-r--r--drivers/media/test-drivers/vivid/vivid-meta-out.h25
-rw-r--r--drivers/media/test-drivers/vivid/vivid-osd.c401
-rw-r--r--drivers/media/test-drivers/vivid/vivid-osd.h30
-rw-r--r--drivers/media/test-drivers/vivid/vivid-radio-common.c177
-rw-r--r--drivers/media/test-drivers/vivid/vivid-radio-common.h28
-rw-r--r--drivers/media/test-drivers/vivid/vivid-radio-rx.c278
-rw-r--r--drivers/media/test-drivers/vivid/vivid-radio-rx.h19
-rw-r--r--drivers/media/test-drivers/vivid/vivid-radio-tx.c128
-rw-r--r--drivers/media/test-drivers/vivid/vivid-radio-tx.h17
-rw-r--r--drivers/media/test-drivers/vivid/vivid-rds-gen.c157
-rw-r--r--drivers/media/test-drivers/vivid/vivid-rds-gen.h42
-rw-r--r--drivers/media/test-drivers/vivid/vivid-sdr-cap.c582
-rw-r--r--drivers/media/test-drivers/vivid/vivid-sdr-cap.h24
-rw-r--r--drivers/media/test-drivers/vivid/vivid-touch-cap.c336
-rw-r--r--drivers/media/test-drivers/vivid/vivid-touch-cap.h39
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-cap.c359
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-cap.h28
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-gen.c306
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-gen.h21
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-out.c247
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-out.h22
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-cap.c1758
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-cap.h58
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-common.c1137
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-common.h41
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-out.c1123
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-out.h44
-rw-r--r--drivers/media/tuners/Kconfig300
-rw-r--r--drivers/media/tuners/Makefile48
-rw-r--r--drivers/media/tuners/e4000.c741
-rw-r--r--drivers/media/tuners/e4000.h29
-rw-r--r--drivers/media/tuners/e4000_priv.h219
-rw-r--r--drivers/media/tuners/fc0011.c (renamed from drivers/media/common/tuners/fc0011.c)56
-rw-r--r--drivers/media/tuners/fc0011.h (renamed from drivers/media/common/tuners/fc0011.h)6
-rw-r--r--drivers/media/tuners/fc0012-priv.h19
-rw-r--r--drivers/media/tuners/fc0012.c (renamed from drivers/media/common/tuners/fc0012.c)144
-rw-r--r--drivers/media/tuners/fc0012.h52
-rw-r--r--drivers/media/tuners/fc0013-priv.h30
-rw-r--r--drivers/media/tuners/fc0013.c (renamed from drivers/media/common/tuners/fc0013.c)98
-rw-r--r--drivers/media/tuners/fc0013.h31
-rw-r--r--drivers/media/tuners/fc001x-common.h26
-rw-r--r--drivers/media/tuners/fc2580.c622
-rw-r--r--drivers/media/tuners/fc2580.h33
-rw-r--r--drivers/media/tuners/fc2580_priv.h134
-rw-r--r--drivers/media/tuners/it913x.c455
-rw-r--r--drivers/media/tuners/it913x.h30
-rw-r--r--drivers/media/tuners/m88rs6000t.c730
-rw-r--r--drivers/media/tuners/m88rs6000t.h20
-rw-r--r--drivers/media/tuners/max2165.c (renamed from drivers/media/common/tuners/max2165.c)34
-rw-r--r--drivers/media/tuners/max2165.h33
-rw-r--r--drivers/media/tuners/max2165_priv.h46
-rw-r--r--drivers/media/tuners/mc44s803.c (renamed from drivers/media/common/tuners/mc44s803.c)45
-rw-r--r--drivers/media/tuners/mc44s803.h31
-rw-r--r--drivers/media/tuners/mc44s803_priv.h (renamed from drivers/media/common/tuners/mc44s803_priv.h)16
-rw-r--r--drivers/media/tuners/msi001.c510
-rw-r--r--drivers/media/tuners/mt2060.c536
-rw-r--r--drivers/media/tuners/mt2060.h52
-rw-r--r--drivers/media/tuners/mt2060_priv.h (renamed from drivers/media/common/tuners/mt2060_priv.h)27
-rw-r--r--drivers/media/tuners/mt2063.c (renamed from drivers/media/common/tuners/mt2063.c)152
-rw-r--r--drivers/media/tuners/mt2063.h29
-rw-r--r--drivers/media/tuners/mt20xx.c (renamed from drivers/media/common/tuners/mt20xx.c)40
-rw-r--r--drivers/media/tuners/mt20xx.h25
-rw-r--r--drivers/media/tuners/mt2131.c (renamed from drivers/media/common/tuners/mt2131.c)36
-rw-r--r--drivers/media/tuners/mt2131.h35
-rw-r--r--drivers/media/tuners/mt2131_priv.h29
-rw-r--r--drivers/media/tuners/mt2266.c (renamed from drivers/media/common/tuners/mt2266.c)26
-rw-r--r--drivers/media/tuners/mt2266.h28
-rw-r--r--drivers/media/tuners/mxl301rf.c338
-rw-r--r--drivers/media/tuners/mxl301rf.h17
-rw-r--r--drivers/media/tuners/mxl5005s.c (renamed from drivers/media/common/tuners/mxl5005s.c)106
-rw-r--r--drivers/media/tuners/mxl5005s.h (renamed from drivers/media/common/tuners/mxl5005s.h)19
-rw-r--r--drivers/media/tuners/mxl5007t.c (renamed from drivers/media/common/tuners/mxl5007t.c)70
-rw-r--r--drivers/media/tuners/mxl5007t.h82
-rw-r--r--drivers/media/tuners/qm1d1b0004.c265
-rw-r--r--drivers/media/tuners/qm1d1b0004.h24
-rw-r--r--drivers/media/tuners/qm1d1c0042.c455
-rw-r--r--drivers/media/tuners/qm1d1c0042.h28
-rw-r--r--drivers/media/tuners/qt1010.c (renamed from drivers/media/common/tuners/qt1010.c)134
-rw-r--r--drivers/media/tuners/qt1010.h40
-rw-r--r--drivers/media/tuners/qt1010_priv.h93
-rw-r--r--drivers/media/tuners/r820t.c2406
-rw-r--r--drivers/media/tuners/r820t.h45
-rw-r--r--drivers/media/tuners/si2157.c1015
-rw-r--r--drivers/media/tuners/si2157.h45
-rw-r--r--drivers/media/tuners/si2157_priv.h94
-rw-r--r--drivers/media/tuners/tda18212.c275
-rw-r--r--drivers/media/tuners/tda18212.h31
-rw-r--r--drivers/media/tuners/tda18218.c (renamed from drivers/media/common/tuners/tda18218.c)97
-rw-r--r--drivers/media/tuners/tda18218.h31
-rw-r--r--drivers/media/tuners/tda18218_priv.h84
-rw-r--r--drivers/media/tuners/tda18250.c889
-rw-r--r--drivers/media/tuners/tda18250.h42
-rw-r--r--drivers/media/tuners/tda18250_priv.h136
-rw-r--r--drivers/media/tuners/tda18271-common.c (renamed from drivers/media/common/tuners/tda18271-common.c)163
-rw-r--r--drivers/media/tuners/tda18271-fe.c (renamed from drivers/media/common/tuners/tda18271-fe.c)83
-rw-r--r--drivers/media/tuners/tda18271-maps.c (renamed from drivers/media/common/tuners/tda18271-maps.c)38
-rw-r--r--drivers/media/tuners/tda18271-priv.h (renamed from drivers/media/common/tuners/tda18271-priv.h)28
-rw-r--r--drivers/media/tuners/tda18271.h127
-rw-r--r--drivers/media/tuners/tda827x.c (renamed from drivers/media/common/tuners/tda827x.c)52
-rw-r--r--drivers/media/tuners/tda827x.h56
-rw-r--r--drivers/media/tuners/tda8290.c (renamed from drivers/media/common/tuners/tda8290.c)183
-rw-r--r--drivers/media/tuners/tda8290.h54
-rw-r--r--drivers/media/tuners/tda9887.c (renamed from drivers/media/common/tuners/tda9887.c)34
-rw-r--r--drivers/media/tuners/tda9887.h26
-rw-r--r--drivers/media/tuners/tea5761.c (renamed from drivers/media/common/tuners/tea5761.c)33
-rw-r--r--drivers/media/tuners/tea5761.h35
-rw-r--r--drivers/media/tuners/tea5767.c (renamed from drivers/media/common/tuners/tea5767.c)42
-rw-r--r--drivers/media/tuners/tea5767.h54
-rw-r--r--drivers/media/tuners/tua9001.c267
-rw-r--r--drivers/media/tuners/tua9001.h46
-rw-r--r--drivers/media/tuners/tua9001_priv.h26
-rw-r--r--drivers/media/tuners/tuner-i2c.h (renamed from drivers/media/common/tuners/tuner-i2c.h)26
-rw-r--r--drivers/media/tuners/tuner-simple.c (renamed from drivers/media/common/tuners/tuner-simple.c)99
-rw-r--r--drivers/media/tuners/tuner-simple.h27
-rw-r--r--drivers/media/tuners/tuner-types.c (renamed from drivers/media/common/tuners/tuner-types.c)377
-rw-r--r--drivers/media/tuners/xc2028-types.h142
-rw-r--r--drivers/media/tuners/xc2028.c1522
-rw-r--r--drivers/media/tuners/xc2028.h73
-rw-r--r--drivers/media/tuners/xc4000.c (renamed from drivers/media/common/tuners/xc4000.c)215
-rw-r--r--drivers/media/tuners/xc4000.h53
-rw-r--r--drivers/media/tuners/xc5000.c1464
-rw-r--r--drivers/media/tuners/xc5000.h60
-rw-r--r--drivers/media/usb/Kconfig72
-rw-r--r--drivers/media/usb/Makefile32
-rw-r--r--drivers/media/usb/airspy/Kconfig11
-rw-r--r--drivers/media/usb/airspy/Makefile2
-rw-r--r--drivers/media/usb/airspy/airspy.c1110
-rw-r--r--drivers/media/usb/as102/Kconfig9
-rw-r--r--drivers/media/usb/as102/Makefile7
-rw-r--r--drivers/media/usb/as102/as102_drv.c393
-rw-r--r--drivers/media/usb/as102/as102_drv.h74
-rw-r--r--drivers/media/usb/as102/as102_fw.c226
-rw-r--r--drivers/media/usb/as102/as102_fw.h25
-rw-r--r--drivers/media/usb/as102/as102_usb_drv.c462
-rw-r--r--drivers/media/usb/as102/as102_usb_drv.h48
-rw-r--r--drivers/media/usb/as102/as10x_cmd.c404
-rw-r--r--drivers/media/usb/as102/as10x_cmd.h514
-rw-r--r--drivers/media/usb/as102/as10x_cmd_cfg.c192
-rw-r--r--drivers/media/usb/as102/as10x_cmd_stream.c198
-rw-r--r--drivers/media/usb/as102/as10x_handle.h42
-rw-r--r--drivers/media/usb/au0828/Kconfig41
-rw-r--r--drivers/media/usb/au0828/Makefile17
-rw-r--r--drivers/media/usb/au0828/au0828-cards.c (renamed from drivers/media/video/au0828/au0828-cards.c)113
-rw-r--r--drivers/media/usb/au0828/au0828-cards.h13
-rw-r--r--drivers/media/usb/au0828/au0828-core.c848
-rw-r--r--drivers/media/usb/au0828/au0828-dvb.c684
-rw-r--r--drivers/media/usb/au0828/au0828-i2c.c (renamed from drivers/media/video/au0828/au0828-i2c.c)93
-rw-r--r--drivers/media/usb/au0828/au0828-input.c398
-rw-r--r--drivers/media/usb/au0828/au0828-reg.h52
-rw-r--r--drivers/media/usb/au0828/au0828-vbi.c77
-rw-r--r--drivers/media/usb/au0828/au0828-video.c2039
-rw-r--r--drivers/media/usb/au0828/au0828.h373
-rw-r--r--drivers/media/usb/b2c2/Kconfig16
-rw-r--r--drivers/media/usb/b2c2/Makefile5
-rw-r--r--drivers/media/usb/b2c2/flexcop-usb.c (renamed from drivers/media/dvb/b2c2/flexcop-usb.c)225
-rw-r--r--drivers/media/usb/b2c2/flexcop-usb.h (renamed from drivers/media/dvb/b2c2/flexcop-usb.h)19
-rw-r--r--drivers/media/usb/cx231xx/Kconfig59
-rw-r--r--drivers/media/usb/cx231xx/Makefile13
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-417.c1791
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-audio.c703
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-avcore.c (renamed from drivers/media/video/cx231xx/cx231xx-avcore.c)633
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c1983
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-conf-reg.h (renamed from drivers/media/video/cx231xx/cx231xx-conf-reg.h)14
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-core.c (renamed from drivers/media/video/cx231xx/cx231xx-core.c)367
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-dif.h (renamed from drivers/media/video/cx231xx/cx231xx-dif.h)15
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-dvb.c1175
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-i2c.c (renamed from drivers/media/video/cx231xx/cx231xx-i2c.c)191
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-input.c101
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c (renamed from drivers/media/video/cx231xx/cx231xx-pcb-cfg.c)75
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h (renamed from drivers/media/video/cx231xx/cx231xx-pcb-cfg.h)43
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-reg.h (renamed from drivers/media/video/cx231xx/cx231xx-reg.h)34
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-vbi.c659
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-vbi.h53
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-video.c1921
-rw-r--r--drivers/media/usb/cx231xx/cx231xx.h (renamed from drivers/media/video/cx231xx/cx231xx.h)210
-rw-r--r--drivers/media/usb/dvb-usb-v2/Kconfig178
-rw-r--r--drivers/media/usb/dvb-usb-v2/Makefile49
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9015.c1556
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9015.h131
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9035.c2203
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9035.h156
-rw-r--r--drivers/media/usb/dvb-usb-v2/anysee.c1405
-rw-r--r--drivers/media/usb/dvb-usb-v2/anysee.h (renamed from drivers/media/dvb/dvb-usb/anysee.h)33
-rw-r--r--drivers/media/usb/dvb-usb-v2/au6610.c200
-rw-r--r--drivers/media/usb/dvb-usb-v2/au6610.h19
-rw-r--r--drivers/media/usb/dvb-usb-v2/az6007.c992
-rw-r--r--drivers/media/usb/dvb-usb-v2/ce6230.c286
-rw-r--r--drivers/media/usb/dvb-usb-v2/ce6230.h36
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb.h417
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb_common.h22
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb_core.c1126
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c96
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvbsky.c829
-rw-r--r--drivers/media/usb/dvb-usb-v2/ec168.c378
-rw-r--r--drivers/media/usb/dvb-usb-v2/ec168.h39
-rw-r--r--drivers/media/usb/dvb-usb-v2/gl861.c578
-rw-r--r--drivers/media/usb/dvb-usb-v2/lmedm04.c1274
-rw-r--r--drivers/media/usb/dvb-usb-v2/lmedm04.h (renamed from drivers/media/dvb/dvb-usb/lmedm04.h)6
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c (renamed from drivers/media/dvb/dvb-usb/mxl111sf-demod.c)64
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h35
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c (renamed from drivers/media/dvb/dvb-usb/mxl111sf-gpio.c)27
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h37
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c (renamed from drivers/media/dvb/dvb-usb/mxl111sf-i2c.c)40
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h16
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c (renamed from drivers/media/dvb/dvb-usb/mxl111sf-phy.c)23
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h34
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h (renamed from drivers/media/dvb/dvb-usb/mxl111sf-reg.h)23
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c (renamed from drivers/media/dvb/dvb-usb/mxl111sf-tuner.c)50
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h65
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf.c1466
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf.h (renamed from drivers/media/dvb/dvb-usb/mxl111sf.h)56
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.c2013
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.h (renamed from drivers/media/dvb/dvb-usb/rtl28xxu.h)89
-rw-r--r--drivers/media/usb/dvb-usb-v2/usb_urb.c353
-rw-r--r--drivers/media/usb/dvb-usb-v2/zd1301.c289
-rw-r--r--drivers/media/usb/dvb-usb/Kconfig349
-rw-r--r--drivers/media/usb/dvb-usb/Makefile86
-rw-r--r--drivers/media/usb/dvb-usb/a800.c159
-rw-r--r--drivers/media/usb/dvb-usb/af9005-fe.c (renamed from drivers/media/dvb/dvb-usb/af9005-fe.c)41
-rw-r--r--drivers/media/usb/dvb-usb/af9005-remote.c (renamed from drivers/media/dvb/dvb-usb/af9005-remote.c)17
-rw-r--r--drivers/media/usb/dvb-usb/af9005-script.h (renamed from drivers/media/dvb/dvb-usb/af9005-script.h)1
-rw-r--r--drivers/media/usb/dvb-usb/af9005.c (renamed from drivers/media/dvb/dvb-usb/af9005.c)366
-rw-r--r--drivers/media/usb/dvb-usb/af9005.h (renamed from drivers/media/dvb/dvb-usb/af9005.h)17
-rw-r--r--drivers/media/usb/dvb-usb/az6027.c (renamed from drivers/media/dvb/dvb-usb/az6027.c)349
-rw-r--r--drivers/media/usb/dvb-usb/az6027.h (renamed from drivers/media/dvb/dvb-usb/az6027.h)1
-rw-r--r--drivers/media/usb/dvb-usb/cinergyT2-core.c265
-rw-r--r--drivers/media/usb/dvb-usb/cinergyT2-fe.c317
-rw-r--r--drivers/media/usb/dvb-usb/cinergyT2.h (renamed from drivers/media/dvb/dvb-usb/cinergyT2.h)16
-rw-r--r--drivers/media/usb/dvb-usb/cxusb-analog.c1836
-rw-r--r--drivers/media/usb/dvb-usb/cxusb.c2400
-rw-r--r--drivers/media/usb/dvb-usb/cxusb.h204
-rw-r--r--drivers/media/usb/dvb-usb/dib0700.h78
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_core.c (renamed from drivers/media/dvb/dvb-usb/dib0700_core.c)285
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_devices.c5248
-rw-r--r--drivers/media/usb/dvb-usb/dib07x0.h (renamed from drivers/media/dvb/dvb-usb/dib07x0.h)1
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-common.c400
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-mb.c489
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-mc-common.c167
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-mc.c166
-rw-r--r--drivers/media/usb/dvb-usb/dibusb.h (renamed from drivers/media/dvb/dvb-usb/dibusb.h)14
-rw-r--r--drivers/media/usb/dvb-usb/digitv.c (renamed from drivers/media/dvb/dvb-usb/digitv.c)96
-rw-r--r--drivers/media/usb/dvb-usb/digitv.h (renamed from drivers/media/dvb/dvb-usb/digitv.h)6
-rw-r--r--drivers/media/usb/dvb-usb/dtt200u-fe.c257
-rw-r--r--drivers/media/usb/dvb-usb/dtt200u.c432
-rw-r--r--drivers/media/usb/dvb-usb/dtt200u.h (renamed from drivers/media/dvb/dvb-usb/dtt200u.h)9
-rw-r--r--drivers/media/usb/dvb-usb/dtv5100.c (renamed from drivers/media/dvb/dvb-usb/dtv5100.c)52
-rw-r--r--drivers/media/usb/dvb-usb/dtv5100.h38
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-common.h (renamed from drivers/media/dvb/dvb-usb/dvb-usb-common.h)6
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-dvb.c (renamed from drivers/media/dvb/dvb-usb/dvb-usb-dvb.c)93
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-firmware.c155
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-i2c.c49
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-init.c350
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-remote.c (renamed from drivers/media/dvb/dvb-usb/dvb-usb-remote.c)16
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-urb.c (renamed from drivers/media/dvb/dvb-usb/dvb-usb-urb.c)5
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb.h (renamed from drivers/media/dvb/dvb-usb/dvb-usb.h)152
-rw-r--r--drivers/media/usb/dvb-usb/dw2102.c2661
-rw-r--r--drivers/media/usb/dvb-usb/dw2102.h (renamed from drivers/media/dvb/dvb-usb/dw2102.h)1
-rw-r--r--drivers/media/usb/dvb-usb/gp8psk.c400
-rw-r--r--drivers/media/usb/dvb-usb/gp8psk.h34
-rw-r--r--drivers/media/usb/dvb-usb/m920x.c (renamed from drivers/media/dvb/dvb-usb/m920x.c)386
-rw-r--r--drivers/media/usb/dvb-usb/m920x.h (renamed from drivers/media/dvb/dvb-usb/m920x.h)1
-rw-r--r--drivers/media/usb/dvb-usb/nova-t-usb2.c (renamed from drivers/media/dvb/dvb-usb/nova-t-usb2.c)60
-rw-r--r--drivers/media/usb/dvb-usb/opera1.c (renamed from drivers/media/dvb/dvb-usb/opera1.c)54
-rw-r--r--drivers/media/usb/dvb-usb/pctv452e.c (renamed from drivers/media/dvb/dvb-usb/pctv452e.c)197
-rw-r--r--drivers/media/usb/dvb-usb/technisat-usb2.c (renamed from drivers/media/dvb/dvb-usb/technisat-usb2.c)154
-rw-r--r--drivers/media/usb/dvb-usb/ttusb2.c (renamed from drivers/media/dvb/dvb-usb/ttusb2.c)83
-rw-r--r--drivers/media/usb/dvb-usb/ttusb2.h (renamed from drivers/media/dvb/dvb-usb/ttusb2.h)7
-rw-r--r--drivers/media/usb/dvb-usb/umt-010.c (renamed from drivers/media/dvb/dvb-usb/umt-010.c)29
-rw-r--r--drivers/media/usb/dvb-usb/usb-urb.c (renamed from drivers/media/dvb/dvb-usb/usb-urb.c)13
-rw-r--r--drivers/media/usb/dvb-usb/vp702x-fe.c (renamed from drivers/media/dvb/dvb-usb/vp702x-fe.c)38
-rw-r--r--drivers/media/usb/dvb-usb/vp702x.c (renamed from drivers/media/dvb/dvb-usb/vp702x.c)61
-rw-r--r--drivers/media/usb/dvb-usb/vp702x.h (renamed from drivers/media/dvb/dvb-usb/vp702x.h)1
-rw-r--r--drivers/media/usb/dvb-usb/vp7045-fe.c (renamed from drivers/media/dvb/dvb-usb/vp7045-fe.c)23
-rw-r--r--drivers/media/usb/dvb-usb/vp7045.c256
-rw-r--r--drivers/media/usb/dvb-usb/vp7045.h (renamed from drivers/media/dvb/dvb-usb/vp7045.h)9
-rw-r--r--drivers/media/usb/em28xx/Kconfig84
-rw-r--r--drivers/media/usb/em28xx/Makefile15
-rw-r--r--drivers/media/usb/em28xx/em28xx-audio.c975
-rw-r--r--drivers/media/usb/em28xx/em28xx-camera.c412
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c4243
-rw-r--r--drivers/media/usb/em28xx/em28xx-core.c1173
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c2155
-rw-r--r--drivers/media/usb/em28xx/em28xx-i2c.c1029
-rw-r--r--drivers/media/usb/em28xx/em28xx-input.c933
-rw-r--r--drivers/media/usb/em28xx/em28xx-reg.h301
-rw-r--r--drivers/media/usb/em28xx/em28xx-v4l.h11
-rw-r--r--drivers/media/usb/em28xx/em28xx-vbi.c87
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c2926
-rw-r--r--drivers/media/usb/em28xx/em28xx.h846
-rw-r--r--drivers/media/usb/go7007/Kconfig52
-rw-r--r--drivers/media/usb/go7007/Makefile12
-rw-r--r--drivers/media/usb/go7007/go7007-driver.c740
-rw-r--r--drivers/media/usb/go7007/go7007-fw.c1623
-rw-r--r--drivers/media/usb/go7007/go7007-i2c.c216
-rw-r--r--drivers/media/usb/go7007/go7007-loader.c133
-rw-r--r--drivers/media/usb/go7007/go7007-priv.h298
-rw-r--r--drivers/media/usb/go7007/go7007-usb.c1356
-rw-r--r--drivers/media/usb/go7007/go7007-v4l2.c1147
-rw-r--r--drivers/media/usb/go7007/s2250-board.c628
-rw-r--r--drivers/media/usb/go7007/snd-go7007.c271
-rw-r--r--drivers/media/usb/gspca/Kconfig455
-rw-r--r--drivers/media/usb/gspca/Makefile100
-rw-r--r--drivers/media/usb/gspca/autogain_functions.c165
-rw-r--r--drivers/media/usb/gspca/benq.c (renamed from drivers/media/video/gspca/benq.c)42
-rw-r--r--drivers/media/usb/gspca/conex.c (renamed from drivers/media/video/gspca/conex.c)245
-rw-r--r--drivers/media/usb/gspca/cpia1.c (renamed from drivers/media/video/gspca/cpia1.c)623
-rw-r--r--drivers/media/usb/gspca/dtcs033.c430
-rw-r--r--drivers/media/usb/gspca/etoms.c (renamed from drivers/media/video/gspca/etoms.c)282
-rw-r--r--drivers/media/usb/gspca/finepix.c (renamed from drivers/media/video/gspca/finepix.c)48
-rw-r--r--drivers/media/usb/gspca/gl860/Kconfig9
-rw-r--r--drivers/media/usb/gspca/gl860/Makefile11
-rw-r--r--drivers/media/usb/gspca/gl860/gl860-mi1320.c (renamed from drivers/media/video/gspca/gl860/gl860-mi1320.c)101
-rw-r--r--drivers/media/usb/gspca/gl860/gl860-mi2020.c793
-rw-r--r--drivers/media/usb/gspca/gl860/gl860-ov2640.c (renamed from drivers/media/video/gspca/gl860/gl860-ov2640.c)14
-rw-r--r--drivers/media/usb/gspca/gl860/gl860-ov9655.c373
-rw-r--r--drivers/media/usb/gspca/gl860/gl860.c (renamed from drivers/media/video/gspca/gl860/gl860.c)280
-rw-r--r--drivers/media/usb/gspca/gl860/gl860.h (renamed from drivers/media/video/gspca/gl860/gl860.h)14
-rw-r--r--drivers/media/usb/gspca/gspca.c1719
-rw-r--r--drivers/media/usb/gspca/gspca.h243
-rw-r--r--drivers/media/usb/gspca/jeilinj.c537
-rw-r--r--drivers/media/usb/gspca/jl2005bcd.c (renamed from drivers/media/video/gspca/jl2005bcd.c)124
-rw-r--r--drivers/media/usb/gspca/jpeg.h (renamed from drivers/media/video/gspca/jpeg.h)20
-rw-r--r--drivers/media/usb/gspca/kinect.c474
-rw-r--r--drivers/media/usb/gspca/konica.c478
-rw-r--r--drivers/media/usb/gspca/m5602/Kconfig10
-rw-r--r--drivers/media/usb/gspca/m5602/Makefile12
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_bridge.h (renamed from drivers/media/video/gspca/m5602/m5602_bridge.h)48
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_core.c (renamed from drivers/media/video/gspca/m5602/m5602_core.c)87
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_mt9m111.c600
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_mt9m111.h125
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_ov7660.c468
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_ov7660.h105
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_ov9650.c784
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_ov9650.h155
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_po1030.c623
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_po1030.h166
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_s5k4aa.c759
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_s5k4aa.h84
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_s5k83a.c576
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_s5k83a.h60
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_sensor.h (renamed from drivers/media/video/gspca/m5602/m5602_sensor.h)9
-rw-r--r--drivers/media/usb/gspca/mars.c (renamed from drivers/media/video/gspca/mars.c)74
-rw-r--r--drivers/media/usb/gspca/mr97310a.c1077
-rw-r--r--drivers/media/usb/gspca/nw80x.c (renamed from drivers/media/video/gspca/nw80x.c)258
-rw-r--r--drivers/media/usb/gspca/ov519.c (renamed from drivers/media/video/gspca/ov519.c)981
-rw-r--r--drivers/media/usb/gspca/ov534.c1608
-rw-r--r--drivers/media/usb/gspca/ov534_9.c1821
-rw-r--r--drivers/media/usb/gspca/pac207.c (renamed from drivers/media/video/gspca/pac207.c)77
-rw-r--r--drivers/media/usb/gspca/pac7302.c951
-rw-r--r--drivers/media/usb/gspca/pac7311.c (renamed from drivers/media/video/gspca/pac7311.c)31
-rw-r--r--drivers/media/usb/gspca/pac_common.h119
-rw-r--r--drivers/media/usb/gspca/se401.c (renamed from drivers/media/video/gspca/se401.c)215
-rw-r--r--drivers/media/usb/gspca/se401.h (renamed from drivers/media/video/gspca/se401.h)16
-rw-r--r--drivers/media/usb/gspca/sn9c2028.c963
-rw-r--r--drivers/media/usb/gspca/sn9c2028.h48
-rw-r--r--drivers/media/usb/gspca/sn9c20x.c (renamed from drivers/media/video/gspca/sn9c20x.c)169
-rw-r--r--drivers/media/usb/gspca/sonixb.c (renamed from drivers/media/video/gspca/sonixb.c)690
-rw-r--r--drivers/media/usb/gspca/sonixj.c (renamed from drivers/media/video/gspca/sonixj.c)658
-rw-r--r--drivers/media/usb/gspca/spca1528.c438
-rw-r--r--drivers/media/usb/gspca/spca500.c (renamed from drivers/media/video/gspca/spca500.c)286
-rw-r--r--drivers/media/usb/gspca/spca501.c (renamed from drivers/media/video/gspca/spca501.c)319
-rw-r--r--drivers/media/usb/gspca/spca505.c (renamed from drivers/media/video/gspca/spca505.c)137
-rw-r--r--drivers/media/usb/gspca/spca506.c599
-rw-r--r--drivers/media/usb/gspca/spca508.c (renamed from drivers/media/video/gspca/spca508.c)145
-rw-r--r--drivers/media/usb/gspca/spca561.c911
-rw-r--r--drivers/media/usb/gspca/sq905.c (renamed from drivers/media/video/gspca/sq905.c)65
-rw-r--r--drivers/media/usb/gspca/sq905c.c (renamed from drivers/media/video/gspca/sq905c.c)86
-rw-r--r--drivers/media/usb/gspca/sq930x.c (renamed from drivers/media/video/gspca/sq930x.c)174
-rw-r--r--drivers/media/usb/gspca/stk014.c434
-rw-r--r--drivers/media/usb/gspca/stk1135.c676
-rw-r--r--drivers/media/usb/gspca/stk1135.h44
-rw-r--r--drivers/media/usb/gspca/stv0680.c (renamed from drivers/media/video/gspca/stv0680.c)63
-rw-r--r--drivers/media/usb/gspca/stv06xx/Kconfig10
-rw-r--r--drivers/media/usb/gspca/stv06xx/Makefile11
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx.c (renamed from drivers/media/video/gspca/stv06xx/stv06xx.c)155
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx.h (renamed from drivers/media/video/gspca/stv06xx/stv06xx.h)17
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c (renamed from drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c)36
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.h (renamed from drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h)15
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c (renamed from drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c)46
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.h (renamed from drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h)15
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_sensor.h74
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c (renamed from drivers/media/video/gspca/stv06xx/stv06xx_st6422.c)22
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_st6422.h38
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c (renamed from drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c)55
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.h (renamed from drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h)15
-rw-r--r--drivers/media/usb/gspca/sunplus.c (renamed from drivers/media/video/gspca/sunplus.c)335
-rw-r--r--drivers/media/usb/gspca/t613.c1041
-rw-r--r--drivers/media/usb/gspca/topro.c (renamed from drivers/media/video/gspca/topro.c)522
-rw-r--r--drivers/media/usb/gspca/touptek.c727
-rw-r--r--drivers/media/usb/gspca/tv8532.c365
-rw-r--r--drivers/media/usb/gspca/vc032x.c (renamed from drivers/media/video/gspca/vc032x.c)793
-rw-r--r--drivers/media/usb/gspca/vicam.c353
-rw-r--r--drivers/media/usb/gspca/w996Xcf.c (renamed from drivers/media/video/gspca/w996Xcf.c)89
-rw-r--r--drivers/media/usb/gspca/xirlink_cit.c (renamed from drivers/media/video/gspca/xirlink_cit.c)603
-rw-r--r--drivers/media/usb/gspca/zc3xx-reg.h (renamed from drivers/media/video/gspca/zc3xx-reg.h)7
-rw-r--r--drivers/media/usb/gspca/zc3xx.c (renamed from drivers/media/video/gspca/zc3xx.c)359
-rw-r--r--drivers/media/usb/hackrf/Kconfig11
-rw-r--r--drivers/media/usb/hackrf/Makefile2
-rw-r--r--drivers/media/usb/hackrf/hackrf.c1548
-rw-r--r--drivers/media/usb/hdpvr/Kconfig11
-rw-r--r--drivers/media/usb/hdpvr/Makefile4
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-control.c (renamed from drivers/media/video/hdpvr/hdpvr-control.c)59
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-core.c (renamed from drivers/media/video/hdpvr/hdpvr-core.c)153
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-i2c.c192
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-video.c1258
-rw-r--r--drivers/media/usb/hdpvr/hdpvr.h (renamed from drivers/media/video/hdpvr/hdpvr.h)55
-rw-r--r--drivers/media/usb/msi2500/Kconfig6
-rw-r--r--drivers/media/usb/msi2500/Makefile2
-rw-r--r--drivers/media/usb/msi2500/msi2500.c1307
-rw-r--r--drivers/media/usb/pvrusb2/Kconfig68
-rw-r--r--drivers/media/usb/pvrusb2/Makefile21
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-audio.c70
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-audio.h13
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-context.c (renamed from drivers/media/video/pvrusb2/pvrusb2-context.c)44
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-context.h72
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c68
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h24
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-ctrl.c (renamed from drivers/media/video/pvrusb2/pvrusb2-ctrl.c)52
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-ctrl.h (renamed from drivers/media/video/pvrusb2/pvrusb2-ctrl.h)26
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c (renamed from drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c)60
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h28
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-debug.h46
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-debugifc.c (renamed from drivers/media/video/pvrusb2/pvrusb2-debugifc.c)36
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-debugifc.h28
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-devattr.c718
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-devattr.h (renamed from drivers/media/video/pvrusb2/pvrusb2-devattr.h)27
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-dvb.c (renamed from drivers/media/video/pvrusb2/pvrusb2-dvb.c)119
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-dvb.h45
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-eeprom.c133
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-eeprom.h15
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-encoder.c (renamed from drivers/media/video/pvrusb2/pvrusb2-encoder.c)71
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-encoder.h18
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h52
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h (renamed from drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h)31
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-hdw.c (renamed from drivers/media/video/pvrusb2/pvrusb2-hdw.c)607
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-hdw.h (renamed from drivers/media/video/pvrusb2/pvrusb2-hdw.h)43
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c (renamed from drivers/media/video/pvrusb2/pvrusb2-i2c-core.c)129
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h15
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-io.c672
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-io.h78
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-ioread.c (renamed from drivers/media/video/pvrusb2/pvrusb2-ioread.c)89
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-ioread.h24
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-main.c145
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-std.c218
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-std.h29
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-sysfs.c (renamed from drivers/media/video/pvrusb2/pvrusb2-sysfs.c)106
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-sysfs.h24
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-util.h38
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-v4l2.c1262
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-v4l2.h15
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c87
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h24
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-wm8775.c43
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-wm8775.h28
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2.h18
-rw-r--r--drivers/media/usb/pwc/Kconfig49
-rw-r--r--drivers/media/usb/pwc/Makefile5
-rw-r--r--drivers/media/usb/pwc/pwc-ctrl.c (renamed from drivers/media/video/pwc/pwc-ctrl.c)37
-rw-r--r--drivers/media/usb/pwc/pwc-dec1.c20
-rw-r--r--drivers/media/usb/pwc/pwc-dec1.h27
-rw-r--r--drivers/media/usb/pwc/pwc-dec23.c (renamed from drivers/media/video/pwc/pwc-dec23.c)27
-rw-r--r--drivers/media/usb/pwc/pwc-dec23.h49
-rw-r--r--drivers/media/usb/pwc/pwc-if.c (renamed from drivers/media/video/pwc/pwc-if.c)428
-rw-r--r--drivers/media/usb/pwc/pwc-kiara.c (renamed from drivers/media/video/pwc/pwc-kiara.c)14
-rw-r--r--drivers/media/usb/pwc/pwc-kiara.h36
-rw-r--r--drivers/media/usb/pwc/pwc-misc.c81
-rw-r--r--drivers/media/usb/pwc/pwc-nala.h (renamed from drivers/media/video/pwc/pwc-nala.h)1
-rw-r--r--drivers/media/usb/pwc/pwc-timon.c (renamed from drivers/media/video/pwc/pwc-timon.c)14
-rw-r--r--drivers/media/usb/pwc/pwc-timon.h51
-rw-r--r--drivers/media/usb/pwc/pwc-uncompress.c92
-rw-r--r--drivers/media/usb/pwc/pwc-v4l.c (renamed from drivers/media/video/pwc/pwc-v4l.c)201
-rw-r--r--drivers/media/usb/pwc/pwc.h (renamed from drivers/media/video/pwc/pwc.h)49
-rw-r--r--drivers/media/usb/s2255/Kconfig9
-rw-r--r--drivers/media/usb/s2255/Makefile3
-rw-r--r--drivers/media/usb/s2255/s2255drv.c2378
-rw-r--r--drivers/media/usb/siano/Kconfig14
-rw-r--r--drivers/media/usb/siano/Makefile6
-rw-r--r--drivers/media/usb/siano/smsusb.c736
-rw-r--r--drivers/media/usb/stk1160/Kconfig15
-rw-r--r--drivers/media/usb/stk1160/Makefile8
-rw-r--r--drivers/media/usb/stk1160/stk1160-ac97.c155
-rw-r--r--drivers/media/usb/stk1160/stk1160-core.c430
-rw-r--r--drivers/media/usb/stk1160/stk1160-i2c.c284
-rw-r--r--drivers/media/usb/stk1160/stk1160-reg.h127
-rw-r--r--drivers/media/usb/stk1160/stk1160-v4l.c841
-rw-r--r--drivers/media/usb/stk1160/stk1160-video.c523
-rw-r--r--drivers/media/usb/stk1160/stk1160.h195
-rw-r--r--drivers/media/usb/ttusb-budget/Kconfig19
-rw-r--r--drivers/media/usb/ttusb-budget/Makefile4
-rw-r--r--drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c (renamed from drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c)280
-rw-r--r--drivers/media/usb/ttusb-dec/Kconfig22
-rw-r--r--drivers/media/usb/ttusb-dec/Makefile2
-rw-r--r--drivers/media/usb/ttusb-dec/ttusb_dec.c (renamed from drivers/media/dvb/ttusb-dec/ttusb_dec.c)417
-rw-r--r--drivers/media/usb/ttusb-dec/ttusbdecfe.c (renamed from drivers/media/dvb/ttusb-dec/ttusbdecfe.c)63
-rw-r--r--drivers/media/usb/ttusb-dec/ttusbdecfe.h24
-rw-r--r--drivers/media/usb/usbtv/Kconfig12
-rw-r--r--drivers/media/usb/usbtv/Makefile6
-rw-r--r--drivers/media/usb/usbtv/usbtv-audio.c377
-rw-r--r--drivers/media/usb/usbtv/usbtv-core.c167
-rw-r--r--drivers/media/usb/usbtv/usbtv-video.c972
-rw-r--r--drivers/media/usb/usbtv/usbtv.h138
-rw-r--r--drivers/media/usb/uvc/Kconfig22
-rw-r--r--drivers/media/usb/uvc/Makefile7
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c3396
-rw-r--r--drivers/media/usb/uvc/uvc_debugfs.c104
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c3286
-rw-r--r--drivers/media/usb/uvc/uvc_entity.c161
-rw-r--r--drivers/media/usb/uvc/uvc_isight.c140
-rw-r--r--drivers/media/usb/uvc/uvc_metadata.c261
-rw-r--r--drivers/media/usb/uvc/uvc_queue.c397
-rw-r--r--drivers/media/usb/uvc/uvc_status.c412
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c1282
-rw-r--r--drivers/media/usb/uvc/uvc_video.c2342
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h814
-rw-r--r--drivers/media/v4l2-core/Kconfig88
-rw-r--r--drivers/media/v4l2-core/Makefile38
-rw-r--r--drivers/media/v4l2-core/tuner-core.c (renamed from drivers/media/video/tuner-core.c)345
-rw-r--r--drivers/media/v4l2-core/v4l2-async.c976
-rw-r--r--drivers/media/v4l2-core/v4l2-cci.c203
-rw-r--r--drivers/media/v4l2-core/v4l2-common.c808
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c1207
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-api.c1361
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-core.c2802
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-defs.c1685
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-priv.h95
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-request.c501
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c1258
-rw-r--r--drivers/media/v4l2-core/v4l2-device.c295
-rw-r--r--drivers/media/v4l2-core/v4l2-dv-timings.c1275
-rw-r--r--drivers/media/v4l2-core/v4l2-event.c373
-rw-r--r--drivers/media/v4l2-core/v4l2-fh.c119
-rw-r--r--drivers/media/v4l2-core/v4l2-flash-led-class.c746
-rw-r--r--drivers/media/v4l2-core/v4l2-fwnode.c1299
-rw-r--r--drivers/media/v4l2-core/v4l2-h264.c453
-rw-r--r--drivers/media/v4l2-core/v4l2-i2c.c185
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c3532
-rw-r--r--drivers/media/v4l2-core/v4l2-isp.c132
-rw-r--r--drivers/media/v4l2-core/v4l2-jpeg.c713
-rw-r--r--drivers/media/v4l2-core/v4l2-mc.c614
-rw-r--r--drivers/media/v4l2-core/v4l2-mem2mem.c1624
-rw-r--r--drivers/media/v4l2-core/v4l2-spi.c78
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev-priv.h14
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c2639
-rw-r--r--drivers/media/v4l2-core/v4l2-trace.c12
-rw-r--r--drivers/media/v4l2-core/v4l2-vp9.c1850
-rw-r--r--drivers/media/video/Kconfig1218
-rw-r--r--drivers/media/video/Makefile217
-rw-r--r--drivers/media/video/adv7180.c734
-rw-r--r--drivers/media/video/ak881x.c359
-rw-r--r--drivers/media/video/aptina-pll.h56
-rw-r--r--drivers/media/video/arv.c885
-rw-r--r--drivers/media/video/as3645a.c888
-rw-r--r--drivers/media/video/atmel-isi.c1099
-rw-r--r--drivers/media/video/au0828/Kconfig18
-rw-r--r--drivers/media/video/au0828/Makefile9
-rw-r--r--drivers/media/video/au0828/au0828-cards.h27
-rw-r--r--drivers/media/video/au0828/au0828-core.c295
-rw-r--r--drivers/media/video/au0828/au0828-dvb.c458
-rw-r--r--drivers/media/video/au0828/au0828-reg.h65
-rw-r--r--drivers/media/video/au0828/au0828-vbi.c138
-rw-r--r--drivers/media/video/au0828/au0828-video.c1998
-rw-r--r--drivers/media/video/au0828/au0828.h302
-rw-r--r--drivers/media/video/blackfin/Kconfig10
-rw-r--r--drivers/media/video/blackfin/Makefile2
-rw-r--r--drivers/media/video/blackfin/bfin_capture.c1063
-rw-r--r--drivers/media/video/blackfin/ppi.c271
-rw-r--r--drivers/media/video/bt8xx/Kconfig27
-rw-r--r--drivers/media/video/bt8xx/Makefile13
-rw-r--r--drivers/media/video/bt8xx/bttv-audio-hook.c382
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c4636
-rw-r--r--drivers/media/video/bt8xx/bttv-if.c121
-rw-r--r--drivers/media/video/bt8xx/bttv-input.c589
-rw-r--r--drivers/media/video/bt8xx/bttv-risc.c909
-rw-r--r--drivers/media/video/bt8xx/bttv-vbi.c459
-rw-r--r--drivers/media/video/btcx-risc.c260
-rw-r--r--drivers/media/video/btcx-risc.h34
-rw-r--r--drivers/media/video/bw-qcam.c1113
-rw-r--r--drivers/media/video/c-qcam.c883
-rw-r--r--drivers/media/video/cpia2/Kconfig9
-rw-r--r--drivers/media/video/cpia2/Makefile3
-rw-r--r--drivers/media/video/cpia2/cpia2.h487
-rw-r--r--drivers/media/video/cpia2/cpia2_core.c2413
-rw-r--r--drivers/media/video/cpia2/cpia2_registers.h476
-rw-r--r--drivers/media/video/cpia2/cpia2_usb.c955
-rw-r--r--drivers/media/video/cpia2/cpia2_v4l.c1250
-rw-r--r--drivers/media/video/cs5345.c252
-rw-r--r--drivers/media/video/cs8420.h50
-rw-r--r--drivers/media/video/cx18/Kconfig35
-rw-r--r--drivers/media/video/cx18/Makefile13
-rw-r--r--drivers/media/video/cx18/cx18-alsa-mixer.c175
-rw-r--r--drivers/media/video/cx18/cx18-alsa-mixer.h23
-rw-r--r--drivers/media/video/cx18/cx18-alsa-pcm.c356
-rw-r--r--drivers/media/video/cx18/cx18-alsa-pcm.h27
-rw-r--r--drivers/media/video/cx18/cx18-alsa.h75
-rw-r--r--drivers/media/video/cx18/cx18-audio.c92
-rw-r--r--drivers/media/video/cx18/cx18-audio.h24
-rw-r--r--drivers/media/video/cx18/cx18-cards.h157
-rw-r--r--drivers/media/video/cx18/cx18-controls.c131
-rw-r--r--drivers/media/video/cx18/cx18-controls.h24
-rw-r--r--drivers/media/video/cx18/cx18-dvb.h25
-rw-r--r--drivers/media/video/cx18/cx18-fileops.h41
-rw-r--r--drivers/media/video/cx18/cx18-firmware.h25
-rw-r--r--drivers/media/video/cx18/cx18-gpio.h34
-rw-r--r--drivers/media/video/cx18/cx18-i2c.h29
-rw-r--r--drivers/media/video/cx18/cx18-io.c97
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c1212
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.h33
-rw-r--r--drivers/media/video/cx18/cx18-irq.c81
-rw-r--r--drivers/media/video/cx18/cx18-irq.h35
-rw-r--r--drivers/media/video/cx18/cx18-queue.h98
-rw-r--r--drivers/media/video/cx18/cx18-streams.c1060
-rw-r--r--drivers/media/video/cx18/cx18-streams.h62
-rw-r--r--drivers/media/video/cx18/cx18-vbi.h26
-rw-r--r--drivers/media/video/cx18/cx18-version.h28
-rw-r--r--drivers/media/video/cx18/cx18-video.c32
-rw-r--r--drivers/media/video/cx18/cx18-video.h22
-rw-r--r--drivers/media/video/cx231xx/Kconfig51
-rw-r--r--drivers/media/video/cx231xx/Makefile16
-rw-r--r--drivers/media/video/cx231xx/cx231xx-417.c2195
-rw-r--r--drivers/media/video/cx231xx/cx231xx-audio.c780
-rw-r--r--drivers/media/video/cx231xx/cx231xx-cards.c1381
-rw-r--r--drivers/media/video/cx231xx/cx231xx-dvb.c796
-rw-r--r--drivers/media/video/cx231xx/cx231xx-input.c119
-rw-r--r--drivers/media/video/cx231xx/cx231xx-vbi.c710
-rw-r--r--drivers/media/video/cx231xx/cx231xx-vbi.h65
-rw-r--r--drivers/media/video/cx231xx/cx231xx-video.c2645
-rw-r--r--drivers/media/video/cx23885/Kconfig46
-rw-r--r--drivers/media/video/cx23885/Makefile15
-rw-r--r--drivers/media/video/cx23885/altera-ci.h100
-rw-r--r--drivers/media/video/cx23885/cimax2.h47
-rw-r--r--drivers/media/video/cx23885/cx23885-417.c1788
-rw-r--r--drivers/media/video/cx23885/cx23885-av.c35
-rw-r--r--drivers/media/video/cx23885/cx23885-av.h27
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c1680
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c1357
-rw-r--r--drivers/media/video/cx23885/cx23885-f300.h2
-rw-r--r--drivers/media/video/cx23885/cx23885-input.h30
-rw-r--r--drivers/media/video/cx23885/cx23885-ioctl.c208
-rw-r--r--drivers/media/video/cx23885/cx23885-ioctl.h39
-rw-r--r--drivers/media/video/cx23885/cx23885-ir.h31
-rw-r--r--drivers/media/video/cx23885/cx23885-vbi.c295
-rw-r--r--drivers/media/video/cx23885/cx23885-video.c1926
-rw-r--r--drivers/media/video/cx23885/cx23888-ir.h28
-rw-r--r--drivers/media/video/cx23885/netup-eeprom.c107
-rw-r--r--drivers/media/video/cx23885/netup-eeprom.h42
-rw-r--r--drivers/media/video/cx23885/netup-init.c125
-rw-r--r--drivers/media/video/cx23885/netup-init.h25
-rw-r--r--drivers/media/video/cx25821/Kconfig34
-rw-r--r--drivers/media/video/cx25821/Makefile13
-rw-r--r--drivers/media/video/cx25821/cx25821-audio-upstream.c778
-rw-r--r--drivers/media/video/cx25821/cx25821-audio-upstream.h62
-rw-r--r--drivers/media/video/cx25821/cx25821-audio.h62
-rw-r--r--drivers/media/video/cx25821/cx25821-biffuncs.h45
-rw-r--r--drivers/media/video/cx25821/cx25821-cards.c72
-rw-r--r--drivers/media/video/cx25821/cx25821-gpio.c98
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-defines.h42
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-video.h49
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream-ch2.c802
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream-ch2.h138
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream.c856
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream.h139
-rw-r--r--drivers/media/video/cx25821/cx25821-video.c1990
-rw-r--r--drivers/media/video/cx25821/cx25821-video.h186
-rw-r--r--drivers/media/video/cx25821/cx25821.h617
-rw-r--r--drivers/media/video/cx25840/Kconfig8
-rw-r--r--drivers/media/video/cx25840/Makefile6
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c5340
-rw-r--r--drivers/media/video/cx25840/cx25840-core.h137
-rw-r--r--drivers/media/video/cx25840/cx25840-firmware.c166
-rw-r--r--drivers/media/video/cx88/Kconfig86
-rw-r--r--drivers/media/video/cx88/Makefile16
-rw-r--r--drivers/media/video/cx88/cx88-alsa.c992
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c1385
-rw-r--r--drivers/media/video/cx88/cx88-core.c1126
-rw-r--r--drivers/media/video/cx88/cx88-dsp.c322
-rw-r--r--drivers/media/video/cx88/cx88-i2c.c184
-rw-r--r--drivers/media/video/cx88/cx88-mpeg.c929
-rw-r--r--drivers/media/video/cx88/cx88-vbi.c245
-rw-r--r--drivers/media/video/cx88/cx88-video.c2194
-rw-r--r--drivers/media/video/cx88/cx88-vp3054-i2c.c159
-rw-r--r--drivers/media/video/cx88/cx88-vp3054-i2c.h41
-rw-r--r--drivers/media/video/cx88/cx88.h732
-rw-r--r--drivers/media/video/davinci/Kconfig117
-rw-r--r--drivers/media/video/davinci/Makefile20
-rw-r--r--drivers/media/video/davinci/ccdc_hw_device.h110
-rw-r--r--drivers/media/video/davinci/dm355_ccdc.c1072
-rw-r--r--drivers/media/video/davinci/dm355_ccdc_regs.h310
-rw-r--r--drivers/media/video/davinci/dm644x_ccdc.c1081
-rw-r--r--drivers/media/video/davinci/dm644x_ccdc_regs.h153
-rw-r--r--drivers/media/video/davinci/isif.c1162
-rw-r--r--drivers/media/video/davinci/isif_regs.h269
-rw-r--r--drivers/media/video/davinci/vpbe.c886
-rw-r--r--drivers/media/video/davinci/vpbe_display.c1828
-rw-r--r--drivers/media/video/davinci/vpbe_osd.c1605
-rw-r--r--drivers/media/video/davinci/vpbe_osd_regs.h364
-rw-r--r--drivers/media/video/davinci/vpbe_venc.c706
-rw-r--r--drivers/media/video/davinci/vpbe_venc_regs.h177
-rw-r--r--drivers/media/video/davinci/vpfe_capture.c2079
-rw-r--r--drivers/media/video/davinci/vpif.c473
-rw-r--r--drivers/media/video/davinci/vpif_capture.c2416
-rw-r--r--drivers/media/video/davinci/vpif_capture.h162
-rw-r--r--drivers/media/video/davinci/vpif_display.c1916
-rw-r--r--drivers/media/video/davinci/vpif_display.h172
-rw-r--r--drivers/media/video/davinci/vpss.c482
-rw-r--r--drivers/media/video/em28xx/Kconfig58
-rw-r--r--drivers/media/video/em28xx/Makefile15
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c751
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c3422
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c1263
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c1112
-rw-r--r--drivers/media/video/em28xx/em28xx-i2c.c567
-rw-r--r--drivers/media/video/em28xx/em28xx-input.c645
-rw-r--r--drivers/media/video/em28xx/em28xx-reg.h275
-rw-r--r--drivers/media/video/em28xx/em28xx-vbi.c145
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c2606
-rw-r--r--drivers/media/video/em28xx/em28xx.h809
-rw-r--r--drivers/media/video/fsl-viu.c1673
-rw-r--r--drivers/media/video/gspca/Kconfig425
-rw-r--r--drivers/media/video/gspca/Makefile93
-rw-r--r--drivers/media/video/gspca/autogain_functions.c178
-rw-r--r--drivers/media/video/gspca/autogain_functions.h183
-rw-r--r--drivers/media/video/gspca/gl860/Kconfig8
-rw-r--r--drivers/media/video/gspca/gl860/Makefile10
-rw-r--r--drivers/media/video/gspca/gl860/gl860-mi2020.c733
-rw-r--r--drivers/media/video/gspca/gl860/gl860-ov9655.c336
-rw-r--r--drivers/media/video/gspca/gspca.c2460
-rw-r--r--drivers/media/video/gspca/gspca.h259
-rw-r--r--drivers/media/video/gspca/jeilinj.c585
-rw-r--r--drivers/media/video/gspca/kinect.c416
-rw-r--r--drivers/media/video/gspca/konica.c634
-rw-r--r--drivers/media/video/gspca/m5602/Kconfig11
-rw-r--r--drivers/media/video/gspca/m5602/Makefile11
-rw-r--r--drivers/media/video/gspca/m5602/m5602_mt9m111.c647
-rw-r--r--drivers/media/video/gspca/m5602/m5602_mt9m111.h271
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov7660.c488
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov7660.h260
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov9650.c881
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov9650.h307
-rw-r--r--drivers/media/video/gspca/m5602/m5602_po1030.c763
-rw-r--r--drivers/media/video/gspca/m5602/m5602_po1030.h272
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k4aa.c726
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k4aa.h283
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k83a.c605
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k83a.h193
-rw-r--r--drivers/media/video/gspca/mr97310a.c1262
-rw-r--r--drivers/media/video/gspca/ov534.c1578
-rw-r--r--drivers/media/video/gspca/ov534_9.c1606
-rw-r--r--drivers/media/video/gspca/pac7302.c1004
-rw-r--r--drivers/media/video/gspca/pac_common.h134
-rw-r--r--drivers/media/video/gspca/sn9c2028.c740
-rw-r--r--drivers/media/video/gspca/sn9c2028.h51
-rw-r--r--drivers/media/video/gspca/spca1528.c593
-rw-r--r--drivers/media/video/gspca/spca506.c717
-rw-r--r--drivers/media/video/gspca/spca561.c1109
-rw-r--r--drivers/media/video/gspca/stk014.c522
-rw-r--r--drivers/media/video/gspca/stv06xx/Kconfig9
-rw-r--r--drivers/media/video/gspca/stv06xx/Makefile10
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_sensor.h87
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_st6422.h52
-rw-r--r--drivers/media/video/gspca/t613.c1466
-rw-r--r--drivers/media/video/gspca/tv8532.c421
-rw-r--r--drivers/media/video/gspca/vicam.c379
-rw-r--r--drivers/media/video/hdpvr/Kconfig10
-rw-r--r--drivers/media/video/hdpvr/Makefile7
-rw-r--r--drivers/media/video/hdpvr/hdpvr-i2c.c231
-rw-r--r--drivers/media/video/hdpvr/hdpvr-video.c1289
-rw-r--r--drivers/media/video/ibmmpeg2.h94
-rw-r--r--drivers/media/video/imx074.c475
-rw-r--r--drivers/media/video/indycam.c390
-rw-r--r--drivers/media/video/indycam.h93
-rw-r--r--drivers/media/video/ir-kbd-i2c.c489
-rw-r--r--drivers/media/video/ivtv/Kconfig45
-rw-r--r--drivers/media/video/ivtv/Makefile14
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.h309
-rw-r--r--drivers/media/video/ivtv/ivtv-controls.h28
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.h44
-rw-r--r--drivers/media/video/ivtv/ivtv-firmware.h31
-rw-r--r--drivers/media/video/ivtv/ivtv-gpio.h29
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.h32
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c1911
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.h36
-rw-r--r--drivers/media/video/ivtv/ivtv-irq.h53
-rw-r--r--drivers/media/video/ivtv/ivtv-mailbox.c387
-rw-r--r--drivers/media/video/ivtv/ivtv-mailbox.h35
-rw-r--r--drivers/media/video/ivtv/ivtv-queue.h96
-rw-r--r--drivers/media/video/ivtv/ivtv-routing.h27
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.h37
-rw-r--r--drivers/media/video/ivtv/ivtv-udma.c234
-rw-r--r--drivers/media/video/ivtv/ivtv-udma.h48
-rw-r--r--drivers/media/video/ivtv/ivtv-vbi.h34
-rw-r--r--drivers/media/video/ivtv/ivtv-version.h26
-rw-r--r--drivers/media/video/ivtv/ivtv-yuv.h44
-rw-r--r--drivers/media/video/ks0127.h51
-rw-r--r--drivers/media/video/m52790.c216
-rw-r--r--drivers/media/video/m5mols/Kconfig5
-rw-r--r--drivers/media/video/m5mols/Makefile3
-rw-r--r--drivers/media/video/m5mols/m5mols.h334
-rw-r--r--drivers/media/video/m5mols/m5mols_capture.c155
-rw-r--r--drivers/media/video/m5mols/m5mols_controls.c628
-rw-r--r--drivers/media/video/m5mols/m5mols_core.c990
-rw-r--r--drivers/media/video/m5mols/m5mols_reg.h362
-rw-r--r--drivers/media/video/marvell-ccic/Kconfig23
-rw-r--r--drivers/media/video/marvell-ccic/Makefile6
-rw-r--r--drivers/media/video/marvell-ccic/mcam-core.c1878
-rw-r--r--drivers/media/video/marvell-ccic/mmp-driver.c380
-rw-r--r--drivers/media/video/mem2mem_testdev.c1056
-rw-r--r--drivers/media/video/meye.c1964
-rw-r--r--drivers/media/video/meye.h324
-rw-r--r--drivers/media/video/mt9m001.c737
-rw-r--r--drivers/media/video/mt9m032.c869
-rw-r--r--drivers/media/video/mt9m111.c1015
-rw-r--r--drivers/media/video/mt9p031.c1068
-rw-r--r--drivers/media/video/mt9t001.c824
-rw-r--r--drivers/media/video/mt9t031.c857
-rw-r--r--drivers/media/video/mt9t112.c1125
-rw-r--r--drivers/media/video/mt9v011.c712
-rw-r--r--drivers/media/video/mt9v022.c879
-rw-r--r--drivers/media/video/mt9v032.c763
-rw-r--r--drivers/media/video/mx1_camera.c888
-rw-r--r--drivers/media/video/mx2_camera.c1869
-rw-r--r--drivers/media/video/mx2_emmaprp.c1012
-rw-r--r--drivers/media/video/mx3_camera.c1300
-rw-r--r--drivers/media/video/noon010pc30.c851
-rw-r--r--drivers/media/video/omap/Kconfig14
-rw-r--r--drivers/media/video/omap/Makefile8
-rw-r--r--drivers/media/video/omap/omap_vout.c2289
-rw-r--r--drivers/media/video/omap/omap_vout_vrfb.c390
-rw-r--r--drivers/media/video/omap/omap_vout_vrfb.h40
-rw-r--r--drivers/media/video/omap1_camera.c1723
-rw-r--r--drivers/media/video/omap24xxcam-dma.c601
-rw-r--r--drivers/media/video/omap24xxcam.c1881
-rw-r--r--drivers/media/video/omap24xxcam.h593
-rw-r--r--drivers/media/video/omap3isp/Makefile11
-rw-r--r--drivers/media/video/omap3isp/cfa_coef_table.h61
-rw-r--r--drivers/media/video/omap3isp/isp.c2230
-rw-r--r--drivers/media/video/omap3isp/ispccdc.c2535
-rw-r--r--drivers/media/video/omap3isp/ispccdc.h209
-rw-r--r--drivers/media/video/omap3isp/ispccp2.h98
-rw-r--r--drivers/media/video/omap3isp/ispcsiphy.c249
-rw-r--r--drivers/media/video/omap3isp/ispcsiphy.h63
-rw-r--r--drivers/media/video/omap3isp/isphist.h40
-rw-r--r--drivers/media/video/omap3isp/ispqueue.c1157
-rw-r--r--drivers/media/video/omap3isp/ispqueue.h187
-rw-r--r--drivers/media/video/omap3isp/ispreg.h1586
-rw-r--r--drivers/media/video/omap3isp/ispvideo.c1392
-rw-r--r--drivers/media/video/omap3isp/luma_enhance_table.h42
-rw-r--r--drivers/media/video/omap3isp/noise_filter_table.h30
-rw-r--r--drivers/media/video/ov2640.c1110
-rw-r--r--drivers/media/video/ov5642.c1073
-rw-r--r--drivers/media/video/ov6650.c1053
-rw-r--r--drivers/media/video/ov7670.c1586
-rw-r--r--drivers/media/video/ov772x.c1130
-rw-r--r--drivers/media/video/ov9640.c745
-rw-r--r--drivers/media/video/ov9740.c1005
-rw-r--r--drivers/media/video/pms.c1153
-rw-r--r--drivers/media/video/pvrusb2/Kconfig66
-rw-r--r--drivers/media/video/pvrusb2/Makefile22
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-audio.c96
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-audio.h37
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-context.h94
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c95
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h48
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h52
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debug.h69
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debugifc.h52
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.c569
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-dvb.h41
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-eeprom.c164
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-eeprom.h39
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-encoder.h42
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h72
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-core.h40
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-io.c695
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-io.h102
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ioread.h48
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-main.c182
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-std.c411
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-std.h59
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-sysfs.h46
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-util.h62
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c1421
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.h39
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-video-v4l.c114
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-video-v4l.h48
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-wm8775.c70
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-wm8775.h52
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2.h42
-rw-r--r--drivers/media/video/pwc/Kconfig48
-rw-r--r--drivers/media/video/pwc/Makefile4
-rw-r--r--drivers/media/video/pwc/philips.txt236
-rw-r--r--drivers/media/video/pwc/pwc-dec1.c32
-rw-r--r--drivers/media/video/pwc/pwc-dec1.h39
-rw-r--r--drivers/media/video/pwc/pwc-dec23.h61
-rw-r--r--drivers/media/video/pwc/pwc-kiara.h48
-rw-r--r--drivers/media/video/pwc/pwc-misc.c93
-rw-r--r--drivers/media/video/pwc/pwc-timon.h63
-rw-r--r--drivers/media/video/pwc/pwc-uncompress.c107
-rw-r--r--drivers/media/video/pxa_camera.c1852
-rw-r--r--drivers/media/video/s2255drv.c2688
-rw-r--r--drivers/media/video/s5k6aa.c1670
-rw-r--r--drivers/media/video/s5p-fimc/Kconfig48
-rw-r--r--drivers/media/video/s5p-fimc/Makefile7
-rw-r--r--drivers/media/video/s5p-fimc/fimc-capture.c1712
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.c1239
-rw-r--r--drivers/media/video/s5p-fimc/fimc-lite-reg.c300
-rw-r--r--drivers/media/video/s5p-fimc/fimc-lite-reg.h150
-rw-r--r--drivers/media/video/s5p-fimc/fimc-lite.c1607
-rw-r--r--drivers/media/video/s5p-fimc/fimc-lite.h213
-rw-r--r--drivers/media/video/s5p-fimc/fimc-m2m.c824
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.c1036
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.h120
-rw-r--r--drivers/media/video/s5p-fimc/fimc-reg.h326
-rw-r--r--drivers/media/video/s5p-fimc/mipi-csis.c722
-rw-r--r--drivers/media/video/s5p-fimc/mipi-csis.h25
-rw-r--r--drivers/media/video/s5p-g2d/Makefile3
-rw-r--r--drivers/media/video/s5p-g2d/g2d.c827
-rw-r--r--drivers/media/video/s5p-jpeg/Makefile2
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-core.c1533
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-core.h150
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-hw.h357
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-regs.h170
-rw-r--r--drivers/media/video/s5p-mfc/Makefile5
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc.c1223
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_cmd.c120
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_cmd.h30
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_common.h570
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c343
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h29
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_debug.h48
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_dec.c1038
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_dec.h23
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_enc.c1829
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_enc.h23
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_intr.h26
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_opr.c1397
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_opr.h93
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_pm.c137
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_pm.h24
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_shm.c47
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_shm.h90
-rw-r--r--drivers/media/video/s5p-tv/Kconfig86
-rw-r--r--drivers/media/video/s5p-tv/Makefile19
-rw-r--r--drivers/media/video/s5p-tv/hdmi_drv.c1007
-rw-r--r--drivers/media/video/s5p-tv/hdmiphy_drv.c329
-rw-r--r--drivers/media/video/s5p-tv/mixer.h365
-rw-r--r--drivers/media/video/s5p-tv/mixer_drv.c487
-rw-r--r--drivers/media/video/s5p-tv/mixer_grp_layer.c270
-rw-r--r--drivers/media/video/s5p-tv/mixer_reg.c553
-rw-r--r--drivers/media/video/s5p-tv/mixer_video.c1112
-rw-r--r--drivers/media/video/s5p-tv/mixer_vp_layer.c241
-rw-r--r--drivers/media/video/s5p-tv/regs-hdmi.h146
-rw-r--r--drivers/media/video/s5p-tv/regs-mixer.h122
-rw-r--r--drivers/media/video/s5p-tv/regs-sdo.h63
-rw-r--r--drivers/media/video/s5p-tv/regs-vp.h88
-rw-r--r--drivers/media/video/s5p-tv/sdo_drv.c452
-rw-r--r--drivers/media/video/s5p-tv/sii9234_drv.c432
-rw-r--r--drivers/media/video/saa7121.h132
-rw-r--r--drivers/media/video/saa7134/Kconfig64
-rw-r--r--drivers/media/video/saa7134/Makefile16
-rw-r--r--drivers/media/video/saa7134/saa6752hs.c1012
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c1368
-rw-r--r--drivers/media/video/saa7134/saa7134-empress.c590
-rw-r--r--drivers/media/video/saa7134/saa7134-ts.c327
-rw-r--r--drivers/media/video/saa7134/saa7134-vbi.c255
-rw-r--r--drivers/media/video/saa7134/saa7134-video.c2661
-rw-r--r--drivers/media/video/saa7146.h112
-rw-r--r--drivers/media/video/saa7146reg.h283
-rw-r--r--drivers/media/video/saa7164/Kconfig18
-rw-r--r--drivers/media/video/saa7164/Makefile12
-rw-r--r--drivers/media/video/saa7164/saa7164-dvb.c556
-rw-r--r--drivers/media/video/saa7164/saa7164-encoder.c1500
-rw-r--r--drivers/media/video/saa7164/saa7164-i2c.c141
-rw-r--r--drivers/media/video/saa7164/saa7164-vbi.c1374
-rw-r--r--drivers/media/video/saa7191.c659
-rw-r--r--drivers/media/video/saa7191.h245
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c2331
-rw-r--r--drivers/media/video/sh_mobile_csi2.c398
-rw-r--r--drivers/media/video/sh_vou.c1497
-rw-r--r--drivers/media/video/smiapp-pll.c418
-rw-r--r--drivers/media/video/smiapp-pll.h103
-rw-r--r--drivers/media/video/smiapp/Kconfig6
-rw-r--r--drivers/media/video/smiapp/Makefile5
-rw-r--r--drivers/media/video/smiapp/smiapp-core.c2896
-rw-r--r--drivers/media/video/smiapp/smiapp-limits.c132
-rw-r--r--drivers/media/video/smiapp/smiapp-limits.h128
-rw-r--r--drivers/media/video/smiapp/smiapp-quirk.c306
-rw-r--r--drivers/media/video/smiapp/smiapp-quirk.h83
-rw-r--r--drivers/media/video/smiapp/smiapp-reg-defs.h503
-rw-r--r--drivers/media/video/smiapp/smiapp-reg.h122
-rw-r--r--drivers/media/video/smiapp/smiapp-regs.c273
-rw-r--r--drivers/media/video/smiapp/smiapp-regs.h49
-rw-r--r--drivers/media/video/smiapp/smiapp.h252
-rw-r--r--drivers/media/video/sn9c102/Kconfig14
-rw-r--r--drivers/media/video/sn9c102/Makefile15
-rw-r--r--drivers/media/video/sn9c102/sn9c102.h211
-rw-r--r--drivers/media/video/sn9c102/sn9c102_config.h86
-rw-r--r--drivers/media/video/sn9c102/sn9c102_core.c3421
-rw-r--r--drivers/media/video/sn9c102/sn9c102_devtable.h147
-rw-r--r--drivers/media/video/sn9c102/sn9c102_hv7131d.c264
-rw-r--r--drivers/media/video/sn9c102/sn9c102_hv7131r.c363
-rw-r--r--drivers/media/video/sn9c102/sn9c102_mi0343.c352
-rw-r--r--drivers/media/video/sn9c102/sn9c102_mi0360.c453
-rw-r--r--drivers/media/video/sn9c102/sn9c102_mt9v111.c260
-rw-r--r--drivers/media/video/sn9c102/sn9c102_ov7630.c626
-rw-r--r--drivers/media/video/sn9c102/sn9c102_ov7660.c538
-rw-r--r--drivers/media/video/sn9c102/sn9c102_pas106b.c302
-rw-r--r--drivers/media/video/sn9c102/sn9c102_pas202bcb.c335
-rw-r--r--drivers/media/video/sn9c102/sn9c102_sensor.h307
-rw-r--r--drivers/media/video/sn9c102/sn9c102_tas5110c1b.c154
-rw-r--r--drivers/media/video/sn9c102/sn9c102_tas5110d.c119
-rw-r--r--drivers/media/video/sn9c102/sn9c102_tas5130d1b.c165
-rw-r--r--drivers/media/video/soc_camera.c1544
-rw-r--r--drivers/media/video/soc_camera_platform.c197
-rw-r--r--drivers/media/video/soc_mediabus.c487
-rw-r--r--drivers/media/video/sr030pc30.c871
-rw-r--r--drivers/media/video/sta2x11_vip.c1550
-rw-r--r--drivers/media/video/sta2x11_vip.h40
-rw-r--r--drivers/media/video/stk-sensor.c595
-rw-r--r--drivers/media/video/stk-webcam.c1380
-rw-r--r--drivers/media/video/stk-webcam.h134
-rw-r--r--drivers/media/video/tcm825x.c937
-rw-r--r--drivers/media/video/tcm825x.h200
-rw-r--r--drivers/media/video/tda7432.c485
-rw-r--r--drivers/media/video/tda9840.c224
-rw-r--r--drivers/media/video/tea6415c.c187
-rw-r--r--drivers/media/video/tea6420.c169
-rw-r--r--drivers/media/video/ths7303.c140
-rw-r--r--drivers/media/video/timblogiw.c880
-rw-r--r--drivers/media/video/tlg2300/Kconfig16
-rw-r--r--drivers/media/video/tlg2300/Makefile9
-rw-r--r--drivers/media/video/tlg2300/pd-alsa.c332
-rw-r--r--drivers/media/video/tlg2300/pd-common.h281
-rw-r--r--drivers/media/video/tlg2300/pd-dvb.c596
-rw-r--r--drivers/media/video/tlg2300/pd-main.c534
-rw-r--r--drivers/media/video/tlg2300/pd-radio.c421
-rw-r--r--drivers/media/video/tlg2300/pd-video.c1668
-rw-r--r--drivers/media/video/tlg2300/vendorcmds.h243
-rw-r--r--drivers/media/video/tm6000/Kconfig33
-rw-r--r--drivers/media/video/tm6000/Makefile15
-rw-r--r--drivers/media/video/tm6000/tm6000-alsa.c528
-rw-r--r--drivers/media/video/tm6000/tm6000-cards.c1413
-rw-r--r--drivers/media/video/tm6000/tm6000-core.c935
-rw-r--r--drivers/media/video/tm6000/tm6000-dvb.c467
-rw-r--r--drivers/media/video/tm6000/tm6000-i2c.c334
-rw-r--r--drivers/media/video/tm6000/tm6000-input.c498
-rw-r--r--drivers/media/video/tm6000/tm6000-regs.h600
-rw-r--r--drivers/media/video/tm6000/tm6000-stds.c638
-rw-r--r--drivers/media/video/tm6000/tm6000-usb-isoc.h50
-rw-r--r--drivers/media/video/tm6000/tm6000-video.c1818
-rw-r--r--drivers/media/video/tm6000/tm6000.h399
-rw-r--r--drivers/media/video/tveeprom.c792
-rw-r--r--drivers/media/video/tvp5150.c1235
-rw-r--r--drivers/media/video/tvp7002.c1145
-rw-r--r--drivers/media/video/tw9910.c958
-rw-r--r--drivers/media/video/upd64083.c246
-rw-r--r--drivers/media/video/usbvision/Kconfig12
-rw-r--r--drivers/media/video/usbvision/Makefile6
-rw-r--r--drivers/media/video/usbvision/usbvision-cards.c1133
-rw-r--r--drivers/media/video/usbvision/usbvision-cards.h69
-rw-r--r--drivers/media/video/usbvision/usbvision-core.c2518
-rw-r--r--drivers/media/video/usbvision/usbvision-i2c.c456
-rw-r--r--drivers/media/video/usbvision/usbvision-video.c1690
-rw-r--r--drivers/media/video/usbvision/usbvision.h535
-rw-r--r--drivers/media/video/uvc/Kconfig18
-rw-r--r--drivers/media/video/uvc/Makefile6
-rw-r--r--drivers/media/video/uvc/uvc_ctrl.c2166
-rw-r--r--drivers/media/video/uvc/uvc_debugfs.c136
-rw-r--r--drivers/media/video/uvc/uvc_driver.c2448
-rw-r--r--drivers/media/video/uvc/uvc_entity.c126
-rw-r--r--drivers/media/video/uvc/uvc_isight.c137
-rw-r--r--drivers/media/video/uvc/uvc_queue.c359
-rw-r--r--drivers/media/video/uvc/uvc_status.c237
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c1317
-rw-r--r--drivers/media/video/uvc/uvc_video.c1857
-rw-r--r--drivers/media/video/uvc/uvcvideo.h709
-rw-r--r--drivers/media/video/v4l2-common.c623
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c1045
-rw-r--r--drivers/media/video/v4l2-ctrls.c2650
-rw-r--r--drivers/media/video/v4l2-dev.c993
-rw-r--r--drivers/media/video/v4l2-device.c280
-rw-r--r--drivers/media/video/v4l2-event.c313
-rw-r--r--drivers/media/video/v4l2-fh.c120
-rw-r--r--drivers/media/video/v4l2-int-device.c164
-rw-r--r--drivers/media/video/v4l2-ioctl.c2295
-rw-r--r--drivers/media/video/v4l2-mem2mem.c631
-rw-r--r--drivers/media/video/v4l2-subdev.c470
-rw-r--r--drivers/media/video/via-camera.c1514
-rw-r--r--drivers/media/video/videobuf-core.c1205
-rw-r--r--drivers/media/video/videobuf-dma-contig.c499
-rw-r--r--drivers/media/video/videobuf-dma-sg.c633
-rw-r--r--drivers/media/video/videobuf-dvb.c403
-rw-r--r--drivers/media/video/videobuf-vmalloc.c349
-rw-r--r--drivers/media/video/videobuf2-core.c2120
-rw-r--r--drivers/media/video/videobuf2-dma-contig.c186
-rw-r--r--drivers/media/video/videobuf2-dma-sg.c283
-rw-r--r--drivers/media/video/videobuf2-memops.c227
-rw-r--r--drivers/media/video/videobuf2-vmalloc.c222
-rw-r--r--drivers/media/video/vino.c4349
-rw-r--r--drivers/media/video/vino.h138
-rw-r--r--drivers/media/video/vivi.c1537
-rw-r--r--drivers/media/video/vp27smpx.c211
-rw-r--r--drivers/media/video/vs6624.c928
-rw-r--r--drivers/media/video/vs6624_regs.h337
-rw-r--r--drivers/media/video/w9966.c981
-rw-r--r--drivers/media/video/zoran/Kconfig74
-rw-r--r--drivers/media/video/zoran/Makefile6
-rw-r--r--drivers/media/video/zoran/videocodec.c407
-rw-r--r--drivers/media/video/zoran/videocodec.h353
-rw-r--r--drivers/media/video/zoran/zoran.h401
-rw-r--r--drivers/media/video/zoran/zoran_card.c1524
-rw-r--r--drivers/media/video/zoran/zoran_card.h54
-rw-r--r--drivers/media/video/zoran/zoran_device.c1640
-rw-r--r--drivers/media/video/zoran/zoran_device.h95
-rw-r--r--drivers/media/video/zoran/zoran_driver.c3090
-rw-r--r--drivers/media/video/zoran/zoran_procfs.c225
-rw-r--r--drivers/media/video/zoran/zoran_procfs.h36
-rw-r--r--drivers/media/video/zoran/zr36016.c524
-rw-r--r--drivers/media/video/zoran/zr36016.h111
-rw-r--r--drivers/media/video/zoran/zr36050.c900
-rw-r--r--drivers/media/video/zoran/zr36057.h168
-rw-r--r--drivers/media/video/zoran/zr36060.c1010
-rw-r--r--drivers/media/video/zoran/zr36060.h220
-rw-r--r--drivers/media/video/zr364xx.c1701
3945 files changed, 1154222 insertions, 491122 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 9575db429df4..6abc9302cd84 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -1,129 +1,268 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Multimedia device configuration
#
+#
+# NOTE: CEC and Remote Controller support should not depend on MEDIA_SUPPORT
+#
+source "drivers/media/rc/Kconfig"
+source "drivers/media/cec/Kconfig"
+
menuconfig MEDIA_SUPPORT
tristate "Multimedia support"
depends on HAS_IOMEM
help
- If you want to use Video for Linux, DVB for Linux, or DAB adapters,
- enable this option and other options below.
+ If you want to use media devices, including Webcams, Video grabber
+ devices and/or TV devices, V4L2 codecs, etc, enable this option
+ and other options below.
+
+ Additional info and docs are available on the web at
+ <https://linuxtv.org>
if MEDIA_SUPPORT
-comment "Multimedia core support"
+config MEDIA_SUPPORT_FILTER
+ bool "Filter media drivers"
+ default y if !EXPERT
+ help
+ Configuring the media subsystem can be complex, as there are
+ hundreds of drivers and other config options.
-#
-# Media controller
-#
+ This menu offers option that will help the Kernel's config
+ system to hide drivers that are out of the scope of the
+ user needs, and disabling core support for unused APIs.
-config MEDIA_CONTROLLER
- bool "Media Controller API (EXPERIMENTAL)"
- depends on EXPERIMENTAL
- ---help---
- Enable the media controller API used to query media devices internal
- topology and configure it dynamically.
+ If not selected, all non-optional media core functionality
+ needed to support media drivers will be enabled. Also, all
+ media device drivers should be shown.
- This API is mostly used by camera interfaces in embedded platforms.
+config MEDIA_SUBDRV_AUTOSELECT
+ bool "Autoselect ancillary drivers (tuners, sensors, i2c, spi, frontends)"
+ depends on HAS_IOMEM
+ select I2C
+ select I2C_MUX
+ default y if MEDIA_SUPPORT_FILTER
+ help
+ By default, a media driver auto-selects all possible ancillary
+ devices such as tuners, sensors, video encoders/decoders and
+ frontends, that are used by any of the supported devices.
+
+ This is generally the right thing to do, except when there
+ are strict constraints with regards to the kernel size,
+ like on embedded systems.
+
+ Use this option with care, as deselecting ancillary drivers which
+ are, in fact, necessary will result in the lack of the needed
+ functionality for your device (it may not tune or may not have
+ the needed demodulators).
+
+ If unsure say Y.
+
+menu "Media device types"
#
-# V4L core and enabled API's
+# Multimedia support - automatically enable V4L2 and DVB core
#
+config MEDIA_CAMERA_SUPPORT
+ bool
+ prompt "Cameras and video grabbers" if MEDIA_SUPPORT_FILTER
+ default y if !MEDIA_SUPPORT_FILTER
+ help
+ Enable support for webcams and video grabbers.
-config VIDEO_DEV
- tristate "Video For Linux"
- ---help---
- V4L core support for video capture and overlay devices, webcams and
- AM/FM radio cards.
+ Say Y when you have a webcam or a video capture grabber board.
+
+config MEDIA_ANALOG_TV_SUPPORT
+ bool
+ prompt "Analog TV" if MEDIA_SUPPORT_FILTER
+ default y if !MEDIA_SUPPORT_FILTER
+ help
+ Enable analog TV support.
+
+ Say Y when you have a TV board with analog support or with a
+ hybrid analog/digital TV chipset.
+
+ Note: There are several DVB cards that are based on chips that
+ support both analog and digital TV. Disabling this option
+ will disable support for them.
- This kernel includes support for the new Video for Linux Two API,
- (V4L2).
+config MEDIA_DIGITAL_TV_SUPPORT
+ bool
+ prompt "Digital TV" if MEDIA_SUPPORT_FILTER
+ default y if !MEDIA_SUPPORT_FILTER
+ help
+ Enable digital TV support.
+
+ Say Y when you have a board with digital support or a board with
+ hybrid digital TV and analog TV.
+
+config MEDIA_RADIO_SUPPORT
+ bool
+ prompt "AM/FM radio receivers/transmitters" if MEDIA_SUPPORT_FILTER
+ default y if !MEDIA_SUPPORT_FILTER
+ help
+ Enable AM/FM radio support.
Additional info and docs are available on the web at
- <http://linuxtv.org>
+ <https://linuxtv.org>
- Documentation for V4L2 is also available on the web at
- <http://bytesex.org/v4l/>.
+ Say Y when you have a board with radio support.
- To compile this driver as a module, choose M here: the
- module will be called videodev.
+ Note: There are several TV cards that are based on chips that
+ support radio reception. Disabling this option will
+ disable support for them.
-config VIDEO_V4L2_COMMON
- tristate
- depends on (I2C || I2C=n) && VIDEO_DEV
- default (I2C || I2C=n) && VIDEO_DEV
+config MEDIA_SDR_SUPPORT
+ bool
+ prompt "Software defined radio" if MEDIA_SUPPORT_FILTER
+ default y if !MEDIA_SUPPORT_FILTER
+ help
+ Enable software defined radio support.
+
+ Say Y when you have a software defined radio device.
+
+config MEDIA_PLATFORM_SUPPORT
+ bool
+ prompt "Platform-specific devices" if MEDIA_SUPPORT_FILTER
+ default y if !MEDIA_SUPPORT_FILTER
+ help
+ Enable support for complex cameras, codecs, and other hardware
+ that are integrated at the CPU, GPU or on Image Signalling Processor
+ and don't use PCI, USB or Firewire buses.
-config VIDEO_V4L2_SUBDEV_API
- bool "V4L2 sub-device userspace API (EXPERIMENTAL)"
- depends on VIDEO_DEV && MEDIA_CONTROLLER && EXPERIMENTAL
- ---help---
- Enables the V4L2 sub-device pad-level userspace API used to configure
- video format, size and frame rate between hardware blocks.
+ This is found on Embedded hardware (SoC), on V4L2 codecs and
+ on some GPU and newer CPU chipsets.
+
+ Say Y when you want to be able to see such devices.
+
+config MEDIA_TEST_SUPPORT
+ bool
+ prompt "Test drivers" if MEDIA_SUPPORT_FILTER
+ default y if !MEDIA_SUPPORT_FILTER
+ help
+ These drivers should not be used on production kernels, but
+ can be useful on debug ones. This option enables several dummy drivers
+ that simulate real hardware. Very useful to test userspace
+ applications and to validate if the subsystem core doesn't
+ have regressions.
+
+ Say Y if you want to use some virtual test driver.
+
+ In case of doubts, say N.
+ Say Y when you have a software defined radio device.
+endmenu # media device types
+
+
+menu "Media core support"
+ visible if !MEDIA_SUPPORT_FILTER
+
+config VIDEO_DEV
+ tristate "Video4Linux core"
+ default MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT || MEDIA_SDR_SUPPORT || MEDIA_PLATFORM_SUPPORT || MEDIA_TEST_SUPPORT
+ depends on (I2C || I2C=n)
+ select RATIONAL
+ select VIDEOBUF2_V4L2 if VIDEOBUF2_CORE
+ help
+ Enables the V4L2 API, used by cameras, analog TV, video grabbers,
+ radio devices and by some input devices.
+
+config MEDIA_CONTROLLER
+ bool "Media Controller API"
+ default MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_PLATFORM_SUPPORT
+ help
+ Enable the media controller API used to query media devices internal
+ topology and configure it dynamically.
This API is mostly used by camera interfaces in embedded platforms.
#
# DVB Core
+# Only enables if one of DTV is selected
#
config DVB_CORE
- tristate "DVB for Linux"
+ tristate "DVB Core"
+ depends on MEDIA_DIGITAL_TV_SUPPORT
+ depends on (I2C || I2C=n)
+ default MEDIA_DIGITAL_TV_SUPPORT
select CRC32
help
- DVB core utility functions for device handling, software fallbacks etc.
+ Enables the DVB API, used by Digital TV devices. Supports several
+ standards, including DVB, ATSC, ISDB and CMDB.
+
+endmenu # Media core support
- Enable this if you own a DVB/ATSC adapter and want to use it or if
- you compile Linux for a digital SetTopBox.
+#
+# Extra per-media API core functionality
- Say Y when you have a DVB or an ATSC card and want to use it.
+menu "Video4Linux options"
+ visible if VIDEO_DEV
- API specs and user tools are available from <http://www.linuxtv.org/>.
+source "drivers/media/v4l2-core/Kconfig"
+endmenu
- Please report problems regarding this support to the LinuxDVB
- mailing list.
+menu "Media controller options"
+ visible if MEDIA_CONTROLLER
- If unsure say N.
+source "drivers/media/mc/Kconfig"
+endmenu
-config DVB_NET
- bool "DVB Network Support"
- default (NET && INET)
- depends on NET && INET && DVB_CORE
- help
- This option enables DVB Network Support which is a part of the DVB
- standard. It is used, for example, by automatic firmware updates used
- on Set-Top-Boxes. It can also be used to access the Internet via the
- DVB card, if the network provider supports it.
+menu "Digital TV options"
+ visible if DVB_CORE
- You may want to disable the network support on embedded devices. If
- unsure say Y.
+source "drivers/media/dvb-core/Kconfig"
+endmenu
-config VIDEO_MEDIA
- tristate
- default (DVB_CORE && (VIDEO_DEV = n)) || (VIDEO_DEV && (DVB_CORE = n)) || (DVB_CORE && VIDEO_DEV)
+menu "Media drivers"
-comment "Multimedia drivers"
+comment "Drivers filtered as selected at 'Filter media drivers'"
+ depends on MEDIA_SUPPORT_FILTER
-source "drivers/media/common/Kconfig"
-source "drivers/media/rc/Kconfig"
+comment "Media drivers"
-#
-# Tuner drivers for DVB and V4L
-#
+source "drivers/media/usb/Kconfig"
+source "drivers/media/pci/Kconfig"
+source "drivers/media/radio/Kconfig"
-source "drivers/media/common/tuners/Kconfig"
+if MEDIA_PLATFORM_SUPPORT
+source "drivers/media/platform/Kconfig"
+source "drivers/media/mmc/Kconfig"
+endif
-#
-# Video/Radio/Hybrid adapters
-#
+if MEDIA_TEST_SUPPORT
+source "drivers/media/test-drivers/Kconfig"
+endif
-source "drivers/media/video/Kconfig"
+source "drivers/media/firewire/Kconfig"
-source "drivers/media/radio/Kconfig"
+# Common driver options
+source "drivers/media/common/Kconfig"
+
+endmenu
#
-# DVB adapters
+# Ancillary drivers (tuners, i2c, spi, frontends)
#
-source "drivers/media/dvb/Kconfig"
+config MEDIA_HIDE_ANCILLARY_SUBDRV
+ bool
+ depends on MEDIA_SUBDRV_AUTOSELECT && !COMPILE_TEST && !EXPERT
+ default y
+
+menu "Media ancillary drivers"
+
+config MEDIA_ATTACH
+ bool
+ depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT
+ depends on MODULES
+ default MODULES
+
+source "drivers/media/i2c/Kconfig"
+source "drivers/media/spi/Kconfig"
+source "drivers/media/tuners/Kconfig"
+source "drivers/media/dvb-frontends/Kconfig"
+
+endmenu
endif # MEDIA_SUPPORT
diff --git a/drivers/media/Makefile b/drivers/media/Makefile
index 64755c99ded2..20fac24e4f0f 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -1,14 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the kernel multimedia device drivers.
#
-media-objs := media-device.o media-devnode.o media-entity.o
+#
+# I2C drivers should come before other drivers, otherwise they'll fail
+# when compiled as builtin drivers
+#
+obj-y += i2c/ tuners/
+obj-$(CONFIG_DVB_CORE) += dvb-frontends/
+#
+# Now, let's link-in the media controller core
+#
ifeq ($(CONFIG_MEDIA_CONTROLLER),y)
- obj-$(CONFIG_MEDIA_SUPPORT) += media.o
+ obj-$(CONFIG_MEDIA_SUPPORT) += mc/
endif
-obj-y += common/ rc/ video/
+obj-$(CONFIG_VIDEO_DEV) += v4l2-core/
+obj-$(CONFIG_DVB_CORE) += dvb-core/
+
+# There are both core and drivers at RC subtree - merge before drivers
+obj-y += rc/
+obj-$(CONFIG_CEC_CORE) += cec/
+
+#
+# Finally, merge the drivers that require the core
+#
+
+obj-y += common/ platform/ pci/ usb/ mmc/ firewire/ spi/ test-drivers/
obj-$(CONFIG_VIDEO_DEV) += radio/
-obj-$(CONFIG_DVB_CORE) += dvb/
+
diff --git a/drivers/media/cec/Kconfig b/drivers/media/cec/Kconfig
new file mode 100644
index 000000000000..94ef3349b8d6
--- /dev/null
+++ b/drivers/media/cec/Kconfig
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config CEC_CORE
+ tristate
+
+config CEC_NOTIFIER
+ bool
+
+config CEC_PIN
+ bool
+
+menu "CEC support"
+
+config MEDIA_CEC_RC
+ bool "HDMI CEC RC integration"
+ depends on CEC_CORE && RC_CORE
+ depends on CEC_CORE=m || RC_CORE=y
+ help
+ Pass on CEC remote control messages to the RC framework.
+
+config CEC_PIN_ERROR_INJ
+ bool "Enable CEC error injection support"
+ depends on CEC_PIN && DEBUG_FS
+ help
+ This option enables CEC error injection using debugfs.
+
+menuconfig MEDIA_CEC_SUPPORT
+ bool
+ prompt "HDMI CEC drivers"
+ default y if MEDIA_SUPPORT && !MEDIA_SUPPORT_FILTER
+ help
+ Enable support for HDMI CEC (Consumer Electronics Control),
+ which is an optional HDMI feature.
+
+ Say Y when you have an HDMI receiver, transmitter or a USB CEC
+ adapter that supports HDMI CEC.
+
+if MEDIA_CEC_SUPPORT
+source "drivers/media/cec/i2c/Kconfig"
+source "drivers/media/cec/platform/Kconfig"
+source "drivers/media/cec/usb/Kconfig"
+endif
+
+endmenu
diff --git a/drivers/media/cec/Makefile b/drivers/media/cec/Makefile
new file mode 100644
index 000000000000..23539339bc81
--- /dev/null
+++ b/drivers/media/cec/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-y += core/ i2c/ platform/ usb/
diff --git a/drivers/media/cec/core/Makefile b/drivers/media/cec/core/Makefile
new file mode 100644
index 000000000000..ad8677d8c896
--- /dev/null
+++ b/drivers/media/cec/core/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+cec-objs := cec-core.o cec-adap.o cec-api.o
+
+ifeq ($(CONFIG_CEC_NOTIFIER),y)
+ cec-objs += cec-notifier.o
+endif
+
+ifeq ($(CONFIG_CEC_PIN),y)
+ cec-objs += cec-pin.o
+endif
+
+ifeq ($(CONFIG_CEC_PIN_ERROR_INJ),y)
+ cec-objs += cec-pin-error-inj.o
+endif
+
+obj-$(CONFIG_CEC_CORE) += cec.o
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
new file mode 100644
index 000000000000..ba6828ef540e
--- /dev/null
+++ b/drivers/media/cec/core/cec-adap.c
@@ -0,0 +1,2364 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cec-adap.c - HDMI Consumer Electronics Control framework - CEC adapter
+ *
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/ktime.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_device.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_file.h>
+
+#include "cec-priv.h"
+
+static void cec_fill_msg_report_features(struct cec_adapter *adap,
+ struct cec_msg *msg,
+ unsigned int la_idx);
+
+static int cec_log_addr2idx(const struct cec_adapter *adap, u8 log_addr)
+{
+ int i;
+
+ for (i = 0; i < adap->log_addrs.num_log_addrs; i++)
+ if (adap->log_addrs.log_addr[i] == log_addr)
+ return i;
+ return -1;
+}
+
+static unsigned int cec_log_addr2dev(const struct cec_adapter *adap, u8 log_addr)
+{
+ int i = cec_log_addr2idx(adap, log_addr);
+
+ return adap->log_addrs.primary_device_type[i < 0 ? 0 : i];
+}
+
+u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size,
+ unsigned int *offset)
+{
+ unsigned int loc = cec_get_edid_spa_location(edid, size);
+
+ if (offset)
+ *offset = loc;
+ if (loc == 0)
+ return CEC_PHYS_ADDR_INVALID;
+ return (edid[loc] << 8) | edid[loc + 1];
+}
+EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr);
+
+void cec_fill_conn_info_from_drm(struct cec_connector_info *conn_info,
+ const struct drm_connector *connector)
+{
+ memset(conn_info, 0, sizeof(*conn_info));
+ conn_info->type = CEC_CONNECTOR_TYPE_DRM;
+ conn_info->drm.card_no = connector->dev->primary->index;
+ conn_info->drm.connector_id = connector->base.id;
+}
+EXPORT_SYMBOL_GPL(cec_fill_conn_info_from_drm);
+
+/*
+ * Queue a new event for this filehandle. If ts == 0, then set it
+ * to the current time.
+ *
+ * We keep a queue of at most max_event events where max_event differs
+ * per event. If the queue becomes full, then drop the oldest event and
+ * keep track of how many events we've dropped.
+ */
+void cec_queue_event_fh(struct cec_fh *fh,
+ const struct cec_event *new_ev, u64 ts)
+{
+ static const u16 max_events[CEC_NUM_EVENTS] = {
+ 1, 1, 800, 800, 8, 8, 8, 8
+ };
+ struct cec_event_entry *entry;
+ unsigned int ev_idx = new_ev->event - 1;
+
+ if (WARN_ON(ev_idx >= ARRAY_SIZE(fh->events)))
+ return;
+
+ if (ts == 0)
+ ts = ktime_get_ns();
+
+ mutex_lock(&fh->lock);
+ if (ev_idx < CEC_NUM_CORE_EVENTS)
+ entry = &fh->core_events[ev_idx];
+ else
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (entry) {
+ if (new_ev->event == CEC_EVENT_LOST_MSGS &&
+ fh->queued_events[ev_idx]) {
+ entry->ev.lost_msgs.lost_msgs +=
+ new_ev->lost_msgs.lost_msgs;
+ goto unlock;
+ }
+ entry->ev = *new_ev;
+ entry->ev.ts = ts;
+
+ if (fh->queued_events[ev_idx] < max_events[ev_idx]) {
+ /* Add new msg at the end of the queue */
+ list_add_tail(&entry->list, &fh->events[ev_idx]);
+ fh->queued_events[ev_idx]++;
+ fh->total_queued_events++;
+ goto unlock;
+ }
+
+ if (ev_idx >= CEC_NUM_CORE_EVENTS) {
+ list_add_tail(&entry->list, &fh->events[ev_idx]);
+ /* drop the oldest event */
+ entry = list_first_entry(&fh->events[ev_idx],
+ struct cec_event_entry, list);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ }
+ /* Mark that events were lost */
+ entry = list_first_entry_or_null(&fh->events[ev_idx],
+ struct cec_event_entry, list);
+ if (entry)
+ entry->ev.flags |= CEC_EVENT_FL_DROPPED_EVENTS;
+
+unlock:
+ mutex_unlock(&fh->lock);
+ wake_up_interruptible(&fh->wait);
+}
+
+/* Queue a new event for all open filehandles. */
+static void cec_queue_event(struct cec_adapter *adap,
+ const struct cec_event *ev)
+{
+ u64 ts = ktime_get_ns();
+ struct cec_fh *fh;
+
+ mutex_lock(&adap->devnode.lock_fhs);
+ list_for_each_entry(fh, &adap->devnode.fhs, list)
+ cec_queue_event_fh(fh, ev, ts);
+ mutex_unlock(&adap->devnode.lock_fhs);
+}
+
+/* Notify userspace that the CEC pin changed state at the given time. */
+void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high,
+ bool dropped_events, ktime_t ts)
+{
+ struct cec_event ev = {
+ .event = is_high ? CEC_EVENT_PIN_CEC_HIGH :
+ CEC_EVENT_PIN_CEC_LOW,
+ .flags = dropped_events ? CEC_EVENT_FL_DROPPED_EVENTS : 0,
+ };
+ struct cec_fh *fh;
+
+ mutex_lock(&adap->devnode.lock_fhs);
+ list_for_each_entry(fh, &adap->devnode.fhs, list) {
+ if (fh->mode_follower == CEC_MODE_MONITOR_PIN)
+ cec_queue_event_fh(fh, &ev, ktime_to_ns(ts));
+ }
+ mutex_unlock(&adap->devnode.lock_fhs);
+}
+EXPORT_SYMBOL_GPL(cec_queue_pin_cec_event);
+
+/* Notify userspace that the HPD pin changed state at the given time. */
+void cec_queue_pin_hpd_event(struct cec_adapter *adap, bool is_high, ktime_t ts)
+{
+ struct cec_event ev = {
+ .event = is_high ? CEC_EVENT_PIN_HPD_HIGH :
+ CEC_EVENT_PIN_HPD_LOW,
+ };
+ struct cec_fh *fh;
+
+ mutex_lock(&adap->devnode.lock_fhs);
+ list_for_each_entry(fh, &adap->devnode.fhs, list)
+ cec_queue_event_fh(fh, &ev, ktime_to_ns(ts));
+ mutex_unlock(&adap->devnode.lock_fhs);
+}
+EXPORT_SYMBOL_GPL(cec_queue_pin_hpd_event);
+
+/* Notify userspace that the 5V pin changed state at the given time. */
+void cec_queue_pin_5v_event(struct cec_adapter *adap, bool is_high, ktime_t ts)
+{
+ struct cec_event ev = {
+ .event = is_high ? CEC_EVENT_PIN_5V_HIGH :
+ CEC_EVENT_PIN_5V_LOW,
+ };
+ struct cec_fh *fh;
+
+ mutex_lock(&adap->devnode.lock_fhs);
+ list_for_each_entry(fh, &adap->devnode.fhs, list)
+ cec_queue_event_fh(fh, &ev, ktime_to_ns(ts));
+ mutex_unlock(&adap->devnode.lock_fhs);
+}
+EXPORT_SYMBOL_GPL(cec_queue_pin_5v_event);
+
+/*
+ * Queue a new message for this filehandle.
+ *
+ * We keep a queue of at most CEC_MAX_MSG_RX_QUEUE_SZ messages. If the
+ * queue becomes full, then drop the oldest message and keep track
+ * of how many messages we've dropped.
+ */
+static void cec_queue_msg_fh(struct cec_fh *fh, const struct cec_msg *msg)
+{
+ static const struct cec_event ev_lost_msgs = {
+ .event = CEC_EVENT_LOST_MSGS,
+ .flags = 0,
+ {
+ .lost_msgs = { 1 },
+ },
+ };
+ struct cec_msg_entry *entry;
+
+ mutex_lock(&fh->lock);
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (entry) {
+ entry->msg = *msg;
+ /* Add new msg at the end of the queue */
+ list_add_tail(&entry->list, &fh->msgs);
+
+ if (fh->queued_msgs < CEC_MAX_MSG_RX_QUEUE_SZ) {
+ /* All is fine if there is enough room */
+ fh->queued_msgs++;
+ mutex_unlock(&fh->lock);
+ wake_up_interruptible(&fh->wait);
+ return;
+ }
+
+ /*
+ * if the message queue is full, then drop the oldest one and
+ * send a lost message event.
+ */
+ entry = list_first_entry(&fh->msgs, struct cec_msg_entry, list);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ mutex_unlock(&fh->lock);
+
+ /*
+ * We lost a message, either because kmalloc failed or the queue
+ * was full.
+ */
+ cec_queue_event_fh(fh, &ev_lost_msgs, ktime_get_ns());
+}
+
+/*
+ * Queue the message for those filehandles that are in monitor mode.
+ * If valid_la is true (this message is for us or was sent by us),
+ * then pass it on to any monitoring filehandle. If this message
+ * isn't for us or from us, then only give it to filehandles that
+ * are in MONITOR_ALL mode.
+ *
+ * This can only happen if the CEC_CAP_MONITOR_ALL capability is
+ * set and the CEC adapter was placed in 'monitor all' mode.
+ */
+static void cec_queue_msg_monitor(struct cec_adapter *adap,
+ const struct cec_msg *msg,
+ bool valid_la)
+{
+ struct cec_fh *fh;
+ u32 monitor_mode = valid_la ? CEC_MODE_MONITOR :
+ CEC_MODE_MONITOR_ALL;
+
+ mutex_lock(&adap->devnode.lock_fhs);
+ list_for_each_entry(fh, &adap->devnode.fhs, list) {
+ if (fh->mode_follower >= monitor_mode)
+ cec_queue_msg_fh(fh, msg);
+ }
+ mutex_unlock(&adap->devnode.lock_fhs);
+}
+
+/*
+ * Queue the message for follower filehandles.
+ */
+static void cec_queue_msg_followers(struct cec_adapter *adap,
+ const struct cec_msg *msg)
+{
+ struct cec_fh *fh;
+
+ mutex_lock(&adap->devnode.lock_fhs);
+ list_for_each_entry(fh, &adap->devnode.fhs, list) {
+ if (fh->mode_follower == CEC_MODE_FOLLOWER)
+ cec_queue_msg_fh(fh, msg);
+ }
+ mutex_unlock(&adap->devnode.lock_fhs);
+}
+
+/* Notify userspace of an adapter state change. */
+static void cec_post_state_event(struct cec_adapter *adap)
+{
+ struct cec_event ev = {
+ .event = CEC_EVENT_STATE_CHANGE,
+ };
+
+ ev.state_change.phys_addr = adap->phys_addr;
+ ev.state_change.log_addr_mask = adap->log_addrs.log_addr_mask;
+ ev.state_change.have_conn_info =
+ adap->conn_info.type != CEC_CONNECTOR_TYPE_NO_CONNECTOR;
+ cec_queue_event(adap, &ev);
+}
+
+/*
+ * A CEC transmit (and a possible wait for reply) completed.
+ * If this was in blocking mode, then complete it, otherwise
+ * queue the message for userspace to dequeue later.
+ *
+ * This function is called with adap->lock held.
+ */
+static void cec_data_completed(struct cec_data *data)
+{
+ /*
+ * Delete this transmit from the filehandle's xfer_list since
+ * we're done with it.
+ *
+ * Note that if the filehandle is closed before this transmit
+ * finished, then the release() function will set data->fh to NULL.
+ * Without that we would be referring to a closed filehandle.
+ */
+ if (data->fh)
+ list_del_init(&data->xfer_list);
+
+ if (data->blocking) {
+ /*
+ * Someone is blocking so mark the message as completed
+ * and call complete.
+ */
+ data->completed = true;
+ complete(&data->c);
+ } else {
+ /*
+ * No blocking, so just queue the message if needed and
+ * free the memory.
+ */
+ if (data->fh)
+ cec_queue_msg_fh(data->fh, &data->msg);
+ kfree(data);
+ }
+}
+
+/*
+ * A pending CEC transmit needs to be cancelled, either because the CEC
+ * adapter is disabled or the transmit takes an impossibly long time to
+ * finish, or the reply timed out.
+ *
+ * This function is called with adap->lock held.
+ */
+static void cec_data_cancel(struct cec_data *data, u8 tx_status, u8 rx_status)
+{
+ struct cec_adapter *adap = data->adap;
+
+ /*
+ * It's either the current transmit, or it is a pending
+ * transmit. Take the appropriate action to clear it.
+ */
+ if (adap->transmitting == data) {
+ adap->transmitting = NULL;
+ } else {
+ list_del_init(&data->list);
+ if (!(data->msg.tx_status & CEC_TX_STATUS_OK))
+ if (!WARN_ON(!adap->transmit_queue_sz))
+ adap->transmit_queue_sz--;
+ }
+
+ if (data->msg.tx_status & CEC_TX_STATUS_OK) {
+ data->msg.rx_ts = ktime_get_ns();
+ data->msg.rx_status = rx_status;
+ if (!data->blocking)
+ data->msg.tx_status = 0;
+ } else {
+ data->msg.tx_ts = ktime_get_ns();
+ data->msg.tx_status |= tx_status |
+ CEC_TX_STATUS_MAX_RETRIES;
+ data->msg.tx_error_cnt++;
+ data->attempts = 0;
+ if (!data->blocking)
+ data->msg.rx_status = 0;
+ }
+
+ /* Queue transmitted message for monitoring purposes */
+ cec_queue_msg_monitor(adap, &data->msg, 1);
+
+ if (!data->blocking && data->msg.sequence)
+ /* Allow drivers to react to a canceled transmit */
+ call_void_op(adap, adap_nb_transmit_canceled, &data->msg);
+
+ cec_data_completed(data);
+}
+
+/*
+ * Flush all pending transmits and cancel any pending timeout work.
+ *
+ * This function is called with adap->lock held.
+ */
+static void cec_flush(struct cec_adapter *adap)
+{
+ struct cec_data *data, *n;
+
+ /*
+ * If the adapter is disabled, or we're asked to stop,
+ * then cancel any pending transmits.
+ */
+ while (!list_empty(&adap->transmit_queue)) {
+ data = list_first_entry(&adap->transmit_queue,
+ struct cec_data, list);
+ cec_data_cancel(data, CEC_TX_STATUS_ABORTED, 0);
+ }
+ if (adap->transmitting)
+ adap->transmit_in_progress_aborted = true;
+
+ /* Cancel the pending timeout work. */
+ list_for_each_entry_safe(data, n, &adap->wait_queue, list) {
+ if (cancel_delayed_work(&data->work))
+ cec_data_cancel(data, CEC_TX_STATUS_OK, CEC_RX_STATUS_ABORTED);
+ /*
+ * If cancel_delayed_work returned false, then
+ * the cec_wait_timeout function is running,
+ * which will call cec_data_completed. So no
+ * need to do anything special in that case.
+ */
+ }
+ /*
+ * If something went wrong and this counter isn't what it should
+ * be, then this will reset it back to 0. Warn if it is not 0,
+ * since it indicates a bug, either in this framework or in a
+ * CEC driver.
+ */
+ if (WARN_ON(adap->transmit_queue_sz))
+ adap->transmit_queue_sz = 0;
+}
+
+/*
+ * Main CEC state machine
+ *
+ * Wait until the thread should be stopped, or we are not transmitting and
+ * a new transmit message is queued up, in which case we start transmitting
+ * that message. When the adapter finished transmitting the message it will
+ * call cec_transmit_done().
+ *
+ * If the adapter is disabled, then remove all queued messages instead.
+ *
+ * If the current transmit times out, then cancel that transmit.
+ */
+int cec_thread_func(void *_adap)
+{
+ struct cec_adapter *adap = _adap;
+
+ for (;;) {
+ unsigned int signal_free_time;
+ struct cec_data *data;
+ bool timeout = false;
+ u8 attempts;
+
+ if (adap->transmit_in_progress) {
+ int err;
+
+ /*
+ * We are transmitting a message, so add a timeout
+ * to prevent the state machine to get stuck waiting
+ * for this message to finalize and add a check to
+ * see if the adapter is disabled in which case the
+ * transmit should be canceled.
+ */
+ err = wait_event_interruptible_timeout(adap->kthread_waitq,
+ (adap->needs_hpd &&
+ (!adap->is_configured && !adap->is_configuring)) ||
+ kthread_should_stop() ||
+ (!adap->transmit_in_progress &&
+ !list_empty(&adap->transmit_queue)),
+ msecs_to_jiffies(adap->xfer_timeout_ms));
+ timeout = err == 0;
+ } else {
+ /* Otherwise we just wait for something to happen. */
+ wait_event_interruptible(adap->kthread_waitq,
+ kthread_should_stop() ||
+ (!adap->transmit_in_progress &&
+ !list_empty(&adap->transmit_queue)));
+ }
+
+ mutex_lock(&adap->lock);
+
+ if ((adap->needs_hpd &&
+ (!adap->is_configured && !adap->is_configuring)) ||
+ kthread_should_stop()) {
+ cec_flush(adap);
+ goto unlock;
+ }
+
+ if (adap->transmit_in_progress &&
+ adap->transmit_in_progress_aborted) {
+ if (adap->transmitting)
+ cec_data_cancel(adap->transmitting,
+ CEC_TX_STATUS_ABORTED, 0);
+ adap->transmit_in_progress = false;
+ adap->transmit_in_progress_aborted = false;
+ goto unlock;
+ }
+ if (adap->transmit_in_progress && timeout) {
+ /*
+ * If we timeout, then log that. Normally this does
+ * not happen and it is an indication of a faulty CEC
+ * adapter driver, or the CEC bus is in some weird
+ * state. On rare occasions it can happen if there is
+ * so much traffic on the bus that the adapter was
+ * unable to transmit for xfer_timeout_ms (2.1s by
+ * default).
+ */
+ if (adap->transmitting) {
+ pr_warn("cec-%s: message %*ph timed out\n", adap->name,
+ adap->transmitting->msg.len,
+ adap->transmitting->msg.msg);
+ /* Just give up on this. */
+ cec_data_cancel(adap->transmitting,
+ CEC_TX_STATUS_TIMEOUT, 0);
+ } else {
+ pr_warn("cec-%s: transmit timed out\n", adap->name);
+ }
+ adap->transmit_in_progress = false;
+ adap->tx_timeout_cnt++;
+ goto unlock;
+ }
+
+ /*
+ * If we are still transmitting, or there is nothing new to
+ * transmit, then just continue waiting.
+ */
+ if (adap->transmit_in_progress || list_empty(&adap->transmit_queue))
+ goto unlock;
+
+ /* Get a new message to transmit */
+ data = list_first_entry(&adap->transmit_queue,
+ struct cec_data, list);
+ list_del_init(&data->list);
+ if (!WARN_ON(!data->adap->transmit_queue_sz))
+ adap->transmit_queue_sz--;
+
+ /* Make this the current transmitting message */
+ adap->transmitting = data;
+
+ /*
+ * Suggested number of attempts as per the CEC 2.0 spec:
+ * 4 attempts is the default, except for 'secondary poll
+ * messages', i.e. poll messages not sent during the adapter
+ * configuration phase when it allocates logical addresses.
+ */
+ if (data->msg.len == 1 && adap->is_configured)
+ attempts = 2;
+ else
+ attempts = 4;
+
+ /* Set the suggested signal free time */
+ if (data->attempts) {
+ /* should be >= 3 data bit periods for a retry */
+ signal_free_time = CEC_SIGNAL_FREE_TIME_RETRY;
+ } else if (adap->last_initiator !=
+ cec_msg_initiator(&data->msg)) {
+ /* should be >= 5 data bit periods for new initiator */
+ signal_free_time = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
+ adap->last_initiator = cec_msg_initiator(&data->msg);
+ } else {
+ /*
+ * should be >= 7 data bit periods for sending another
+ * frame immediately after another.
+ */
+ signal_free_time = CEC_SIGNAL_FREE_TIME_NEXT_XFER;
+ }
+ if (data->attempts == 0)
+ data->attempts = attempts;
+
+ adap->transmit_in_progress_aborted = false;
+ /* Tell the adapter to transmit, cancel on error */
+ if (call_op(adap, adap_transmit, data->attempts,
+ signal_free_time, &data->msg))
+ cec_data_cancel(data, CEC_TX_STATUS_ABORTED, 0);
+ else
+ adap->transmit_in_progress = true;
+
+unlock:
+ mutex_unlock(&adap->lock);
+
+ if (kthread_should_stop())
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Called by the CEC adapter if a transmit finished.
+ */
+void cec_transmit_done_ts(struct cec_adapter *adap, u8 status,
+ u8 arb_lost_cnt, u8 nack_cnt, u8 low_drive_cnt,
+ u8 error_cnt, ktime_t ts)
+{
+ struct cec_data *data;
+ struct cec_msg *msg;
+ unsigned int attempts_made = arb_lost_cnt + nack_cnt +
+ low_drive_cnt + error_cnt;
+ bool done = status & (CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_OK);
+ bool aborted = adap->transmit_in_progress_aborted;
+
+ dprintk(2, "%s: status 0x%02x\n", __func__, status);
+ if (attempts_made < 1)
+ attempts_made = 1;
+
+ mutex_lock(&adap->lock);
+ data = adap->transmitting;
+ if (!data) {
+ /*
+ * This might happen if a transmit was issued and the cable is
+ * unplugged while the transmit is ongoing. Ignore this
+ * transmit in that case.
+ */
+ if (!adap->transmit_in_progress)
+ dprintk(1, "%s was called without an ongoing transmit!\n",
+ __func__);
+ adap->transmit_in_progress = false;
+ goto wake_thread;
+ }
+ adap->transmit_in_progress = false;
+ adap->transmit_in_progress_aborted = false;
+
+ msg = &data->msg;
+
+ /* Drivers must fill in the status! */
+ WARN_ON(status == 0);
+ msg->tx_ts = ktime_to_ns(ts);
+ msg->tx_status |= status;
+ msg->tx_arb_lost_cnt += arb_lost_cnt;
+ msg->tx_nack_cnt += nack_cnt;
+ msg->tx_low_drive_cnt += low_drive_cnt;
+ msg->tx_error_cnt += error_cnt;
+
+ adap->tx_arb_lost_cnt += arb_lost_cnt;
+ adap->tx_low_drive_cnt += low_drive_cnt;
+ adap->tx_error_cnt += error_cnt;
+
+ /*
+ * Low Drive transmission errors should really not happen for
+ * well-behaved CEC devices and proper HDMI cables.
+ *
+ * Ditto for the 'Error' status.
+ *
+ * For the first few times that this happens, log this.
+ * Stop logging after that, since that will not add any more
+ * useful information and instead it will just flood the kernel log.
+ */
+ if (done && adap->tx_low_drive_log_cnt < 8 && msg->tx_low_drive_cnt) {
+ adap->tx_low_drive_log_cnt++;
+ dprintk(0, "low drive counter: %u (seq %u: %*ph)\n",
+ msg->tx_low_drive_cnt, msg->sequence,
+ msg->len, msg->msg);
+ }
+ if (done && adap->tx_error_log_cnt < 8 && msg->tx_error_cnt) {
+ adap->tx_error_log_cnt++;
+ dprintk(0, "error counter: %u (seq %u: %*ph)\n",
+ msg->tx_error_cnt, msg->sequence,
+ msg->len, msg->msg);
+ }
+
+ /* Mark that we're done with this transmit */
+ adap->transmitting = NULL;
+
+ /*
+ * If there are still retry attempts left and there was an error and
+ * the hardware didn't signal that it retried itself (by setting
+ * CEC_TX_STATUS_MAX_RETRIES), then we will retry ourselves.
+ */
+ if (!aborted && data->attempts > attempts_made && !done) {
+ /* Retry this message */
+ data->attempts -= attempts_made;
+ if (msg->timeout)
+ dprintk(2, "retransmit: %*ph (attempts: %d, wait for %*ph)\n",
+ msg->len, msg->msg, data->attempts,
+ data->match_len, data->match_reply);
+ else
+ dprintk(2, "retransmit: %*ph (attempts: %d)\n",
+ msg->len, msg->msg, data->attempts);
+ /* Add the message in front of the transmit queue */
+ list_add(&data->list, &adap->transmit_queue);
+ adap->transmit_queue_sz++;
+ goto wake_thread;
+ }
+
+ if (aborted && !done)
+ status |= CEC_TX_STATUS_ABORTED;
+ data->attempts = 0;
+
+ /* Always set CEC_TX_STATUS_MAX_RETRIES on error */
+ if (!(status & CEC_TX_STATUS_OK))
+ msg->tx_status |= CEC_TX_STATUS_MAX_RETRIES;
+
+ /* Queue transmitted message for monitoring purposes */
+ cec_queue_msg_monitor(adap, msg, 1);
+
+ if ((status & CEC_TX_STATUS_OK) && adap->is_configured &&
+ msg->timeout) {
+ /*
+ * Queue the message into the wait queue if we want to wait
+ * for a reply.
+ */
+ list_add_tail(&data->list, &adap->wait_queue);
+ schedule_delayed_work(&data->work,
+ msecs_to_jiffies(msg->timeout));
+ } else {
+ /* Otherwise we're done */
+ cec_data_completed(data);
+ }
+
+wake_thread:
+ /*
+ * Wake up the main thread to see if another message is ready
+ * for transmitting or to retry the current message.
+ */
+ wake_up_interruptible(&adap->kthread_waitq);
+ mutex_unlock(&adap->lock);
+}
+EXPORT_SYMBOL_GPL(cec_transmit_done_ts);
+
+void cec_transmit_attempt_done_ts(struct cec_adapter *adap,
+ u8 status, ktime_t ts)
+{
+ switch (status & ~CEC_TX_STATUS_MAX_RETRIES) {
+ case CEC_TX_STATUS_OK:
+ cec_transmit_done_ts(adap, status, 0, 0, 0, 0, ts);
+ return;
+ case CEC_TX_STATUS_ARB_LOST:
+ cec_transmit_done_ts(adap, status, 1, 0, 0, 0, ts);
+ return;
+ case CEC_TX_STATUS_NACK:
+ cec_transmit_done_ts(adap, status, 0, 1, 0, 0, ts);
+ return;
+ case CEC_TX_STATUS_LOW_DRIVE:
+ cec_transmit_done_ts(adap, status, 0, 0, 1, 0, ts);
+ return;
+ case CEC_TX_STATUS_ERROR:
+ cec_transmit_done_ts(adap, status, 0, 0, 0, 1, ts);
+ return;
+ default:
+ /* Should never happen */
+ WARN(1, "cec-%s: invalid status 0x%02x\n", adap->name, status);
+ return;
+ }
+}
+EXPORT_SYMBOL_GPL(cec_transmit_attempt_done_ts);
+
+/*
+ * Called when waiting for a reply times out.
+ */
+static void cec_wait_timeout(struct work_struct *work)
+{
+ struct cec_data *data = container_of(work, struct cec_data, work.work);
+ struct cec_adapter *adap = data->adap;
+
+ mutex_lock(&adap->lock);
+ /*
+ * Sanity check in case the timeout and the arrival of the message
+ * happened at the same time.
+ */
+ if (list_empty(&data->list))
+ goto unlock;
+
+ /* Mark the message as timed out */
+ list_del_init(&data->list);
+ cec_data_cancel(data, CEC_TX_STATUS_OK, CEC_RX_STATUS_TIMEOUT);
+unlock:
+ mutex_unlock(&adap->lock);
+}
+
+/*
+ * Transmit a message. The fh argument may be NULL if the transmit is not
+ * associated with a specific filehandle.
+ *
+ * This function is called with adap->lock held.
+ */
+int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
+ struct cec_fh *fh, bool block)
+{
+ struct cec_data *data;
+ bool is_raw = msg_is_raw(msg);
+ bool reply_vendor_id = (msg->flags & CEC_MSG_FL_REPLY_VENDOR_ID) &&
+ msg->len > 1 && msg->msg[1] == CEC_MSG_VENDOR_COMMAND_WITH_ID;
+ int err;
+
+ if (adap->devnode.unregistered)
+ return -ENODEV;
+
+ msg->rx_ts = 0;
+ msg->tx_ts = 0;
+ msg->rx_status = 0;
+ msg->tx_status = 0;
+ msg->tx_arb_lost_cnt = 0;
+ msg->tx_nack_cnt = 0;
+ msg->tx_low_drive_cnt = 0;
+ msg->tx_error_cnt = 0;
+ msg->sequence = 0;
+ msg->flags &= CEC_MSG_FL_REPLY_TO_FOLLOWERS | CEC_MSG_FL_RAW |
+ (reply_vendor_id ? CEC_MSG_FL_REPLY_VENDOR_ID : 0);
+
+ if ((reply_vendor_id || msg->reply) && msg->timeout == 0) {
+ /* Make sure the timeout isn't 0. */
+ msg->timeout = 1000;
+ }
+
+ if (!msg->timeout)
+ msg->flags &= ~CEC_MSG_FL_REPLY_TO_FOLLOWERS;
+
+ /* Sanity checks */
+ if (msg->len == 0 || msg->len > CEC_MAX_MSG_SIZE) {
+ dprintk(1, "%s: invalid length %d\n", __func__, msg->len);
+ return -EINVAL;
+ }
+ if (reply_vendor_id && msg->len < 6) {
+ dprintk(1, "%s: <Vendor Command With ID> message too short\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len);
+
+ if (msg->timeout)
+ dprintk(2, "%s: %*ph (wait for 0x%02x%s)\n",
+ __func__, msg->len, msg->msg, msg->reply,
+ !block ? ", nb" : "");
+ else
+ dprintk(2, "%s: %*ph%s\n",
+ __func__, msg->len, msg->msg, !block ? " (nb)" : "");
+
+ if (msg->timeout && msg->len == 1) {
+ dprintk(1, "%s: can't reply to poll msg\n", __func__);
+ return -EINVAL;
+ }
+
+ if (is_raw) {
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+ } else {
+ /* A CDC-Only device can only send CDC messages */
+ if ((adap->log_addrs.flags & CEC_LOG_ADDRS_FL_CDC_ONLY) &&
+ (msg->len == 1 || msg->msg[1] != CEC_MSG_CDC_MESSAGE)) {
+ dprintk(1, "%s: not a CDC message\n", __func__);
+ return -EINVAL;
+ }
+
+ if (msg->len >= 4 && msg->msg[1] == CEC_MSG_CDC_MESSAGE) {
+ msg->msg[2] = adap->phys_addr >> 8;
+ msg->msg[3] = adap->phys_addr & 0xff;
+ }
+
+ if (msg->len == 1) {
+ if (cec_msg_destination(msg) == 0xf) {
+ dprintk(1, "%s: invalid poll message\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (cec_has_log_addr(adap, cec_msg_destination(msg))) {
+ /*
+ * If the destination is a logical address our
+ * adapter has already claimed, then just NACK
+ * this. It depends on the hardware what it will
+ * do with a POLL to itself (some OK this), so
+ * it is just as easy to handle it here so the
+ * behavior will be consistent.
+ */
+ msg->tx_ts = ktime_get_ns();
+ msg->tx_status = CEC_TX_STATUS_NACK |
+ CEC_TX_STATUS_MAX_RETRIES;
+ msg->tx_nack_cnt = 1;
+ msg->sequence = ++adap->sequence;
+ if (!msg->sequence)
+ msg->sequence = ++adap->sequence;
+ return 0;
+ }
+ }
+ if (msg->len > 1 && !cec_msg_is_broadcast(msg) &&
+ cec_has_log_addr(adap, cec_msg_destination(msg))) {
+ dprintk(1, "%s: destination is the adapter itself\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (msg->len > 1 && adap->is_configured &&
+ !cec_has_log_addr(adap, cec_msg_initiator(msg))) {
+ dprintk(1, "%s: initiator has unknown logical address %d\n",
+ __func__, cec_msg_initiator(msg));
+ return -EINVAL;
+ }
+ /*
+ * Special case: allow Ping and IMAGE/TEXT_VIEW_ON to be
+ * transmitted to a TV, even if the adapter is unconfigured.
+ * This makes it possible to detect or wake up displays that
+ * pull down the HPD when in standby.
+ */
+ if (!adap->is_configured && !adap->is_configuring &&
+ (msg->len > 2 ||
+ cec_msg_destination(msg) != CEC_LOG_ADDR_TV ||
+ (msg->len == 2 && msg->msg[1] != CEC_MSG_IMAGE_VIEW_ON &&
+ msg->msg[1] != CEC_MSG_TEXT_VIEW_ON))) {
+ dprintk(1, "%s: adapter is unconfigured\n", __func__);
+ return -ENONET;
+ }
+ }
+
+ if (!adap->is_configured && !adap->is_configuring) {
+ if (adap->needs_hpd) {
+ dprintk(1, "%s: adapter is unconfigured and needs HPD\n",
+ __func__);
+ return -ENONET;
+ }
+ if (reply_vendor_id || msg->reply) {
+ dprintk(1, "%s: adapter is unconfigured so reply is not supported\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+
+ if (adap->transmit_queue_sz >= CEC_MAX_MSG_TX_QUEUE_SZ) {
+ dprintk(2, "%s: transmit queue full\n", __func__);
+ return -EBUSY;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ msg->sequence = ++adap->sequence;
+ if (!msg->sequence)
+ msg->sequence = ++adap->sequence;
+
+ data->msg = *msg;
+ data->fh = fh;
+ data->adap = adap;
+ data->blocking = block;
+ if (reply_vendor_id) {
+ memcpy(data->match_reply, msg->msg + 1, 4);
+ data->match_reply[4] = msg->reply;
+ data->match_len = 5;
+ } else if (msg->timeout) {
+ data->match_reply[0] = msg->reply;
+ data->match_len = 1;
+ }
+
+ init_completion(&data->c);
+ INIT_DELAYED_WORK(&data->work, cec_wait_timeout);
+
+ if (fh)
+ list_add_tail(&data->xfer_list, &fh->xfer_list);
+ else
+ INIT_LIST_HEAD(&data->xfer_list);
+
+ list_add_tail(&data->list, &adap->transmit_queue);
+ adap->transmit_queue_sz++;
+ if (!adap->transmitting)
+ wake_up_interruptible(&adap->kthread_waitq);
+
+ /* All done if we don't need to block waiting for completion */
+ if (!block)
+ return 0;
+
+ /*
+ * Release the lock and wait, retake the lock afterwards.
+ */
+ mutex_unlock(&adap->lock);
+ err = wait_for_completion_killable(&data->c);
+ cancel_delayed_work_sync(&data->work);
+ mutex_lock(&adap->lock);
+
+ if (err)
+ adap->transmit_in_progress_aborted = true;
+
+ /* Cancel the transmit if it was interrupted */
+ if (!data->completed) {
+ if (data->msg.tx_status & CEC_TX_STATUS_OK)
+ cec_data_cancel(data, CEC_TX_STATUS_OK, CEC_RX_STATUS_ABORTED);
+ else
+ cec_data_cancel(data, CEC_TX_STATUS_ABORTED, 0);
+ }
+
+ /* The transmit completed (possibly with an error) */
+ *msg = data->msg;
+ if (WARN_ON(!list_empty(&data->list)))
+ list_del(&data->list);
+ if (WARN_ON(!list_empty(&data->xfer_list)))
+ list_del(&data->xfer_list);
+ kfree(data);
+ return 0;
+}
+
+/* Helper function to be used by drivers and this framework. */
+int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg,
+ bool block)
+{
+ int ret;
+
+ mutex_lock(&adap->lock);
+ ret = cec_transmit_msg_fh(adap, msg, NULL, block);
+ mutex_unlock(&adap->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cec_transmit_msg);
+
+/*
+ * I don't like forward references but without this the low-level
+ * cec_received_msg() function would come after a bunch of high-level
+ * CEC protocol handling functions. That was very confusing.
+ */
+static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
+ bool is_reply);
+
+#define DIRECTED 0x80
+#define BCAST1_4 0x40
+#define BCAST2_0 0x20 /* broadcast only allowed for >= 2.0 */
+#define BCAST (BCAST1_4 | BCAST2_0)
+#define BOTH (BCAST | DIRECTED)
+
+/*
+ * Specify minimum length and whether the message is directed, broadcast
+ * or both. Messages that do not match the criteria are ignored as per
+ * the CEC specification.
+ */
+static const u8 cec_msg_size[256] = {
+ [CEC_MSG_ACTIVE_SOURCE] = 4 | BCAST,
+ [CEC_MSG_IMAGE_VIEW_ON] = 2 | DIRECTED,
+ [CEC_MSG_TEXT_VIEW_ON] = 2 | DIRECTED,
+ [CEC_MSG_INACTIVE_SOURCE] = 4 | DIRECTED,
+ [CEC_MSG_REQUEST_ACTIVE_SOURCE] = 2 | BCAST,
+ [CEC_MSG_ROUTING_CHANGE] = 6 | BCAST,
+ [CEC_MSG_ROUTING_INFORMATION] = 4 | BCAST,
+ [CEC_MSG_SET_STREAM_PATH] = 4 | BCAST,
+ [CEC_MSG_STANDBY] = 2 | BOTH,
+ [CEC_MSG_RECORD_OFF] = 2 | DIRECTED,
+ [CEC_MSG_RECORD_ON] = 3 | DIRECTED,
+ [CEC_MSG_RECORD_STATUS] = 3 | DIRECTED,
+ [CEC_MSG_RECORD_TV_SCREEN] = 2 | DIRECTED,
+ [CEC_MSG_CLEAR_ANALOGUE_TIMER] = 13 | DIRECTED,
+ [CEC_MSG_CLEAR_DIGITAL_TIMER] = 16 | DIRECTED,
+ [CEC_MSG_CLEAR_EXT_TIMER] = 13 | DIRECTED,
+ [CEC_MSG_SET_ANALOGUE_TIMER] = 13 | DIRECTED,
+ [CEC_MSG_SET_DIGITAL_TIMER] = 16 | DIRECTED,
+ [CEC_MSG_SET_EXT_TIMER] = 13 | DIRECTED,
+ [CEC_MSG_SET_TIMER_PROGRAM_TITLE] = 2 | DIRECTED,
+ [CEC_MSG_TIMER_CLEARED_STATUS] = 3 | DIRECTED,
+ [CEC_MSG_TIMER_STATUS] = 3 | DIRECTED,
+ [CEC_MSG_CEC_VERSION] = 3 | DIRECTED,
+ [CEC_MSG_GET_CEC_VERSION] = 2 | DIRECTED,
+ [CEC_MSG_GIVE_PHYSICAL_ADDR] = 2 | DIRECTED,
+ [CEC_MSG_GET_MENU_LANGUAGE] = 2 | DIRECTED,
+ [CEC_MSG_REPORT_PHYSICAL_ADDR] = 5 | BCAST,
+ [CEC_MSG_SET_MENU_LANGUAGE] = 5 | BCAST,
+ [CEC_MSG_REPORT_FEATURES] = 6 | BCAST,
+ [CEC_MSG_GIVE_FEATURES] = 2 | DIRECTED,
+ [CEC_MSG_DECK_CONTROL] = 3 | DIRECTED,
+ [CEC_MSG_DECK_STATUS] = 3 | DIRECTED,
+ [CEC_MSG_GIVE_DECK_STATUS] = 3 | DIRECTED,
+ [CEC_MSG_PLAY] = 3 | DIRECTED,
+ [CEC_MSG_GIVE_TUNER_DEVICE_STATUS] = 3 | DIRECTED,
+ [CEC_MSG_SELECT_ANALOGUE_SERVICE] = 6 | DIRECTED,
+ [CEC_MSG_SELECT_DIGITAL_SERVICE] = 9 | DIRECTED,
+ [CEC_MSG_TUNER_DEVICE_STATUS] = 7 | DIRECTED,
+ [CEC_MSG_TUNER_STEP_DECREMENT] = 2 | DIRECTED,
+ [CEC_MSG_TUNER_STEP_INCREMENT] = 2 | DIRECTED,
+ [CEC_MSG_DEVICE_VENDOR_ID] = 5 | BCAST,
+ [CEC_MSG_GIVE_DEVICE_VENDOR_ID] = 2 | DIRECTED,
+ [CEC_MSG_VENDOR_COMMAND] = 2 | DIRECTED,
+ [CEC_MSG_VENDOR_COMMAND_WITH_ID] = 5 | BOTH,
+ [CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN] = 2 | BOTH,
+ [CEC_MSG_VENDOR_REMOTE_BUTTON_UP] = 2 | BOTH,
+ [CEC_MSG_SET_OSD_STRING] = 3 | DIRECTED,
+ [CEC_MSG_GIVE_OSD_NAME] = 2 | DIRECTED,
+ [CEC_MSG_SET_OSD_NAME] = 2 | DIRECTED,
+ [CEC_MSG_MENU_REQUEST] = 3 | DIRECTED,
+ [CEC_MSG_MENU_STATUS] = 3 | DIRECTED,
+ [CEC_MSG_USER_CONTROL_PRESSED] = 3 | DIRECTED,
+ [CEC_MSG_USER_CONTROL_RELEASED] = 2 | DIRECTED,
+ [CEC_MSG_GIVE_DEVICE_POWER_STATUS] = 2 | DIRECTED,
+ [CEC_MSG_REPORT_POWER_STATUS] = 3 | DIRECTED | BCAST2_0,
+ [CEC_MSG_FEATURE_ABORT] = 4 | DIRECTED,
+ [CEC_MSG_ABORT] = 2 | DIRECTED,
+ [CEC_MSG_GIVE_AUDIO_STATUS] = 2 | DIRECTED,
+ [CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS] = 2 | DIRECTED,
+ [CEC_MSG_REPORT_AUDIO_STATUS] = 3 | DIRECTED,
+ [CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR] = 2 | DIRECTED,
+ [CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR] = 2 | DIRECTED,
+ [CEC_MSG_SET_SYSTEM_AUDIO_MODE] = 3 | BOTH,
+ [CEC_MSG_SET_AUDIO_VOLUME_LEVEL] = 3 | DIRECTED,
+ [CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST] = 2 | DIRECTED,
+ [CEC_MSG_SYSTEM_AUDIO_MODE_STATUS] = 3 | DIRECTED,
+ [CEC_MSG_SET_AUDIO_RATE] = 3 | DIRECTED,
+ [CEC_MSG_INITIATE_ARC] = 2 | DIRECTED,
+ [CEC_MSG_REPORT_ARC_INITIATED] = 2 | DIRECTED,
+ [CEC_MSG_REPORT_ARC_TERMINATED] = 2 | DIRECTED,
+ [CEC_MSG_REQUEST_ARC_INITIATION] = 2 | DIRECTED,
+ [CEC_MSG_REQUEST_ARC_TERMINATION] = 2 | DIRECTED,
+ [CEC_MSG_TERMINATE_ARC] = 2 | DIRECTED,
+ [CEC_MSG_REQUEST_CURRENT_LATENCY] = 4 | BCAST,
+ [CEC_MSG_REPORT_CURRENT_LATENCY] = 6 | BCAST,
+ [CEC_MSG_CDC_MESSAGE] = 2 | BCAST,
+};
+
+/* Called by the CEC adapter if a message is received */
+void cec_received_msg_ts(struct cec_adapter *adap,
+ struct cec_msg *msg, ktime_t ts)
+{
+ struct cec_data *data;
+ u8 msg_init = cec_msg_initiator(msg);
+ u8 msg_dest = cec_msg_destination(msg);
+ u8 cmd = msg->msg[1];
+ bool is_reply = false;
+ bool valid_la = true;
+ bool monitor_valid_la = true;
+ u8 min_len = 0;
+
+ if (WARN_ON(!msg->len || msg->len > CEC_MAX_MSG_SIZE))
+ return;
+
+ if (adap->devnode.unregistered)
+ return;
+
+ /*
+ * Some CEC adapters will receive the messages that they transmitted.
+ * This test filters out those messages by checking if we are the
+ * initiator, and just returning in that case.
+ *
+ * Note that this won't work if this is an Unregistered device.
+ *
+ * It is bad practice if the hardware receives the message that it
+ * transmitted and luckily most CEC adapters behave correctly in this
+ * respect.
+ */
+ if (msg_init != CEC_LOG_ADDR_UNREGISTERED &&
+ cec_has_log_addr(adap, msg_init))
+ return;
+
+ msg->rx_ts = ktime_to_ns(ts);
+ msg->rx_status = CEC_RX_STATUS_OK;
+ msg->sequence = msg->reply = msg->timeout = 0;
+ msg->tx_status = 0;
+ msg->tx_ts = 0;
+ msg->tx_arb_lost_cnt = 0;
+ msg->tx_nack_cnt = 0;
+ msg->tx_low_drive_cnt = 0;
+ msg->tx_error_cnt = 0;
+ msg->flags = 0;
+ memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len);
+
+ mutex_lock(&adap->lock);
+ dprintk(2, "%s: %*ph\n", __func__, msg->len, msg->msg);
+
+ if (!adap->transmit_in_progress)
+ adap->last_initiator = 0xff;
+
+ /* Check if this message was for us (directed or broadcast). */
+ if (!cec_msg_is_broadcast(msg)) {
+ valid_la = cec_has_log_addr(adap, msg_dest);
+ monitor_valid_la = valid_la;
+ }
+
+ /*
+ * Check if the length is not too short or if the message is a
+ * broadcast message where a directed message was expected or
+ * vice versa. If so, then the message has to be ignored (according
+ * to section CEC 7.3 and CEC 12.2).
+ */
+ if (valid_la && msg->len > 1 && cec_msg_size[cmd]) {
+ u8 dir_fl = cec_msg_size[cmd] & BOTH;
+
+ min_len = cec_msg_size[cmd] & 0x1f;
+ if (msg->len < min_len)
+ valid_la = false;
+ else if (!cec_msg_is_broadcast(msg) && !(dir_fl & DIRECTED))
+ valid_la = false;
+ else if (cec_msg_is_broadcast(msg) && !(dir_fl & BCAST))
+ valid_la = false;
+ else if (cec_msg_is_broadcast(msg) &&
+ adap->log_addrs.cec_version < CEC_OP_CEC_VERSION_2_0 &&
+ !(dir_fl & BCAST1_4))
+ valid_la = false;
+ }
+ if (valid_la && min_len) {
+ /* These messages have special length requirements */
+ switch (cmd) {
+ case CEC_MSG_RECORD_ON:
+ switch (msg->msg[2]) {
+ case CEC_OP_RECORD_SRC_OWN:
+ break;
+ case CEC_OP_RECORD_SRC_DIGITAL:
+ if (msg->len < 10)
+ valid_la = false;
+ break;
+ case CEC_OP_RECORD_SRC_ANALOG:
+ if (msg->len < 7)
+ valid_la = false;
+ break;
+ case CEC_OP_RECORD_SRC_EXT_PLUG:
+ if (msg->len < 4)
+ valid_la = false;
+ break;
+ case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR:
+ if (msg->len < 5)
+ valid_la = false;
+ break;
+ }
+ break;
+ }
+ }
+
+ /* It's a valid message and not a poll or CDC message */
+ if (valid_la && msg->len > 1 && cmd != CEC_MSG_CDC_MESSAGE) {
+ bool abort = cmd == CEC_MSG_FEATURE_ABORT;
+
+ /* The aborted command is in msg[2] */
+ if (abort)
+ cmd = msg->msg[2];
+
+ /*
+ * Walk over all transmitted messages that are waiting for a
+ * reply.
+ */
+ list_for_each_entry(data, &adap->wait_queue, list) {
+ struct cec_msg *dst = &data->msg;
+
+ /*
+ * The *only* CEC message that has two possible replies
+ * is CEC_MSG_INITIATE_ARC.
+ * In this case allow either of the two replies.
+ */
+ if (!abort && dst->msg[1] == CEC_MSG_INITIATE_ARC &&
+ (cmd == CEC_MSG_REPORT_ARC_INITIATED ||
+ cmd == CEC_MSG_REPORT_ARC_TERMINATED) &&
+ (data->match_reply[0] == CEC_MSG_REPORT_ARC_INITIATED ||
+ data->match_reply[0] == CEC_MSG_REPORT_ARC_TERMINATED)) {
+ dst->reply = cmd;
+ data->match_reply[0] = cmd;
+ }
+
+ /* Does the command match? */
+ if ((abort && cmd != dst->msg[1]) ||
+ (!abort && memcmp(data->match_reply, msg->msg + 1, data->match_len)))
+ continue;
+
+ /* Does the addressing match? */
+ if (msg_init != cec_msg_destination(dst) &&
+ !cec_msg_is_broadcast(dst))
+ continue;
+
+ /* We got a reply */
+ memcpy(dst->msg, msg->msg, msg->len);
+ dst->len = msg->len;
+ dst->rx_ts = msg->rx_ts;
+ dst->rx_status = msg->rx_status;
+ if (abort)
+ dst->rx_status |= CEC_RX_STATUS_FEATURE_ABORT;
+ msg->flags = dst->flags;
+ msg->sequence = dst->sequence;
+ /* Remove it from the wait_queue */
+ list_del_init(&data->list);
+
+ /* Cancel the pending timeout work */
+ if (!cancel_delayed_work(&data->work)) {
+ mutex_unlock(&adap->lock);
+ cancel_delayed_work_sync(&data->work);
+ mutex_lock(&adap->lock);
+ }
+ /*
+ * Mark this as a reply, provided someone is still
+ * waiting for the answer.
+ */
+ if (data->fh)
+ is_reply = true;
+ cec_data_completed(data);
+ break;
+ }
+ }
+ mutex_unlock(&adap->lock);
+
+ /* Pass the message on to any monitoring filehandles */
+ cec_queue_msg_monitor(adap, msg, monitor_valid_la);
+
+ /* We're done if it is not for us or a poll message */
+ if (!valid_la || msg->len <= 1)
+ return;
+
+ if (adap->log_addrs.log_addr_mask == 0)
+ return;
+
+ /*
+ * Process the message on the protocol level. If is_reply is true,
+ * then cec_receive_notify() won't pass on the reply to the listener(s)
+ * since that was already done by cec_data_completed() above.
+ */
+ cec_receive_notify(adap, msg, is_reply);
+}
+EXPORT_SYMBOL_GPL(cec_received_msg_ts);
+
+/* Logical Address Handling */
+
+/*
+ * Attempt to claim a specific logical address.
+ *
+ * This function is called with adap->lock held.
+ */
+static int cec_config_log_addr(struct cec_adapter *adap,
+ unsigned int idx,
+ unsigned int log_addr)
+{
+ struct cec_log_addrs *las = &adap->log_addrs;
+ struct cec_msg msg = { };
+ const unsigned int max_retries = 2;
+ unsigned int i;
+ int err;
+
+ if (cec_has_log_addr(adap, log_addr))
+ return 0;
+
+ /* Send poll message */
+ msg.len = 1;
+ msg.msg[0] = (log_addr << 4) | log_addr;
+
+ for (i = 0; i < max_retries; i++) {
+ err = cec_transmit_msg_fh(adap, &msg, NULL, true);
+
+ /*
+ * While trying to poll the physical address was reset
+ * and the adapter was unconfigured, so bail out.
+ */
+ if (adap->phys_addr == CEC_PHYS_ADDR_INVALID)
+ return -EINTR;
+
+ /* Also bail out if the PA changed while configuring. */
+ if (adap->must_reconfigure)
+ return -EINTR;
+
+ if (err)
+ return err;
+
+ /*
+ * The message was aborted or timed out due to a disconnect or
+ * unconfigure, just bail out.
+ */
+ if (msg.tx_status &
+ (CEC_TX_STATUS_ABORTED | CEC_TX_STATUS_TIMEOUT))
+ return -EINTR;
+ if (msg.tx_status & CEC_TX_STATUS_OK)
+ return 0;
+ if (msg.tx_status & CEC_TX_STATUS_NACK)
+ break;
+ /*
+ * Retry up to max_retries times if the message was neither
+ * OKed or NACKed. This can happen due to e.g. a Lost
+ * Arbitration condition.
+ */
+ }
+
+ /*
+ * If we are unable to get an OK or a NACK after max_retries attempts
+ * (and note that each attempt already consists of four polls), then
+ * we assume that something is really weird and that it is not a
+ * good idea to try and claim this logical address.
+ */
+ if (i == max_retries) {
+ dprintk(0, "polling for LA %u failed with tx_status=0x%04x\n",
+ log_addr, msg.tx_status);
+ return 0;
+ }
+
+ /*
+ * Message not acknowledged, so this logical
+ * address is free to use.
+ */
+ err = call_op(adap, adap_log_addr, log_addr);
+ if (err)
+ return err;
+
+ las->log_addr[idx] = log_addr;
+ las->log_addr_mask |= 1 << log_addr;
+ return 1;
+}
+
+/*
+ * Unconfigure the adapter: clear all logical addresses and send
+ * the state changed event.
+ *
+ * This function is called with adap->lock held.
+ */
+static void cec_adap_unconfigure(struct cec_adapter *adap)
+{
+ if (!adap->needs_hpd || adap->phys_addr != CEC_PHYS_ADDR_INVALID)
+ WARN_ON(call_op(adap, adap_log_addr, CEC_LOG_ADDR_INVALID));
+ adap->log_addrs.log_addr_mask = 0;
+ adap->is_configured = false;
+ cec_flush(adap);
+ wake_up_interruptible(&adap->kthread_waitq);
+ cec_post_state_event(adap);
+ call_void_op(adap, adap_unconfigured);
+}
+
+/*
+ * Attempt to claim the required logical addresses.
+ */
+static int cec_config_thread_func(void *arg)
+{
+ /* The various LAs for each type of device */
+ static const u8 tv_log_addrs[] = {
+ CEC_LOG_ADDR_TV, CEC_LOG_ADDR_SPECIFIC,
+ CEC_LOG_ADDR_INVALID
+ };
+ static const u8 record_log_addrs[] = {
+ CEC_LOG_ADDR_RECORD_1, CEC_LOG_ADDR_RECORD_2,
+ CEC_LOG_ADDR_RECORD_3,
+ CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
+ CEC_LOG_ADDR_INVALID
+ };
+ static const u8 tuner_log_addrs[] = {
+ CEC_LOG_ADDR_TUNER_1, CEC_LOG_ADDR_TUNER_2,
+ CEC_LOG_ADDR_TUNER_3, CEC_LOG_ADDR_TUNER_4,
+ CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
+ CEC_LOG_ADDR_INVALID
+ };
+ static const u8 playback_log_addrs[] = {
+ CEC_LOG_ADDR_PLAYBACK_1, CEC_LOG_ADDR_PLAYBACK_2,
+ CEC_LOG_ADDR_PLAYBACK_3,
+ CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
+ CEC_LOG_ADDR_INVALID
+ };
+ static const u8 audiosystem_log_addrs[] = {
+ CEC_LOG_ADDR_AUDIOSYSTEM,
+ CEC_LOG_ADDR_INVALID
+ };
+ static const u8 specific_use_log_addrs[] = {
+ CEC_LOG_ADDR_SPECIFIC,
+ CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
+ CEC_LOG_ADDR_INVALID
+ };
+ static const u8 *type2addrs[6] = {
+ [CEC_LOG_ADDR_TYPE_TV] = tv_log_addrs,
+ [CEC_LOG_ADDR_TYPE_RECORD] = record_log_addrs,
+ [CEC_LOG_ADDR_TYPE_TUNER] = tuner_log_addrs,
+ [CEC_LOG_ADDR_TYPE_PLAYBACK] = playback_log_addrs,
+ [CEC_LOG_ADDR_TYPE_AUDIOSYSTEM] = audiosystem_log_addrs,
+ [CEC_LOG_ADDR_TYPE_SPECIFIC] = specific_use_log_addrs,
+ };
+ static const u16 type2mask[] = {
+ [CEC_LOG_ADDR_TYPE_TV] = CEC_LOG_ADDR_MASK_TV,
+ [CEC_LOG_ADDR_TYPE_RECORD] = CEC_LOG_ADDR_MASK_RECORD,
+ [CEC_LOG_ADDR_TYPE_TUNER] = CEC_LOG_ADDR_MASK_TUNER,
+ [CEC_LOG_ADDR_TYPE_PLAYBACK] = CEC_LOG_ADDR_MASK_PLAYBACK,
+ [CEC_LOG_ADDR_TYPE_AUDIOSYSTEM] = CEC_LOG_ADDR_MASK_AUDIOSYSTEM,
+ [CEC_LOG_ADDR_TYPE_SPECIFIC] = CEC_LOG_ADDR_MASK_SPECIFIC,
+ };
+ struct cec_adapter *adap = arg;
+ struct cec_log_addrs *las = &adap->log_addrs;
+ int err;
+ int i, j;
+
+ mutex_lock(&adap->lock);
+ dprintk(1, "physical address: %x.%x.%x.%x, claim %d logical addresses\n",
+ cec_phys_addr_exp(adap->phys_addr), las->num_log_addrs);
+ las->log_addr_mask = 0;
+
+ if (las->log_addr_type[0] == CEC_LOG_ADDR_TYPE_UNREGISTERED)
+ goto configured;
+
+reconfigure:
+ for (i = 0; i < las->num_log_addrs; i++) {
+ unsigned int type = las->log_addr_type[i];
+ const u8 *la_list;
+ u8 last_la;
+
+ /*
+ * The TV functionality can only map to physical address 0.
+ * For any other address, try the Specific functionality
+ * instead as per the spec.
+ */
+ if (adap->phys_addr && type == CEC_LOG_ADDR_TYPE_TV)
+ type = CEC_LOG_ADDR_TYPE_SPECIFIC;
+
+ la_list = type2addrs[type];
+ last_la = las->log_addr[i];
+ las->log_addr[i] = CEC_LOG_ADDR_INVALID;
+ if (last_la == CEC_LOG_ADDR_INVALID ||
+ last_la == CEC_LOG_ADDR_UNREGISTERED ||
+ !((1 << last_la) & type2mask[type]))
+ last_la = la_list[0];
+
+ err = cec_config_log_addr(adap, i, last_la);
+
+ if (adap->must_reconfigure) {
+ adap->must_reconfigure = false;
+ las->log_addr_mask = 0;
+ goto reconfigure;
+ }
+
+ if (err > 0) /* Reused last LA */
+ continue;
+
+ if (err < 0)
+ goto unconfigure;
+
+ for (j = 0; la_list[j] != CEC_LOG_ADDR_INVALID; j++) {
+ /* Tried this one already, skip it */
+ if (la_list[j] == last_la)
+ continue;
+ /* The backup addresses are CEC 2.0 specific */
+ if ((la_list[j] == CEC_LOG_ADDR_BACKUP_1 ||
+ la_list[j] == CEC_LOG_ADDR_BACKUP_2) &&
+ las->cec_version < CEC_OP_CEC_VERSION_2_0)
+ continue;
+
+ err = cec_config_log_addr(adap, i, la_list[j]);
+ if (err == 0) /* LA is in use */
+ continue;
+ if (err < 0)
+ goto unconfigure;
+ /* Done, claimed an LA */
+ break;
+ }
+
+ if (la_list[j] == CEC_LOG_ADDR_INVALID)
+ dprintk(1, "could not claim LA %d\n", i);
+ }
+
+ if (adap->log_addrs.log_addr_mask == 0 &&
+ !(las->flags & CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK))
+ goto unconfigure;
+
+configured:
+ if (adap->log_addrs.log_addr_mask == 0) {
+ /* Fall back to unregistered */
+ las->log_addr[0] = CEC_LOG_ADDR_UNREGISTERED;
+ las->log_addr_mask = 1 << las->log_addr[0];
+ for (i = 1; i < las->num_log_addrs; i++)
+ las->log_addr[i] = CEC_LOG_ADDR_INVALID;
+ }
+ for (i = las->num_log_addrs; i < CEC_MAX_LOG_ADDRS; i++)
+ las->log_addr[i] = CEC_LOG_ADDR_INVALID;
+ adap->is_configured = true;
+ adap->is_configuring = false;
+ adap->must_reconfigure = false;
+ cec_post_state_event(adap);
+
+ /*
+ * Now post the Report Features and Report Physical Address broadcast
+ * messages. Note that these are non-blocking transmits, meaning that
+ * they are just queued up and once adap->lock is unlocked the main
+ * thread will kick in and start transmitting these.
+ *
+ * If after this function is done (but before one or more of these
+ * messages are actually transmitted) the CEC adapter is unconfigured,
+ * then any remaining messages will be dropped by the main thread.
+ */
+ for (i = 0; i < las->num_log_addrs; i++) {
+ struct cec_msg msg = {};
+
+ if (las->log_addr[i] == CEC_LOG_ADDR_INVALID ||
+ (las->flags & CEC_LOG_ADDRS_FL_CDC_ONLY))
+ continue;
+
+ msg.msg[0] = (las->log_addr[i] << 4) | 0x0f;
+
+ /* Report Features must come first according to CEC 2.0 */
+ if (las->log_addr[i] != CEC_LOG_ADDR_UNREGISTERED &&
+ adap->log_addrs.cec_version >= CEC_OP_CEC_VERSION_2_0) {
+ cec_fill_msg_report_features(adap, &msg, i);
+ cec_transmit_msg_fh(adap, &msg, NULL, false);
+ }
+
+ /* Report Physical Address */
+ cec_msg_report_physical_addr(&msg, adap->phys_addr,
+ las->primary_device_type[i]);
+ dprintk(1, "config: la %d pa %x.%x.%x.%x\n",
+ las->log_addr[i],
+ cec_phys_addr_exp(adap->phys_addr));
+ cec_transmit_msg_fh(adap, &msg, NULL, false);
+
+ /* Report Vendor ID */
+ if (adap->log_addrs.vendor_id != CEC_VENDOR_ID_NONE) {
+ cec_msg_device_vendor_id(&msg,
+ adap->log_addrs.vendor_id);
+ cec_transmit_msg_fh(adap, &msg, NULL, false);
+ }
+ }
+ adap->kthread_config = NULL;
+ complete(&adap->config_completion);
+ mutex_unlock(&adap->lock);
+ call_void_op(adap, configured);
+ return 0;
+
+unconfigure:
+ for (i = 0; i < las->num_log_addrs; i++)
+ las->log_addr[i] = CEC_LOG_ADDR_INVALID;
+ cec_adap_unconfigure(adap);
+ adap->is_configuring = false;
+ adap->must_reconfigure = false;
+ adap->kthread_config = NULL;
+ complete(&adap->config_completion);
+ mutex_unlock(&adap->lock);
+ return 0;
+}
+
+/*
+ * Called from either __cec_s_phys_addr or __cec_s_log_addrs to claim the
+ * logical addresses.
+ *
+ * This function is called with adap->lock held.
+ */
+static void cec_claim_log_addrs(struct cec_adapter *adap, bool block)
+{
+ if (WARN_ON(adap->is_claiming_log_addrs ||
+ adap->is_configuring || adap->is_configured))
+ return;
+
+ adap->is_claiming_log_addrs = true;
+
+ init_completion(&adap->config_completion);
+
+ /* Ready to kick off the thread */
+ adap->is_configuring = true;
+ adap->kthread_config = kthread_run(cec_config_thread_func, adap,
+ "ceccfg-%s", adap->name);
+ if (IS_ERR(adap->kthread_config)) {
+ adap->kthread_config = NULL;
+ adap->is_configuring = false;
+ } else if (block) {
+ mutex_unlock(&adap->lock);
+ wait_for_completion(&adap->config_completion);
+ mutex_lock(&adap->lock);
+ }
+ adap->is_claiming_log_addrs = false;
+}
+
+/*
+ * Helper function to enable/disable the CEC adapter.
+ *
+ * This function is called with adap->lock held.
+ */
+int cec_adap_enable(struct cec_adapter *adap)
+{
+ bool enable;
+ int ret = 0;
+
+ enable = adap->monitor_all_cnt || adap->monitor_pin_cnt ||
+ adap->log_addrs.num_log_addrs;
+ if (adap->needs_hpd)
+ enable = enable && adap->phys_addr != CEC_PHYS_ADDR_INVALID;
+
+ if (adap->devnode.unregistered)
+ enable = false;
+
+ if (enable == adap->is_enabled)
+ return 0;
+
+ /* serialize adap_enable */
+ mutex_lock(&adap->devnode.lock);
+ if (enable) {
+ adap->last_initiator = 0xff;
+ adap->transmit_in_progress = false;
+ adap->tx_low_drive_log_cnt = 0;
+ adap->tx_error_log_cnt = 0;
+ ret = adap->ops->adap_enable(adap, true);
+ if (!ret) {
+ /*
+ * Enable monitor-all/pin modes if needed. We warn, but
+ * continue if this fails as this is not a critical error.
+ */
+ if (adap->monitor_all_cnt)
+ WARN_ON(call_op(adap, adap_monitor_all_enable, true));
+ if (adap->monitor_pin_cnt)
+ WARN_ON(call_op(adap, adap_monitor_pin_enable, true));
+ }
+ } else {
+ /* Disable monitor-all/pin modes if needed (needs_hpd == 1) */
+ if (adap->monitor_all_cnt)
+ WARN_ON(call_op(adap, adap_monitor_all_enable, false));
+ if (adap->monitor_pin_cnt)
+ WARN_ON(call_op(adap, adap_monitor_pin_enable, false));
+ WARN_ON(adap->ops->adap_enable(adap, false));
+ adap->last_initiator = 0xff;
+ adap->transmit_in_progress = false;
+ adap->transmit_in_progress_aborted = false;
+ if (adap->transmitting)
+ cec_data_cancel(adap->transmitting, CEC_TX_STATUS_ABORTED, 0);
+ }
+ if (!ret)
+ adap->is_enabled = enable;
+ wake_up_interruptible(&adap->kthread_waitq);
+ mutex_unlock(&adap->devnode.lock);
+ return ret;
+}
+
+/* Set a new physical address and send an event notifying userspace of this.
+ *
+ * This function is called with adap->lock held.
+ */
+void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
+{
+ bool becomes_invalid = phys_addr == CEC_PHYS_ADDR_INVALID;
+ bool is_invalid = adap->phys_addr == CEC_PHYS_ADDR_INVALID;
+
+ if (phys_addr == adap->phys_addr)
+ return;
+ if (!becomes_invalid && adap->devnode.unregistered)
+ return;
+
+ dprintk(1, "new physical address %x.%x.%x.%x\n",
+ cec_phys_addr_exp(phys_addr));
+ if (becomes_invalid || !is_invalid) {
+ adap->phys_addr = CEC_PHYS_ADDR_INVALID;
+ cec_post_state_event(adap);
+ cec_adap_unconfigure(adap);
+ if (becomes_invalid) {
+ cec_adap_enable(adap);
+ return;
+ }
+ }
+
+ adap->phys_addr = phys_addr;
+ if (is_invalid)
+ cec_adap_enable(adap);
+
+ cec_post_state_event(adap);
+ if (!adap->log_addrs.num_log_addrs)
+ return;
+ if (adap->is_configuring)
+ adap->must_reconfigure = true;
+ else
+ cec_claim_log_addrs(adap, block);
+}
+
+void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
+{
+ if (IS_ERR_OR_NULL(adap))
+ return;
+
+ mutex_lock(&adap->lock);
+ __cec_s_phys_addr(adap, phys_addr, block);
+ mutex_unlock(&adap->lock);
+}
+EXPORT_SYMBOL_GPL(cec_s_phys_addr);
+
+/*
+ * Note: In the drm subsystem, prefer calling (if possible):
+ *
+ * cec_s_phys_addr(adap, connector->display_info.source_physical_address, false);
+ */
+void cec_s_phys_addr_from_edid(struct cec_adapter *adap,
+ const struct edid *edid)
+{
+ u16 pa = CEC_PHYS_ADDR_INVALID;
+
+ if (edid && edid->extensions)
+ pa = cec_get_edid_phys_addr((const u8 *)edid,
+ EDID_LENGTH * (edid->extensions + 1), NULL);
+ cec_s_phys_addr(adap, pa, false);
+}
+EXPORT_SYMBOL_GPL(cec_s_phys_addr_from_edid);
+
+void cec_s_conn_info(struct cec_adapter *adap,
+ const struct cec_connector_info *conn_info)
+{
+ if (IS_ERR_OR_NULL(adap))
+ return;
+
+ if (!(adap->capabilities & CEC_CAP_CONNECTOR_INFO))
+ return;
+
+ mutex_lock(&adap->lock);
+ if (conn_info)
+ adap->conn_info = *conn_info;
+ else
+ memset(&adap->conn_info, 0, sizeof(adap->conn_info));
+ cec_post_state_event(adap);
+ mutex_unlock(&adap->lock);
+}
+EXPORT_SYMBOL_GPL(cec_s_conn_info);
+
+/*
+ * Called from either the ioctl or a driver to set the logical addresses.
+ *
+ * This function is called with adap->lock held.
+ */
+int __cec_s_log_addrs(struct cec_adapter *adap,
+ struct cec_log_addrs *log_addrs, bool block)
+{
+ u16 type_mask = 0;
+ int err;
+ int i;
+
+ if (adap->devnode.unregistered)
+ return -ENODEV;
+
+ if (!log_addrs || log_addrs->num_log_addrs == 0) {
+ if (!adap->log_addrs.num_log_addrs)
+ return 0;
+ if (adap->is_configuring || adap->is_configured)
+ cec_adap_unconfigure(adap);
+ adap->log_addrs.num_log_addrs = 0;
+ for (i = 0; i < CEC_MAX_LOG_ADDRS; i++)
+ adap->log_addrs.log_addr[i] = CEC_LOG_ADDR_INVALID;
+ adap->log_addrs.osd_name[0] = '\0';
+ adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE;
+ adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;
+ cec_adap_enable(adap);
+ return 0;
+ }
+
+ if (log_addrs->flags & CEC_LOG_ADDRS_FL_CDC_ONLY) {
+ /*
+ * Sanitize log_addrs fields if a CDC-Only device is
+ * requested.
+ */
+ log_addrs->num_log_addrs = 1;
+ log_addrs->osd_name[0] = '\0';
+ log_addrs->vendor_id = CEC_VENDOR_ID_NONE;
+ log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED;
+ /*
+ * This is just an internal convention since a CDC-Only device
+ * doesn't have to be a switch. But switches already use
+ * unregistered, so it makes some kind of sense to pick this
+ * as the primary device. Since a CDC-Only device never sends
+ * any 'normal' CEC messages this primary device type is never
+ * sent over the CEC bus.
+ */
+ log_addrs->primary_device_type[0] = CEC_OP_PRIM_DEVTYPE_SWITCH;
+ log_addrs->all_device_types[0] = 0;
+ log_addrs->features[0][0] = 0;
+ log_addrs->features[0][1] = 0;
+ }
+
+ /* Ensure the osd name is 0-terminated */
+ log_addrs->osd_name[sizeof(log_addrs->osd_name) - 1] = '\0';
+
+ /* Sanity checks */
+ if (log_addrs->num_log_addrs > adap->available_log_addrs) {
+ dprintk(1, "num_log_addrs > %d\n", adap->available_log_addrs);
+ return -EINVAL;
+ }
+
+ /*
+ * Vendor ID is a 24 bit number, so check if the value is
+ * within the correct range.
+ */
+ if (log_addrs->vendor_id != CEC_VENDOR_ID_NONE &&
+ (log_addrs->vendor_id & 0xff000000) != 0) {
+ dprintk(1, "invalid vendor ID\n");
+ return -EINVAL;
+ }
+
+ if (log_addrs->cec_version != CEC_OP_CEC_VERSION_1_4 &&
+ log_addrs->cec_version != CEC_OP_CEC_VERSION_2_0) {
+ dprintk(1, "invalid CEC version\n");
+ return -EINVAL;
+ }
+
+ if (log_addrs->num_log_addrs > 1)
+ for (i = 0; i < log_addrs->num_log_addrs; i++)
+ if (log_addrs->log_addr_type[i] ==
+ CEC_LOG_ADDR_TYPE_UNREGISTERED) {
+ dprintk(1, "num_log_addrs > 1 can't be combined with unregistered LA\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < log_addrs->num_log_addrs; i++) {
+ const u8 feature_sz = ARRAY_SIZE(log_addrs->features[0]);
+ u8 *features = log_addrs->features[i];
+ bool op_is_dev_features = false;
+ unsigned int j;
+
+ log_addrs->log_addr[i] = CEC_LOG_ADDR_INVALID;
+ if (log_addrs->log_addr_type[i] > CEC_LOG_ADDR_TYPE_UNREGISTERED) {
+ dprintk(1, "unknown logical address type\n");
+ return -EINVAL;
+ }
+ if (type_mask & (1 << log_addrs->log_addr_type[i])) {
+ dprintk(1, "duplicate logical address type\n");
+ return -EINVAL;
+ }
+ type_mask |= 1 << log_addrs->log_addr_type[i];
+ if ((type_mask & (1 << CEC_LOG_ADDR_TYPE_RECORD)) &&
+ (type_mask & (1 << CEC_LOG_ADDR_TYPE_PLAYBACK))) {
+ /* Record already contains the playback functionality */
+ dprintk(1, "invalid record + playback combination\n");
+ return -EINVAL;
+ }
+ if (log_addrs->primary_device_type[i] >
+ CEC_OP_PRIM_DEVTYPE_PROCESSOR) {
+ dprintk(1, "unknown primary device type\n");
+ return -EINVAL;
+ }
+ if (log_addrs->primary_device_type[i] == 2) {
+ dprintk(1, "invalid primary device type\n");
+ return -EINVAL;
+ }
+ for (j = 0; j < feature_sz; j++) {
+ if ((features[j] & 0x80) == 0) {
+ if (op_is_dev_features)
+ break;
+ op_is_dev_features = true;
+ }
+ }
+ if (!op_is_dev_features || j == feature_sz) {
+ dprintk(1, "malformed features\n");
+ return -EINVAL;
+ }
+ /* Zero unused part of the feature array */
+ memset(features + j + 1, 0, feature_sz - j - 1);
+ }
+
+ if (log_addrs->cec_version >= CEC_OP_CEC_VERSION_2_0) {
+ if (log_addrs->num_log_addrs > 2) {
+ dprintk(1, "CEC 2.0 allows no more than 2 logical addresses\n");
+ return -EINVAL;
+ }
+ if (log_addrs->num_log_addrs == 2) {
+ if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_AUDIOSYSTEM) |
+ (1 << CEC_LOG_ADDR_TYPE_TV)))) {
+ dprintk(1, "two LAs is only allowed for audiosystem and TV\n");
+ return -EINVAL;
+ }
+ if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_PLAYBACK) |
+ (1 << CEC_LOG_ADDR_TYPE_RECORD)))) {
+ dprintk(1, "an audiosystem/TV can only be combined with record or playback\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ /* Zero unused LAs */
+ for (i = log_addrs->num_log_addrs; i < CEC_MAX_LOG_ADDRS; i++) {
+ log_addrs->primary_device_type[i] = 0;
+ log_addrs->log_addr_type[i] = 0;
+ log_addrs->all_device_types[i] = 0;
+ memset(log_addrs->features[i], 0,
+ sizeof(log_addrs->features[i]));
+ }
+
+ log_addrs->log_addr_mask = adap->log_addrs.log_addr_mask;
+ adap->log_addrs = *log_addrs;
+ err = cec_adap_enable(adap);
+ if (!err && adap->phys_addr != CEC_PHYS_ADDR_INVALID)
+ cec_claim_log_addrs(adap, block);
+ return err;
+}
+
+int cec_s_log_addrs(struct cec_adapter *adap,
+ struct cec_log_addrs *log_addrs, bool block)
+{
+ int err;
+
+ mutex_lock(&adap->lock);
+ err = __cec_s_log_addrs(adap, log_addrs, block);
+ mutex_unlock(&adap->lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(cec_s_log_addrs);
+
+/* High-level core CEC message handling */
+
+/* Fill in the Report Features message */
+static void cec_fill_msg_report_features(struct cec_adapter *adap,
+ struct cec_msg *msg,
+ unsigned int la_idx)
+{
+ const struct cec_log_addrs *las = &adap->log_addrs;
+ const u8 *features = las->features[la_idx];
+ bool op_is_dev_features = false;
+ unsigned int idx;
+
+ /* Report Features */
+ msg->msg[0] = (las->log_addr[la_idx] << 4) | 0x0f;
+ msg->len = 4;
+ msg->msg[1] = CEC_MSG_REPORT_FEATURES;
+ msg->msg[2] = adap->log_addrs.cec_version;
+ msg->msg[3] = las->all_device_types[la_idx];
+
+ /* Write RC Profiles first, then Device Features */
+ for (idx = 0; idx < ARRAY_SIZE(las->features[0]); idx++) {
+ msg->msg[msg->len++] = features[idx];
+ if ((features[idx] & CEC_OP_FEAT_EXT) == 0) {
+ if (op_is_dev_features)
+ break;
+ op_is_dev_features = true;
+ }
+ }
+}
+
+/* Transmit the Feature Abort message */
+static int cec_feature_abort_reason(struct cec_adapter *adap,
+ struct cec_msg *msg, u8 reason)
+{
+ struct cec_msg tx_msg = { };
+
+ /*
+ * Don't reply with CEC_MSG_FEATURE_ABORT to a CEC_MSG_FEATURE_ABORT
+ * message!
+ */
+ if (msg->msg[1] == CEC_MSG_FEATURE_ABORT)
+ return 0;
+ /* Don't Feature Abort messages from 'Unregistered' */
+ if (cec_msg_initiator(msg) == CEC_LOG_ADDR_UNREGISTERED)
+ return 0;
+ cec_msg_set_reply_to(&tx_msg, msg);
+ cec_msg_feature_abort(&tx_msg, msg->msg[1], reason);
+ return cec_transmit_msg(adap, &tx_msg, false);
+}
+
+static int cec_feature_abort(struct cec_adapter *adap, struct cec_msg *msg)
+{
+ return cec_feature_abort_reason(adap, msg,
+ CEC_OP_ABORT_UNRECOGNIZED_OP);
+}
+
+static int cec_feature_refused(struct cec_adapter *adap, struct cec_msg *msg)
+{
+ return cec_feature_abort_reason(adap, msg,
+ CEC_OP_ABORT_REFUSED);
+}
+
+/*
+ * Called when a CEC message is received. This function will do any
+ * necessary core processing. The is_reply bool is true if this message
+ * is a reply to an earlier transmit.
+ *
+ * The message is either a broadcast message or a valid directed message.
+ */
+static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
+ bool is_reply)
+{
+ bool is_broadcast = cec_msg_is_broadcast(msg);
+ u8 dest_laddr = cec_msg_destination(msg);
+ u8 init_laddr = cec_msg_initiator(msg);
+ u8 devtype = cec_log_addr2dev(adap, dest_laddr);
+ int la_idx = cec_log_addr2idx(adap, dest_laddr);
+ bool from_unregistered = init_laddr == 0xf;
+ struct cec_msg tx_cec_msg = { };
+
+ dprintk(2, "%s: %*ph\n", __func__, msg->len, msg->msg);
+
+ /* If this is a CDC-Only device, then ignore any non-CDC messages */
+ if (cec_is_cdc_only(&adap->log_addrs) &&
+ msg->msg[1] != CEC_MSG_CDC_MESSAGE)
+ return 0;
+
+ /* Allow drivers to process the message first */
+ if (adap->ops->received && !adap->devnode.unregistered &&
+ adap->ops->received(adap, msg) != -ENOMSG)
+ return 0;
+
+ /*
+ * REPORT_PHYSICAL_ADDR, CEC_MSG_USER_CONTROL_PRESSED and
+ * CEC_MSG_USER_CONTROL_RELEASED messages always have to be
+ * handled by the CEC core, even if the passthrough mode is on.
+ * The others are just ignored if passthrough mode is on.
+ */
+ switch (msg->msg[1]) {
+ case CEC_MSG_GET_CEC_VERSION:
+ case CEC_MSG_ABORT:
+ case CEC_MSG_GIVE_DEVICE_POWER_STATUS:
+ case CEC_MSG_GIVE_OSD_NAME:
+ /*
+ * These messages reply with a directed message, so ignore if
+ * the initiator is Unregistered.
+ */
+ if (!adap->passthrough && from_unregistered)
+ return 0;
+ fallthrough;
+ case CEC_MSG_GIVE_DEVICE_VENDOR_ID:
+ case CEC_MSG_GIVE_FEATURES:
+ case CEC_MSG_GIVE_PHYSICAL_ADDR:
+ /*
+ * Skip processing these messages if the passthrough mode
+ * is on.
+ */
+ if (adap->passthrough)
+ goto skip_processing;
+ /* Ignore if addressing is wrong */
+ if (is_broadcast)
+ return 0;
+ break;
+
+ case CEC_MSG_USER_CONTROL_PRESSED:
+ case CEC_MSG_USER_CONTROL_RELEASED:
+ /* Wrong addressing mode: don't process */
+ if (is_broadcast || from_unregistered)
+ goto skip_processing;
+ break;
+
+ case CEC_MSG_REPORT_PHYSICAL_ADDR:
+ /*
+ * This message is always processed, regardless of the
+ * passthrough setting.
+ *
+ * Exception: don't process if wrong addressing mode.
+ */
+ if (!is_broadcast)
+ goto skip_processing;
+ break;
+
+ default:
+ break;
+ }
+
+ cec_msg_set_reply_to(&tx_cec_msg, msg);
+
+ switch (msg->msg[1]) {
+ /* The following messages are processed but still passed through */
+ case CEC_MSG_REPORT_PHYSICAL_ADDR: {
+ u16 pa = (msg->msg[2] << 8) | msg->msg[3];
+
+ dprintk(1, "reported physical address %x.%x.%x.%x for logical address %d\n",
+ cec_phys_addr_exp(pa), init_laddr);
+ break;
+ }
+
+ case CEC_MSG_USER_CONTROL_PRESSED:
+ if (!(adap->capabilities & CEC_CAP_RC) ||
+ !(adap->log_addrs.flags & CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU))
+ break;
+
+#ifdef CONFIG_MEDIA_CEC_RC
+ switch (msg->msg[2]) {
+ /*
+ * Play function, this message can have variable length
+ * depending on the specific play function that is used.
+ */
+ case CEC_OP_UI_CMD_PLAY_FUNCTION:
+ if (msg->len == 2)
+ rc_keydown(adap->rc, RC_PROTO_CEC,
+ msg->msg[2], 0);
+ else
+ rc_keydown(adap->rc, RC_PROTO_CEC,
+ msg->msg[2] << 8 | msg->msg[3], 0);
+ break;
+ /*
+ * Other function messages that are not handled.
+ * Currently the RC framework does not allow to supply an
+ * additional parameter to a keypress. These "keys" contain
+ * other information such as channel number, an input number
+ * etc.
+ * For the time being these messages are not processed by the
+ * framework and are simply forwarded to the user space.
+ */
+ case CEC_OP_UI_CMD_SELECT_BROADCAST_TYPE:
+ case CEC_OP_UI_CMD_SELECT_SOUND_PRESENTATION:
+ case CEC_OP_UI_CMD_TUNE_FUNCTION:
+ case CEC_OP_UI_CMD_SELECT_MEDIA_FUNCTION:
+ case CEC_OP_UI_CMD_SELECT_AV_INPUT_FUNCTION:
+ case CEC_OP_UI_CMD_SELECT_AUDIO_INPUT_FUNCTION:
+ break;
+ default:
+ rc_keydown(adap->rc, RC_PROTO_CEC, msg->msg[2], 0);
+ break;
+ }
+#endif
+ break;
+
+ case CEC_MSG_USER_CONTROL_RELEASED:
+ if (!(adap->capabilities & CEC_CAP_RC) ||
+ !(adap->log_addrs.flags & CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU))
+ break;
+#ifdef CONFIG_MEDIA_CEC_RC
+ rc_keyup(adap->rc);
+#endif
+ break;
+
+ /*
+ * The remaining messages are only processed if the passthrough mode
+ * is off.
+ */
+ case CEC_MSG_GET_CEC_VERSION:
+ cec_msg_cec_version(&tx_cec_msg, adap->log_addrs.cec_version);
+ return cec_transmit_msg(adap, &tx_cec_msg, false);
+
+ case CEC_MSG_GIVE_PHYSICAL_ADDR:
+ /* Do nothing for CEC switches using addr 15 */
+ if (devtype == CEC_OP_PRIM_DEVTYPE_SWITCH && dest_laddr == 15)
+ return 0;
+ cec_msg_report_physical_addr(&tx_cec_msg, adap->phys_addr, devtype);
+ return cec_transmit_msg(adap, &tx_cec_msg, false);
+
+ case CEC_MSG_GIVE_DEVICE_VENDOR_ID:
+ if (adap->log_addrs.vendor_id == CEC_VENDOR_ID_NONE)
+ return cec_feature_abort(adap, msg);
+ cec_msg_device_vendor_id(&tx_cec_msg, adap->log_addrs.vendor_id);
+ return cec_transmit_msg(adap, &tx_cec_msg, false);
+
+ case CEC_MSG_ABORT:
+ /* Do nothing for CEC switches */
+ if (devtype == CEC_OP_PRIM_DEVTYPE_SWITCH)
+ return 0;
+ return cec_feature_refused(adap, msg);
+
+ case CEC_MSG_GIVE_OSD_NAME: {
+ if (adap->log_addrs.osd_name[0] == 0)
+ return cec_feature_abort(adap, msg);
+ cec_msg_set_osd_name(&tx_cec_msg, adap->log_addrs.osd_name);
+ return cec_transmit_msg(adap, &tx_cec_msg, false);
+ }
+
+ case CEC_MSG_GIVE_FEATURES:
+ if (adap->log_addrs.cec_version < CEC_OP_CEC_VERSION_2_0)
+ return cec_feature_abort(adap, msg);
+ cec_fill_msg_report_features(adap, &tx_cec_msg, la_idx);
+ return cec_transmit_msg(adap, &tx_cec_msg, false);
+
+ default:
+ /*
+ * Unprocessed messages are aborted if userspace isn't doing
+ * any processing either.
+ */
+ if (!is_broadcast && !is_reply && !adap->follower_cnt &&
+ !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT)
+ return cec_feature_abort(adap, msg);
+ break;
+ }
+
+skip_processing:
+ /* If this was a reply, then we're done, unless otherwise specified */
+ if (is_reply && !(msg->flags & CEC_MSG_FL_REPLY_TO_FOLLOWERS))
+ return 0;
+
+ /*
+ * Send to the exclusive follower if there is one, otherwise send
+ * to all followers.
+ */
+ if (adap->cec_follower)
+ cec_queue_msg_fh(adap->cec_follower, msg);
+ else
+ cec_queue_msg_followers(adap, msg);
+ return 0;
+}
+
+/*
+ * Helper functions to keep track of the 'monitor all' use count.
+ *
+ * These functions are called with adap->lock held.
+ */
+int cec_monitor_all_cnt_inc(struct cec_adapter *adap)
+{
+ int ret;
+
+ if (adap->monitor_all_cnt++)
+ return 0;
+
+ ret = cec_adap_enable(adap);
+ if (ret)
+ adap->monitor_all_cnt--;
+ return ret;
+}
+
+void cec_monitor_all_cnt_dec(struct cec_adapter *adap)
+{
+ if (WARN_ON(!adap->monitor_all_cnt))
+ return;
+ if (--adap->monitor_all_cnt)
+ return;
+ WARN_ON(call_op(adap, adap_monitor_all_enable, false));
+ cec_adap_enable(adap);
+}
+
+/*
+ * Helper functions to keep track of the 'monitor pin' use count.
+ *
+ * These functions are called with adap->lock held.
+ */
+int cec_monitor_pin_cnt_inc(struct cec_adapter *adap)
+{
+ int ret;
+
+ if (adap->monitor_pin_cnt++)
+ return 0;
+
+ ret = cec_adap_enable(adap);
+ if (ret)
+ adap->monitor_pin_cnt--;
+ return ret;
+}
+
+void cec_monitor_pin_cnt_dec(struct cec_adapter *adap)
+{
+ if (WARN_ON(!adap->monitor_pin_cnt))
+ return;
+ if (--adap->monitor_pin_cnt)
+ return;
+ WARN_ON(call_op(adap, adap_monitor_pin_enable, false));
+ cec_adap_enable(adap);
+}
+
+#ifdef CONFIG_DEBUG_FS
+/*
+ * Log the current state of the CEC adapter.
+ * Very useful for debugging.
+ */
+int cec_adap_status(struct seq_file *file, void *priv)
+{
+ struct cec_adapter *adap = dev_get_drvdata(file->private);
+ struct cec_data *data;
+
+ mutex_lock(&adap->lock);
+ seq_printf(file, "enabled: %d\n", adap->is_enabled);
+ seq_printf(file, "configured: %d\n", adap->is_configured);
+ seq_printf(file, "configuring: %d\n", adap->is_configuring);
+ seq_printf(file, "phys_addr: %x.%x.%x.%x\n",
+ cec_phys_addr_exp(adap->phys_addr));
+ seq_printf(file, "number of LAs: %d\n", adap->log_addrs.num_log_addrs);
+ seq_printf(file, "LA mask: 0x%04x\n", adap->log_addrs.log_addr_mask);
+ if (adap->cec_follower)
+ seq_printf(file, "has CEC follower%s\n",
+ adap->passthrough ? " (in passthrough mode)" : "");
+ if (adap->cec_initiator)
+ seq_puts(file, "has CEC initiator\n");
+ if (adap->monitor_all_cnt)
+ seq_printf(file, "file handles in Monitor All mode: %u\n",
+ adap->monitor_all_cnt);
+ if (adap->monitor_pin_cnt)
+ seq_printf(file, "file handles in Monitor Pin mode: %u\n",
+ adap->monitor_pin_cnt);
+ if (adap->tx_timeout_cnt) {
+ seq_printf(file, "transmit timeout count: %u\n",
+ adap->tx_timeout_cnt);
+ adap->tx_timeout_cnt = 0;
+ }
+ if (adap->tx_low_drive_cnt) {
+ seq_printf(file, "transmit low drive count: %u\n",
+ adap->tx_low_drive_cnt);
+ adap->tx_low_drive_cnt = 0;
+ }
+ if (adap->tx_arb_lost_cnt) {
+ seq_printf(file, "transmit arbitration lost count: %u\n",
+ adap->tx_arb_lost_cnt);
+ adap->tx_arb_lost_cnt = 0;
+ }
+ if (adap->tx_error_cnt) {
+ seq_printf(file, "transmit error count: %u\n",
+ adap->tx_error_cnt);
+ adap->tx_error_cnt = 0;
+ }
+ data = adap->transmitting;
+ if (data)
+ seq_printf(file, "transmitting message: %*ph (reply: %*ph, timeout: %ums)\n",
+ data->msg.len, data->msg.msg,
+ data->match_len, data->match_reply,
+ data->msg.timeout);
+ seq_printf(file, "pending transmits: %u\n", adap->transmit_queue_sz);
+ list_for_each_entry(data, &adap->transmit_queue, list) {
+ seq_printf(file, "queued tx message: %*ph (reply: %*ph, timeout: %ums)\n",
+ data->msg.len, data->msg.msg,
+ data->match_len, data->match_reply,
+ data->msg.timeout);
+ }
+ list_for_each_entry(data, &adap->wait_queue, list) {
+ seq_printf(file, "message waiting for reply: %*ph (reply: %*ph, timeout: %ums)\n",
+ data->msg.len, data->msg.msg,
+ data->match_len, data->match_reply,
+ data->msg.timeout);
+ }
+
+ call_void_op(adap, adap_status, file);
+ mutex_unlock(&adap->lock);
+ return 0;
+}
+#endif
diff --git a/drivers/media/cec/core/cec-api.c b/drivers/media/cec/core/cec-api.c
new file mode 100644
index 000000000000..2b50578d107e
--- /dev/null
+++ b/drivers/media/cec/core/cec-api.c
@@ -0,0 +1,701 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cec-api.c - HDMI Consumer Electronics Control framework - API
+ *
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/ktime.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+
+#include <media/cec-pin.h>
+#include "cec-priv.h"
+#include "cec-pin-priv.h"
+
+static inline struct cec_devnode *cec_devnode_data(struct file *filp)
+{
+ struct cec_fh *fh = filp->private_data;
+
+ return &fh->adap->devnode;
+}
+
+/* CEC file operations */
+
+static __poll_t cec_poll(struct file *filp,
+ struct poll_table_struct *poll)
+{
+ struct cec_fh *fh = filp->private_data;
+ struct cec_adapter *adap = fh->adap;
+ __poll_t res = 0;
+
+ poll_wait(filp, &fh->wait, poll);
+ if (!cec_is_registered(adap))
+ return EPOLLERR | EPOLLHUP | EPOLLPRI;
+ mutex_lock(&adap->lock);
+ if (adap->is_configured &&
+ adap->transmit_queue_sz < CEC_MAX_MSG_TX_QUEUE_SZ)
+ res |= EPOLLOUT | EPOLLWRNORM;
+ if (fh->queued_msgs)
+ res |= EPOLLIN | EPOLLRDNORM;
+ if (fh->total_queued_events)
+ res |= EPOLLPRI;
+ mutex_unlock(&adap->lock);
+ return res;
+}
+
+static bool cec_is_busy(const struct cec_adapter *adap,
+ const struct cec_fh *fh)
+{
+ bool valid_initiator = adap->cec_initiator && adap->cec_initiator == fh;
+ bool valid_follower = adap->cec_follower && adap->cec_follower == fh;
+
+ /*
+ * Exclusive initiators and followers can always access the CEC adapter
+ */
+ if (valid_initiator || valid_follower)
+ return false;
+ /*
+ * All others can only access the CEC adapter if there is no
+ * exclusive initiator and they are in INITIATOR mode.
+ */
+ return adap->cec_initiator ||
+ fh->mode_initiator == CEC_MODE_NO_INITIATOR;
+}
+
+static long cec_adap_g_caps(struct cec_adapter *adap,
+ struct cec_caps __user *parg)
+{
+ struct cec_caps caps = {};
+
+ strscpy(caps.driver, adap->devnode.dev.parent->driver->name,
+ sizeof(caps.driver));
+ strscpy(caps.name, adap->name, sizeof(caps.name));
+ caps.available_log_addrs = adap->available_log_addrs;
+ caps.capabilities = adap->capabilities;
+ caps.version = LINUX_VERSION_CODE;
+ if (copy_to_user(parg, &caps, sizeof(caps)))
+ return -EFAULT;
+ return 0;
+}
+
+static long cec_adap_g_phys_addr(struct cec_adapter *adap,
+ __u16 __user *parg)
+{
+ u16 phys_addr;
+
+ mutex_lock(&adap->lock);
+ phys_addr = adap->phys_addr;
+ mutex_unlock(&adap->lock);
+ if (copy_to_user(parg, &phys_addr, sizeof(phys_addr)))
+ return -EFAULT;
+ return 0;
+}
+
+static int cec_validate_phys_addr(u16 phys_addr)
+{
+ int i;
+
+ if (phys_addr == CEC_PHYS_ADDR_INVALID)
+ return 0;
+ for (i = 0; i < 16; i += 4)
+ if (phys_addr & (0xf << i))
+ break;
+ if (i == 16)
+ return 0;
+ for (i += 4; i < 16; i += 4)
+ if ((phys_addr & (0xf << i)) == 0)
+ return -EINVAL;
+ return 0;
+}
+
+static long cec_adap_s_phys_addr(struct cec_adapter *adap, struct cec_fh *fh,
+ bool block, __u16 __user *parg)
+{
+ u16 phys_addr;
+ long err;
+
+ if (!(adap->capabilities & CEC_CAP_PHYS_ADDR))
+ return -ENOTTY;
+ if (copy_from_user(&phys_addr, parg, sizeof(phys_addr)))
+ return -EFAULT;
+
+ err = cec_validate_phys_addr(phys_addr);
+ if (err)
+ return err;
+ mutex_lock(&adap->lock);
+ if (cec_is_busy(adap, fh))
+ err = -EBUSY;
+ else
+ __cec_s_phys_addr(adap, phys_addr, block);
+ mutex_unlock(&adap->lock);
+ return err;
+}
+
+static long cec_adap_g_log_addrs(struct cec_adapter *adap,
+ struct cec_log_addrs __user *parg)
+{
+ struct cec_log_addrs log_addrs;
+
+ mutex_lock(&adap->lock);
+ /*
+ * We use memcpy here instead of assignment since there is a
+ * hole at the end of struct cec_log_addrs that an assignment
+ * might ignore. So when we do copy_to_user() we could leak
+ * one byte of memory.
+ */
+ memcpy(&log_addrs, &adap->log_addrs, sizeof(log_addrs));
+ if (!adap->is_configured)
+ memset(log_addrs.log_addr, CEC_LOG_ADDR_INVALID,
+ sizeof(log_addrs.log_addr));
+ mutex_unlock(&adap->lock);
+
+ if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
+ return -EFAULT;
+ return 0;
+}
+
+static long cec_adap_s_log_addrs(struct cec_adapter *adap, struct cec_fh *fh,
+ bool block, struct cec_log_addrs __user *parg)
+{
+ struct cec_log_addrs log_addrs;
+ long err = -EBUSY;
+
+ if (!(adap->capabilities & CEC_CAP_LOG_ADDRS))
+ return -ENOTTY;
+ if (copy_from_user(&log_addrs, parg, sizeof(log_addrs)))
+ return -EFAULT;
+ log_addrs.flags &= CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK |
+ CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU |
+ CEC_LOG_ADDRS_FL_CDC_ONLY;
+ mutex_lock(&adap->lock);
+ if (!adap->is_claiming_log_addrs && !adap->is_configuring &&
+ (!log_addrs.num_log_addrs || !adap->is_configured) &&
+ !cec_is_busy(adap, fh)) {
+ err = __cec_s_log_addrs(adap, &log_addrs, block);
+ if (!err)
+ log_addrs = adap->log_addrs;
+ }
+ mutex_unlock(&adap->lock);
+ if (err)
+ return err;
+ if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
+ return -EFAULT;
+ return 0;
+}
+
+static long cec_adap_g_connector_info(struct cec_adapter *adap,
+ struct cec_log_addrs __user *parg)
+{
+ int ret = 0;
+
+ if (!(adap->capabilities & CEC_CAP_CONNECTOR_INFO))
+ return -ENOTTY;
+
+ mutex_lock(&adap->lock);
+ if (copy_to_user(parg, &adap->conn_info, sizeof(adap->conn_info)))
+ ret = -EFAULT;
+ mutex_unlock(&adap->lock);
+ return ret;
+}
+
+static long cec_transmit(struct cec_adapter *adap, struct cec_fh *fh,
+ bool block, struct cec_msg __user *parg)
+{
+ struct cec_msg msg = {};
+ long err = 0;
+
+ if (!(adap->capabilities & CEC_CAP_TRANSMIT))
+ return -ENOTTY;
+ if (copy_from_user(&msg, parg, sizeof(msg)))
+ return -EFAULT;
+
+ mutex_lock(&adap->lock);
+ if (adap->log_addrs.num_log_addrs == 0)
+ err = -EPERM;
+ else if (adap->is_configuring && !msg_is_raw(&msg))
+ err = -ENONET;
+ else if (cec_is_busy(adap, fh))
+ err = -EBUSY;
+ else
+ err = cec_transmit_msg_fh(adap, &msg, fh, block);
+ mutex_unlock(&adap->lock);
+ if (err)
+ return err;
+ if (copy_to_user(parg, &msg, sizeof(msg)))
+ return -EFAULT;
+ return 0;
+}
+
+/* Called by CEC_RECEIVE: wait for a message to arrive */
+static int cec_receive_msg(struct cec_fh *fh, struct cec_msg *msg, bool block)
+{
+ u32 timeout = msg->timeout;
+ int res;
+
+ do {
+ mutex_lock(&fh->lock);
+ /* Are there received messages queued up? */
+ if (fh->queued_msgs) {
+ /* Yes, return the first one */
+ struct cec_msg_entry *entry =
+ list_first_entry(&fh->msgs,
+ struct cec_msg_entry, list);
+
+ list_del(&entry->list);
+ *msg = entry->msg;
+ kfree(entry);
+ fh->queued_msgs--;
+ mutex_unlock(&fh->lock);
+ /* restore original timeout value */
+ msg->timeout = timeout;
+ return 0;
+ }
+
+ /* No, return EAGAIN in non-blocking mode or wait */
+ mutex_unlock(&fh->lock);
+
+ /* Return when in non-blocking mode */
+ if (!block)
+ return -EAGAIN;
+
+ if (msg->timeout) {
+ /* The user specified a timeout */
+ res = wait_event_interruptible_timeout(fh->wait,
+ fh->queued_msgs,
+ msecs_to_jiffies(msg->timeout));
+ if (res == 0)
+ res = -ETIMEDOUT;
+ else if (res > 0)
+ res = 0;
+ } else {
+ /* Wait indefinitely */
+ res = wait_event_interruptible(fh->wait,
+ fh->queued_msgs);
+ }
+ /* Exit on error, otherwise loop to get the new message */
+ } while (!res);
+ return res;
+}
+
+static long cec_receive(struct cec_adapter *adap, struct cec_fh *fh,
+ bool block, struct cec_msg __user *parg)
+{
+ struct cec_msg msg = {};
+ long err;
+
+ if (copy_from_user(&msg, parg, sizeof(msg)))
+ return -EFAULT;
+
+ err = cec_receive_msg(fh, &msg, block);
+ if (err)
+ return err;
+ msg.flags = 0;
+ if (copy_to_user(parg, &msg, sizeof(msg)))
+ return -EFAULT;
+ return 0;
+}
+
+static long cec_dqevent(struct cec_adapter *adap, struct cec_fh *fh,
+ bool block, struct cec_event __user *parg)
+{
+ struct cec_event_entry *ev = NULL;
+ u64 ts = ~0ULL;
+ unsigned int i;
+ unsigned int ev_idx;
+ long err = 0;
+
+ mutex_lock(&fh->lock);
+ while (!fh->total_queued_events && block) {
+ mutex_unlock(&fh->lock);
+ err = wait_event_interruptible(fh->wait,
+ fh->total_queued_events);
+ if (err)
+ return err;
+ mutex_lock(&fh->lock);
+ }
+
+ /* Find the oldest event */
+ for (i = 0; i < CEC_NUM_EVENTS; i++) {
+ struct cec_event_entry *entry =
+ list_first_entry_or_null(&fh->events[i],
+ struct cec_event_entry, list);
+
+ if (entry && entry->ev.ts <= ts) {
+ ev = entry;
+ ev_idx = i;
+ ts = ev->ev.ts;
+ }
+ }
+
+ if (!ev) {
+ err = -EAGAIN;
+ goto unlock;
+ }
+ list_del(&ev->list);
+
+ if (copy_to_user(parg, &ev->ev, sizeof(ev->ev)))
+ err = -EFAULT;
+ if (ev_idx >= CEC_NUM_CORE_EVENTS)
+ kfree(ev);
+ fh->queued_events[ev_idx]--;
+ fh->total_queued_events--;
+
+unlock:
+ mutex_unlock(&fh->lock);
+ return err;
+}
+
+static long cec_g_mode(struct cec_adapter *adap, struct cec_fh *fh,
+ u32 __user *parg)
+{
+ u32 mode = fh->mode_initiator | fh->mode_follower;
+
+ if (copy_to_user(parg, &mode, sizeof(mode)))
+ return -EFAULT;
+ return 0;
+}
+
+static long cec_s_mode(struct cec_adapter *adap, struct cec_fh *fh,
+ u32 __user *parg)
+{
+ u32 mode;
+ u8 mode_initiator;
+ u8 mode_follower;
+ bool send_pin_event = false;
+ long err = 0;
+
+ if (copy_from_user(&mode, parg, sizeof(mode)))
+ return -EFAULT;
+ if (mode & ~(CEC_MODE_INITIATOR_MSK | CEC_MODE_FOLLOWER_MSK)) {
+ dprintk(1, "%s: invalid mode bits set\n", __func__);
+ return -EINVAL;
+ }
+
+ mode_initiator = mode & CEC_MODE_INITIATOR_MSK;
+ mode_follower = mode & CEC_MODE_FOLLOWER_MSK;
+
+ if (mode_initiator > CEC_MODE_EXCL_INITIATOR ||
+ mode_follower > CEC_MODE_MONITOR_ALL) {
+ dprintk(1, "%s: unknown mode\n", __func__);
+ return -EINVAL;
+ }
+
+ if (mode_follower == CEC_MODE_MONITOR_ALL &&
+ !(adap->capabilities & CEC_CAP_MONITOR_ALL)) {
+ dprintk(1, "%s: MONITOR_ALL not supported\n", __func__);
+ return -EINVAL;
+ }
+
+ if (mode_follower == CEC_MODE_MONITOR_PIN &&
+ !(adap->capabilities & CEC_CAP_MONITOR_PIN)) {
+ dprintk(1, "%s: MONITOR_PIN not supported\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Follower modes should always be able to send CEC messages */
+ if ((mode_initiator == CEC_MODE_NO_INITIATOR ||
+ !(adap->capabilities & CEC_CAP_TRANSMIT)) &&
+ mode_follower >= CEC_MODE_FOLLOWER &&
+ mode_follower <= CEC_MODE_EXCL_FOLLOWER_PASSTHRU) {
+ dprintk(1, "%s: cannot transmit\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Monitor modes require CEC_MODE_NO_INITIATOR */
+ if (mode_initiator && mode_follower >= CEC_MODE_MONITOR_PIN) {
+ dprintk(1, "%s: monitor modes require NO_INITIATOR\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Monitor modes require CAP_NET_ADMIN */
+ if (mode_follower >= CEC_MODE_MONITOR_PIN && !capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ mutex_lock(&adap->lock);
+ /*
+ * You can't become exclusive follower if someone else already
+ * has that job.
+ */
+ if ((mode_follower == CEC_MODE_EXCL_FOLLOWER ||
+ mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) &&
+ adap->cec_follower && adap->cec_follower != fh)
+ err = -EBUSY;
+ /*
+ * You can't become exclusive initiator if someone else already
+ * has that job.
+ */
+ if (mode_initiator == CEC_MODE_EXCL_INITIATOR &&
+ adap->cec_initiator && adap->cec_initiator != fh)
+ err = -EBUSY;
+
+ if (!err) {
+ bool old_mon_all = fh->mode_follower == CEC_MODE_MONITOR_ALL;
+ bool new_mon_all = mode_follower == CEC_MODE_MONITOR_ALL;
+
+ if (old_mon_all != new_mon_all) {
+ if (new_mon_all)
+ err = cec_monitor_all_cnt_inc(adap);
+ else
+ cec_monitor_all_cnt_dec(adap);
+ }
+ }
+
+ if (!err) {
+ bool old_mon_pin = fh->mode_follower == CEC_MODE_MONITOR_PIN;
+ bool new_mon_pin = mode_follower == CEC_MODE_MONITOR_PIN;
+
+ if (old_mon_pin != new_mon_pin) {
+ send_pin_event = new_mon_pin;
+ if (new_mon_pin)
+ err = cec_monitor_pin_cnt_inc(adap);
+ else
+ cec_monitor_pin_cnt_dec(adap);
+ }
+ }
+
+ if (err) {
+ mutex_unlock(&adap->lock);
+ return err;
+ }
+
+ if (fh->mode_follower == CEC_MODE_FOLLOWER)
+ adap->follower_cnt--;
+ if (mode_follower == CEC_MODE_FOLLOWER)
+ adap->follower_cnt++;
+ if (send_pin_event) {
+ struct cec_event ev = {
+ .flags = CEC_EVENT_FL_INITIAL_STATE,
+ };
+
+ ev.event = adap->cec_pin_is_high ? CEC_EVENT_PIN_CEC_HIGH :
+ CEC_EVENT_PIN_CEC_LOW;
+ cec_queue_event_fh(fh, &ev, 0);
+ }
+ if (mode_follower == CEC_MODE_EXCL_FOLLOWER ||
+ mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) {
+ adap->passthrough =
+ mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
+ adap->cec_follower = fh;
+ } else if (adap->cec_follower == fh) {
+ adap->passthrough = false;
+ adap->cec_follower = NULL;
+ }
+ if (mode_initiator == CEC_MODE_EXCL_INITIATOR)
+ adap->cec_initiator = fh;
+ else if (adap->cec_initiator == fh)
+ adap->cec_initiator = NULL;
+ fh->mode_initiator = mode_initiator;
+ fh->mode_follower = mode_follower;
+ mutex_unlock(&adap->lock);
+ return 0;
+}
+
+static long cec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct cec_fh *fh = filp->private_data;
+ struct cec_adapter *adap = fh->adap;
+ bool block = !(filp->f_flags & O_NONBLOCK);
+ void __user *parg = (void __user *)arg;
+
+ if (!cec_is_registered(adap))
+ return -ENODEV;
+
+ switch (cmd) {
+ case CEC_ADAP_G_CAPS:
+ return cec_adap_g_caps(adap, parg);
+
+ case CEC_ADAP_G_PHYS_ADDR:
+ return cec_adap_g_phys_addr(adap, parg);
+
+ case CEC_ADAP_S_PHYS_ADDR:
+ return cec_adap_s_phys_addr(adap, fh, block, parg);
+
+ case CEC_ADAP_G_LOG_ADDRS:
+ return cec_adap_g_log_addrs(adap, parg);
+
+ case CEC_ADAP_S_LOG_ADDRS:
+ return cec_adap_s_log_addrs(adap, fh, block, parg);
+
+ case CEC_ADAP_G_CONNECTOR_INFO:
+ return cec_adap_g_connector_info(adap, parg);
+
+ case CEC_TRANSMIT:
+ return cec_transmit(adap, fh, block, parg);
+
+ case CEC_RECEIVE:
+ return cec_receive(adap, fh, block, parg);
+
+ case CEC_DQEVENT:
+ return cec_dqevent(adap, fh, block, parg);
+
+ case CEC_G_MODE:
+ return cec_g_mode(adap, fh, parg);
+
+ case CEC_S_MODE:
+ return cec_s_mode(adap, fh, parg);
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int cec_open(struct inode *inode, struct file *filp)
+{
+ struct cec_devnode *devnode =
+ container_of(inode->i_cdev, struct cec_devnode, cdev);
+ struct cec_adapter *adap = to_cec_adapter(devnode);
+ struct cec_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ /*
+ * Initial events that are automatically sent when the cec device is
+ * opened.
+ */
+ struct cec_event ev = {
+ .event = CEC_EVENT_STATE_CHANGE,
+ .flags = CEC_EVENT_FL_INITIAL_STATE,
+ };
+ unsigned int i;
+ int err;
+
+ if (!fh)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&fh->msgs);
+ INIT_LIST_HEAD(&fh->xfer_list);
+ for (i = 0; i < CEC_NUM_EVENTS; i++)
+ INIT_LIST_HEAD(&fh->events[i]);
+ mutex_init(&fh->lock);
+ init_waitqueue_head(&fh->wait);
+
+ fh->mode_initiator = CEC_MODE_INITIATOR;
+ fh->adap = adap;
+
+ err = cec_get_device(adap);
+ if (err) {
+ kfree(fh);
+ return err;
+ }
+
+ filp->private_data = fh;
+
+ /* Queue up initial state events */
+ ev.state_change.phys_addr = adap->phys_addr;
+ ev.state_change.log_addr_mask = adap->log_addrs.log_addr_mask;
+ ev.state_change.have_conn_info =
+ adap->conn_info.type != CEC_CONNECTOR_TYPE_NO_CONNECTOR;
+ cec_queue_event_fh(fh, &ev, 0);
+#ifdef CONFIG_CEC_PIN
+ if (adap->pin && adap->pin->ops->read_hpd &&
+ !adap->devnode.unregistered) {
+ err = adap->pin->ops->read_hpd(adap);
+ if (err >= 0) {
+ ev.event = err ? CEC_EVENT_PIN_HPD_HIGH :
+ CEC_EVENT_PIN_HPD_LOW;
+ cec_queue_event_fh(fh, &ev, 0);
+ }
+ }
+ if (adap->pin && adap->pin->ops->read_5v &&
+ !adap->devnode.unregistered) {
+ err = adap->pin->ops->read_5v(adap);
+ if (err >= 0) {
+ ev.event = err ? CEC_EVENT_PIN_5V_HIGH :
+ CEC_EVENT_PIN_5V_LOW;
+ cec_queue_event_fh(fh, &ev, 0);
+ }
+ }
+#endif
+
+ mutex_lock(&devnode->lock);
+ mutex_lock(&devnode->lock_fhs);
+ list_add(&fh->list, &devnode->fhs);
+ mutex_unlock(&devnode->lock_fhs);
+ mutex_unlock(&devnode->lock);
+
+ return 0;
+}
+
+/* Override for the release function */
+static int cec_release(struct inode *inode, struct file *filp)
+{
+ struct cec_devnode *devnode = cec_devnode_data(filp);
+ struct cec_adapter *adap = to_cec_adapter(devnode);
+ struct cec_fh *fh = filp->private_data;
+ unsigned int i;
+
+ mutex_lock(&adap->lock);
+ if (adap->cec_initiator == fh)
+ adap->cec_initiator = NULL;
+ if (adap->cec_follower == fh) {
+ adap->cec_follower = NULL;
+ adap->passthrough = false;
+ }
+ if (fh->mode_follower == CEC_MODE_FOLLOWER)
+ adap->follower_cnt--;
+ if (fh->mode_follower == CEC_MODE_MONITOR_PIN)
+ cec_monitor_pin_cnt_dec(adap);
+ if (fh->mode_follower == CEC_MODE_MONITOR_ALL)
+ cec_monitor_all_cnt_dec(adap);
+ mutex_unlock(&adap->lock);
+
+ mutex_lock(&devnode->lock);
+ mutex_lock(&devnode->lock_fhs);
+ list_del(&fh->list);
+ mutex_unlock(&devnode->lock_fhs);
+ mutex_unlock(&devnode->lock);
+
+ /* Unhook pending transmits from this filehandle. */
+ mutex_lock(&adap->lock);
+ while (!list_empty(&fh->xfer_list)) {
+ struct cec_data *data =
+ list_first_entry(&fh->xfer_list, struct cec_data, xfer_list);
+
+ data->blocking = false;
+ data->fh = NULL;
+ list_del_init(&data->xfer_list);
+ }
+ mutex_unlock(&adap->lock);
+
+ mutex_lock(&fh->lock);
+ while (!list_empty(&fh->msgs)) {
+ struct cec_msg_entry *entry =
+ list_first_entry(&fh->msgs, struct cec_msg_entry, list);
+
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ for (i = CEC_NUM_CORE_EVENTS; i < CEC_NUM_EVENTS; i++) {
+ while (!list_empty(&fh->events[i])) {
+ struct cec_event_entry *entry =
+ list_first_entry(&fh->events[i],
+ struct cec_event_entry, list);
+
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ }
+ mutex_unlock(&fh->lock);
+ kfree(fh);
+
+ cec_put_device(adap);
+ filp->private_data = NULL;
+ return 0;
+}
+
+const struct file_operations cec_devnode_fops = {
+ .owner = THIS_MODULE,
+ .open = cec_open,
+ .unlocked_ioctl = cec_ioctl,
+ .compat_ioctl = cec_ioctl,
+ .release = cec_release,
+ .poll = cec_poll,
+};
diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c
new file mode 100644
index 000000000000..dd6e24a0899b
--- /dev/null
+++ b/drivers/media/cec/core/cec-core.c
@@ -0,0 +1,445 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cec-core.c - HDMI Consumer Electronics Control framework - Core
+ *
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "cec-priv.h"
+
+#define CEC_NUM_DEVICES 256
+#define CEC_NAME "cec"
+
+/*
+ * 400 ms is the time it takes for one 16 byte message to be
+ * transferred and 5 is the maximum number of retries. Add
+ * another 100 ms as a margin. So if the transmit doesn't
+ * finish before that time something is really wrong and we
+ * have to time out.
+ *
+ * This is a sign that something it really wrong and a warning
+ * will be issued.
+ */
+#define CEC_XFER_TIMEOUT_MS (5 * 400 + 100)
+
+int cec_debug;
+module_param_named(debug, cec_debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+
+static bool debug_phys_addr;
+module_param(debug_phys_addr, bool, 0644);
+MODULE_PARM_DESC(debug_phys_addr, "add CEC_CAP_PHYS_ADDR if set");
+
+static dev_t cec_dev_t;
+
+/* Active devices */
+static DEFINE_MUTEX(cec_devnode_lock);
+static DECLARE_BITMAP(cec_devnode_nums, CEC_NUM_DEVICES);
+
+static struct dentry *top_cec_dir;
+
+/* dev to cec_devnode */
+#define to_cec_devnode(cd) container_of(cd, struct cec_devnode, dev)
+
+/* Called when the last user of the cec device exits. */
+static void cec_devnode_release(struct device *cd)
+{
+ struct cec_devnode *devnode = to_cec_devnode(cd);
+
+ mutex_lock(&cec_devnode_lock);
+ /* Mark device node number as free */
+ clear_bit(devnode->minor, cec_devnode_nums);
+ mutex_unlock(&cec_devnode_lock);
+
+ cec_delete_adapter(to_cec_adapter(devnode));
+}
+
+static const struct bus_type cec_bus_type = {
+ .name = CEC_NAME,
+};
+
+/*
+ * Register a cec device node
+ *
+ * The registration code assigns minor numbers and registers the new device node
+ * with the kernel. An error is returned if no free minor number can be found,
+ * or if the registration of the device node fails.
+ *
+ * Zero is returned on success.
+ *
+ * Note that if the cec_devnode_register call fails, the release() callback of
+ * the cec_devnode structure is *not* called, so the caller is responsible for
+ * freeing any data.
+ */
+static int __must_check cec_devnode_register(struct cec_devnode *devnode,
+ struct module *owner)
+{
+ int minor;
+ int ret;
+
+ /* Part 1: Find a free minor number */
+ mutex_lock(&cec_devnode_lock);
+ minor = find_first_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES);
+ if (minor == CEC_NUM_DEVICES) {
+ mutex_unlock(&cec_devnode_lock);
+ pr_err("could not get a free minor\n");
+ return -ENFILE;
+ }
+
+ set_bit(minor, cec_devnode_nums);
+ mutex_unlock(&cec_devnode_lock);
+
+ devnode->minor = minor;
+ devnode->dev.bus = &cec_bus_type;
+ devnode->dev.devt = MKDEV(MAJOR(cec_dev_t), minor);
+ devnode->dev.release = cec_devnode_release;
+ dev_set_name(&devnode->dev, "cec%d", devnode->minor);
+ device_initialize(&devnode->dev);
+
+ /* Part 2: Initialize and register the character device */
+ cdev_init(&devnode->cdev, &cec_devnode_fops);
+ devnode->cdev.owner = owner;
+ kobject_set_name(&devnode->cdev.kobj, "cec%d", devnode->minor);
+
+ devnode->registered = true;
+ ret = cdev_device_add(&devnode->cdev, &devnode->dev);
+ if (ret) {
+ devnode->registered = false;
+ pr_err("%s: cdev_device_add failed\n", __func__);
+ goto clr_bit;
+ }
+
+ return 0;
+
+clr_bit:
+ mutex_lock(&cec_devnode_lock);
+ clear_bit(devnode->minor, cec_devnode_nums);
+ mutex_unlock(&cec_devnode_lock);
+ return ret;
+}
+
+/*
+ * Unregister a cec device node
+ *
+ * This unregisters the passed device. Future open calls will be met with
+ * errors.
+ *
+ * This function can safely be called if the device node has never been
+ * registered or has already been unregistered.
+ */
+static void cec_devnode_unregister(struct cec_adapter *adap)
+{
+ struct cec_devnode *devnode = &adap->devnode;
+ struct cec_fh *fh;
+
+ mutex_lock(&devnode->lock);
+
+ /* Check if devnode was never registered or already unregistered */
+ if (!devnode->registered || devnode->unregistered) {
+ mutex_unlock(&devnode->lock);
+ return;
+ }
+ devnode->registered = false;
+ devnode->unregistered = true;
+
+ mutex_lock(&devnode->lock_fhs);
+ list_for_each_entry(fh, &devnode->fhs, list)
+ wake_up_interruptible(&fh->wait);
+ mutex_unlock(&devnode->lock_fhs);
+
+ mutex_unlock(&devnode->lock);
+
+ mutex_lock(&adap->lock);
+ __cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false);
+ __cec_s_log_addrs(adap, NULL, false);
+ // Disable the adapter (since adap->devnode.unregistered is true)
+ cec_adap_enable(adap);
+ mutex_unlock(&adap->lock);
+
+ cdev_device_del(&devnode->cdev, &devnode->dev);
+ put_device(&devnode->dev);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t cec_error_inj_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct seq_file *sf = file->private_data;
+ struct cec_adapter *adap = sf->private;
+ char *buf;
+ char *line;
+ char *p;
+
+ buf = memdup_user_nul(ubuf, min_t(size_t, PAGE_SIZE, count));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+ p = buf;
+ while (p && *p) {
+ p = skip_spaces(p);
+ line = strsep(&p, "\n");
+ if (!*line || *line == '#')
+ continue;
+ if (!call_op(adap, error_inj_parse_line, line)) {
+ kfree(buf);
+ return -EINVAL;
+ }
+ }
+ kfree(buf);
+ return count;
+}
+
+static int cec_error_inj_show(struct seq_file *sf, void *unused)
+{
+ struct cec_adapter *adap = sf->private;
+
+ return call_op(adap, error_inj_show, sf);
+}
+
+static int cec_error_inj_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cec_error_inj_show, inode->i_private);
+}
+
+static const struct file_operations cec_error_inj_fops = {
+ .open = cec_error_inj_open,
+ .write = cec_error_inj_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif
+
+struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
+ void *priv, const char *name, u32 caps,
+ u8 available_las)
+{
+ struct cec_adapter *adap;
+ int res;
+
+#ifndef CONFIG_MEDIA_CEC_RC
+ caps &= ~CEC_CAP_RC;
+#endif
+
+ if (WARN_ON(!caps))
+ return ERR_PTR(-EINVAL);
+ if (WARN_ON(!ops))
+ return ERR_PTR(-EINVAL);
+ if (WARN_ON(!available_las || available_las > CEC_MAX_LOG_ADDRS))
+ return ERR_PTR(-EINVAL);
+ adap = kzalloc(sizeof(*adap), GFP_KERNEL);
+ if (!adap)
+ return ERR_PTR(-ENOMEM);
+ strscpy(adap->name, name, sizeof(adap->name));
+ adap->phys_addr = CEC_PHYS_ADDR_INVALID;
+ adap->cec_pin_is_high = true;
+ adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;
+ adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE;
+ adap->capabilities = caps | CEC_CAP_REPLY_VENDOR_ID;
+ if (debug_phys_addr)
+ adap->capabilities |= CEC_CAP_PHYS_ADDR;
+ adap->needs_hpd = caps & CEC_CAP_NEEDS_HPD;
+ adap->available_log_addrs = available_las;
+ adap->sequence = 0;
+ adap->ops = ops;
+ adap->priv = priv;
+ mutex_init(&adap->lock);
+ INIT_LIST_HEAD(&adap->transmit_queue);
+ INIT_LIST_HEAD(&adap->wait_queue);
+ init_waitqueue_head(&adap->kthread_waitq);
+
+ /* adap->devnode initialization */
+ INIT_LIST_HEAD(&adap->devnode.fhs);
+ mutex_init(&adap->devnode.lock_fhs);
+ mutex_init(&adap->devnode.lock);
+
+ adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name);
+ if (IS_ERR(adap->kthread)) {
+ pr_err("cec-%s: kernel_thread() failed\n", name);
+ res = PTR_ERR(adap->kthread);
+ kfree(adap);
+ return ERR_PTR(res);
+ }
+
+#ifdef CONFIG_MEDIA_CEC_RC
+ if (!(caps & CEC_CAP_RC))
+ return adap;
+
+ /* Prepare the RC input device */
+ adap->rc = rc_allocate_device(RC_DRIVER_SCANCODE);
+ if (!adap->rc) {
+ pr_err("cec-%s: failed to allocate memory for rc_dev\n",
+ name);
+ kthread_stop(adap->kthread);
+ kfree(adap);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ snprintf(adap->input_phys, sizeof(adap->input_phys),
+ "%s/input0", adap->name);
+
+ adap->rc->device_name = adap->name;
+ adap->rc->input_phys = adap->input_phys;
+ adap->rc->input_id.bustype = BUS_CEC;
+ adap->rc->input_id.vendor = 0;
+ adap->rc->input_id.product = 0;
+ adap->rc->input_id.version = 1;
+ adap->rc->driver_name = CEC_NAME;
+ adap->rc->allowed_protocols = RC_PROTO_BIT_CEC;
+ adap->rc->priv = adap;
+ adap->rc->map_name = RC_MAP_CEC;
+ adap->rc->timeout = MS_TO_US(550);
+#endif
+ return adap;
+}
+EXPORT_SYMBOL_GPL(cec_allocate_adapter);
+
+int cec_register_adapter(struct cec_adapter *adap,
+ struct device *parent)
+{
+ int res;
+
+ if (IS_ERR_OR_NULL(adap))
+ return 0;
+
+ if (WARN_ON(!parent))
+ return -EINVAL;
+
+ adap->owner = parent->driver->owner;
+ adap->devnode.dev.parent = parent;
+ if (!adap->xfer_timeout_ms)
+ adap->xfer_timeout_ms = CEC_XFER_TIMEOUT_MS;
+
+#ifdef CONFIG_MEDIA_CEC_RC
+ if (adap->capabilities & CEC_CAP_RC) {
+ adap->rc->dev.parent = parent;
+ res = rc_register_device(adap->rc);
+
+ if (res) {
+ pr_err("cec-%s: failed to prepare input device\n",
+ adap->name);
+ rc_free_device(adap->rc);
+ adap->rc = NULL;
+ return res;
+ }
+ }
+#endif
+
+ res = cec_devnode_register(&adap->devnode, adap->owner);
+ if (res) {
+#ifdef CONFIG_MEDIA_CEC_RC
+ /* Note: rc_unregister also calls rc_free */
+ rc_unregister_device(adap->rc);
+ adap->rc = NULL;
+#endif
+ return res;
+ }
+
+ dev_set_drvdata(&adap->devnode.dev, adap);
+#ifdef CONFIG_DEBUG_FS
+ if (!top_cec_dir)
+ return 0;
+
+ adap->cec_dir = debugfs_create_dir(dev_name(&adap->devnode.dev),
+ top_cec_dir);
+
+ debugfs_create_devm_seqfile(&adap->devnode.dev, "status", adap->cec_dir,
+ cec_adap_status);
+
+ if (!adap->ops->error_inj_show || !adap->ops->error_inj_parse_line)
+ return 0;
+ debugfs_create_file("error-inj", 0644, adap->cec_dir, adap,
+ &cec_error_inj_fops);
+#endif
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cec_register_adapter);
+
+void cec_unregister_adapter(struct cec_adapter *adap)
+{
+ if (IS_ERR_OR_NULL(adap))
+ return;
+
+#ifdef CONFIG_MEDIA_CEC_RC
+ /* Note: rc_unregister also calls rc_free */
+ rc_unregister_device(adap->rc);
+ adap->rc = NULL;
+#endif
+ debugfs_remove_recursive(adap->cec_dir);
+#ifdef CONFIG_CEC_NOTIFIER
+ cec_notifier_cec_adap_unregister(adap->notifier, adap);
+#endif
+ cec_devnode_unregister(adap);
+}
+EXPORT_SYMBOL_GPL(cec_unregister_adapter);
+
+void cec_delete_adapter(struct cec_adapter *adap)
+{
+ if (IS_ERR_OR_NULL(adap))
+ return;
+ if (adap->kthread_config)
+ kthread_stop(adap->kthread_config);
+ kthread_stop(adap->kthread);
+ if (adap->ops->adap_free)
+ adap->ops->adap_free(adap);
+#ifdef CONFIG_MEDIA_CEC_RC
+ rc_free_device(adap->rc);
+#endif
+ kfree(adap);
+}
+EXPORT_SYMBOL_GPL(cec_delete_adapter);
+
+/*
+ * Initialise cec for linux
+ */
+static int __init cec_devnode_init(void)
+{
+ int ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES, CEC_NAME);
+
+ if (ret < 0) {
+ pr_warn("cec: unable to allocate major\n");
+ return ret;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ top_cec_dir = debugfs_create_dir("cec", NULL);
+ if (IS_ERR_OR_NULL(top_cec_dir)) {
+ pr_warn("cec: Failed to create debugfs cec dir\n");
+ top_cec_dir = NULL;
+ }
+#endif
+
+ ret = bus_register(&cec_bus_type);
+ if (ret < 0) {
+ debugfs_remove_recursive(top_cec_dir);
+ unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
+ pr_warn("cec: bus_register failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void __exit cec_devnode_exit(void)
+{
+ debugfs_remove_recursive(top_cec_dir);
+ bus_unregister(&cec_bus_type);
+ unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
+}
+
+subsys_initcall(cec_devnode_init);
+module_exit(cec_devnode_exit)
+
+MODULE_AUTHOR("Hans Verkuil <hverkuil@kernel.org>");
+MODULE_DESCRIPTION("Device node registration for cec drivers");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/cec/core/cec-notifier.c b/drivers/media/cec/core/cec-notifier.c
new file mode 100644
index 000000000000..1fed0b1c71e9
--- /dev/null
+++ b/drivers/media/cec/core/cec-notifier.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cec-notifier.c - notify CEC drivers of physical address changes
+ *
+ * Copyright 2016 Russell King.
+ * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <linux/export.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/list.h>
+#include <linux/kref.h>
+#include <linux/of_platform.h>
+
+#include <media/cec.h>
+#include <media/cec-notifier.h>
+#include <drm/drm_edid.h>
+
+struct cec_notifier {
+ struct mutex lock;
+ struct list_head head;
+ struct kref kref;
+ struct device *hdmi_dev;
+ struct cec_connector_info conn_info;
+ const char *port_name;
+ struct cec_adapter *cec_adap;
+
+ u16 phys_addr;
+};
+
+static LIST_HEAD(cec_notifiers);
+static DEFINE_MUTEX(cec_notifiers_lock);
+
+/**
+ * cec_notifier_get_conn - find or create a new cec_notifier for the given
+ * device and connector tuple.
+ * @hdmi_dev: device that sends the events.
+ * @port_name: the connector name from which the event occurs
+ *
+ * If a notifier for device @dev already exists, then increase the refcount
+ * and return that notifier.
+ *
+ * If it doesn't exist, then allocate a new notifier struct and return a
+ * pointer to that new struct.
+ *
+ * Return NULL if the memory could not be allocated.
+ */
+static struct cec_notifier *
+cec_notifier_get_conn(struct device *hdmi_dev, const char *port_name)
+{
+ struct cec_notifier *n;
+
+ mutex_lock(&cec_notifiers_lock);
+ list_for_each_entry(n, &cec_notifiers, head) {
+ if (n->hdmi_dev == hdmi_dev &&
+ (!port_name ||
+ (n->port_name && !strcmp(n->port_name, port_name)))) {
+ kref_get(&n->kref);
+ mutex_unlock(&cec_notifiers_lock);
+ return n;
+ }
+ }
+ n = kzalloc(sizeof(*n), GFP_KERNEL);
+ if (!n)
+ goto unlock;
+ n->hdmi_dev = hdmi_dev;
+ if (port_name) {
+ n->port_name = kstrdup(port_name, GFP_KERNEL);
+ if (!n->port_name) {
+ kfree(n);
+ n = NULL;
+ goto unlock;
+ }
+ }
+ n->phys_addr = CEC_PHYS_ADDR_INVALID;
+
+ mutex_init(&n->lock);
+ kref_init(&n->kref);
+ list_add_tail(&n->head, &cec_notifiers);
+unlock:
+ mutex_unlock(&cec_notifiers_lock);
+ return n;
+}
+
+static void cec_notifier_release(struct kref *kref)
+{
+ struct cec_notifier *n =
+ container_of(kref, struct cec_notifier, kref);
+
+ list_del(&n->head);
+ kfree(n->port_name);
+ kfree(n);
+}
+
+static void cec_notifier_put(struct cec_notifier *n)
+{
+ mutex_lock(&cec_notifiers_lock);
+ kref_put(&n->kref, cec_notifier_release);
+ mutex_unlock(&cec_notifiers_lock);
+}
+
+struct cec_notifier *
+cec_notifier_conn_register(struct device *hdmi_dev, const char *port_name,
+ const struct cec_connector_info *conn_info)
+{
+ struct cec_notifier *n = cec_notifier_get_conn(hdmi_dev, port_name);
+
+ if (!n)
+ return n;
+
+ mutex_lock(&n->lock);
+ n->phys_addr = CEC_PHYS_ADDR_INVALID;
+ if (conn_info)
+ n->conn_info = *conn_info;
+ else
+ memset(&n->conn_info, 0, sizeof(n->conn_info));
+ if (n->cec_adap) {
+ if (!n->cec_adap->adap_controls_phys_addr)
+ cec_phys_addr_invalidate(n->cec_adap);
+ cec_s_conn_info(n->cec_adap, conn_info);
+ }
+ mutex_unlock(&n->lock);
+ return n;
+}
+EXPORT_SYMBOL_GPL(cec_notifier_conn_register);
+
+void cec_notifier_conn_unregister(struct cec_notifier *n)
+{
+ if (!n)
+ return;
+
+ mutex_lock(&n->lock);
+ memset(&n->conn_info, 0, sizeof(n->conn_info));
+ n->phys_addr = CEC_PHYS_ADDR_INVALID;
+ if (n->cec_adap) {
+ if (!n->cec_adap->adap_controls_phys_addr)
+ cec_phys_addr_invalidate(n->cec_adap);
+ cec_s_conn_info(n->cec_adap, NULL);
+ }
+ mutex_unlock(&n->lock);
+ cec_notifier_put(n);
+}
+EXPORT_SYMBOL_GPL(cec_notifier_conn_unregister);
+
+struct cec_notifier *
+cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *port_name,
+ struct cec_adapter *adap)
+{
+ struct cec_notifier *n;
+
+ if (WARN_ON(!adap))
+ return NULL;
+
+ n = cec_notifier_get_conn(hdmi_dev, port_name);
+ if (!n)
+ return n;
+
+ mutex_lock(&n->lock);
+ n->cec_adap = adap;
+ adap->conn_info = n->conn_info;
+ adap->notifier = n;
+ if (!adap->adap_controls_phys_addr)
+ cec_s_phys_addr(adap, n->phys_addr, false);
+ mutex_unlock(&n->lock);
+ return n;
+}
+EXPORT_SYMBOL_GPL(cec_notifier_cec_adap_register);
+
+void cec_notifier_cec_adap_unregister(struct cec_notifier *n,
+ struct cec_adapter *adap)
+{
+ if (!n)
+ return;
+
+ mutex_lock(&n->lock);
+ adap->notifier = NULL;
+ n->cec_adap = NULL;
+ mutex_unlock(&n->lock);
+ cec_notifier_put(n);
+}
+EXPORT_SYMBOL_GPL(cec_notifier_cec_adap_unregister);
+
+void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa)
+{
+ if (n == NULL)
+ return;
+
+ mutex_lock(&n->lock);
+ n->phys_addr = pa;
+ if (n->cec_adap && !n->cec_adap->adap_controls_phys_addr)
+ cec_s_phys_addr(n->cec_adap, n->phys_addr, false);
+ mutex_unlock(&n->lock);
+}
+EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr);
+
+/*
+ * Note: In the drm subsystem, prefer calling (if possible):
+ *
+ * cec_notifier_set_phys_addr(n, connector->display_info.source_physical_address);
+ */
+void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n,
+ const struct edid *edid)
+{
+ u16 pa = CEC_PHYS_ADDR_INVALID;
+
+ if (n == NULL)
+ return;
+
+ if (edid && edid->extensions)
+ pa = cec_get_edid_phys_addr((const u8 *)edid,
+ EDID_LENGTH * (edid->extensions + 1), NULL);
+ cec_notifier_set_phys_addr(n, pa);
+}
+EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid);
+
+struct device *cec_notifier_parse_hdmi_phandle(struct device *dev)
+{
+ struct platform_device *hdmi_pdev;
+ struct device *hdmi_dev = NULL;
+ struct device_node *np;
+
+ np = of_parse_phandle(dev->of_node, "hdmi-phandle", 0);
+
+ if (!np) {
+ dev_err(dev, "Failed to find HDMI node in device tree\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ hdmi_pdev = of_find_device_by_node(np);
+ if (hdmi_pdev)
+ hdmi_dev = &hdmi_pdev->dev;
+#if IS_REACHABLE(CONFIG_I2C)
+ if (!hdmi_dev) {
+ struct i2c_client *hdmi_client = of_find_i2c_device_by_node(np);
+
+ if (hdmi_client)
+ hdmi_dev = &hdmi_client->dev;
+ }
+#endif
+ of_node_put(np);
+ if (!hdmi_dev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ /*
+ * Note that the device struct is only used as a key into the
+ * cec_notifiers list, it is never actually accessed.
+ * So we decrement the reference here so we don't leak
+ * memory.
+ */
+ put_device(hdmi_dev);
+ return hdmi_dev;
+}
+EXPORT_SYMBOL_GPL(cec_notifier_parse_hdmi_phandle);
diff --git a/drivers/media/cec/core/cec-pin-error-inj.c b/drivers/media/cec/core/cec-pin-error-inj.c
new file mode 100644
index 000000000000..d9e613c7ce3f
--- /dev/null
+++ b/drivers/media/cec/core/cec-pin-error-inj.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <linux/delay.h>
+#include <linux/sched/types.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+
+#include <media/cec-pin.h>
+#include "cec-pin-priv.h"
+
+struct cec_error_inj_cmd {
+ unsigned int mode_offset;
+ int arg_idx;
+ const char *cmd;
+};
+
+static const struct cec_error_inj_cmd cec_error_inj_cmds[] = {
+ { CEC_ERROR_INJ_RX_NACK_OFFSET, -1, "rx-nack" },
+ { CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET,
+ CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX, "rx-low-drive" },
+ { CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET, -1, "rx-add-byte" },
+ { CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET, -1, "rx-remove-byte" },
+ { CEC_ERROR_INJ_RX_ARB_LOST_OFFSET,
+ CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX, "rx-arb-lost" },
+
+ { CEC_ERROR_INJ_TX_NO_EOM_OFFSET, -1, "tx-no-eom" },
+ { CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET, -1, "tx-early-eom" },
+ { CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET,
+ CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX, "tx-add-bytes" },
+ { CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET, -1, "tx-remove-byte" },
+ { CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX, "tx-short-bit" },
+ { CEC_ERROR_INJ_TX_LONG_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX, "tx-long-bit" },
+ { CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX, "tx-custom-bit" },
+ { CEC_ERROR_INJ_TX_SHORT_START_OFFSET, -1, "tx-short-start" },
+ { CEC_ERROR_INJ_TX_LONG_START_OFFSET, -1, "tx-long-start" },
+ { CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET, -1, "tx-custom-start" },
+ { CEC_ERROR_INJ_TX_LAST_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX, "tx-last-bit" },
+ { CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET,
+ CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX, "tx-low-drive" },
+ { 0, -1, NULL }
+};
+
+u16 cec_pin_rx_error_inj(struct cec_pin *pin)
+{
+ u16 cmd = CEC_ERROR_INJ_OP_ANY;
+
+ /* Only when 18 bits have been received do we have a valid cmd */
+ if (!(pin->error_inj[cmd] & CEC_ERROR_INJ_RX_MASK) &&
+ pin->rx_bit >= 18)
+ cmd = pin->rx_msg.msg[1];
+ return (pin->error_inj[cmd] & CEC_ERROR_INJ_RX_MASK) ? cmd :
+ CEC_ERROR_INJ_OP_ANY;
+}
+
+u16 cec_pin_tx_error_inj(struct cec_pin *pin)
+{
+ u16 cmd = CEC_ERROR_INJ_OP_ANY;
+
+ if (!(pin->error_inj[cmd] & CEC_ERROR_INJ_TX_MASK) &&
+ pin->tx_msg.len > 1)
+ cmd = pin->tx_msg.msg[1];
+ return (pin->error_inj[cmd] & CEC_ERROR_INJ_TX_MASK) ? cmd :
+ CEC_ERROR_INJ_OP_ANY;
+}
+
+bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
+{
+ static const char *delims = " \t\r";
+ struct cec_pin *pin = adap->pin;
+ unsigned int i;
+ bool has_pos = false;
+ char *p = line;
+ char *token;
+ char *comma;
+ u64 *error;
+ u8 *args;
+ bool has_op;
+ u8 op;
+ u8 mode;
+ u8 pos;
+
+ p = skip_spaces(p);
+ token = strsep(&p, delims);
+ if (!strcmp(token, "clear")) {
+ memset(pin->error_inj, 0, sizeof(pin->error_inj));
+ pin->rx_toggle = pin->tx_toggle = false;
+ pin->rx_no_low_drive = false;
+ pin->tx_ignore_nack_until_eom = false;
+ pin->tx_custom_pulse = false;
+ pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_falling_edge = false;
+ pin->tx_glitch_rising_edge = false;
+ return true;
+ }
+ if (!strcmp(token, "rx-clear")) {
+ for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++)
+ pin->error_inj[i] &= ~CEC_ERROR_INJ_RX_MASK;
+ pin->rx_toggle = false;
+ pin->rx_no_low_drive = false;
+ return true;
+ }
+ if (!strcmp(token, "tx-clear")) {
+ for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++)
+ pin->error_inj[i] &= ~CEC_ERROR_INJ_TX_MASK;
+ pin->tx_toggle = false;
+ pin->tx_ignore_nack_until_eom = false;
+ pin->tx_custom_pulse = false;
+ pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_falling_edge = false;
+ pin->tx_glitch_rising_edge = false;
+ return true;
+ }
+ if (!strcmp(token, "rx-no-low-drive")) {
+ pin->rx_no_low_drive = true;
+ return true;
+ }
+ if (!strcmp(token, "tx-ignore-nack-until-eom")) {
+ pin->tx_ignore_nack_until_eom = true;
+ return true;
+ }
+ if (!strcmp(token, "tx-custom-pulse")) {
+ pin->tx_custom_pulse = true;
+ cec_pin_start_timer(pin);
+ return true;
+ }
+ if (!strcmp(token, "tx-glitch-falling-edge")) {
+ pin->tx_glitch_falling_edge = true;
+ return true;
+ }
+ if (!strcmp(token, "tx-glitch-rising-edge")) {
+ pin->tx_glitch_rising_edge = true;
+ return true;
+ }
+ if (!p)
+ return false;
+
+ p = skip_spaces(p);
+ if (!strcmp(token, "tx-custom-low-usecs")) {
+ u32 usecs;
+
+ if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
+ return false;
+ pin->tx_custom_low_usecs = usecs;
+ return true;
+ }
+ if (!strcmp(token, "tx-custom-high-usecs")) {
+ u32 usecs;
+
+ if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
+ return false;
+ pin->tx_glitch_high_usecs = usecs;
+ return true;
+ }
+ if (!strcmp(token, "tx-glitch-low-usecs")) {
+ u32 usecs;
+
+ if (kstrtou32(p, 0, &usecs) || usecs > 100)
+ return false;
+ pin->tx_glitch_low_usecs = usecs;
+ return true;
+ }
+ if (!strcmp(token, "tx-glitch-high-usecs")) {
+ u32 usecs;
+
+ if (kstrtou32(p, 0, &usecs) || usecs > 100)
+ return false;
+ pin->tx_glitch_high_usecs = usecs;
+ return true;
+ }
+
+ comma = strchr(token, ',');
+ if (comma)
+ *comma++ = '\0';
+ if (!strcmp(token, "any")) {
+ has_op = false;
+ error = pin->error_inj + CEC_ERROR_INJ_OP_ANY;
+ args = pin->error_inj_args[CEC_ERROR_INJ_OP_ANY];
+ } else if (!kstrtou8(token, 0, &op)) {
+ has_op = true;
+ error = pin->error_inj + op;
+ args = pin->error_inj_args[op];
+ } else {
+ return false;
+ }
+
+ mode = CEC_ERROR_INJ_MODE_ONCE;
+ if (comma) {
+ if (!strcmp(comma, "off"))
+ mode = CEC_ERROR_INJ_MODE_OFF;
+ else if (!strcmp(comma, "once"))
+ mode = CEC_ERROR_INJ_MODE_ONCE;
+ else if (!strcmp(comma, "always"))
+ mode = CEC_ERROR_INJ_MODE_ALWAYS;
+ else if (!strcmp(comma, "toggle"))
+ mode = CEC_ERROR_INJ_MODE_TOGGLE;
+ else
+ return false;
+ }
+
+ token = strsep(&p, delims);
+ if (p) {
+ p = skip_spaces(p);
+ has_pos = !kstrtou8(p, 0, &pos);
+ }
+
+ if (!strcmp(token, "clear")) {
+ *error = 0;
+ return true;
+ }
+ if (!strcmp(token, "rx-clear")) {
+ *error &= ~CEC_ERROR_INJ_RX_MASK;
+ return true;
+ }
+ if (!strcmp(token, "tx-clear")) {
+ *error &= ~CEC_ERROR_INJ_TX_MASK;
+ return true;
+ }
+
+ for (i = 0; cec_error_inj_cmds[i].cmd; i++) {
+ const char *cmd = cec_error_inj_cmds[i].cmd;
+ unsigned int mode_offset;
+ u64 mode_mask;
+ int arg_idx;
+ bool is_bit_pos = true;
+
+ if (strcmp(token, cmd))
+ continue;
+
+ mode_offset = cec_error_inj_cmds[i].mode_offset;
+ mode_mask = CEC_ERROR_INJ_MODE_MASK << mode_offset;
+ arg_idx = cec_error_inj_cmds[i].arg_idx;
+
+ if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET) {
+ if (has_op)
+ return false;
+ if (!has_pos)
+ pos = 0x0f;
+ is_bit_pos = false;
+ } else if (mode_offset == CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET) {
+ if (!has_pos || !pos)
+ return false;
+ is_bit_pos = false;
+ }
+
+ if (arg_idx >= 0 && is_bit_pos) {
+ if (!has_pos || pos >= 160)
+ return false;
+ if (has_op && pos < 10 + 8)
+ return false;
+ /* Invalid bit position may not be the Ack bit */
+ if ((mode_offset == CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET ||
+ mode_offset == CEC_ERROR_INJ_TX_LONG_BIT_OFFSET ||
+ mode_offset == CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET) &&
+ (pos % 10) == 9)
+ return false;
+ }
+ *error &= ~mode_mask;
+ *error |= (u64)mode << mode_offset;
+ if (arg_idx >= 0)
+ args[arg_idx] = pos;
+ return true;
+ }
+ return false;
+}
+
+static void cec_pin_show_cmd(struct seq_file *sf, u32 cmd, u8 mode)
+{
+ if (cmd == CEC_ERROR_INJ_OP_ANY)
+ seq_puts(sf, "any,");
+ else
+ seq_printf(sf, "0x%02x,", cmd);
+ switch (mode) {
+ case CEC_ERROR_INJ_MODE_ONCE:
+ seq_puts(sf, "once ");
+ break;
+ case CEC_ERROR_INJ_MODE_ALWAYS:
+ seq_puts(sf, "always ");
+ break;
+ case CEC_ERROR_INJ_MODE_TOGGLE:
+ seq_puts(sf, "toggle ");
+ break;
+ default:
+ seq_puts(sf, "off ");
+ break;
+ }
+}
+
+int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
+{
+ struct cec_pin *pin = adap->pin;
+ unsigned int i, j;
+
+ seq_puts(sf, "# Clear error injections:\n");
+ seq_puts(sf, "# clear clear all rx and tx error injections\n");
+ seq_puts(sf, "# rx-clear clear all rx error injections\n");
+ seq_puts(sf, "# tx-clear clear all tx error injections\n");
+ seq_puts(sf, "# <op> clear clear all rx and tx error injections for <op>\n");
+ seq_puts(sf, "# <op> rx-clear clear all rx error injections for <op>\n");
+ seq_puts(sf, "# <op> tx-clear clear all tx error injections for <op>\n");
+ seq_puts(sf, "#\n");
+ seq_puts(sf, "# RX error injection settings:\n");
+ seq_puts(sf, "# rx-no-low-drive do not generate low-drive pulses\n");
+ seq_puts(sf, "#\n");
+ seq_puts(sf, "# RX error injection:\n");
+ seq_puts(sf, "# <op>[,<mode>] rx-nack NACK the message instead of sending an ACK\n");
+ seq_puts(sf, "# <op>[,<mode>] rx-low-drive <bit> force a low-drive condition at this bit position\n");
+ seq_puts(sf, "# <op>[,<mode>] rx-add-byte add a spurious byte to the received CEC message\n");
+ seq_puts(sf, "# <op>[,<mode>] rx-remove-byte remove the last byte from the received CEC message\n");
+ seq_puts(sf, "# any[,<mode>] rx-arb-lost [<poll>] generate a POLL message to trigger an arbitration lost\n");
+ seq_puts(sf, "#\n");
+ seq_puts(sf, "# TX error injection settings:\n");
+ seq_puts(sf, "# tx-ignore-nack-until-eom ignore early NACKs until EOM\n");
+ seq_puts(sf, "# tx-custom-low-usecs <usecs> define the 'low' time for the custom pulse\n");
+ seq_puts(sf, "# tx-custom-high-usecs <usecs> define the 'high' time for the custom pulse\n");
+ seq_puts(sf, "# tx-custom-pulse transmit the custom pulse once the bus is idle\n");
+ seq_puts(sf, "# tx-glitch-low-usecs <usecs> define the 'low' time for the glitch pulse\n");
+ seq_puts(sf, "# tx-glitch-high-usecs <usecs> define the 'high' time for the glitch pulse\n");
+ seq_puts(sf, "# tx-glitch-falling-edge send the glitch pulse after every falling edge\n");
+ seq_puts(sf, "# tx-glitch-rising-edge send the glitch pulse after every rising edge\n");
+ seq_puts(sf, "#\n");
+ seq_puts(sf, "# TX error injection:\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-no-eom don't set the EOM bit\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-early-eom set the EOM bit one byte too soon\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-add-bytes <num> append <num> (1-255) spurious bytes to the message\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-remove-byte drop the last byte from the message\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-short-bit <bit> make this bit shorter than allowed\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-long-bit <bit> make this bit longer than allowed\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-custom-bit <bit> send the custom pulse instead of this bit\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-short-start send a start pulse that's too short\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-long-start send a start pulse that's too long\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-custom-start send the custom pulse instead of the start pulse\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-last-bit <bit> stop sending after this bit\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-low-drive <bit> force a low-drive condition at this bit position\n");
+ seq_puts(sf, "#\n");
+ seq_puts(sf, "# <op> CEC message opcode (0-255) or 'any'\n");
+ seq_puts(sf, "# <mode> 'once' (default), 'always', 'toggle' or 'off'\n");
+ seq_puts(sf, "# <bit> CEC message bit (0-159)\n");
+ seq_puts(sf, "# 10 bits per 'byte': bits 0-7: data, bit 8: EOM, bit 9: ACK\n");
+ seq_puts(sf, "# <poll> CEC poll message used to test arbitration lost (0x00-0xff, default 0x0f)\n");
+ seq_puts(sf, "# <usecs> microseconds (0-10000000, default 1000)\n");
+
+ seq_puts(sf, "\nclear\n");
+
+ for (i = 0; i < ARRAY_SIZE(pin->error_inj); i++) {
+ u64 e = pin->error_inj[i];
+
+ for (j = 0; cec_error_inj_cmds[j].cmd; j++) {
+ const char *cmd = cec_error_inj_cmds[j].cmd;
+ unsigned int mode;
+ unsigned int mode_offset;
+ int arg_idx;
+
+ mode_offset = cec_error_inj_cmds[j].mode_offset;
+ arg_idx = cec_error_inj_cmds[j].arg_idx;
+ mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK;
+ if (!mode)
+ continue;
+ cec_pin_show_cmd(sf, i, mode);
+ seq_puts(sf, cmd);
+ if (arg_idx >= 0)
+ seq_printf(sf, " %u",
+ pin->error_inj_args[i][arg_idx]);
+ seq_puts(sf, "\n");
+ }
+ }
+
+ if (pin->rx_no_low_drive)
+ seq_puts(sf, "rx-no-low-drive\n");
+ if (pin->tx_ignore_nack_until_eom)
+ seq_puts(sf, "tx-ignore-nack-until-eom\n");
+ if (pin->tx_glitch_falling_edge)
+ seq_puts(sf, "tx-glitch-falling-edge\n");
+ if (pin->tx_glitch_rising_edge)
+ seq_puts(sf, "tx-glitch-rising-edge\n");
+ if (pin->tx_custom_pulse)
+ seq_puts(sf, "tx-custom-pulse\n");
+ if (pin->tx_custom_low_usecs != CEC_TIM_CUSTOM_DEFAULT)
+ seq_printf(sf, "tx-custom-low-usecs %u\n",
+ pin->tx_custom_low_usecs);
+ if (pin->tx_custom_high_usecs != CEC_TIM_CUSTOM_DEFAULT)
+ seq_printf(sf, "tx-custom-high-usecs %u\n",
+ pin->tx_custom_high_usecs);
+ if (pin->tx_glitch_low_usecs != CEC_TIM_GLITCH_DEFAULT)
+ seq_printf(sf, "tx-glitch-low-usecs %u\n",
+ pin->tx_glitch_low_usecs);
+ if (pin->tx_glitch_high_usecs != CEC_TIM_GLITCH_DEFAULT)
+ seq_printf(sf, "tx-glitch-high-usecs %u\n",
+ pin->tx_glitch_high_usecs);
+ return 0;
+}
diff --git a/drivers/media/cec/core/cec-pin-priv.h b/drivers/media/cec/core/cec-pin-priv.h
new file mode 100644
index 000000000000..e7801be9adb9
--- /dev/null
+++ b/drivers/media/cec/core/cec-pin-priv.h
@@ -0,0 +1,260 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cec-pin-priv.h - internal cec-pin header
+ *
+ * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#ifndef LINUX_CEC_PIN_PRIV_H
+#define LINUX_CEC_PIN_PRIV_H
+
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <media/cec-pin.h>
+
+#define call_pin_op(pin, op, arg...) \
+ ((pin && pin->ops->op && !pin->adap->devnode.unregistered) ? \
+ pin->ops->op(pin->adap, ## arg) : 0)
+
+#define call_void_pin_op(pin, op, arg...) \
+ do { \
+ if (pin && pin->ops->op && \
+ !pin->adap->devnode.unregistered) \
+ pin->ops->op(pin->adap, ## arg); \
+ } while (0)
+
+enum cec_pin_state {
+ /* CEC is off */
+ CEC_ST_OFF,
+ /* CEC is idle, waiting for Rx or Tx */
+ CEC_ST_IDLE,
+
+ /* Tx states */
+
+ /* Pending Tx, waiting for Signal Free Time to expire */
+ CEC_ST_TX_WAIT,
+ /* Low-drive was detected, wait for bus to go high */
+ CEC_ST_TX_WAIT_FOR_HIGH,
+ /* Drive CEC low for the start bit */
+ CEC_ST_TX_START_BIT_LOW,
+ /* Drive CEC high for the start bit */
+ CEC_ST_TX_START_BIT_HIGH,
+ /* Generate a start bit period that is too short */
+ CEC_ST_TX_START_BIT_HIGH_SHORT,
+ /* Generate a start bit period that is too long */
+ CEC_ST_TX_START_BIT_HIGH_LONG,
+ /* Drive CEC low for the start bit using the custom timing */
+ CEC_ST_TX_START_BIT_LOW_CUSTOM,
+ /* Drive CEC high for the start bit using the custom timing */
+ CEC_ST_TX_START_BIT_HIGH_CUSTOM,
+ /* Drive CEC low for the 0 bit */
+ CEC_ST_TX_DATA_BIT_0_LOW,
+ /* Drive CEC high for the 0 bit */
+ CEC_ST_TX_DATA_BIT_0_HIGH,
+ /* Generate a bit period that is too short */
+ CEC_ST_TX_DATA_BIT_0_HIGH_SHORT,
+ /* Generate a bit period that is too long */
+ CEC_ST_TX_DATA_BIT_0_HIGH_LONG,
+ /* Drive CEC low for the 1 bit */
+ CEC_ST_TX_DATA_BIT_1_LOW,
+ /* Drive CEC high for the 1 bit */
+ CEC_ST_TX_DATA_BIT_1_HIGH,
+ /* Generate a bit period that is too short */
+ CEC_ST_TX_DATA_BIT_1_HIGH_SHORT,
+ /* Generate a bit period that is too long */
+ CEC_ST_TX_DATA_BIT_1_HIGH_LONG,
+ /*
+ * Wait for start of sample time to check for Ack bit or first
+ * four initiator bits to check for Arbitration Lost.
+ */
+ CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE,
+ /* Wait for end of bit period after sampling */
+ CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE,
+ /* Generate a bit period that is too short */
+ CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT,
+ /* Generate a bit period that is too long */
+ CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG,
+ /* Drive CEC low for a data bit using the custom timing */
+ CEC_ST_TX_DATA_BIT_LOW_CUSTOM,
+ /* Drive CEC high for a data bit using the custom timing */
+ CEC_ST_TX_DATA_BIT_HIGH_CUSTOM,
+ /* Drive CEC low for a standalone pulse using the custom timing */
+ CEC_ST_TX_PULSE_LOW_CUSTOM,
+ /* Drive CEC high for a standalone pulse using the custom timing */
+ CEC_ST_TX_PULSE_HIGH_CUSTOM,
+ /* Start low drive */
+ CEC_ST_TX_LOW_DRIVE,
+
+ /* Rx states */
+
+ /* Start bit low detected */
+ CEC_ST_RX_START_BIT_LOW,
+ /* Start bit high detected */
+ CEC_ST_RX_START_BIT_HIGH,
+ /* Wait for bit sample time */
+ CEC_ST_RX_DATA_SAMPLE,
+ /* Wait for earliest end of bit period after sampling */
+ CEC_ST_RX_DATA_POST_SAMPLE,
+ /* Wait for CEC to go low (i.e. end of bit period) */
+ CEC_ST_RX_DATA_WAIT_FOR_LOW,
+ /* Drive CEC low to send 0 Ack bit */
+ CEC_ST_RX_ACK_LOW,
+ /* End of 0 Ack time, wait for earliest end of bit period */
+ CEC_ST_RX_ACK_LOW_POST,
+ /* Wait for CEC to go high (i.e. end of bit period */
+ CEC_ST_RX_ACK_HIGH_POST,
+ /* Wait for earliest end of bit period and end of message */
+ CEC_ST_RX_ACK_FINISH,
+ /* Start low drive */
+ CEC_ST_RX_LOW_DRIVE,
+
+ /* Monitor pin using interrupts */
+ CEC_ST_RX_IRQ,
+
+ /* Total number of pin states */
+ CEC_PIN_STATES
+};
+
+/* Error Injection */
+
+/* Error injection modes */
+#define CEC_ERROR_INJ_MODE_OFF 0
+#define CEC_ERROR_INJ_MODE_ONCE 1
+#define CEC_ERROR_INJ_MODE_ALWAYS 2
+#define CEC_ERROR_INJ_MODE_TOGGLE 3
+#define CEC_ERROR_INJ_MODE_MASK 3ULL
+
+/* Receive error injection options */
+#define CEC_ERROR_INJ_RX_NACK_OFFSET 0
+#define CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET 2
+#define CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET 4
+#define CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET 6
+#define CEC_ERROR_INJ_RX_ARB_LOST_OFFSET 8
+#define CEC_ERROR_INJ_RX_MASK 0xffffULL
+
+/* Transmit error injection options */
+#define CEC_ERROR_INJ_TX_NO_EOM_OFFSET 16
+#define CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET 18
+#define CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET 20
+#define CEC_ERROR_INJ_TX_LONG_BIT_OFFSET 22
+#define CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET 24
+#define CEC_ERROR_INJ_TX_SHORT_START_OFFSET 26
+#define CEC_ERROR_INJ_TX_LONG_START_OFFSET 28
+#define CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET 30
+#define CEC_ERROR_INJ_TX_LAST_BIT_OFFSET 32
+#define CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET 34
+#define CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET 36
+#define CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET 38
+#define CEC_ERROR_INJ_TX_MASK 0xffffffffffff0000ULL
+
+#define CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX 0
+#define CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX 1
+
+#define CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX 2
+#define CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX 3
+#define CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX 4
+#define CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX 5
+#define CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX 6
+#define CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX 7
+#define CEC_ERROR_INJ_NUM_ARGS 8
+
+/* Special CEC op values */
+#define CEC_ERROR_INJ_OP_ANY 0x00000100
+
+/* The default for the low/high time of the custom pulse */
+#define CEC_TIM_CUSTOM_DEFAULT 1000
+
+/* The default for the low/high time of the glitch pulse */
+#define CEC_TIM_GLITCH_DEFAULT 1
+
+#define CEC_NUM_PIN_EVENTS 128
+#define CEC_PIN_EVENT_FL_IS_HIGH (1 << 0)
+#define CEC_PIN_EVENT_FL_DROPPED (1 << 1)
+
+#define CEC_PIN_IRQ_UNCHANGED 0
+#define CEC_PIN_IRQ_DISABLE 1
+#define CEC_PIN_IRQ_ENABLE 2
+
+struct cec_pin {
+ struct cec_adapter *adap;
+ const struct cec_pin_ops *ops;
+ struct task_struct *kthread;
+ wait_queue_head_t kthread_waitq;
+ struct hrtimer timer;
+ ktime_t ts;
+ unsigned int wait_usecs;
+ u16 la_mask;
+ bool monitor_all;
+ bool rx_eom;
+ bool enabled_irq;
+ bool enable_irq_failed;
+ enum cec_pin_state state;
+ struct cec_msg tx_msg;
+ u32 tx_bit;
+ bool tx_nacked;
+ u32 tx_signal_free_time;
+ bool tx_toggle;
+ struct cec_msg rx_msg;
+ u32 rx_bit;
+ bool rx_toggle;
+ u32 rx_start_bit_low_too_short_cnt;
+ u64 rx_start_bit_low_too_short_ts;
+ u32 rx_start_bit_low_too_short_delta;
+ u32 rx_start_bit_too_short_cnt;
+ u64 rx_start_bit_too_short_ts;
+ u32 rx_start_bit_too_short_delta;
+ u32 rx_start_bit_too_long_cnt;
+ u32 rx_data_bit_too_short_cnt;
+ u64 rx_data_bit_too_short_ts;
+ u32 rx_data_bit_too_short_delta;
+ u32 rx_data_bit_too_long_cnt;
+ u32 rx_low_drive_cnt;
+
+ struct cec_msg work_rx_msg;
+ u8 work_tx_status;
+ ktime_t work_tx_ts;
+ atomic_t work_irq_change;
+ atomic_t work_pin_num_events;
+ unsigned int work_pin_events_wr;
+ unsigned int work_pin_events_rd;
+ ktime_t work_pin_ts[CEC_NUM_PIN_EVENTS];
+ u8 work_pin_events[CEC_NUM_PIN_EVENTS];
+ bool work_pin_events_dropped;
+ u32 work_pin_events_dropped_cnt;
+ ktime_t timer_ts;
+ u32 timer_cnt;
+ u32 timer_100us_overruns;
+ u32 timer_300us_overruns;
+ u32 timer_max_overrun;
+ u32 timer_sum_overrun;
+
+ bool rx_no_low_drive;
+ u32 tx_custom_low_usecs;
+ u32 tx_custom_high_usecs;
+ u32 tx_glitch_low_usecs;
+ u32 tx_glitch_high_usecs;
+ bool tx_ignore_nack_until_eom;
+ bool tx_custom_pulse;
+ bool tx_generated_poll;
+ bool tx_post_eom;
+ bool tx_glitch_falling_edge;
+ bool tx_glitch_rising_edge;
+ u8 tx_extra_bytes;
+ u32 tx_low_drive_cnt;
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+ u64 error_inj[CEC_ERROR_INJ_OP_ANY + 1];
+ u8 error_inj_args[CEC_ERROR_INJ_OP_ANY + 1][CEC_ERROR_INJ_NUM_ARGS];
+#endif
+};
+
+void cec_pin_start_timer(struct cec_pin *pin);
+
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line);
+int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf);
+
+u16 cec_pin_rx_error_inj(struct cec_pin *pin);
+u16 cec_pin_tx_error_inj(struct cec_pin *pin);
+#endif
+
+#endif
diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c
new file mode 100644
index 000000000000..4d7155281daa
--- /dev/null
+++ b/drivers/media/cec/core/cec-pin.c
@@ -0,0 +1,1397 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <linux/delay.h>
+#include <linux/sched/types.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+
+#include <media/cec-pin.h>
+#include "cec-pin-priv.h"
+
+/* All timings are in microseconds */
+
+/* start bit timings */
+#define CEC_TIM_START_BIT_LOW 3700
+#define CEC_TIM_START_BIT_LOW_MIN 3500
+#define CEC_TIM_START_BIT_LOW_MAX 3900
+#define CEC_TIM_START_BIT_TOTAL 4500
+#define CEC_TIM_START_BIT_TOTAL_MIN 4300
+#define CEC_TIM_START_BIT_TOTAL_MAX 4700
+
+/* data bit timings */
+#define CEC_TIM_DATA_BIT_0_LOW 1500
+#define CEC_TIM_DATA_BIT_0_LOW_MIN 1300
+#define CEC_TIM_DATA_BIT_0_LOW_MAX 1700
+#define CEC_TIM_DATA_BIT_1_LOW 600
+#define CEC_TIM_DATA_BIT_1_LOW_MIN 400
+#define CEC_TIM_DATA_BIT_1_LOW_MAX 800
+#define CEC_TIM_DATA_BIT_TOTAL 2400
+#define CEC_TIM_DATA_BIT_TOTAL_MIN 2050
+#define CEC_TIM_DATA_BIT_TOTAL_MAX 2750
+/* earliest safe time to sample the bit state */
+#define CEC_TIM_DATA_BIT_SAMPLE 850
+/* earliest time the bit is back to 1 (T7 + 50) */
+#define CEC_TIM_DATA_BIT_HIGH 1750
+
+/* when idle, sample once per millisecond */
+#define CEC_TIM_IDLE_SAMPLE 1000
+/* when processing the start bit, sample twice per millisecond */
+#define CEC_TIM_START_BIT_SAMPLE 500
+/* when polling for a state change, sample once every 50 microseconds */
+#define CEC_TIM_SAMPLE 50
+
+#define CEC_TIM_LOW_DRIVE_ERROR (1.5 * CEC_TIM_DATA_BIT_TOTAL)
+
+/*
+ * Total data bit time that is too short/long for a valid bit,
+ * used for error injection.
+ */
+#define CEC_TIM_DATA_BIT_TOTAL_SHORT 1800
+#define CEC_TIM_DATA_BIT_TOTAL_LONG 2900
+
+/*
+ * Total start bit time that is too short/long for a valid bit,
+ * used for error injection.
+ */
+#define CEC_TIM_START_BIT_TOTAL_SHORT 4100
+#define CEC_TIM_START_BIT_TOTAL_LONG 5000
+
+/* Data bits are 0-7, EOM is bit 8 and ACK is bit 9 */
+#define EOM_BIT 8
+#define ACK_BIT 9
+
+struct cec_state {
+ const char * const name;
+ unsigned int usecs;
+};
+
+static const struct cec_state states[CEC_PIN_STATES] = {
+ { "Off", 0 },
+ { "Idle", CEC_TIM_IDLE_SAMPLE },
+ { "Tx Wait", CEC_TIM_SAMPLE },
+ { "Tx Wait for High", CEC_TIM_IDLE_SAMPLE },
+ { "Tx Start Bit Low", CEC_TIM_START_BIT_LOW },
+ { "Tx Start Bit High", CEC_TIM_START_BIT_TOTAL - CEC_TIM_START_BIT_LOW },
+ { "Tx Start Bit High Short", CEC_TIM_START_BIT_TOTAL_SHORT - CEC_TIM_START_BIT_LOW },
+ { "Tx Start Bit High Long", CEC_TIM_START_BIT_TOTAL_LONG - CEC_TIM_START_BIT_LOW },
+ { "Tx Start Bit Low Custom", 0 },
+ { "Tx Start Bit High Custom", 0 },
+ { "Tx Data 0 Low", CEC_TIM_DATA_BIT_0_LOW },
+ { "Tx Data 0 High", CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_0_LOW },
+ { "Tx Data 0 High Short", CEC_TIM_DATA_BIT_TOTAL_SHORT - CEC_TIM_DATA_BIT_0_LOW },
+ { "Tx Data 0 High Long", CEC_TIM_DATA_BIT_TOTAL_LONG - CEC_TIM_DATA_BIT_0_LOW },
+ { "Tx Data 1 Low", CEC_TIM_DATA_BIT_1_LOW },
+ { "Tx Data 1 High", CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_1_LOW },
+ { "Tx Data 1 High Short", CEC_TIM_DATA_BIT_TOTAL_SHORT - CEC_TIM_DATA_BIT_1_LOW },
+ { "Tx Data 1 High Long", CEC_TIM_DATA_BIT_TOTAL_LONG - CEC_TIM_DATA_BIT_1_LOW },
+ { "Tx Data 1 High Pre Sample", CEC_TIM_DATA_BIT_SAMPLE - CEC_TIM_DATA_BIT_1_LOW },
+ { "Tx Data 1 High Post Sample", CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_SAMPLE },
+ { "Tx Data 1 High Post Sample Short", CEC_TIM_DATA_BIT_TOTAL_SHORT - CEC_TIM_DATA_BIT_SAMPLE },
+ { "Tx Data 1 High Post Sample Long", CEC_TIM_DATA_BIT_TOTAL_LONG - CEC_TIM_DATA_BIT_SAMPLE },
+ { "Tx Data Bit Low Custom", 0 },
+ { "Tx Data Bit High Custom", 0 },
+ { "Tx Pulse Low Custom", 0 },
+ { "Tx Pulse High Custom", 0 },
+ { "Tx Low Drive", CEC_TIM_LOW_DRIVE_ERROR },
+ { "Rx Start Bit Low", CEC_TIM_SAMPLE },
+ { "Rx Start Bit High", CEC_TIM_SAMPLE },
+ { "Rx Data Sample", CEC_TIM_DATA_BIT_SAMPLE },
+ { "Rx Data Post Sample", CEC_TIM_DATA_BIT_HIGH - CEC_TIM_DATA_BIT_SAMPLE },
+ { "Rx Data Wait for Low", CEC_TIM_SAMPLE },
+ { "Rx Ack Low", CEC_TIM_DATA_BIT_0_LOW },
+ { "Rx Ack Low Post", CEC_TIM_DATA_BIT_HIGH - CEC_TIM_DATA_BIT_0_LOW },
+ { "Rx Ack High Post", CEC_TIM_DATA_BIT_HIGH },
+ { "Rx Ack Finish", CEC_TIM_DATA_BIT_TOTAL_MIN - CEC_TIM_DATA_BIT_HIGH },
+ { "Rx Low Drive", CEC_TIM_LOW_DRIVE_ERROR },
+ { "Rx Irq", 0 },
+};
+
+static void cec_pin_update(struct cec_pin *pin, bool v, bool force)
+{
+ if (!force && v == pin->adap->cec_pin_is_high)
+ return;
+
+ pin->adap->cec_pin_is_high = v;
+ if (atomic_read(&pin->work_pin_num_events) < CEC_NUM_PIN_EVENTS) {
+ u8 ev = v;
+
+ if (pin->work_pin_events_dropped) {
+ pin->work_pin_events_dropped = false;
+ ev |= CEC_PIN_EVENT_FL_DROPPED;
+ }
+ pin->work_pin_events[pin->work_pin_events_wr] = ev;
+ pin->work_pin_ts[pin->work_pin_events_wr] = ktime_get();
+ pin->work_pin_events_wr =
+ (pin->work_pin_events_wr + 1) % CEC_NUM_PIN_EVENTS;
+ atomic_inc(&pin->work_pin_num_events);
+ } else {
+ pin->work_pin_events_dropped = true;
+ pin->work_pin_events_dropped_cnt++;
+ }
+ wake_up_interruptible(&pin->kthread_waitq);
+}
+
+static bool cec_pin_read(struct cec_pin *pin)
+{
+ bool v = call_pin_op(pin, read);
+
+ cec_pin_update(pin, v, false);
+ return v;
+}
+
+static void cec_pin_insert_glitch(struct cec_pin *pin, bool rising_edge)
+{
+ /*
+ * Insert a short glitch after the falling or rising edge to
+ * simulate reflections on the CEC line. This can be used to
+ * test deglitch filters, which should be present in CEC devices
+ * to deal with noise on the line.
+ */
+ if (!pin->tx_glitch_high_usecs || !pin->tx_glitch_low_usecs)
+ return;
+ if (rising_edge) {
+ udelay(pin->tx_glitch_high_usecs);
+ call_void_pin_op(pin, low);
+ udelay(pin->tx_glitch_low_usecs);
+ call_void_pin_op(pin, high);
+ } else {
+ udelay(pin->tx_glitch_low_usecs);
+ call_void_pin_op(pin, high);
+ udelay(pin->tx_glitch_high_usecs);
+ call_void_pin_op(pin, low);
+ }
+}
+
+static void cec_pin_low(struct cec_pin *pin)
+{
+ call_void_pin_op(pin, low);
+ if (pin->tx_glitch_falling_edge && pin->adap->cec_pin_is_high)
+ cec_pin_insert_glitch(pin, false);
+ cec_pin_update(pin, false, false);
+}
+
+static bool cec_pin_high(struct cec_pin *pin)
+{
+ call_void_pin_op(pin, high);
+ if (pin->tx_glitch_rising_edge && !pin->adap->cec_pin_is_high)
+ cec_pin_insert_glitch(pin, true);
+ return cec_pin_read(pin);
+}
+
+static bool rx_error_inj(struct cec_pin *pin, unsigned int mode_offset,
+ int arg_idx, u8 *arg)
+{
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+ u16 cmd = cec_pin_rx_error_inj(pin);
+ u64 e = pin->error_inj[cmd];
+ unsigned int mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK;
+
+ if (arg_idx >= 0) {
+ u8 pos = pin->error_inj_args[cmd][arg_idx];
+
+ if (arg)
+ *arg = pos;
+ else if (pos != pin->rx_bit)
+ return false;
+ }
+
+ switch (mode) {
+ case CEC_ERROR_INJ_MODE_ONCE:
+ pin->error_inj[cmd] &=
+ ~(CEC_ERROR_INJ_MODE_MASK << mode_offset);
+ return true;
+ case CEC_ERROR_INJ_MODE_ALWAYS:
+ return true;
+ case CEC_ERROR_INJ_MODE_TOGGLE:
+ return pin->rx_toggle;
+ default:
+ return false;
+ }
+#else
+ return false;
+#endif
+}
+
+static bool rx_nack(struct cec_pin *pin)
+{
+ return rx_error_inj(pin, CEC_ERROR_INJ_RX_NACK_OFFSET, -1, NULL);
+}
+
+static bool rx_low_drive(struct cec_pin *pin)
+{
+ return rx_error_inj(pin, CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET,
+ CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX, NULL);
+}
+
+static bool rx_add_byte(struct cec_pin *pin)
+{
+ return rx_error_inj(pin, CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET, -1, NULL);
+}
+
+static bool rx_remove_byte(struct cec_pin *pin)
+{
+ return rx_error_inj(pin, CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET, -1, NULL);
+}
+
+static bool rx_arb_lost(struct cec_pin *pin, u8 *poll)
+{
+ return pin->tx_msg.len == 0 &&
+ rx_error_inj(pin, CEC_ERROR_INJ_RX_ARB_LOST_OFFSET,
+ CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX, poll);
+}
+
+static bool tx_error_inj(struct cec_pin *pin, unsigned int mode_offset,
+ int arg_idx, u8 *arg)
+{
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+ u16 cmd = cec_pin_tx_error_inj(pin);
+ u64 e = pin->error_inj[cmd];
+ unsigned int mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK;
+
+ if (arg_idx >= 0) {
+ u8 pos = pin->error_inj_args[cmd][arg_idx];
+
+ if (arg)
+ *arg = pos;
+ else if (pos != pin->tx_bit)
+ return false;
+ }
+
+ switch (mode) {
+ case CEC_ERROR_INJ_MODE_ONCE:
+ pin->error_inj[cmd] &=
+ ~(CEC_ERROR_INJ_MODE_MASK << mode_offset);
+ return true;
+ case CEC_ERROR_INJ_MODE_ALWAYS:
+ return true;
+ case CEC_ERROR_INJ_MODE_TOGGLE:
+ return pin->tx_toggle;
+ default:
+ return false;
+ }
+#else
+ return false;
+#endif
+}
+
+static bool tx_no_eom(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_NO_EOM_OFFSET, -1, NULL);
+}
+
+static bool tx_early_eom(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET, -1, NULL);
+}
+
+static bool tx_short_bit(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX, NULL);
+}
+
+static bool tx_long_bit(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_LONG_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX, NULL);
+}
+
+static bool tx_custom_bit(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX, NULL);
+}
+
+static bool tx_short_start(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_SHORT_START_OFFSET, -1, NULL);
+}
+
+static bool tx_long_start(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_LONG_START_OFFSET, -1, NULL);
+}
+
+static bool tx_custom_start(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET,
+ -1, NULL);
+}
+
+static bool tx_last_bit(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_LAST_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX, NULL);
+}
+
+static u8 tx_add_bytes(struct cec_pin *pin)
+{
+ u8 bytes;
+
+ if (tx_error_inj(pin, CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET,
+ CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX, &bytes))
+ return bytes;
+ return 0;
+}
+
+static bool tx_remove_byte(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET, -1, NULL);
+}
+
+static bool tx_low_drive(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET,
+ CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX, NULL);
+}
+
+static void cec_pin_to_idle(struct cec_pin *pin)
+{
+ /*
+ * Reset all status fields, release the bus and
+ * go to idle state.
+ */
+ pin->rx_bit = pin->tx_bit = 0;
+ pin->rx_msg.len = 0;
+ memset(pin->rx_msg.msg, 0, sizeof(pin->rx_msg.msg));
+ pin->ts = ns_to_ktime(0);
+ pin->tx_generated_poll = false;
+ pin->tx_post_eom = false;
+ if (pin->state >= CEC_ST_TX_WAIT &&
+ pin->state <= CEC_ST_TX_LOW_DRIVE)
+ pin->tx_toggle ^= 1;
+ if (pin->state >= CEC_ST_RX_START_BIT_LOW &&
+ pin->state <= CEC_ST_RX_LOW_DRIVE)
+ pin->rx_toggle ^= 1;
+ pin->state = CEC_ST_IDLE;
+}
+
+/*
+ * Handle Transmit-related states
+ *
+ * Basic state changes when transmitting:
+ *
+ * Idle -> Tx Wait (waiting for the end of signal free time) ->
+ * Tx Start Bit Low -> Tx Start Bit High ->
+ *
+ * Regular data bits + EOM:
+ * Tx Data 0 Low -> Tx Data 0 High ->
+ * or:
+ * Tx Data 1 Low -> Tx Data 1 High ->
+ *
+ * First 4 data bits or Ack bit:
+ * Tx Data 0 Low -> Tx Data 0 High ->
+ * or:
+ * Tx Data 1 Low -> Tx Data 1 High -> Tx Data 1 Pre Sample ->
+ * Tx Data 1 Post Sample ->
+ *
+ * After the last Ack go to Idle.
+ *
+ * If it detects a Low Drive condition then:
+ * Tx Wait For High -> Idle
+ *
+ * If it loses arbitration, then it switches to state Rx Data Post Sample.
+ */
+static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts)
+{
+ bool v;
+ bool is_ack_bit, ack;
+
+ switch (pin->state) {
+ case CEC_ST_TX_WAIT_FOR_HIGH:
+ if (cec_pin_read(pin))
+ cec_pin_to_idle(pin);
+ break;
+
+ case CEC_ST_TX_START_BIT_LOW:
+ if (tx_short_start(pin)) {
+ /*
+ * Error Injection: send an invalid (too short)
+ * start pulse.
+ */
+ pin->state = CEC_ST_TX_START_BIT_HIGH_SHORT;
+ } else if (tx_long_start(pin)) {
+ /*
+ * Error Injection: send an invalid (too long)
+ * start pulse.
+ */
+ pin->state = CEC_ST_TX_START_BIT_HIGH_LONG;
+ } else {
+ pin->state = CEC_ST_TX_START_BIT_HIGH;
+ }
+ /* Generate start bit */
+ cec_pin_high(pin);
+ break;
+
+ case CEC_ST_TX_START_BIT_LOW_CUSTOM:
+ pin->state = CEC_ST_TX_START_BIT_HIGH_CUSTOM;
+ /* Generate start bit */
+ cec_pin_high(pin);
+ break;
+
+ case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG:
+ if (pin->tx_nacked) {
+ cec_pin_to_idle(pin);
+ pin->tx_msg.len = 0;
+ if (pin->tx_generated_poll)
+ break;
+ pin->work_tx_ts = ts;
+ pin->work_tx_status = CEC_TX_STATUS_NACK;
+ wake_up_interruptible(&pin->kthread_waitq);
+ break;
+ }
+ fallthrough;
+ case CEC_ST_TX_DATA_BIT_0_HIGH:
+ case CEC_ST_TX_DATA_BIT_0_HIGH_SHORT:
+ case CEC_ST_TX_DATA_BIT_0_HIGH_LONG:
+ case CEC_ST_TX_DATA_BIT_1_HIGH:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_SHORT:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_LONG:
+ /*
+ * If the read value is 1, then all is OK, otherwise we have a
+ * low drive condition.
+ *
+ * Special case: when we generate a poll message due to an
+ * Arbitration Lost error injection, then ignore this since
+ * the pin can actually be low in that case.
+ */
+ if (!cec_pin_read(pin) && !pin->tx_generated_poll) {
+ /*
+ * It's 0, so someone detected an error and pulled the
+ * line low for 1.5 times the nominal bit period.
+ */
+ pin->tx_msg.len = 0;
+ pin->state = CEC_ST_TX_WAIT_FOR_HIGH;
+ pin->work_tx_ts = ts;
+ pin->work_tx_status = CEC_TX_STATUS_LOW_DRIVE;
+ pin->tx_low_drive_cnt++;
+ wake_up_interruptible(&pin->kthread_waitq);
+ break;
+ }
+ fallthrough;
+ case CEC_ST_TX_DATA_BIT_HIGH_CUSTOM:
+ if (tx_last_bit(pin)) {
+ /* Error Injection: just stop sending after this bit */
+ cec_pin_to_idle(pin);
+ pin->tx_msg.len = 0;
+ if (pin->tx_generated_poll)
+ break;
+ pin->work_tx_ts = ts;
+ pin->work_tx_status = CEC_TX_STATUS_OK;
+ wake_up_interruptible(&pin->kthread_waitq);
+ break;
+ }
+ pin->tx_bit++;
+ fallthrough;
+ case CEC_ST_TX_START_BIT_HIGH:
+ case CEC_ST_TX_START_BIT_HIGH_SHORT:
+ case CEC_ST_TX_START_BIT_HIGH_LONG:
+ case CEC_ST_TX_START_BIT_HIGH_CUSTOM:
+ if (tx_low_drive(pin)) {
+ /* Error injection: go to low drive */
+ cec_pin_low(pin);
+ pin->state = CEC_ST_TX_LOW_DRIVE;
+ pin->tx_msg.len = 0;
+ if (pin->tx_generated_poll)
+ break;
+ pin->work_tx_ts = ts;
+ pin->work_tx_status = CEC_TX_STATUS_LOW_DRIVE;
+ pin->tx_low_drive_cnt++;
+ wake_up_interruptible(&pin->kthread_waitq);
+ break;
+ }
+ if (pin->tx_bit / 10 >= pin->tx_msg.len + pin->tx_extra_bytes) {
+ cec_pin_to_idle(pin);
+ pin->tx_msg.len = 0;
+ if (pin->tx_generated_poll)
+ break;
+ pin->work_tx_ts = ts;
+ pin->work_tx_status = CEC_TX_STATUS_OK;
+ wake_up_interruptible(&pin->kthread_waitq);
+ break;
+ }
+
+ switch (pin->tx_bit % 10) {
+ default: {
+ /*
+ * In the CEC_ERROR_INJ_TX_ADD_BYTES case we transmit
+ * extra bytes, so pin->tx_bit / 10 can become >= 16.
+ * Generate bit values for those extra bytes instead
+ * of reading them from the transmit buffer.
+ */
+ unsigned int idx = (pin->tx_bit / 10);
+ u8 val = idx;
+
+ if (idx < pin->tx_msg.len)
+ val = pin->tx_msg.msg[idx];
+ v = val & (1 << (7 - (pin->tx_bit % 10)));
+
+ pin->state = v ? CEC_ST_TX_DATA_BIT_1_LOW :
+ CEC_ST_TX_DATA_BIT_0_LOW;
+ break;
+ }
+ case EOM_BIT: {
+ unsigned int tot_len = pin->tx_msg.len +
+ pin->tx_extra_bytes;
+ unsigned int tx_byte_idx = pin->tx_bit / 10;
+
+ v = !pin->tx_post_eom && tx_byte_idx == tot_len - 1;
+ if (tot_len > 1 && tx_byte_idx == tot_len - 2 &&
+ tx_early_eom(pin)) {
+ /* Error injection: set EOM one byte early */
+ v = true;
+ pin->tx_post_eom = true;
+ } else if (v && tx_no_eom(pin)) {
+ /* Error injection: no EOM */
+ v = false;
+ }
+ pin->state = v ? CEC_ST_TX_DATA_BIT_1_LOW :
+ CEC_ST_TX_DATA_BIT_0_LOW;
+ break;
+ }
+ case ACK_BIT:
+ pin->state = CEC_ST_TX_DATA_BIT_1_LOW;
+ break;
+ }
+ if (tx_custom_bit(pin))
+ pin->state = CEC_ST_TX_DATA_BIT_LOW_CUSTOM;
+ cec_pin_low(pin);
+ break;
+
+ case CEC_ST_TX_DATA_BIT_0_LOW:
+ case CEC_ST_TX_DATA_BIT_1_LOW:
+ v = pin->state == CEC_ST_TX_DATA_BIT_1_LOW;
+ is_ack_bit = pin->tx_bit % 10 == ACK_BIT;
+ if (v && (pin->tx_bit < 4 || is_ack_bit)) {
+ pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE;
+ } else if (!is_ack_bit && tx_short_bit(pin)) {
+ /* Error Injection: send an invalid (too short) bit */
+ pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH_SHORT :
+ CEC_ST_TX_DATA_BIT_0_HIGH_SHORT;
+ } else if (!is_ack_bit && tx_long_bit(pin)) {
+ /* Error Injection: send an invalid (too long) bit */
+ pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH_LONG :
+ CEC_ST_TX_DATA_BIT_0_HIGH_LONG;
+ } else {
+ pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH :
+ CEC_ST_TX_DATA_BIT_0_HIGH;
+ }
+ cec_pin_high(pin);
+ break;
+
+ case CEC_ST_TX_DATA_BIT_LOW_CUSTOM:
+ pin->state = CEC_ST_TX_DATA_BIT_HIGH_CUSTOM;
+ cec_pin_high(pin);
+ break;
+
+ case CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE:
+ /* Read the CEC value at the sample time */
+ v = cec_pin_read(pin);
+ is_ack_bit = pin->tx_bit % 10 == ACK_BIT;
+ /*
+ * If v == 0 and we're within the first 4 bits
+ * of the initiator, then someone else started
+ * transmitting and we lost the arbitration
+ * (i.e. the logical address of the other
+ * transmitter has more leading 0 bits in the
+ * initiator).
+ */
+ if (!v && !is_ack_bit && !pin->tx_generated_poll) {
+ pin->tx_msg.len = 0;
+ pin->work_tx_ts = ts;
+ pin->work_tx_status = CEC_TX_STATUS_ARB_LOST;
+ wake_up_interruptible(&pin->kthread_waitq);
+ pin->rx_bit = pin->tx_bit;
+ pin->tx_bit = 0;
+ memset(pin->rx_msg.msg, 0, sizeof(pin->rx_msg.msg));
+ pin->rx_msg.msg[0] = pin->tx_msg.msg[0];
+ pin->rx_msg.msg[0] &= (0xff << (8 - pin->rx_bit));
+ pin->rx_msg.len = 0;
+ pin->ts = ktime_sub_us(ts, CEC_TIM_DATA_BIT_SAMPLE);
+ pin->state = CEC_ST_RX_DATA_POST_SAMPLE;
+ pin->rx_bit++;
+ break;
+ }
+ pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE;
+ if (!is_ack_bit && tx_short_bit(pin)) {
+ /* Error Injection: send an invalid (too short) bit */
+ pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT;
+ } else if (!is_ack_bit && tx_long_bit(pin)) {
+ /* Error Injection: send an invalid (too long) bit */
+ pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG;
+ }
+ if (!is_ack_bit)
+ break;
+ /* Was the message ACKed? */
+ ack = cec_msg_is_broadcast(&pin->tx_msg) ? v : !v;
+ if (!ack && (!pin->tx_ignore_nack_until_eom ||
+ pin->tx_bit / 10 == pin->tx_msg.len - 1) &&
+ !pin->tx_post_eom) {
+ /*
+ * Note: the CEC spec is ambiguous regarding
+ * what action to take when a NACK appears
+ * before the last byte of the payload was
+ * transmitted: either stop transmitting
+ * immediately, or wait until the last byte
+ * was transmitted.
+ *
+ * Most CEC implementations appear to stop
+ * immediately, and that's what we do here
+ * as well.
+ */
+ pin->tx_nacked = true;
+ }
+ break;
+
+ case CEC_ST_TX_PULSE_LOW_CUSTOM:
+ cec_pin_high(pin);
+ pin->state = CEC_ST_TX_PULSE_HIGH_CUSTOM;
+ break;
+
+ case CEC_ST_TX_PULSE_HIGH_CUSTOM:
+ cec_pin_to_idle(pin);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Handle Receive-related states
+ *
+ * Basic state changes when receiving:
+ *
+ * Rx Start Bit Low -> Rx Start Bit High ->
+ * Regular data bits + EOM:
+ * Rx Data Sample -> Rx Data Post Sample -> Rx Data High ->
+ * Ack bit 0:
+ * Rx Ack Low -> Rx Ack Low Post -> Rx Data High ->
+ * Ack bit 1:
+ * Rx Ack High Post -> Rx Data High ->
+ * Ack bit 0 && EOM:
+ * Rx Ack Low -> Rx Ack Low Post -> Rx Ack Finish -> Idle
+ */
+static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
+{
+ s32 delta;
+ bool v;
+ bool ack;
+ bool bcast, for_us;
+ u8 dest;
+ u8 poll;
+
+ switch (pin->state) {
+ /* Receive states */
+ case CEC_ST_RX_START_BIT_LOW:
+ v = cec_pin_read(pin);
+ if (!v)
+ break;
+ pin->state = CEC_ST_RX_START_BIT_HIGH;
+ delta = ktime_us_delta(ts, pin->ts);
+ /* Start bit low is too short, go back to idle */
+ if (delta < CEC_TIM_START_BIT_LOW_MIN - CEC_TIM_IDLE_SAMPLE) {
+ if (!pin->rx_start_bit_low_too_short_cnt++) {
+ pin->rx_start_bit_low_too_short_ts = ktime_to_ns(pin->ts);
+ pin->rx_start_bit_low_too_short_delta = delta;
+ }
+ cec_pin_to_idle(pin);
+ break;
+ }
+ if (rx_arb_lost(pin, &poll)) {
+ cec_msg_init(&pin->tx_msg, poll >> 4, poll & 0xf);
+ pin->tx_generated_poll = true;
+ pin->tx_extra_bytes = 0;
+ pin->state = CEC_ST_TX_START_BIT_HIGH;
+ pin->ts = ts;
+ }
+ break;
+
+ case CEC_ST_RX_START_BIT_HIGH:
+ v = cec_pin_read(pin);
+ delta = ktime_us_delta(ts, pin->ts);
+ /*
+ * Unfortunately the spec does not specify when to give up
+ * and go to idle. We just pick TOTAL_LONG.
+ */
+ if (v && delta > CEC_TIM_START_BIT_TOTAL_LONG) {
+ pin->rx_start_bit_too_long_cnt++;
+ cec_pin_to_idle(pin);
+ break;
+ }
+ if (v)
+ break;
+ /* Start bit is too short, go back to idle */
+ if (delta < CEC_TIM_START_BIT_TOTAL_MIN - CEC_TIM_IDLE_SAMPLE) {
+ if (!pin->rx_start_bit_too_short_cnt++) {
+ pin->rx_start_bit_too_short_ts = ktime_to_ns(pin->ts);
+ pin->rx_start_bit_too_short_delta = delta;
+ }
+ cec_pin_to_idle(pin);
+ break;
+ }
+ if (rx_low_drive(pin)) {
+ /* Error injection: go to low drive */
+ cec_pin_low(pin);
+ pin->state = CEC_ST_RX_LOW_DRIVE;
+ pin->rx_low_drive_cnt++;
+ break;
+ }
+ pin->state = CEC_ST_RX_DATA_SAMPLE;
+ pin->ts = ts;
+ pin->rx_eom = false;
+ break;
+
+ case CEC_ST_RX_DATA_SAMPLE:
+ v = cec_pin_read(pin);
+ pin->state = CEC_ST_RX_DATA_POST_SAMPLE;
+ switch (pin->rx_bit % 10) {
+ default:
+ if (pin->rx_bit / 10 < CEC_MAX_MSG_SIZE)
+ pin->rx_msg.msg[pin->rx_bit / 10] |=
+ v << (7 - (pin->rx_bit % 10));
+ break;
+ case EOM_BIT:
+ pin->rx_eom = v;
+ pin->rx_msg.len = pin->rx_bit / 10 + 1;
+ break;
+ case ACK_BIT:
+ break;
+ }
+ pin->rx_bit++;
+ break;
+
+ case CEC_ST_RX_DATA_POST_SAMPLE:
+ pin->state = CEC_ST_RX_DATA_WAIT_FOR_LOW;
+ break;
+
+ case CEC_ST_RX_DATA_WAIT_FOR_LOW:
+ v = cec_pin_read(pin);
+ delta = ktime_us_delta(ts, pin->ts);
+ /*
+ * Unfortunately the spec does not specify when to give up
+ * and go to idle. We just pick TOTAL_LONG.
+ */
+ if (v && delta > CEC_TIM_DATA_BIT_TOTAL_LONG) {
+ pin->rx_data_bit_too_long_cnt++;
+ cec_pin_to_idle(pin);
+ break;
+ }
+ if (v)
+ break;
+
+ if (rx_low_drive(pin)) {
+ /* Error injection: go to low drive */
+ cec_pin_low(pin);
+ pin->state = CEC_ST_RX_LOW_DRIVE;
+ pin->rx_low_drive_cnt++;
+ break;
+ }
+
+ /*
+ * Go to low drive state when the total bit time is
+ * too short.
+ */
+ if (delta < CEC_TIM_DATA_BIT_TOTAL_MIN && !pin->rx_no_low_drive) {
+ if (!pin->rx_data_bit_too_short_cnt++) {
+ pin->rx_data_bit_too_short_ts = ktime_to_ns(pin->ts);
+ pin->rx_data_bit_too_short_delta = delta;
+ }
+ cec_pin_low(pin);
+ pin->state = CEC_ST_RX_LOW_DRIVE;
+ pin->rx_low_drive_cnt++;
+ break;
+ }
+ pin->ts = ts;
+ if (pin->rx_bit % 10 != 9) {
+ pin->state = CEC_ST_RX_DATA_SAMPLE;
+ break;
+ }
+
+ dest = cec_msg_destination(&pin->rx_msg);
+ bcast = dest == CEC_LOG_ADDR_BROADCAST;
+ /* for_us == broadcast or directed to us */
+ for_us = bcast || (pin->la_mask & (1 << dest));
+ /* ACK bit value */
+ ack = bcast ? 1 : !for_us;
+
+ if (for_us && rx_nack(pin)) {
+ /* Error injection: toggle the ACK bit */
+ ack = !ack;
+ }
+
+ if (ack) {
+ /* No need to write to the bus, just wait */
+ pin->state = CEC_ST_RX_ACK_HIGH_POST;
+ break;
+ }
+ cec_pin_low(pin);
+ pin->state = CEC_ST_RX_ACK_LOW;
+ break;
+
+ case CEC_ST_RX_ACK_LOW:
+ cec_pin_high(pin);
+ pin->state = CEC_ST_RX_ACK_LOW_POST;
+ break;
+
+ case CEC_ST_RX_ACK_LOW_POST:
+ case CEC_ST_RX_ACK_HIGH_POST:
+ v = cec_pin_read(pin);
+ if (v && pin->rx_eom) {
+ pin->work_rx_msg = pin->rx_msg;
+ pin->work_rx_msg.rx_ts = ktime_to_ns(ts);
+ wake_up_interruptible(&pin->kthread_waitq);
+ pin->ts = ts;
+ pin->state = CEC_ST_RX_ACK_FINISH;
+ break;
+ }
+ pin->rx_bit++;
+ pin->state = CEC_ST_RX_DATA_WAIT_FOR_LOW;
+ break;
+
+ case CEC_ST_RX_ACK_FINISH:
+ cec_pin_to_idle(pin);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Main timer function
+ *
+ */
+static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
+{
+ struct cec_pin *pin = container_of(timer, struct cec_pin, timer);
+ struct cec_adapter *adap = pin->adap;
+ ktime_t ts;
+ s32 delta;
+ u32 usecs;
+
+ ts = ktime_get();
+ if (ktime_to_ns(pin->timer_ts)) {
+ delta = ktime_us_delta(ts, pin->timer_ts);
+ pin->timer_cnt++;
+ if (delta > 100 && pin->state != CEC_ST_IDLE) {
+ /* Keep track of timer overruns */
+ pin->timer_sum_overrun += delta;
+ pin->timer_100us_overruns++;
+ if (delta > 300)
+ pin->timer_300us_overruns++;
+ if (delta > pin->timer_max_overrun)
+ pin->timer_max_overrun = delta;
+ }
+ }
+ if (adap->monitor_pin_cnt)
+ cec_pin_read(pin);
+
+ if (pin->wait_usecs) {
+ /*
+ * If we are monitoring the pin, then we have to
+ * sample at regular intervals.
+ */
+ if (pin->wait_usecs > 150) {
+ pin->wait_usecs -= 100;
+ pin->timer_ts = ktime_add_us(ts, 100);
+ hrtimer_forward_now(timer, us_to_ktime(100));
+ return HRTIMER_RESTART;
+ }
+ if (pin->wait_usecs > 100) {
+ pin->wait_usecs /= 2;
+ pin->timer_ts = ktime_add_us(ts, pin->wait_usecs);
+ hrtimer_forward_now(timer,
+ us_to_ktime(pin->wait_usecs));
+ return HRTIMER_RESTART;
+ }
+ pin->timer_ts = ktime_add_us(ts, pin->wait_usecs);
+ hrtimer_forward_now(timer,
+ us_to_ktime(pin->wait_usecs));
+ pin->wait_usecs = 0;
+ return HRTIMER_RESTART;
+ }
+
+ switch (pin->state) {
+ /* Transmit states */
+ case CEC_ST_TX_WAIT_FOR_HIGH:
+ case CEC_ST_TX_START_BIT_LOW:
+ case CEC_ST_TX_START_BIT_HIGH:
+ case CEC_ST_TX_START_BIT_HIGH_SHORT:
+ case CEC_ST_TX_START_BIT_HIGH_LONG:
+ case CEC_ST_TX_START_BIT_LOW_CUSTOM:
+ case CEC_ST_TX_START_BIT_HIGH_CUSTOM:
+ case CEC_ST_TX_DATA_BIT_0_LOW:
+ case CEC_ST_TX_DATA_BIT_0_HIGH:
+ case CEC_ST_TX_DATA_BIT_0_HIGH_SHORT:
+ case CEC_ST_TX_DATA_BIT_0_HIGH_LONG:
+ case CEC_ST_TX_DATA_BIT_1_LOW:
+ case CEC_ST_TX_DATA_BIT_1_HIGH:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_SHORT:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_LONG:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG:
+ case CEC_ST_TX_DATA_BIT_LOW_CUSTOM:
+ case CEC_ST_TX_DATA_BIT_HIGH_CUSTOM:
+ case CEC_ST_TX_PULSE_LOW_CUSTOM:
+ case CEC_ST_TX_PULSE_HIGH_CUSTOM:
+ cec_pin_tx_states(pin, ts);
+ break;
+
+ /* Receive states */
+ case CEC_ST_RX_START_BIT_LOW:
+ case CEC_ST_RX_START_BIT_HIGH:
+ case CEC_ST_RX_DATA_SAMPLE:
+ case CEC_ST_RX_DATA_POST_SAMPLE:
+ case CEC_ST_RX_DATA_WAIT_FOR_LOW:
+ case CEC_ST_RX_ACK_LOW:
+ case CEC_ST_RX_ACK_LOW_POST:
+ case CEC_ST_RX_ACK_HIGH_POST:
+ case CEC_ST_RX_ACK_FINISH:
+ cec_pin_rx_states(pin, ts);
+ break;
+
+ case CEC_ST_IDLE:
+ case CEC_ST_TX_WAIT:
+ if (!cec_pin_high(pin)) {
+ /* Start bit, switch to receive state */
+ pin->ts = ts;
+ pin->state = CEC_ST_RX_START_BIT_LOW;
+ /*
+ * If a transmit is pending, then that transmit should
+ * use a signal free time of no more than
+ * CEC_SIGNAL_FREE_TIME_NEW_INITIATOR since it will
+ * have a new initiator due to the receive that is now
+ * starting.
+ */
+ if (pin->tx_msg.len && pin->tx_signal_free_time >
+ CEC_SIGNAL_FREE_TIME_NEW_INITIATOR)
+ pin->tx_signal_free_time =
+ CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
+ break;
+ }
+ if (ktime_to_ns(pin->ts) == 0)
+ pin->ts = ts;
+ if (pin->tx_msg.len) {
+ /*
+ * Check if the bus has been free for long enough
+ * so we can kick off the pending transmit.
+ */
+ delta = ktime_us_delta(ts, pin->ts);
+ if (delta / CEC_TIM_DATA_BIT_TOTAL >=
+ pin->tx_signal_free_time) {
+ pin->tx_nacked = false;
+ if (tx_custom_start(pin))
+ pin->state = CEC_ST_TX_START_BIT_LOW_CUSTOM;
+ else
+ pin->state = CEC_ST_TX_START_BIT_LOW;
+ /* Generate start bit */
+ cec_pin_low(pin);
+ break;
+ }
+ if (delta / CEC_TIM_DATA_BIT_TOTAL >=
+ pin->tx_signal_free_time - 1)
+ pin->state = CEC_ST_TX_WAIT;
+ break;
+ }
+ if (pin->tx_custom_pulse && pin->state == CEC_ST_IDLE) {
+ pin->tx_custom_pulse = false;
+ /* Generate custom pulse */
+ cec_pin_low(pin);
+ pin->state = CEC_ST_TX_PULSE_LOW_CUSTOM;
+ break;
+ }
+ if (pin->state != CEC_ST_IDLE || pin->ops->enable_irq == NULL ||
+ pin->enable_irq_failed || adap->is_configuring ||
+ adap->is_configured || adap->monitor_all_cnt || !adap->monitor_pin_cnt)
+ break;
+ /* Switch to interrupt mode */
+ atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_ENABLE);
+ pin->state = CEC_ST_RX_IRQ;
+ wake_up_interruptible(&pin->kthread_waitq);
+ return HRTIMER_NORESTART;
+
+ case CEC_ST_TX_LOW_DRIVE:
+ case CEC_ST_RX_LOW_DRIVE:
+ cec_pin_high(pin);
+ cec_pin_to_idle(pin);
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pin->state) {
+ case CEC_ST_TX_START_BIT_LOW_CUSTOM:
+ case CEC_ST_TX_DATA_BIT_LOW_CUSTOM:
+ case CEC_ST_TX_PULSE_LOW_CUSTOM:
+ usecs = pin->tx_custom_low_usecs;
+ break;
+ case CEC_ST_TX_START_BIT_HIGH_CUSTOM:
+ case CEC_ST_TX_DATA_BIT_HIGH_CUSTOM:
+ case CEC_ST_TX_PULSE_HIGH_CUSTOM:
+ usecs = pin->tx_custom_high_usecs;
+ break;
+ default:
+ usecs = states[pin->state].usecs;
+ break;
+ }
+
+ if (!adap->monitor_pin_cnt || usecs <= 150) {
+ pin->wait_usecs = 0;
+ pin->timer_ts = ktime_add_us(ts, usecs);
+ hrtimer_forward_now(timer, us_to_ktime(usecs));
+ return HRTIMER_RESTART;
+ }
+ pin->wait_usecs = usecs - 100;
+ pin->timer_ts = ktime_add_us(ts, 100);
+ hrtimer_forward_now(timer, us_to_ktime(100));
+ return HRTIMER_RESTART;
+}
+
+static int cec_pin_thread_func(void *_adap)
+{
+ struct cec_adapter *adap = _adap;
+ struct cec_pin *pin = adap->pin;
+
+ pin->enabled_irq = false;
+ pin->enable_irq_failed = false;
+ for (;;) {
+ wait_event_interruptible(pin->kthread_waitq,
+ kthread_should_stop() ||
+ pin->work_rx_msg.len ||
+ pin->work_tx_status ||
+ atomic_read(&pin->work_irq_change) ||
+ atomic_read(&pin->work_pin_num_events));
+
+ if (kthread_should_stop())
+ break;
+
+ if (pin->work_rx_msg.len) {
+ struct cec_msg *msg = &pin->work_rx_msg;
+
+ if (msg->len > 1 && msg->len < CEC_MAX_MSG_SIZE &&
+ rx_add_byte(pin)) {
+ /* Error injection: add byte to the message */
+ msg->msg[msg->len++] = 0x55;
+ }
+ if (msg->len > 2 && rx_remove_byte(pin)) {
+ /* Error injection: remove byte from message */
+ msg->len--;
+ }
+ if (msg->len > CEC_MAX_MSG_SIZE)
+ msg->len = CEC_MAX_MSG_SIZE;
+ cec_received_msg_ts(adap, msg,
+ ns_to_ktime(pin->work_rx_msg.rx_ts));
+ msg->len = 0;
+ }
+
+ if (pin->work_tx_status) {
+ unsigned int tx_status = pin->work_tx_status;
+
+ pin->work_tx_status = 0;
+ cec_transmit_attempt_done_ts(adap, tx_status,
+ pin->work_tx_ts);
+ }
+
+ while (atomic_read(&pin->work_pin_num_events)) {
+ unsigned int idx = pin->work_pin_events_rd;
+ u8 v = pin->work_pin_events[idx];
+
+ cec_queue_pin_cec_event(adap,
+ v & CEC_PIN_EVENT_FL_IS_HIGH,
+ v & CEC_PIN_EVENT_FL_DROPPED,
+ pin->work_pin_ts[idx]);
+ pin->work_pin_events_rd = (idx + 1) % CEC_NUM_PIN_EVENTS;
+ atomic_dec(&pin->work_pin_num_events);
+ }
+
+ switch (atomic_xchg(&pin->work_irq_change,
+ CEC_PIN_IRQ_UNCHANGED)) {
+ case CEC_PIN_IRQ_DISABLE:
+ if (pin->enabled_irq) {
+ pin->ops->disable_irq(adap);
+ pin->enabled_irq = false;
+ pin->enable_irq_failed = false;
+ }
+ cec_pin_high(pin);
+ if (pin->state == CEC_ST_OFF)
+ break;
+ cec_pin_to_idle(pin);
+ hrtimer_start(&pin->timer, ns_to_ktime(0),
+ HRTIMER_MODE_REL);
+ break;
+ case CEC_PIN_IRQ_ENABLE:
+ if (pin->enabled_irq || !pin->ops->enable_irq ||
+ pin->adap->devnode.unregistered)
+ break;
+ pin->enable_irq_failed = !pin->ops->enable_irq(adap);
+ if (pin->enable_irq_failed) {
+ cec_pin_to_idle(pin);
+ hrtimer_start(&pin->timer, ns_to_ktime(0),
+ HRTIMER_MODE_REL);
+ } else {
+ pin->enabled_irq = true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (pin->enabled_irq) {
+ pin->ops->disable_irq(pin->adap);
+ pin->enabled_irq = false;
+ pin->enable_irq_failed = false;
+ cec_pin_high(pin);
+ }
+ return 0;
+}
+
+static int cec_pin_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct cec_pin *pin = adap->pin;
+
+ if (enable) {
+ cec_pin_read(pin);
+ cec_pin_to_idle(pin);
+ pin->tx_msg.len = 0;
+ pin->timer_ts = ns_to_ktime(0);
+ atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_UNCHANGED);
+ if (!pin->kthread) {
+ pin->kthread = kthread_run(cec_pin_thread_func, adap,
+ "cec-pin");
+ if (IS_ERR(pin->kthread)) {
+ int err = PTR_ERR(pin->kthread);
+
+ pr_err("cec-pin: kernel_thread() failed\n");
+ pin->kthread = NULL;
+ return err;
+ }
+ }
+ hrtimer_start(&pin->timer, ns_to_ktime(0),
+ HRTIMER_MODE_REL);
+ } else if (pin->kthread) {
+ hrtimer_cancel(&pin->timer);
+ cec_pin_high(pin);
+ cec_pin_to_idle(pin);
+ pin->state = CEC_ST_OFF;
+ pin->work_tx_status = 0;
+ atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_DISABLE);
+ wake_up_interruptible(&pin->kthread_waitq);
+ }
+ return 0;
+}
+
+static int cec_pin_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+{
+ struct cec_pin *pin = adap->pin;
+
+ if (log_addr == CEC_LOG_ADDR_INVALID)
+ pin->la_mask = 0;
+ else
+ pin->la_mask |= (1 << log_addr);
+ return 0;
+}
+
+void cec_pin_start_timer(struct cec_pin *pin)
+{
+ if (pin->state != CEC_ST_RX_IRQ)
+ return;
+
+ atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_DISABLE);
+ wake_up_interruptible(&pin->kthread_waitq);
+}
+
+static int cec_pin_adap_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct cec_pin *pin = adap->pin;
+
+ /*
+ * If a receive is in progress, then this transmit should use
+ * a signal free time of max CEC_SIGNAL_FREE_TIME_NEW_INITIATOR
+ * since when it starts transmitting it will have a new initiator.
+ */
+ if (pin->state != CEC_ST_IDLE &&
+ signal_free_time > CEC_SIGNAL_FREE_TIME_NEW_INITIATOR)
+ signal_free_time = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
+
+ pin->tx_signal_free_time = signal_free_time;
+ pin->tx_extra_bytes = 0;
+ pin->tx_msg = *msg;
+ if (msg->len > 1) {
+ /* Error injection: add byte to the message */
+ pin->tx_extra_bytes = tx_add_bytes(pin);
+ }
+ if (msg->len > 2 && tx_remove_byte(pin)) {
+ /* Error injection: remove byte from the message */
+ pin->tx_msg.len--;
+ }
+ pin->work_tx_status = 0;
+ pin->tx_bit = 0;
+ cec_pin_start_timer(pin);
+ return 0;
+}
+
+static void cec_pin_adap_status(struct cec_adapter *adap,
+ struct seq_file *file)
+{
+ struct cec_pin *pin = adap->pin;
+
+ seq_printf(file, "state: %s\n", states[pin->state].name);
+ seq_printf(file, "tx_bit: %d\n", pin->tx_bit);
+ seq_printf(file, "rx_bit: %d\n", pin->rx_bit);
+ seq_printf(file, "cec pin: %d\n", call_pin_op(pin, read));
+ seq_printf(file, "cec pin events dropped: %u\n",
+ pin->work_pin_events_dropped_cnt);
+ if (pin->ops->enable_irq)
+ seq_printf(file, "irq %s\n", pin->enabled_irq ? "enabled" :
+ (pin->enable_irq_failed ? "failed" : "disabled"));
+ if (pin->timer_100us_overruns) {
+ seq_printf(file, "timer overruns > 100us: %u of %u\n",
+ pin->timer_100us_overruns, pin->timer_cnt);
+ seq_printf(file, "timer overruns > 300us: %u of %u\n",
+ pin->timer_300us_overruns, pin->timer_cnt);
+ seq_printf(file, "max timer overrun: %u usecs\n",
+ pin->timer_max_overrun);
+ seq_printf(file, "avg timer overrun: %u usecs\n",
+ pin->timer_sum_overrun / pin->timer_100us_overruns);
+ }
+ if (pin->rx_start_bit_low_too_short_cnt)
+ seq_printf(file,
+ "rx start bit low too short: %u (delta %u, ts %llu)\n",
+ pin->rx_start_bit_low_too_short_cnt,
+ pin->rx_start_bit_low_too_short_delta,
+ pin->rx_start_bit_low_too_short_ts);
+ if (pin->rx_start_bit_too_short_cnt)
+ seq_printf(file,
+ "rx start bit too short: %u (delta %u, ts %llu)\n",
+ pin->rx_start_bit_too_short_cnt,
+ pin->rx_start_bit_too_short_delta,
+ pin->rx_start_bit_too_short_ts);
+ if (pin->rx_start_bit_too_long_cnt)
+ seq_printf(file, "rx start bit too long: %u\n",
+ pin->rx_start_bit_too_long_cnt);
+ if (pin->rx_data_bit_too_short_cnt)
+ seq_printf(file,
+ "rx data bit too short: %u (delta %u, ts %llu)\n",
+ pin->rx_data_bit_too_short_cnt,
+ pin->rx_data_bit_too_short_delta,
+ pin->rx_data_bit_too_short_ts);
+ if (pin->rx_data_bit_too_long_cnt)
+ seq_printf(file, "rx data bit too long: %u\n",
+ pin->rx_data_bit_too_long_cnt);
+ seq_printf(file, "rx initiated low drive: %u\n", pin->rx_low_drive_cnt);
+ seq_printf(file, "tx detected low drive: %u\n", pin->tx_low_drive_cnt);
+ pin->work_pin_events_dropped_cnt = 0;
+ pin->timer_cnt = 0;
+ pin->timer_100us_overruns = 0;
+ pin->timer_300us_overruns = 0;
+ pin->timer_max_overrun = 0;
+ pin->timer_sum_overrun = 0;
+ pin->rx_start_bit_low_too_short_cnt = 0;
+ pin->rx_start_bit_too_short_cnt = 0;
+ pin->rx_start_bit_too_long_cnt = 0;
+ pin->rx_data_bit_too_short_cnt = 0;
+ pin->rx_data_bit_too_long_cnt = 0;
+ pin->rx_low_drive_cnt = 0;
+ pin->tx_low_drive_cnt = 0;
+ call_void_pin_op(pin, status, file);
+}
+
+static int cec_pin_adap_monitor_all_enable(struct cec_adapter *adap,
+ bool enable)
+{
+ struct cec_pin *pin = adap->pin;
+
+ pin->monitor_all = enable;
+ return 0;
+}
+
+static void cec_pin_adap_free(struct cec_adapter *adap)
+{
+ struct cec_pin *pin = adap->pin;
+
+ if (pin->kthread)
+ kthread_stop(pin->kthread);
+ pin->kthread = NULL;
+ if (pin->ops->free)
+ pin->ops->free(adap);
+ adap->pin = NULL;
+ kfree(pin);
+}
+
+static int cec_pin_received(struct cec_adapter *adap, struct cec_msg *msg)
+{
+ struct cec_pin *pin = adap->pin;
+
+ if (pin->ops->received && !adap->devnode.unregistered)
+ return pin->ops->received(adap, msg);
+ return -ENOMSG;
+}
+
+void cec_pin_changed(struct cec_adapter *adap, bool value)
+{
+ struct cec_pin *pin = adap->pin;
+
+ cec_pin_update(pin, value, false);
+ if (!value && (adap->is_configuring || adap->is_configured ||
+ adap->monitor_all_cnt || !adap->monitor_pin_cnt))
+ atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_DISABLE);
+}
+EXPORT_SYMBOL_GPL(cec_pin_changed);
+
+static const struct cec_adap_ops cec_pin_adap_ops = {
+ .adap_enable = cec_pin_adap_enable,
+ .adap_monitor_all_enable = cec_pin_adap_monitor_all_enable,
+ .adap_log_addr = cec_pin_adap_log_addr,
+ .adap_transmit = cec_pin_adap_transmit,
+ .adap_status = cec_pin_adap_status,
+ .adap_free = cec_pin_adap_free,
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+ .error_inj_parse_line = cec_pin_error_inj_parse_line,
+ .error_inj_show = cec_pin_error_inj_show,
+#endif
+ .received = cec_pin_received,
+};
+
+struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops,
+ void *priv, const char *name, u32 caps)
+{
+ struct cec_adapter *adap;
+ struct cec_pin *pin = kzalloc(sizeof(*pin), GFP_KERNEL);
+
+ if (pin == NULL)
+ return ERR_PTR(-ENOMEM);
+ pin->ops = pin_ops;
+ atomic_set(&pin->work_pin_num_events, 0);
+ hrtimer_setup(&pin->timer, cec_pin_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ init_waitqueue_head(&pin->kthread_waitq);
+ pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT;
+
+ adap = cec_allocate_adapter(&cec_pin_adap_ops, priv, name,
+ caps | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN,
+ CEC_MAX_LOG_ADDRS);
+
+ if (IS_ERR(adap)) {
+ kfree(pin);
+ return adap;
+ }
+
+ adap->pin = pin;
+ pin->adap = adap;
+ cec_pin_update(pin, cec_pin_high(pin), true);
+ return adap;
+}
+EXPORT_SYMBOL_GPL(cec_pin_allocate_adapter);
diff --git a/drivers/media/cec/core/cec-priv.h b/drivers/media/cec/core/cec-priv.h
new file mode 100644
index 000000000000..ce42a37c4ac0
--- /dev/null
+++ b/drivers/media/cec/core/cec-priv.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * cec-priv.h - HDMI Consumer Electronics Control internal header
+ *
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#ifndef _CEC_PRIV_H
+#define _CEC_PRIV_H
+
+#include <linux/cec-funcs.h>
+#include <media/cec-notifier.h>
+
+#define dprintk(lvl, fmt, arg...) \
+ do { \
+ if (lvl <= cec_debug) \
+ pr_info("cec-%s: " fmt, adap->name, ## arg); \
+ } while (0)
+
+#define call_op(adap, op, arg...) \
+ ((adap->ops->op && !adap->devnode.unregistered) ? \
+ adap->ops->op(adap, ## arg) : 0)
+
+#define call_void_op(adap, op, arg...) \
+ do { \
+ if (adap->ops->op && !adap->devnode.unregistered) \
+ adap->ops->op(adap, ## arg); \
+ } while (0)
+
+/* devnode to cec_adapter */
+#define to_cec_adapter(node) container_of(node, struct cec_adapter, devnode)
+
+static inline bool msg_is_raw(const struct cec_msg *msg)
+{
+ return msg->flags & CEC_MSG_FL_RAW;
+}
+
+/* cec-core.c */
+extern int cec_debug;
+
+/* cec-adap.c */
+int cec_monitor_all_cnt_inc(struct cec_adapter *adap);
+void cec_monitor_all_cnt_dec(struct cec_adapter *adap);
+int cec_monitor_pin_cnt_inc(struct cec_adapter *adap);
+void cec_monitor_pin_cnt_dec(struct cec_adapter *adap);
+int cec_adap_status(struct seq_file *file, void *priv);
+int cec_thread_func(void *_adap);
+int cec_adap_enable(struct cec_adapter *adap);
+void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block);
+int __cec_s_log_addrs(struct cec_adapter *adap,
+ struct cec_log_addrs *log_addrs, bool block);
+int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
+ struct cec_fh *fh, bool block);
+void cec_queue_event_fh(struct cec_fh *fh,
+ const struct cec_event *new_ev, u64 ts);
+
+/* cec-api.c */
+extern const struct file_operations cec_devnode_fops;
+
+#endif
diff --git a/drivers/media/cec/i2c/Kconfig b/drivers/media/cec/i2c/Kconfig
new file mode 100644
index 000000000000..c31abc26f602
--- /dev/null
+++ b/drivers/media/cec/i2c/Kconfig
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# I2C drivers
+
+config CEC_CH7322
+ tristate "Chrontel CH7322 CEC controller"
+ depends on I2C
+ select REGMAP
+ select REGMAP_I2C
+ select CEC_CORE
+ help
+ This is a driver for the Chrontel CH7322 CEC controller. It uses the
+ generic CEC framework interface.
+ CEC bus is present in the HDMI connector and enables communication
+ between compatible devices.
+
+config CEC_NXP_TDA9950
+ tristate "NXP Semiconductors TDA9950/TDA998X HDMI CEC"
+ depends on I2C
+ select CEC_NOTIFIER
+ select CEC_CORE
+ default DRM_I2C_NXP_TDA998X
+ help
+ This is a driver for the NXP TDA9950 CEC controller and for the CEC
+ controller block integrated into several NXP TDA998x HDMI encoders.
diff --git a/drivers/media/cec/i2c/Makefile b/drivers/media/cec/i2c/Makefile
new file mode 100644
index 000000000000..95c9eda52583
--- /dev/null
+++ b/drivers/media/cec/i2c/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the CEC I2C device drivers.
+#
+obj-$(CONFIG_CEC_CH7322) += ch7322.o
+obj-$(CONFIG_CEC_NXP_TDA9950) += tda9950.o
diff --git a/drivers/media/cec/i2c/ch7322.c b/drivers/media/cec/i2c/ch7322.c
new file mode 100644
index 000000000000..b8755337b394
--- /dev/null
+++ b/drivers/media/cec/i2c/ch7322.c
@@ -0,0 +1,602 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the Chrontel CH7322 CEC Controller
+ *
+ * Copyright 2020 Google LLC.
+ */
+
+/*
+ * Notes
+ *
+ * - This device powers on in Auto Mode which has limited functionality. This
+ * driver disables Auto Mode when it attaches.
+ *
+ */
+
+#include <linux/cec.h>
+#include <linux/dmi.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/regmap.h>
+#include <media/cec.h>
+#include <media/cec-notifier.h>
+
+#define CH7322_WRITE 0x00
+#define CH7322_WRITE_MSENT 0x80
+#define CH7322_WRITE_BOK 0x40
+#define CH7322_WRITE_NMASK 0x0f
+
+/* Write buffer is 0x01-0x10 */
+#define CH7322_WRBUF 0x01
+#define CH7322_WRBUF_LEN 0x10
+
+#define CH7322_READ 0x40
+#define CH7322_READ_NRDT 0x80
+#define CH7322_READ_MSENT 0x20
+#define CH7322_READ_NMASK 0x0f
+
+/* Read buffer is 0x41-0x50 */
+#define CH7322_RDBUF 0x41
+#define CH7322_RDBUF_LEN 0x10
+
+#define CH7322_MODE 0x11
+#define CH7322_MODE_AUTO 0x78
+#define CH7322_MODE_SW 0xb5
+
+#define CH7322_RESET 0x12
+#define CH7322_RESET_RST 0x00
+
+#define CH7322_POWER 0x13
+#define CH7322_POWER_FPD 0x04
+
+#define CH7322_CFG0 0x17
+#define CH7322_CFG0_EOBEN 0x40
+#define CH7322_CFG0_PEOB 0x20
+#define CH7322_CFG0_CLRSPP 0x10
+#define CH7322_CFG0_FLOW 0x08
+
+#define CH7322_CFG1 0x1a
+#define CH7322_CFG1_STDBYO 0x04
+#define CH7322_CFG1_HPBP 0x02
+#define CH7322_CFG1_PIO 0x01
+
+#define CH7322_INTCTL 0x1b
+#define CH7322_INTCTL_INTPB 0x80
+#define CH7322_INTCTL_STDBY 0x40
+#define CH7322_INTCTL_HPDFALL 0x20
+#define CH7322_INTCTL_HPDRISE 0x10
+#define CH7322_INTCTL_RXMSG 0x08
+#define CH7322_INTCTL_TXMSG 0x04
+#define CH7322_INTCTL_NEWPHA 0x02
+#define CH7322_INTCTL_ERROR 0x01
+
+#define CH7322_DVCLKFNH 0x1d
+#define CH7322_DVCLKFNL 0x1e
+
+#define CH7322_CTL 0x31
+#define CH7322_CTL_FSTDBY 0x80
+#define CH7322_CTL_PLSEN 0x40
+#define CH7322_CTL_PLSPB 0x20
+#define CH7322_CTL_SPADL 0x10
+#define CH7322_CTL_HINIT 0x08
+#define CH7322_CTL_WPHYA 0x04
+#define CH7322_CTL_H1T 0x02
+#define CH7322_CTL_S1T 0x01
+
+#define CH7322_PAWH 0x32
+#define CH7322_PAWL 0x33
+
+#define CH7322_ADDLW 0x34
+#define CH7322_ADDLW_MASK 0xf0
+
+#define CH7322_ADDLR 0x3d
+#define CH7322_ADDLR_HPD 0x80
+#define CH7322_ADDLR_MASK 0x0f
+
+#define CH7322_INTDATA 0x3e
+#define CH7322_INTDATA_MODE 0x80
+#define CH7322_INTDATA_STDBY 0x40
+#define CH7322_INTDATA_HPDFALL 0x20
+#define CH7322_INTDATA_HPDRISE 0x10
+#define CH7322_INTDATA_RXMSG 0x08
+#define CH7322_INTDATA_TXMSG 0x04
+#define CH7322_INTDATA_NEWPHA 0x02
+#define CH7322_INTDATA_ERROR 0x01
+
+#define CH7322_EVENT 0x3f
+#define CH7322_EVENT_TXERR 0x80
+#define CH7322_EVENT_HRST 0x40
+#define CH7322_EVENT_HFST 0x20
+#define CH7322_EVENT_PHACHG 0x10
+#define CH7322_EVENT_ACTST 0x08
+#define CH7322_EVENT_PHARDY 0x04
+#define CH7322_EVENT_BSOK 0x02
+#define CH7322_EVENT_ERRADCF 0x01
+
+#define CH7322_DID 0x51
+#define CH7322_DID_CH7322 0x5b
+#define CH7322_DID_CH7323 0x5f
+
+#define CH7322_REVISIONID 0x52
+
+#define CH7322_PARH 0x53
+#define CH7322_PARL 0x54
+
+#define CH7322_IOCFG2 0x75
+#define CH7322_IOCFG_CIO 0x80
+#define CH7322_IOCFG_IOCFGMASK 0x78
+#define CH7322_IOCFG_AUDIO 0x04
+#define CH7322_IOCFG_SPAMST 0x02
+#define CH7322_IOCFG_SPAMSP 0x01
+
+#define CH7322_CTL3 0x7b
+#define CH7322_CTL3_SWENA 0x80
+#define CH7322_CTL3_FC_INIT 0x40
+#define CH7322_CTL3_SML_FL 0x20
+#define CH7322_CTL3_SM_RDST 0x10
+#define CH7322_CTL3_SPP_CIAH 0x08
+#define CH7322_CTL3_SPP_CIAL 0x04
+#define CH7322_CTL3_SPP_ACTH 0x02
+#define CH7322_CTL3_SPP_ACTL 0x01
+
+/* BOK status means NACK */
+#define CH7322_TX_FLAG_NACK BIT(0)
+/* Device will retry automatically */
+#define CH7322_TX_FLAG_RETRY BIT(1)
+
+struct ch7322 {
+ struct i2c_client *i2c;
+ struct regmap *regmap;
+ struct cec_adapter *cec;
+ struct mutex mutex; /* device access mutex */
+ u8 tx_flags;
+};
+
+static const struct regmap_config ch7322_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x7f,
+ .disable_locking = true,
+};
+
+static int ch7322_send_message(struct ch7322 *ch7322, const struct cec_msg *msg)
+{
+ unsigned int val;
+ unsigned int len = msg->len;
+ int ret;
+ int i;
+
+ WARN_ON(!mutex_is_locked(&ch7322->mutex));
+
+ if (len > CH7322_WRBUF_LEN || len < 1)
+ return -EINVAL;
+
+ ret = regmap_read(ch7322->regmap, CH7322_WRITE, &val);
+ if (ret)
+ return ret;
+
+ /* Buffer not ready */
+ if (!(val & CH7322_WRITE_MSENT))
+ return -EBUSY;
+
+ if (cec_msg_opcode(msg) == -1 &&
+ cec_msg_initiator(msg) == cec_msg_destination(msg)) {
+ ch7322->tx_flags = CH7322_TX_FLAG_NACK | CH7322_TX_FLAG_RETRY;
+ } else if (cec_msg_is_broadcast(msg)) {
+ ch7322->tx_flags = CH7322_TX_FLAG_NACK;
+ } else {
+ ch7322->tx_flags = CH7322_TX_FLAG_RETRY;
+ }
+
+ ret = regmap_write(ch7322->regmap, CH7322_WRITE, len - 1);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < len; i++) {
+ ret = regmap_write(ch7322->regmap,
+ CH7322_WRBUF + i, msg->msg[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ch7322_receive_message(struct ch7322 *ch7322, struct cec_msg *msg)
+{
+ unsigned int val;
+ int ret = 0;
+ int i;
+
+ WARN_ON(!mutex_is_locked(&ch7322->mutex));
+
+ ret = regmap_read(ch7322->regmap, CH7322_READ, &val);
+ if (ret)
+ return ret;
+
+ /* Message not ready */
+ if (!(val & CH7322_READ_NRDT))
+ return -EIO;
+
+ msg->len = (val & CH7322_READ_NMASK) + 1;
+
+ /* Read entire RDBUF to clear state */
+ for (i = 0; i < CH7322_RDBUF_LEN; i++) {
+ ret = regmap_read(ch7322->regmap, CH7322_RDBUF + i, &val);
+ if (ret)
+ return ret;
+ msg->msg[i] = (u8)val;
+ }
+
+ return 0;
+}
+
+static void ch7322_tx_done(struct ch7322 *ch7322)
+{
+ int ret;
+ unsigned int val;
+ u8 status, flags;
+
+ mutex_lock(&ch7322->mutex);
+ ret = regmap_read(ch7322->regmap, CH7322_WRITE, &val);
+ flags = ch7322->tx_flags;
+ mutex_unlock(&ch7322->mutex);
+
+ /*
+ * The device returns a one-bit OK status which usually means ACK but
+ * actually means NACK when sending a logical address query or a
+ * broadcast.
+ */
+ if (ret)
+ status = CEC_TX_STATUS_ERROR;
+ else if ((val & CH7322_WRITE_BOK) && (flags & CH7322_TX_FLAG_NACK))
+ status = CEC_TX_STATUS_NACK;
+ else if (val & CH7322_WRITE_BOK)
+ status = CEC_TX_STATUS_OK;
+ else if (flags & CH7322_TX_FLAG_NACK)
+ status = CEC_TX_STATUS_OK;
+ else
+ status = CEC_TX_STATUS_NACK;
+
+ if (status == CEC_TX_STATUS_NACK && (flags & CH7322_TX_FLAG_RETRY))
+ status |= CEC_TX_STATUS_MAX_RETRIES;
+
+ cec_transmit_attempt_done(ch7322->cec, status);
+}
+
+static void ch7322_rx_done(struct ch7322 *ch7322)
+{
+ struct cec_msg msg;
+ int ret;
+
+ mutex_lock(&ch7322->mutex);
+ ret = ch7322_receive_message(ch7322, &msg);
+ mutex_unlock(&ch7322->mutex);
+
+ if (ret)
+ dev_err(&ch7322->i2c->dev, "cec receive error: %d\n", ret);
+ else
+ cec_received_msg(ch7322->cec, &msg);
+}
+
+/*
+ * This device can either monitor the DDC lines to obtain the physical address
+ * or it can allow the host to program it. This driver lets the device obtain
+ * it.
+ */
+static void ch7322_phys_addr(struct ch7322 *ch7322)
+{
+ unsigned int pah, pal;
+ int ret = 0;
+
+ mutex_lock(&ch7322->mutex);
+ ret |= regmap_read(ch7322->regmap, CH7322_PARH, &pah);
+ ret |= regmap_read(ch7322->regmap, CH7322_PARL, &pal);
+ mutex_unlock(&ch7322->mutex);
+
+ if (ret)
+ dev_err(&ch7322->i2c->dev, "phys addr error\n");
+ else
+ cec_s_phys_addr(ch7322->cec, pal | (pah << 8), false);
+}
+
+static irqreturn_t ch7322_irq(int irq, void *dev)
+{
+ struct ch7322 *ch7322 = dev;
+ unsigned int data = 0;
+
+ mutex_lock(&ch7322->mutex);
+ regmap_read(ch7322->regmap, CH7322_INTDATA, &data);
+ regmap_write(ch7322->regmap, CH7322_INTDATA, data);
+ mutex_unlock(&ch7322->mutex);
+
+ if (data & CH7322_INTDATA_HPDFALL)
+ cec_phys_addr_invalidate(ch7322->cec);
+
+ if (data & CH7322_INTDATA_TXMSG)
+ ch7322_tx_done(ch7322);
+
+ if (data & CH7322_INTDATA_RXMSG)
+ ch7322_rx_done(ch7322);
+
+ if (data & CH7322_INTDATA_NEWPHA)
+ ch7322_phys_addr(ch7322);
+
+ if (data & CH7322_INTDATA_ERROR)
+ dev_dbg(&ch7322->i2c->dev, "unknown error\n");
+
+ return IRQ_HANDLED;
+}
+
+/* This device is always enabled */
+static int ch7322_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ return 0;
+}
+
+static int ch7322_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+{
+ struct ch7322 *ch7322 = cec_get_drvdata(adap);
+ int ret;
+
+ mutex_lock(&ch7322->mutex);
+ ret = regmap_update_bits(ch7322->regmap, CH7322_ADDLW,
+ CH7322_ADDLW_MASK, log_addr << 4);
+ mutex_unlock(&ch7322->mutex);
+
+ return ret;
+}
+
+static int ch7322_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct ch7322 *ch7322 = cec_get_drvdata(adap);
+ int ret;
+
+ mutex_lock(&ch7322->mutex);
+ ret = ch7322_send_message(ch7322, msg);
+ mutex_unlock(&ch7322->mutex);
+
+ return ret;
+}
+
+static const struct cec_adap_ops ch7322_cec_adap_ops = {
+ .adap_enable = ch7322_cec_adap_enable,
+ .adap_log_addr = ch7322_cec_adap_log_addr,
+ .adap_transmit = ch7322_cec_adap_transmit,
+};
+
+#if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI)
+
+struct ch7322_conn_match {
+ const char *dev_name;
+ const char *pci_name;
+ const char *port_name;
+};
+
+static struct ch7322_conn_match google_endeavour[] = {
+ { "i2c-PRP0001:00", "0000:00:02.0", "Port B" },
+ { "i2c-PRP0001:01", "0000:00:02.0", "Port C" },
+ { },
+};
+
+static const struct dmi_system_id ch7322_dmi_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Google"),
+ DMI_MATCH(DMI_BOARD_NAME, "Endeavour"),
+ },
+ .driver_data = google_endeavour,
+ },
+ { },
+};
+
+/* Make a best-effort attempt to locate a matching HDMI port */
+static int ch7322_get_port(struct i2c_client *client,
+ struct device **dev,
+ const char **port)
+{
+ const struct dmi_system_id *system;
+ const struct ch7322_conn_match *conn;
+
+ *dev = NULL;
+ *port = NULL;
+
+ system = dmi_first_match(ch7322_dmi_table);
+ if (!system)
+ return 0;
+
+ for (conn = system->driver_data; conn->dev_name; conn++) {
+ if (!strcmp(dev_name(&client->dev), conn->dev_name)) {
+ struct device *d;
+
+ d = bus_find_device_by_name(&pci_bus_type, NULL,
+ conn->pci_name);
+ if (!d)
+ return -EPROBE_DEFER;
+
+ put_device(d);
+
+ *dev = d;
+ *port = conn->port_name;
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+#else
+
+static int ch7322_get_port(struct i2c_client *client,
+ struct device **dev,
+ const char **port)
+{
+ *dev = NULL;
+ *port = NULL;
+
+ return 0;
+}
+
+#endif
+
+static int ch7322_probe(struct i2c_client *client)
+{
+ struct device *hdmi_dev;
+ const char *port_name;
+ struct ch7322 *ch7322;
+ struct cec_notifier *notifier = NULL;
+ u32 caps = CEC_CAP_DEFAULTS;
+ int ret;
+ unsigned int val;
+
+ ret = ch7322_get_port(client, &hdmi_dev, &port_name);
+ if (ret)
+ return ret;
+
+ if (hdmi_dev)
+ caps |= CEC_CAP_CONNECTOR_INFO;
+
+ ch7322 = devm_kzalloc(&client->dev, sizeof(*ch7322), GFP_KERNEL);
+ if (!ch7322)
+ return -ENOMEM;
+
+ ch7322->regmap = devm_regmap_init_i2c(client, &ch7322_regmap);
+ if (IS_ERR(ch7322->regmap))
+ return PTR_ERR(ch7322->regmap);
+
+ ret = regmap_read(ch7322->regmap, CH7322_DID, &val);
+ if (ret)
+ return ret;
+
+ if (val != CH7322_DID_CH7322)
+ return -EOPNOTSUPP;
+
+ mutex_init(&ch7322->mutex);
+ ch7322->i2c = client;
+ ch7322->tx_flags = 0;
+
+ i2c_set_clientdata(client, ch7322);
+
+ /* Disable auto mode */
+ ret = regmap_write(ch7322->regmap, CH7322_MODE, CH7322_MODE_SW);
+ if (ret)
+ goto err_mutex;
+
+ /* Enable logical address register */
+ ret = regmap_update_bits(ch7322->regmap, CH7322_CTL,
+ CH7322_CTL_SPADL, CH7322_CTL_SPADL);
+ if (ret)
+ goto err_mutex;
+
+ ch7322->cec = cec_allocate_adapter(&ch7322_cec_adap_ops, ch7322,
+ dev_name(&client->dev),
+ caps, 1);
+
+ if (IS_ERR(ch7322->cec)) {
+ ret = PTR_ERR(ch7322->cec);
+ goto err_mutex;
+ }
+
+ ch7322->cec->adap_controls_phys_addr = true;
+
+ if (hdmi_dev) {
+ notifier = cec_notifier_cec_adap_register(hdmi_dev,
+ port_name,
+ ch7322->cec);
+ if (!notifier) {
+ ret = -ENOMEM;
+ goto err_cec;
+ }
+ }
+
+ /* Configure, mask, and clear interrupt */
+ ret = regmap_write(ch7322->regmap, CH7322_CFG1, 0);
+ if (ret)
+ goto err_notifier;
+ ret = regmap_write(ch7322->regmap, CH7322_INTCTL, CH7322_INTCTL_INTPB);
+ if (ret)
+ goto err_notifier;
+ ret = regmap_write(ch7322->regmap, CH7322_INTDATA, 0xff);
+ if (ret)
+ goto err_notifier;
+
+ /* If HPD is up read physical address */
+ ret = regmap_read(ch7322->regmap, CH7322_ADDLR, &val);
+ if (ret)
+ goto err_notifier;
+ if (val & CH7322_ADDLR_HPD)
+ ch7322_phys_addr(ch7322);
+
+ ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ ch7322_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ client->name, ch7322);
+ if (ret)
+ goto err_notifier;
+
+ /* Unmask interrupt */
+ mutex_lock(&ch7322->mutex);
+ ret = regmap_write(ch7322->regmap, CH7322_INTCTL, 0xff);
+ mutex_unlock(&ch7322->mutex);
+
+ if (ret)
+ goto err_notifier;
+
+ ret = cec_register_adapter(ch7322->cec, &client->dev);
+ if (ret)
+ goto err_notifier;
+
+ dev_info(&client->dev, "device registered\n");
+
+ return 0;
+
+err_notifier:
+ if (notifier)
+ cec_notifier_cec_adap_unregister(notifier, ch7322->cec);
+err_cec:
+ cec_delete_adapter(ch7322->cec);
+err_mutex:
+ mutex_destroy(&ch7322->mutex);
+ return ret;
+}
+
+static void ch7322_remove(struct i2c_client *client)
+{
+ struct ch7322 *ch7322 = i2c_get_clientdata(client);
+
+ /* Mask interrupt */
+ mutex_lock(&ch7322->mutex);
+ regmap_write(ch7322->regmap, CH7322_INTCTL, CH7322_INTCTL_INTPB);
+ mutex_unlock(&ch7322->mutex);
+
+ cec_unregister_adapter(ch7322->cec);
+ mutex_destroy(&ch7322->mutex);
+
+ dev_info(&client->dev, "device unregistered\n");
+}
+
+static const struct of_device_id ch7322_of_match[] = {
+ { .compatible = "chrontel,ch7322", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ch7322_of_match);
+
+static struct i2c_driver ch7322_i2c_driver = {
+ .driver = {
+ .name = "ch7322",
+ .of_match_table = ch7322_of_match,
+ },
+ .probe = ch7322_probe,
+ .remove = ch7322_remove,
+};
+
+module_i2c_driver(ch7322_i2c_driver);
+
+MODULE_DESCRIPTION("Chrontel CH7322 CEC Controller Driver");
+MODULE_AUTHOR("Jeff Chase <jnchase@google.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/cec/i2c/tda9950.c b/drivers/media/cec/i2c/tda9950.c
new file mode 100644
index 000000000000..cbff851e0c85
--- /dev/null
+++ b/drivers/media/cec/i2c/tda9950.c
@@ -0,0 +1,507 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TDA9950 Consumer Electronics Control driver
+ *
+ * The NXP TDA9950 implements the HDMI Consumer Electronics Control
+ * interface. The host interface is similar to a mailbox: the data
+ * registers starting at REG_CDR0 are written to send a command to the
+ * internal CPU, and replies are read from these registers.
+ *
+ * As the data registers represent a mailbox, they must be accessed
+ * as a single I2C transaction. See the TDA9950 data sheet for details.
+ */
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_data/tda9950.h>
+#include <linux/slab.h>
+#include <drm/drm_edid.h>
+#include <media/cec.h>
+#include <media/cec-notifier.h>
+
+enum {
+ REG_CSR = 0x00,
+ CSR_BUSY = BIT(7),
+ CSR_INT = BIT(6),
+ CSR_ERR = BIT(5),
+
+ REG_CER = 0x01,
+
+ REG_CVR = 0x02,
+
+ REG_CCR = 0x03,
+ CCR_RESET = BIT(7),
+ CCR_ON = BIT(6),
+
+ REG_ACKH = 0x04,
+ REG_ACKL = 0x05,
+
+ REG_CCONR = 0x06,
+ CCONR_ENABLE_ERROR = BIT(4),
+ CCONR_RETRY_MASK = 7,
+
+ REG_CDR0 = 0x07,
+
+ CDR1_REQ = 0x00,
+ CDR1_CNF = 0x01,
+ CDR1_IND = 0x81,
+ CDR1_ERR = 0x82,
+ CDR1_IER = 0x83,
+
+ CDR2_CNF_SUCCESS = 0x00,
+ CDR2_CNF_OFF_STATE = 0x80,
+ CDR2_CNF_BAD_REQ = 0x81,
+ CDR2_CNF_CEC_ACCESS = 0x82,
+ CDR2_CNF_ARB_ERROR = 0x83,
+ CDR2_CNF_BAD_TIMING = 0x84,
+ CDR2_CNF_NACK_ADDR = 0x85,
+ CDR2_CNF_NACK_DATA = 0x86,
+};
+
+struct tda9950_priv {
+ struct i2c_client *client;
+ struct device *hdmi;
+ struct cec_adapter *adap;
+ struct tda9950_glue *glue;
+ u16 addresses;
+ struct cec_msg rx_msg;
+ struct cec_notifier *notify;
+ bool open;
+};
+
+static int tda9950_write_range(struct i2c_client *client, u8 addr, u8 *p, int cnt)
+{
+ struct i2c_msg msg;
+ u8 buf[CEC_MAX_MSG_SIZE + 3];
+ int ret;
+
+ if (WARN_ON(cnt > sizeof(buf) - 1))
+ return -EINVAL;
+
+ buf[0] = addr;
+ memcpy(buf + 1, p, cnt);
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = cnt + 1;
+ msg.buf = buf;
+
+ dev_dbg(&client->dev, "wr 0x%02x: %*ph\n", addr, cnt, p);
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ dev_err(&client->dev, "Error %d writing to cec:0x%x\n", ret, addr);
+ return ret < 0 ? ret : 0;
+}
+
+static void tda9950_write(struct i2c_client *client, u8 addr, u8 val)
+{
+ tda9950_write_range(client, addr, &val, 1);
+}
+
+static int tda9950_read_range(struct i2c_client *client, u8 addr, u8 *p, int cnt)
+{
+ struct i2c_msg msg[2];
+ int ret;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &addr;
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = cnt;
+ msg[1].buf = p;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0)
+ dev_err(&client->dev, "Error %d reading from cec:0x%x\n", ret, addr);
+
+ dev_dbg(&client->dev, "rd 0x%02x: %*ph\n", addr, cnt, p);
+
+ return ret;
+}
+
+static u8 tda9950_read(struct i2c_client *client, u8 addr)
+{
+ int ret;
+ u8 val;
+
+ ret = tda9950_read_range(client, addr, &val, 1);
+ if (ret < 0)
+ val = 0;
+
+ return val;
+}
+
+static irqreturn_t tda9950_irq(int irq, void *data)
+{
+ struct tda9950_priv *priv = data;
+ unsigned int tx_status;
+ u8 csr, cconr, buf[19];
+ u8 arb_lost_cnt, nack_cnt, err_cnt;
+
+ if (!priv->open)
+ return IRQ_NONE;
+
+ csr = tda9950_read(priv->client, REG_CSR);
+ if (!(csr & CSR_INT))
+ return IRQ_NONE;
+
+ cconr = tda9950_read(priv->client, REG_CCONR) & CCONR_RETRY_MASK;
+
+ tda9950_read_range(priv->client, REG_CDR0, buf, sizeof(buf));
+
+ /*
+ * This should never happen: the data sheet says that there will
+ * always be a valid message if the interrupt line is asserted.
+ */
+ if (buf[0] == 0) {
+ dev_warn(&priv->client->dev, "interrupt pending, but no message?\n");
+ return IRQ_NONE;
+ }
+
+ switch (buf[1]) {
+ case CDR1_CNF: /* transmit result */
+ arb_lost_cnt = nack_cnt = err_cnt = 0;
+ switch (buf[2]) {
+ case CDR2_CNF_SUCCESS:
+ tx_status = CEC_TX_STATUS_OK;
+ break;
+
+ case CDR2_CNF_ARB_ERROR:
+ tx_status = CEC_TX_STATUS_ARB_LOST;
+ arb_lost_cnt = cconr;
+ break;
+
+ case CDR2_CNF_NACK_ADDR:
+ tx_status = CEC_TX_STATUS_NACK;
+ nack_cnt = cconr;
+ break;
+
+ default: /* some other error, refer to TDA9950 docs */
+ dev_err(&priv->client->dev, "CNF reply error 0x%02x\n",
+ buf[2]);
+ tx_status = CEC_TX_STATUS_ERROR;
+ err_cnt = cconr;
+ break;
+ }
+ /* TDA9950 executes all retries for us */
+ if (tx_status != CEC_TX_STATUS_OK)
+ tx_status |= CEC_TX_STATUS_MAX_RETRIES;
+ cec_transmit_done(priv->adap, tx_status, arb_lost_cnt,
+ nack_cnt, 0, err_cnt);
+ break;
+
+ case CDR1_IND:
+ priv->rx_msg.len = buf[0] - 2;
+ if (priv->rx_msg.len > CEC_MAX_MSG_SIZE)
+ priv->rx_msg.len = CEC_MAX_MSG_SIZE;
+
+ memcpy(priv->rx_msg.msg, buf + 2, priv->rx_msg.len);
+ cec_received_msg(priv->adap, &priv->rx_msg);
+ break;
+
+ default: /* unknown */
+ dev_err(&priv->client->dev, "unknown service id 0x%02x\n",
+ buf[1]);
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int tda9950_cec_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct tda9950_priv *priv = adap->priv;
+ u8 buf[CEC_MAX_MSG_SIZE + 2];
+
+ buf[0] = 2 + msg->len;
+ buf[1] = CDR1_REQ;
+ memcpy(buf + 2, msg->msg, msg->len);
+
+ if (attempts > 5)
+ attempts = 5;
+
+ tda9950_write(priv->client, REG_CCONR, attempts);
+
+ return tda9950_write_range(priv->client, REG_CDR0, buf, 2 + msg->len);
+}
+
+static int tda9950_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+ struct tda9950_priv *priv = adap->priv;
+ u16 addresses;
+ u8 buf[2];
+
+ if (addr == CEC_LOG_ADDR_INVALID)
+ addresses = priv->addresses = 0;
+ else
+ addresses = priv->addresses |= BIT(addr);
+
+ /* TDA9950 doesn't want address 15 set */
+ addresses &= 0x7fff;
+ buf[0] = addresses >> 8;
+ buf[1] = addresses;
+
+ return tda9950_write_range(priv->client, REG_ACKH, buf, 2);
+}
+
+/*
+ * When operating as part of the TDA998x, we need additional handling
+ * to initialise and shut down the TDA9950 part of the device. These
+ * two hooks are provided to allow the TDA998x code to perform those
+ * activities.
+ */
+static int tda9950_glue_open(struct tda9950_priv *priv)
+{
+ int ret = 0;
+
+ if (priv->glue && priv->glue->open)
+ ret = priv->glue->open(priv->glue->data);
+
+ priv->open = true;
+
+ return ret;
+}
+
+static void tda9950_glue_release(struct tda9950_priv *priv)
+{
+ priv->open = false;
+
+ if (priv->glue && priv->glue->release)
+ priv->glue->release(priv->glue->data);
+}
+
+static int tda9950_open(struct tda9950_priv *priv)
+{
+ struct i2c_client *client = priv->client;
+ int ret;
+
+ ret = tda9950_glue_open(priv);
+ if (ret)
+ return ret;
+
+ /* Reset the TDA9950, and wait 250ms for it to recover */
+ tda9950_write(client, REG_CCR, CCR_RESET);
+ msleep(250);
+
+ tda9950_cec_adap_log_addr(priv->adap, CEC_LOG_ADDR_INVALID);
+
+ /* Start the command processor */
+ tda9950_write(client, REG_CCR, CCR_ON);
+
+ return 0;
+}
+
+static void tda9950_release(struct tda9950_priv *priv)
+{
+ struct i2c_client *client = priv->client;
+ int timeout = 50;
+ u8 csr;
+
+ /* Stop the command processor */
+ tda9950_write(client, REG_CCR, 0);
+
+ /* Wait up to .5s for it to signal non-busy */
+ do {
+ csr = tda9950_read(client, REG_CSR);
+ if (!(csr & CSR_BUSY) || !--timeout)
+ break;
+ msleep(10);
+ } while (1);
+
+ /* Warn the user that their IRQ may die if it's shared. */
+ if (csr & CSR_BUSY)
+ dev_warn(&client->dev, "command processor failed to stop, irq%d may die (csr=0x%02x)\n",
+ client->irq, csr);
+
+ tda9950_glue_release(priv);
+}
+
+static int tda9950_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct tda9950_priv *priv = adap->priv;
+
+ if (!enable) {
+ tda9950_release(priv);
+ return 0;
+ } else {
+ return tda9950_open(priv);
+ }
+}
+
+static const struct cec_adap_ops tda9950_cec_ops = {
+ .adap_enable = tda9950_cec_adap_enable,
+ .adap_log_addr = tda9950_cec_adap_log_addr,
+ .adap_transmit = tda9950_cec_transmit,
+};
+
+/*
+ * When operating as part of the TDA998x, we need to claim additional
+ * resources. These two hooks permit the management of those resources.
+ */
+static void tda9950_devm_glue_exit(void *data)
+{
+ struct tda9950_glue *glue = data;
+
+ if (glue && glue->exit)
+ glue->exit(glue->data);
+}
+
+static int tda9950_devm_glue_init(struct device *dev, struct tda9950_glue *glue)
+{
+ int ret;
+
+ if (glue && glue->init) {
+ ret = glue->init(glue->data);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_add_action(dev, tda9950_devm_glue_exit, glue);
+ if (ret)
+ tda9950_devm_glue_exit(glue);
+
+ return ret;
+}
+
+static void tda9950_cec_del(void *data)
+{
+ struct tda9950_priv *priv = data;
+
+ cec_delete_adapter(priv->adap);
+}
+
+static int tda9950_probe(struct i2c_client *client)
+{
+ struct tda9950_glue *glue = client->dev.platform_data;
+ struct device *dev = &client->dev;
+ struct tda9950_priv *priv;
+ unsigned long irqflags;
+ int ret;
+ u8 cvr;
+
+ /*
+ * We must have I2C functionality: our multi-byte accesses
+ * must be performed as a single contiguous transaction.
+ */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev,
+ "adapter does not support I2C functionality\n");
+ return -ENXIO;
+ }
+
+ /* We must have an interrupt to be functional. */
+ if (client->irq <= 0) {
+ dev_err(&client->dev, "driver requires an interrupt\n");
+ return -ENXIO;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->client = client;
+ priv->glue = glue;
+
+ i2c_set_clientdata(client, priv);
+
+ /*
+ * If we're part of a TDA998x, we want the class devices to be
+ * associated with the HDMI Tx so we have a tight relationship
+ * between the HDMI interface and the CEC interface.
+ */
+ priv->hdmi = dev;
+ if (glue && glue->parent)
+ priv->hdmi = glue->parent;
+
+ priv->adap = cec_allocate_adapter(&tda9950_cec_ops, priv, "tda9950",
+ CEC_CAP_DEFAULTS |
+ CEC_CAP_CONNECTOR_INFO,
+ CEC_MAX_LOG_ADDRS);
+ if (IS_ERR(priv->adap))
+ return PTR_ERR(priv->adap);
+
+ ret = devm_add_action(dev, tda9950_cec_del, priv);
+ if (ret) {
+ cec_delete_adapter(priv->adap);
+ return ret;
+ }
+
+ ret = tda9950_devm_glue_init(dev, glue);
+ if (ret)
+ return ret;
+
+ ret = tda9950_glue_open(priv);
+ if (ret)
+ return ret;
+
+ cvr = tda9950_read(client, REG_CVR);
+
+ dev_info(&client->dev,
+ "TDA9950 CEC interface, hardware version %u.%u\n",
+ cvr >> 4, cvr & 15);
+
+ tda9950_glue_release(priv);
+
+ irqflags = IRQF_TRIGGER_FALLING;
+ if (glue)
+ irqflags = glue->irq_flags;
+
+ ret = devm_request_threaded_irq(dev, client->irq, NULL, tda9950_irq,
+ irqflags | IRQF_SHARED | IRQF_ONESHOT,
+ dev_name(&client->dev), priv);
+ if (ret < 0)
+ return ret;
+
+ priv->notify = cec_notifier_cec_adap_register(priv->hdmi, NULL,
+ priv->adap);
+ if (!priv->notify)
+ return -ENOMEM;
+
+ ret = cec_register_adapter(priv->adap, priv->hdmi);
+ if (ret < 0) {
+ cec_notifier_cec_adap_unregister(priv->notify, priv->adap);
+ return ret;
+ }
+
+ /*
+ * CEC documentation says we must not call cec_delete_adapter
+ * after a successful call to cec_register_adapter().
+ */
+ devm_remove_action(dev, tda9950_cec_del, priv);
+
+ return 0;
+}
+
+static void tda9950_remove(struct i2c_client *client)
+{
+ struct tda9950_priv *priv = i2c_get_clientdata(client);
+
+ cec_notifier_cec_adap_unregister(priv->notify, priv->adap);
+ cec_unregister_adapter(priv->adap);
+}
+
+static struct i2c_device_id tda9950_ids[] = {
+ { "tda9950" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tda9950_ids);
+
+static struct i2c_driver tda9950_driver = {
+ .probe = tda9950_probe,
+ .remove = tda9950_remove,
+ .driver = {
+ .name = "tda9950",
+ },
+ .id_table = tda9950_ids,
+};
+
+module_i2c_driver(tda9950_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@armlinux.org.uk>");
+MODULE_DESCRIPTION("TDA9950/TDA998x Consumer Electronics Control Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/cec/platform/Kconfig b/drivers/media/cec/platform/Kconfig
new file mode 100644
index 000000000000..e40413609f53
--- /dev/null
+++ b/drivers/media/cec/platform/Kconfig
@@ -0,0 +1,120 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Platform drivers
+
+config CEC_CROS_EC
+ tristate "ChromeOS EC CEC driver"
+ depends on CROS_EC
+ select CEC_CORE
+ select CEC_NOTIFIER
+ select CROS_EC_PROTO
+ help
+ If you say yes here you will get support for the
+ ChromeOS Embedded Controller's CEC.
+ The CEC bus is present in the HDMI connector and enables communication
+ between compatible devices.
+
+config CEC_MESON_AO
+ tristate "Amlogic Meson AO CEC driver"
+ depends on ARCH_MESON || COMPILE_TEST
+ select CEC_CORE
+ select CEC_NOTIFIER
+ help
+ This is a driver for Amlogic Meson SoCs AO CEC interface. It uses the
+ generic CEC framework interface.
+ CEC bus is present in the HDMI connector and enables communication
+
+config CEC_MESON_G12A_AO
+ tristate "Amlogic Meson G12A AO CEC driver"
+ depends on ARCH_MESON || COMPILE_TEST
+ depends on COMMON_CLK && OF
+ select REGMAP
+ select REGMAP_MMIO
+ select CEC_CORE
+ select CEC_NOTIFIER
+ help
+ This is a driver for Amlogic Meson G12A SoCs AO CEC interface.
+ This driver if for the new AO-CEC module found in G12A SoCs,
+ usually named AO_CEC_B in documentation.
+ It uses the generic CEC framework interface.
+ CEC bus is present in the HDMI connector and enables communication
+ between compatible devices.
+
+config CEC_GPIO
+ tristate "Generic GPIO-based CEC driver"
+ depends on PREEMPTION || COMPILE_TEST
+ select CEC_CORE
+ select CEC_PIN
+ select CEC_NOTIFIER
+ select GPIOLIB
+ help
+ This is a generic GPIO-based CEC driver.
+ The CEC bus is present in the HDMI connector and enables communication
+ between compatible devices.
+
+config CEC_SAMSUNG_S5P
+ tristate "Samsung S5P CEC driver"
+ depends on ARCH_EXYNOS || COMPILE_TEST
+ select CEC_CORE
+ select CEC_NOTIFIER
+ help
+ This is a driver for Samsung S5P HDMI CEC interface. It uses the
+ generic CEC framework interface.
+ CEC bus is present in the HDMI connector and enables communication
+ between compatible devices.
+
+config CEC_STI
+ tristate "STMicroelectronics STiH4xx HDMI CEC driver"
+ depends on ARCH_STI || COMPILE_TEST
+ select CEC_CORE
+ select CEC_NOTIFIER
+ help
+ This is a driver for STIH4xx HDMI CEC interface. It uses the
+ generic CEC framework interface.
+ CEC bus is present in the HDMI connector and enables communication
+ between compatible devices.
+
+config CEC_STM32
+ tristate "STMicroelectronics STM32 HDMI CEC driver"
+ depends on ARCH_STM32 || COMPILE_TEST
+ select REGMAP
+ select REGMAP_MMIO
+ select CEC_CORE
+ help
+ This is a driver for STM32 interface. It uses the
+ generic CEC framework interface.
+ CEC bus is present in the HDMI connector and enables communication
+ between compatible devices.
+
+config CEC_TEGRA
+ tristate "Tegra HDMI CEC driver"
+ depends on ARCH_TEGRA || COMPILE_TEST
+ select CEC_CORE
+ select CEC_NOTIFIER
+ help
+ This is a driver for the Tegra HDMI CEC interface. It uses the
+ generic CEC framework interface.
+ The CEC bus is present in the HDMI connector and enables communication
+ between compatible devices.
+
+config CEC_SECO
+ tristate "SECO Boards HDMI CEC driver"
+ depends on X86 || (COMPILE_TEST && HAS_IOPORT)
+ depends on PCI && DMI
+ select CEC_CORE
+ select CEC_NOTIFIER
+ help
+ This is a driver for SECO Boards integrated CEC interface.
+ Selecting it will enable support for this device.
+ CEC bus is present in the HDMI connector and enables communication
+ between compatible devices.
+
+config CEC_SECO_RC
+ bool "SECO Boards IR RC5 support"
+ depends on CEC_SECO
+ depends on RC_CORE=y || RC_CORE = CEC_SECO
+ help
+ If you say yes here you will get support for the
+ SECO Boards Consumer-IR in seco-cec driver.
+ The embedded controller supports RC5 protocol only, default mapping
+ is set to rc-hauppauge.
diff --git a/drivers/media/cec/platform/Makefile b/drivers/media/cec/platform/Makefile
new file mode 100644
index 000000000000..a51e98ab4958
--- /dev/null
+++ b/drivers/media/cec/platform/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the CEC platform device drivers.
+#
+
+# Please keep it in alphabetic order
+obj-$(CONFIG_CEC_CROS_EC) += cros-ec/
+obj-$(CONFIG_CEC_GPIO) += cec-gpio/
+obj-y += meson/
+obj-$(CONFIG_CEC_SAMSUNG_S5P) += s5p/
+obj-$(CONFIG_CEC_SECO) += seco/
+obj-$(CONFIG_CEC_STI) += sti/
+obj-$(CONFIG_CEC_STM32) += stm32/
+obj-$(CONFIG_CEC_TEGRA) += tegra/
+
diff --git a/drivers/media/cec/platform/cec-gpio/Makefile b/drivers/media/cec/platform/cec-gpio/Makefile
new file mode 100644
index 000000000000..a40c621dbd24
--- /dev/null
+++ b/drivers/media/cec/platform/cec-gpio/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_CEC_GPIO) += cec-gpio.o
diff --git a/drivers/media/cec/platform/cec-gpio/cec-gpio.c b/drivers/media/cec/platform/cec-gpio/cec-gpio.c
new file mode 100644
index 000000000000..842555ed42c7
--- /dev/null
+++ b/drivers/media/cec/platform/cec-gpio/cec-gpio.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <media/cec-notifier.h>
+#include <media/cec-pin.h>
+
+struct cec_gpio {
+ struct cec_adapter *adap;
+ struct cec_notifier *notifier;
+ struct device *dev;
+
+ struct gpio_desc *cec_gpio;
+ int cec_irq;
+ bool cec_is_low;
+
+ struct gpio_desc *hpd_gpio;
+ int hpd_irq;
+ bool hpd_is_high;
+ ktime_t hpd_ts;
+
+ struct gpio_desc *v5_gpio;
+ int v5_irq;
+ bool v5_is_high;
+ ktime_t v5_ts;
+};
+
+static int cec_gpio_read(struct cec_adapter *adap)
+{
+ struct cec_gpio *cec = cec_get_drvdata(adap);
+
+ if (cec->cec_is_low)
+ return 0;
+ return gpiod_get_value(cec->cec_gpio);
+}
+
+static void cec_gpio_high(struct cec_adapter *adap)
+{
+ struct cec_gpio *cec = cec_get_drvdata(adap);
+
+ if (!cec->cec_is_low)
+ return;
+ cec->cec_is_low = false;
+ gpiod_set_value(cec->cec_gpio, 1);
+}
+
+static void cec_gpio_low(struct cec_adapter *adap)
+{
+ struct cec_gpio *cec = cec_get_drvdata(adap);
+
+ if (cec->cec_is_low)
+ return;
+ cec->cec_is_low = true;
+ gpiod_set_value(cec->cec_gpio, 0);
+}
+
+static irqreturn_t cec_gpio_5v_irq_handler_thread(int irq, void *priv)
+{
+ struct cec_gpio *cec = priv;
+ int val = gpiod_get_value_cansleep(cec->v5_gpio);
+ bool is_high = val > 0;
+
+ if (val < 0 || is_high == cec->v5_is_high)
+ return IRQ_HANDLED;
+
+ cec->v5_is_high = is_high;
+ cec_queue_pin_5v_event(cec->adap, cec->v5_is_high, cec->v5_ts);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cec_gpio_5v_irq_handler(int irq, void *priv)
+{
+ struct cec_gpio *cec = priv;
+
+ cec->v5_ts = ktime_get();
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t cec_gpio_hpd_irq_handler_thread(int irq, void *priv)
+{
+ struct cec_gpio *cec = priv;
+ int val = gpiod_get_value_cansleep(cec->hpd_gpio);
+ bool is_high = val > 0;
+
+ if (val < 0 || is_high == cec->hpd_is_high)
+ return IRQ_HANDLED;
+
+ cec->hpd_is_high = is_high;
+ cec_queue_pin_hpd_event(cec->adap, cec->hpd_is_high, cec->hpd_ts);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cec_gpio_hpd_irq_handler(int irq, void *priv)
+{
+ struct cec_gpio *cec = priv;
+
+ cec->hpd_ts = ktime_get();
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t cec_gpio_cec_irq_handler(int irq, void *priv)
+{
+ struct cec_gpio *cec = priv;
+ int val = gpiod_get_value(cec->cec_gpio);
+
+ if (val >= 0)
+ cec_pin_changed(cec->adap, val > 0);
+ return IRQ_HANDLED;
+}
+
+static bool cec_gpio_cec_enable_irq(struct cec_adapter *adap)
+{
+ struct cec_gpio *cec = cec_get_drvdata(adap);
+
+ enable_irq(cec->cec_irq);
+ return true;
+}
+
+static void cec_gpio_cec_disable_irq(struct cec_adapter *adap)
+{
+ struct cec_gpio *cec = cec_get_drvdata(adap);
+
+ disable_irq(cec->cec_irq);
+}
+
+static void cec_gpio_status(struct cec_adapter *adap, struct seq_file *file)
+{
+ struct cec_gpio *cec = cec_get_drvdata(adap);
+
+ seq_printf(file, "mode: %s\n", cec->cec_is_low ? "low-drive" : "read");
+ seq_printf(file, "using irq: %d\n", cec->cec_irq);
+ if (cec->hpd_gpio)
+ seq_printf(file, "hpd: %s\n",
+ cec->hpd_is_high ? "high" : "low");
+ if (cec->v5_gpio)
+ seq_printf(file, "5V: %s\n",
+ cec->v5_is_high ? "high" : "low");
+}
+
+static int cec_gpio_read_hpd(struct cec_adapter *adap)
+{
+ struct cec_gpio *cec = cec_get_drvdata(adap);
+
+ if (!cec->hpd_gpio)
+ return -ENOTTY;
+ return gpiod_get_value_cansleep(cec->hpd_gpio);
+}
+
+static int cec_gpio_read_5v(struct cec_adapter *adap)
+{
+ struct cec_gpio *cec = cec_get_drvdata(adap);
+
+ if (!cec->v5_gpio)
+ return -ENOTTY;
+ return gpiod_get_value_cansleep(cec->v5_gpio);
+}
+
+static const struct cec_pin_ops cec_gpio_pin_ops = {
+ .read = cec_gpio_read,
+ .low = cec_gpio_low,
+ .high = cec_gpio_high,
+ .enable_irq = cec_gpio_cec_enable_irq,
+ .disable_irq = cec_gpio_cec_disable_irq,
+ .status = cec_gpio_status,
+ .read_hpd = cec_gpio_read_hpd,
+ .read_5v = cec_gpio_read_5v,
+};
+
+static int cec_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *hdmi_dev;
+ struct cec_gpio *cec;
+ u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN;
+ int ret;
+
+ hdmi_dev = cec_notifier_parse_hdmi_phandle(dev);
+ if (PTR_ERR(hdmi_dev) == -EPROBE_DEFER)
+ return PTR_ERR(hdmi_dev);
+ if (IS_ERR(hdmi_dev))
+ caps |= CEC_CAP_PHYS_ADDR;
+
+ cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL);
+ if (!cec)
+ return -ENOMEM;
+
+ cec->dev = dev;
+
+ cec->cec_gpio = devm_gpiod_get(dev, "cec", GPIOD_OUT_HIGH_OPEN_DRAIN);
+ if (IS_ERR(cec->cec_gpio))
+ return PTR_ERR(cec->cec_gpio);
+ cec->cec_irq = gpiod_to_irq(cec->cec_gpio);
+
+ cec->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
+ if (IS_ERR(cec->hpd_gpio))
+ return PTR_ERR(cec->hpd_gpio);
+
+ cec->v5_gpio = devm_gpiod_get_optional(dev, "v5", GPIOD_IN);
+ if (IS_ERR(cec->v5_gpio))
+ return PTR_ERR(cec->v5_gpio);
+
+ cec->adap = cec_pin_allocate_adapter(&cec_gpio_pin_ops,
+ cec, pdev->name, caps);
+ if (IS_ERR(cec->adap))
+ return PTR_ERR(cec->adap);
+
+ ret = devm_request_irq(dev, cec->cec_irq, cec_gpio_cec_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN,
+ cec->adap->name, cec);
+ if (ret)
+ goto del_adap;
+
+ if (cec->hpd_gpio) {
+ cec->hpd_irq = gpiod_to_irq(cec->hpd_gpio);
+ ret = devm_request_threaded_irq(dev, cec->hpd_irq,
+ cec_gpio_hpd_irq_handler,
+ cec_gpio_hpd_irq_handler_thread,
+ IRQF_ONESHOT |
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "hpd-gpio", cec);
+ if (ret)
+ goto del_adap;
+ }
+
+ if (cec->v5_gpio) {
+ cec->v5_irq = gpiod_to_irq(cec->v5_gpio);
+ ret = devm_request_threaded_irq(dev, cec->v5_irq,
+ cec_gpio_5v_irq_handler,
+ cec_gpio_5v_irq_handler_thread,
+ IRQF_ONESHOT |
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "v5-gpio", cec);
+ if (ret)
+ goto del_adap;
+ }
+
+ if (!IS_ERR(hdmi_dev)) {
+ cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL,
+ cec->adap);
+ if (!cec->notifier) {
+ ret = -ENOMEM;
+ goto del_adap;
+ }
+ }
+
+ ret = cec_register_adapter(cec->adap, &pdev->dev);
+ if (ret)
+ goto unreg_notifier;
+
+ platform_set_drvdata(pdev, cec);
+ return 0;
+
+unreg_notifier:
+ cec_notifier_cec_adap_unregister(cec->notifier, cec->adap);
+del_adap:
+ cec_delete_adapter(cec->adap);
+ return ret;
+}
+
+static void cec_gpio_remove(struct platform_device *pdev)
+{
+ struct cec_gpio *cec = platform_get_drvdata(pdev);
+
+ cec_notifier_cec_adap_unregister(cec->notifier, cec->adap);
+ cec_unregister_adapter(cec->adap);
+}
+
+static const struct of_device_id cec_gpio_match[] = {
+ {
+ .compatible = "cec-gpio",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cec_gpio_match);
+
+static struct platform_driver cec_gpio_pdrv = {
+ .probe = cec_gpio_probe,
+ .remove = cec_gpio_remove,
+ .driver = {
+ .name = "cec-gpio",
+ .of_match_table = cec_gpio_match,
+ },
+};
+
+module_platform_driver(cec_gpio_pdrv);
+
+MODULE_AUTHOR("Hans Verkuil <hverkuil@kernel.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CEC GPIO driver");
diff --git a/drivers/media/cec/platform/cros-ec/Makefile b/drivers/media/cec/platform/cros-ec/Makefile
new file mode 100644
index 000000000000..d7e3511078ef
--- /dev/null
+++ b/drivers/media/cec/platform/cros-ec/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_CEC_CROS_EC) += cros-ec-cec.o
diff --git a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c
new file mode 100644
index 000000000000..419b9a7abcce
--- /dev/null
+++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c
@@ -0,0 +1,602 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * CEC driver for ChromeOS Embedded Controller
+ *
+ * Copyright (c) 2018 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
+#include <linux/cec.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
+#include <media/cec.h>
+#include <media/cec-notifier.h>
+
+#define DRV_NAME "cros-ec-cec"
+
+/**
+ * struct cros_ec_cec_port - Driver data for a single EC CEC port
+ *
+ * @port_num: port number
+ * @adap: CEC adapter
+ * @notify: CEC notifier pointer
+ * @rx_msg: storage for a received message
+ * @cros_ec_cec: pointer to the parent struct
+ */
+struct cros_ec_cec_port {
+ int port_num;
+ struct cec_adapter *adap;
+ struct cec_notifier *notify;
+ struct cec_msg rx_msg;
+ struct cros_ec_cec *cros_ec_cec;
+};
+
+/**
+ * struct cros_ec_cec - Driver data for EC CEC
+ *
+ * @cros_ec: Pointer to EC device
+ * @notifier: Notifier info for responding to EC events
+ * @write_cmd_version: Highest supported version of EC_CMD_CEC_WRITE_MSG.
+ * @num_ports: Number of CEC ports
+ * @ports: Array of ports
+ */
+struct cros_ec_cec {
+ struct cros_ec_device *cros_ec;
+ struct notifier_block notifier;
+ int write_cmd_version;
+ int num_ports;
+ struct cros_ec_cec_port *ports[EC_CEC_MAX_PORTS];
+};
+
+static void cros_ec_cec_received_message(struct cros_ec_cec_port *port,
+ uint8_t *msg, uint8_t len)
+{
+ if (len > CEC_MAX_MSG_SIZE)
+ len = CEC_MAX_MSG_SIZE;
+
+ port->rx_msg.len = len;
+ memcpy(port->rx_msg.msg, msg, len);
+
+ cec_received_msg(port->adap, &port->rx_msg);
+}
+
+static void handle_cec_message(struct cros_ec_cec *cros_ec_cec)
+{
+ struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
+ uint8_t *cec_message = cros_ec->event_data.data.cec_message;
+ unsigned int len = cros_ec->event_size;
+ struct cros_ec_cec_port *port;
+ /*
+ * There are two ways of receiving CEC messages:
+ * 1. Old EC firmware which only supports one port sends the data in a
+ * cec_message MKBP event.
+ * 2. New EC firmware which supports multiple ports uses
+ * EC_MKBP_CEC_HAVE_DATA to notify that data is ready and
+ * EC_CMD_CEC_READ_MSG to read it.
+ * Check that the EC only has one CEC port, and then we can assume the
+ * message is from port 0.
+ */
+ if (cros_ec_cec->num_ports != 1) {
+ dev_err(cros_ec->dev,
+ "received cec_message on device with %d ports\n",
+ cros_ec_cec->num_ports);
+ return;
+ }
+ port = cros_ec_cec->ports[0];
+
+ cros_ec_cec_received_message(port, cec_message, len);
+}
+
+static void cros_ec_cec_read_message(struct cros_ec_cec_port *port)
+{
+ struct cros_ec_device *cros_ec = port->cros_ec_cec->cros_ec;
+ struct ec_params_cec_read params = {
+ .port = port->port_num,
+ };
+ struct ec_response_cec_read response;
+ int ret;
+
+ ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_READ_MSG, &params,
+ sizeof(params), &response, sizeof(response));
+ if (ret < 0) {
+ dev_err(cros_ec->dev,
+ "error reading CEC message on EC: %d\n", ret);
+ return;
+ }
+
+ cros_ec_cec_received_message(port, response.msg, response.msg_len);
+}
+
+static void handle_cec_event(struct cros_ec_cec *cros_ec_cec)
+{
+ struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
+ uint32_t cec_events = cros_ec->event_data.data.cec_events;
+ uint32_t port_num = EC_MKBP_EVENT_CEC_GET_PORT(cec_events);
+ uint32_t events = EC_MKBP_EVENT_CEC_GET_EVENTS(cec_events);
+ struct cros_ec_cec_port *port;
+
+ if (port_num >= cros_ec_cec->num_ports) {
+ dev_err(cros_ec->dev,
+ "received CEC event for invalid port %d\n", port_num);
+ return;
+ }
+ port = cros_ec_cec->ports[port_num];
+
+ if (events & EC_MKBP_CEC_SEND_OK)
+ cec_transmit_attempt_done(port->adap, CEC_TX_STATUS_OK);
+
+ /* FW takes care of all retries, tell core to avoid more retries */
+ if (events & EC_MKBP_CEC_SEND_FAILED)
+ cec_transmit_attempt_done(port->adap,
+ CEC_TX_STATUS_MAX_RETRIES |
+ CEC_TX_STATUS_NACK);
+
+ if (events & EC_MKBP_CEC_HAVE_DATA)
+ cros_ec_cec_read_message(port);
+}
+
+static int cros_ec_cec_event(struct notifier_block *nb,
+ unsigned long queued_during_suspend,
+ void *_notify)
+{
+ struct cros_ec_cec *cros_ec_cec;
+ struct cros_ec_device *cros_ec;
+
+ cros_ec_cec = container_of(nb, struct cros_ec_cec, notifier);
+ cros_ec = cros_ec_cec->cros_ec;
+
+ if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_EVENT) {
+ handle_cec_event(cros_ec_cec);
+ return NOTIFY_OK;
+ }
+
+ if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_MESSAGE) {
+ handle_cec_message(cros_ec_cec);
+ return NOTIFY_OK;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int cros_ec_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct cros_ec_cec_port *port = adap->priv;
+ struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
+ struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
+ struct ec_params_cec_set params = {
+ .cmd = CEC_CMD_LOGICAL_ADDRESS,
+ .port = port->port_num,
+ .val = logical_addr,
+ };
+ int ret;
+
+ ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_SET, &params, sizeof(params),
+ NULL, 0);
+ if (ret < 0) {
+ dev_err(cros_ec->dev,
+ "error setting CEC logical address on EC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cros_ec_cec_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *cec_msg)
+{
+ struct cros_ec_cec_port *port = adap->priv;
+ struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
+ struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
+ struct ec_params_cec_write params;
+ struct ec_params_cec_write_v1 params_v1;
+ int ret;
+
+ if (cros_ec_cec->write_cmd_version == 0) {
+ memcpy(params.msg, cec_msg->msg, cec_msg->len);
+ ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_WRITE_MSG, &params,
+ cec_msg->len, NULL, 0);
+ } else {
+ params_v1.port = port->port_num;
+ params_v1.msg_len = cec_msg->len;
+ memcpy(params_v1.msg, cec_msg->msg, cec_msg->len);
+ ret = cros_ec_cmd(cros_ec, cros_ec_cec->write_cmd_version,
+ EC_CMD_CEC_WRITE_MSG, &params_v1,
+ sizeof(params_v1), NULL, 0);
+ }
+
+ if (ret < 0) {
+ dev_err(cros_ec->dev,
+ "error writing CEC msg on EC: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cros_ec_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct cros_ec_cec_port *port = adap->priv;
+ struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
+ struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
+ struct ec_params_cec_set params = {
+ .cmd = CEC_CMD_ENABLE,
+ .port = port->port_num,
+ .val = enable,
+ };
+ int ret;
+
+ ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_SET, &params, sizeof(params),
+ NULL, 0);
+ if (ret < 0) {
+ dev_err(cros_ec->dev,
+ "error %sabling CEC on EC: %d\n",
+ (enable ? "en" : "dis"), ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct cec_adap_ops cros_ec_cec_ops = {
+ .adap_enable = cros_ec_cec_adap_enable,
+ .adap_log_addr = cros_ec_cec_set_log_addr,
+ .adap_transmit = cros_ec_cec_transmit,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int cros_ec_cec_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(cros_ec_cec->cros_ec->irq);
+
+ return 0;
+}
+
+static int cros_ec_cec_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(cros_ec_cec->cros_ec->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(cros_ec_cec_pm_ops,
+ cros_ec_cec_suspend, cros_ec_cec_resume);
+
+#if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI)
+
+/*
+ * Specify the DRM device name handling the HDMI output and the HDMI connector
+ * corresponding to each CEC port. The order of connectors must match the order
+ * in the EC (first connector is EC port 0, ...), and the number of connectors
+ * must match the number of ports in the EC (which can be queried using the
+ * EC_CMD_CEC_PORT_COUNT host command).
+ */
+
+struct cec_dmi_match {
+ const char *sys_vendor;
+ const char *product_name;
+ const char *devname;
+ const char *const *conns;
+};
+
+static const char *const port_b_conns[] = { "Port B", NULL };
+static const char *const port_db_conns[] = { "Port D", "Port B", NULL };
+static const char *const port_ba_conns[] = { "Port B", "Port A", NULL };
+static const char *const port_ab_conns[] = { "Port A", "Port B", NULL };
+static const char *const port_d_conns[] = { "Port D", NULL };
+
+static const struct cec_dmi_match cec_dmi_match_table[] = {
+ /* Google Fizz */
+ { "Google", "Fizz", "0000:00:02.0", port_b_conns },
+ /* Google Brask */
+ { "Google", "Brask", "0000:00:02.0", port_b_conns },
+ /* Google Moli */
+ { "Google", "Moli", "0000:00:02.0", port_b_conns },
+ /* Google Kinox */
+ { "Google", "Kinox", "0000:00:02.0", port_b_conns },
+ /* Google Kuldax */
+ { "Google", "Kuldax", "0000:00:02.0", port_b_conns },
+ /* Google Aurash */
+ { "Google", "Aurash", "0000:00:02.0", port_b_conns },
+ /* Google Gladios */
+ { "Google", "Gladios", "0000:00:02.0", port_b_conns },
+ /* Google Lisbon */
+ { "Google", "Lisbon", "0000:00:02.0", port_b_conns },
+ /* Google Dibbi */
+ { "Google", "Dibbi", "0000:00:02.0", port_db_conns },
+ /* Google Constitution */
+ { "Google", "Constitution", "0000:00:02.0", port_ba_conns },
+ /* Google Boxy */
+ { "Google", "Boxy", "0000:00:02.0", port_d_conns },
+ /* Google Taranza */
+ { "Google", "Taranza", "0000:00:02.0", port_db_conns },
+ /* Google Dexi */
+ { "Google", "Dexi", "0000:00:02.0", port_db_conns },
+ /* Google Dita */
+ { "Google", "Dita", "0000:00:02.0", port_db_conns },
+ /* Google Dirks */
+ { "Google", "Dirks", "0000:00:02.0", port_ab_conns },
+ /* Google Moxie */
+ { "Google", "Moxie", "0000:00:02.0", port_b_conns },
+};
+
+static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
+ const char * const **conns)
+{
+ int i;
+
+ for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) {
+ const struct cec_dmi_match *m = &cec_dmi_match_table[i];
+
+ if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
+ dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
+ struct device *d;
+
+ /* Find the device, bail out if not yet registered */
+ d = bus_find_device_by_name(&pci_bus_type, NULL,
+ m->devname);
+ if (!d)
+ return ERR_PTR(-EPROBE_DEFER);
+ put_device(d);
+ *conns = m->conns;
+ return d;
+ }
+ }
+
+ /* Hardware support must be added in the cec_dmi_match_table */
+ dev_warn(dev, "CEC notifier not configured for this hardware\n");
+
+ return ERR_PTR(-ENODEV);
+}
+
+#else
+
+static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
+ const char * const **conns)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+#endif
+
+static int cros_ec_cec_get_num_ports(struct cros_ec_cec *cros_ec_cec)
+{
+ struct ec_response_cec_port_count response;
+ int ret;
+
+ ret = cros_ec_cmd(cros_ec_cec->cros_ec, 0, EC_CMD_CEC_PORT_COUNT, NULL,
+ 0, &response, sizeof(response));
+ if (ret < 0) {
+ /*
+ * Old EC firmware only supports one port and does not support
+ * the port count command, so fall back to assuming one port.
+ */
+ cros_ec_cec->num_ports = 1;
+ return 0;
+ }
+
+ if (response.port_count == 0) {
+ dev_err(cros_ec_cec->cros_ec->dev,
+ "EC reports 0 CEC ports\n");
+ return -ENODEV;
+ }
+
+ if (response.port_count > EC_CEC_MAX_PORTS) {
+ dev_err(cros_ec_cec->cros_ec->dev,
+ "EC reports too many ports: %d\n", response.port_count);
+ return -EINVAL;
+ }
+
+ cros_ec_cec->num_ports = response.port_count;
+ return 0;
+}
+
+static int cros_ec_cec_get_write_cmd_version(struct cros_ec_cec *cros_ec_cec)
+{
+ struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
+ struct ec_params_get_cmd_versions_v1 params = {
+ .cmd = EC_CMD_CEC_WRITE_MSG,
+ };
+ struct ec_response_get_cmd_versions response;
+ int ret;
+
+ ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GET_CMD_VERSIONS, &params,
+ sizeof(params), &response, sizeof(response));
+ if (ret < 0) {
+ dev_err(cros_ec->dev,
+ "error getting CEC write command version: %d\n", ret);
+ return ret;
+ }
+
+ if (response.version_mask & EC_VER_MASK(1)) {
+ cros_ec_cec->write_cmd_version = 1;
+ } else {
+ if (cros_ec_cec->num_ports != 1) {
+ dev_err(cros_ec->dev,
+ "v0 write command only supports 1 port, %d reported\n",
+ cros_ec_cec->num_ports);
+ return -EINVAL;
+ }
+ cros_ec_cec->write_cmd_version = 0;
+ }
+
+ return 0;
+}
+
+static int cros_ec_cec_init_port(struct device *dev,
+ struct cros_ec_cec *cros_ec_cec,
+ int port_num, struct device *hdmi_dev,
+ const char * const *conns)
+{
+ struct cros_ec_cec_port *port;
+ int ret;
+
+ port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->cros_ec_cec = cros_ec_cec;
+ port->port_num = port_num;
+
+ port->adap = cec_allocate_adapter(&cros_ec_cec_ops, port, DRV_NAME,
+ CEC_CAP_DEFAULTS |
+ CEC_CAP_CONNECTOR_INFO, 1);
+ if (IS_ERR(port->adap))
+ return PTR_ERR(port->adap);
+
+ if (!conns[port_num]) {
+ dev_err(dev, "no conn for port %d\n", port_num);
+ ret = -ENODEV;
+ goto out_probe_adapter;
+ }
+
+ port->notify = cec_notifier_cec_adap_register(hdmi_dev, conns[port_num],
+ port->adap);
+ if (!port->notify) {
+ ret = -ENOMEM;
+ goto out_probe_adapter;
+ }
+
+ ret = cec_register_adapter(port->adap, dev);
+ if (ret < 0)
+ goto out_probe_notify;
+
+ cros_ec_cec->ports[port_num] = port;
+
+ return 0;
+
+out_probe_notify:
+ cec_notifier_cec_adap_unregister(port->notify, port->adap);
+out_probe_adapter:
+ cec_delete_adapter(port->adap);
+ return ret;
+}
+
+static int cros_ec_cec_probe(struct platform_device *pdev)
+{
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
+ struct cros_ec_device *cros_ec = ec_dev->ec_dev;
+ struct cros_ec_cec *cros_ec_cec;
+ struct cros_ec_cec_port *port;
+ struct device *hdmi_dev;
+ const char * const *conns = NULL;
+ int ret;
+
+ hdmi_dev = cros_ec_cec_find_hdmi_dev(&pdev->dev, &conns);
+ if (IS_ERR(hdmi_dev))
+ return PTR_ERR(hdmi_dev);
+
+ cros_ec_cec = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_cec),
+ GFP_KERNEL);
+ if (!cros_ec_cec)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, cros_ec_cec);
+ cros_ec_cec->cros_ec = cros_ec;
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ ret = cros_ec_cec_get_num_ports(cros_ec_cec);
+ if (ret)
+ return ret;
+
+ ret = cros_ec_cec_get_write_cmd_version(cros_ec_cec);
+ if (ret)
+ return ret;
+
+ for (int i = 0; i < cros_ec_cec->num_ports; i++) {
+ ret = cros_ec_cec_init_port(&pdev->dev, cros_ec_cec, i,
+ hdmi_dev, conns);
+ if (ret)
+ goto unregister_ports;
+ }
+
+ /* Get CEC events from the EC. */
+ cros_ec_cec->notifier.notifier_call = cros_ec_cec_event;
+ ret = blocking_notifier_chain_register(&cros_ec->event_notifier,
+ &cros_ec_cec->notifier);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register notifier\n");
+ goto unregister_ports;
+ }
+
+ return 0;
+
+unregister_ports:
+ /*
+ * Unregister any adapters which have been registered. We don't add the
+ * port to the array until the adapter has been registered successfully,
+ * so any non-NULL ports must have been registered.
+ */
+ for (int i = 0; i < cros_ec_cec->num_ports; i++) {
+ port = cros_ec_cec->ports[i];
+ if (!port)
+ break;
+ cec_notifier_cec_adap_unregister(port->notify, port->adap);
+ cec_unregister_adapter(port->adap);
+ }
+ return ret;
+}
+
+static void cros_ec_cec_remove(struct platform_device *pdev)
+{
+ struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ struct cros_ec_cec_port *port;
+ int ret;
+
+ /*
+ * blocking_notifier_chain_unregister() only fails if the notifier isn't
+ * in the list. We know it was added to it by .probe(), so there should
+ * be no need for error checking. Be cautious and still check.
+ */
+ ret = blocking_notifier_chain_unregister(
+ &cros_ec_cec->cros_ec->event_notifier,
+ &cros_ec_cec->notifier);
+ if (ret)
+ dev_err(dev, "failed to unregister notifier\n");
+
+ for (int i = 0; i < cros_ec_cec->num_ports; i++) {
+ port = cros_ec_cec->ports[i];
+ cec_notifier_cec_adap_unregister(port->notify, port->adap);
+ cec_unregister_adapter(port->adap);
+ }
+}
+
+static const struct platform_device_id cros_ec_cec_id[] = {
+ { DRV_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_cec_id);
+
+static struct platform_driver cros_ec_cec_driver = {
+ .probe = cros_ec_cec_probe,
+ .remove = cros_ec_cec_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &cros_ec_cec_pm_ops,
+ },
+ .id_table = cros_ec_cec_id,
+};
+
+module_platform_driver(cros_ec_cec_driver);
+
+MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/cec/platform/meson/Makefile b/drivers/media/cec/platform/meson/Makefile
new file mode 100644
index 000000000000..34fc5d444d0e
--- /dev/null
+++ b/drivers/media/cec/platform/meson/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_CEC_MESON_AO) += ao-cec.o
+obj-$(CONFIG_CEC_MESON_G12A_AO) += ao-cec-g12a.o
diff --git a/drivers/media/cec/platform/meson/ao-cec-g12a.c b/drivers/media/cec/platform/meson/ao-cec-g12a.c
new file mode 100644
index 000000000000..41f5b8669cb0
--- /dev/null
+++ b/drivers/media/cec/platform/meson/ao-cec-g12a.c
@@ -0,0 +1,792 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Amlogic Meson AO CEC G12A Controller
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <media/cec.h>
+#include <media/cec-notifier.h>
+#include <linux/clk-provider.h>
+
+/* CEC Registers */
+
+#define CECB_CLK_CNTL_REG0 0x00
+
+#define CECB_CLK_CNTL_N1 GENMASK(11, 0)
+#define CECB_CLK_CNTL_N2 GENMASK(23, 12)
+#define CECB_CLK_CNTL_DUAL_EN BIT(28)
+#define CECB_CLK_CNTL_OUTPUT_EN BIT(30)
+#define CECB_CLK_CNTL_INPUT_EN BIT(31)
+
+#define CECB_CLK_CNTL_REG1 0x04
+
+#define CECB_CLK_CNTL_M1 GENMASK(11, 0)
+#define CECB_CLK_CNTL_M2 GENMASK(23, 12)
+#define CECB_CLK_CNTL_BYPASS_EN BIT(24)
+
+/*
+ * [14:12] Filter_del. For glitch-filtering CEC line, ignore signal
+ * change pulse width < filter_del * T(filter_tick) * 3.
+ * [9:8] Filter_tick_sel: Select which periodical pulse for
+ * glitch-filtering CEC line signal.
+ * - 0=Use T(xtal)*3 = 125ns;
+ * - 1=Use once-per-1us pulse;
+ * - 2=Use once-per-10us pulse;
+ * - 3=Use once-per-100us pulse.
+ * [3] Sysclk_en. 0=Disable system clock; 1=Enable system clock.
+ * [2:1] cntl_clk
+ * - 0 = Disable clk (Power-off mode)
+ * - 1 = Enable gated clock (Normal mode)
+ * - 2 = Enable free-run clk (Debug mode)
+ * [0] SW_RESET 1=Apply reset; 0=No reset.
+ */
+#define CECB_GEN_CNTL_REG 0x08
+
+#define CECB_GEN_CNTL_RESET BIT(0)
+#define CECB_GEN_CNTL_CLK_DISABLE 0
+#define CECB_GEN_CNTL_CLK_ENABLE 1
+#define CECB_GEN_CNTL_CLK_ENABLE_DBG 2
+#define CECB_GEN_CNTL_CLK_CTRL_MASK GENMASK(2, 1)
+#define CECB_GEN_CNTL_SYS_CLK_EN BIT(3)
+#define CECB_GEN_CNTL_FILTER_TICK_125NS 0
+#define CECB_GEN_CNTL_FILTER_TICK_1US 1
+#define CECB_GEN_CNTL_FILTER_TICK_10US 2
+#define CECB_GEN_CNTL_FILTER_TICK_100US 3
+#define CECB_GEN_CNTL_FILTER_TICK_SEL GENMASK(9, 8)
+#define CECB_GEN_CNTL_FILTER_DEL GENMASK(14, 12)
+
+/*
+ * [7:0] cec_reg_addr
+ * [15:8] cec_reg_wrdata
+ * [16] cec_reg_wr
+ * - 0 = Read
+ * - 1 = Write
+ * [31:24] cec_reg_rddata
+ */
+#define CECB_RW_REG 0x0c
+
+#define CECB_RW_ADDR GENMASK(7, 0)
+#define CECB_RW_WR_DATA GENMASK(15, 8)
+#define CECB_RW_WRITE_EN BIT(16)
+#define CECB_RW_BUS_BUSY BIT(23)
+#define CECB_RW_RD_DATA GENMASK(31, 24)
+
+/*
+ * [0] DONE Interrupt
+ * [1] End Of Message Interrupt
+ * [2] Not Acknowlegde Interrupt
+ * [3] Arbitration Loss Interrupt
+ * [4] Initiator Error Interrupt
+ * [5] Follower Error Interrupt
+ * [6] Wake-Up Interrupt
+ */
+#define CECB_INTR_MASKN_REG 0x10
+#define CECB_INTR_CLR_REG 0x14
+#define CECB_INTR_STAT_REG 0x18
+
+#define CECB_INTR_DONE BIT(0)
+#define CECB_INTR_EOM BIT(1)
+#define CECB_INTR_NACK BIT(2)
+#define CECB_INTR_ARB_LOSS BIT(3)
+#define CECB_INTR_INITIATOR_ERR BIT(4)
+#define CECB_INTR_FOLLOWER_ERR BIT(5)
+#define CECB_INTR_WAKE_UP BIT(6)
+
+/* CEC Commands */
+
+#define CECB_CTRL 0x00
+
+#define CECB_CTRL_SEND BIT(0)
+#define CECB_CTRL_TYPE GENMASK(2, 1)
+#define CECB_CTRL_TYPE_RETRY 0
+#define CECB_CTRL_TYPE_NEW 1
+#define CECB_CTRL_TYPE_NEXT 2
+
+#define CECB_CTRL2 0x01
+
+#define CECB_CTRL2_RISE_DEL_MAX GENMASK(4, 0)
+
+#define CECB_INTR_MASK 0x02
+#define CECB_LADD_LOW 0x05
+#define CECB_LADD_HIGH 0x06
+#define CECB_TX_CNT 0x07
+#define CECB_RX_CNT 0x08
+#define CECB_STAT0 0x09
+#define CECB_TX_DATA00 0x10
+#define CECB_TX_DATA01 0x11
+#define CECB_TX_DATA02 0x12
+#define CECB_TX_DATA03 0x13
+#define CECB_TX_DATA04 0x14
+#define CECB_TX_DATA05 0x15
+#define CECB_TX_DATA06 0x16
+#define CECB_TX_DATA07 0x17
+#define CECB_TX_DATA08 0x18
+#define CECB_TX_DATA09 0x19
+#define CECB_TX_DATA10 0x1A
+#define CECB_TX_DATA11 0x1B
+#define CECB_TX_DATA12 0x1C
+#define CECB_TX_DATA13 0x1D
+#define CECB_TX_DATA14 0x1E
+#define CECB_TX_DATA15 0x1F
+#define CECB_RX_DATA00 0x20
+#define CECB_RX_DATA01 0x21
+#define CECB_RX_DATA02 0x22
+#define CECB_RX_DATA03 0x23
+#define CECB_RX_DATA04 0x24
+#define CECB_RX_DATA05 0x25
+#define CECB_RX_DATA06 0x26
+#define CECB_RX_DATA07 0x27
+#define CECB_RX_DATA08 0x28
+#define CECB_RX_DATA09 0x29
+#define CECB_RX_DATA10 0x2A
+#define CECB_RX_DATA11 0x2B
+#define CECB_RX_DATA12 0x2C
+#define CECB_RX_DATA13 0x2D
+#define CECB_RX_DATA14 0x2E
+#define CECB_RX_DATA15 0x2F
+#define CECB_LOCK_BUF 0x30
+
+#define CECB_LOCK_BUF_EN BIT(0)
+
+#define CECB_WAKEUPCTRL 0x31
+
+struct meson_ao_cec_g12a_data {
+ /* Setup the internal CECB_CTRL2 register */
+ bool ctrl2_setup;
+};
+
+struct meson_ao_cec_g12a_device {
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ struct regmap *regmap_cec;
+ spinlock_t cec_reg_lock;
+ struct cec_notifier *notify;
+ struct cec_adapter *adap;
+ struct cec_msg rx_msg;
+ struct clk *oscin;
+ struct clk *core;
+ const struct meson_ao_cec_g12a_data *data;
+};
+
+static const struct regmap_config meson_ao_cec_g12a_regmap_conf = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = CECB_INTR_STAT_REG,
+};
+
+/*
+ * The AO-CECB embeds a dual/divider to generate a more precise
+ * 32,768KHz clock for CEC core clock.
+ * ______ ______
+ * | | | |
+ * ______ | Div1 |-| Cnt1 | ______
+ * | | /|______| |______|\ | |
+ * Xtal-->| Gate |---| ______ ______ X-X--| Gate |-->
+ * |______| | \| | | |/ | |______|
+ * | | Div2 |-| Cnt2 | |
+ * | |______| |______| |
+ * |_______________________|
+ *
+ * The dividing can be switched to single or dual, with a counter
+ * for each divider to set when the switching is done.
+ * The entire dividing mechanism can be also bypassed.
+ */
+
+struct meson_ao_cec_g12a_dualdiv_clk {
+ struct clk_hw hw;
+ struct regmap *regmap;
+};
+
+#define hw_to_meson_ao_cec_g12a_dualdiv_clk(_hw) \
+ container_of(_hw, struct meson_ao_cec_g12a_dualdiv_clk, hw) \
+
+static unsigned long
+meson_ao_cec_g12a_dualdiv_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk =
+ hw_to_meson_ao_cec_g12a_dualdiv_clk(hw);
+ unsigned long n1;
+ u32 reg0, reg1;
+
+ regmap_read(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, &reg0);
+ regmap_read(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, &reg1);
+
+ if (reg1 & CECB_CLK_CNTL_BYPASS_EN)
+ return parent_rate;
+
+ if (reg0 & CECB_CLK_CNTL_DUAL_EN) {
+ unsigned long n2, m1, m2, f1, f2, p1, p2;
+
+ n1 = FIELD_GET(CECB_CLK_CNTL_N1, reg0) + 1;
+ n2 = FIELD_GET(CECB_CLK_CNTL_N2, reg0) + 1;
+
+ m1 = FIELD_GET(CECB_CLK_CNTL_M1, reg1) + 1;
+ m2 = FIELD_GET(CECB_CLK_CNTL_M1, reg1) + 1;
+
+ f1 = DIV_ROUND_CLOSEST(parent_rate, n1);
+ f2 = DIV_ROUND_CLOSEST(parent_rate, n2);
+
+ p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2));
+ p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2));
+
+ return DIV_ROUND_UP(100000000, p1 + p2);
+ }
+
+ n1 = FIELD_GET(CECB_CLK_CNTL_N1, reg0) + 1;
+
+ return DIV_ROUND_CLOSEST(parent_rate, n1);
+}
+
+static int meson_ao_cec_g12a_dualdiv_clk_enable(struct clk_hw *hw)
+{
+ struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk =
+ hw_to_meson_ao_cec_g12a_dualdiv_clk(hw);
+
+
+ /* Disable Input & Output */
+ regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,
+ CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN,
+ 0);
+
+ /* Set N1 & N2 */
+ regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,
+ CECB_CLK_CNTL_N1,
+ FIELD_PREP(CECB_CLK_CNTL_N1, 733 - 1));
+
+ regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,
+ CECB_CLK_CNTL_N2,
+ FIELD_PREP(CECB_CLK_CNTL_N2, 732 - 1));
+
+ /* Set M1 & M2 */
+ regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG1,
+ CECB_CLK_CNTL_M1,
+ FIELD_PREP(CECB_CLK_CNTL_M1, 8 - 1));
+
+ regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG1,
+ CECB_CLK_CNTL_M2,
+ FIELD_PREP(CECB_CLK_CNTL_M2, 11 - 1));
+
+ /* Enable Dual divisor */
+ regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,
+ CECB_CLK_CNTL_DUAL_EN, CECB_CLK_CNTL_DUAL_EN);
+
+ /* Disable divisor bypass */
+ regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG1,
+ CECB_CLK_CNTL_BYPASS_EN, 0);
+
+ /* Enable Input & Output */
+ regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,
+ CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN,
+ CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN);
+
+ return 0;
+}
+
+static void meson_ao_cec_g12a_dualdiv_clk_disable(struct clk_hw *hw)
+{
+ struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk =
+ hw_to_meson_ao_cec_g12a_dualdiv_clk(hw);
+
+ regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0,
+ CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN,
+ 0);
+}
+
+static int meson_ao_cec_g12a_dualdiv_clk_is_enabled(struct clk_hw *hw)
+{
+ struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk =
+ hw_to_meson_ao_cec_g12a_dualdiv_clk(hw);
+ int val;
+
+ regmap_read(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, &val);
+
+ return !!(val & (CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN));
+}
+
+static const struct clk_ops meson_ao_cec_g12a_dualdiv_clk_ops = {
+ .recalc_rate = meson_ao_cec_g12a_dualdiv_clk_recalc_rate,
+ .is_enabled = meson_ao_cec_g12a_dualdiv_clk_is_enabled,
+ .enable = meson_ao_cec_g12a_dualdiv_clk_enable,
+ .disable = meson_ao_cec_g12a_dualdiv_clk_disable,
+};
+
+static int meson_ao_cec_g12a_setup_clk(struct meson_ao_cec_g12a_device *ao_cec)
+{
+ struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk;
+ struct device *dev = &ao_cec->pdev->dev;
+ struct clk_init_data init;
+ const char *parent_name;
+ struct clk *clk;
+ char *name;
+
+ dualdiv_clk = devm_kzalloc(dev, sizeof(*dualdiv_clk), GFP_KERNEL);
+ if (!dualdiv_clk)
+ return -ENOMEM;
+
+ name = kasprintf(GFP_KERNEL, "%s#dualdiv_clk", dev_name(dev));
+ if (!name)
+ return -ENOMEM;
+
+ parent_name = __clk_get_name(ao_cec->oscin);
+
+ init.name = name;
+ init.ops = &meson_ao_cec_g12a_dualdiv_clk_ops;
+ init.flags = 0;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ dualdiv_clk->regmap = ao_cec->regmap;
+ dualdiv_clk->hw.init = &init;
+
+ clk = devm_clk_register(dev, &dualdiv_clk->hw);
+ kfree(name);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "failed to register clock\n");
+ return PTR_ERR(clk);
+ }
+
+ ao_cec->core = clk;
+
+ return 0;
+}
+
+static int meson_ao_cec_g12a_read(void *context, unsigned int addr,
+ unsigned int *data)
+{
+ struct meson_ao_cec_g12a_device *ao_cec = context;
+ u32 reg = FIELD_PREP(CECB_RW_ADDR, addr);
+ int ret = 0;
+
+ ret = regmap_write(ao_cec->regmap, CECB_RW_REG, reg);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(ao_cec->regmap, CECB_RW_REG, reg,
+ !(reg & CECB_RW_BUS_BUSY),
+ 5, 1000);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(ao_cec->regmap, CECB_RW_REG, &reg);
+
+ *data = FIELD_GET(CECB_RW_RD_DATA, reg);
+
+ return ret;
+}
+
+static int meson_ao_cec_g12a_write(void *context, unsigned int addr,
+ unsigned int data)
+{
+ struct meson_ao_cec_g12a_device *ao_cec = context;
+ u32 reg = FIELD_PREP(CECB_RW_ADDR, addr) |
+ FIELD_PREP(CECB_RW_WR_DATA, data) |
+ CECB_RW_WRITE_EN;
+
+ return regmap_write(ao_cec->regmap, CECB_RW_REG, reg);
+}
+
+static const struct regmap_config meson_ao_cec_g12a_cec_regmap_conf = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .reg_read = meson_ao_cec_g12a_read,
+ .reg_write = meson_ao_cec_g12a_write,
+ .max_register = 0xffff,
+};
+
+static inline void
+meson_ao_cec_g12a_irq_setup(struct meson_ao_cec_g12a_device *ao_cec,
+ bool enable)
+{
+ u32 cfg = CECB_INTR_DONE | CECB_INTR_EOM | CECB_INTR_NACK |
+ CECB_INTR_ARB_LOSS | CECB_INTR_INITIATOR_ERR |
+ CECB_INTR_FOLLOWER_ERR;
+
+ regmap_write(ao_cec->regmap, CECB_INTR_MASKN_REG,
+ enable ? cfg : 0);
+}
+
+static void meson_ao_cec_g12a_irq_rx(struct meson_ao_cec_g12a_device *ao_cec)
+{
+ int i, ret = 0;
+ u32 val;
+
+ ret = regmap_read(ao_cec->regmap_cec, CECB_RX_CNT, &val);
+
+ ao_cec->rx_msg.len = val;
+ if (ao_cec->rx_msg.len > CEC_MAX_MSG_SIZE)
+ ao_cec->rx_msg.len = CEC_MAX_MSG_SIZE;
+
+ for (i = 0; i < ao_cec->rx_msg.len; i++) {
+ ret |= regmap_read(ao_cec->regmap_cec,
+ CECB_RX_DATA00 + i, &val);
+
+ ao_cec->rx_msg.msg[i] = val & 0xff;
+ }
+
+ ret |= regmap_write(ao_cec->regmap_cec, CECB_LOCK_BUF, 0);
+ if (ret)
+ return;
+
+ cec_received_msg(ao_cec->adap, &ao_cec->rx_msg);
+}
+
+static irqreturn_t meson_ao_cec_g12a_irq(int irq, void *data)
+{
+ struct meson_ao_cec_g12a_device *ao_cec = data;
+ u32 stat;
+
+ regmap_read(ao_cec->regmap, CECB_INTR_STAT_REG, &stat);
+ if (stat)
+ return IRQ_WAKE_THREAD;
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t meson_ao_cec_g12a_irq_thread(int irq, void *data)
+{
+ struct meson_ao_cec_g12a_device *ao_cec = data;
+ u32 stat;
+
+ regmap_read(ao_cec->regmap, CECB_INTR_STAT_REG, &stat);
+ regmap_write(ao_cec->regmap, CECB_INTR_CLR_REG, stat);
+
+ if (stat & CECB_INTR_DONE)
+ cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_OK);
+
+ if (stat & CECB_INTR_EOM)
+ meson_ao_cec_g12a_irq_rx(ao_cec);
+
+ if (stat & CECB_INTR_NACK)
+ cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_NACK);
+
+ if (stat & CECB_INTR_ARB_LOSS) {
+ regmap_write(ao_cec->regmap_cec, CECB_TX_CNT, 0);
+ regmap_update_bits(ao_cec->regmap_cec, CECB_CTRL,
+ CECB_CTRL_SEND | CECB_CTRL_TYPE, 0);
+ cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_ARB_LOST);
+ }
+
+ /* Initiator reports an error on the CEC bus */
+ if (stat & CECB_INTR_INITIATOR_ERR)
+ cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_ERROR);
+
+ /* Follower reports a receive error, just reset RX buffer */
+ if (stat & CECB_INTR_FOLLOWER_ERR)
+ regmap_write(ao_cec->regmap_cec, CECB_LOCK_BUF, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int
+meson_ao_cec_g12a_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct meson_ao_cec_g12a_device *ao_cec = adap->priv;
+ int ret = 0;
+
+ if (logical_addr == CEC_LOG_ADDR_INVALID) {
+ /* Assume this will allways succeed */
+ regmap_write(ao_cec->regmap_cec, CECB_LADD_LOW, 0);
+ regmap_write(ao_cec->regmap_cec, CECB_LADD_HIGH, 0);
+
+ return 0;
+ } else if (logical_addr < 8) {
+ ret = regmap_update_bits(ao_cec->regmap_cec, CECB_LADD_LOW,
+ BIT(logical_addr),
+ BIT(logical_addr));
+ } else {
+ ret = regmap_update_bits(ao_cec->regmap_cec, CECB_LADD_HIGH,
+ BIT(logical_addr - 8),
+ BIT(logical_addr - 8));
+ }
+
+ /* Always set Broadcast/Unregistered 15 address */
+ ret |= regmap_update_bits(ao_cec->regmap_cec, CECB_LADD_HIGH,
+ BIT(CEC_LOG_ADDR_UNREGISTERED - 8),
+ BIT(CEC_LOG_ADDR_UNREGISTERED - 8));
+
+ return ret ? -EIO : 0;
+}
+
+static int meson_ao_cec_g12a_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct meson_ao_cec_g12a_device *ao_cec = adap->priv;
+ unsigned int type;
+ int ret = 0;
+ u32 val;
+ int i;
+
+ /* Check if RX is in progress */
+ ret = regmap_read(ao_cec->regmap_cec, CECB_LOCK_BUF, &val);
+ if (ret)
+ return ret;
+ if (val & CECB_LOCK_BUF_EN)
+ return -EBUSY;
+
+ /* Check if TX Busy */
+ ret = regmap_read(ao_cec->regmap_cec, CECB_CTRL, &val);
+ if (ret)
+ return ret;
+ if (val & CECB_CTRL_SEND)
+ return -EBUSY;
+
+ switch (signal_free_time) {
+ case CEC_SIGNAL_FREE_TIME_RETRY:
+ type = CECB_CTRL_TYPE_RETRY;
+ break;
+ case CEC_SIGNAL_FREE_TIME_NEXT_XFER:
+ type = CECB_CTRL_TYPE_NEXT;
+ break;
+ case CEC_SIGNAL_FREE_TIME_NEW_INITIATOR:
+ default:
+ type = CECB_CTRL_TYPE_NEW;
+ break;
+ }
+
+ for (i = 0; i < msg->len; i++)
+ ret |= regmap_write(ao_cec->regmap_cec, CECB_TX_DATA00 + i,
+ msg->msg[i]);
+
+ ret |= regmap_write(ao_cec->regmap_cec, CECB_TX_CNT, msg->len);
+ if (ret)
+ return -EIO;
+
+ ret = regmap_update_bits(ao_cec->regmap_cec, CECB_CTRL,
+ CECB_CTRL_SEND |
+ CECB_CTRL_TYPE,
+ CECB_CTRL_SEND |
+ FIELD_PREP(CECB_CTRL_TYPE, type));
+
+ return ret;
+}
+
+static int meson_ao_cec_g12a_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct meson_ao_cec_g12a_device *ao_cec = adap->priv;
+
+ meson_ao_cec_g12a_irq_setup(ao_cec, false);
+
+ regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,
+ CECB_GEN_CNTL_RESET, CECB_GEN_CNTL_RESET);
+
+ if (!enable)
+ return 0;
+
+ /* Setup Filter */
+ regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,
+ CECB_GEN_CNTL_FILTER_TICK_SEL |
+ CECB_GEN_CNTL_FILTER_DEL,
+ FIELD_PREP(CECB_GEN_CNTL_FILTER_TICK_SEL,
+ CECB_GEN_CNTL_FILTER_TICK_1US) |
+ FIELD_PREP(CECB_GEN_CNTL_FILTER_DEL, 7));
+
+ /* Enable System Clock */
+ regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,
+ CECB_GEN_CNTL_SYS_CLK_EN,
+ CECB_GEN_CNTL_SYS_CLK_EN);
+
+ /* Enable gated clock (Normal mode). */
+ regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,
+ CECB_GEN_CNTL_CLK_CTRL_MASK,
+ FIELD_PREP(CECB_GEN_CNTL_CLK_CTRL_MASK,
+ CECB_GEN_CNTL_CLK_ENABLE));
+
+ /* Release Reset */
+ regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG,
+ CECB_GEN_CNTL_RESET, 0);
+
+ if (ao_cec->data->ctrl2_setup)
+ regmap_write(ao_cec->regmap_cec, CECB_CTRL2,
+ FIELD_PREP(CECB_CTRL2_RISE_DEL_MAX, 2));
+
+ meson_ao_cec_g12a_irq_setup(ao_cec, true);
+
+ return 0;
+}
+
+static const struct cec_adap_ops meson_ao_cec_g12a_ops = {
+ .adap_enable = meson_ao_cec_g12a_adap_enable,
+ .adap_log_addr = meson_ao_cec_g12a_set_log_addr,
+ .adap_transmit = meson_ao_cec_g12a_transmit,
+};
+
+static int meson_ao_cec_g12a_probe(struct platform_device *pdev)
+{
+ struct meson_ao_cec_g12a_device *ao_cec;
+ struct device *hdmi_dev;
+ void __iomem *base;
+ int ret, irq;
+
+ hdmi_dev = cec_notifier_parse_hdmi_phandle(&pdev->dev);
+ if (IS_ERR(hdmi_dev))
+ return PTR_ERR(hdmi_dev);
+
+ ao_cec = devm_kzalloc(&pdev->dev, sizeof(*ao_cec), GFP_KERNEL);
+ if (!ao_cec)
+ return -ENOMEM;
+
+ ao_cec->data = of_device_get_match_data(&pdev->dev);
+ if (!ao_cec->data) {
+ dev_err(&pdev->dev, "failed to get match data\n");
+ return -ENODEV;
+ }
+
+ spin_lock_init(&ao_cec->cec_reg_lock);
+ ao_cec->pdev = pdev;
+
+ ao_cec->adap = cec_allocate_adapter(&meson_ao_cec_g12a_ops, ao_cec,
+ "meson_g12a_ao_cec",
+ CEC_CAP_DEFAULTS |
+ CEC_CAP_CONNECTOR_INFO,
+ CEC_MAX_LOG_ADDRS);
+ if (IS_ERR(ao_cec->adap))
+ return PTR_ERR(ao_cec->adap);
+
+ ao_cec->adap->owner = THIS_MODULE;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto out_probe_adapter;
+ }
+
+ ao_cec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &meson_ao_cec_g12a_regmap_conf);
+ if (IS_ERR(ao_cec->regmap)) {
+ ret = PTR_ERR(ao_cec->regmap);
+ goto out_probe_adapter;
+ }
+
+ ao_cec->regmap_cec = devm_regmap_init(&pdev->dev, NULL, ao_cec,
+ &meson_ao_cec_g12a_cec_regmap_conf);
+ if (IS_ERR(ao_cec->regmap_cec)) {
+ ret = PTR_ERR(ao_cec->regmap_cec);
+ goto out_probe_adapter;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_threaded_irq(&pdev->dev, irq,
+ meson_ao_cec_g12a_irq,
+ meson_ao_cec_g12a_irq_thread,
+ 0, NULL, ao_cec);
+ if (ret) {
+ dev_err(&pdev->dev, "irq request failed\n");
+ goto out_probe_adapter;
+ }
+
+ ao_cec->oscin = devm_clk_get(&pdev->dev, "oscin");
+ if (IS_ERR(ao_cec->oscin)) {
+ dev_err(&pdev->dev, "oscin clock request failed\n");
+ ret = PTR_ERR(ao_cec->oscin);
+ goto out_probe_adapter;
+ }
+
+ ret = meson_ao_cec_g12a_setup_clk(ao_cec);
+ if (ret)
+ goto out_probe_adapter;
+
+ ret = clk_prepare_enable(ao_cec->core);
+ if (ret) {
+ dev_err(&pdev->dev, "core clock enable failed\n");
+ goto out_probe_adapter;
+ }
+
+ device_reset_optional(&pdev->dev);
+
+ platform_set_drvdata(pdev, ao_cec);
+
+ ao_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, NULL,
+ ao_cec->adap);
+ if (!ao_cec->notify) {
+ ret = -ENOMEM;
+ goto out_probe_core_clk;
+ }
+
+ ret = cec_register_adapter(ao_cec->adap, &pdev->dev);
+ if (ret < 0)
+ goto out_probe_notify;
+
+ /* Setup Hardware */
+ regmap_write(ao_cec->regmap, CECB_GEN_CNTL_REG, CECB_GEN_CNTL_RESET);
+
+ return 0;
+
+out_probe_notify:
+ cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap);
+
+out_probe_core_clk:
+ clk_disable_unprepare(ao_cec->core);
+
+out_probe_adapter:
+ cec_delete_adapter(ao_cec->adap);
+
+ dev_err(&pdev->dev, "CEC controller registration failed\n");
+
+ return ret;
+}
+
+static void meson_ao_cec_g12a_remove(struct platform_device *pdev)
+{
+ struct meson_ao_cec_g12a_device *ao_cec = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(ao_cec->core);
+
+ cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap);
+
+ cec_unregister_adapter(ao_cec->adap);
+}
+
+static const struct meson_ao_cec_g12a_data ao_cec_g12a_data = {
+ .ctrl2_setup = false,
+};
+
+static const struct meson_ao_cec_g12a_data ao_cec_sm1_data = {
+ .ctrl2_setup = true,
+};
+
+static const struct of_device_id meson_ao_cec_g12a_of_match[] = {
+ {
+ .compatible = "amlogic,meson-g12a-ao-cec",
+ .data = &ao_cec_g12a_data,
+ },
+ {
+ .compatible = "amlogic,meson-sm1-ao-cec",
+ .data = &ao_cec_sm1_data,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, meson_ao_cec_g12a_of_match);
+
+static struct platform_driver meson_ao_cec_g12a_driver = {
+ .probe = meson_ao_cec_g12a_probe,
+ .remove = meson_ao_cec_g12a_remove,
+ .driver = {
+ .name = "meson-ao-cec-g12a",
+ .of_match_table = of_match_ptr(meson_ao_cec_g12a_of_match),
+ },
+};
+
+module_platform_driver(meson_ao_cec_g12a_driver);
+
+MODULE_DESCRIPTION("Meson AO CEC G12A Controller driver");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/cec/platform/meson/ao-cec.c b/drivers/media/cec/platform/meson/ao-cec.c
new file mode 100644
index 000000000000..145efd9af6ac
--- /dev/null
+++ b/drivers/media/cec/platform/meson/ao-cec.c
@@ -0,0 +1,728 @@
+/*
+ * Driver for Amlogic Meson AO CEC Controller
+ *
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved
+ * Copyright (C) 2017 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/reset.h>
+#include <media/cec.h>
+#include <media/cec-notifier.h>
+
+/* CEC Registers */
+
+/*
+ * [2:1] cntl_clk
+ * - 0 = Disable clk (Power-off mode)
+ * - 1 = Enable gated clock (Normal mode)
+ * - 2 = Enable free-run clk (Debug mode)
+ */
+#define CEC_GEN_CNTL_REG 0x00
+
+#define CEC_GEN_CNTL_RESET BIT(0)
+#define CEC_GEN_CNTL_CLK_DISABLE 0
+#define CEC_GEN_CNTL_CLK_ENABLE 1
+#define CEC_GEN_CNTL_CLK_ENABLE_DBG 2
+#define CEC_GEN_CNTL_CLK_CTRL_MASK GENMASK(2, 1)
+
+/*
+ * [7:0] cec_reg_addr
+ * [15:8] cec_reg_wrdata
+ * [16] cec_reg_wr
+ * - 0 = Read
+ * - 1 = Write
+ * [23] bus free
+ * [31:24] cec_reg_rddata
+ */
+#define CEC_RW_REG 0x04
+
+#define CEC_RW_ADDR GENMASK(7, 0)
+#define CEC_RW_WR_DATA GENMASK(15, 8)
+#define CEC_RW_WRITE_EN BIT(16)
+#define CEC_RW_BUS_BUSY BIT(23)
+#define CEC_RW_RD_DATA GENMASK(31, 24)
+
+/*
+ * [1] tx intr
+ * [2] rx intr
+ */
+#define CEC_INTR_MASKN_REG 0x08
+#define CEC_INTR_CLR_REG 0x0c
+#define CEC_INTR_STAT_REG 0x10
+
+#define CEC_INTR_TX BIT(1)
+#define CEC_INTR_RX BIT(2)
+
+/* CEC Commands */
+
+#define CEC_TX_MSG_0_HEADER 0x00
+#define CEC_TX_MSG_1_OPCODE 0x01
+#define CEC_TX_MSG_2_OP1 0x02
+#define CEC_TX_MSG_3_OP2 0x03
+#define CEC_TX_MSG_4_OP3 0x04
+#define CEC_TX_MSG_5_OP4 0x05
+#define CEC_TX_MSG_6_OP5 0x06
+#define CEC_TX_MSG_7_OP6 0x07
+#define CEC_TX_MSG_8_OP7 0x08
+#define CEC_TX_MSG_9_OP8 0x09
+#define CEC_TX_MSG_A_OP9 0x0A
+#define CEC_TX_MSG_B_OP10 0x0B
+#define CEC_TX_MSG_C_OP11 0x0C
+#define CEC_TX_MSG_D_OP12 0x0D
+#define CEC_TX_MSG_E_OP13 0x0E
+#define CEC_TX_MSG_F_OP14 0x0F
+#define CEC_TX_MSG_LENGTH 0x10
+#define CEC_TX_MSG_CMD 0x11
+#define CEC_TX_WRITE_BUF 0x12
+#define CEC_TX_CLEAR_BUF 0x13
+#define CEC_RX_MSG_CMD 0x14
+#define CEC_RX_CLEAR_BUF 0x15
+#define CEC_LOGICAL_ADDR0 0x16
+#define CEC_LOGICAL_ADDR1 0x17
+#define CEC_LOGICAL_ADDR2 0x18
+#define CEC_LOGICAL_ADDR3 0x19
+#define CEC_LOGICAL_ADDR4 0x1A
+#define CEC_CLOCK_DIV_H 0x1B
+#define CEC_CLOCK_DIV_L 0x1C
+#define CEC_QUIESCENT_25MS_BIT7_0 0x20
+#define CEC_QUIESCENT_25MS_BIT11_8 0x21
+#define CEC_STARTBITMINL2H_3MS5_BIT7_0 0x22
+#define CEC_STARTBITMINL2H_3MS5_BIT8 0x23
+#define CEC_STARTBITMAXL2H_3MS9_BIT7_0 0x24
+#define CEC_STARTBITMAXL2H_3MS9_BIT8 0x25
+#define CEC_STARTBITMINH_0MS6_BIT7_0 0x26
+#define CEC_STARTBITMINH_0MS6_BIT8 0x27
+#define CEC_STARTBITMAXH_1MS0_BIT7_0 0x28
+#define CEC_STARTBITMAXH_1MS0_BIT8 0x29
+#define CEC_STARTBITMINTOT_4MS3_BIT7_0 0x2A
+#define CEC_STARTBITMINTOT_4MS3_BIT9_8 0x2B
+#define CEC_STARTBITMAXTOT_4MS7_BIT7_0 0x2C
+#define CEC_STARTBITMAXTOT_4MS7_BIT9_8 0x2D
+#define CEC_LOGIC1MINL2H_0MS4_BIT7_0 0x2E
+#define CEC_LOGIC1MINL2H_0MS4_BIT8 0x2F
+#define CEC_LOGIC1MAXL2H_0MS8_BIT7_0 0x30
+#define CEC_LOGIC1MAXL2H_0MS8_BIT8 0x31
+#define CEC_LOGIC0MINL2H_1MS3_BIT7_0 0x32
+#define CEC_LOGIC0MINL2H_1MS3_BIT8 0x33
+#define CEC_LOGIC0MAXL2H_1MS7_BIT7_0 0x34
+#define CEC_LOGIC0MAXL2H_1MS7_BIT8 0x35
+#define CEC_LOGICMINTOTAL_2MS05_BIT7_0 0x36
+#define CEC_LOGICMINTOTAL_2MS05_BIT9_8 0x37
+#define CEC_LOGICMAXHIGH_2MS8_BIT7_0 0x38
+#define CEC_LOGICMAXHIGH_2MS8_BIT8 0x39
+#define CEC_LOGICERRLOW_3MS4_BIT7_0 0x3A
+#define CEC_LOGICERRLOW_3MS4_BIT8 0x3B
+#define CEC_NOMSMPPOINT_1MS05 0x3C
+#define CEC_DELCNTR_LOGICERR 0x3E
+#define CEC_TXTIME_17MS_BIT7_0 0x40
+#define CEC_TXTIME_17MS_BIT10_8 0x41
+#define CEC_TXTIME_2BIT_BIT7_0 0x42
+#define CEC_TXTIME_2BIT_BIT10_8 0x43
+#define CEC_TXTIME_4BIT_BIT7_0 0x44
+#define CEC_TXTIME_4BIT_BIT10_8 0x45
+#define CEC_STARTBITNOML2H_3MS7_BIT7_0 0x46
+#define CEC_STARTBITNOML2H_3MS7_BIT8 0x47
+#define CEC_STARTBITNOMH_0MS8_BIT7_0 0x48
+#define CEC_STARTBITNOMH_0MS8_BIT8 0x49
+#define CEC_LOGIC1NOML2H_0MS6_BIT7_0 0x4A
+#define CEC_LOGIC1NOML2H_0MS6_BIT8 0x4B
+#define CEC_LOGIC0NOML2H_1MS5_BIT7_0 0x4C
+#define CEC_LOGIC0NOML2H_1MS5_BIT8 0x4D
+#define CEC_LOGIC1NOMH_1MS8_BIT7_0 0x4E
+#define CEC_LOGIC1NOMH_1MS8_BIT8 0x4F
+#define CEC_LOGIC0NOMH_0MS9_BIT7_0 0x50
+#define CEC_LOGIC0NOMH_0MS9_BIT8 0x51
+#define CEC_LOGICERRLOW_3MS6_BIT7_0 0x52
+#define CEC_LOGICERRLOW_3MS6_BIT8 0x53
+#define CEC_CHKCONTENTION_0MS1 0x54
+#define CEC_PREPARENXTBIT_0MS05_BIT7_0 0x56
+#define CEC_PREPARENXTBIT_0MS05_BIT8 0x57
+#define CEC_NOMSMPACKPOINT_0MS45 0x58
+#define CEC_ACK0NOML2H_1MS5_BIT7_0 0x5A
+#define CEC_ACK0NOML2H_1MS5_BIT8 0x5B
+#define CEC_BUGFIX_DISABLE_0 0x60
+#define CEC_BUGFIX_DISABLE_1 0x61
+#define CEC_RX_MSG_0_HEADER 0x80
+#define CEC_RX_MSG_1_OPCODE 0x81
+#define CEC_RX_MSG_2_OP1 0x82
+#define CEC_RX_MSG_3_OP2 0x83
+#define CEC_RX_MSG_4_OP3 0x84
+#define CEC_RX_MSG_5_OP4 0x85
+#define CEC_RX_MSG_6_OP5 0x86
+#define CEC_RX_MSG_7_OP6 0x87
+#define CEC_RX_MSG_8_OP7 0x88
+#define CEC_RX_MSG_9_OP8 0x89
+#define CEC_RX_MSG_A_OP9 0x8A
+#define CEC_RX_MSG_B_OP10 0x8B
+#define CEC_RX_MSG_C_OP11 0x8C
+#define CEC_RX_MSG_D_OP12 0x8D
+#define CEC_RX_MSG_E_OP13 0x8E
+#define CEC_RX_MSG_F_OP14 0x8F
+#define CEC_RX_MSG_LENGTH 0x90
+#define CEC_RX_MSG_STATUS 0x91
+#define CEC_RX_NUM_MSG 0x92
+#define CEC_TX_MSG_STATUS 0x93
+#define CEC_TX_NUM_MSG 0x94
+
+
+/* CEC_TX_MSG_CMD definition */
+#define TX_NO_OP 0 /* No transaction */
+#define TX_REQ_CURRENT 1 /* Transmit earliest message in buffer */
+#define TX_ABORT 2 /* Abort transmitting earliest message */
+#define TX_REQ_NEXT 3 /* Overwrite earliest msg, transmit next */
+
+/* tx_msg_status definition */
+#define TX_IDLE 0 /* No transaction */
+#define TX_BUSY 1 /* Transmitter is busy */
+#define TX_DONE 2 /* Message successfully transmitted */
+#define TX_ERROR 3 /* Message transmitted with error */
+
+/* rx_msg_cmd */
+#define RX_NO_OP 0 /* No transaction */
+#define RX_ACK_CURRENT 1 /* Read earliest message in buffer */
+#define RX_DISABLE 2 /* Disable receiving latest message */
+#define RX_ACK_NEXT 3 /* Clear earliest msg, read next */
+
+/* rx_msg_status */
+#define RX_IDLE 0 /* No transaction */
+#define RX_BUSY 1 /* Receiver is busy */
+#define RX_DONE 2 /* Message has been received successfully */
+#define RX_ERROR 3 /* Message has been received with error */
+
+/* RX_CLEAR_BUF options */
+#define CLEAR_START 1
+#define CLEAR_STOP 0
+
+/* CEC_LOGICAL_ADDRx options */
+#define LOGICAL_ADDR_MASK 0xf
+#define LOGICAL_ADDR_VALID BIT(4)
+#define LOGICAL_ADDR_DISABLE 0
+
+#define CEC_CLK_RATE 32768
+
+struct meson_ao_cec_device {
+ struct platform_device *pdev;
+ void __iomem *base;
+ struct clk *core;
+ spinlock_t cec_reg_lock;
+ struct cec_notifier *notify;
+ struct cec_adapter *adap;
+ struct cec_msg rx_msg;
+};
+
+#define writel_bits_relaxed(mask, val, addr) \
+ writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr)
+
+static inline int meson_ao_cec_wait_busy(struct meson_ao_cec_device *ao_cec)
+{
+ ktime_t timeout = ktime_add_us(ktime_get(), 5000);
+
+ while (readl_relaxed(ao_cec->base + CEC_RW_REG) & CEC_RW_BUS_BUSY) {
+ if (ktime_compare(ktime_get(), timeout) > 0)
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void meson_ao_cec_read(struct meson_ao_cec_device *ao_cec,
+ unsigned long address, u8 *data,
+ int *res)
+{
+ unsigned long flags;
+ u32 reg = FIELD_PREP(CEC_RW_ADDR, address);
+ int ret = 0;
+
+ if (res && *res)
+ return;
+
+ spin_lock_irqsave(&ao_cec->cec_reg_lock, flags);
+
+ ret = meson_ao_cec_wait_busy(ao_cec);
+ if (ret)
+ goto read_out;
+
+ writel_relaxed(reg, ao_cec->base + CEC_RW_REG);
+
+ ret = meson_ao_cec_wait_busy(ao_cec);
+ if (ret)
+ goto read_out;
+
+ *data = FIELD_GET(CEC_RW_RD_DATA,
+ readl_relaxed(ao_cec->base + CEC_RW_REG));
+
+read_out:
+ spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags);
+
+ if (res)
+ *res = ret;
+}
+
+static void meson_ao_cec_write(struct meson_ao_cec_device *ao_cec,
+ unsigned long address, u8 data,
+ int *res)
+{
+ unsigned long flags;
+ u32 reg = FIELD_PREP(CEC_RW_ADDR, address) |
+ FIELD_PREP(CEC_RW_WR_DATA, data) |
+ CEC_RW_WRITE_EN;
+ int ret = 0;
+
+ if (res && *res)
+ return;
+
+ spin_lock_irqsave(&ao_cec->cec_reg_lock, flags);
+
+ ret = meson_ao_cec_wait_busy(ao_cec);
+ if (ret)
+ goto write_out;
+
+ writel_relaxed(reg, ao_cec->base + CEC_RW_REG);
+
+write_out:
+ spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags);
+
+ if (res)
+ *res = ret;
+}
+
+static inline void meson_ao_cec_irq_setup(struct meson_ao_cec_device *ao_cec,
+ bool enable)
+{
+ u32 cfg = CEC_INTR_TX | CEC_INTR_RX;
+
+ writel_bits_relaxed(cfg, enable ? cfg : 0,
+ ao_cec->base + CEC_INTR_MASKN_REG);
+}
+
+static inline int meson_ao_cec_clear(struct meson_ao_cec_device *ao_cec)
+{
+ int ret = 0;
+
+ meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_DISABLE, &ret);
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT, &ret);
+ meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, 1, &ret);
+ meson_ao_cec_write(ao_cec, CEC_TX_CLEAR_BUF, 1, &ret);
+ if (ret)
+ return ret;
+
+ udelay(100);
+
+ meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, 0, &ret);
+ meson_ao_cec_write(ao_cec, CEC_TX_CLEAR_BUF, 0, &ret);
+ if (ret)
+ return ret;
+
+ udelay(100);
+
+ meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_NO_OP, &ret);
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_NO_OP, &ret);
+
+ return ret;
+}
+
+static int meson_ao_cec_arbit_bit_time_set(struct meson_ao_cec_device *ao_cec,
+ unsigned int bit_set,
+ unsigned int time_set)
+{
+ int ret = 0;
+
+ switch (bit_set) {
+ case CEC_SIGNAL_FREE_TIME_RETRY:
+ meson_ao_cec_write(ao_cec, CEC_TXTIME_4BIT_BIT7_0,
+ time_set & 0xff, &ret);
+ meson_ao_cec_write(ao_cec, CEC_TXTIME_4BIT_BIT10_8,
+ (time_set >> 8) & 0x7, &ret);
+ break;
+
+ case CEC_SIGNAL_FREE_TIME_NEW_INITIATOR:
+ meson_ao_cec_write(ao_cec, CEC_TXTIME_2BIT_BIT7_0,
+ time_set & 0xff, &ret);
+ meson_ao_cec_write(ao_cec, CEC_TXTIME_2BIT_BIT10_8,
+ (time_set >> 8) & 0x7, &ret);
+ break;
+
+ case CEC_SIGNAL_FREE_TIME_NEXT_XFER:
+ meson_ao_cec_write(ao_cec, CEC_TXTIME_17MS_BIT7_0,
+ time_set & 0xff, &ret);
+ meson_ao_cec_write(ao_cec, CEC_TXTIME_17MS_BIT10_8,
+ (time_set >> 8) & 0x7, &ret);
+ break;
+ }
+
+ return ret;
+}
+
+static irqreturn_t meson_ao_cec_irq(int irq, void *data)
+{
+ struct meson_ao_cec_device *ao_cec = data;
+ u32 stat = readl_relaxed(ao_cec->base + CEC_INTR_STAT_REG);
+
+ if (stat)
+ return IRQ_WAKE_THREAD;
+
+ return IRQ_NONE;
+}
+
+static void meson_ao_cec_irq_tx(struct meson_ao_cec_device *ao_cec)
+{
+ unsigned long tx_status = 0;
+ u8 stat;
+ int ret = 0;
+
+ meson_ao_cec_read(ao_cec, CEC_TX_MSG_STATUS, &stat, &ret);
+ if (ret)
+ goto tx_reg_err;
+
+ switch (stat) {
+ case TX_DONE:
+ tx_status = CEC_TX_STATUS_OK;
+ break;
+
+ case TX_BUSY:
+ tx_status = CEC_TX_STATUS_ARB_LOST;
+ break;
+
+ case TX_IDLE:
+ tx_status = CEC_TX_STATUS_LOW_DRIVE;
+ break;
+
+ case TX_ERROR:
+ default:
+ tx_status = CEC_TX_STATUS_NACK;
+ break;
+ }
+
+ /* Clear Interruption */
+ writel_relaxed(CEC_INTR_TX, ao_cec->base + CEC_INTR_CLR_REG);
+
+ /* Stop TX */
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_NO_OP, &ret);
+ if (ret)
+ goto tx_reg_err;
+
+ cec_transmit_attempt_done(ao_cec->adap, tx_status);
+ return;
+
+tx_reg_err:
+ cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_ERROR);
+}
+
+static void meson_ao_cec_irq_rx(struct meson_ao_cec_device *ao_cec)
+{
+ int i, ret = 0;
+ u8 reg;
+
+ meson_ao_cec_read(ao_cec, CEC_RX_MSG_STATUS, &reg, &ret);
+ if (reg != RX_DONE)
+ goto rx_out;
+
+ meson_ao_cec_read(ao_cec, CEC_RX_NUM_MSG, &reg, &ret);
+ if (reg != 1)
+ goto rx_out;
+
+ meson_ao_cec_read(ao_cec, CEC_RX_MSG_LENGTH, &reg, &ret);
+
+ ao_cec->rx_msg.len = reg + 1;
+ if (ao_cec->rx_msg.len > CEC_MAX_MSG_SIZE)
+ ao_cec->rx_msg.len = CEC_MAX_MSG_SIZE;
+
+ for (i = 0; i < ao_cec->rx_msg.len; i++) {
+ u8 byte;
+
+ meson_ao_cec_read(ao_cec, CEC_RX_MSG_0_HEADER + i, &byte, &ret);
+
+ ao_cec->rx_msg.msg[i] = byte;
+ }
+
+ if (ret)
+ goto rx_out;
+
+ cec_received_msg(ao_cec->adap, &ao_cec->rx_msg);
+
+rx_out:
+ /* Clear Interruption */
+ writel_relaxed(CEC_INTR_RX, ao_cec->base + CEC_INTR_CLR_REG);
+
+ /* Ack RX message */
+ meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_ACK_CURRENT, &ret);
+ meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_NO_OP, &ret);
+
+ /* Clear RX buffer */
+ meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, CLEAR_START, &ret);
+ meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, CLEAR_STOP, &ret);
+}
+
+static irqreturn_t meson_ao_cec_irq_thread(int irq, void *data)
+{
+ struct meson_ao_cec_device *ao_cec = data;
+ u32 stat = readl_relaxed(ao_cec->base + CEC_INTR_STAT_REG);
+
+ if (stat & CEC_INTR_TX)
+ meson_ao_cec_irq_tx(ao_cec);
+
+ meson_ao_cec_irq_rx(ao_cec);
+
+ return IRQ_HANDLED;
+}
+
+static int meson_ao_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct meson_ao_cec_device *ao_cec = adap->priv;
+ int ret = 0;
+
+ meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0,
+ LOGICAL_ADDR_DISABLE, &ret);
+ if (ret)
+ return ret;
+
+ ret = meson_ao_cec_clear(ao_cec);
+ if (ret)
+ return ret;
+
+ if (logical_addr == CEC_LOG_ADDR_INVALID)
+ return 0;
+
+ meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0,
+ logical_addr & LOGICAL_ADDR_MASK, &ret);
+ if (ret)
+ return ret;
+
+ udelay(100);
+
+ meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0,
+ (logical_addr & LOGICAL_ADDR_MASK) |
+ LOGICAL_ADDR_VALID, &ret);
+
+ return ret;
+}
+
+static int meson_ao_cec_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct meson_ao_cec_device *ao_cec = adap->priv;
+ int i, ret = 0;
+ u8 reg;
+
+ meson_ao_cec_read(ao_cec, CEC_TX_MSG_STATUS, &reg, &ret);
+ if (ret)
+ return ret;
+
+ if (reg == TX_BUSY) {
+ dev_dbg(&ao_cec->pdev->dev, "%s: busy TX: aborting\n",
+ __func__);
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT, &ret);
+ }
+
+ for (i = 0; i < msg->len; i++) {
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_0_HEADER + i,
+ msg->msg[i], &ret);
+ }
+
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_LENGTH, msg->len - 1, &ret);
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_REQ_CURRENT, &ret);
+
+ return ret;
+}
+
+static int meson_ao_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct meson_ao_cec_device *ao_cec = adap->priv;
+ int ret;
+
+ meson_ao_cec_irq_setup(ao_cec, false);
+
+ writel_bits_relaxed(CEC_GEN_CNTL_RESET, CEC_GEN_CNTL_RESET,
+ ao_cec->base + CEC_GEN_CNTL_REG);
+
+ if (!enable)
+ return 0;
+
+ /* Enable gated clock (Normal mode). */
+ writel_bits_relaxed(CEC_GEN_CNTL_CLK_CTRL_MASK,
+ FIELD_PREP(CEC_GEN_CNTL_CLK_CTRL_MASK,
+ CEC_GEN_CNTL_CLK_ENABLE),
+ ao_cec->base + CEC_GEN_CNTL_REG);
+
+ udelay(100);
+
+ /* Release Reset */
+ writel_bits_relaxed(CEC_GEN_CNTL_RESET, 0,
+ ao_cec->base + CEC_GEN_CNTL_REG);
+
+ /* Clear buffers */
+ ret = meson_ao_cec_clear(ao_cec);
+ if (ret)
+ return ret;
+
+ /* CEC arbitration 3/5/7 bit time set. */
+ ret = meson_ao_cec_arbit_bit_time_set(ao_cec,
+ CEC_SIGNAL_FREE_TIME_RETRY,
+ 0x118);
+ if (ret)
+ return ret;
+ ret = meson_ao_cec_arbit_bit_time_set(ao_cec,
+ CEC_SIGNAL_FREE_TIME_NEW_INITIATOR,
+ 0x000);
+ if (ret)
+ return ret;
+ ret = meson_ao_cec_arbit_bit_time_set(ao_cec,
+ CEC_SIGNAL_FREE_TIME_NEXT_XFER,
+ 0x2aa);
+ if (ret)
+ return ret;
+
+ meson_ao_cec_irq_setup(ao_cec, true);
+
+ return 0;
+}
+
+static const struct cec_adap_ops meson_ao_cec_ops = {
+ .adap_enable = meson_ao_cec_adap_enable,
+ .adap_log_addr = meson_ao_cec_set_log_addr,
+ .adap_transmit = meson_ao_cec_transmit,
+};
+
+static int meson_ao_cec_probe(struct platform_device *pdev)
+{
+ struct meson_ao_cec_device *ao_cec;
+ struct device *hdmi_dev;
+ int ret, irq;
+
+ hdmi_dev = cec_notifier_parse_hdmi_phandle(&pdev->dev);
+
+ if (IS_ERR(hdmi_dev))
+ return PTR_ERR(hdmi_dev);
+
+ ao_cec = devm_kzalloc(&pdev->dev, sizeof(*ao_cec), GFP_KERNEL);
+ if (!ao_cec)
+ return -ENOMEM;
+
+ spin_lock_init(&ao_cec->cec_reg_lock);
+
+ ao_cec->adap = cec_allocate_adapter(&meson_ao_cec_ops, ao_cec,
+ "meson_ao_cec",
+ CEC_CAP_DEFAULTS |
+ CEC_CAP_CONNECTOR_INFO,
+ 1); /* Use 1 for now */
+ if (IS_ERR(ao_cec->adap))
+ return PTR_ERR(ao_cec->adap);
+
+ ao_cec->adap->owner = THIS_MODULE;
+
+ ao_cec->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ao_cec->base)) {
+ ret = PTR_ERR(ao_cec->base);
+ goto out_probe_adapter;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_threaded_irq(&pdev->dev, irq,
+ meson_ao_cec_irq,
+ meson_ao_cec_irq_thread,
+ 0, NULL, ao_cec);
+ if (ret) {
+ dev_err(&pdev->dev, "irq request failed\n");
+ goto out_probe_adapter;
+ }
+
+ ao_cec->core = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(ao_cec->core)) {
+ dev_err(&pdev->dev, "core clock request failed\n");
+ ret = PTR_ERR(ao_cec->core);
+ goto out_probe_adapter;
+ }
+
+ ret = clk_prepare_enable(ao_cec->core);
+ if (ret) {
+ dev_err(&pdev->dev, "core clock enable failed\n");
+ goto out_probe_adapter;
+ }
+
+ ret = clk_set_rate(ao_cec->core, CEC_CLK_RATE);
+ if (ret) {
+ dev_err(&pdev->dev, "core clock set rate failed\n");
+ goto out_probe_clk;
+ }
+
+ device_reset_optional(&pdev->dev);
+
+ ao_cec->pdev = pdev;
+ platform_set_drvdata(pdev, ao_cec);
+
+ ao_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, NULL,
+ ao_cec->adap);
+ if (!ao_cec->notify) {
+ ret = -ENOMEM;
+ goto out_probe_clk;
+ }
+
+ ret = cec_register_adapter(ao_cec->adap, &pdev->dev);
+ if (ret < 0)
+ goto out_probe_notify;
+
+ /* Setup Hardware */
+ writel_relaxed(CEC_GEN_CNTL_RESET,
+ ao_cec->base + CEC_GEN_CNTL_REG);
+
+ return 0;
+
+out_probe_notify:
+ cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap);
+
+out_probe_clk:
+ clk_disable_unprepare(ao_cec->core);
+
+out_probe_adapter:
+ cec_delete_adapter(ao_cec->adap);
+
+ dev_err(&pdev->dev, "CEC controller registration failed\n");
+
+ return ret;
+}
+
+static void meson_ao_cec_remove(struct platform_device *pdev)
+{
+ struct meson_ao_cec_device *ao_cec = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(ao_cec->core);
+
+ cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap);
+ cec_unregister_adapter(ao_cec->adap);
+}
+
+static const struct of_device_id meson_ao_cec_of_match[] = {
+ { .compatible = "amlogic,meson-gx-ao-cec", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, meson_ao_cec_of_match);
+
+static struct platform_driver meson_ao_cec_driver = {
+ .probe = meson_ao_cec_probe,
+ .remove = meson_ao_cec_remove,
+ .driver = {
+ .name = "meson-ao-cec",
+ .of_match_table = meson_ao_cec_of_match,
+ },
+};
+
+module_platform_driver(meson_ao_cec_driver);
+
+MODULE_DESCRIPTION("Meson AO CEC Controller driver");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/cec/platform/s5p/Makefile b/drivers/media/cec/platform/s5p/Makefile
new file mode 100644
index 000000000000..92bf7b8557c5
--- /dev/null
+++ b/drivers/media/cec/platform/s5p/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_CEC_SAMSUNG_S5P) += s5p-cec.o
+s5p-cec-y += s5p_cec.o exynos_hdmi_cecctrl.o
diff --git a/drivers/media/cec/platform/s5p/exynos_hdmi_cec.h b/drivers/media/cec/platform/s5p/exynos_hdmi_cec.h
new file mode 100644
index 000000000000..325db8c432bd
--- /dev/null
+++ b/drivers/media/cec/platform/s5p/exynos_hdmi_cec.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* drivers/media/platform/s5p-cec/exynos_hdmi_cec.h
+ *
+ * Copyright (c) 2010, 2014 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * Header file for interface of Samsung Exynos hdmi cec hardware
+ */
+
+#ifndef _EXYNOS_HDMI_CEC_H_
+#define _EXYNOS_HDMI_CEC_H_ __FILE__
+
+#include <linux/regmap.h>
+#include "s5p_cec.h"
+
+void s5p_cec_set_divider(struct s5p_cec_dev *cec);
+void s5p_cec_enable_rx(struct s5p_cec_dev *cec);
+void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec);
+void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec);
+void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec);
+void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec);
+void s5p_cec_reset(struct s5p_cec_dev *cec);
+void s5p_cec_tx_reset(struct s5p_cec_dev *cec);
+void s5p_cec_rx_reset(struct s5p_cec_dev *cec);
+void s5p_cec_threshold(struct s5p_cec_dev *cec);
+void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data,
+ size_t count, u8 retries);
+void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr);
+u32 s5p_cec_get_status(struct s5p_cec_dev *cec);
+void s5p_clr_pending_tx(struct s5p_cec_dev *cec);
+void s5p_clr_pending_rx(struct s5p_cec_dev *cec);
+void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer);
+
+#endif /* _EXYNOS_HDMI_CEC_H_ */
diff --git a/drivers/media/cec/platform/s5p/exynos_hdmi_cecctrl.c b/drivers/media/cec/platform/s5p/exynos_hdmi_cecctrl.c
new file mode 100644
index 000000000000..eb981ebce362
--- /dev/null
+++ b/drivers/media/cec/platform/s5p/exynos_hdmi_cecctrl.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c
+ *
+ * Copyright (c) 2009, 2014 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * cec ftn file for Samsung TVOUT driver
+ */
+
+#include <linux/io.h>
+#include <linux/device.h>
+
+#include "exynos_hdmi_cec.h"
+#include "regs-cec.h"
+
+#define S5P_HDMI_FIN 24000000
+#define CEC_DIV_RATIO 320000
+
+#define CEC_MESSAGE_BROADCAST_MASK 0x0F
+#define CEC_MESSAGE_BROADCAST 0x0F
+#define CEC_FILTER_THRESHOLD 0x15
+
+void s5p_cec_set_divider(struct s5p_cec_dev *cec)
+{
+ u32 div_ratio, div_val;
+ unsigned int reg;
+
+ div_ratio = S5P_HDMI_FIN / CEC_DIV_RATIO - 1;
+
+ if (regmap_read(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, &reg)) {
+ dev_err(cec->dev, "failed to read phy control\n");
+ return;
+ }
+
+ reg = (reg & ~(0x3FF << 16)) | (div_ratio << 16);
+
+ if (regmap_write(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, reg)) {
+ dev_err(cec->dev, "failed to write phy control\n");
+ return;
+ }
+
+ div_val = CEC_DIV_RATIO * 0.00005 - 1;
+
+ writeb(0x0, cec->reg + S5P_CEC_DIVISOR_3);
+ writeb(0x0, cec->reg + S5P_CEC_DIVISOR_2);
+ writeb(0x0, cec->reg + S5P_CEC_DIVISOR_1);
+ writeb(div_val, cec->reg + S5P_CEC_DIVISOR_0);
+}
+
+void s5p_cec_enable_rx(struct s5p_cec_dev *cec)
+{
+ u8 reg;
+
+ reg = readb(cec->reg + S5P_CEC_RX_CTRL);
+ reg |= S5P_CEC_RX_CTRL_ENABLE;
+ writeb(reg, cec->reg + S5P_CEC_RX_CTRL);
+}
+
+void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec)
+{
+ u8 reg;
+
+ reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
+ reg |= S5P_CEC_IRQ_RX_DONE;
+ reg |= S5P_CEC_IRQ_RX_ERROR;
+ writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
+}
+
+void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec)
+{
+ u8 reg;
+
+ reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
+ reg &= ~S5P_CEC_IRQ_RX_DONE;
+ reg &= ~S5P_CEC_IRQ_RX_ERROR;
+ writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
+}
+
+void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec)
+{
+ u8 reg;
+
+ reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
+ reg |= S5P_CEC_IRQ_TX_DONE;
+ reg |= S5P_CEC_IRQ_TX_ERROR;
+ writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
+}
+
+void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec)
+{
+ u8 reg;
+
+ reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
+ reg &= ~S5P_CEC_IRQ_TX_DONE;
+ reg &= ~S5P_CEC_IRQ_TX_ERROR;
+ writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
+}
+
+void s5p_cec_reset(struct s5p_cec_dev *cec)
+{
+ u8 reg;
+
+ writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL);
+ writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL);
+
+ reg = readb(cec->reg + 0xc4);
+ reg &= ~0x1;
+ writeb(reg, cec->reg + 0xc4);
+}
+
+void s5p_cec_tx_reset(struct s5p_cec_dev *cec)
+{
+ writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL);
+}
+
+void s5p_cec_rx_reset(struct s5p_cec_dev *cec)
+{
+ u8 reg;
+
+ writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL);
+
+ reg = readb(cec->reg + 0xc4);
+ reg &= ~0x1;
+ writeb(reg, cec->reg + 0xc4);
+}
+
+void s5p_cec_threshold(struct s5p_cec_dev *cec)
+{
+ writeb(CEC_FILTER_THRESHOLD, cec->reg + S5P_CEC_RX_FILTER_TH);
+ writeb(0, cec->reg + S5P_CEC_RX_FILTER_CTRL);
+}
+
+void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data,
+ size_t count, u8 retries)
+{
+ int i = 0;
+ u8 reg;
+
+ while (i < count) {
+ writeb(data[i], cec->reg + (S5P_CEC_TX_BUFF0 + (i * 4)));
+ i++;
+ }
+
+ writeb(count, cec->reg + S5P_CEC_TX_BYTES);
+ reg = readb(cec->reg + S5P_CEC_TX_CTRL);
+ reg |= S5P_CEC_TX_CTRL_START;
+ reg &= ~0x70;
+ reg |= retries << 4;
+
+ if ((data[0] & CEC_MESSAGE_BROADCAST_MASK) == CEC_MESSAGE_BROADCAST) {
+ dev_dbg(cec->dev, "Broadcast");
+ reg |= S5P_CEC_TX_CTRL_BCAST;
+ } else {
+ dev_dbg(cec->dev, "No Broadcast");
+ reg &= ~S5P_CEC_TX_CTRL_BCAST;
+ }
+
+ writeb(reg, cec->reg + S5P_CEC_TX_CTRL);
+ dev_dbg(cec->dev, "cec-tx: cec count (%zu): %*ph", count,
+ (int)count, data);
+}
+
+void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr)
+{
+ writeb(addr & 0x0F, cec->reg + S5P_CEC_LOGIC_ADDR);
+}
+
+u32 s5p_cec_get_status(struct s5p_cec_dev *cec)
+{
+ u32 status = 0;
+
+ status = readb(cec->reg + S5P_CEC_STATUS_0) & 0xf;
+ status |= (readb(cec->reg + S5P_CEC_TX_STAT1) & 0xf) << 4;
+ status |= readb(cec->reg + S5P_CEC_STATUS_1) << 8;
+ status |= readb(cec->reg + S5P_CEC_STATUS_2) << 16;
+ status |= readb(cec->reg + S5P_CEC_STATUS_3) << 24;
+
+ dev_dbg(cec->dev, "status = 0x%x!\n", status);
+
+ return status;
+}
+
+void s5p_clr_pending_tx(struct s5p_cec_dev *cec)
+{
+ writeb(S5P_CEC_IRQ_TX_DONE | S5P_CEC_IRQ_TX_ERROR,
+ cec->reg + S5P_CEC_IRQ_CLEAR);
+}
+
+void s5p_clr_pending_rx(struct s5p_cec_dev *cec)
+{
+ writeb(S5P_CEC_IRQ_RX_DONE | S5P_CEC_IRQ_RX_ERROR,
+ cec->reg + S5P_CEC_IRQ_CLEAR);
+}
+
+void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer)
+{
+ u32 i = 0;
+ char debug[40];
+
+ while (i < size) {
+ buffer[i] = readb(cec->reg + S5P_CEC_RX_BUFF0 + (i * 4));
+ sprintf(debug + i * 2, "%02x ", buffer[i]);
+ i++;
+ }
+ dev_dbg(cec->dev, "cec-rx: cec size(%d): %s", size, debug);
+}
diff --git a/drivers/media/cec/platform/s5p/regs-cec.h b/drivers/media/cec/platform/s5p/regs-cec.h
new file mode 100644
index 000000000000..447f717028a2
--- /dev/null
+++ b/drivers/media/cec/platform/s5p/regs-cec.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* drivers/media/platform/s5p-cec/regs-cec.h
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * register header file for Samsung TVOUT driver
+ */
+
+#ifndef __EXYNOS_REGS__H
+#define __EXYNOS_REGS__H
+
+/*
+ * Register part
+ */
+#define S5P_CEC_STATUS_0 (0x0000)
+#define S5P_CEC_STATUS_1 (0x0004)
+#define S5P_CEC_STATUS_2 (0x0008)
+#define S5P_CEC_STATUS_3 (0x000C)
+#define S5P_CEC_IRQ_MASK (0x0010)
+#define S5P_CEC_IRQ_CLEAR (0x0014)
+#define S5P_CEC_LOGIC_ADDR (0x0020)
+#define S5P_CEC_DIVISOR_0 (0x0030)
+#define S5P_CEC_DIVISOR_1 (0x0034)
+#define S5P_CEC_DIVISOR_2 (0x0038)
+#define S5P_CEC_DIVISOR_3 (0x003C)
+
+#define S5P_CEC_TX_CTRL (0x0040)
+#define S5P_CEC_TX_BYTES (0x0044)
+#define S5P_CEC_TX_STAT0 (0x0060)
+#define S5P_CEC_TX_STAT1 (0x0064)
+#define S5P_CEC_TX_BUFF0 (0x0080)
+#define S5P_CEC_TX_BUFF1 (0x0084)
+#define S5P_CEC_TX_BUFF2 (0x0088)
+#define S5P_CEC_TX_BUFF3 (0x008C)
+#define S5P_CEC_TX_BUFF4 (0x0090)
+#define S5P_CEC_TX_BUFF5 (0x0094)
+#define S5P_CEC_TX_BUFF6 (0x0098)
+#define S5P_CEC_TX_BUFF7 (0x009C)
+#define S5P_CEC_TX_BUFF8 (0x00A0)
+#define S5P_CEC_TX_BUFF9 (0x00A4)
+#define S5P_CEC_TX_BUFF10 (0x00A8)
+#define S5P_CEC_TX_BUFF11 (0x00AC)
+#define S5P_CEC_TX_BUFF12 (0x00B0)
+#define S5P_CEC_TX_BUFF13 (0x00B4)
+#define S5P_CEC_TX_BUFF14 (0x00B8)
+#define S5P_CEC_TX_BUFF15 (0x00BC)
+
+#define S5P_CEC_RX_CTRL (0x00C0)
+#define S5P_CEC_RX_STAT0 (0x00E0)
+#define S5P_CEC_RX_STAT1 (0x00E4)
+#define S5P_CEC_RX_BUFF0 (0x0100)
+#define S5P_CEC_RX_BUFF1 (0x0104)
+#define S5P_CEC_RX_BUFF2 (0x0108)
+#define S5P_CEC_RX_BUFF3 (0x010C)
+#define S5P_CEC_RX_BUFF4 (0x0110)
+#define S5P_CEC_RX_BUFF5 (0x0114)
+#define S5P_CEC_RX_BUFF6 (0x0118)
+#define S5P_CEC_RX_BUFF7 (0x011C)
+#define S5P_CEC_RX_BUFF8 (0x0120)
+#define S5P_CEC_RX_BUFF9 (0x0124)
+#define S5P_CEC_RX_BUFF10 (0x0128)
+#define S5P_CEC_RX_BUFF11 (0x012C)
+#define S5P_CEC_RX_BUFF12 (0x0130)
+#define S5P_CEC_RX_BUFF13 (0x0134)
+#define S5P_CEC_RX_BUFF14 (0x0138)
+#define S5P_CEC_RX_BUFF15 (0x013C)
+
+#define S5P_CEC_RX_FILTER_CTRL (0x0180)
+#define S5P_CEC_RX_FILTER_TH (0x0184)
+
+/*
+ * Bit definition part
+ */
+#define S5P_CEC_IRQ_TX_DONE (1<<0)
+#define S5P_CEC_IRQ_TX_ERROR (1<<1)
+#define S5P_CEC_IRQ_RX_DONE (1<<4)
+#define S5P_CEC_IRQ_RX_ERROR (1<<5)
+
+#define S5P_CEC_TX_CTRL_START (1<<0)
+#define S5P_CEC_TX_CTRL_BCAST (1<<1)
+#define S5P_CEC_TX_CTRL_RETRY (0x04<<4)
+#define S5P_CEC_TX_CTRL_RESET (1<<7)
+
+#define S5P_CEC_RX_CTRL_ENABLE (1<<0)
+#define S5P_CEC_RX_CTRL_RESET (1<<7)
+
+#define S5P_CEC_LOGIC_ADDR_MASK (0xF)
+
+/* PMU Registers for PHY */
+#define EXYNOS_HDMI_PHY_CONTROL 0x700
+
+#endif /* __EXYNOS_REGS__H */
diff --git a/drivers/media/cec/platform/s5p/s5p_cec.c b/drivers/media/cec/platform/s5p/s5p_cec.c
new file mode 100644
index 000000000000..4a92d3230f66
--- /dev/null
+++ b/drivers/media/cec/platform/s5p/s5p_cec.c
@@ -0,0 +1,309 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* drivers/media/platform/s5p-cec/s5p_cec.c
+ *
+ * Samsung S5P CEC driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * This driver is based on the "cec interface driver for exynos soc" by
+ * SangPil Moon.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <media/cec.h>
+#include <media/cec-notifier.h>
+
+#include "exynos_hdmi_cec.h"
+#include "regs-cec.h"
+#include "s5p_cec.h"
+
+#define CEC_NAME "s5p-cec"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+
+static int s5p_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ int ret;
+ struct s5p_cec_dev *cec = cec_get_drvdata(adap);
+
+ if (enable) {
+ ret = pm_runtime_resume_and_get(cec->dev);
+ if (ret < 0)
+ return ret;
+
+ s5p_cec_reset(cec);
+
+ s5p_cec_set_divider(cec);
+ s5p_cec_threshold(cec);
+
+ s5p_cec_unmask_tx_interrupts(cec);
+ s5p_cec_unmask_rx_interrupts(cec);
+ s5p_cec_enable_rx(cec);
+ } else {
+ s5p_cec_mask_tx_interrupts(cec);
+ s5p_cec_mask_rx_interrupts(cec);
+ pm_runtime_put(cec->dev);
+ }
+
+ return 0;
+}
+
+static int s5p_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+ struct s5p_cec_dev *cec = cec_get_drvdata(adap);
+
+ s5p_cec_set_addr(cec, addr);
+ return 0;
+}
+
+static int s5p_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct s5p_cec_dev *cec = cec_get_drvdata(adap);
+
+ /*
+ * Unclear if 0 retries are allowed by the hardware, so have 1 as
+ * the minimum.
+ */
+ s5p_cec_copy_packet(cec, msg->msg, msg->len, max(1, attempts - 1));
+ return 0;
+}
+
+static irqreturn_t s5p_cec_irq_handler(int irq, void *priv)
+{
+ struct s5p_cec_dev *cec = priv;
+ u32 status = 0;
+
+ status = s5p_cec_get_status(cec);
+
+ dev_dbg(cec->dev, "irq received\n");
+
+ if (status & CEC_STATUS_TX_DONE) {
+ if (status & CEC_STATUS_TX_NACK) {
+ dev_dbg(cec->dev, "CEC_STATUS_TX_NACK set\n");
+ cec->tx = STATE_NACK;
+ } else if (status & CEC_STATUS_TX_ERROR) {
+ dev_dbg(cec->dev, "CEC_STATUS_TX_ERROR set\n");
+ cec->tx = STATE_ERROR;
+ } else {
+ dev_dbg(cec->dev, "CEC_STATUS_TX_DONE\n");
+ cec->tx = STATE_DONE;
+ }
+ s5p_clr_pending_tx(cec);
+ }
+
+ if (status & CEC_STATUS_RX_DONE) {
+ if (status & CEC_STATUS_RX_ERROR) {
+ dev_dbg(cec->dev, "CEC_STATUS_RX_ERROR set\n");
+ s5p_cec_rx_reset(cec);
+ s5p_cec_enable_rx(cec);
+ } else {
+ dev_dbg(cec->dev, "CEC_STATUS_RX_DONE set\n");
+ if (cec->rx != STATE_IDLE)
+ dev_dbg(cec->dev, "Buffer overrun (worker did not process previous message)\n");
+ cec->rx = STATE_BUSY;
+ cec->msg.len = status >> 24;
+ if (cec->msg.len > CEC_MAX_MSG_SIZE)
+ cec->msg.len = CEC_MAX_MSG_SIZE;
+ cec->msg.rx_status = CEC_RX_STATUS_OK;
+ s5p_cec_get_rx_buf(cec, cec->msg.len,
+ cec->msg.msg);
+ cec->rx = STATE_DONE;
+ s5p_cec_enable_rx(cec);
+ }
+ /* Clear interrupt pending bit */
+ s5p_clr_pending_rx(cec);
+ }
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t s5p_cec_irq_handler_thread(int irq, void *priv)
+{
+ struct s5p_cec_dev *cec = priv;
+
+ dev_dbg(cec->dev, "irq processing thread\n");
+ switch (cec->tx) {
+ case STATE_DONE:
+ cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
+ cec->tx = STATE_IDLE;
+ break;
+ case STATE_NACK:
+ cec_transmit_done(cec->adap,
+ CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_NACK,
+ 0, 1, 0, 0);
+ cec->tx = STATE_IDLE;
+ break;
+ case STATE_ERROR:
+ cec_transmit_done(cec->adap,
+ CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_ERROR,
+ 0, 0, 0, 1);
+ cec->tx = STATE_IDLE;
+ break;
+ case STATE_BUSY:
+ dev_err(cec->dev, "state set to busy, this should not occur here\n");
+ break;
+ default:
+ break;
+ }
+
+ switch (cec->rx) {
+ case STATE_DONE:
+ cec_received_msg(cec->adap, &cec->msg);
+ cec->rx = STATE_IDLE;
+ break;
+ default:
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct cec_adap_ops s5p_cec_adap_ops = {
+ .adap_enable = s5p_cec_adap_enable,
+ .adap_log_addr = s5p_cec_adap_log_addr,
+ .adap_transmit = s5p_cec_adap_transmit,
+};
+
+static int s5p_cec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *hdmi_dev;
+ struct s5p_cec_dev *cec;
+ bool needs_hpd = of_property_read_bool(pdev->dev.of_node, "needs-hpd");
+ int ret;
+
+ hdmi_dev = cec_notifier_parse_hdmi_phandle(dev);
+
+ if (IS_ERR(hdmi_dev))
+ return PTR_ERR(hdmi_dev);
+
+ cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
+ if (!cec)
+ return -ENOMEM;
+
+ cec->dev = dev;
+
+ cec->irq = platform_get_irq(pdev, 0);
+ if (cec->irq < 0)
+ return cec->irq;
+
+ ret = devm_request_threaded_irq(dev, cec->irq, s5p_cec_irq_handler,
+ s5p_cec_irq_handler_thread, 0, pdev->name, cec);
+ if (ret)
+ return ret;
+
+ cec->clk = devm_clk_get(dev, "hdmicec");
+ if (IS_ERR(cec->clk))
+ return PTR_ERR(cec->clk);
+
+ cec->pmu = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "samsung,syscon-phandle");
+ if (IS_ERR(cec->pmu))
+ return -EPROBE_DEFER;
+
+ cec->reg = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(cec->reg))
+ return PTR_ERR(cec->reg);
+
+ cec->adap = cec_allocate_adapter(&s5p_cec_adap_ops, cec, CEC_NAME,
+ CEC_CAP_DEFAULTS | (needs_hpd ? CEC_CAP_NEEDS_HPD : 0) |
+ CEC_CAP_CONNECTOR_INFO, 1);
+ ret = PTR_ERR_OR_ZERO(cec->adap);
+ if (ret)
+ return ret;
+
+ cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL,
+ cec->adap);
+ if (!cec->notifier) {
+ ret = -ENOMEM;
+ goto err_delete_adapter;
+ }
+
+ ret = cec_register_adapter(cec->adap, &pdev->dev);
+ if (ret)
+ goto err_notifier;
+
+ platform_set_drvdata(pdev, cec);
+ pm_runtime_enable(dev);
+
+ dev_dbg(dev, "successfully probed\n");
+ return 0;
+
+err_notifier:
+ cec_notifier_cec_adap_unregister(cec->notifier, cec->adap);
+
+err_delete_adapter:
+ cec_delete_adapter(cec->adap);
+ return ret;
+}
+
+static void s5p_cec_remove(struct platform_device *pdev)
+{
+ struct s5p_cec_dev *cec = platform_get_drvdata(pdev);
+
+ cec_notifier_cec_adap_unregister(cec->notifier, cec->adap);
+ cec_unregister_adapter(cec->adap);
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int __maybe_unused s5p_cec_runtime_suspend(struct device *dev)
+{
+ struct s5p_cec_dev *cec = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(cec->clk);
+ return 0;
+}
+
+static int __maybe_unused s5p_cec_runtime_resume(struct device *dev)
+{
+ struct s5p_cec_dev *cec = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(cec->clk);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static const struct dev_pm_ops s5p_cec_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(s5p_cec_runtime_suspend, s5p_cec_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id s5p_cec_match[] = {
+ {
+ .compatible = "samsung,s5p-cec",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, s5p_cec_match);
+
+static struct platform_driver s5p_cec_pdrv = {
+ .probe = s5p_cec_probe,
+ .remove = s5p_cec_remove,
+ .driver = {
+ .name = CEC_NAME,
+ .of_match_table = s5p_cec_match,
+ .pm = &s5p_cec_pm_ops,
+ },
+};
+
+module_platform_driver(s5p_cec_pdrv);
+
+MODULE_AUTHOR("Kamil Debski <kamil@wypas.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Samsung S5P CEC driver");
diff --git a/drivers/media/cec/platform/s5p/s5p_cec.h b/drivers/media/cec/platform/s5p/s5p_cec.h
new file mode 100644
index 000000000000..34d033b20f96
--- /dev/null
+++ b/drivers/media/cec/platform/s5p/s5p_cec.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* drivers/media/platform/s5p-cec/s5p_cec.h
+ *
+ * Samsung S5P HDMI CEC driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ */
+
+#ifndef _S5P_CEC_H_
+#define _S5P_CEC_H_ __FILE__
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <media/cec.h>
+
+#include "exynos_hdmi_cec.h"
+#include "regs-cec.h"
+#include "s5p_cec.h"
+
+#define CEC_NAME "s5p-cec"
+
+#define CEC_STATUS_TX_RUNNING (1 << 0)
+#define CEC_STATUS_TX_TRANSFERRING (1 << 1)
+#define CEC_STATUS_TX_DONE (1 << 2)
+#define CEC_STATUS_TX_ERROR (1 << 3)
+#define CEC_STATUS_TX_NACK (1 << 4)
+#define CEC_STATUS_TX_BYTES (0xFF << 8)
+#define CEC_STATUS_RX_RUNNING (1 << 16)
+#define CEC_STATUS_RX_RECEIVING (1 << 17)
+#define CEC_STATUS_RX_DONE (1 << 18)
+#define CEC_STATUS_RX_ERROR (1 << 19)
+#define CEC_STATUS_RX_BCAST (1 << 20)
+#define CEC_STATUS_RX_BYTES (0xFF << 24)
+
+#define CEC_WORKER_TX_DONE (1 << 0)
+#define CEC_WORKER_RX_MSG (1 << 1)
+
+/* CEC Rx buffer size */
+#define CEC_RX_BUFF_SIZE 16
+/* CEC Tx buffer size */
+#define CEC_TX_BUFF_SIZE 16
+
+enum cec_state {
+ STATE_IDLE,
+ STATE_BUSY,
+ STATE_DONE,
+ STATE_NACK,
+ STATE_ERROR
+};
+
+struct cec_notifier;
+
+struct s5p_cec_dev {
+ struct cec_adapter *adap;
+ struct clk *clk;
+ struct device *dev;
+ struct mutex lock;
+ struct regmap *pmu;
+ struct cec_notifier *notifier;
+ int irq;
+ void __iomem *reg;
+
+ enum cec_state rx;
+ enum cec_state tx;
+ struct cec_msg msg;
+};
+
+#endif /* _S5P_CEC_H_ */
diff --git a/drivers/media/cec/platform/seco/Makefile b/drivers/media/cec/platform/seco/Makefile
new file mode 100644
index 000000000000..aa1ca8ccdb8b
--- /dev/null
+++ b/drivers/media/cec/platform/seco/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_CEC_SECO) += seco-cec.o
diff --git a/drivers/media/cec/platform/seco/seco-cec.c b/drivers/media/cec/platform/seco/seco-cec.c
new file mode 100644
index 000000000000..b7bb49f02395
--- /dev/null
+++ b/drivers/media/cec/platform/seco/seco-cec.c
@@ -0,0 +1,788 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * CEC driver for SECO X86 Boards
+ *
+ * Author: Ettore Chimenti <ek5.chimenti@gmail.com>
+ * Copyright (C) 2018, SECO SpA.
+ * Copyright (C) 2018, Aidilab Srl.
+ */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+/* CEC Framework */
+#include <media/cec-notifier.h>
+
+#include "seco-cec.h"
+
+struct secocec_data {
+ struct device *dev;
+ struct platform_device *pdev;
+ struct cec_adapter *cec_adap;
+ struct cec_notifier *notifier;
+ struct rc_dev *ir;
+ char ir_input_phys[32];
+ int irq;
+};
+
+#define smb_wr16(cmd, data) smb_word_op(SECOCEC_MICRO_ADDRESS, \
+ cmd, data, SMBUS_WRITE, NULL)
+#define smb_rd16(cmd, res) smb_word_op(SECOCEC_MICRO_ADDRESS, \
+ cmd, 0, SMBUS_READ, res)
+
+static int smb_word_op(u16 slave_addr, u8 cmd, u16 data,
+ u8 operation, u16 *result)
+{
+ unsigned int count;
+ int status = 0;
+
+ /* Active wait until ready */
+ for (count = 0; count <= SMBTIMEOUT; ++count) {
+ if (!(inb(HSTS) & BRA_INUSE_STS))
+ break;
+ udelay(SMB_POLL_UDELAY);
+ }
+
+ if (count > SMBTIMEOUT)
+ /* Reset the lock instead of failing */
+ outb(0xff, HSTS);
+
+ outb(0x00, HCNT);
+ outb((u8)(slave_addr & 0xfe) | operation, XMIT_SLVA);
+ outb(cmd, HCMD);
+ inb(HCNT);
+
+ if (operation == SMBUS_WRITE) {
+ outb((u8)data, HDAT0);
+ outb((u8)(data >> 8), HDAT1);
+ }
+
+ outb(BRA_START + BRA_SMB_CMD_WORD_DATA, HCNT);
+
+ for (count = 0; count <= SMBTIMEOUT; count++) {
+ if (!(inb(HSTS) & BRA_HOST_BUSY))
+ break;
+ udelay(SMB_POLL_UDELAY);
+ }
+
+ if (count > SMBTIMEOUT) {
+ status = -EBUSY;
+ goto err;
+ }
+
+ if (inb(HSTS) & BRA_HSTS_ERR_MASK) {
+ status = -EIO;
+ goto err;
+ }
+
+ if (operation == SMBUS_READ)
+ *result = ((inb(HDAT0) & 0xff) + ((inb(HDAT1) & 0xff) << 8));
+
+err:
+ outb(0xff, HSTS);
+ return status;
+}
+
+static int secocec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct secocec_data *cec = cec_get_drvdata(adap);
+ struct device *dev = cec->dev;
+ u16 val = 0;
+ int status;
+
+ if (enable) {
+ /* Clear the status register */
+ status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
+ if (status)
+ goto err;
+
+ status = smb_wr16(SECOCEC_STATUS_REG_1, val);
+ if (status)
+ goto err;
+
+ /* Enable the interrupts */
+ status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
+ if (status)
+ goto err;
+
+ status = smb_wr16(SECOCEC_ENABLE_REG_1,
+ val | SECOCEC_ENABLE_REG_1_CEC);
+ if (status)
+ goto err;
+
+ dev_dbg(dev, "Device enabled\n");
+ } else {
+ /* Clear the status register */
+ status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
+ status = smb_wr16(SECOCEC_STATUS_REG_1, val);
+
+ /* Disable the interrupts */
+ status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
+ status = smb_wr16(SECOCEC_ENABLE_REG_1, val &
+ ~SECOCEC_ENABLE_REG_1_CEC &
+ ~SECOCEC_ENABLE_REG_1_IR);
+
+ dev_dbg(dev, "Device disabled\n");
+ }
+
+ return 0;
+err:
+ return status;
+}
+
+static int secocec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ u16 enable_val = 0;
+ int status;
+
+ /* Disable device */
+ status = smb_rd16(SECOCEC_ENABLE_REG_1, &enable_val);
+ if (status)
+ return status;
+
+ status = smb_wr16(SECOCEC_ENABLE_REG_1,
+ enable_val & ~SECOCEC_ENABLE_REG_1_CEC);
+ if (status)
+ return status;
+
+ /* Write logical address
+ * NOTE: CEC_LOG_ADDR_INVALID is mapped to the 'Unregistered' LA
+ */
+ status = smb_wr16(SECOCEC_DEVICE_LA, logical_addr & 0xf);
+ if (status)
+ return status;
+
+ /* Re-enable device */
+ status = smb_wr16(SECOCEC_ENABLE_REG_1,
+ enable_val | SECOCEC_ENABLE_REG_1_CEC);
+ if (status)
+ return status;
+
+ return 0;
+}
+
+static int secocec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ u16 payload_len, payload_id_len, destination, val = 0;
+ u8 *payload_msg;
+ int status;
+ u8 i;
+
+ /* Device msg len already accounts for header */
+ payload_id_len = msg->len - 1;
+
+ /* Send data length */
+ status = smb_wr16(SECOCEC_WRITE_DATA_LENGTH, payload_id_len);
+ if (status)
+ goto err;
+
+ /* Send Operation ID if present */
+ if (payload_id_len > 0) {
+ status = smb_wr16(SECOCEC_WRITE_OPERATION_ID, msg->msg[1]);
+ if (status)
+ goto err;
+ }
+ /* Send data if present */
+ if (payload_id_len > 1) {
+ /* Only data; */
+ payload_len = msg->len - 2;
+ payload_msg = &msg->msg[2];
+
+ /* Copy message into registers */
+ for (i = 0; i < payload_len; i += 2) {
+ /* hi byte */
+ val = payload_msg[i + 1] << 8;
+
+ /* lo byte */
+ val |= payload_msg[i];
+
+ status = smb_wr16(SECOCEC_WRITE_DATA_00 + i / 2, val);
+ if (status)
+ goto err;
+ }
+ }
+ /* Send msg source/destination and fire msg */
+ destination = msg->msg[0];
+ status = smb_wr16(SECOCEC_WRITE_BYTE0, destination);
+ if (status)
+ goto err;
+
+ return 0;
+
+err:
+ return status;
+}
+
+static void secocec_tx_done(struct cec_adapter *adap, u16 status_val)
+{
+ if (status_val & SECOCEC_STATUS_TX_ERROR_MASK) {
+ if (status_val & SECOCEC_STATUS_TX_NACK_ERROR)
+ cec_transmit_attempt_done(adap, CEC_TX_STATUS_NACK);
+ else
+ cec_transmit_attempt_done(adap, CEC_TX_STATUS_ERROR);
+ } else {
+ cec_transmit_attempt_done(adap, CEC_TX_STATUS_OK);
+ }
+
+ /* Reset status reg */
+ status_val = SECOCEC_STATUS_TX_ERROR_MASK |
+ SECOCEC_STATUS_MSG_SENT_MASK |
+ SECOCEC_STATUS_TX_NACK_ERROR;
+ smb_wr16(SECOCEC_STATUS, status_val);
+}
+
+static void secocec_rx_done(struct cec_adapter *adap, u16 status_val)
+{
+ struct secocec_data *cec = cec_get_drvdata(adap);
+ struct device *dev = cec->dev;
+ struct cec_msg msg = { };
+ bool flag_overflow = false;
+ u8 payload_len, i = 0;
+ u8 *payload_msg;
+ u16 val = 0;
+ int status;
+
+ if (status_val & SECOCEC_STATUS_RX_OVERFLOW_MASK) {
+ /* NOTE: Untested, it also might not be necessary */
+ dev_warn(dev, "Received more than 16 bytes. Discarding\n");
+ flag_overflow = true;
+ }
+
+ if (status_val & SECOCEC_STATUS_RX_ERROR_MASK) {
+ dev_warn(dev, "Message received with errors. Discarding\n");
+ status = -EIO;
+ goto rxerr;
+ }
+
+ /* Read message length */
+ status = smb_rd16(SECOCEC_READ_DATA_LENGTH, &val);
+ if (status)
+ return;
+
+ /* Device msg len already accounts for the header */
+ msg.len = min(val + 1, CEC_MAX_MSG_SIZE);
+
+ /* Read logical address */
+ status = smb_rd16(SECOCEC_READ_BYTE0, &val);
+ if (status)
+ return;
+
+ /* device stores source LA and destination */
+ msg.msg[0] = val;
+
+ /* Read operation ID */
+ status = smb_rd16(SECOCEC_READ_OPERATION_ID, &val);
+ if (status)
+ return;
+
+ msg.msg[1] = val;
+
+ /* Read data if present */
+ if (msg.len > 1) {
+ payload_len = msg.len - 2;
+ payload_msg = &msg.msg[2];
+
+ /* device stores 2 bytes in every 16-bit val */
+ for (i = 0; i < payload_len; i += 2) {
+ status = smb_rd16(SECOCEC_READ_DATA_00 + i / 2, &val);
+ if (status)
+ return;
+
+ /* low byte, skipping header */
+ payload_msg[i] = val & 0x00ff;
+
+ /* hi byte */
+ payload_msg[i + 1] = (val & 0xff00) >> 8;
+ }
+ }
+
+ cec_received_msg(cec->cec_adap, &msg);
+
+ /* Reset status reg */
+ status_val = SECOCEC_STATUS_MSG_RECEIVED_MASK;
+ if (flag_overflow)
+ status_val |= SECOCEC_STATUS_RX_OVERFLOW_MASK;
+
+ status = smb_wr16(SECOCEC_STATUS, status_val);
+
+ return;
+
+rxerr:
+ /* Reset error reg */
+ status_val = SECOCEC_STATUS_MSG_RECEIVED_MASK |
+ SECOCEC_STATUS_RX_ERROR_MASK;
+ if (flag_overflow)
+ status_val |= SECOCEC_STATUS_RX_OVERFLOW_MASK;
+ smb_wr16(SECOCEC_STATUS, status_val);
+}
+
+static const struct cec_adap_ops secocec_cec_adap_ops = {
+ /* Low-level callbacks */
+ .adap_enable = secocec_adap_enable,
+ .adap_log_addr = secocec_adap_log_addr,
+ .adap_transmit = secocec_adap_transmit,
+};
+
+#ifdef CONFIG_CEC_SECO_RC
+static int secocec_ir_probe(void *priv)
+{
+ struct secocec_data *cec = priv;
+ struct device *dev = cec->dev;
+ int status;
+ u16 val;
+
+ /* Prepare the RC input device */
+ cec->ir = devm_rc_allocate_device(dev, RC_DRIVER_SCANCODE);
+ if (!cec->ir)
+ return -ENOMEM;
+
+ snprintf(cec->ir_input_phys, sizeof(cec->ir_input_phys),
+ "%s/input0", dev_name(dev));
+
+ cec->ir->device_name = dev_name(dev);
+ cec->ir->input_phys = cec->ir_input_phys;
+ cec->ir->input_id.bustype = BUS_HOST;
+ cec->ir->input_id.vendor = 0;
+ cec->ir->input_id.product = 0;
+ cec->ir->input_id.version = 1;
+ cec->ir->driver_name = SECOCEC_DEV_NAME;
+ cec->ir->allowed_protocols = RC_PROTO_BIT_RC5;
+ cec->ir->priv = cec;
+ cec->ir->map_name = RC_MAP_HAUPPAUGE;
+ cec->ir->timeout = MS_TO_US(100);
+
+ /* Clear the status register */
+ status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
+ if (status != 0)
+ goto err;
+
+ status = smb_wr16(SECOCEC_STATUS_REG_1, val);
+ if (status != 0)
+ goto err;
+
+ /* Enable the interrupts */
+ status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
+ if (status != 0)
+ goto err;
+
+ status = smb_wr16(SECOCEC_ENABLE_REG_1,
+ val | SECOCEC_ENABLE_REG_1_IR);
+ if (status != 0)
+ goto err;
+
+ dev_dbg(dev, "IR enabled\n");
+
+ status = devm_rc_register_device(dev, cec->ir);
+
+ if (status) {
+ dev_err(dev, "Failed to prepare input device\n");
+ cec->ir = NULL;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ smb_rd16(SECOCEC_ENABLE_REG_1, &val);
+
+ smb_wr16(SECOCEC_ENABLE_REG_1,
+ val & ~SECOCEC_ENABLE_REG_1_IR);
+
+ dev_dbg(dev, "IR disabled\n");
+ return status;
+}
+
+static int secocec_ir_rx(struct secocec_data *priv)
+{
+ struct secocec_data *cec = priv;
+ struct device *dev = cec->dev;
+ u16 val, status, key, addr, toggle;
+
+ if (!cec->ir)
+ return -ENODEV;
+
+ status = smb_rd16(SECOCEC_IR_READ_DATA, &val);
+ if (status != 0)
+ goto err;
+
+ key = val & SECOCEC_IR_COMMAND_MASK;
+ addr = (val & SECOCEC_IR_ADDRESS_MASK) >> SECOCEC_IR_ADDRESS_SHL;
+ toggle = (val & SECOCEC_IR_TOGGLE_MASK) >> SECOCEC_IR_TOGGLE_SHL;
+
+ rc_keydown(cec->ir, RC_PROTO_RC5, RC_SCANCODE_RC5(addr, key), toggle);
+
+ dev_dbg(dev, "IR key pressed: 0x%02x addr 0x%02x toggle 0x%02x\n", key,
+ addr, toggle);
+
+ return 0;
+
+err:
+ dev_err(dev, "IR Receive message failed (%d)\n", status);
+ return -EIO;
+}
+#else
+static void secocec_ir_rx(struct secocec_data *priv)
+{
+}
+
+static int secocec_ir_probe(void *priv)
+{
+ return 0;
+}
+#endif
+
+static irqreturn_t secocec_irq_handler(int irq, void *priv)
+{
+ struct secocec_data *cec = priv;
+ struct device *dev = cec->dev;
+ u16 status_val, cec_val, val = 0;
+ int status;
+
+ /* Read status register */
+ status = smb_rd16(SECOCEC_STATUS_REG_1, &status_val);
+ if (status)
+ goto err;
+
+ if (status_val & SECOCEC_STATUS_REG_1_CEC) {
+ /* Read CEC status register */
+ status = smb_rd16(SECOCEC_STATUS, &cec_val);
+ if (status)
+ goto err;
+
+ if (cec_val & SECOCEC_STATUS_MSG_RECEIVED_MASK)
+ secocec_rx_done(cec->cec_adap, cec_val);
+
+ if (cec_val & SECOCEC_STATUS_MSG_SENT_MASK)
+ secocec_tx_done(cec->cec_adap, cec_val);
+
+ if ((~cec_val & SECOCEC_STATUS_MSG_SENT_MASK) &&
+ (~cec_val & SECOCEC_STATUS_MSG_RECEIVED_MASK))
+ dev_warn_once(dev,
+ "Message not received or sent, but interrupt fired");
+
+ val = SECOCEC_STATUS_REG_1_CEC;
+ }
+
+ if (status_val & SECOCEC_STATUS_REG_1_IR) {
+ val |= SECOCEC_STATUS_REG_1_IR;
+
+ secocec_ir_rx(cec);
+ }
+
+ /* Reset status register */
+ status = smb_wr16(SECOCEC_STATUS_REG_1, val);
+ if (status)
+ goto err;
+
+ return IRQ_HANDLED;
+
+err:
+ dev_err_once(dev, "IRQ: R/W SMBus operation failed %d\n", status);
+
+ /* Reset status register */
+ val = SECOCEC_STATUS_REG_1_CEC | SECOCEC_STATUS_REG_1_IR;
+ smb_wr16(SECOCEC_STATUS_REG_1, val);
+
+ return IRQ_HANDLED;
+}
+
+struct cec_dmi_match {
+ const char *sys_vendor;
+ const char *product_name;
+ const char *devname;
+ const char *conn;
+};
+
+static const struct cec_dmi_match secocec_dmi_match_table[] = {
+ /* UDOO X86 */
+ { "SECO", "UDOO x86", "0000:00:02.0", "Port B" },
+};
+
+static struct device *secocec_cec_find_hdmi_dev(struct device *dev,
+ const char **conn)
+{
+ int i;
+
+ for (i = 0 ; i < ARRAY_SIZE(secocec_dmi_match_table) ; ++i) {
+ const struct cec_dmi_match *m = &secocec_dmi_match_table[i];
+
+ if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
+ dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
+ struct device *d;
+
+ /* Find the device, bail out if not yet registered */
+ d = bus_find_device_by_name(&pci_bus_type, NULL,
+ m->devname);
+ if (!d)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ put_device(d);
+ *conn = m->conn;
+ return d;
+ }
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+
+static int secocec_acpi_probe(struct secocec_data *sdev)
+{
+ struct device *dev = sdev->dev;
+ struct gpio_desc *gpio;
+ int irq = 0;
+
+ gpio = devm_gpiod_get(dev, NULL, GPIOD_IN);
+ if (IS_ERR(gpio)) {
+ dev_err(dev, "Cannot request interrupt gpio\n");
+ return PTR_ERR(gpio);
+ }
+
+ irq = gpiod_to_irq(gpio);
+ if (irq < 0) {
+ dev_err(dev, "Cannot find valid irq\n");
+ return -ENODEV;
+ }
+ dev_dbg(dev, "irq-gpio is bound to IRQ %d\n", irq);
+
+ sdev->irq = irq;
+
+ return 0;
+}
+
+static int secocec_probe(struct platform_device *pdev)
+{
+ struct secocec_data *secocec;
+ struct device *dev = &pdev->dev;
+ struct device *hdmi_dev;
+ const char *conn = NULL;
+ int ret;
+ u16 val;
+
+ hdmi_dev = secocec_cec_find_hdmi_dev(&pdev->dev, &conn);
+ if (IS_ERR(hdmi_dev))
+ return PTR_ERR(hdmi_dev);
+
+ secocec = devm_kzalloc(dev, sizeof(*secocec), GFP_KERNEL);
+ if (!secocec)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, secocec);
+
+ /* Request SMBus regions */
+ if (!request_muxed_region(BRA_SMB_BASE_ADDR, 7, "CEC00001")) {
+ dev_err(dev, "Request memory region failed\n");
+ return -ENXIO;
+ }
+
+ secocec->pdev = pdev;
+ secocec->dev = dev;
+
+ if (!has_acpi_companion(dev)) {
+ dev_dbg(dev, "Cannot find any ACPI companion\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = secocec_acpi_probe(secocec);
+ if (ret) {
+ dev_err(dev, "Cannot assign gpio to IRQ\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* Firmware version check */
+ ret = smb_rd16(SECOCEC_VERSION, &val);
+ if (ret) {
+ dev_err(dev, "Cannot check fw version\n");
+ goto err;
+ }
+ if (val < SECOCEC_LATEST_FW) {
+ dev_err(dev, "CEC Firmware not supported (v.%04x). Use ver > v.%04x\n",
+ val, SECOCEC_LATEST_FW);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = devm_request_threaded_irq(dev,
+ secocec->irq,
+ NULL,
+ secocec_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dev_name(&pdev->dev), secocec);
+
+ if (ret) {
+ dev_err(dev, "Cannot request IRQ %d\n", secocec->irq);
+ ret = -EIO;
+ goto err;
+ }
+
+ /* Allocate CEC adapter */
+ secocec->cec_adap = cec_allocate_adapter(&secocec_cec_adap_ops,
+ secocec,
+ dev_name(dev),
+ CEC_CAP_DEFAULTS |
+ CEC_CAP_CONNECTOR_INFO,
+ SECOCEC_MAX_ADDRS);
+
+ if (IS_ERR(secocec->cec_adap)) {
+ ret = PTR_ERR(secocec->cec_adap);
+ goto err;
+ }
+
+ secocec->notifier = cec_notifier_cec_adap_register(hdmi_dev, conn,
+ secocec->cec_adap);
+ if (!secocec->notifier) {
+ ret = -ENOMEM;
+ goto err_delete_adapter;
+ }
+
+ ret = cec_register_adapter(secocec->cec_adap, dev);
+ if (ret)
+ goto err_notifier;
+
+ ret = secocec_ir_probe(secocec);
+ if (ret)
+ goto err_notifier;
+
+ platform_set_drvdata(pdev, secocec);
+
+ dev_dbg(dev, "Device registered\n");
+
+ return ret;
+
+err_notifier:
+ cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
+err_delete_adapter:
+ cec_delete_adapter(secocec->cec_adap);
+err:
+ release_region(BRA_SMB_BASE_ADDR, 7);
+ dev_err(dev, "%s device probe failed\n", dev_name(dev));
+
+ return ret;
+}
+
+static void secocec_remove(struct platform_device *pdev)
+{
+ struct secocec_data *secocec = platform_get_drvdata(pdev);
+ u16 val;
+
+ if (secocec->ir) {
+ smb_rd16(SECOCEC_ENABLE_REG_1, &val);
+
+ smb_wr16(SECOCEC_ENABLE_REG_1, val & ~SECOCEC_ENABLE_REG_1_IR);
+
+ dev_dbg(&pdev->dev, "IR disabled\n");
+ }
+ cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
+ cec_unregister_adapter(secocec->cec_adap);
+
+ release_region(BRA_SMB_BASE_ADDR, 7);
+
+ dev_dbg(&pdev->dev, "CEC device removed\n");
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int secocec_suspend(struct device *dev)
+{
+ int status;
+ u16 val;
+
+ dev_dbg(dev, "Device going to suspend, disabling\n");
+
+ /* Clear the status register */
+ status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
+ if (status)
+ goto err;
+
+ status = smb_wr16(SECOCEC_STATUS_REG_1, val);
+ if (status)
+ goto err;
+
+ /* Disable the interrupts */
+ status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
+ if (status)
+ goto err;
+
+ status = smb_wr16(SECOCEC_ENABLE_REG_1, val &
+ ~SECOCEC_ENABLE_REG_1_CEC & ~SECOCEC_ENABLE_REG_1_IR);
+ if (status)
+ goto err;
+
+ return 0;
+
+err:
+ dev_err(dev, "Suspend failed: %d\n", status);
+ return status;
+}
+
+static int secocec_resume(struct device *dev)
+{
+ int status;
+ u16 val;
+
+ dev_dbg(dev, "Resuming device from suspend\n");
+
+ /* Clear the status register */
+ status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
+ if (status)
+ goto err;
+
+ status = smb_wr16(SECOCEC_STATUS_REG_1, val);
+ if (status)
+ goto err;
+
+ /* Enable the interrupts */
+ status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
+ if (status)
+ goto err;
+
+ status = smb_wr16(SECOCEC_ENABLE_REG_1, val | SECOCEC_ENABLE_REG_1_CEC);
+ if (status)
+ goto err;
+
+ dev_dbg(dev, "Device resumed from suspend\n");
+
+ return 0;
+
+err:
+ dev_err(dev, "Resume failed: %d\n", status);
+ return status;
+}
+
+static SIMPLE_DEV_PM_OPS(secocec_pm_ops, secocec_suspend, secocec_resume);
+#define SECOCEC_PM_OPS (&secocec_pm_ops)
+#else
+#define SECOCEC_PM_OPS NULL
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id secocec_acpi_match[] = {
+ {"CEC00001", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, secocec_acpi_match);
+#endif
+
+static struct platform_driver secocec_driver = {
+ .driver = {
+ .name = SECOCEC_DEV_NAME,
+ .acpi_match_table = ACPI_PTR(secocec_acpi_match),
+ .pm = SECOCEC_PM_OPS,
+ },
+ .probe = secocec_probe,
+ .remove = secocec_remove,
+};
+
+module_platform_driver(secocec_driver);
+
+MODULE_DESCRIPTION("SECO CEC X86 Driver");
+MODULE_AUTHOR("Ettore Chimenti <ek5.chimenti@gmail.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/media/cec/platform/seco/seco-cec.h b/drivers/media/cec/platform/seco/seco-cec.h
new file mode 100644
index 000000000000..843de8c7dfd4
--- /dev/null
+++ b/drivers/media/cec/platform/seco/seco-cec.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * SECO X86 Boards CEC register defines
+ *
+ * Author: Ettore Chimenti <ek5.chimenti@gmail.com>
+ * Copyright (C) 2018, SECO Spa.
+ * Copyright (C) 2018, Aidilab Srl.
+ */
+
+#ifndef __SECO_CEC_H__
+#define __SECO_CEC_H__
+
+#define SECOCEC_MAX_ADDRS 1
+#define SECOCEC_DEV_NAME "secocec"
+#define SECOCEC_LATEST_FW 0x0f0b
+
+#define SMBTIMEOUT 0xfff
+#define SMB_POLL_UDELAY 10
+
+#define SMBUS_WRITE 0
+#define SMBUS_READ 1
+
+#define CMD_BYTE_DATA 0
+#define CMD_WORD_DATA 1
+
+/*
+ * SMBus definitons for Braswell
+ */
+
+#define BRA_DONE_STATUS BIT(7)
+#define BRA_INUSE_STS BIT(6)
+#define BRA_FAILED_OP BIT(4)
+#define BRA_BUS_ERR BIT(3)
+#define BRA_DEV_ERR BIT(2)
+#define BRA_INTR BIT(1)
+#define BRA_HOST_BUSY BIT(0)
+#define BRA_HSTS_ERR_MASK (BRA_FAILED_OP | BRA_BUS_ERR | BRA_DEV_ERR)
+
+#define BRA_PEC_EN BIT(7)
+#define BRA_START BIT(6)
+#define BRA_LAST__BYTE BIT(5)
+#define BRA_INTREN BIT(0)
+#define BRA_SMB_CMD (7 << 2)
+#define BRA_SMB_CMD_QUICK (0 << 2)
+#define BRA_SMB_CMD_BYTE (1 << 2)
+#define BRA_SMB_CMD_BYTE_DATA (2 << 2)
+#define BRA_SMB_CMD_WORD_DATA (3 << 2)
+#define BRA_SMB_CMD_PROCESS_CALL (4 << 2)
+#define BRA_SMB_CMD_BLOCK (5 << 2)
+#define BRA_SMB_CMD_I2CREAD (6 << 2)
+#define BRA_SMB_CMD_BLOCK_PROCESS (7 << 2)
+
+#define BRA_SMB_BASE_ADDR 0x2040
+#define HSTS (BRA_SMB_BASE_ADDR + 0)
+#define HCNT (BRA_SMB_BASE_ADDR + 2)
+#define HCMD (BRA_SMB_BASE_ADDR + 3)
+#define XMIT_SLVA (BRA_SMB_BASE_ADDR + 4)
+#define HDAT0 (BRA_SMB_BASE_ADDR + 5)
+#define HDAT1 (BRA_SMB_BASE_ADDR + 6)
+
+/*
+ * Microcontroller Address
+ */
+
+#define SECOCEC_MICRO_ADDRESS 0x40
+
+/*
+ * STM32 SMBus Registers
+ */
+
+#define SECOCEC_VERSION 0x00
+#define SECOCEC_ENABLE_REG_1 0x01
+#define SECOCEC_ENABLE_REG_2 0x02
+#define SECOCEC_STATUS_REG_1 0x03
+#define SECOCEC_STATUS_REG_2 0x04
+
+#define SECOCEC_STATUS 0x28
+#define SECOCEC_DEVICE_LA 0x29
+#define SECOCEC_READ_OPERATION_ID 0x2a
+#define SECOCEC_READ_DATA_LENGTH 0x2b
+#define SECOCEC_READ_DATA_00 0x2c
+#define SECOCEC_READ_DATA_02 0x2d
+#define SECOCEC_READ_DATA_04 0x2e
+#define SECOCEC_READ_DATA_06 0x2f
+#define SECOCEC_READ_DATA_08 0x30
+#define SECOCEC_READ_DATA_10 0x31
+#define SECOCEC_READ_DATA_12 0x32
+#define SECOCEC_READ_BYTE0 0x33
+#define SECOCEC_WRITE_OPERATION_ID 0x34
+#define SECOCEC_WRITE_DATA_LENGTH 0x35
+#define SECOCEC_WRITE_DATA_00 0x36
+#define SECOCEC_WRITE_DATA_02 0x37
+#define SECOCEC_WRITE_DATA_04 0x38
+#define SECOCEC_WRITE_DATA_06 0x39
+#define SECOCEC_WRITE_DATA_08 0x3a
+#define SECOCEC_WRITE_DATA_10 0x3b
+#define SECOCEC_WRITE_DATA_12 0x3c
+#define SECOCEC_WRITE_BYTE0 0x3d
+
+#define SECOCEC_IR_READ_DATA 0x3e
+
+/*
+ * IR
+ */
+
+#define SECOCEC_IR_COMMAND_MASK 0x007F
+#define SECOCEC_IR_COMMAND_SHL 0
+#define SECOCEC_IR_ADDRESS_MASK 0x1F00
+#define SECOCEC_IR_ADDRESS_SHL 8
+#define SECOCEC_IR_TOGGLE_MASK 0x8000
+#define SECOCEC_IR_TOGGLE_SHL 15
+
+/*
+ * Enabling register
+ */
+
+#define SECOCEC_ENABLE_REG_1_CEC 0x1000
+#define SECOCEC_ENABLE_REG_1_IR 0x2000
+#define SECOCEC_ENABLE_REG_1_IR_PASSTHROUGH 0x4000
+
+/*
+ * Status register
+ */
+
+#define SECOCEC_STATUS_REG_1_CEC SECOCEC_ENABLE_REG_1_CEC
+#define SECOCEC_STATUS_REG_1_IR SECOCEC_ENABLE_REG_1_IR
+#define SECOCEC_STATUS_REG_1_IR_PASSTHR SECOCEC_ENABLE_REG_1_IR_PASSTHR
+
+/*
+ * Status data
+ */
+
+#define SECOCEC_STATUS_MSG_RECEIVED_MASK BIT(0)
+#define SECOCEC_STATUS_RX_ERROR_MASK BIT(1)
+#define SECOCEC_STATUS_MSG_SENT_MASK BIT(2)
+#define SECOCEC_STATUS_TX_ERROR_MASK BIT(3)
+
+#define SECOCEC_STATUS_TX_NACK_ERROR BIT(4)
+#define SECOCEC_STATUS_RX_OVERFLOW_MASK BIT(5)
+
+#endif /* __SECO_CEC_H__ */
diff --git a/drivers/media/cec/platform/sti/Makefile b/drivers/media/cec/platform/sti/Makefile
new file mode 100644
index 000000000000..26ec5ba1c633
--- /dev/null
+++ b/drivers/media/cec/platform/sti/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_CEC_STI) += stih-cec.o
diff --git a/drivers/media/cec/platform/sti/stih-cec.c b/drivers/media/cec/platform/sti/stih-cec.c
new file mode 100644
index 000000000000..49843d576c7c
--- /dev/null
+++ b/drivers/media/cec/platform/sti/stih-cec.c
@@ -0,0 +1,397 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * STIH4xx CEC driver
+ * Copyright (C) STMicroelectronics SA 2016
+ *
+ */
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include <media/cec.h>
+#include <media/cec-notifier.h>
+
+#define CEC_NAME "stih-cec"
+
+/* CEC registers */
+#define CEC_CLK_DIV 0x0
+#define CEC_CTRL 0x4
+#define CEC_IRQ_CTRL 0x8
+#define CEC_STATUS 0xC
+#define CEC_EXT_STATUS 0x10
+#define CEC_TX_CTRL 0x14
+#define CEC_FREE_TIME_THRESH 0x18
+#define CEC_BIT_TOUT_THRESH 0x1C
+#define CEC_BIT_PULSE_THRESH 0x20
+#define CEC_DATA 0x24
+#define CEC_TX_ARRAY_CTRL 0x28
+#define CEC_CTRL2 0x2C
+#define CEC_TX_ERROR_STS 0x30
+#define CEC_ADDR_TABLE 0x34
+#define CEC_DATA_ARRAY_CTRL 0x38
+#define CEC_DATA_ARRAY_STATUS 0x3C
+#define CEC_TX_DATA_BASE 0x40
+#define CEC_TX_DATA_TOP 0x50
+#define CEC_TX_DATA_SIZE 0x1
+#define CEC_RX_DATA_BASE 0x54
+#define CEC_RX_DATA_TOP 0x64
+#define CEC_RX_DATA_SIZE 0x1
+
+/* CEC_CTRL2 */
+#define CEC_LINE_INACTIVE_EN BIT(0)
+#define CEC_AUTO_BUS_ERR_EN BIT(1)
+#define CEC_STOP_ON_ARB_ERR_EN BIT(2)
+#define CEC_TX_REQ_WAIT_EN BIT(3)
+
+/* CEC_DATA_ARRAY_CTRL */
+#define CEC_TX_ARRAY_EN BIT(0)
+#define CEC_RX_ARRAY_EN BIT(1)
+#define CEC_TX_ARRAY_RESET BIT(2)
+#define CEC_RX_ARRAY_RESET BIT(3)
+#define CEC_TX_N_OF_BYTES_IRQ_EN BIT(4)
+#define CEC_TX_STOP_ON_NACK BIT(7)
+
+/* CEC_TX_ARRAY_CTRL */
+#define CEC_TX_N_OF_BYTES 0x1F
+#define CEC_TX_START BIT(5)
+#define CEC_TX_AUTO_SOM_EN BIT(6)
+#define CEC_TX_AUTO_EOM_EN BIT(7)
+
+/* CEC_IRQ_CTRL */
+#define CEC_TX_DONE_IRQ_EN BIT(0)
+#define CEC_ERROR_IRQ_EN BIT(2)
+#define CEC_RX_DONE_IRQ_EN BIT(3)
+#define CEC_RX_SOM_IRQ_EN BIT(4)
+#define CEC_RX_EOM_IRQ_EN BIT(5)
+#define CEC_FREE_TIME_IRQ_EN BIT(6)
+#define CEC_PIN_STS_IRQ_EN BIT(7)
+
+/* CEC_CTRL */
+#define CEC_IN_FILTER_EN BIT(0)
+#define CEC_PWR_SAVE_EN BIT(1)
+#define CEC_EN BIT(4)
+#define CEC_ACK_CTRL BIT(5)
+#define CEC_RX_RESET_EN BIT(6)
+#define CEC_IGNORE_RX_ERROR BIT(7)
+
+/* CEC_STATUS */
+#define CEC_TX_DONE_STS BIT(0)
+#define CEC_TX_ACK_GET_STS BIT(1)
+#define CEC_ERROR_STS BIT(2)
+#define CEC_RX_DONE_STS BIT(3)
+#define CEC_RX_SOM_STS BIT(4)
+#define CEC_RX_EOM_STS BIT(5)
+#define CEC_FREE_TIME_IRQ_STS BIT(6)
+#define CEC_PIN_STS BIT(7)
+#define CEC_SBIT_TOUT_STS BIT(8)
+#define CEC_DBIT_TOUT_STS BIT(9)
+#define CEC_LPULSE_ERROR_STS BIT(10)
+#define CEC_HPULSE_ERROR_STS BIT(11)
+#define CEC_TX_ERROR BIT(12)
+#define CEC_TX_ARB_ERROR BIT(13)
+#define CEC_RX_ERROR_MIN BIT(14)
+#define CEC_RX_ERROR_MAX BIT(15)
+
+/* Signal free time in bit periods (2.4ms) */
+#define CEC_PRESENT_INIT_SFT 7
+#define CEC_NEW_INIT_SFT 5
+#define CEC_RETRANSMIT_SFT 3
+
+/* Constants for CEC_BIT_TOUT_THRESH register */
+#define CEC_SBIT_TOUT_47MS BIT(1)
+#define CEC_SBIT_TOUT_48MS (BIT(0) | BIT(1))
+#define CEC_SBIT_TOUT_50MS BIT(2)
+#define CEC_DBIT_TOUT_27MS BIT(0)
+#define CEC_DBIT_TOUT_28MS BIT(1)
+#define CEC_DBIT_TOUT_29MS (BIT(0) | BIT(1))
+
+/* Constants for CEC_BIT_PULSE_THRESH register */
+#define CEC_BIT_LPULSE_03MS BIT(1)
+#define CEC_BIT_HPULSE_03MS BIT(3)
+
+/* Constants for CEC_DATA_ARRAY_STATUS register */
+#define CEC_RX_N_OF_BYTES 0x1F
+#define CEC_TX_N_OF_BYTES_SENT BIT(5)
+#define CEC_RX_OVERRUN BIT(6)
+
+struct stih_cec {
+ struct cec_adapter *adap;
+ struct device *dev;
+ struct clk *clk;
+ void __iomem *regs;
+ int irq;
+ u32 irq_status;
+ struct cec_notifier *notifier;
+};
+
+static int stih_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct stih_cec *cec = cec_get_drvdata(adap);
+
+ if (enable) {
+ /* The doc says (input TCLK_PERIOD * CEC_CLK_DIV) = 0.1ms */
+ unsigned long clk_freq = clk_get_rate(cec->clk);
+ u32 cec_clk_div = clk_freq / 10000;
+
+ writel(cec_clk_div, cec->regs + CEC_CLK_DIV);
+
+ /* Configuration of the durations activating a timeout */
+ writel(CEC_SBIT_TOUT_47MS | (CEC_DBIT_TOUT_28MS << 4),
+ cec->regs + CEC_BIT_TOUT_THRESH);
+
+ /* Configuration of the smallest allowed duration for pulses */
+ writel(CEC_BIT_LPULSE_03MS | CEC_BIT_HPULSE_03MS,
+ cec->regs + CEC_BIT_PULSE_THRESH);
+
+ /* Minimum received bit period threshold */
+ writel(BIT(5) | BIT(7), cec->regs + CEC_TX_CTRL);
+
+ /* Configuration of transceiver data arrays */
+ writel(CEC_TX_ARRAY_EN | CEC_RX_ARRAY_EN | CEC_TX_STOP_ON_NACK,
+ cec->regs + CEC_DATA_ARRAY_CTRL);
+
+ /* Configuration of the control bits for CEC Transceiver */
+ writel(CEC_IN_FILTER_EN | CEC_EN | CEC_RX_RESET_EN,
+ cec->regs + CEC_CTRL);
+
+ /* Clear logical addresses */
+ writel(0, cec->regs + CEC_ADDR_TABLE);
+
+ /* Clear the status register */
+ writel(0x0, cec->regs + CEC_STATUS);
+
+ /* Enable the interrupts */
+ writel(CEC_TX_DONE_IRQ_EN | CEC_RX_DONE_IRQ_EN |
+ CEC_RX_SOM_IRQ_EN | CEC_RX_EOM_IRQ_EN |
+ CEC_ERROR_IRQ_EN,
+ cec->regs + CEC_IRQ_CTRL);
+
+ } else {
+ /* Clear logical addresses */
+ writel(0, cec->regs + CEC_ADDR_TABLE);
+
+ /* Clear the status register */
+ writel(0x0, cec->regs + CEC_STATUS);
+
+ /* Disable the interrupts */
+ writel(0, cec->regs + CEC_IRQ_CTRL);
+ }
+
+ return 0;
+}
+
+static int stih_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct stih_cec *cec = cec_get_drvdata(adap);
+ u32 reg = readl(cec->regs + CEC_ADDR_TABLE);
+
+ reg |= 1 << logical_addr;
+
+ if (logical_addr == CEC_LOG_ADDR_INVALID)
+ reg = 0;
+
+ writel(reg, cec->regs + CEC_ADDR_TABLE);
+
+ return 0;
+}
+
+static int stih_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct stih_cec *cec = cec_get_drvdata(adap);
+ int i;
+
+ /* Copy message into registers */
+ for (i = 0; i < msg->len; i++)
+ writeb(msg->msg[i], cec->regs + CEC_TX_DATA_BASE + i);
+
+ /*
+ * Start transmission, configure hardware to add start and stop bits
+ * Signal free time is handled by the hardware
+ */
+ writel(CEC_TX_AUTO_SOM_EN | CEC_TX_AUTO_EOM_EN | CEC_TX_START |
+ msg->len, cec->regs + CEC_TX_ARRAY_CTRL);
+
+ return 0;
+}
+
+static void stih_tx_done(struct stih_cec *cec, u32 status)
+{
+ if (status & CEC_TX_ERROR) {
+ cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_ERROR);
+ return;
+ }
+
+ if (status & CEC_TX_ARB_ERROR) {
+ cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_ARB_LOST);
+ return;
+ }
+
+ if (!(status & CEC_TX_ACK_GET_STS)) {
+ cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_NACK);
+ return;
+ }
+
+ cec_transmit_attempt_done(cec->adap, CEC_TX_STATUS_OK);
+}
+
+static void stih_rx_done(struct stih_cec *cec, u32 status)
+{
+ struct cec_msg msg = {};
+ u8 i;
+
+ if (status & CEC_RX_ERROR_MIN)
+ return;
+
+ if (status & CEC_RX_ERROR_MAX)
+ return;
+
+ msg.len = readl(cec->regs + CEC_DATA_ARRAY_STATUS) & 0x1f;
+
+ if (!msg.len)
+ return;
+
+ if (msg.len > CEC_MAX_MSG_SIZE)
+ msg.len = CEC_MAX_MSG_SIZE;
+
+ for (i = 0; i < msg.len; i++)
+ msg.msg[i] = readl(cec->regs + CEC_RX_DATA_BASE + i);
+
+ cec_received_msg(cec->adap, &msg);
+}
+
+static irqreturn_t stih_cec_irq_handler_thread(int irq, void *priv)
+{
+ struct stih_cec *cec = priv;
+
+ if (cec->irq_status & CEC_TX_DONE_STS)
+ stih_tx_done(cec, cec->irq_status);
+
+ if (cec->irq_status & CEC_RX_DONE_STS)
+ stih_rx_done(cec, cec->irq_status);
+
+ cec->irq_status = 0;
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t stih_cec_irq_handler(int irq, void *priv)
+{
+ struct stih_cec *cec = priv;
+
+ cec->irq_status = readl(cec->regs + CEC_STATUS);
+ writel(cec->irq_status, cec->regs + CEC_STATUS);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static const struct cec_adap_ops sti_cec_adap_ops = {
+ .adap_enable = stih_cec_adap_enable,
+ .adap_log_addr = stih_cec_adap_log_addr,
+ .adap_transmit = stih_cec_adap_transmit,
+};
+
+static int stih_cec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct stih_cec *cec;
+ struct device *hdmi_dev;
+ int ret;
+
+ hdmi_dev = cec_notifier_parse_hdmi_phandle(dev);
+
+ if (IS_ERR(hdmi_dev))
+ return PTR_ERR(hdmi_dev);
+
+ cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL);
+ if (!cec)
+ return -ENOMEM;
+
+ cec->dev = dev;
+
+ cec->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(cec->regs))
+ return PTR_ERR(cec->regs);
+
+ cec->irq = platform_get_irq(pdev, 0);
+ if (cec->irq < 0)
+ return cec->irq;
+
+ ret = devm_request_threaded_irq(dev, cec->irq, stih_cec_irq_handler,
+ stih_cec_irq_handler_thread, 0,
+ pdev->name, cec);
+ if (ret)
+ return ret;
+
+ cec->clk = devm_clk_get(dev, "cec-clk");
+ if (IS_ERR(cec->clk)) {
+ dev_err(dev, "Cannot get cec clock\n");
+ return PTR_ERR(cec->clk);
+ }
+
+ cec->adap = cec_allocate_adapter(&sti_cec_adap_ops, cec, CEC_NAME,
+ CEC_CAP_DEFAULTS |
+ CEC_CAP_CONNECTOR_INFO,
+ CEC_MAX_LOG_ADDRS);
+ ret = PTR_ERR_OR_ZERO(cec->adap);
+ if (ret)
+ return ret;
+
+ cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL,
+ cec->adap);
+ if (!cec->notifier) {
+ ret = -ENOMEM;
+ goto err_delete_adapter;
+ }
+
+ ret = cec_register_adapter(cec->adap, &pdev->dev);
+ if (ret)
+ goto err_notifier;
+
+ platform_set_drvdata(pdev, cec);
+ return 0;
+
+err_notifier:
+ cec_notifier_cec_adap_unregister(cec->notifier, cec->adap);
+
+err_delete_adapter:
+ cec_delete_adapter(cec->adap);
+ return ret;
+}
+
+static void stih_cec_remove(struct platform_device *pdev)
+{
+ struct stih_cec *cec = platform_get_drvdata(pdev);
+
+ cec_notifier_cec_adap_unregister(cec->notifier, cec->adap);
+ cec_unregister_adapter(cec->adap);
+}
+
+static const struct of_device_id stih_cec_match[] = {
+ {
+ .compatible = "st,stih-cec",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stih_cec_match);
+
+static struct platform_driver stih_cec_pdrv = {
+ .probe = stih_cec_probe,
+ .remove = stih_cec_remove,
+ .driver = {
+ .name = CEC_NAME,
+ .of_match_table = stih_cec_match,
+ },
+};
+
+module_platform_driver(stih_cec_pdrv);
+
+MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@linaro.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("STIH4xx CEC driver");
diff --git a/drivers/media/cec/platform/stm32/Makefile b/drivers/media/cec/platform/stm32/Makefile
new file mode 100644
index 000000000000..b7597a00befa
--- /dev/null
+++ b/drivers/media/cec/platform/stm32/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_CEC_STM32) += stm32-cec.o
diff --git a/drivers/media/cec/platform/stm32/stm32-cec.c b/drivers/media/cec/platform/stm32/stm32-cec.c
new file mode 100644
index 000000000000..1ec0cece0a5b
--- /dev/null
+++ b/drivers/media/cec/platform/stm32/stm32-cec.c
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * STM32 CEC driver
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <media/cec.h>
+
+#define CEC_NAME "stm32-cec"
+
+/* CEC registers */
+#define CEC_CR 0x0000 /* Control Register */
+#define CEC_CFGR 0x0004 /* ConFiGuration Register */
+#define CEC_TXDR 0x0008 /* Rx data Register */
+#define CEC_RXDR 0x000C /* Rx data Register */
+#define CEC_ISR 0x0010 /* Interrupt and status Register */
+#define CEC_IER 0x0014 /* Interrupt enable Register */
+
+#define TXEOM BIT(2)
+#define TXSOM BIT(1)
+#define CECEN BIT(0)
+
+#define LSTN BIT(31)
+#define OAR GENMASK(30, 16)
+#define SFTOP BIT(8)
+#define BRDNOGEN BIT(7)
+#define LBPEGEN BIT(6)
+#define BREGEN BIT(5)
+#define BRESTP BIT(4)
+#define RXTOL BIT(3)
+#define SFT GENMASK(2, 0)
+#define FULL_CFG (LSTN | SFTOP | BRDNOGEN | LBPEGEN | BREGEN | BRESTP \
+ | RXTOL)
+
+#define TXACKE BIT(12)
+#define TXERR BIT(11)
+#define TXUDR BIT(10)
+#define TXEND BIT(9)
+#define TXBR BIT(8)
+#define ARBLST BIT(7)
+#define RXACKE BIT(6)
+#define RXOVR BIT(2)
+#define RXEND BIT(1)
+#define RXBR BIT(0)
+
+#define ALL_TX_IT (TXEND | TXBR | TXACKE | TXERR | TXUDR | ARBLST)
+#define ALL_RX_IT (RXEND | RXBR | RXACKE | RXOVR)
+
+/*
+ * 400 ms is the time it takes for one 16 byte message to be
+ * transferred and 5 is the maximum number of retries. Add
+ * another 100 ms as a margin.
+ */
+#define CEC_XFER_TIMEOUT_MS (5 * 400 + 100)
+
+struct stm32_cec {
+ struct cec_adapter *adap;
+ struct device *dev;
+ struct clk *clk_cec;
+ struct clk *clk_hdmi_cec;
+ struct reset_control *rstc;
+ struct regmap *regmap;
+ int irq;
+ u32 irq_status;
+ struct cec_msg rx_msg;
+ struct cec_msg tx_msg;
+ int tx_cnt;
+};
+
+static void cec_hw_init(struct stm32_cec *cec)
+{
+ regmap_update_bits(cec->regmap, CEC_CR, TXEOM | TXSOM | CECEN, 0);
+
+ regmap_update_bits(cec->regmap, CEC_IER, ALL_TX_IT | ALL_RX_IT,
+ ALL_TX_IT | ALL_RX_IT);
+
+ regmap_update_bits(cec->regmap, CEC_CFGR, FULL_CFG, FULL_CFG);
+}
+
+static void stm32_tx_done(struct stm32_cec *cec, u32 status)
+{
+ if (status & (TXERR | TXUDR)) {
+ cec_transmit_done(cec->adap, CEC_TX_STATUS_ERROR,
+ 0, 0, 0, 1);
+ return;
+ }
+
+ if (status & ARBLST) {
+ cec_transmit_done(cec->adap, CEC_TX_STATUS_ARB_LOST,
+ 1, 0, 0, 0);
+ return;
+ }
+
+ if (status & TXACKE) {
+ cec_transmit_done(cec->adap, CEC_TX_STATUS_NACK,
+ 0, 1, 0, 0);
+ return;
+ }
+
+ if (cec->irq_status & TXBR) {
+ /* send next byte */
+ if (cec->tx_cnt < cec->tx_msg.len)
+ regmap_write(cec->regmap, CEC_TXDR,
+ cec->tx_msg.msg[cec->tx_cnt++]);
+
+ /* TXEOM is set to command transmission of the last byte */
+ if (cec->tx_cnt == cec->tx_msg.len)
+ regmap_update_bits(cec->regmap, CEC_CR, TXEOM, TXEOM);
+ }
+
+ if (cec->irq_status & TXEND)
+ cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
+}
+
+static void stm32_rx_done(struct stm32_cec *cec, u32 status)
+{
+ if (cec->irq_status & (RXACKE | RXOVR)) {
+ cec->rx_msg.len = 0;
+ return;
+ }
+
+ if (cec->irq_status & RXBR) {
+ u32 val;
+
+ regmap_read(cec->regmap, CEC_RXDR, &val);
+ cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF;
+ }
+
+ if (cec->irq_status & RXEND) {
+ cec_received_msg(cec->adap, &cec->rx_msg);
+ cec->rx_msg.len = 0;
+ }
+}
+
+static irqreturn_t stm32_cec_irq_thread(int irq, void *arg)
+{
+ struct stm32_cec *cec = arg;
+
+ if (cec->irq_status & ALL_TX_IT)
+ stm32_tx_done(cec, cec->irq_status);
+
+ if (cec->irq_status & ALL_RX_IT)
+ stm32_rx_done(cec, cec->irq_status);
+
+ cec->irq_status = 0;
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t stm32_cec_irq_handler(int irq, void *arg)
+{
+ struct stm32_cec *cec = arg;
+
+ regmap_read(cec->regmap, CEC_ISR, &cec->irq_status);
+
+ regmap_update_bits(cec->regmap, CEC_ISR,
+ ALL_TX_IT | ALL_RX_IT,
+ ALL_TX_IT | ALL_RX_IT);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int stm32_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct stm32_cec *cec = adap->priv;
+ int ret = 0;
+
+ if (enable) {
+ ret = clk_enable(cec->clk_cec);
+ if (ret)
+ dev_err(cec->dev, "fail to enable cec clock\n");
+
+ clk_enable(cec->clk_hdmi_cec);
+ regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN);
+ } else {
+ clk_disable(cec->clk_cec);
+ clk_disable(cec->clk_hdmi_cec);
+ regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0);
+ }
+
+ return ret;
+}
+
+static int stm32_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct stm32_cec *cec = adap->priv;
+ u32 oar = (1 << logical_addr) << 16;
+ u32 val;
+
+ /* Poll every 100µs the register CEC_CR to wait end of transmission */
+ regmap_read_poll_timeout(cec->regmap, CEC_CR, val, !(val & TXSOM),
+ 100, CEC_XFER_TIMEOUT_MS * 1000);
+ regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0);
+
+ if (logical_addr == CEC_LOG_ADDR_INVALID)
+ regmap_update_bits(cec->regmap, CEC_CFGR, OAR, 0);
+ else
+ regmap_update_bits(cec->regmap, CEC_CFGR, oar, oar);
+
+ regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN);
+
+ return 0;
+}
+
+static int stm32_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct stm32_cec *cec = adap->priv;
+
+ /* Copy message */
+ cec->tx_msg = *msg;
+ cec->tx_cnt = 0;
+
+ /*
+ * If the CEC message consists of only one byte,
+ * TXEOM must be set before of TXSOM.
+ */
+ if (cec->tx_msg.len == 1)
+ regmap_update_bits(cec->regmap, CEC_CR, TXEOM, TXEOM);
+
+ /* TXSOM is set to command transmission of the first byte */
+ regmap_update_bits(cec->regmap, CEC_CR, TXSOM, TXSOM);
+
+ /* Write the header (first byte of message) */
+ regmap_write(cec->regmap, CEC_TXDR, cec->tx_msg.msg[0]);
+ cec->tx_cnt++;
+
+ return 0;
+}
+
+static const struct cec_adap_ops stm32_cec_adap_ops = {
+ .adap_enable = stm32_cec_adap_enable,
+ .adap_log_addr = stm32_cec_adap_log_addr,
+ .adap_transmit = stm32_cec_adap_transmit,
+};
+
+static const struct regmap_config stm32_cec_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = sizeof(u32),
+ .max_register = 0x14,
+};
+
+static int stm32_cec_probe(struct platform_device *pdev)
+{
+ u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_MODE_MONITOR_ALL;
+ struct stm32_cec *cec;
+ void __iomem *mmio;
+ int ret;
+
+ cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
+ if (!cec)
+ return -ENOMEM;
+
+ cec->dev = &pdev->dev;
+
+ mmio = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(mmio))
+ return PTR_ERR(mmio);
+
+ cec->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "cec", mmio,
+ &stm32_cec_regmap_cfg);
+
+ if (IS_ERR(cec->regmap))
+ return PTR_ERR(cec->regmap);
+
+ cec->irq = platform_get_irq(pdev, 0);
+ if (cec->irq < 0)
+ return cec->irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, cec->irq,
+ stm32_cec_irq_handler,
+ stm32_cec_irq_thread,
+ 0,
+ pdev->name, cec);
+ if (ret)
+ return ret;
+
+ cec->clk_cec = devm_clk_get(&pdev->dev, "cec");
+ if (IS_ERR(cec->clk_cec))
+ return dev_err_probe(&pdev->dev, PTR_ERR(cec->clk_cec),
+ "Cannot get cec clock\n");
+
+ ret = clk_prepare(cec->clk_cec);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to prepare cec clock\n");
+ return ret;
+ }
+
+ cec->clk_hdmi_cec = devm_clk_get(&pdev->dev, "hdmi-cec");
+ if (IS_ERR(cec->clk_hdmi_cec) &&
+ PTR_ERR(cec->clk_hdmi_cec) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto err_unprepare_cec_clk;
+ }
+
+ if (!IS_ERR(cec->clk_hdmi_cec)) {
+ ret = clk_prepare(cec->clk_hdmi_cec);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't prepare hdmi-cec clock\n");
+ goto err_unprepare_cec_clk;
+ }
+ }
+
+ /*
+ * CEC_CAP_PHYS_ADDR caps should be removed when a cec notifier is
+ * available for example when a drm driver can provide edid
+ */
+ cec->adap = cec_allocate_adapter(&stm32_cec_adap_ops, cec,
+ CEC_NAME, caps, CEC_MAX_LOG_ADDRS);
+ ret = PTR_ERR_OR_ZERO(cec->adap);
+ if (ret)
+ goto err_unprepare_hdmi_cec_clk;
+
+ ret = cec_register_adapter(cec->adap, &pdev->dev);
+ if (ret)
+ goto err_delete_adapter;
+
+ cec_hw_init(cec);
+
+ platform_set_drvdata(pdev, cec);
+
+ return 0;
+
+err_delete_adapter:
+ cec_delete_adapter(cec->adap);
+
+err_unprepare_hdmi_cec_clk:
+ clk_unprepare(cec->clk_hdmi_cec);
+
+err_unprepare_cec_clk:
+ clk_unprepare(cec->clk_cec);
+ return ret;
+}
+
+static void stm32_cec_remove(struct platform_device *pdev)
+{
+ struct stm32_cec *cec = platform_get_drvdata(pdev);
+
+ clk_unprepare(cec->clk_cec);
+ clk_unprepare(cec->clk_hdmi_cec);
+
+ cec_unregister_adapter(cec->adap);
+}
+
+static const struct of_device_id stm32_cec_of_match[] = {
+ { .compatible = "st,stm32-cec" },
+ { /* end node */ }
+};
+MODULE_DEVICE_TABLE(of, stm32_cec_of_match);
+
+static struct platform_driver stm32_cec_driver = {
+ .probe = stm32_cec_probe,
+ .remove = stm32_cec_remove,
+ .driver = {
+ .name = CEC_NAME,
+ .of_match_table = stm32_cec_of_match,
+ },
+};
+
+module_platform_driver(stm32_cec_driver);
+
+MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 Consumer Electronics Control");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/cec/platform/tegra/Makefile b/drivers/media/cec/platform/tegra/Makefile
new file mode 100644
index 000000000000..275d1c019d49
--- /dev/null
+++ b/drivers/media/cec/platform/tegra/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_CEC_TEGRA) += tegra_cec.o
diff --git a/drivers/media/cec/platform/tegra/tegra_cec.c b/drivers/media/cec/platform/tegra/tegra_cec.c
new file mode 100644
index 000000000000..3ed50097262f
--- /dev/null
+++ b/drivers/media/cec/platform/tegra/tegra_cec.c
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Tegra CEC implementation
+ *
+ * The original 3.10 CEC driver using a custom API:
+ *
+ * Copyright (c) 2012-2015, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Conversion to the CEC framework and to the mainline kernel:
+ *
+ * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/clk/tegra.h>
+
+#include <media/cec-notifier.h>
+
+#include "tegra_cec.h"
+
+#define TEGRA_CEC_NAME "tegra-cec"
+
+struct tegra_cec {
+ struct cec_adapter *adap;
+ struct device *dev;
+ struct clk *clk;
+ void __iomem *cec_base;
+ struct cec_notifier *notifier;
+ int tegra_cec_irq;
+ bool rx_done;
+ bool tx_done;
+ int tx_status;
+ u8 rx_buf[CEC_MAX_MSG_SIZE];
+ u8 rx_buf_cnt;
+ u32 tx_buf[CEC_MAX_MSG_SIZE];
+ u8 tx_buf_cur;
+ u8 tx_buf_cnt;
+};
+
+static inline u32 cec_read(struct tegra_cec *cec, u32 reg)
+{
+ return readl(cec->cec_base + reg);
+}
+
+static inline void cec_write(struct tegra_cec *cec, u32 reg, u32 val)
+{
+ writel(val, cec->cec_base + reg);
+}
+
+static void tegra_cec_error_recovery(struct tegra_cec *cec)
+{
+ u32 hw_ctrl;
+
+ hw_ctrl = cec_read(cec, TEGRA_CEC_HW_CONTROL);
+ cec_write(cec, TEGRA_CEC_HW_CONTROL, 0);
+ cec_write(cec, TEGRA_CEC_INT_STAT, 0xffffffff);
+ cec_write(cec, TEGRA_CEC_HW_CONTROL, hw_ctrl);
+}
+
+static irqreturn_t tegra_cec_irq_thread_handler(int irq, void *data)
+{
+ struct device *dev = data;
+ struct tegra_cec *cec = dev_get_drvdata(dev);
+
+ if (cec->tx_done) {
+ cec_transmit_attempt_done(cec->adap, cec->tx_status);
+ cec->tx_done = false;
+ }
+ if (cec->rx_done) {
+ struct cec_msg msg = {};
+
+ msg.len = cec->rx_buf_cnt;
+ memcpy(msg.msg, cec->rx_buf, msg.len);
+ cec_received_msg(cec->adap, &msg);
+ cec->rx_done = false;
+ cec->rx_buf_cnt = 0;
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tegra_cec_irq_handler(int irq, void *data)
+{
+ struct device *dev = data;
+ struct tegra_cec *cec = dev_get_drvdata(dev);
+ u32 status, mask;
+
+ status = cec_read(cec, TEGRA_CEC_INT_STAT);
+ mask = cec_read(cec, TEGRA_CEC_INT_MASK);
+
+ status &= mask;
+
+ if (!status)
+ return IRQ_HANDLED;
+
+ if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) {
+ dev_err(dev, "TX underrun, interrupt timing issue!\n");
+
+ tegra_cec_error_recovery(cec);
+ cec_write(cec, TEGRA_CEC_INT_MASK,
+ mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);
+
+ cec->tx_done = true;
+ cec->tx_status = CEC_TX_STATUS_ERROR;
+ return IRQ_WAKE_THREAD;
+ }
+
+ if ((status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) ||
+ (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) {
+ tegra_cec_error_recovery(cec);
+ cec_write(cec, TEGRA_CEC_INT_MASK,
+ mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);
+
+ cec->tx_done = true;
+ if (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)
+ cec->tx_status = CEC_TX_STATUS_LOW_DRIVE;
+ else
+ cec->tx_status = CEC_TX_STATUS_ARB_LOST;
+ return IRQ_WAKE_THREAD;
+ }
+
+ if (status & TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED) {
+ cec_write(cec, TEGRA_CEC_INT_STAT,
+ TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED);
+
+ if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) {
+ tegra_cec_error_recovery(cec);
+
+ cec->tx_done = true;
+ cec->tx_status = CEC_TX_STATUS_NACK;
+ } else {
+ cec->tx_done = true;
+ cec->tx_status = CEC_TX_STATUS_OK;
+ }
+ return IRQ_WAKE_THREAD;
+ }
+
+ if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD)
+ dev_warn(dev, "TX NAKed on the fly!\n");
+
+ if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY) {
+ if (cec->tx_buf_cur == cec->tx_buf_cnt) {
+ cec_write(cec, TEGRA_CEC_INT_MASK,
+ mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);
+ } else {
+ cec_write(cec, TEGRA_CEC_TX_REGISTER,
+ cec->tx_buf[cec->tx_buf_cur++]);
+ cec_write(cec, TEGRA_CEC_INT_STAT,
+ TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY);
+ }
+ }
+
+ if (status & TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED) {
+ cec_write(cec, TEGRA_CEC_INT_STAT,
+ TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED);
+ cec->rx_done = false;
+ cec->rx_buf_cnt = 0;
+ }
+ if (status & TEGRA_CEC_INT_STAT_RX_REGISTER_FULL) {
+ u32 v;
+
+ cec_write(cec, TEGRA_CEC_INT_STAT,
+ TEGRA_CEC_INT_STAT_RX_REGISTER_FULL);
+ v = cec_read(cec, TEGRA_CEC_RX_REGISTER);
+ if (cec->rx_buf_cnt < CEC_MAX_MSG_SIZE)
+ cec->rx_buf[cec->rx_buf_cnt++] = v & 0xff;
+ if (v & TEGRA_CEC_RX_REGISTER_EOM) {
+ cec->rx_done = true;
+ return IRQ_WAKE_THREAD;
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int tegra_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct tegra_cec *cec = adap->priv;
+
+ cec->rx_buf_cnt = 0;
+ cec->tx_buf_cnt = 0;
+ cec->tx_buf_cur = 0;
+
+ cec_write(cec, TEGRA_CEC_HW_CONTROL, 0);
+ cec_write(cec, TEGRA_CEC_INT_MASK, 0);
+ cec_write(cec, TEGRA_CEC_INT_STAT, 0xffffffff);
+ cec_write(cec, TEGRA_CEC_SW_CONTROL, 0);
+
+ if (!enable)
+ return 0;
+
+ cec_write(cec, TEGRA_CEC_INPUT_FILTER, (1U << 31) | 0x20);
+
+ cec_write(cec, TEGRA_CEC_RX_TIMING_0,
+ (0x7a << TEGRA_CEC_RX_TIM0_START_BIT_MAX_LO_TIME_SHIFT) |
+ (0x6d << TEGRA_CEC_RX_TIM0_START_BIT_MIN_LO_TIME_SHIFT) |
+ (0x93 << TEGRA_CEC_RX_TIM0_START_BIT_MAX_DURATION_SHIFT) |
+ (0x86 << TEGRA_CEC_RX_TIM0_START_BIT_MIN_DURATION_SHIFT));
+
+ cec_write(cec, TEGRA_CEC_RX_TIMING_1,
+ (0x35 << TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_LO_TIME_SHIFT) |
+ (0x21 << TEGRA_CEC_RX_TIM1_DATA_BIT_SAMPLE_TIME_SHIFT) |
+ (0x56 << TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_DURATION_SHIFT) |
+ (0x40 << TEGRA_CEC_RX_TIM1_DATA_BIT_MIN_DURATION_SHIFT));
+
+ cec_write(cec, TEGRA_CEC_RX_TIMING_2,
+ (0x50 << TEGRA_CEC_RX_TIM2_END_OF_BLOCK_TIME_SHIFT));
+
+ cec_write(cec, TEGRA_CEC_TX_TIMING_0,
+ (0x74 << TEGRA_CEC_TX_TIM0_START_BIT_LO_TIME_SHIFT) |
+ (0x8d << TEGRA_CEC_TX_TIM0_START_BIT_DURATION_SHIFT) |
+ (0x08 << TEGRA_CEC_TX_TIM0_BUS_XITION_TIME_SHIFT) |
+ (0x71 << TEGRA_CEC_TX_TIM0_BUS_ERROR_LO_TIME_SHIFT));
+
+ cec_write(cec, TEGRA_CEC_TX_TIMING_1,
+ (0x2f << TEGRA_CEC_TX_TIM1_LO_DATA_BIT_LO_TIME_SHIFT) |
+ (0x13 << TEGRA_CEC_TX_TIM1_HI_DATA_BIT_LO_TIME_SHIFT) |
+ (0x4b << TEGRA_CEC_TX_TIM1_DATA_BIT_DURATION_SHIFT) |
+ (0x21 << TEGRA_CEC_TX_TIM1_ACK_NAK_BIT_SAMPLE_TIME_SHIFT));
+
+ cec_write(cec, TEGRA_CEC_TX_TIMING_2,
+ (0x07 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_ADDITIONAL_FRAME_SHIFT) |
+ (0x05 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_NEW_FRAME_SHIFT) |
+ (0x03 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_RETRY_FRAME_SHIFT));
+
+ cec_write(cec, TEGRA_CEC_INT_MASK,
+ TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN |
+ TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD |
+ TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED |
+ TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED |
+ TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED |
+ TEGRA_CEC_INT_MASK_RX_REGISTER_FULL |
+ TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED);
+
+ cec_write(cec, TEGRA_CEC_HW_CONTROL, TEGRA_CEC_HWCTRL_TX_RX_MODE);
+ return 0;
+}
+
+static int tegra_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct tegra_cec *cec = adap->priv;
+ u32 state = cec_read(cec, TEGRA_CEC_HW_CONTROL);
+
+ if (logical_addr == CEC_LOG_ADDR_INVALID)
+ state &= ~TEGRA_CEC_HWCTRL_RX_LADDR_MASK;
+ else
+ state |= TEGRA_CEC_HWCTRL_RX_LADDR((1 << logical_addr));
+
+ cec_write(cec, TEGRA_CEC_HW_CONTROL, state);
+ return 0;
+}
+
+static int tegra_cec_adap_monitor_all_enable(struct cec_adapter *adap,
+ bool enable)
+{
+ struct tegra_cec *cec = adap->priv;
+ u32 reg = cec_read(cec, TEGRA_CEC_HW_CONTROL);
+
+ if (enable)
+ reg |= TEGRA_CEC_HWCTRL_RX_SNOOP;
+ else
+ reg &= ~TEGRA_CEC_HWCTRL_RX_SNOOP;
+ cec_write(cec, TEGRA_CEC_HW_CONTROL, reg);
+ return 0;
+}
+
+static int tegra_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time_ms, struct cec_msg *msg)
+{
+ bool retry_xfer = signal_free_time_ms == CEC_SIGNAL_FREE_TIME_RETRY;
+ struct tegra_cec *cec = adap->priv;
+ unsigned int i;
+ u32 mode = 0;
+ u32 mask;
+
+ if (cec_msg_is_broadcast(msg))
+ mode = TEGRA_CEC_TX_REG_BCAST;
+
+ cec->tx_buf_cur = 0;
+ cec->tx_buf_cnt = msg->len;
+
+ for (i = 0; i < msg->len; i++) {
+ cec->tx_buf[i] = mode | msg->msg[i];
+ if (i == 0)
+ cec->tx_buf[i] |= TEGRA_CEC_TX_REG_START_BIT;
+ if (i == msg->len - 1)
+ cec->tx_buf[i] |= TEGRA_CEC_TX_REG_EOM;
+ if (i == 0 && retry_xfer)
+ cec->tx_buf[i] |= TEGRA_CEC_TX_REG_RETRY;
+ }
+
+ mask = cec_read(cec, TEGRA_CEC_INT_MASK);
+ cec_write(cec, TEGRA_CEC_INT_MASK,
+ mask | TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);
+
+ return 0;
+}
+
+static const struct cec_adap_ops tegra_cec_ops = {
+ .adap_enable = tegra_cec_adap_enable,
+ .adap_log_addr = tegra_cec_adap_log_addr,
+ .adap_transmit = tegra_cec_adap_transmit,
+ .adap_monitor_all_enable = tegra_cec_adap_monitor_all_enable,
+};
+
+static int tegra_cec_probe(struct platform_device *pdev)
+{
+ struct device *hdmi_dev;
+ struct tegra_cec *cec;
+ struct resource *res;
+ int ret = 0;
+
+ hdmi_dev = cec_notifier_parse_hdmi_phandle(&pdev->dev);
+
+ if (IS_ERR(hdmi_dev))
+ return PTR_ERR(hdmi_dev);
+
+ cec = devm_kzalloc(&pdev->dev, sizeof(struct tegra_cec), GFP_KERNEL);
+
+ if (!cec)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Unable to allocate resources for device\n");
+ return -EBUSY;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+ pdev->name)) {
+ dev_err(&pdev->dev,
+ "Unable to request mem region for device\n");
+ return -EBUSY;
+ }
+
+ cec->tegra_cec_irq = platform_get_irq(pdev, 0);
+
+ if (cec->tegra_cec_irq < 0)
+ return cec->tegra_cec_irq;
+
+ cec->cec_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+
+ if (!cec->cec_base) {
+ dev_err(&pdev->dev, "Unable to grab IOs for device\n");
+ return -EBUSY;
+ }
+
+ cec->clk = devm_clk_get(&pdev->dev, "cec");
+
+ if (IS_ERR_OR_NULL(cec->clk)) {
+ dev_err(&pdev->dev, "Can't get clock for CEC\n");
+ return -ENOENT;
+ }
+
+ ret = clk_prepare_enable(cec->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to prepare clock for CEC\n");
+ return ret;
+ }
+
+ /* set context info. */
+ cec->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, cec);
+
+ ret = devm_request_threaded_irq(&pdev->dev, cec->tegra_cec_irq,
+ tegra_cec_irq_handler, tegra_cec_irq_thread_handler,
+ 0, "cec_irq", &pdev->dev);
+
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Unable to request interrupt for device\n");
+ goto err_clk;
+ }
+
+ cec->adap = cec_allocate_adapter(&tegra_cec_ops, cec, TEGRA_CEC_NAME,
+ CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL |
+ CEC_CAP_CONNECTOR_INFO,
+ CEC_MAX_LOG_ADDRS);
+ if (IS_ERR(cec->adap)) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "Couldn't create cec adapter\n");
+ goto err_clk;
+ }
+
+ cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL,
+ cec->adap);
+ if (!cec->notifier) {
+ ret = -ENOMEM;
+ goto err_adapter;
+ }
+
+ ret = cec_register_adapter(cec->adap, &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't register device\n");
+ goto err_notifier;
+ }
+
+ return 0;
+
+err_notifier:
+ cec_notifier_cec_adap_unregister(cec->notifier, cec->adap);
+err_adapter:
+ cec_delete_adapter(cec->adap);
+err_clk:
+ clk_disable_unprepare(cec->clk);
+ return ret;
+}
+
+static void tegra_cec_remove(struct platform_device *pdev)
+{
+ struct tegra_cec *cec = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(cec->clk);
+
+ cec_notifier_cec_adap_unregister(cec->notifier, cec->adap);
+ cec_unregister_adapter(cec->adap);
+}
+
+#ifdef CONFIG_PM
+static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct tegra_cec *cec = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(cec->clk);
+
+ dev_notice(&pdev->dev, "suspended\n");
+ return 0;
+}
+
+static int tegra_cec_resume(struct platform_device *pdev)
+{
+ struct tegra_cec *cec = platform_get_drvdata(pdev);
+
+ dev_notice(&pdev->dev, "Resuming\n");
+
+ return clk_prepare_enable(cec->clk);
+}
+#endif
+
+static const struct of_device_id tegra_cec_of_match[] = {
+ { .compatible = "nvidia,tegra114-cec", },
+ { .compatible = "nvidia,tegra124-cec", },
+ { .compatible = "nvidia,tegra210-cec", },
+ {},
+};
+
+static struct platform_driver tegra_cec_driver = {
+ .driver = {
+ .name = TEGRA_CEC_NAME,
+ .of_match_table = tegra_cec_of_match,
+ },
+ .probe = tegra_cec_probe,
+ .remove = tegra_cec_remove,
+
+#ifdef CONFIG_PM
+ .suspend = tegra_cec_suspend,
+ .resume = tegra_cec_resume,
+#endif
+};
+
+module_platform_driver(tegra_cec_driver);
+
+MODULE_DESCRIPTION("Tegra HDMI CEC driver");
+MODULE_AUTHOR("NVIDIA CORPORATION");
+MODULE_AUTHOR("Cisco Systems, Inc. and/or its affiliates");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/cec/platform/tegra/tegra_cec.h b/drivers/media/cec/platform/tegra/tegra_cec.h
new file mode 100644
index 000000000000..8c370be38e1e
--- /dev/null
+++ b/drivers/media/cec/platform/tegra/tegra_cec.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Tegra CEC register definitions
+ *
+ * The original 3.10 CEC driver using a custom API:
+ *
+ * Copyright (c) 2012-2015, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Conversion to the CEC framework and to the mainline kernel:
+ *
+ * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#ifndef TEGRA_CEC_H
+#define TEGRA_CEC_H
+
+/* CEC registers */
+#define TEGRA_CEC_SW_CONTROL 0x000
+#define TEGRA_CEC_HW_CONTROL 0x004
+#define TEGRA_CEC_INPUT_FILTER 0x008
+#define TEGRA_CEC_TX_REGISTER 0x010
+#define TEGRA_CEC_RX_REGISTER 0x014
+#define TEGRA_CEC_RX_TIMING_0 0x018
+#define TEGRA_CEC_RX_TIMING_1 0x01c
+#define TEGRA_CEC_RX_TIMING_2 0x020
+#define TEGRA_CEC_TX_TIMING_0 0x024
+#define TEGRA_CEC_TX_TIMING_1 0x028
+#define TEGRA_CEC_TX_TIMING_2 0x02c
+#define TEGRA_CEC_INT_STAT 0x030
+#define TEGRA_CEC_INT_MASK 0x034
+#define TEGRA_CEC_HW_DEBUG_RX 0x038
+#define TEGRA_CEC_HW_DEBUG_TX 0x03c
+
+#define TEGRA_CEC_HWCTRL_RX_LADDR_MASK 0x7fff
+#define TEGRA_CEC_HWCTRL_RX_LADDR(x) \
+ ((x) & TEGRA_CEC_HWCTRL_RX_LADDR_MASK)
+#define TEGRA_CEC_HWCTRL_RX_SNOOP BIT(15)
+#define TEGRA_CEC_HWCTRL_RX_NAK_MODE BIT(16)
+#define TEGRA_CEC_HWCTRL_TX_NAK_MODE BIT(24)
+#define TEGRA_CEC_HWCTRL_FAST_SIM_MODE BIT(30)
+#define TEGRA_CEC_HWCTRL_TX_RX_MODE BIT(31)
+
+#define TEGRA_CEC_INPUT_FILTER_MODE BIT(31)
+#define TEGRA_CEC_INPUT_FILTER_FIFO_LENGTH_SHIFT 0
+
+#define TEGRA_CEC_TX_REG_DATA_SHIFT 0
+#define TEGRA_CEC_TX_REG_EOM BIT(8)
+#define TEGRA_CEC_TX_REG_BCAST BIT(12)
+#define TEGRA_CEC_TX_REG_START_BIT BIT(16)
+#define TEGRA_CEC_TX_REG_RETRY BIT(17)
+
+#define TEGRA_CEC_RX_REGISTER_SHIFT 0
+#define TEGRA_CEC_RX_REGISTER_EOM BIT(8)
+#define TEGRA_CEC_RX_REGISTER_ACK BIT(9)
+
+#define TEGRA_CEC_RX_TIM0_START_BIT_MAX_LO_TIME_SHIFT 0
+#define TEGRA_CEC_RX_TIM0_START_BIT_MIN_LO_TIME_SHIFT 8
+#define TEGRA_CEC_RX_TIM0_START_BIT_MAX_DURATION_SHIFT 16
+#define TEGRA_CEC_RX_TIM0_START_BIT_MIN_DURATION_SHIFT 24
+
+#define TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_LO_TIME_SHIFT 0
+#define TEGRA_CEC_RX_TIM1_DATA_BIT_SAMPLE_TIME_SHIFT 8
+#define TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_DURATION_SHIFT 16
+#define TEGRA_CEC_RX_TIM1_DATA_BIT_MIN_DURATION_SHIFT 24
+
+#define TEGRA_CEC_RX_TIM2_END_OF_BLOCK_TIME_SHIFT 0
+
+#define TEGRA_CEC_TX_TIM0_START_BIT_LO_TIME_SHIFT 0
+#define TEGRA_CEC_TX_TIM0_START_BIT_DURATION_SHIFT 8
+#define TEGRA_CEC_TX_TIM0_BUS_XITION_TIME_SHIFT 16
+#define TEGRA_CEC_TX_TIM0_BUS_ERROR_LO_TIME_SHIFT 24
+
+#define TEGRA_CEC_TX_TIM1_LO_DATA_BIT_LO_TIME_SHIFT 0
+#define TEGRA_CEC_TX_TIM1_HI_DATA_BIT_LO_TIME_SHIFT 8
+#define TEGRA_CEC_TX_TIM1_DATA_BIT_DURATION_SHIFT 16
+#define TEGRA_CEC_TX_TIM1_ACK_NAK_BIT_SAMPLE_TIME_SHIFT 24
+
+#define TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_ADDITIONAL_FRAME_SHIFT 0
+#define TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_NEW_FRAME_SHIFT 4
+#define TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_RETRY_FRAME_SHIFT 8
+
+#define TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY BIT(0)
+#define TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN BIT(1)
+#define TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD BIT(2)
+#define TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED BIT(3)
+#define TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED BIT(4)
+#define TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED BIT(5)
+#define TEGRA_CEC_INT_STAT_RX_REGISTER_FULL BIT(8)
+#define TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN BIT(9)
+#define TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED BIT(10)
+#define TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED BIT(11)
+#define TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED BIT(12)
+#define TEGRA_CEC_INT_STAT_FILTERED_RX_DATA_PIN_TRANSITION_H2L BIT(13)
+#define TEGRA_CEC_INT_STAT_FILTERED_RX_DATA_PIN_TRANSITION_L2H BIT(14)
+
+#define TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY BIT(0)
+#define TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN BIT(1)
+#define TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD BIT(2)
+#define TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED BIT(3)
+#define TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED BIT(4)
+#define TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED BIT(5)
+#define TEGRA_CEC_INT_MASK_RX_REGISTER_FULL BIT(8)
+#define TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN BIT(9)
+#define TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED BIT(10)
+#define TEGRA_CEC_INT_MASK_RX_BUS_ANOMALY_DETECTED BIT(11)
+#define TEGRA_CEC_INT_MASK_RX_BUS_ERROR_DETECTED BIT(12)
+#define TEGRA_CEC_INT_MASK_FILTERED_RX_DATA_PIN_TRANSITION_H2L BIT(13)
+#define TEGRA_CEC_INT_MASK_FILTERED_RX_DATA_PIN_TRANSITION_L2H BIT(14)
+
+#define TEGRA_CEC_HW_DEBUG_TX_DURATION_COUNT_SHIFT 0
+#define TEGRA_CEC_HW_DEBUG_TX_TXBIT_COUNT_SHIFT 17
+#define TEGRA_CEC_HW_DEBUG_TX_STATE_SHIFT 21
+#define TEGRA_CEC_HW_DEBUG_TX_FORCELOOUT BIT(25)
+#define TEGRA_CEC_HW_DEBUG_TX_TXDATABIT_SAMPLE_TIMER BIT(26)
+
+#endif /* TEGRA_CEC_H */
diff --git a/drivers/media/cec/usb/Kconfig b/drivers/media/cec/usb/Kconfig
new file mode 100644
index 000000000000..6faf4742981d
--- /dev/null
+++ b/drivers/media/cec/usb/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# USB drivers
+
+if USB_SUPPORT && TTY
+source "drivers/media/cec/usb/extron-da-hd-4k-plus/Kconfig"
+source "drivers/media/cec/usb/pulse8/Kconfig"
+source "drivers/media/cec/usb/rainshadow/Kconfig"
+endif
diff --git a/drivers/media/cec/usb/Makefile b/drivers/media/cec/usb/Makefile
new file mode 100644
index 000000000000..c082679f5318
--- /dev/null
+++ b/drivers/media/cec/usb/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the CEC USB device drivers.
+#
+obj-$(CONFIG_USB_EXTRON_DA_HD_4K_PLUS_CEC) += extron-da-hd-4k-plus/
+obj-$(CONFIG_USB_PULSE8_CEC) += pulse8/
+obj-$(CONFIG_USB_RAINSHADOW_CEC) += rainshadow/
diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/Kconfig b/drivers/media/cec/usb/extron-da-hd-4k-plus/Kconfig
new file mode 100644
index 000000000000..5354f0eebe5c
--- /dev/null
+++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config USB_EXTRON_DA_HD_4K_PLUS_CEC
+ tristate "Extron DA HD 4K Plus CEC driver"
+ depends on VIDEO_DEV
+ depends on USB
+ depends on USB_ACM
+ select CEC_CORE
+ select SERIO
+ select SERIO_SERPORT
+ help
+ This is a CEC driver for the Extron DA HD 4K Plus HDMI Splitter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called extron-da-hd-4k-plus-cec.
diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/Makefile b/drivers/media/cec/usb/extron-da-hd-4k-plus/Makefile
new file mode 100644
index 000000000000..08d58524419f
--- /dev/null
+++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/Makefile
@@ -0,0 +1,2 @@
+extron-da-hd-4k-plus-cec-objs := extron-da-hd-4k-plus.o cec-splitter.o
+obj-$(CONFIG_USB_EXTRON_DA_HD_4K_PLUS_CEC) := extron-da-hd-4k-plus-cec.o
diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.c b/drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.c
new file mode 100644
index 000000000000..73fdec4b791d
--- /dev/null
+++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.c
@@ -0,0 +1,657 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Copyright 2021-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <media/cec.h>
+
+#include "cec-splitter.h"
+
+/*
+ * Helper function to reply to a received message with a Feature Abort
+ * message.
+ */
+static int cec_feature_abort_reason(struct cec_adapter *adap,
+ struct cec_msg *msg, u8 reason)
+{
+ struct cec_msg tx_msg = { };
+
+ /*
+ * Don't reply with CEC_MSG_FEATURE_ABORT to a CEC_MSG_FEATURE_ABORT
+ * message!
+ */
+ if (msg->msg[1] == CEC_MSG_FEATURE_ABORT)
+ return 0;
+ /* Don't Feature Abort messages from 'Unregistered' */
+ if (cec_msg_initiator(msg) == CEC_LOG_ADDR_UNREGISTERED)
+ return 0;
+ cec_msg_set_reply_to(&tx_msg, msg);
+ cec_msg_feature_abort(&tx_msg, msg->msg[1], reason);
+ return cec_transmit_msg(adap, &tx_msg, false);
+}
+
+/* Transmit an Active Source message from this output port to a sink */
+static void cec_port_out_active_source(struct cec_splitter_port *p)
+{
+ struct cec_adapter *adap = p->adap;
+ struct cec_msg msg;
+
+ if (!adap->is_configured)
+ return;
+ p->is_active_source = true;
+ cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0);
+ cec_msg_active_source(&msg, adap->phys_addr);
+ cec_transmit_msg(adap, &msg, false);
+}
+
+/* Transmit Active Source messages from all output ports to the sinks */
+static void cec_out_active_source(struct cec_splitter *splitter)
+{
+ unsigned int i;
+
+ for (i = 0; i < splitter->num_out_ports; i++)
+ cec_port_out_active_source(splitter->ports[i]);
+}
+
+/* Transmit a Standby message from this output port to a sink */
+static void cec_port_out_standby(struct cec_splitter_port *p)
+{
+ struct cec_adapter *adap = p->adap;
+ struct cec_msg msg;
+
+ if (!adap->is_configured)
+ return;
+ cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0);
+ cec_msg_standby(&msg);
+ cec_transmit_msg(adap, &msg, false);
+}
+
+/* Transmit Standby messages from all output ports to the sinks */
+static void cec_out_standby(struct cec_splitter *splitter)
+{
+ unsigned int i;
+
+ for (i = 0; i < splitter->num_out_ports; i++)
+ cec_port_out_standby(splitter->ports[i]);
+}
+
+/* Transmit an Image/Text View On message from this output port to a sink */
+static void cec_port_out_wakeup(struct cec_splitter_port *p, u8 opcode)
+{
+ struct cec_adapter *adap = p->adap;
+ u8 la = adap->log_addrs.log_addr[0];
+ struct cec_msg msg;
+
+ if (la == CEC_LOG_ADDR_INVALID)
+ la = CEC_LOG_ADDR_UNREGISTERED;
+ cec_msg_init(&msg, la, 0);
+ msg.len = 2;
+ msg.msg[1] = opcode;
+ cec_transmit_msg(adap, &msg, false);
+}
+
+/* Transmit Image/Text View On messages from all output ports to the sinks */
+static void cec_out_wakeup(struct cec_splitter *splitter, u8 opcode)
+{
+ unsigned int i;
+
+ for (i = 0; i < splitter->num_out_ports; i++)
+ cec_port_out_wakeup(splitter->ports[i], opcode);
+}
+
+/*
+ * Update the power state of the unconfigured CEC device to either
+ * Off or On depending on the current state of the splitter.
+ * This keeps the outputs in a consistent state.
+ */
+void cec_splitter_unconfigured_output(struct cec_splitter_port *p)
+{
+ p->video_latency = 1;
+ p->power_status = p->splitter->is_standby ?
+ CEC_OP_POWER_STATUS_TO_STANDBY : CEC_OP_POWER_STATUS_TO_ON;
+
+ /* The adapter was unconfigured, so clear the sequence and ts values */
+ p->out_give_device_power_status_seq = 0;
+ p->out_give_device_power_status_ts = ktime_set(0, 0);
+ p->out_request_current_latency_seq = 0;
+ p->out_request_current_latency_ts = ktime_set(0, 0);
+}
+
+/*
+ * Update the power state of the newly configured CEC device to either
+ * Off or On depending on the current state of the splitter.
+ * This keeps the outputs in a consistent state.
+ */
+void cec_splitter_configured_output(struct cec_splitter_port *p)
+{
+ p->video_latency = 1;
+ p->power_status = p->splitter->is_standby ?
+ CEC_OP_POWER_STATUS_TO_STANDBY : CEC_OP_POWER_STATUS_TO_ON;
+
+ if (p->splitter->is_standby) {
+ /*
+ * Some sinks only obey Standby if it comes from the
+ * active source.
+ */
+ cec_port_out_active_source(p);
+ cec_port_out_standby(p);
+ } else {
+ cec_port_out_wakeup(p, CEC_MSG_IMAGE_VIEW_ON);
+ }
+}
+
+/* Pass the in_msg on to all output ports */
+static void cec_out_passthrough(struct cec_splitter *splitter,
+ const struct cec_msg *in_msg)
+{
+ unsigned int i;
+
+ for (i = 0; i < splitter->num_out_ports; i++) {
+ struct cec_splitter_port *p = splitter->ports[i];
+ struct cec_adapter *adap = p->adap;
+ struct cec_msg msg;
+
+ if (!adap->is_configured)
+ continue;
+ cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0);
+ msg.len = in_msg->len;
+ memcpy(msg.msg + 1, in_msg->msg + 1, msg.len - 1);
+ cec_transmit_msg(adap, &msg, false);
+ }
+}
+
+/*
+ * See if all output ports received the Report Current Latency message,
+ * and if so, transmit the result from the input port to the video source.
+ */
+static void cec_out_report_current_latency(struct cec_splitter *splitter,
+ struct cec_adapter *input_adap)
+{
+ struct cec_msg reply = {};
+ unsigned int reply_lat = 0;
+ unsigned int cnt = 0;
+ unsigned int i;
+
+ for (i = 0; i < splitter->num_out_ports; i++) {
+ struct cec_splitter_port *p = splitter->ports[i];
+ struct cec_adapter *adap = p->adap;
+
+ /* Skip unconfigured ports */
+ if (!adap->is_configured)
+ continue;
+ /* Return if a port is still waiting for a reply */
+ if (p->out_request_current_latency_seq)
+ return;
+ reply_lat += p->video_latency - 1;
+ cnt++;
+ }
+
+ /*
+ * All ports that can reply, replied, so clear the sequence
+ * and timestamp values.
+ */
+ for (i = 0; i < splitter->num_out_ports; i++) {
+ struct cec_splitter_port *p = splitter->ports[i];
+
+ p->out_request_current_latency_seq = 0;
+ p->out_request_current_latency_ts = ktime_set(0, 0);
+ }
+
+ /*
+ * Return if there were no replies or the input port is no longer
+ * configured.
+ */
+ if (!cnt || !input_adap->is_configured)
+ return;
+
+ /* Reply with the average latency */
+ reply_lat = 1 + reply_lat / cnt;
+ cec_msg_init(&reply, input_adap->log_addrs.log_addr[0],
+ splitter->request_current_latency_dest);
+ cec_msg_report_current_latency(&reply, input_adap->phys_addr,
+ reply_lat, 1, 1, 1);
+ cec_transmit_msg(input_adap, &reply, false);
+}
+
+/* Transmit Request Current Latency to all output ports */
+static int cec_out_request_current_latency(struct cec_splitter *splitter)
+{
+ ktime_t now = ktime_get();
+ bool error = true;
+ unsigned int i;
+
+ for (i = 0; i < splitter->num_out_ports; i++) {
+ struct cec_splitter_port *p = splitter->ports[i];
+ struct cec_adapter *adap = p->adap;
+
+ if (!adap->is_configured) {
+ /* Clear if not configured */
+ p->out_request_current_latency_seq = 0;
+ p->out_request_current_latency_ts = ktime_set(0, 0);
+ } else if (!p->out_request_current_latency_seq) {
+ /*
+ * Keep the old ts if an earlier request is still
+ * pending. This ensures that the request will
+ * eventually time out based on the timestamp of
+ * the first request if the sink is unresponsive.
+ */
+ p->out_request_current_latency_ts = now;
+ }
+ }
+
+ for (i = 0; i < splitter->num_out_ports; i++) {
+ struct cec_splitter_port *p = splitter->ports[i];
+ struct cec_adapter *adap = p->adap;
+ struct cec_msg msg;
+
+ if (!adap->is_configured)
+ continue;
+ cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0);
+ cec_msg_request_current_latency(&msg, true, adap->phys_addr);
+ if (cec_transmit_msg(adap, &msg, false))
+ continue;
+ p->out_request_current_latency_seq = msg.sequence | (1U << 31);
+ error = false;
+ }
+ return error ? -ENODEV : 0;
+}
+
+/*
+ * See if all output ports received the Report Power Status message,
+ * and if so, transmit the result from the input port to the video source.
+ */
+static void cec_out_report_power_status(struct cec_splitter *splitter,
+ struct cec_adapter *input_adap)
+{
+ struct cec_msg reply = {};
+ /* The target power status of the splitter itself */
+ u8 splitter_pwr = splitter->is_standby ?
+ CEC_OP_POWER_STATUS_STANDBY : CEC_OP_POWER_STATUS_ON;
+ /*
+ * The transient power status of the splitter, used if not all
+ * output report the target power status.
+ */
+ u8 splitter_transient_pwr = splitter->is_standby ?
+ CEC_OP_POWER_STATUS_TO_STANDBY : CEC_OP_POWER_STATUS_TO_ON;
+ u8 reply_pwr = splitter_pwr;
+ unsigned int i;
+
+ for (i = 0; i < splitter->num_out_ports; i++) {
+ struct cec_splitter_port *p = splitter->ports[i];
+
+ /* Skip if no sink was found (HPD was low for more than 5s) */
+ if (!p->found_sink)
+ continue;
+
+ /* Return if a port is still waiting for a reply */
+ if (p->out_give_device_power_status_seq)
+ return;
+ if (p->power_status != splitter_pwr)
+ reply_pwr = splitter_transient_pwr;
+ }
+
+ /*
+ * All ports that can reply, replied, so clear the sequence
+ * and timestamp values.
+ */
+ for (i = 0; i < splitter->num_out_ports; i++) {
+ struct cec_splitter_port *p = splitter->ports[i];
+
+ p->out_give_device_power_status_seq = 0;
+ p->out_give_device_power_status_ts = ktime_set(0, 0);
+ }
+
+ /* Return if the input port is no longer configured. */
+ if (!input_adap->is_configured)
+ return;
+
+ /* Reply with the new power status */
+ cec_msg_init(&reply, input_adap->log_addrs.log_addr[0],
+ splitter->give_device_power_status_dest);
+ cec_msg_report_power_status(&reply, reply_pwr);
+ cec_transmit_msg(input_adap, &reply, false);
+}
+
+/* Transmit Give Device Power Status to all output ports */
+static int cec_out_give_device_power_status(struct cec_splitter *splitter)
+{
+ ktime_t now = ktime_get();
+ bool error = true;
+ unsigned int i;
+
+ for (i = 0; i < splitter->num_out_ports; i++) {
+ struct cec_splitter_port *p = splitter->ports[i];
+ struct cec_adapter *adap = p->adap;
+
+ /*
+ * Keep the old ts if an earlier request is still
+ * pending. This ensures that the request will
+ * eventually time out based on the timestamp of
+ * the first request if the sink is unresponsive.
+ */
+ if (adap->is_configured && !p->out_give_device_power_status_seq)
+ p->out_give_device_power_status_ts = now;
+ }
+
+ for (i = 0; i < splitter->num_out_ports; i++) {
+ struct cec_splitter_port *p = splitter->ports[i];
+ struct cec_adapter *adap = p->adap;
+ struct cec_msg msg;
+
+ if (!adap->is_configured)
+ continue;
+
+ cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0);
+ cec_msg_give_device_power_status(&msg, true);
+ if (cec_transmit_msg(adap, &msg, false))
+ continue;
+ p->out_give_device_power_status_seq = msg.sequence | (1U << 31);
+ error = false;
+ }
+ return error ? -ENODEV : 0;
+}
+
+/*
+ * CEC messages received on the HDMI input of the splitter are
+ * forwarded (if relevant) to the HDMI outputs of the splitter.
+ */
+int cec_splitter_received_input(struct cec_splitter_port *p, struct cec_msg *msg)
+{
+ if (!cec_msg_status_is_ok(msg))
+ return 0;
+
+ if (msg->len < 2)
+ return -ENOMSG;
+
+ switch (msg->msg[1]) {
+ case CEC_MSG_DEVICE_VENDOR_ID:
+ case CEC_MSG_REPORT_POWER_STATUS:
+ case CEC_MSG_SET_STREAM_PATH:
+ case CEC_MSG_ROUTING_CHANGE:
+ case CEC_MSG_REQUEST_ACTIVE_SOURCE:
+ case CEC_MSG_SYSTEM_AUDIO_MODE_STATUS:
+ return 0;
+
+ case CEC_MSG_STANDBY:
+ p->splitter->is_standby = true;
+ cec_out_standby(p->splitter);
+ return 0;
+
+ case CEC_MSG_IMAGE_VIEW_ON:
+ case CEC_MSG_TEXT_VIEW_ON:
+ p->splitter->is_standby = false;
+ cec_out_wakeup(p->splitter, msg->msg[1]);
+ return 0;
+
+ case CEC_MSG_ACTIVE_SOURCE:
+ cec_out_active_source(p->splitter);
+ return 0;
+
+ case CEC_MSG_SET_SYSTEM_AUDIO_MODE:
+ cec_out_passthrough(p->splitter, msg);
+ return 0;
+
+ case CEC_MSG_GIVE_DEVICE_POWER_STATUS:
+ p->splitter->give_device_power_status_dest =
+ cec_msg_initiator(msg);
+ if (cec_out_give_device_power_status(p->splitter))
+ cec_feature_abort_reason(p->adap, msg,
+ CEC_OP_ABORT_INCORRECT_MODE);
+ return 0;
+
+ case CEC_MSG_REQUEST_CURRENT_LATENCY: {
+ u16 pa;
+
+ p->splitter->request_current_latency_dest =
+ cec_msg_initiator(msg);
+ cec_ops_request_current_latency(msg, &pa);
+ if (pa == p->adap->phys_addr &&
+ cec_out_request_current_latency(p->splitter))
+ cec_feature_abort_reason(p->adap, msg,
+ CEC_OP_ABORT_INCORRECT_MODE);
+ return 0;
+ }
+
+ default:
+ return -ENOMSG;
+ }
+ return -ENOMSG;
+}
+
+void cec_splitter_nb_transmit_canceled_output(struct cec_splitter_port *p,
+ const struct cec_msg *msg,
+ struct cec_adapter *input_adap)
+{
+ struct cec_splitter *splitter = p->splitter;
+ u32 seq = msg->sequence | (1U << 31);
+
+ /*
+ * If this is the result of a failed non-blocking transmit, or it is
+ * the result of the failed reply to a non-blocking transmit, then
+ * check if the original transmit was to get the current power status
+ * or latency and, if so, assume that the remove device is for one
+ * reason or another unavailable and assume that it is in the same
+ * power status as the splitter, or has no video latency.
+ */
+ if ((cec_msg_recv_is_tx_result(msg) && !(msg->tx_status & CEC_TX_STATUS_OK)) ||
+ (cec_msg_recv_is_rx_result(msg) && !(msg->rx_status & CEC_RX_STATUS_OK))) {
+ u8 tx_op = msg->msg[1];
+
+ if (msg->len < 2)
+ return;
+ if (cec_msg_recv_is_rx_result(msg) &&
+ (msg->rx_status & CEC_RX_STATUS_FEATURE_ABORT))
+ tx_op = msg->msg[2];
+ switch (tx_op) {
+ case CEC_MSG_GIVE_DEVICE_POWER_STATUS:
+ if (p->out_give_device_power_status_seq != seq)
+ break;
+ p->out_give_device_power_status_seq = 0;
+ p->out_give_device_power_status_ts = ktime_set(0, 0);
+ p->power_status = splitter->is_standby ?
+ CEC_OP_POWER_STATUS_STANDBY :
+ CEC_OP_POWER_STATUS_ON;
+ cec_out_report_power_status(splitter, input_adap);
+ break;
+ case CEC_MSG_REQUEST_CURRENT_LATENCY:
+ if (p->out_request_current_latency_seq != seq)
+ break;
+ p->video_latency = 1;
+ p->out_request_current_latency_seq = 0;
+ p->out_request_current_latency_ts = ktime_set(0, 0);
+ cec_out_report_current_latency(splitter, input_adap);
+ break;
+ }
+ return;
+ }
+
+ if (cec_msg_recv_is_tx_result(msg)) {
+ if (p->out_request_current_latency_seq != seq)
+ return;
+ p->out_request_current_latency_ts = ns_to_ktime(msg->tx_ts);
+ return;
+ }
+}
+
+/*
+ * CEC messages received on an HDMI output of the splitter
+ * are processed here.
+ */
+int cec_splitter_received_output(struct cec_splitter_port *p, struct cec_msg *msg,
+ struct cec_adapter *input_adap)
+{
+ struct cec_adapter *adap = p->adap;
+ struct cec_splitter *splitter = p->splitter;
+ u32 seq = msg->sequence | (1U << 31);
+ struct cec_msg reply = {};
+ u16 pa;
+
+ if (!adap->is_configured || msg->len < 2)
+ return -ENOMSG;
+
+ switch (msg->msg[1]) {
+ case CEC_MSG_REPORT_POWER_STATUS: {
+ u8 pwr;
+
+ cec_ops_report_power_status(msg, &pwr);
+ if (pwr > CEC_OP_POWER_STATUS_TO_STANDBY)
+ pwr = splitter->is_standby ?
+ CEC_OP_POWER_STATUS_TO_STANDBY :
+ CEC_OP_POWER_STATUS_TO_ON;
+ p->power_status = pwr;
+ if (p->out_give_device_power_status_seq == seq) {
+ p->out_give_device_power_status_seq = 0;
+ p->out_give_device_power_status_ts = ktime_set(0, 0);
+ }
+ cec_out_report_power_status(splitter, input_adap);
+ return 0;
+ }
+
+ case CEC_MSG_REPORT_CURRENT_LATENCY: {
+ u8 video_lat;
+ u8 low_lat_mode;
+ u8 audio_out_comp;
+ u8 audio_out_delay;
+
+ cec_ops_report_current_latency(msg, &pa,
+ &video_lat, &low_lat_mode,
+ &audio_out_comp, &audio_out_delay);
+ if (!video_lat || video_lat >= 252)
+ video_lat = 1;
+ p->video_latency = video_lat;
+ if (p->out_request_current_latency_seq == seq) {
+ p->out_request_current_latency_seq = 0;
+ p->out_request_current_latency_ts = ktime_set(0, 0);
+ }
+ cec_out_report_current_latency(splitter, input_adap);
+ return 0;
+ }
+
+ case CEC_MSG_STANDBY:
+ case CEC_MSG_ROUTING_CHANGE:
+ case CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS:
+ return 0;
+
+ case CEC_MSG_ACTIVE_SOURCE:
+ cec_ops_active_source(msg, &pa);
+ if (pa == 0)
+ p->is_active_source = false;
+ return 0;
+
+ case CEC_MSG_REQUEST_ACTIVE_SOURCE:
+ if (!p->is_active_source)
+ return 0;
+ cec_msg_set_reply_to(&reply, msg);
+ cec_msg_active_source(&reply, adap->phys_addr);
+ cec_transmit_msg(adap, &reply, false);
+ return 0;
+
+ case CEC_MSG_GIVE_DEVICE_POWER_STATUS:
+ cec_msg_set_reply_to(&reply, msg);
+ cec_msg_report_power_status(&reply, splitter->is_standby ?
+ CEC_OP_POWER_STATUS_STANDBY :
+ CEC_OP_POWER_STATUS_ON);
+ cec_transmit_msg(adap, &reply, false);
+ return 0;
+
+ case CEC_MSG_SET_STREAM_PATH:
+ cec_ops_set_stream_path(msg, &pa);
+ if (pa == adap->phys_addr) {
+ cec_msg_set_reply_to(&reply, msg);
+ cec_msg_active_source(&reply, pa);
+ cec_transmit_msg(adap, &reply, false);
+ }
+ return 0;
+
+ default:
+ return -ENOMSG;
+ }
+ return -ENOMSG;
+}
+
+/*
+ * Called every second to check for timed out messages and whether there
+ * still is a video sink connected or not.
+ *
+ * Returns true if sinks were lost.
+ */
+bool cec_splitter_poll(struct cec_splitter *splitter,
+ struct cec_adapter *input_adap, bool debug)
+{
+ ktime_t now = ktime_get();
+ u8 pwr = splitter->is_standby ?
+ CEC_OP_POWER_STATUS_STANDBY : CEC_OP_POWER_STATUS_ON;
+ unsigned int max_delay_ms = input_adap->xfer_timeout_ms + 2000;
+ unsigned int i;
+ bool res = false;
+
+ for (i = 0; i < splitter->num_out_ports; i++) {
+ struct cec_splitter_port *p = splitter->ports[i];
+ s64 pwr_delta, lat_delta;
+ bool pwr_timeout, lat_timeout;
+
+ if (!p)
+ continue;
+
+ pwr_delta = ktime_ms_delta(now, p->out_give_device_power_status_ts);
+ pwr_timeout = p->out_give_device_power_status_seq &&
+ pwr_delta >= max_delay_ms;
+ lat_delta = ktime_ms_delta(now, p->out_request_current_latency_ts);
+ lat_timeout = p->out_request_current_latency_seq &&
+ lat_delta >= max_delay_ms;
+
+ /*
+ * If the HPD is low for more than 5 seconds, then assume no display
+ * is connected.
+ */
+ if (p->found_sink && ktime_to_ns(p->lost_sink_ts) &&
+ ktime_ms_delta(now, p->lost_sink_ts) > 5000) {
+ if (debug)
+ dev_info(splitter->dev,
+ "port %u: HPD low for more than 5s, assume no sink is connected.\n",
+ p->port);
+ p->found_sink = false;
+ p->lost_sink_ts = ktime_set(0, 0);
+ res = true;
+ }
+
+ /*
+ * If the power status request timed out, then set the port's
+ * power status to that of the splitter, ensuring a consistent
+ * power state.
+ */
+ if (pwr_timeout) {
+ mutex_lock(&p->adap->lock);
+ if (debug)
+ dev_info(splitter->dev,
+ "port %u: give up on power status for seq %u\n",
+ p->port,
+ p->out_give_device_power_status_seq & ~(1 << 31));
+ p->power_status = pwr;
+ p->out_give_device_power_status_seq = 0;
+ p->out_give_device_power_status_ts = ktime_set(0, 0);
+ mutex_unlock(&p->adap->lock);
+ cec_out_report_power_status(splitter, input_adap);
+ }
+
+ /*
+ * If the current latency request timed out, then set the port's
+ * latency to 1.
+ */
+ if (lat_timeout) {
+ mutex_lock(&p->adap->lock);
+ if (debug)
+ dev_info(splitter->dev,
+ "port %u: give up on latency for seq %u\n",
+ p->port,
+ p->out_request_current_latency_seq & ~(1 << 31));
+ p->video_latency = 1;
+ p->out_request_current_latency_seq = 0;
+ p->out_request_current_latency_ts = ktime_set(0, 0);
+ mutex_unlock(&p->adap->lock);
+ cec_out_report_current_latency(splitter, input_adap);
+ }
+ }
+ return res;
+}
diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.h b/drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.h
new file mode 100644
index 000000000000..7422f7c5719e
--- /dev/null
+++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * Copyright 2021-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#ifndef _CEC_SPLITTER_H_
+#define _CEC_SPLITTER_H_
+
+struct cec_splitter;
+
+#define STATE_CHANGE_MAX_REPEATS 2
+
+struct cec_splitter_port {
+ struct cec_splitter *splitter;
+ struct cec_adapter *adap;
+ unsigned int port;
+ bool is_active_source;
+ bool found_sink;
+ ktime_t lost_sink_ts;
+ u32 out_request_current_latency_seq;
+ ktime_t out_request_current_latency_ts;
+ u8 video_latency;
+ u32 out_give_device_power_status_seq;
+ ktime_t out_give_device_power_status_ts;
+ u8 power_status;
+};
+
+struct cec_splitter {
+ struct device *dev;
+ unsigned int num_out_ports;
+ struct cec_splitter_port **ports;
+
+ /* High-level splitter state */
+ u8 request_current_latency_dest;
+ u8 give_device_power_status_dest;
+ bool is_standby;
+};
+
+void cec_splitter_unconfigured_output(struct cec_splitter_port *port);
+void cec_splitter_configured_output(struct cec_splitter_port *port);
+int cec_splitter_received_input(struct cec_splitter_port *port, struct cec_msg *msg);
+int cec_splitter_received_output(struct cec_splitter_port *port, struct cec_msg *msg,
+ struct cec_adapter *input_adap);
+void cec_splitter_nb_transmit_canceled_output(struct cec_splitter_port *port,
+ const struct cec_msg *msg,
+ struct cec_adapter *input_adap);
+bool cec_splitter_poll(struct cec_splitter *splitter,
+ struct cec_adapter *input_adap, bool debug);
+
+#endif
diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c
new file mode 100644
index 000000000000..e2eff17952ab
--- /dev/null
+++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c
@@ -0,0 +1,1836 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2021-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * Currently this driver does not fully support the serial port of the
+ * Extron, only the USB port is fully supported.
+ *
+ * Issues specific to using the serial port instead of the USB since the
+ * serial port doesn't detect if the device is powered off:
+ *
+ * - Some periodic ping mechanism is needed to detect when the Extron is
+ * powered off and when it is powered on again.
+ * - What to do when it is powered off and the driver is modprobed? Keep
+ * trying to contact the Extron indefinitely?
+ */
+
+#include <linux/completion.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+
+#include "extron-da-hd-4k-plus.h"
+
+MODULE_AUTHOR("Hans Verkuil <hverkuil@kernel.org>");
+MODULE_DESCRIPTION("Extron DA HD 4K PLUS HDMI CEC driver");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-1)");
+
+static unsigned int vendor_id;
+module_param(vendor_id, uint, 0444);
+MODULE_PARM_DESC(vendor_id, "CEC Vendor ID");
+
+static char manufacturer_name[4];
+module_param_string(manufacturer_name, manufacturer_name,
+ sizeof(manufacturer_name), 0644);
+MODULE_PARM_DESC(manufacturer_name,
+ "EDID Vendor String (3 uppercase characters)");
+
+static bool hpd_never_low;
+module_param(hpd_never_low, bool, 0644);
+MODULE_PARM_DESC(hpd_never_low, "Input HPD will never go low (1), or go low if all output HPDs are low (0, default)");
+
+#define EXTRON_TIMEOUT_SECS 6
+
+static const u8 hdmi_edid[256] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x20, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
+ 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26,
+ 0x0f, 0x50, 0x54, 0x20, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,
+ 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
+ 0x45, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18,
+ 0x87, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68,
+ 0x64, 0x6d, 0x69, 0x2d, 0x31, 0x30, 0x38, 0x30,
+ 0x70, 0x36, 0x30, 0x0a, 0x00, 0x00, 0x00, 0xfe,
+ 0x00, 0x73, 0x65, 0x72, 0x69, 0x6f, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x95,
+
+ 0x02, 0x03, 0x1b, 0xf1, 0x42, 0x10, 0x01, 0x23,
+ 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x68,
+ 0x03, 0x0c, 0x00, 0x10, 0x00, 0x00, 0x21, 0x01,
+ 0xe2, 0x00, 0xca, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89,
+};
+
+static const u8 hdmi_edid_4k_300[256] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x20, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
+ 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26,
+ 0x0f, 0x50, 0x54, 0x20, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,
+ 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
+ 0x45, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18,
+ 0x87, 0x3c, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68,
+ 0x64, 0x6d, 0x69, 0x2d, 0x34, 0x6b, 0x2d, 0x36,
+ 0x30, 0x30, 0x0a, 0x20, 0x00, 0x00, 0x00, 0xfe,
+ 0x00, 0x73, 0x65, 0x72, 0x69, 0x6f, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x87,
+
+ 0x02, 0x03, 0x1f, 0xf1, 0x43, 0x10, 0x5f, 0x01,
+ 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00,
+ 0x6b, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x00, 0x3c,
+ 0x21, 0x00, 0x20, 0x01, 0xe2, 0x00, 0xca, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6,
+};
+
+static const u8 hdmi_edid_4k_600[256] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x20, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
+ 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26,
+ 0x0f, 0x50, 0x54, 0x20, 0x00, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0xe8,
+ 0x00, 0x30, 0xf2, 0x70, 0x5a, 0x80, 0xb0, 0x58,
+ 0x8a, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18,
+ 0x87, 0x3c, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68,
+ 0x64, 0x6d, 0x69, 0x2d, 0x34, 0x6b, 0x2d, 0x36,
+ 0x30, 0x30, 0x0a, 0x20, 0x00, 0x00, 0x00, 0xfe,
+ 0x00, 0x73, 0x65, 0x72, 0x69, 0x6f, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x4c,
+
+ 0x02, 0x03, 0x28, 0xf1, 0x44, 0x61, 0x5f, 0x10,
+ 0x01, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00,
+ 0x00, 0x6b, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x00,
+ 0x3c, 0x21, 0x00, 0x20, 0x01, 0x67, 0xd8, 0x5d,
+ 0xc4, 0x01, 0x78, 0x00, 0x00, 0xe2, 0x00, 0xca,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82,
+};
+
+static int extron_send_byte(struct serio *serio, char byte)
+{
+ int err, i;
+
+ for (i = 0; i < 100; i++) {
+ err = serio_write(serio, byte);
+ if (!err)
+ break;
+ usleep_range(80, 120);
+ }
+ if (err)
+ dev_warn(&serio->dev, "unable to write byte after 100 attempts\n");
+ return err ? -EIO : 0;
+}
+
+static int extron_send_len(struct serio *serio, const char *command,
+ const unsigned char *bin, unsigned int len)
+{
+ int err = 0;
+
+ for (; !err && *command; command++)
+ err = extron_send_byte(serio, *command);
+ if (!err)
+ err = extron_send_byte(serio, '\r');
+ if (bin)
+ for (; !err && len; len--)
+ err = extron_send_byte(serio, *bin++);
+ return err;
+}
+
+static int extron_send_and_wait_len(struct extron *extron, struct extron_port *port,
+ const char *cmd, const unsigned char *bin,
+ unsigned int len, const char *response)
+{
+ int timeout = EXTRON_TIMEOUT_SECS * HZ;
+ int err;
+
+ if (debug) {
+ if (response)
+ dev_info(extron->dev, "transmit %s (response: %s)\n",
+ cmd, response);
+ else
+ dev_info(extron->dev, "transmit %s\n", cmd);
+ }
+
+ mutex_lock(&extron->serio_lock);
+ if (port) {
+ init_completion(&port->cmd_done);
+ port->cmd_error = 0;
+ port->response = response;
+ } else {
+ init_completion(&extron->cmd_done);
+ extron->cmd_error = 0;
+ extron->response = response;
+ }
+ err = extron_send_len(extron->serio, cmd, bin, len);
+
+ if (!err && response &&
+ !wait_for_completion_timeout(port ? &port->cmd_done : &extron->cmd_done, timeout)) {
+ dev_info(extron->dev, "transmit %s failed with %s (expected: %s)\n",
+ cmd, extron->reply, response);
+ err = -ETIMEDOUT;
+ }
+
+ if (!err && response && (port ? port->cmd_error : extron->cmd_error)) {
+ dev_info(extron->dev, "transmit %s failed with E%02u (expected: %s)\n",
+ cmd, port ? port->cmd_error : extron->cmd_error, response);
+ if (port)
+ port->cmd_error = 0;
+ else
+ extron->cmd_error = 0;
+ err = -EPROTO;
+ }
+ if (port)
+ port->response = NULL;
+ else
+ extron->response = NULL;
+ mutex_unlock(&extron->serio_lock);
+ return err;
+}
+
+static int extron_send_and_wait(struct extron *extron, struct extron_port *port,
+ const char *cmd, const char *response)
+{
+ return extron_send_and_wait_len(extron, port, cmd, NULL, 0, response);
+}
+
+static void extron_parse_edid(struct extron_port *port)
+{
+ const u8 *edid = port->edid;
+ unsigned int i, end;
+ u8 d;
+
+ port->has_4kp30 = false;
+ port->has_4kp60 = false;
+ port->has_qy = false;
+ port->has_qs = false;
+ /* Store Established Timings 1 and 2 */
+ port->est_i = edid[0x23];
+ port->est_ii = edid[0x24];
+
+ // Check DTDs in base block
+ for (i = 0; i < 4; i++) {
+ const u8 *dtd = edid + 0x36 + i * 18;
+ unsigned int w, h;
+ unsigned int mhz;
+ u64 pclk;
+
+ if (!dtd[0] && !dtd[1])
+ continue;
+ w = dtd[2] + ((dtd[4] & 0xf0) << 4);
+ h = dtd[5] + ((dtd[7] & 0xf0) << 4);
+ if (w != 3840 || h != 2160)
+ continue;
+
+ w += dtd[3] + ((dtd[4] & 0x0f) << 8);
+ h += dtd[6] + ((dtd[7] & 0x0f) << 8);
+ pclk = dtd[0] + (dtd[1] << 8);
+ pclk *= 100000;
+ mhz = div_u64(pclk, w * h);
+ if (mhz >= 297)
+ port->has_4kp30 = true;
+ if (mhz >= 594)
+ port->has_4kp60 = true;
+ }
+
+ if (port->edid_blocks == 1)
+ return;
+
+ edid += 128;
+
+ /* Return if not a CTA-861 extension block */
+ if (edid[0] != 0x02 || edid[1] != 0x03)
+ return;
+
+ /* search Video Data Block (tag 2) */
+ d = edid[2] & 0x7f;
+ /* Check if there are Data Blocks */
+ if (d <= 4)
+ return;
+
+ i = 4;
+ end = d;
+
+ do {
+ u8 tag = edid[i] >> 5;
+ u8 len = edid[i] & 0x1f;
+
+ /* Avoid buffer overrun in case the EDID is malformed */
+ if (i + len + 1 > 0x7f)
+ return;
+
+ switch (tag) {
+ case 2: /* Video Data Block */
+ /* Search for VIC 97 */
+ if (memchr(edid + i + 1, 97, len))
+ port->has_4kp60 = true;
+ /* Search for VIC 95 */
+ if (memchr(edid + i + 1, 95, len))
+ port->has_4kp30 = true;
+ break;
+
+ case 7: /* Use Extended Tag */
+ switch (edid[i + 1]) {
+ case 0: /* Video Capability Data Block */
+ if (edid[i + 2] & 0x80)
+ port->has_qy = true;
+ if (edid[i + 2] & 0x40)
+ port->has_qs = true;
+ break;
+ }
+ break;
+ }
+ i += len + 1;
+ } while (i < end);
+}
+
+static int get_edid_tag_location(const u8 *edid, unsigned int size,
+ u8 want_tag, u8 ext_tag)
+{
+ unsigned int offset = 128;
+ int i, end;
+ u8 d;
+
+ edid += offset;
+
+ /* Return if not a CTA-861 extension block */
+ if (size < 256 || edid[0] != 0x02 || edid[1] != 0x03)
+ return -ENOENT;
+
+ /* search tag */
+ d = edid[0x02] & 0x7f;
+ if (d <= 4)
+ return -ENOENT;
+
+ i = 0x04;
+ end = 0x00 + d;
+
+ do {
+ unsigned char tag = edid[i] >> 5;
+ unsigned char len = edid[i] & 0x1f;
+
+ if (tag != want_tag || i + len > end) {
+ i += len + 1;
+ continue;
+ }
+
+ if (tag < 7 || (len >= 1 && edid[i + 1] == ext_tag))
+ return offset + i;
+ i += len + 1;
+ } while (i < end);
+ return -ENOENT;
+}
+
+static void extron_edid_crc(u8 *edid)
+{
+ u8 sum = 0;
+ int offset;
+
+ /* Update CRC */
+ for (offset = 0; offset < 127; offset++)
+ sum += edid[offset];
+ edid[127] = 256 - sum;
+}
+
+/*
+ * Fill in EDID string. As per VESA EDID-1.3, strings are at most 13 chars
+ * long. If shorter then add a 0x0a character after the string and pad the
+ * remainder with spaces.
+ */
+static void extron_set_edid_string(u8 *start, const char *s)
+{
+ const unsigned int max_len = 13;
+ int len = strlen(s);
+
+ memset(start, ' ', max_len);
+ if (len > max_len)
+ len = max_len;
+ memcpy(start, s, len);
+ if (len < max_len)
+ start[len] = 0x0a;
+}
+
+static void extron_update_edid(struct extron_port *port, unsigned int blocks)
+{
+ int offset;
+ u8 c1, c2;
+
+ c1 = ((manufacturer_name[0] - '@') << 2) |
+ (((manufacturer_name[1] - '@') >> 3) & 0x03);
+ c2 = (((manufacturer_name[1] - '@') & 0x07) << 5) |
+ ((manufacturer_name[2] - '@') & 0x1f);
+
+ port->edid_tmp[8] = c1;
+ port->edid_tmp[9] = c2;
+
+ /* Set Established Timings, but always enable VGA */
+ port->edid_tmp[0x23] = port->est_i | 0x20;
+ port->edid_tmp[0x24] = port->est_ii;
+
+ /* Set the Monitor Name to the unit name */
+ extron_set_edid_string(port->edid_tmp + 0x5f, port->extron->unit_name);
+ /* Set the ASCII String to the CEC adapter name */
+ extron_set_edid_string(port->edid_tmp + 0x71, port->adap->name);
+
+ extron_edid_crc(port->edid_tmp);
+
+ /* Find Video Capability Data Block */
+ offset = get_edid_tag_location(port->edid_tmp, blocks * 128, 7, 0);
+ if (offset > 0) {
+ port->edid_tmp[offset + 2] &= ~0xc0;
+ if (port->has_qy)
+ port->edid_tmp[offset + 2] |= 0x80;
+ if (port->has_qs)
+ port->edid_tmp[offset + 2] |= 0x40;
+ }
+
+ extron_edid_crc(port->edid_tmp + 128);
+}
+
+static int extron_write_edid(struct extron_port *port,
+ const u8 *edid, unsigned int blocks)
+{
+ struct extron *extron = port->extron;
+ u16 phys_addr = CEC_PHYS_ADDR_INVALID;
+ int ret;
+
+ if (cec_get_edid_spa_location(edid, blocks * 128))
+ phys_addr = 0;
+
+ if (mutex_lock_interruptible(&extron->edid_lock))
+ return -EINTR;
+
+ memcpy(port->edid_tmp, edid, blocks * 128);
+
+ if (manufacturer_name[0])
+ extron_update_edid(port, blocks);
+
+ ret = extron_send_and_wait_len(port->extron, port, "W+UF256,in.bin",
+ port->edid_tmp, sizeof(port->edid_tmp),
+ "Upl");
+ if (ret)
+ goto unlock;
+ ret = extron_send_and_wait(port->extron, port, "WI1,in.binEDID",
+ "EdidI01");
+ if (ret)
+ goto unlock;
+
+ port->edid_blocks = blocks;
+ memcpy(port->edid, port->edid_tmp, blocks * 128);
+ port->read_edid = true;
+ mutex_unlock(&extron->edid_lock);
+
+ cec_s_phys_addr(port->adap, phys_addr, false);
+ return 0;
+
+unlock:
+ mutex_unlock(&extron->edid_lock);
+ return ret;
+}
+
+static void update_edid_work(struct work_struct *w)
+{
+ struct extron *extron = container_of(w, struct extron,
+ work_update_edid.work);
+ struct extron_port *in = extron->ports[extron->num_out_ports];
+ struct extron_port *p;
+ bool has_edid = false;
+ bool has_4kp30 = true;
+ bool has_4kp60 = true;
+ bool has_qy = true;
+ bool has_qs = true;
+ u8 est_i = 0xff;
+ u8 est_ii = 0xff;
+ unsigned int out;
+
+ for (out = 0; has_4kp60 && out < extron->num_out_ports; out++) {
+ p = extron->ports[out];
+ if (p->read_edid) {
+ has_4kp60 = p->has_4kp60;
+ est_i &= p->est_i;
+ est_ii &= p->est_ii;
+ has_edid = true;
+ }
+ }
+ for (out = 0; has_4kp30 && out < extron->num_out_ports; out++)
+ if (extron->ports[out]->read_edid)
+ has_4kp30 = extron->ports[out]->has_4kp30;
+
+ for (out = 0; has_qy && out < extron->num_out_ports; out++)
+ if (extron->ports[out]->read_edid)
+ has_qy = extron->ports[out]->has_qy;
+
+ for (out = 0; has_qs && out < extron->num_out_ports; out++)
+ if (extron->ports[out]->read_edid)
+ has_qs = extron->ports[out]->has_qs;
+
+ /* exit if no output port had an EDID */
+ if (!has_edid)
+ return;
+
+ /* exit if the input EDID properties remained unchanged */
+ if (has_4kp60 == in->has_4kp60 && has_4kp30 == in->has_4kp30 &&
+ has_qy == in->has_qy && has_qs == in->has_qs &&
+ est_i == in->est_i && est_ii == in->est_ii)
+ return;
+
+ in->has_4kp60 = has_4kp60;
+ in->has_4kp30 = has_4kp30;
+ in->has_qy = has_qy;
+ in->has_qs = has_qs;
+ in->est_i = est_i;
+ in->est_ii = est_ii;
+ extron_write_edid(extron->ports[extron->num_out_ports],
+ has_4kp60 ? hdmi_edid_4k_600 :
+ (has_4kp30 ? hdmi_edid_4k_300 : hdmi_edid), 2);
+}
+
+static void extron_read_edid(struct extron_port *port)
+{
+ struct extron *extron = port->extron;
+ char cmd[10], reply[10];
+ unsigned int idx;
+
+ idx = port->port.port + (port->is_input ? 0 : extron->num_in_ports);
+ snprintf(cmd, sizeof(cmd), "WR%uEDID", idx);
+ snprintf(reply, sizeof(reply), "EdidR%u", idx);
+ if (mutex_lock_interruptible(&extron->edid_lock))
+ return;
+ if (port->read_edid)
+ goto unlock;
+ extron->edid_bytes_read = 0;
+ extron->edid_port = port;
+ port->edid_blocks = 0;
+ if (!port->has_edid)
+ goto no_edid;
+
+ extron->edid_reading = true;
+
+ if (!extron_send_and_wait(extron, port, cmd, reply))
+ wait_for_completion_killable_timeout(&extron->edid_completion,
+ msecs_to_jiffies(1000));
+ if (port->edid_blocks) {
+ extron_parse_edid(port);
+ port->read_edid = true;
+ if (!port->is_input)
+ v4l2_ctrl_s_ctrl(port->ctrl_tx_edid_present, 1);
+ }
+no_edid:
+ extron->edid_reading = false;
+unlock:
+ mutex_unlock(&extron->edid_lock);
+ cancel_delayed_work_sync(&extron->work_update_edid);
+ if (manufacturer_name[0])
+ schedule_delayed_work(&extron->work_update_edid,
+ msecs_to_jiffies(1000));
+}
+
+static void extron_irq_work_handler(struct work_struct *work)
+{
+ struct extron_port *port =
+ container_of(work, struct extron_port, irq_work);
+ struct extron *extron = port->extron;
+ unsigned long flags;
+ bool update_pa;
+ u16 pa;
+ bool update_has_signal;
+ bool has_signal;
+ bool update_has_edid;
+ bool has_edid;
+ u32 status;
+
+ spin_lock_irqsave(&port->msg_lock, flags);
+ while (port->rx_msg_num) {
+ spin_unlock_irqrestore(&port->msg_lock, flags);
+ cec_received_msg(port->adap,
+ &port->rx_msg[port->rx_msg_cur_idx]);
+ spin_lock_irqsave(&port->msg_lock, flags);
+ if (port->rx_msg_num)
+ port->rx_msg_num--;
+ port->rx_msg_cur_idx =
+ (port->rx_msg_cur_idx + 1) % NUM_MSGS;
+ }
+ update_pa = port->update_phys_addr;
+ pa = port->phys_addr;
+ port->update_phys_addr = false;
+ update_has_signal = port->update_has_signal;
+ has_signal = port->has_signal;
+ port->update_has_signal = false;
+ update_has_edid = port->update_has_edid;
+ has_edid = port->has_edid;
+ port->update_has_edid = false;
+ status = port->tx_done_status;
+ port->tx_done_status = 0;
+ spin_unlock_irqrestore(&port->msg_lock, flags);
+
+ if (status)
+ cec_transmit_done(port->adap, status, 0, 0, 0, 0);
+
+ if (update_has_signal && port->is_input)
+ v4l2_ctrl_s_ctrl(port->ctrl_rx_power_present, has_signal);
+
+ if (update_has_edid && !port->is_input) {
+ v4l2_ctrl_s_ctrl(port->ctrl_tx_hotplug,
+ port->has_edid);
+ if (port->has_edid) {
+ port->port.found_sink = true;
+ port->port.lost_sink_ts = ktime_set(0, 0);
+ } else {
+ port->port.lost_sink_ts = ktime_get();
+ }
+ if (!has_edid) {
+ port->edid_blocks = 0;
+ port->read_edid = false;
+ if (extron->edid_reading && !has_edid &&
+ extron->edid_port == port)
+ extron->edid_reading = false;
+ v4l2_ctrl_s_ctrl(port->ctrl_tx_edid_present, 0);
+ } else if (!extron->edid_reading || extron->edid_port != port) {
+ extron_read_edid(port);
+ }
+ }
+ if (update_pa)
+ cec_s_phys_addr(port->adap, pa, false);
+}
+
+static void extron_process_received(struct extron_port *port, const char *data)
+{
+ struct cec_msg msg = {};
+ unsigned int len = strlen(data);
+ unsigned long irq_flags;
+ unsigned int idx;
+
+ if (!port || port->disconnected)
+ return;
+
+ if (len < 5 || (len - 2) % 3 || data[len - 2] != '*')
+ goto malformed;
+
+ while (*data != '*') {
+ int v = hex2bin(&msg.msg[msg.len], data + 1, 1);
+
+ if (*data != '%' || v)
+ goto malformed;
+ msg.len++;
+ data += 3;
+ }
+
+ spin_lock_irqsave(&port->msg_lock, irq_flags);
+ idx = (port->rx_msg_cur_idx + port->rx_msg_num) %
+ NUM_MSGS;
+ if (port->rx_msg_num == NUM_MSGS) {
+ dev_warn(port->dev,
+ "message queue is full, dropping %*ph\n",
+ msg.len, msg.msg);
+ spin_unlock_irqrestore(&port->msg_lock,
+ irq_flags);
+ return;
+ }
+ port->rx_msg_num++;
+ port->rx_msg[idx] = msg;
+ spin_unlock_irqrestore(&port->msg_lock, irq_flags);
+ if (!port->disconnected)
+ schedule_work(&port->irq_work);
+ return;
+
+malformed:
+ dev_info(port->extron->dev, "malformed msg received: '%s'\n", data);
+}
+
+static void extron_port_signal_change(struct extron_port *port, bool has_sig)
+{
+ unsigned long irq_flags;
+ bool update = false;
+
+ if (!port)
+ return;
+
+ spin_lock_irqsave(&port->msg_lock, irq_flags);
+ if (!port->update_has_signal && port->has_signal != has_sig) {
+ port->update_has_signal = true;
+ update = true;
+ }
+ port->has_signal = has_sig;
+ spin_unlock_irqrestore(&port->msg_lock, irq_flags);
+ if (update && !port->disconnected)
+ schedule_work(&port->irq_work);
+}
+
+static void extron_process_signal_change(struct extron *extron, const char *data)
+{
+ unsigned int i;
+
+ extron_port_signal_change(extron->ports[extron->num_out_ports],
+ data[0] == '1');
+ for (i = 0; i < extron->num_out_ports; i++)
+ extron_port_signal_change(extron->ports[i],
+ data[2 + 2 * i] != '0');
+}
+
+static void extron_port_edid_change(struct extron_port *port, bool has_edid)
+{
+ unsigned long irq_flags;
+ bool update = false;
+
+ if (!port)
+ return;
+
+ spin_lock_irqsave(&port->msg_lock, irq_flags);
+ if (!port->update_has_edid && port->has_edid != has_edid) {
+ port->update_has_edid = true;
+ update = true;
+ }
+ port->has_edid = has_edid;
+ spin_unlock_irqrestore(&port->msg_lock, irq_flags);
+ if (update && !port->disconnected)
+ schedule_work(&port->irq_work);
+}
+
+static void extron_process_edid_change(struct extron *extron, const char *data)
+{
+ unsigned int i;
+
+ /*
+ * Do nothing if the Extron isn't ready yet. Trying to do this
+ * while the Extron firmware is still settling will fail.
+ */
+ if (!extron->is_ready)
+ return;
+
+ for (i = 0; i < extron->num_out_ports; i++)
+ extron_port_edid_change(extron->ports[i],
+ data[2 + 2 * i] != '0');
+}
+
+static void extron_phys_addr_change(struct extron_port *port, u16 pa)
+{
+ unsigned long irq_flags;
+ bool update = false;
+
+ if (!port)
+ return;
+
+ spin_lock_irqsave(&port->msg_lock, irq_flags);
+ if (!port->update_phys_addr && port->phys_addr != pa) {
+ update = true;
+ port->update_phys_addr = true;
+ }
+ port->phys_addr = pa;
+ spin_unlock_irqrestore(&port->msg_lock, irq_flags);
+ if (update && !port->disconnected)
+ schedule_work(&port->irq_work);
+}
+
+static void extron_process_tx_done(struct extron_port *port, char status)
+{
+ unsigned long irq_flags;
+ unsigned int tx_status;
+
+ if (!port)
+ return;
+
+ switch (status) {
+ case '0':
+ tx_status = CEC_TX_STATUS_NACK | CEC_TX_STATUS_MAX_RETRIES;
+ break;
+ case '1':
+ tx_status = CEC_TX_STATUS_OK;
+ break;
+ default:
+ tx_status = CEC_TX_STATUS_ERROR;
+ break;
+ }
+ spin_lock_irqsave(&port->msg_lock, irq_flags);
+ port->tx_done_status = tx_status;
+ spin_unlock_irqrestore(&port->msg_lock, irq_flags);
+ if (!port->disconnected)
+ schedule_work(&port->irq_work);
+}
+
+static void extron_add_edid(struct extron_port *port, const char *hex)
+{
+ struct extron *extron = port ? port->extron : NULL;
+
+ if (!port || port != extron->edid_port)
+ return;
+ while (extron->edid_bytes_read < sizeof(port->edid) && *hex) {
+ int err = hex2bin(&port->edid[extron->edid_bytes_read], hex, 1);
+
+ if (err) {
+ extron->edid_reading = false;
+ complete(&extron->edid_completion);
+ break;
+ }
+ extron->edid_bytes_read++;
+ hex += 2;
+ }
+ if (extron->edid_bytes_read == 128 &&
+ port->edid[126] == 0) {
+ /* There are no extension blocks, we're done */
+ port->edid_blocks = 1;
+ extron->edid_reading = false;
+ complete(&extron->edid_completion);
+ }
+ if (extron->edid_bytes_read < sizeof(port->edid))
+ return;
+ if (!*hex)
+ port->edid_blocks = 2;
+ extron->edid_reading = false;
+ complete(&extron->edid_completion);
+}
+
+static irqreturn_t extron_interrupt(struct serio *serio, unsigned char data,
+ unsigned int flags)
+{
+ struct extron *extron = serio_get_drvdata(serio);
+ struct extron_port *port = NULL;
+ bool found_response;
+ unsigned int p;
+
+ if (data == '\r' || data == '\n') {
+ if (extron->idx == 0)
+ return IRQ_HANDLED;
+ memcpy(extron->data, extron->buf, extron->idx);
+ extron->len = extron->idx;
+ extron->data[extron->len] = 0;
+ if (debug)
+ dev_info(extron->dev, "received %s\n", extron->data);
+ extron->idx = 0;
+ if (!memcmp(extron->data, "Sig", 3) &&
+ extron->data[4] == '*') {
+ extron_process_signal_change(extron, extron->data + 3);
+ } else if (!memcmp(extron->data, "Hdcp", 4) &&
+ extron->data[5] == '*') {
+ extron_process_edid_change(extron, extron->data + 4);
+ } else if (!memcmp(extron->data, "DcecI", 5) &&
+ extron->data[5] >= '1' &&
+ extron->data[5] < '1' + extron->num_in_ports) {
+ unsigned int p = extron->data[5] - '1';
+
+ p += extron->num_out_ports;
+ extron_process_tx_done(extron->ports[p],
+ extron->data[extron->len - 1]);
+ } else if (!memcmp(extron->data, "Ceci", 4) &&
+ extron->data[4] >= '1' &&
+ extron->data[4] < '1' + extron->num_in_ports &&
+ extron->data[5] == '*') {
+ unsigned int p = extron->data[4] - '1';
+
+ p += extron->num_out_ports;
+ extron_process_received(extron->ports[p],
+ extron->data + 6);
+ } else if (!memcmp(extron->data, "DcecO", 5) &&
+ extron->data[5] >= '1' &&
+ extron->data[5] < '1' + extron->num_out_ports) {
+ unsigned int p = extron->data[5] - '1';
+
+ extron_process_tx_done(extron->ports[p],
+ extron->data[extron->len - 1]);
+ } else if (!memcmp(extron->data, "Ceco", 4) &&
+ extron->data[4] >= '1' &&
+ extron->data[4] < '1' + extron->num_out_ports &&
+ extron->data[5] == '*') {
+ unsigned int p = extron->data[4] - '1';
+
+ extron_process_received(extron->ports[p],
+ extron->data + 6);
+ } else if (!memcmp(extron->data, "Pceco", 5) &&
+ extron->data[5] >= '1' &&
+ extron->data[5] < '1' + extron->num_out_ports) {
+ unsigned int p = extron->data[5] - '1';
+ unsigned int tmp_pa[2] = { 0xff, 0xff };
+
+ if (sscanf(extron->data + 7, "%%%02x%%%02x",
+ &tmp_pa[0], &tmp_pa[1]) == 2)
+ extron_phys_addr_change(extron->ports[p],
+ tmp_pa[0] << 8 | tmp_pa[1]);
+ } else if (!memcmp(extron->data, "Pceci", 5) &&
+ extron->data[5] >= '1' &&
+ extron->data[5] < '1' + extron->num_in_ports) {
+ unsigned int p = extron->data[5] - '1';
+ unsigned int tmp_pa[2] = { 0xff, 0xff };
+
+ p += extron->num_out_ports;
+ if (sscanf(extron->data + 7, "%%%02x%%%02x",
+ &tmp_pa[0], &tmp_pa[1]) == 2)
+ extron_phys_addr_change(extron->ports[p],
+ tmp_pa[0] << 8 | tmp_pa[1]);
+ } else if (!memcmp(extron->data, "EdidR", 5) &&
+ extron->data[5] >= '1' &&
+ extron->data[5] < '1' + extron->num_ports &&
+ extron->data[6] == '*') {
+ unsigned int p = extron->data[5] - '1';
+
+ if (p)
+ p--;
+ else
+ p = extron->num_out_ports;
+ extron_add_edid(extron->ports[p], extron->data + 7);
+ } else if (extron->edid_reading && extron->len == 32 &&
+ extron->edid_port) {
+ extron_add_edid(extron->edid_port, extron->data);
+ }
+
+ found_response = false;
+ if (extron->response &&
+ !strncmp(extron->response, extron->data,
+ strlen(extron->response)))
+ found_response = true;
+
+ for (p = 0; !found_response && p < extron->num_ports; p++) {
+ port = extron->ports[p];
+ if (port && port->response &&
+ !strncmp(port->response, extron->data,
+ strlen(port->response)))
+ found_response = true;
+ }
+
+ if (!found_response && extron->response &&
+ extron->data[0] == 'E' &&
+ isdigit(extron->data[1]) &&
+ isdigit(extron->data[2]) &&
+ !extron->data[3]) {
+ extron->cmd_error = (extron->data[1] - '0') * 10 +
+ extron->data[2] - '0';
+ extron->response = NULL;
+ complete(&extron->cmd_done);
+ }
+
+ if (!found_response)
+ return IRQ_HANDLED;
+
+ memcpy(extron->reply, extron->data, extron->len);
+ extron->reply[extron->len] = 0;
+ if (!port) {
+ extron->response = NULL;
+ complete(&extron->cmd_done);
+ } else {
+ port->response = NULL;
+ complete(&port->cmd_done);
+ }
+ return IRQ_HANDLED;
+ }
+
+ if (extron->idx >= DATA_SIZE - 1) {
+ dev_info(extron->dev,
+ "throwing away %d bytes of garbage\n", extron->idx);
+ extron->idx = 0;
+ }
+ extron->buf[extron->idx++] = (char)data;
+ return IRQ_HANDLED;
+}
+
+static int extron_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct extron_port *port = cec_get_drvdata(adap);
+
+ return (port->disconnected && enable) ? -ENODEV : 0;
+}
+
+static int extron_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+{
+ struct extron_port *port = cec_get_drvdata(adap);
+ char cmd[26];
+ char resp[25];
+ u8 la = log_addr == CEC_LOG_ADDR_INVALID ? 15 : log_addr;
+ int err;
+
+ if (port->disconnected)
+ return -ENODEV;
+ snprintf(cmd, sizeof(cmd), "W%c%u*%uLCEC",
+ port->direction, port->port.port, la);
+ snprintf(resp, sizeof(resp), "Lcec%c%u*%u",
+ port->direction, port->port.port, la);
+ err = extron_send_and_wait(port->extron, port, cmd, resp);
+ return log_addr != CEC_LOG_ADDR_INVALID && err ? err : 0;
+}
+
+static int extron_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct extron_port *port = cec_get_drvdata(adap);
+ char buf[(CEC_MAX_MSG_SIZE - 1) * 3 + 1];
+ char cmd[sizeof(buf) + 14];
+ unsigned int i;
+
+ if (port->disconnected)
+ return -ENODEV;
+ buf[0] = 0;
+ for (i = 0; i < msg->len - 1; i++)
+ sprintf(buf + i * 3, "%%%02X", msg->msg[i + 1]);
+ snprintf(cmd, sizeof(cmd), "W%c%u*%u*%u*%sDCEC",
+ port->direction, port->port.port,
+ cec_msg_initiator(msg), cec_msg_destination(msg), buf);
+ return extron_send_and_wait(port->extron, port, cmd, NULL);
+}
+
+static void extron_cec_adap_unconfigured(struct cec_adapter *adap)
+{
+ struct extron_port *port = cec_get_drvdata(adap);
+
+ if (port->disconnected)
+ return;
+ if (debug)
+ dev_info(port->extron->dev, "unconfigured port %d (%s)\n",
+ port->port.port,
+ port->extron->splitter.is_standby ? "Off" : "On");
+ if (!port->is_input)
+ cec_splitter_unconfigured_output(&port->port);
+}
+
+static void extron_cec_configured(struct cec_adapter *adap)
+{
+ struct extron_port *port = cec_get_drvdata(adap);
+
+ if (port->disconnected)
+ return;
+ if (debug)
+ dev_info(port->extron->dev, "configured port %d (%s)\n",
+ port->port.port,
+ port->extron->splitter.is_standby ? "Off" : "On");
+ if (!port->is_input)
+ cec_splitter_configured_output(&port->port);
+}
+
+static void extron_cec_adap_nb_transmit_canceled(struct cec_adapter *adap,
+ const struct cec_msg *msg)
+{
+ struct extron_port *port = cec_get_drvdata(adap);
+ struct cec_adapter *input_adap;
+
+ if (!vendor_id)
+ return;
+ if (port->disconnected || port->is_input)
+ return;
+ input_adap = port->extron->ports[port->extron->num_out_ports]->adap;
+ cec_splitter_nb_transmit_canceled_output(&port->port, msg, input_adap);
+}
+
+static int extron_received(struct cec_adapter *adap, struct cec_msg *msg)
+{
+ struct extron_port *port = cec_get_drvdata(adap);
+
+ if (!vendor_id)
+ return -ENOMSG;
+ if (port->disconnected)
+ return -ENOMSG;
+ if (port->is_input)
+ return cec_splitter_received_input(&port->port, msg);
+ return cec_splitter_received_output(&port->port, msg,
+ port->extron->ports[port->extron->num_out_ports]->adap);
+}
+
+#define log_printf(adap, file, fmt, arg...) \
+ do { \
+ if (file) \
+ seq_printf((file), fmt, ## arg); \
+ else \
+ pr_info("cec-%s: " fmt, (adap)->name, ## arg); \
+ } while (0)
+
+static const char * const pwr_state[] = {
+ "on",
+ "standby",
+ "to on",
+ "to standby",
+};
+
+static void extron_adap_status_port(struct extron_port *port, struct seq_file *file)
+{
+ struct cec_adapter *adap = port->adap;
+
+ if (port->disconnected) {
+ log_printf(adap, file,
+ "\tport %u: disconnected\n", port->port.port);
+ return;
+ }
+ if (port->is_input)
+ log_printf(adap, file,
+ "\tport %u: %s signal, %s edid, %s 4kp30, %s 4kp60, %sQS/%sQY, is %s\n",
+ port->port.port,
+ port->has_signal ? "has" : "no",
+ port->has_edid ? "has" : "no",
+ port->has_4kp30 ? "has" : "no",
+ port->has_4kp60 ? "has" : "no",
+ port->has_qs ? "" : "no ",
+ port->has_qy ? "" : "no ",
+ !port->port.adap->is_configured ? "not configured" :
+ pwr_state[port->extron->splitter.is_standby]);
+ else
+ log_printf(adap, file,
+ "\tport %u: %s sink, %s signal, %s edid, %s 4kp30, %s 4kp60, %sQS/%sQY, is %sactive source, is %s\n",
+ port->port.port,
+ port->port.found_sink ? "found" : "no",
+ port->has_signal ? "has" : "no",
+ port->has_edid ? "has" : "no",
+ port->has_4kp30 ? "has" : "no",
+ port->has_4kp60 ? "has" : "no",
+ port->has_qs ? "" : "no ",
+ port->has_qy ? "" : "no ",
+ port->port.is_active_source ? "" : "not ",
+ !port->port.adap->is_configured ? "not configured" :
+ pwr_state[port->port.power_status & 3]);
+ if (port->port.out_give_device_power_status_seq)
+ log_printf(adap, file,
+ "\tport %u: querying power status (%u, %lldms)\n",
+ port->port.port,
+ port->port.out_give_device_power_status_seq & ~(1 << 31),
+ ktime_ms_delta(ktime_get(),
+ port->port.out_give_device_power_status_ts));
+ if (port->port.out_request_current_latency_seq)
+ log_printf(adap, file,
+ "\tport %u: querying latency (%u, %lldms)\n",
+ port->port.port,
+ port->port.out_request_current_latency_seq & ~(1 << 31),
+ ktime_ms_delta(ktime_get(),
+ port->port.out_request_current_latency_ts));
+}
+
+static void extron_adap_status(struct cec_adapter *adap, struct seq_file *file)
+{
+ struct extron_port *port = cec_get_drvdata(adap);
+ struct extron *extron = port->extron;
+ unsigned int i;
+
+ log_printf(adap, file, "name: %s type: %s\n",
+ extron->unit_name, extron->unit_type);
+ log_printf(adap, file, "model: 60-160%c-01 (1 input, %u outputs)\n",
+ '6' + extron->num_out_ports / 2, extron->num_out_ports);
+ log_printf(adap, file, "firmware version: %s CEC engine version: %s\n",
+ extron->unit_fw_version, extron->unit_cec_engine_version);
+ if (extron->hpd_never_low)
+ log_printf(adap, file, "always keep input HPD high\n");
+ else
+ log_printf(adap, file,
+ "pull input HPD low if all output HPDs are low\n");
+ if (vendor_id)
+ log_printf(adap, file,
+ "splitter vendor ID: 0x%06x\n", vendor_id);
+ if (manufacturer_name[0])
+ log_printf(adap, file, "splitter manufacturer name: %s\n",
+ manufacturer_name);
+ log_printf(adap, file, "splitter power status: %s\n",
+ pwr_state[extron->splitter.is_standby]);
+ log_printf(adap, file, "%s port: %d (%s)\n",
+ port->is_input ? "input" : "output",
+ port->port.port, port->name);
+ log_printf(adap, file, "splitter input port:\n");
+ extron_adap_status_port(extron->ports[extron->num_out_ports], file);
+
+ log_printf(adap, file, "splitter output ports:\n");
+ for (i = 0; i < extron->num_out_ports; i++)
+ extron_adap_status_port(extron->ports[i], file);
+
+ if (!port->has_edid || !port->read_edid)
+ return;
+
+ for (i = 0; i < port->edid_blocks * 128; i += 16) {
+ if (i % 128 == 0)
+ log_printf(adap, file, "\n");
+ log_printf(adap, file, "EDID: %*ph\n", 16, port->edid + i);
+ }
+}
+
+static const struct cec_adap_ops extron_cec_adap_ops = {
+ .adap_enable = extron_cec_adap_enable,
+ .adap_log_addr = extron_cec_adap_log_addr,
+ .adap_transmit = extron_cec_adap_transmit,
+ .adap_nb_transmit_canceled = extron_cec_adap_nb_transmit_canceled,
+ .adap_unconfigured = extron_cec_adap_unconfigured,
+ .adap_status = extron_adap_status,
+ .configured = extron_cec_configured,
+ .received = extron_received,
+};
+
+static int extron_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct extron_port *port = video_drvdata(file);
+
+ strscpy(cap->driver, "extron-da-hd-4k-plus-cec", sizeof(cap->driver));
+ strscpy(cap->card, cap->driver, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "serio:%s", port->name);
+ return 0;
+}
+
+static int extron_enum_input(struct file *file, void *priv, struct v4l2_input *inp)
+{
+ struct extron_port *port = video_drvdata(file);
+
+ if (inp->index)
+ return -EINVAL;
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ snprintf(inp->name, sizeof(inp->name), "HDMI IN %u", port->port.port);
+ inp->status = v4l2_ctrl_g_ctrl(port->ctrl_rx_power_present) ?
+ 0 : V4L2_IN_ST_NO_SIGNAL;
+ return 0;
+}
+
+static int extron_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int extron_s_input(struct file *file, void *priv, unsigned int i)
+{
+ return i ? -EINVAL : 0;
+}
+
+static int extron_enum_output(struct file *file, void *priv, struct v4l2_output *out)
+{
+ struct extron_port *port = video_drvdata(file);
+
+ if (out->index)
+ return -EINVAL;
+ out->type = V4L2_OUTPUT_TYPE_ANALOG;
+ snprintf(out->name, sizeof(out->name), "HDMI OUT %u", port->port.port);
+ return 0;
+}
+
+static int extron_g_output(struct file *file, void *priv, unsigned int *o)
+{
+ *o = 0;
+ return 0;
+}
+
+static int extron_s_output(struct file *file, void *priv, unsigned int o)
+{
+ return o ? -EINVAL : 0;
+}
+
+static int extron_g_edid(struct file *file, void *priv,
+ struct v4l2_edid *edid)
+{
+ struct extron_port *port = video_drvdata(file);
+
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+ if (port->disconnected)
+ return -ENODEV;
+ if (edid->pad)
+ return -EINVAL;
+ if (!port->has_edid)
+ return -ENODATA;
+ if (!port->read_edid)
+ extron_read_edid(port);
+ if (!port->read_edid)
+ return -ENODATA;
+ if (edid->start_block == 0 && edid->blocks == 0) {
+ edid->blocks = port->edid_blocks;
+ return 0;
+ }
+ if (edid->start_block >= port->edid_blocks)
+ return -EINVAL;
+ if (edid->blocks > port->edid_blocks - edid->start_block)
+ edid->blocks = port->edid_blocks - edid->start_block;
+ memcpy(edid->edid, port->edid + edid->start_block * 128, edid->blocks * 128);
+ return 0;
+}
+
+static int extron_s_edid(struct file *file, void *priv, struct v4l2_edid *edid)
+{
+ struct extron_port *port = video_drvdata(file);
+
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+ if (port->disconnected)
+ return -ENODEV;
+ if (edid->pad)
+ return -EINVAL;
+
+ /* Unfortunately it is not possible to clear the EDID */
+ if (edid->blocks == 0)
+ return -EINVAL;
+
+ if (edid->blocks > MAX_EDID_BLOCKS) {
+ edid->blocks = MAX_EDID_BLOCKS;
+ return -E2BIG;
+ }
+
+ if (cec_get_edid_spa_location(edid->edid, edid->blocks * 128))
+ v4l2_set_edid_phys_addr(edid->edid, edid->blocks * 128, 0);
+ extron_parse_edid(port);
+ return extron_write_edid(port, edid->edid, edid->blocks);
+}
+
+static int extron_log_status(struct file *file, void *priv)
+{
+ struct extron_port *port = video_drvdata(file);
+
+ extron_adap_status(port->adap, NULL);
+ return v4l2_ctrl_log_status(file, priv);
+}
+
+static const struct v4l2_ioctl_ops extron_ioctl_ops = {
+ .vidioc_querycap = extron_querycap,
+ .vidioc_enum_input = extron_enum_input,
+ .vidioc_g_input = extron_g_input,
+ .vidioc_s_input = extron_s_input,
+ .vidioc_enum_output = extron_enum_output,
+ .vidioc_g_output = extron_g_output,
+ .vidioc_s_output = extron_s_output,
+ .vidioc_g_edid = extron_g_edid,
+ .vidioc_s_edid = extron_s_edid,
+ .vidioc_log_status = extron_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations extron_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct video_device extron_videodev = {
+ .name = "extron-da-hd-4k-plus-cec",
+ .vfl_dir = VFL_DIR_RX,
+ .fops = &extron_fops,
+ .ioctl_ops = &extron_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release_empty,
+};
+
+static void extron_disconnect(struct serio *serio)
+{
+ struct extron *extron = serio_get_drvdata(serio);
+ unsigned int p;
+
+ kthread_stop(extron->kthread_setup);
+
+ for (p = 0; p < extron->num_ports; p++) {
+ struct extron_port *port = extron->ports[p];
+
+ if (!port)
+ continue;
+ port->disconnected = true;
+ cancel_work_sync(&port->irq_work);
+ }
+ cancel_delayed_work_sync(&extron->work_update_edid);
+ for (p = 0; p < extron->num_ports; p++) {
+ struct extron_port *port = extron->ports[p];
+
+ if (!port)
+ continue;
+
+ if (port->cec_was_registered) {
+ if (cec_is_registered(port->adap))
+ cec_unregister_adapter(port->adap);
+ /*
+ * After registering the adapter, the
+ * extron_setup_thread() function took an extra
+ * reference to the device. We call the corresponding
+ * put here.
+ */
+ cec_put_device(port->adap);
+ } else {
+ cec_delete_adapter(port->adap);
+ }
+ video_unregister_device(&port->vdev);
+ }
+
+ complete(&extron->edid_completion);
+
+ for (p = 0; p < extron->num_ports; p++) {
+ struct extron_port *port = extron->ports[p];
+
+ if (!port)
+ continue;
+ v4l2_ctrl_handler_free(&port->hdl);
+ mutex_destroy(&port->video_lock);
+ kfree(port);
+ }
+ mutex_destroy(&extron->edid_lock);
+ mutex_destroy(&extron->serio_lock);
+ extron->serio = NULL;
+ serio_set_drvdata(serio, NULL);
+ serio_close(serio);
+}
+
+static int extron_setup(struct extron *extron)
+{
+ struct serio *serio = extron->serio;
+ struct extron_port *port;
+ u8 *reply = extron->reply;
+ unsigned int p;
+ unsigned int major, minor;
+ int err;
+
+ /*
+ * Attempt to disable CEC: avoid received CEC messages
+ * from interfering with the other serial port traffic.
+ */
+ extron_send_and_wait(extron, NULL, "WI1*0CCEC", NULL);
+ extron_send_and_wait(extron, NULL, "WO0*CCEC", NULL);
+
+ /* Obtain unit part number */
+ err = extron_send_and_wait(extron, NULL, "N", "Pno");
+ if (err)
+ return err;
+ dev_info(extron->dev, "Unit part number: %s\n", reply + 3);
+ if (strcmp(reply + 3, "60-1607-01") &&
+ strcmp(reply + 3, "60-1608-01") &&
+ strcmp(reply + 3, "60-1609-01")) {
+ dev_err(extron->dev, "Unsupported model\n");
+ return -ENODEV;
+ }
+ /* Up to 6 output ports and one input port */
+ extron->num_out_ports = 2 * (reply[9] - '6');
+ extron->splitter.num_out_ports = extron->num_out_ports;
+ extron->splitter.ports = extron->splitter_ports;
+ extron->splitter.dev = extron->dev;
+ extron->num_in_ports = 1;
+ extron->num_ports = extron->num_out_ports + extron->num_in_ports;
+ dev_info(extron->dev, "Unit output ports: %d\n", extron->num_out_ports);
+ dev_info(extron->dev, "Unit input ports: %d\n", extron->num_in_ports);
+
+ err = extron_send_and_wait(extron, NULL, "W CN", "Ipn ");
+ if (err)
+ return err;
+ dev_info(extron->dev, "Unit name: %s\n", reply + 4);
+ strscpy(extron->unit_name, reply + 4, sizeof(extron->unit_name));
+
+ err = extron_send_and_wait(extron, NULL, "*Q", "Bld");
+ if (err)
+ return err;
+ dev_info(extron->dev, "Unit FW Version: %s\n", reply + 3);
+ strscpy(extron->unit_fw_version, reply + 3,
+ sizeof(extron->unit_fw_version));
+ if (sscanf(reply + 3, "%u.%u.", &major, &minor) < 2 ||
+ major < 1 || minor < 2) {
+ dev_err(extron->dev,
+ "Unsupported FW version (only 1.02 or up is supported)\n");
+ return -ENODEV;
+ }
+
+ err = extron_send_and_wait(extron, NULL, "2i", "Inf02*");
+ if (err)
+ return err;
+ dev_info(extron->dev, "Unit Type: %s\n", reply + 6);
+ strscpy(extron->unit_type, reply + 6, sizeof(extron->unit_type));
+
+ err = extron_send_and_wait(extron, NULL, "39Q", "Ver39*");
+ if (err)
+ return err;
+ dev_info(extron->dev, "CEC Engine Version: %s\n", reply + 6);
+ strscpy(extron->unit_cec_engine_version, reply + 6,
+ sizeof(extron->unit_cec_engine_version));
+
+ /* Disable CEC */
+ err = extron_send_and_wait(extron, NULL, "WI1*0CCEC", "CcecI1*");
+ if (err)
+ return err;
+ err = extron_send_and_wait(extron, NULL, "WO0*CCEC", "CcecO0");
+ if (err)
+ return err;
+
+ extron->hpd_never_low = hpd_never_low;
+
+ /* Pull input port HPD low if all output ports also have a low HPD */
+ if (hpd_never_low) {
+ dev_info(extron->dev, "Always keep input HPD high\n");
+ } else {
+ dev_info(extron->dev, "Pull input HPD low if all output HPDs are low\n");
+ extron_send_and_wait(extron, NULL, "W1ihpd", "Ihpd1");
+ }
+
+ for (p = 0; p < extron->num_ports; p++) {
+ u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL;
+
+ if (vendor_id)
+ caps &= ~CEC_CAP_LOG_ADDRS;
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ INIT_WORK(&port->irq_work, extron_irq_work_handler);
+ spin_lock_init(&port->msg_lock);
+ mutex_init(&port->video_lock);
+ port->extron = extron;
+ port->is_input = p >= extron->num_out_ports;
+ port->direction = port->is_input ? 'I' : 'O';
+ port->port.port = 1 + (port->is_input ? p - extron->num_out_ports : p);
+ port->port.splitter = &extron->splitter;
+ port->phys_addr = CEC_PHYS_ADDR_INVALID;
+ snprintf(port->name, sizeof(port->name), "%s-%s-%u",
+ dev_name(&serio->dev), port->is_input ? "in" : "out",
+ port->port.port);
+
+ port->dev = extron->dev;
+ port->adap = cec_allocate_adapter(&extron_cec_adap_ops, port,
+ port->name, caps, 1);
+ err = PTR_ERR_OR_ZERO(port->adap);
+ if (err < 0) {
+ kfree(port);
+ return err;
+ }
+
+ port->adap->xfer_timeout_ms = EXTRON_TIMEOUT_SECS * 1000;
+ port->port.adap = port->adap;
+ port->vdev = extron_videodev;
+ port->vdev.lock = &port->video_lock;
+ port->vdev.v4l2_dev = &extron->v4l2_dev;
+ port->vdev.ctrl_handler = &port->hdl;
+ port->vdev.device_caps = V4L2_CAP_EDID;
+ video_set_drvdata(&port->vdev, port);
+
+ v4l2_ctrl_handler_init(&port->hdl, 2);
+
+ if (port->is_input) {
+ port->vdev.vfl_dir = VFL_DIR_RX;
+ port->ctrl_rx_power_present =
+ v4l2_ctrl_new_std(&port->hdl, NULL,
+ V4L2_CID_DV_RX_POWER_PRESENT,
+ 0, 1, 0, 0);
+ port->has_edid = true;
+ } else {
+ port->vdev.vfl_dir = VFL_DIR_TX;
+ port->ctrl_tx_hotplug =
+ v4l2_ctrl_new_std(&port->hdl, NULL,
+ V4L2_CID_DV_TX_HOTPLUG,
+ 0, 1, 0, 0);
+ port->ctrl_tx_edid_present =
+ v4l2_ctrl_new_std(&port->hdl, NULL,
+ V4L2_CID_DV_TX_EDID_PRESENT,
+ 0, 1, 0, 0);
+ }
+
+ err = port->hdl.error;
+ if (err < 0) {
+ cec_delete_adapter(port->adap);
+ kfree(port);
+ return err;
+ }
+ extron->ports[p] = port;
+ extron->splitter_ports[p] = &port->port;
+ if (port->is_input && manufacturer_name[0])
+ extron_write_edid(port, hdmi_edid, 2);
+ }
+
+ /* Enable CEC (manual mode, i.e. controlled by the driver) */
+ err = extron_send_and_wait(extron, NULL, "WI1*20CCEC", "CcecI1*");
+ if (err)
+ return err;
+
+ err = extron_send_and_wait(extron, NULL, "WO20*CCEC", "CcecO20");
+ if (err)
+ return err;
+
+ /* Set logical addresses to 15 */
+ err = extron_send_and_wait(extron, NULL, "WI1*15LCEC", "LcecI1*15");
+ if (err)
+ return err;
+
+ for (p = 0; p < extron->num_out_ports; p++) {
+ char cmd[20];
+ char resp[20];
+
+ snprintf(cmd, sizeof(cmd), "WO%u*15LCEC", p + 1);
+ snprintf(resp, sizeof(resp), "LcecO%u*15", p + 1);
+ err = extron_send_and_wait(extron, extron->ports[p], cmd, resp);
+ if (err)
+ return err;
+ }
+
+ /*
+ * The Extron is now ready for operation. Specifically it is now
+ * possible to retrieve EDIDs.
+ */
+ extron->is_ready = true;
+
+ /* Query HDCP and Signal states, used to update the initial state */
+ err = extron_send_and_wait(extron, NULL, "WHDCP", "Hdcp");
+ if (err)
+ return err;
+
+ return extron_send_and_wait(extron, NULL, "WLS", "Sig");
+}
+
+static int extron_setup_thread(void *_extron)
+{
+ struct extron *extron = _extron;
+ struct extron_port *port;
+ unsigned int p;
+ bool poll_splitter = false;
+ bool was_connected = true;
+ int err;
+
+ while (1) {
+ if (kthread_should_stop())
+ return 0;
+ err = extron_send_and_wait(extron, NULL, "W3CV", "Vrb3");
+ // that should make it possible to detect a serio disconnect
+ // here by stopping the workqueue
+ if (err >= 0)
+ break;
+ was_connected = false;
+ ssleep(1);
+ }
+
+ /*
+ * If the Extron was not connected at probe() time, i.e. it just got
+ * powered up and while the serial port is working, the firmware is
+ * still booting up, then wait 10 seconds for the firmware to settle.
+ *
+ * Trying to continue too soon means that some commands will not
+ * work yet.
+ */
+ if (!was_connected)
+ ssleep(10);
+
+ err = extron_setup(extron);
+ if (err)
+ goto disable_ports;
+
+ for (p = 0; p < extron->num_ports; p++) {
+ struct cec_log_addrs log_addrs = {};
+
+ port = extron->ports[p];
+ if (port->is_input && manufacturer_name[0])
+ v4l2_disable_ioctl(&port->vdev, VIDIOC_S_EDID);
+ err = video_register_device(&port->vdev, VFL_TYPE_VIDEO, -1);
+ if (err) {
+ v4l2_err(&extron->v4l2_dev, "Failed to register video device\n");
+ goto disable_ports;
+ }
+
+ err = cec_register_adapter(port->adap, extron->dev);
+ if (err < 0)
+ goto disable_ports;
+ port->dev = &port->adap->devnode.dev;
+ port->cec_was_registered = true;
+ /*
+ * This driver is unusual in that the whole setup takes place
+ * in a thread since it can take such a long time before the
+ * Extron Splitter boots up, and you do not want to block the
+ * probe function on this driver. In addition, as soon as
+ * CEC adapters come online, they can be used, and you cannot
+ * just unregister them again if an error occurs, since that
+ * can delete the underlying CEC adapter, which might already
+ * be in use.
+ *
+ * So we take an additional reference to the adapter. This
+ * allows us to unregister the device node if needed, without
+ * deleting the actual adapter.
+ *
+ * In the disconnect function we will do the corresponding
+ * put call to ensure the adapter is deleted.
+ */
+ cec_get_device(port->adap);
+
+ /*
+ * If vendor_id wasn't set, then userspace configures the
+ * CEC devices. Otherwise the driver configures the CEC
+ * devices as TV (input) and Playback (outputs) devices
+ * and the driver processes all CEC messages.
+ */
+ if (!vendor_id)
+ continue;
+
+ log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;
+ log_addrs.num_log_addrs = 1;
+ log_addrs.vendor_id = vendor_id;
+ if (port->is_input) {
+ strscpy(log_addrs.osd_name, "Splitter In",
+ sizeof(log_addrs.osd_name));
+ log_addrs.log_addr_type[0] = CEC_LOG_ADDR_TYPE_TV;
+ log_addrs.primary_device_type[0] = CEC_OP_PRIM_DEVTYPE_TV;
+ log_addrs.all_device_types[0] = CEC_OP_ALL_DEVTYPE_TV;
+ } else {
+ snprintf(log_addrs.osd_name, sizeof(log_addrs.osd_name),
+ "Splitter Out%u", port->port.port);
+ log_addrs.log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK;
+ log_addrs.primary_device_type[0] = CEC_OP_PRIM_DEVTYPE_PLAYBACK;
+ log_addrs.all_device_types[0] = CEC_OP_ALL_DEVTYPE_PLAYBACK;
+ }
+ err = cec_s_log_addrs(port->adap, &log_addrs, false);
+ if (err < 0)
+ goto disable_ports;
+ }
+ poll_splitter = true;
+
+ port = extron->ports[extron->num_out_ports];
+ while (!kthread_should_stop()) {
+ ssleep(1);
+ if (hpd_never_low != extron->hpd_never_low) {
+ /*
+ * Keep input port HPD high at all times, or pull it low
+ * if all output ports also have a low HPD
+ */
+ if (hpd_never_low) {
+ dev_info(extron->dev, "Always keep input HPD high\n");
+ extron_send_and_wait(extron, NULL, "W0ihpd", "Ihpd0");
+ } else {
+ dev_info(extron->dev, "Pull input HPD low if all output HPDs are low\n");
+ extron_send_and_wait(extron, NULL, "W1ihpd", "Ihpd1");
+ }
+ extron->hpd_never_low = hpd_never_low;
+ }
+ if (poll_splitter &&
+ cec_splitter_poll(&extron->splitter, port->adap, debug) &&
+ manufacturer_name[0]) {
+ /*
+ * Sinks were lost, so see if the input edid needs to
+ * be updated.
+ */
+ cancel_delayed_work_sync(&extron->work_update_edid);
+ schedule_delayed_work(&extron->work_update_edid,
+ msecs_to_jiffies(1000));
+ }
+ }
+ return 0;
+
+disable_ports:
+ extron->is_ready = false;
+ for (p = 0; p < extron->num_ports; p++) {
+ struct extron_port *port = extron->ports[p];
+
+ if (!port)
+ continue;
+ port->disconnected = true;
+ cancel_work_sync(&port->irq_work);
+ video_unregister_device(&port->vdev);
+ if (port->cec_was_registered)
+ cec_unregister_adapter(port->adap);
+ }
+ cancel_delayed_work_sync(&extron->work_update_edid);
+ complete(&extron->edid_completion);
+ dev_err(extron->dev, "Setup failed with error %d\n", err);
+ while (!kthread_should_stop())
+ ssleep(1);
+ return err;
+}
+
+static int extron_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct extron *extron;
+ int err = -ENOMEM;
+
+ if (manufacturer_name[0] &&
+ (!isupper(manufacturer_name[0]) ||
+ !isupper(manufacturer_name[1]) ||
+ !isupper(manufacturer_name[2]))) {
+ dev_warn(&serio->dev, "ignoring invalid manufacturer name\n");
+ manufacturer_name[0] = 0;
+ }
+
+ extron = kzalloc(sizeof(*extron), GFP_KERNEL);
+
+ if (!extron)
+ return -ENOMEM;
+
+ extron->serio = serio;
+ extron->dev = &serio->dev;
+ mutex_init(&extron->serio_lock);
+ mutex_init(&extron->edid_lock);
+ INIT_DELAYED_WORK(&extron->work_update_edid, update_edid_work);
+
+ err = v4l2_device_register(extron->dev, &extron->v4l2_dev);
+ if (err)
+ goto free_device;
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto unreg_v4l2_dev;
+
+ serio_set_drvdata(serio, extron);
+ init_completion(&extron->edid_completion);
+
+ extron->kthread_setup = kthread_run(extron_setup_thread, extron,
+ "extron-da-hd-4k-plus-cec-%s", dev_name(&serio->dev));
+ if (!IS_ERR(extron->kthread_setup))
+ return 0;
+
+ dev_err(extron->dev, "kthread_run() failed\n");
+ err = PTR_ERR(extron->kthread_setup);
+
+ extron->serio = NULL;
+ serio_set_drvdata(serio, NULL);
+ serio_close(serio);
+unreg_v4l2_dev:
+ v4l2_device_unregister(&extron->v4l2_dev);
+free_device:
+ mutex_destroy(&extron->edid_lock);
+ mutex_destroy(&extron->serio_lock);
+ kfree(extron);
+ return err;
+}
+
+static const struct serio_device_id extron_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_EXTRON_DA_HD_4K_PLUS,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, extron_serio_ids);
+
+static struct serio_driver extron_drv = {
+ .driver = {
+ .name = "extron-da-hd-4k-plus-cec",
+ },
+ .description = "Extron DA HD 4K PLUS HDMI CEC driver",
+ .id_table = extron_serio_ids,
+ .interrupt = extron_interrupt,
+ .connect = extron_connect,
+ .disconnect = extron_disconnect,
+};
+
+module_serio_driver(extron_drv);
diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.h b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.h
new file mode 100644
index 000000000000..b79f1253ab5d
--- /dev/null
+++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * Copyright 2021-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#ifndef _EXTRON_DA_HD_4K_PLUS_H_
+#define _EXTRON_DA_HD_4K_PLUS_H_
+
+#include <linux/kthread.h>
+#include <linux/serio.h>
+#include <linux/workqueue.h>
+#include <media/cec.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+
+#include "cec-splitter.h"
+
+#define DATA_SIZE 256
+
+#define PING_PERIOD (15 * HZ)
+
+#define NUM_MSGS CEC_MAX_MSG_RX_QUEUE_SZ
+
+#define MAX_PORTS (1 + 6)
+
+#define MAX_EDID_BLOCKS 2
+
+struct extron;
+
+struct extron_port {
+ struct cec_splitter_port port;
+ struct device *dev;
+ struct cec_adapter *adap;
+ struct video_device vdev;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *ctrl_rx_power_present;
+ struct v4l2_ctrl *ctrl_tx_hotplug;
+ struct v4l2_ctrl *ctrl_tx_edid_present;
+ bool is_input;
+ char direction;
+ char name[26];
+ unsigned char edid[MAX_EDID_BLOCKS * 128];
+ unsigned char edid_tmp[MAX_EDID_BLOCKS * 128];
+ unsigned int edid_blocks;
+ bool read_edid;
+ struct extron *extron;
+ struct work_struct irq_work;
+ struct completion cmd_done;
+ const char *response;
+ unsigned int cmd_error;
+ struct cec_msg rx_msg[NUM_MSGS];
+ unsigned int rx_msg_cur_idx, rx_msg_num;
+ /* protect rx_msg_cur_idx and rx_msg_num */
+ spinlock_t msg_lock;
+ u32 tx_done_status;
+ bool update_phys_addr;
+ u16 phys_addr;
+ bool cec_was_registered;
+ bool disconnected;
+ bool update_has_signal;
+ bool has_signal;
+ bool update_has_edid;
+ bool has_edid;
+ bool has_4kp30;
+ bool has_4kp60;
+ bool has_qy;
+ bool has_qs;
+ u8 est_i, est_ii;
+
+ /* locks access to the video_device */
+ struct mutex video_lock;
+};
+
+struct extron {
+ struct cec_splitter splitter;
+ struct device *dev;
+ struct serio *serio;
+ /* locks access to serio */
+ struct mutex serio_lock;
+ unsigned int num_ports;
+ unsigned int num_in_ports;
+ unsigned int num_out_ports;
+ char unit_name[32];
+ char unit_type[64];
+ char unit_fw_version[32];
+ char unit_cec_engine_version[32];
+ struct extron_port *ports[MAX_PORTS];
+ struct cec_splitter_port *splitter_ports[MAX_PORTS];
+ struct v4l2_device v4l2_dev;
+ bool hpd_never_low;
+ struct task_struct *kthread_setup;
+ struct delayed_work work_update_edid;
+
+ /* serializes EDID reading */
+ struct mutex edid_lock;
+ unsigned int edid_bytes_read;
+ struct extron_port *edid_port;
+ struct completion edid_completion;
+ bool edid_reading;
+ bool is_ready;
+
+ struct completion cmd_done;
+ const char *response;
+ unsigned int cmd_error;
+ char data[DATA_SIZE];
+ unsigned int len;
+ char reply[DATA_SIZE];
+ char buf[DATA_SIZE];
+ unsigned int idx;
+};
+
+#endif
diff --git a/drivers/media/cec/usb/pulse8/Kconfig b/drivers/media/cec/usb/pulse8/Kconfig
new file mode 100644
index 000000000000..a0224ef80e6c
--- /dev/null
+++ b/drivers/media/cec/usb/pulse8/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config USB_PULSE8_CEC
+ tristate "Pulse Eight HDMI CEC"
+ select CEC_CORE
+ select USB
+ select USB_ACM
+ select SERIO
+ select SERIO_SERPORT
+ help
+ This is a cec driver for the Pulse Eight HDMI CEC device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pulse8-cec.
diff --git a/drivers/media/cec/usb/pulse8/Makefile b/drivers/media/cec/usb/pulse8/Makefile
new file mode 100644
index 000000000000..7816c68bf928
--- /dev/null
+++ b/drivers/media/cec/usb/pulse8/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_USB_PULSE8_CEC) += pulse8-cec.o
diff --git a/drivers/media/cec/usb/pulse8/pulse8-cec.c b/drivers/media/cec/usb/pulse8/pulse8-cec.c
new file mode 100644
index 000000000000..60569f1670fe
--- /dev/null
+++ b/drivers/media/cec/usb/pulse8/pulse8-cec.c
@@ -0,0 +1,924 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Pulse Eight HDMI CEC driver
+ *
+ * Copyright 2016 Hans Verkuil <hverkuil@kernel.org>
+ */
+
+/*
+ * Notes:
+ *
+ * - Devices with firmware version < 2 do not store their configuration in
+ * EEPROM.
+ *
+ * - In autonomous mode, only messages from a TV will be acknowledged, even
+ * polling messages. Upon receiving a message from a TV, the dongle will
+ * respond to messages from any logical address.
+ *
+ * - In autonomous mode, the dongle will by default reply Feature Abort
+ * [Unrecognized Opcode] when it receives Give Device Vendor ID. It will
+ * however observe vendor ID's reported by other devices and possibly
+ * alter this behavior. When TV's (and TV's only) report that their vendor ID
+ * is LG (0x00e091), the dongle will itself reply that it has the same vendor
+ * ID, and it will respond to at least one vendor specific command.
+ *
+ * - In autonomous mode, the dongle is known to attempt wakeup if it receives
+ * <User Control Pressed> ["Power On"], ["Power] or ["Power Toggle"], or if it
+ * receives <Set Stream Path> with its own physical address. It also does this
+ * if it receives <Vendor Specific Command> [0x03 0x00] from an LG TV.
+ */
+
+#include <linux/completion.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+
+#include <media/cec.h>
+
+MODULE_AUTHOR("Hans Verkuil <hverkuil@kernel.org>");
+MODULE_DESCRIPTION("Pulse Eight HDMI CEC driver");
+MODULE_LICENSE("GPL");
+
+static int debug;
+static int persistent_config;
+module_param(debug, int, 0644);
+module_param(persistent_config, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+MODULE_PARM_DESC(persistent_config, "read config from persistent memory (0-1)");
+
+enum pulse8_msgcodes {
+ MSGCODE_NOTHING = 0,
+ MSGCODE_PING,
+ MSGCODE_TIMEOUT_ERROR,
+ MSGCODE_HIGH_ERROR,
+ MSGCODE_LOW_ERROR,
+ MSGCODE_FRAME_START,
+ MSGCODE_FRAME_DATA,
+ MSGCODE_RECEIVE_FAILED,
+ MSGCODE_COMMAND_ACCEPTED, /* 0x08 */
+ MSGCODE_COMMAND_REJECTED,
+ MSGCODE_SET_ACK_MASK,
+ MSGCODE_TRANSMIT,
+ MSGCODE_TRANSMIT_EOM,
+ MSGCODE_TRANSMIT_IDLETIME,
+ MSGCODE_TRANSMIT_ACK_POLARITY,
+ MSGCODE_TRANSMIT_LINE_TIMEOUT,
+ MSGCODE_TRANSMIT_SUCCEEDED, /* 0x10 */
+ MSGCODE_TRANSMIT_FAILED_LINE,
+ MSGCODE_TRANSMIT_FAILED_ACK,
+ MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA,
+ MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE,
+ MSGCODE_FIRMWARE_VERSION,
+ MSGCODE_START_BOOTLOADER,
+ MSGCODE_GET_BUILDDATE,
+ MSGCODE_SET_CONTROLLED, /* 0x18 */
+ MSGCODE_GET_AUTO_ENABLED,
+ MSGCODE_SET_AUTO_ENABLED,
+ MSGCODE_GET_DEFAULT_LOGICAL_ADDRESS,
+ MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS,
+ MSGCODE_GET_LOGICAL_ADDRESS_MASK,
+ MSGCODE_SET_LOGICAL_ADDRESS_MASK,
+ MSGCODE_GET_PHYSICAL_ADDRESS,
+ MSGCODE_SET_PHYSICAL_ADDRESS, /* 0x20 */
+ MSGCODE_GET_DEVICE_TYPE,
+ MSGCODE_SET_DEVICE_TYPE,
+ MSGCODE_GET_HDMI_VERSION, /* Removed in FW >= 10 */
+ MSGCODE_SET_HDMI_VERSION,
+ MSGCODE_GET_OSD_NAME,
+ MSGCODE_SET_OSD_NAME,
+ MSGCODE_WRITE_EEPROM,
+ MSGCODE_GET_ADAPTER_TYPE, /* 0x28 */
+ MSGCODE_SET_ACTIVE_SOURCE,
+ MSGCODE_GET_AUTO_POWER_ON, /* New for FW >= 10 */
+ MSGCODE_SET_AUTO_POWER_ON,
+
+ MSGCODE_FRAME_EOM = 0x80,
+ MSGCODE_FRAME_ACK = 0x40,
+};
+
+static const char * const pulse8_msgnames[] = {
+ "NOTHING",
+ "PING",
+ "TIMEOUT_ERROR",
+ "HIGH_ERROR",
+ "LOW_ERROR",
+ "FRAME_START",
+ "FRAME_DATA",
+ "RECEIVE_FAILED",
+ "COMMAND_ACCEPTED",
+ "COMMAND_REJECTED",
+ "SET_ACK_MASK",
+ "TRANSMIT",
+ "TRANSMIT_EOM",
+ "TRANSMIT_IDLETIME",
+ "TRANSMIT_ACK_POLARITY",
+ "TRANSMIT_LINE_TIMEOUT",
+ "TRANSMIT_SUCCEEDED",
+ "TRANSMIT_FAILED_LINE",
+ "TRANSMIT_FAILED_ACK",
+ "TRANSMIT_FAILED_TIMEOUT_DATA",
+ "TRANSMIT_FAILED_TIMEOUT_LINE",
+ "FIRMWARE_VERSION",
+ "START_BOOTLOADER",
+ "GET_BUILDDATE",
+ "SET_CONTROLLED",
+ "GET_AUTO_ENABLED",
+ "SET_AUTO_ENABLED",
+ "GET_DEFAULT_LOGICAL_ADDRESS",
+ "SET_DEFAULT_LOGICAL_ADDRESS",
+ "GET_LOGICAL_ADDRESS_MASK",
+ "SET_LOGICAL_ADDRESS_MASK",
+ "GET_PHYSICAL_ADDRESS",
+ "SET_PHYSICAL_ADDRESS",
+ "GET_DEVICE_TYPE",
+ "SET_DEVICE_TYPE",
+ "GET_HDMI_VERSION",
+ "SET_HDMI_VERSION",
+ "GET_OSD_NAME",
+ "SET_OSD_NAME",
+ "WRITE_EEPROM",
+ "GET_ADAPTER_TYPE",
+ "SET_ACTIVE_SOURCE",
+ "GET_AUTO_POWER_ON",
+ "SET_AUTO_POWER_ON",
+};
+
+static const char *pulse8_msgname(u8 cmd)
+{
+ static char unknown_msg[5];
+
+ if ((cmd & 0x3f) < ARRAY_SIZE(pulse8_msgnames))
+ return pulse8_msgnames[cmd & 0x3f];
+ snprintf(unknown_msg, sizeof(unknown_msg), "0x%02x", cmd);
+ return unknown_msg;
+}
+
+#define MSGSTART 0xff
+#define MSGEND 0xfe
+#define MSGESC 0xfd
+#define MSGOFFSET 3
+
+#define DATA_SIZE 256
+
+#define PING_PERIOD (15 * HZ)
+
+#define NUM_MSGS 8
+
+struct pulse8 {
+ struct device *dev;
+ struct serio *serio;
+ struct cec_adapter *adap;
+ unsigned int vers;
+
+ struct delayed_work ping_eeprom_work;
+
+ struct work_struct irq_work;
+ struct cec_msg rx_msg[NUM_MSGS];
+ unsigned int rx_msg_cur_idx, rx_msg_num;
+ /* protect rx_msg_cur_idx and rx_msg_num */
+ spinlock_t msg_lock;
+ u8 new_rx_msg[CEC_MAX_MSG_SIZE];
+ u8 new_rx_msg_len;
+
+ struct work_struct tx_work;
+ u32 tx_done_status;
+ u32 tx_signal_free_time;
+ struct cec_msg tx_msg;
+ bool tx_msg_is_bcast;
+
+ struct completion cmd_done;
+ u8 data[DATA_SIZE];
+ unsigned int len;
+ u8 buf[DATA_SIZE];
+ unsigned int idx;
+ bool escape;
+ bool started;
+
+ /* locks access to the adapter */
+ struct mutex lock;
+ bool config_pending;
+ bool restoring_config;
+ bool autonomous;
+};
+
+static int pulse8_send(struct serio *serio, const u8 *command, u8 cmd_len)
+{
+ int err = 0;
+
+ err = serio_write(serio, MSGSTART);
+ if (err)
+ return err;
+ for (; !err && cmd_len; command++, cmd_len--) {
+ if (*command >= MSGESC) {
+ err = serio_write(serio, MSGESC);
+ if (!err)
+ err = serio_write(serio, *command - MSGOFFSET);
+ } else {
+ err = serio_write(serio, *command);
+ }
+ }
+ if (!err)
+ err = serio_write(serio, MSGEND);
+
+ return err;
+}
+
+static int pulse8_send_and_wait_once(struct pulse8 *pulse8,
+ const u8 *cmd, u8 cmd_len,
+ u8 response, u8 size)
+{
+ int err;
+
+ if (debug > 1)
+ dev_info(pulse8->dev, "transmit %s: %*ph\n",
+ pulse8_msgname(cmd[0]), cmd_len, cmd);
+ init_completion(&pulse8->cmd_done);
+
+ err = pulse8_send(pulse8->serio, cmd, cmd_len);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&pulse8->cmd_done, HZ))
+ return -ETIMEDOUT;
+ if ((pulse8->data[0] & 0x3f) == MSGCODE_COMMAND_REJECTED &&
+ cmd[0] != MSGCODE_SET_CONTROLLED &&
+ cmd[0] != MSGCODE_SET_AUTO_ENABLED &&
+ cmd[0] != MSGCODE_GET_BUILDDATE)
+ return -ENOTTY;
+ if (response &&
+ ((pulse8->data[0] & 0x3f) != response || pulse8->len < size + 1)) {
+ dev_info(pulse8->dev, "transmit %s failed with %s\n",
+ pulse8_msgname(cmd[0]),
+ pulse8_msgname(pulse8->data[0]));
+ return -EIO;
+ }
+ return 0;
+}
+
+static int pulse8_send_and_wait(struct pulse8 *pulse8,
+ const u8 *cmd, u8 cmd_len, u8 response, u8 size)
+{
+ u8 cmd_sc[2];
+ int err;
+
+ err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, response, size);
+ if (err != -ENOTTY)
+ return err;
+
+ cmd_sc[0] = MSGCODE_SET_CONTROLLED;
+ cmd_sc[1] = 1;
+ err = pulse8_send_and_wait_once(pulse8, cmd_sc, 2,
+ MSGCODE_COMMAND_ACCEPTED, 1);
+ if (!err)
+ err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len,
+ response, size);
+ return err == -ENOTTY ? -EIO : err;
+}
+
+static void pulse8_tx_work_handler(struct work_struct *work)
+{
+ struct pulse8 *pulse8 = container_of(work, struct pulse8, tx_work);
+ struct cec_msg *msg = &pulse8->tx_msg;
+ unsigned int i;
+ u8 cmd[2];
+ int err;
+
+ if (msg->len == 0)
+ return;
+
+ mutex_lock(&pulse8->lock);
+ cmd[0] = MSGCODE_TRANSMIT_IDLETIME;
+ cmd[1] = pulse8->tx_signal_free_time;
+ err = pulse8_send_and_wait(pulse8, cmd, 2,
+ MSGCODE_COMMAND_ACCEPTED, 1);
+ cmd[0] = MSGCODE_TRANSMIT_ACK_POLARITY;
+ cmd[1] = cec_msg_is_broadcast(msg);
+ pulse8->tx_msg_is_bcast = cec_msg_is_broadcast(msg);
+ if (!err)
+ err = pulse8_send_and_wait(pulse8, cmd, 2,
+ MSGCODE_COMMAND_ACCEPTED, 1);
+ cmd[0] = msg->len == 1 ? MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT;
+ cmd[1] = msg->msg[0];
+ if (!err)
+ err = pulse8_send_and_wait(pulse8, cmd, 2,
+ MSGCODE_COMMAND_ACCEPTED, 1);
+ if (!err && msg->len > 1) {
+ for (i = 1; !err && i < msg->len; i++) {
+ cmd[0] = ((i == msg->len - 1)) ?
+ MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT;
+ cmd[1] = msg->msg[i];
+ err = pulse8_send_and_wait(pulse8, cmd, 2,
+ MSGCODE_COMMAND_ACCEPTED, 1);
+ }
+ }
+ if (err && debug)
+ dev_info(pulse8->dev, "%s(0x%02x) failed with error %d for msg %*ph\n",
+ pulse8_msgname(cmd[0]), cmd[1],
+ err, msg->len, msg->msg);
+ msg->len = 0;
+ mutex_unlock(&pulse8->lock);
+ if (err)
+ cec_transmit_attempt_done(pulse8->adap, CEC_TX_STATUS_ERROR);
+}
+
+static void pulse8_irq_work_handler(struct work_struct *work)
+{
+ struct pulse8 *pulse8 =
+ container_of(work, struct pulse8, irq_work);
+ unsigned long flags;
+ u32 status;
+
+ spin_lock_irqsave(&pulse8->msg_lock, flags);
+ while (pulse8->rx_msg_num) {
+ spin_unlock_irqrestore(&pulse8->msg_lock, flags);
+ if (debug)
+ dev_info(pulse8->dev, "adap received %*ph\n",
+ pulse8->rx_msg[pulse8->rx_msg_cur_idx].len,
+ pulse8->rx_msg[pulse8->rx_msg_cur_idx].msg);
+ cec_received_msg(pulse8->adap,
+ &pulse8->rx_msg[pulse8->rx_msg_cur_idx]);
+ spin_lock_irqsave(&pulse8->msg_lock, flags);
+ if (pulse8->rx_msg_num)
+ pulse8->rx_msg_num--;
+ pulse8->rx_msg_cur_idx =
+ (pulse8->rx_msg_cur_idx + 1) % NUM_MSGS;
+ }
+ spin_unlock_irqrestore(&pulse8->msg_lock, flags);
+
+ mutex_lock(&pulse8->lock);
+ status = pulse8->tx_done_status;
+ pulse8->tx_done_status = 0;
+ mutex_unlock(&pulse8->lock);
+ if (status)
+ cec_transmit_attempt_done(pulse8->adap, status);
+}
+
+static irqreturn_t pulse8_interrupt(struct serio *serio, unsigned char data,
+ unsigned int flags)
+{
+ struct pulse8 *pulse8 = serio_get_drvdata(serio);
+ unsigned long irq_flags;
+ unsigned int idx;
+
+ if (!pulse8->started && data != MSGSTART)
+ return IRQ_HANDLED;
+ if (data == MSGESC) {
+ pulse8->escape = true;
+ return IRQ_HANDLED;
+ }
+ if (pulse8->escape) {
+ data += MSGOFFSET;
+ pulse8->escape = false;
+ } else if (data == MSGEND) {
+ u8 msgcode = pulse8->buf[0];
+
+ if (debug > 1)
+ dev_info(pulse8->dev, "received %s: %*ph\n",
+ pulse8_msgname(msgcode),
+ pulse8->idx, pulse8->buf);
+ switch (msgcode & 0x3f) {
+ case MSGCODE_FRAME_START:
+ /*
+ * Test if we are receiving a new msg when a previous
+ * message is still pending.
+ */
+ if (!(msgcode & MSGCODE_FRAME_EOM)) {
+ pulse8->new_rx_msg_len = 1;
+ pulse8->new_rx_msg[0] = pulse8->buf[1];
+ break;
+ }
+ fallthrough;
+ case MSGCODE_FRAME_DATA:
+ if (pulse8->new_rx_msg_len < CEC_MAX_MSG_SIZE)
+ pulse8->new_rx_msg[pulse8->new_rx_msg_len++] =
+ pulse8->buf[1];
+ if (!(msgcode & MSGCODE_FRAME_EOM))
+ break;
+
+ spin_lock_irqsave(&pulse8->msg_lock, irq_flags);
+ idx = (pulse8->rx_msg_cur_idx + pulse8->rx_msg_num) %
+ NUM_MSGS;
+ if (pulse8->rx_msg_num == NUM_MSGS) {
+ dev_warn(pulse8->dev,
+ "message queue is full, dropping %*ph\n",
+ pulse8->new_rx_msg_len,
+ pulse8->new_rx_msg);
+ spin_unlock_irqrestore(&pulse8->msg_lock,
+ irq_flags);
+ pulse8->new_rx_msg_len = 0;
+ break;
+ }
+ pulse8->rx_msg_num++;
+ memcpy(pulse8->rx_msg[idx].msg, pulse8->new_rx_msg,
+ pulse8->new_rx_msg_len);
+ pulse8->rx_msg[idx].len = pulse8->new_rx_msg_len;
+ spin_unlock_irqrestore(&pulse8->msg_lock, irq_flags);
+ schedule_work(&pulse8->irq_work);
+ pulse8->new_rx_msg_len = 0;
+ break;
+ case MSGCODE_TRANSMIT_SUCCEEDED:
+ WARN_ON(pulse8->tx_done_status);
+ pulse8->tx_done_status = CEC_TX_STATUS_OK;
+ schedule_work(&pulse8->irq_work);
+ break;
+ case MSGCODE_TRANSMIT_FAILED_ACK:
+ /*
+ * A NACK for a broadcast message makes no sense, these
+ * seem to be spurious messages and are skipped.
+ */
+ if (pulse8->tx_msg_is_bcast)
+ break;
+ WARN_ON(pulse8->tx_done_status);
+ pulse8->tx_done_status = CEC_TX_STATUS_NACK;
+ schedule_work(&pulse8->irq_work);
+ break;
+ case MSGCODE_TRANSMIT_FAILED_LINE:
+ case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA:
+ case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE:
+ WARN_ON(pulse8->tx_done_status);
+ pulse8->tx_done_status = CEC_TX_STATUS_ERROR;
+ schedule_work(&pulse8->irq_work);
+ break;
+ case MSGCODE_HIGH_ERROR:
+ case MSGCODE_LOW_ERROR:
+ case MSGCODE_RECEIVE_FAILED:
+ case MSGCODE_TIMEOUT_ERROR:
+ pulse8->new_rx_msg_len = 0;
+ break;
+ case MSGCODE_COMMAND_ACCEPTED:
+ case MSGCODE_COMMAND_REJECTED:
+ default:
+ if (pulse8->idx == 0)
+ break;
+ memcpy(pulse8->data, pulse8->buf, pulse8->idx);
+ pulse8->len = pulse8->idx;
+ complete(&pulse8->cmd_done);
+ break;
+ }
+ pulse8->idx = 0;
+ pulse8->started = false;
+ return IRQ_HANDLED;
+ } else if (data == MSGSTART) {
+ pulse8->idx = 0;
+ pulse8->started = true;
+ return IRQ_HANDLED;
+ }
+
+ if (pulse8->idx >= DATA_SIZE) {
+ dev_dbg(pulse8->dev,
+ "throwing away %d bytes of garbage\n", pulse8->idx);
+ pulse8->idx = 0;
+ }
+ pulse8->buf[pulse8->idx++] = data;
+ return IRQ_HANDLED;
+}
+
+static int pulse8_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct pulse8 *pulse8 = cec_get_drvdata(adap);
+ u8 cmd[16];
+ int err;
+
+ mutex_lock(&pulse8->lock);
+ cmd[0] = MSGCODE_SET_CONTROLLED;
+ cmd[1] = enable;
+ err = pulse8_send_and_wait(pulse8, cmd, 2,
+ MSGCODE_COMMAND_ACCEPTED, 1);
+ if (!enable) {
+ pulse8->rx_msg_num = 0;
+ pulse8->tx_done_status = 0;
+ }
+ mutex_unlock(&pulse8->lock);
+ return enable ? err : 0;
+}
+
+static int pulse8_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+{
+ struct pulse8 *pulse8 = cec_get_drvdata(adap);
+ u16 mask = 0;
+ u16 pa = adap->phys_addr;
+ u8 cmd[16];
+ int err = 0;
+
+ mutex_lock(&pulse8->lock);
+ if (log_addr != CEC_LOG_ADDR_INVALID)
+ mask = 1 << log_addr;
+ cmd[0] = MSGCODE_SET_ACK_MASK;
+ cmd[1] = mask >> 8;
+ cmd[2] = mask & 0xff;
+ err = pulse8_send_and_wait(pulse8, cmd, 3,
+ MSGCODE_COMMAND_ACCEPTED, 0);
+ if ((err && mask != 0) || pulse8->restoring_config)
+ goto unlock;
+
+ cmd[0] = MSGCODE_SET_AUTO_ENABLED;
+ cmd[1] = log_addr == CEC_LOG_ADDR_INVALID ? 0 : 1;
+ err = pulse8_send_and_wait(pulse8, cmd, 2,
+ MSGCODE_COMMAND_ACCEPTED, 0);
+ if (err)
+ goto unlock;
+ pulse8->autonomous = cmd[1];
+ if (log_addr == CEC_LOG_ADDR_INVALID)
+ goto unlock;
+
+ cmd[0] = MSGCODE_SET_DEVICE_TYPE;
+ cmd[1] = adap->log_addrs.primary_device_type[0];
+ err = pulse8_send_and_wait(pulse8, cmd, 2,
+ MSGCODE_COMMAND_ACCEPTED, 0);
+ if (err)
+ goto unlock;
+
+ switch (adap->log_addrs.primary_device_type[0]) {
+ case CEC_OP_PRIM_DEVTYPE_TV:
+ mask = CEC_LOG_ADDR_MASK_TV;
+ break;
+ case CEC_OP_PRIM_DEVTYPE_RECORD:
+ mask = CEC_LOG_ADDR_MASK_RECORD;
+ break;
+ case CEC_OP_PRIM_DEVTYPE_TUNER:
+ mask = CEC_LOG_ADDR_MASK_TUNER;
+ break;
+ case CEC_OP_PRIM_DEVTYPE_PLAYBACK:
+ mask = CEC_LOG_ADDR_MASK_PLAYBACK;
+ break;
+ case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM:
+ mask = CEC_LOG_ADDR_MASK_AUDIOSYSTEM;
+ break;
+ case CEC_OP_PRIM_DEVTYPE_SWITCH:
+ mask = CEC_LOG_ADDR_MASK_UNREGISTERED;
+ break;
+ case CEC_OP_PRIM_DEVTYPE_PROCESSOR:
+ mask = CEC_LOG_ADDR_MASK_SPECIFIC;
+ break;
+ default:
+ mask = 0;
+ break;
+ }
+ cmd[0] = MSGCODE_SET_LOGICAL_ADDRESS_MASK;
+ cmd[1] = mask >> 8;
+ cmd[2] = mask & 0xff;
+ err = pulse8_send_and_wait(pulse8, cmd, 3,
+ MSGCODE_COMMAND_ACCEPTED, 0);
+ if (err)
+ goto unlock;
+
+ cmd[0] = MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS;
+ cmd[1] = log_addr;
+ err = pulse8_send_and_wait(pulse8, cmd, 2,
+ MSGCODE_COMMAND_ACCEPTED, 0);
+ if (err)
+ goto unlock;
+
+ cmd[0] = MSGCODE_SET_PHYSICAL_ADDRESS;
+ cmd[1] = pa >> 8;
+ cmd[2] = pa & 0xff;
+ err = pulse8_send_and_wait(pulse8, cmd, 3,
+ MSGCODE_COMMAND_ACCEPTED, 0);
+ if (err)
+ goto unlock;
+
+ if (pulse8->vers < 10) {
+ cmd[0] = MSGCODE_SET_HDMI_VERSION;
+ cmd[1] = adap->log_addrs.cec_version;
+ err = pulse8_send_and_wait(pulse8, cmd, 2,
+ MSGCODE_COMMAND_ACCEPTED, 0);
+ if (err)
+ goto unlock;
+ }
+
+ if (adap->log_addrs.osd_name[0]) {
+ size_t osd_len = strlen(adap->log_addrs.osd_name);
+ char *osd_str = cmd + 1;
+
+ cmd[0] = MSGCODE_SET_OSD_NAME;
+ strscpy(cmd + 1, adap->log_addrs.osd_name, sizeof(cmd) - 1);
+ if (osd_len < 4) {
+ memset(osd_str + osd_len, ' ', 4 - osd_len);
+ osd_len = 4;
+ osd_str[osd_len] = '\0';
+ strscpy(adap->log_addrs.osd_name, osd_str,
+ sizeof(adap->log_addrs.osd_name));
+ }
+ err = pulse8_send_and_wait(pulse8, cmd, 1 + osd_len,
+ MSGCODE_COMMAND_ACCEPTED, 0);
+ if (err)
+ goto unlock;
+ }
+
+unlock:
+ if (pulse8->restoring_config)
+ pulse8->restoring_config = false;
+ else
+ pulse8->config_pending = true;
+ mutex_unlock(&pulse8->lock);
+ return log_addr == CEC_LOG_ADDR_INVALID ? 0 : err;
+}
+
+static int pulse8_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct pulse8 *pulse8 = cec_get_drvdata(adap);
+
+ pulse8->tx_msg = *msg;
+ if (debug)
+ dev_info(pulse8->dev, "adap transmit %*ph\n",
+ msg->len, msg->msg);
+ pulse8->tx_signal_free_time = signal_free_time;
+ schedule_work(&pulse8->tx_work);
+ return 0;
+}
+
+static void pulse8_cec_adap_free(struct cec_adapter *adap)
+{
+ struct pulse8 *pulse8 = cec_get_drvdata(adap);
+
+ cancel_delayed_work_sync(&pulse8->ping_eeprom_work);
+ cancel_work_sync(&pulse8->irq_work);
+ cancel_work_sync(&pulse8->tx_work);
+ kfree(pulse8);
+}
+
+static const struct cec_adap_ops pulse8_cec_adap_ops = {
+ .adap_enable = pulse8_cec_adap_enable,
+ .adap_log_addr = pulse8_cec_adap_log_addr,
+ .adap_transmit = pulse8_cec_adap_transmit,
+ .adap_free = pulse8_cec_adap_free,
+};
+
+static void pulse8_disconnect(struct serio *serio)
+{
+ struct pulse8 *pulse8 = serio_get_drvdata(serio);
+
+ cec_unregister_adapter(pulse8->adap);
+ serio_set_drvdata(serio, NULL);
+ serio_close(serio);
+}
+
+static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio,
+ struct cec_log_addrs *log_addrs, u16 *pa)
+{
+ u8 *data = pulse8->data + 1;
+ u8 cmd[2];
+ int err;
+ time64_t date;
+
+ pulse8->vers = 0;
+
+ cmd[0] = MSGCODE_FIRMWARE_VERSION;
+ err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2);
+ if (err)
+ return err;
+ pulse8->vers = (data[0] << 8) | data[1];
+ dev_info(pulse8->dev, "Firmware version %04x\n", pulse8->vers);
+ if (pulse8->vers < 2) {
+ *pa = CEC_PHYS_ADDR_INVALID;
+ return 0;
+ }
+
+ cmd[0] = MSGCODE_GET_BUILDDATE;
+ err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 4);
+ if (err)
+ return err;
+ date = ((unsigned)data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
+ dev_info(pulse8->dev, "Firmware build date %ptT\n", &date);
+
+ dev_dbg(pulse8->dev, "Persistent config:\n");
+ cmd[0] = MSGCODE_GET_AUTO_ENABLED;
+ err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);
+ if (err)
+ return err;
+ pulse8->autonomous = data[0];
+ dev_dbg(pulse8->dev, "Autonomous mode: %s",
+ data[0] ? "on" : "off");
+
+ if (pulse8->vers >= 10) {
+ cmd[0] = MSGCODE_GET_AUTO_POWER_ON;
+ err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);
+ if (!err)
+ dev_dbg(pulse8->dev, "Auto Power On: %s",
+ data[0] ? "on" : "off");
+ }
+
+ cmd[0] = MSGCODE_GET_DEVICE_TYPE;
+ err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);
+ if (err)
+ return err;
+ log_addrs->primary_device_type[0] = data[0];
+ dev_dbg(pulse8->dev, "Primary device type: %d\n", data[0]);
+ switch (log_addrs->primary_device_type[0]) {
+ case CEC_OP_PRIM_DEVTYPE_TV:
+ log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TV;
+ log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_TV;
+ break;
+ case CEC_OP_PRIM_DEVTYPE_RECORD:
+ log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_RECORD;
+ log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_RECORD;
+ break;
+ case CEC_OP_PRIM_DEVTYPE_TUNER:
+ log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TUNER;
+ log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_TUNER;
+ break;
+ case CEC_OP_PRIM_DEVTYPE_PLAYBACK:
+ log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK;
+ log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_PLAYBACK;
+ break;
+ case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM:
+ log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK;
+ log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM;
+ break;
+ case CEC_OP_PRIM_DEVTYPE_SWITCH:
+ log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED;
+ log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH;
+ break;
+ case CEC_OP_PRIM_DEVTYPE_PROCESSOR:
+ log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_SPECIFIC;
+ log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH;
+ break;
+ default:
+ log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED;
+ log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH;
+ dev_info(pulse8->dev, "Unknown Primary Device Type: %d\n",
+ log_addrs->primary_device_type[0]);
+ break;
+ }
+
+ cmd[0] = MSGCODE_GET_LOGICAL_ADDRESS_MASK;
+ err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2);
+ if (err)
+ return err;
+ log_addrs->log_addr_mask = (data[0] << 8) | data[1];
+ dev_dbg(pulse8->dev, "Logical address ACK mask: %x\n",
+ log_addrs->log_addr_mask);
+ if (log_addrs->log_addr_mask)
+ log_addrs->num_log_addrs = 1;
+
+ cmd[0] = MSGCODE_GET_PHYSICAL_ADDRESS;
+ err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);
+ if (err)
+ return err;
+ *pa = (data[0] << 8) | data[1];
+ dev_dbg(pulse8->dev, "Physical address: %x.%x.%x.%x\n",
+ cec_phys_addr_exp(*pa));
+
+ log_addrs->cec_version = CEC_OP_CEC_VERSION_1_4;
+ if (pulse8->vers < 10) {
+ cmd[0] = MSGCODE_GET_HDMI_VERSION;
+ err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);
+ if (err)
+ return err;
+ log_addrs->cec_version = data[0];
+ dev_dbg(pulse8->dev, "CEC version: %d\n", log_addrs->cec_version);
+ }
+
+ cmd[0] = MSGCODE_GET_OSD_NAME;
+ err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 0);
+ if (err)
+ return err;
+ strscpy(log_addrs->osd_name, data, sizeof(log_addrs->osd_name));
+ dev_dbg(pulse8->dev, "OSD name: %s\n", log_addrs->osd_name);
+
+ return 0;
+}
+
+static int pulse8_apply_persistent_config(struct pulse8 *pulse8,
+ struct cec_log_addrs *log_addrs,
+ u16 pa)
+{
+ int err;
+
+ err = cec_s_log_addrs(pulse8->adap, log_addrs, false);
+ if (err)
+ return err;
+
+ cec_s_phys_addr(pulse8->adap, pa, false);
+
+ return 0;
+}
+
+static void pulse8_ping_eeprom_work_handler(struct work_struct *work)
+{
+ struct pulse8 *pulse8 =
+ container_of(work, struct pulse8, ping_eeprom_work.work);
+ u8 cmd;
+
+ mutex_lock(&pulse8->lock);
+ cmd = MSGCODE_PING;
+ if (pulse8_send_and_wait(pulse8, &cmd, 1,
+ MSGCODE_COMMAND_ACCEPTED, 0)) {
+ dev_warn(pulse8->dev, "failed to ping EEPROM\n");
+ goto unlock;
+ }
+
+ if (pulse8->vers < 2)
+ goto unlock;
+
+ if (pulse8->config_pending && persistent_config) {
+ dev_dbg(pulse8->dev, "writing pending config to EEPROM\n");
+ cmd = MSGCODE_WRITE_EEPROM;
+ if (pulse8_send_and_wait(pulse8, &cmd, 1,
+ MSGCODE_COMMAND_ACCEPTED, 0))
+ dev_info(pulse8->dev, "failed to write pending config to EEPROM\n");
+ else
+ pulse8->config_pending = false;
+ }
+unlock:
+ schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD);
+ mutex_unlock(&pulse8->lock);
+}
+
+static int pulse8_connect(struct serio *serio, struct serio_driver *drv)
+{
+ u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL;
+ struct pulse8 *pulse8;
+ int err = -ENOMEM;
+ struct cec_log_addrs log_addrs = {};
+ u16 pa = CEC_PHYS_ADDR_INVALID;
+
+ pulse8 = kzalloc(sizeof(*pulse8), GFP_KERNEL);
+
+ if (!pulse8)
+ return -ENOMEM;
+
+ pulse8->serio = serio;
+ pulse8->adap = cec_allocate_adapter(&pulse8_cec_adap_ops, pulse8,
+ dev_name(&serio->dev), caps, 1);
+ err = PTR_ERR_OR_ZERO(pulse8->adap);
+ if (err < 0) {
+ kfree(pulse8);
+ return err;
+ }
+
+ pulse8->dev = &serio->dev;
+ serio_set_drvdata(serio, pulse8);
+ INIT_WORK(&pulse8->irq_work, pulse8_irq_work_handler);
+ INIT_WORK(&pulse8->tx_work, pulse8_tx_work_handler);
+ INIT_DELAYED_WORK(&pulse8->ping_eeprom_work,
+ pulse8_ping_eeprom_work_handler);
+ mutex_init(&pulse8->lock);
+ spin_lock_init(&pulse8->msg_lock);
+ pulse8->config_pending = false;
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto delete_adap;
+
+ err = pulse8_setup(pulse8, serio, &log_addrs, &pa);
+ if (err)
+ goto close_serio;
+
+ err = cec_register_adapter(pulse8->adap, &serio->dev);
+ if (err < 0)
+ goto close_serio;
+
+ pulse8->dev = &pulse8->adap->devnode.dev;
+
+ if (persistent_config && pulse8->autonomous) {
+ err = pulse8_apply_persistent_config(pulse8, &log_addrs, pa);
+ if (err)
+ goto close_serio;
+ pulse8->restoring_config = true;
+ }
+
+ schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD);
+
+ return 0;
+
+close_serio:
+ pulse8->serio = NULL;
+ serio_set_drvdata(serio, NULL);
+ serio_close(serio);
+delete_adap:
+ cec_delete_adapter(pulse8->adap);
+ return err;
+}
+
+static const struct serio_device_id pulse8_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_PULSE8_CEC,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, pulse8_serio_ids);
+
+static struct serio_driver pulse8_drv = {
+ .driver = {
+ .name = "pulse8-cec",
+ },
+ .description = "Pulse Eight HDMI CEC driver",
+ .id_table = pulse8_serio_ids,
+ .interrupt = pulse8_interrupt,
+ .connect = pulse8_connect,
+ .disconnect = pulse8_disconnect,
+};
+
+module_serio_driver(pulse8_drv);
diff --git a/drivers/media/cec/usb/rainshadow/Kconfig b/drivers/media/cec/usb/rainshadow/Kconfig
new file mode 100644
index 000000000000..c9ef2c192b17
--- /dev/null
+++ b/drivers/media/cec/usb/rainshadow/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config USB_RAINSHADOW_CEC
+ tristate "RainShadow Tech HDMI CEC"
+ select CEC_CORE
+ select USB
+ select USB_ACM
+ select SERIO
+ select SERIO_SERPORT
+ help
+ This is a cec driver for the RainShadow Tech HDMI CEC device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rainshadow-cec.
diff --git a/drivers/media/cec/usb/rainshadow/Makefile b/drivers/media/cec/usb/rainshadow/Makefile
new file mode 100644
index 000000000000..47b33c574c3e
--- /dev/null
+++ b/drivers/media/cec/usb/rainshadow/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_USB_RAINSHADOW_CEC) += rainshadow-cec.o
diff --git a/drivers/media/cec/usb/rainshadow/rainshadow-cec.c b/drivers/media/cec/usb/rainshadow/rainshadow-cec.c
new file mode 100644
index 000000000000..08f58456d682
--- /dev/null
+++ b/drivers/media/cec/usb/rainshadow/rainshadow-cec.c
@@ -0,0 +1,381 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RainShadow Tech HDMI CEC driver
+ *
+ * Copyright 2016 Hans Verkuil <hverkuil@kernel.org>
+ */
+
+/*
+ * Notes:
+ *
+ * The higher level protocols are currently disabled. This can be added
+ * later, similar to how this is done for the Pulse Eight CEC driver.
+ *
+ * Documentation of the protocol is available here:
+ *
+ * http://rainshadowtech.com/doc/HDMICECtoUSBandRS232v2.0.pdf
+ */
+
+#include <linux/completion.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/workqueue.h>
+
+#include <media/cec.h>
+
+MODULE_AUTHOR("Hans Verkuil <hverkuil@kernel.org>");
+MODULE_DESCRIPTION("RainShadow Tech HDMI CEC driver");
+MODULE_LICENSE("GPL");
+
+#define DATA_SIZE 256
+
+struct rain {
+ struct device *dev;
+ struct serio *serio;
+ struct cec_adapter *adap;
+ struct completion cmd_done;
+ struct work_struct work;
+
+ /* Low-level ringbuffer, collecting incoming characters */
+ char buf[DATA_SIZE];
+ unsigned int buf_rd_idx;
+ unsigned int buf_wr_idx;
+ unsigned int buf_len;
+ spinlock_t buf_lock;
+
+ /* command buffer */
+ char cmd[DATA_SIZE];
+ unsigned int cmd_idx;
+ bool cmd_started;
+
+ /* reply to a command, only used to store the firmware version */
+ char cmd_reply[DATA_SIZE];
+
+ struct mutex write_lock;
+};
+
+static void rain_process_msg(struct rain *rain)
+{
+ struct cec_msg msg = {};
+ const char *cmd = rain->cmd + 3;
+ int stat = -1;
+
+ for (; *cmd; cmd++) {
+ if (!isxdigit(*cmd))
+ continue;
+ if (isxdigit(cmd[0]) && isxdigit(cmd[1])) {
+ if (msg.len == CEC_MAX_MSG_SIZE)
+ break;
+ if (hex2bin(msg.msg + msg.len, cmd, 1))
+ continue;
+ msg.len++;
+ cmd++;
+ continue;
+ }
+ if (!cmd[1])
+ stat = hex_to_bin(cmd[0]);
+ break;
+ }
+
+ if (rain->cmd[0] == 'R') {
+ if (stat == 1 || stat == 2)
+ cec_received_msg(rain->adap, &msg);
+ return;
+ }
+
+ switch (stat) {
+ case 1:
+ cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_OK);
+ break;
+ case 2:
+ cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_NACK);
+ break;
+ default:
+ cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_LOW_DRIVE);
+ break;
+ }
+}
+
+static void rain_irq_work_handler(struct work_struct *work)
+{
+ struct rain *rain =
+ container_of(work, struct rain, work);
+
+ while (true) {
+ unsigned long flags;
+ char data;
+
+ spin_lock_irqsave(&rain->buf_lock, flags);
+ if (!rain->buf_len) {
+ spin_unlock_irqrestore(&rain->buf_lock, flags);
+ break;
+ }
+
+ data = rain->buf[rain->buf_rd_idx];
+ rain->buf_len--;
+ rain->buf_rd_idx = (rain->buf_rd_idx + 1) & 0xff;
+
+ spin_unlock_irqrestore(&rain->buf_lock, flags);
+
+ if (!rain->cmd_started && data != '?')
+ continue;
+
+ switch (data) {
+ case '\r':
+ rain->cmd[rain->cmd_idx] = '\0';
+ dev_dbg(rain->dev, "received: %s\n", rain->cmd);
+ if (!memcmp(rain->cmd, "REC", 3) ||
+ !memcmp(rain->cmd, "STA", 3)) {
+ rain_process_msg(rain);
+ } else {
+ strscpy(rain->cmd_reply, rain->cmd,
+ sizeof(rain->cmd_reply));
+ complete(&rain->cmd_done);
+ }
+ rain->cmd_idx = 0;
+ rain->cmd_started = false;
+ break;
+
+ case '\n':
+ rain->cmd_idx = 0;
+ rain->cmd_started = false;
+ break;
+
+ case '?':
+ rain->cmd_idx = 0;
+ rain->cmd_started = true;
+ break;
+
+ default:
+ if (rain->cmd_idx >= DATA_SIZE - 1) {
+ dev_dbg(rain->dev,
+ "throwing away %d bytes of garbage\n", rain->cmd_idx);
+ rain->cmd_idx = 0;
+ }
+ rain->cmd[rain->cmd_idx++] = data;
+ break;
+ }
+ }
+}
+
+static irqreturn_t rain_interrupt(struct serio *serio, unsigned char data,
+ unsigned int flags)
+{
+ struct rain *rain = serio_get_drvdata(serio);
+
+ spin_lock(&rain->buf_lock);
+ if (rain->buf_len == DATA_SIZE) {
+ spin_unlock(&rain->buf_lock);
+ dev_warn_once(rain->dev, "buffer overflow\n");
+ return IRQ_HANDLED;
+ }
+ rain->buf_len++;
+ rain->buf[rain->buf_wr_idx] = data;
+ rain->buf_wr_idx = (rain->buf_wr_idx + 1) & 0xff;
+ spin_unlock(&rain->buf_lock);
+ schedule_work(&rain->work);
+ return IRQ_HANDLED;
+}
+
+static void rain_disconnect(struct serio *serio)
+{
+ struct rain *rain = serio_get_drvdata(serio);
+
+ cancel_work_sync(&rain->work);
+ cec_unregister_adapter(rain->adap);
+ dev_info(&serio->dev, "disconnected\n");
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(rain);
+}
+
+static int rain_send(struct rain *rain, const char *command)
+{
+ int err = serio_write(rain->serio, '!');
+
+ dev_dbg(rain->dev, "send: %s\n", command);
+ while (!err && *command)
+ err = serio_write(rain->serio, *command++);
+ if (!err)
+ err = serio_write(rain->serio, '~');
+
+ return err;
+}
+
+static int rain_send_and_wait(struct rain *rain,
+ const char *cmd, const char *reply)
+{
+ int err;
+
+ init_completion(&rain->cmd_done);
+
+ mutex_lock(&rain->write_lock);
+ err = rain_send(rain, cmd);
+ if (err)
+ goto err;
+
+ if (!wait_for_completion_timeout(&rain->cmd_done, HZ)) {
+ err = -ETIMEDOUT;
+ goto err;
+ }
+ if (reply && strncmp(rain->cmd_reply, reply, strlen(reply))) {
+ dev_dbg(rain->dev,
+ "transmit of '%s': received '%s' instead of '%s'\n",
+ cmd, rain->cmd_reply, reply);
+ err = -EIO;
+ }
+err:
+ mutex_unlock(&rain->write_lock);
+ return err;
+}
+
+static int rain_setup(struct rain *rain, struct serio *serio,
+ struct cec_log_addrs *log_addrs, u16 *pa)
+{
+ int err;
+
+ err = rain_send_and_wait(rain, "R", "REV");
+ if (err)
+ return err;
+ dev_info(rain->dev, "Firmware version %s\n", rain->cmd_reply + 4);
+
+ err = rain_send_and_wait(rain, "Q 1", "QTY");
+ if (err)
+ return err;
+ err = rain_send_and_wait(rain, "c0000", "CFG");
+ if (err)
+ return err;
+ return rain_send_and_wait(rain, "A F 0000", "ADR");
+}
+
+static int rain_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ return 0;
+}
+
+static int rain_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+{
+ struct rain *rain = cec_get_drvdata(adap);
+ u8 cmd[16];
+
+ if (log_addr == CEC_LOG_ADDR_INVALID)
+ log_addr = CEC_LOG_ADDR_UNREGISTERED;
+ snprintf(cmd, sizeof(cmd), "A %x", log_addr);
+ return rain_send_and_wait(rain, cmd, "ADR");
+}
+
+static int rain_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct rain *rain = cec_get_drvdata(adap);
+ char cmd[2 * CEC_MAX_MSG_SIZE + 16];
+ unsigned int i;
+ int err;
+
+ if (msg->len == 1) {
+ snprintf(cmd, sizeof(cmd), "x%x", cec_msg_destination(msg));
+ } else {
+ char hex[3];
+
+ snprintf(cmd, sizeof(cmd), "x%x %02x ",
+ cec_msg_destination(msg), msg->msg[1]);
+ for (i = 2; i < msg->len; i++) {
+ snprintf(hex, sizeof(hex), "%02x", msg->msg[i]);
+ strlcat(cmd, hex, sizeof(cmd));
+ }
+ }
+ mutex_lock(&rain->write_lock);
+ err = rain_send(rain, cmd);
+ mutex_unlock(&rain->write_lock);
+ return err;
+}
+
+static const struct cec_adap_ops rain_cec_adap_ops = {
+ .adap_enable = rain_cec_adap_enable,
+ .adap_log_addr = rain_cec_adap_log_addr,
+ .adap_transmit = rain_cec_adap_transmit,
+};
+
+static int rain_connect(struct serio *serio, struct serio_driver *drv)
+{
+ u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL;
+ struct rain *rain;
+ int err = -ENOMEM;
+ struct cec_log_addrs log_addrs = {};
+ u16 pa = CEC_PHYS_ADDR_INVALID;
+
+ rain = kzalloc(sizeof(*rain), GFP_KERNEL);
+
+ if (!rain)
+ return -ENOMEM;
+
+ rain->serio = serio;
+ rain->adap = cec_allocate_adapter(&rain_cec_adap_ops, rain,
+ dev_name(&serio->dev), caps, 1);
+ err = PTR_ERR_OR_ZERO(rain->adap);
+ if (err < 0)
+ goto free_device;
+
+ rain->dev = &serio->dev;
+ serio_set_drvdata(serio, rain);
+ INIT_WORK(&rain->work, rain_irq_work_handler);
+ mutex_init(&rain->write_lock);
+ spin_lock_init(&rain->buf_lock);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto delete_adap;
+
+ err = rain_setup(rain, serio, &log_addrs, &pa);
+ if (err)
+ goto close_serio;
+
+ err = cec_register_adapter(rain->adap, &serio->dev);
+ if (err < 0)
+ goto close_serio;
+
+ rain->dev = &rain->adap->devnode.dev;
+ return 0;
+
+close_serio:
+ serio_close(serio);
+delete_adap:
+ cec_delete_adapter(rain->adap);
+ serio_set_drvdata(serio, NULL);
+free_device:
+ kfree(rain);
+ return err;
+}
+
+static const struct serio_device_id rain_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_RAINSHADOW_CEC,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, rain_serio_ids);
+
+static struct serio_driver rain_drv = {
+ .driver = {
+ .name = "rainshadow-cec",
+ },
+ .description = "RainShadow Tech HDMI CEC driver",
+ .id_table = rain_serio_ids,
+ .interrupt = rain_interrupt,
+ .connect = rain_connect,
+ .disconnect = rain_disconnect,
+};
+
+module_serio_driver(rain_drv);
diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig
index 769c6f8142d2..adcb6655385a 100644
--- a/drivers/media/common/Kconfig
+++ b/drivers/media/common/Kconfig
@@ -1,9 +1,31 @@
-config VIDEO_SAA7146
+# SPDX-License-Identifier: GPL-2.0-only
+# Used by common drivers, when they need to ask questions
+config MEDIA_COMMON_OPTIONS
+ bool
+
+comment "common driver options"
+ depends on MEDIA_COMMON_OPTIONS
+
+config CYPRESS_FIRMWARE
+ tristate
+ depends on USB
+
+config TTPCI_EEPROM
+ tristate
+ depends on I2C
+
+config UVC_COMMON
+ tristate
+
+config VIDEO_CX2341X
tristate
- depends on I2C && PCI
-config VIDEO_SAA7146_VV
+config VIDEO_TVEEPROM
tristate
- depends on VIDEO_V4L2
- select VIDEOBUF_DMA_SG
- select VIDEO_SAA7146
+ depends on I2C
+
+source "drivers/media/common/b2c2/Kconfig"
+source "drivers/media/common/saa7146/Kconfig"
+source "drivers/media/common/siano/Kconfig"
+source "drivers/media/common/v4l2-tpg/Kconfig"
+source "drivers/media/common/videobuf2/Kconfig"
diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile
index e3ec9639321b..c5ab905e7c20 100644
--- a/drivers/media/common/Makefile
+++ b/drivers/media/common/Makefile
@@ -1,6 +1,10 @@
-saa7146-objs := saa7146_i2c.o saa7146_core.o
-saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += b2c2/ saa7146/ siano/ v4l2-tpg/ videobuf2/
-obj-y += tuners/
-obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o
-obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o
+# Please keep it alphabetically sorted by Kconfig name
+# (e. g. LC_ALL=C sort Makefile)
+obj-$(CONFIG_CYPRESS_FIRMWARE) += cypress_firmware.o
+obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o
+obj-$(CONFIG_UVC_COMMON) += uvc.o
+obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
+obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
diff --git a/drivers/media/common/b2c2/Kconfig b/drivers/media/common/b2c2/Kconfig
new file mode 100644
index 000000000000..27284797e755
--- /dev/null
+++ b/drivers/media/common/b2c2/Kconfig
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config DVB_B2C2_FLEXCOP
+ tristate
+ depends on DVB_CORE && I2C
+ depends on DVB_B2C2_FLEXCOP_PCI || DVB_B2C2_FLEXCOP_USB
+ default y
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_BCM3510 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TUNER_ITD1000 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CX24120 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CX24123 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TUNER_CX24113 if MEDIA_SUBDRV_AUTOSELECT
+
+# Selected via the PCI or USB flexcop drivers
+config DVB_B2C2_FLEXCOP_DEBUG
+ bool
diff --git a/drivers/media/common/b2c2/Makefile b/drivers/media/common/b2c2/Makefile
new file mode 100644
index 000000000000..0e32b77f349b
--- /dev/null
+++ b/drivers/media/common/b2c2/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+b2c2-flexcop-objs += flexcop.o flexcop-fe-tuner.o flexcop-i2c.o
+b2c2-flexcop-objs += flexcop-sram.o flexcop-eeprom.o flexcop-misc.o
+b2c2-flexcop-objs += flexcop-hw-filter.o
+obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o
+
+ccflags-y += -I $(srctree)/drivers/media/dvb-frontends/
+ccflags-y += -I $(srctree)/drivers/media/tuners/
diff --git a/drivers/media/dvb/b2c2/flexcop-common.h b/drivers/media/common/b2c2/flexcop-common.h
index 437912e49824..a468ea7e77a1 100644
--- a/drivers/media/dvb/b2c2/flexcop-common.h
+++ b/drivers/media/common/b2c2/flexcop-common.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
* flexcop-common.h - common header file for device-specific source files
@@ -12,11 +13,10 @@
#include "flexcop-reg.h"
-#include "dmxdev.h"
-#include "dvb_demux.h"
-#include "dvb_filter.h"
-#include "dvb_net.h"
-#include "dvb_frontend.h"
+#include <media/dmxdev.h>
+#include <media/dvb_demux.h>
+#include <media/dvb_net.h>
+#include <media/dvb_frontend.h>
#define FC_MAX_FEED 256
@@ -91,6 +91,7 @@ struct flexcop_device {
int feedcount;
int pid_filtering;
int fullts_streaming_state;
+ int skip_6_hw_pid_filter;
/* bus specific callbacks */
flexcop_ibi_value(*read_ibi_reg) (struct flexcop_device *,
@@ -124,8 +125,6 @@ void flexcop_dma_free(struct flexcop_dma *dma);
int flexcop_dma_control_timer_irq(struct flexcop_device *fc,
flexcop_dma_index_t no, int onoff);
-int flexcop_dma_control_size_irq(struct flexcop_device *fc,
- flexcop_dma_index_t no, int onoff);
int flexcop_dma_config(struct flexcop_device *fc, struct flexcop_dma *dma,
flexcop_dma_index_t dma_idx);
int flexcop_dma_xfer_control(struct flexcop_device *fc,
@@ -169,8 +168,6 @@ int flexcop_sram_init(struct flexcop_device *fc);
void flexcop_determine_revision(struct flexcop_device *fc);
void flexcop_device_name(struct flexcop_device *fc,
const char *prefix, const char *suffix);
-void flexcop_dump_reg(struct flexcop_device *fc,
- flexcop_ibi_register reg, int num);
/* from flexcop-hw-filter.c */
int flexcop_pid_feed_control(struct flexcop_device *fc,
diff --git a/drivers/media/dvb/b2c2/flexcop-eeprom.c b/drivers/media/common/b2c2/flexcop-eeprom.c
index a25373a9bd84..0f2151cd36f2 100644
--- a/drivers/media/dvb/b2c2/flexcop-eeprom.c
+++ b/drivers/media/common/b2c2/flexcop-eeprom.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
* flexcop-eeprom.c - eeprom access methods (currently only MAC address reading)
@@ -136,8 +137,7 @@ int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended)
if ((ret = flexcop_eeprom_lrc_read(fc,0x3f8,buf,8,4)) == 0) {
if (extended != 0) {
- err("TODO: extended (EUI64) MAC addresses aren't "
- "completely supported yet");
+ err("TODO: extended (EUI64) MAC addresses aren't completely supported yet");
ret = -EINVAL;
} else
memcpy(fc->dvb_adapter.proposed_mac,buf,6);
diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/common/b2c2/flexcop-fe-tuner.c
index 850a6c606750..aac1aadb0cb1 100644
--- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
+++ b/drivers/media/common/b2c2/flexcop-fe-tuner.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
* flexcop-fe-tuner.c - methods for frontend attachment and DiSEqC controlling
@@ -12,6 +13,7 @@
#include "cx24113.h"
#include "cx24123.h"
#include "isl6421.h"
+#include "cx24120.h"
#include "mt352.h"
#include "bcm3510.h"
#include "nxt200x.h"
@@ -23,12 +25,22 @@
/* Can we use the specified front-end? Remember that if we are compiled
* into the kernel we can't call code that's in modules. */
-#define FE_SUPPORTED(fe) (defined(CONFIG_DVB_##fe) || \
- (defined(CONFIG_DVB_##fe##_MODULE) && defined(MODULE)))
+#define FE_SUPPORTED(fe) IS_REACHABLE(CONFIG_DVB_ ## fe)
+
+#if FE_SUPPORTED(BCM3510) || (FE_SUPPORTED(CX24120) && FE_SUPPORTED(ISL6421))
+static int flexcop_fe_request_firmware(struct dvb_frontend *fe,
+ const struct firmware **fw, char *name)
+{
+ struct flexcop_device *fc = fe->dvb->priv;
+
+ return request_firmware(fw, name, fc->dev);
+}
+#endif
/* lnb control */
-#if FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299)
-static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+#if (FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299)) && FE_SUPPORTED(PLL)
+static int flexcop_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage)
{
struct flexcop_device *fc = fe->dvb->priv;
flexcop_ibi_value v;
@@ -56,7 +68,7 @@ static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage
#endif
#if FE_SUPPORTED(S5H1420) || FE_SUPPORTED(STV0299) || FE_SUPPORTED(MT312)
-static int flexcop_sleep(struct dvb_frontend* fe)
+static int __maybe_unused flexcop_sleep(struct dvb_frontend* fe)
{
struct flexcop_device *fc = fe->dvb->priv;
if (fc->fe_sleep)
@@ -67,7 +79,7 @@ static int flexcop_sleep(struct dvb_frontend* fe)
/* SkyStar2 DVB-S rev 2.3 */
#if FE_SUPPORTED(MT312) && FE_SUPPORTED(PLL)
-static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+static int flexcop_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
{
/* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */
struct flexcop_device *fc = fe->dvb->priv;
@@ -146,7 +158,7 @@ static int flexcop_diseqc_send_master_cmd(struct dvb_frontend *fe,
}
static int flexcop_diseqc_send_burst(struct dvb_frontend *fe,
- fe_sec_mini_cmd_t minicmd)
+ enum fe_sec_mini_cmd minicmd)
{
return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd);
}
@@ -325,7 +337,7 @@ static int skystar2_rev27_attach(struct flexcop_device *fc,
/* enable no_base_addr - no repeated start when reading */
fc->fc_i2c_adap[2].no_base_addr = 1;
if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap,
- 0x08, 1, 1)) {
+ 0x08, 1, 1, false)) {
err("ISL6421 could NOT be attached");
goto fail_isl;
}
@@ -391,7 +403,7 @@ static int skystar2_rev28_attach(struct flexcop_device *fc,
fc->fc_i2c_adap[2].no_base_addr = 1;
if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap,
- 0x08, 0, 0)) {
+ 0x08, 0, 0, false)) {
err("ISL6421 could NOT be attached");
fc->fc_i2c_adap[2].no_base_addr = 0;
return 0;
@@ -445,13 +457,6 @@ static int airstar_dvbt_attach(struct flexcop_device *fc,
/* AirStar ATSC 1st generation */
#if FE_SUPPORTED(BCM3510)
-static int flexcop_fe_request_firmware(struct dvb_frontend *fe,
- const struct firmware **fw, char* name)
-{
- struct flexcop_device *fc = fe->dvb->priv;
- return request_firmware(fw, name, fc->dev);
-}
-
static struct bcm3510_config air2pc_atsc_first_gen_config = {
.demod_address = 0x0f,
.request_firmware = flexcop_fe_request_firmware,
@@ -469,7 +474,7 @@ static int airstar_atsc1_attach(struct flexcop_device *fc,
/* AirStar ATSC 2nd generation */
#if FE_SUPPORTED(NXT200X) && FE_SUPPORTED(PLL)
-static struct nxt200x_config samsung_tbmv_config = {
+static const struct nxt200x_config samsung_tbmv_config = {
.demod_address = 0x0a,
};
@@ -490,7 +495,6 @@ static int airstar_atsc2_attach(struct flexcop_device *fc,
/* AirStar ATSC 3rd generation */
#if FE_SUPPORTED(LGDT330X)
static struct lgdt330x_config air2pc_atsc_hd5000_config = {
- .demod_address = 0x59,
.demod_chip = LGDT3303,
.serial_mpeg = 0x04,
.clock_polarity_flip = 1,
@@ -499,7 +503,8 @@ static struct lgdt330x_config air2pc_atsc_hd5000_config = {
static int airstar_atsc3_attach(struct flexcop_device *fc,
struct i2c_adapter *i2c)
{
- fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, i2c);
+ fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config,
+ 0x59, i2c);
if (!fc->fe)
return 0;
@@ -619,6 +624,43 @@ fail:
#define cablestar2_attach NULL
#endif
+/* SkyStar S2 PCI DVB-S/S2 card based on Conexant cx24120/cx24118 */
+#if FE_SUPPORTED(CX24120) && FE_SUPPORTED(ISL6421)
+static const struct cx24120_config skystar2_rev3_3_cx24120_config = {
+ .i2c_addr = 0x55,
+ .xtal_khz = 10111,
+ .initial_mpeg_config = { 0xa1, 0x76, 0x07 },
+ .request_firmware = flexcop_fe_request_firmware,
+ .i2c_wr_max = 4,
+};
+
+static int skystarS2_rev33_attach(struct flexcop_device *fc,
+ struct i2c_adapter *i2c)
+{
+ fc->fe = dvb_attach(cx24120_attach,
+ &skystar2_rev3_3_cx24120_config, i2c);
+ if (!fc->fe)
+ return 0;
+
+ fc->dev_type = FC_SKYS2_REV33;
+ fc->fc_i2c_adap[2].no_base_addr = 1;
+ if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap,
+ 0x08, 0, 0, false)) {
+ err("ISL6421 could NOT be attached!");
+ fc->fc_i2c_adap[2].no_base_addr = 0;
+ return 0;
+ }
+ info("ISL6421 successfully attached.");
+
+ if (fc->has_32_hw_pid_filter)
+ fc->skip_6_hw_pid_filter = 1;
+
+ return 1;
+}
+#else
+#define skystarS2_rev33_attach NULL
+#endif
+
static struct {
flexcop_device_type_t type;
int (*attach)(struct flexcop_device *, struct i2c_adapter *);
@@ -632,6 +674,7 @@ static struct {
{ FC_AIR_ATSC1, airstar_atsc1_attach },
{ FC_CABLE, cablestar2_attach },
{ FC_SKY_REV23, skystar2_rev23_attach },
+ { FC_SKYS2_REV33, skystarS2_rev33_attach },
};
/* try to figure out the frontend */
diff --git a/drivers/media/dvb/b2c2/flexcop-hw-filter.c b/drivers/media/common/b2c2/flexcop-hw-filter.c
index 77e45475f4c7..c5a3345c99e9 100644
--- a/drivers/media/dvb/b2c2/flexcop-hw-filter.c
+++ b/drivers/media/common/b2c2/flexcop-hw-filter.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
* flexcop-hw-filter.c - pid and mac address filtering and control functions
@@ -68,7 +69,7 @@ vpid.vregname.field = onoff ? pid : 0x1fff; \
vpid.vregname.trans_field = transval; \
v208.ctrl_208.enablefield = onoff; \
fc->write_ibi_reg(fc, vregname, vpid); \
-fc->write_ibi_reg(fc, ctrl_208, v208);
+fc->write_ibi_reg(fc, ctrl_208, v208)
static void flexcop_pid_Stream1_PID_ctrl(struct flexcop_device *fc,
u16 pid, int onoff)
@@ -117,6 +118,10 @@ static void flexcop_pid_control(struct flexcop_device *fc,
deb_ts("setting pid: %5d %04x at index %d '%s'\n",
pid, pid, index, onoff ? "on" : "off");
+ /* First 6 can be buggy - skip over them if option set */
+ if (fc->skip_6_hw_pid_filter)
+ index += 6;
+
/* We could use bit magic here to reduce source code size.
* I decided against it, but to use the real register names */
switch (index) {
@@ -170,7 +175,10 @@ static int flexcop_toggle_fullts_streaming(struct flexcop_device *fc, int onoff)
int flexcop_pid_feed_control(struct flexcop_device *fc,
struct dvb_demux_feed *dvbdmxfeed, int onoff)
{
- int max_pid_filter = 6 + fc->has_32_hw_pid_filter*32;
+ int max_pid_filter = 6;
+
+ max_pid_filter -= 6 * fc->skip_6_hw_pid_filter;
+ max_pid_filter += 32 * fc->has_32_hw_pid_filter;
fc->feedcount += onoff ? 1 : -1; /* the number of PIDs/Feed currently requested */
if (dvbdmxfeed->index >= max_pid_filter)
@@ -217,7 +225,12 @@ void flexcop_hw_filter_init(struct flexcop_device *fc)
{
int i;
flexcop_ibi_value v;
- for (i = 0; i < 6 + 32*fc->has_32_hw_pid_filter; i++)
+ int max_pid_filter = 6;
+
+ max_pid_filter -= 6 * fc->skip_6_hw_pid_filter;
+ max_pid_filter += 32 * fc->has_32_hw_pid_filter;
+
+ for (i = 0; i < max_pid_filter; i++)
flexcop_pid_control(fc, i, 0x1fff, 0);
flexcop_pid_group_filter(fc, 0, 0x1fe0);
diff --git a/drivers/media/dvb/b2c2/flexcop-i2c.c b/drivers/media/common/b2c2/flexcop-i2c.c
index 965d5eb33752..21edf870d927 100644
--- a/drivers/media/dvb/b2c2/flexcop-i2c.c
+++ b/drivers/media/common/b2c2/flexcop-i2c.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
* flexcop-i2c.c - flexcop internal 2Wire bus (I2C) and dvb i2c initialization
@@ -33,8 +34,8 @@ static int flexcop_i2c_operation(struct flexcop_device *fc,
return -EREMOTEIO;
}
}
- deb_i2c("tried %d times i2c operation, "
- "never finished or too many ack errors.\n", i);
+ deb_i2c("tried %d times i2c operation, never finished or too many ack errors.\n",
+ i);
return -EREMOTEIO;
}
@@ -104,40 +105,36 @@ static int flexcop_i2c_write4(struct flexcop_device *fc,
}
int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c,
- flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)
+ flexcop_access_op_t op, u8 chipaddr,
+ u8 start_addr, u8 *buf, u16 size)
{
int ret;
-
-#ifdef DUMP_I2C_MESSAGES
- int i;
-#endif
+ int len = size;
+ u8 *p;
+ u8 addr = start_addr;
u16 bytes_to_transfer;
flexcop_ibi_value r100;
- deb_i2c("op = %d\n",op);
+ deb_i2c("port %d %s(%02x): register %02x, size: %d\n",
+ i2c->port,
+ op == FC_READ ? "rd" : "wr",
+ chipaddr, start_addr, size);
r100.raw = 0;
r100.tw_sm_c_100.chipaddr = chipaddr;
r100.tw_sm_c_100.twoWS_rw = op;
r100.tw_sm_c_100.twoWS_port_reg = i2c->port;
-#ifdef DUMP_I2C_MESSAGES
- printk(KERN_DEBUG "%d ", i2c->port);
- if (op == FC_READ)
- printk("rd(");
- else
- printk("wr(");
- printk("%02x): %02x ", chipaddr, addr);
-#endif
-
/* in that case addr is the only value ->
* we write it twice as baseaddr and val0
* BBTI is doing it like that for ISL6421 at least */
if (i2c->no_base_addr && len == 0 && op == FC_WRITE) {
- buf = &addr;
+ buf = &start_addr;
len = 1;
}
+ p = buf;
+
while (len != 0) {
bytes_to_transfer = len > 4 ? 4 : len;
@@ -145,26 +142,21 @@ int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c,
r100.tw_sm_c_100.baseaddr = addr;
if (op == FC_READ)
- ret = flexcop_i2c_read4(i2c, r100, buf);
+ ret = flexcop_i2c_read4(i2c, r100, p);
else
- ret = flexcop_i2c_write4(i2c->fc, r100, buf);
-
-#ifdef DUMP_I2C_MESSAGES
- for (i = 0; i < bytes_to_transfer; i++)
- printk("%02x ", buf[i]);
-#endif
+ ret = flexcop_i2c_write4(i2c->fc, r100, p);
if (ret < 0)
return ret;
- buf += bytes_to_transfer;
+ p += bytes_to_transfer;
addr += bytes_to_transfer;
len -= bytes_to_transfer;
}
-
-#ifdef DUMP_I2C_MESSAGES
- printk("\n");
-#endif
+ deb_i2c_dump("port %d %s(%02x): register %02x: %*ph\n",
+ i2c->port,
+ op == FC_READ ? "rd" : "wr",
+ chipaddr, start_addr, size, buf);
return 0;
}
@@ -217,7 +209,7 @@ static u32 flexcop_i2c_func(struct i2c_adapter *adapter)
return I2C_FUNC_I2C;
}
-static struct i2c_algorithm flexcop_algo = {
+static const struct i2c_algorithm flexcop_algo = {
.master_xfer = flexcop_master_xfer,
.functionality = flexcop_i2c_func,
};
@@ -234,12 +226,12 @@ int flexcop_i2c_init(struct flexcop_device *fc)
fc->fc_i2c_adap[1].port = FC_I2C_PORT_EEPROM;
fc->fc_i2c_adap[2].port = FC_I2C_PORT_TUNER;
- strlcpy(fc->fc_i2c_adap[0].i2c_adap.name, "B2C2 FlexCop I2C to demod",
- sizeof(fc->fc_i2c_adap[0].i2c_adap.name));
- strlcpy(fc->fc_i2c_adap[1].i2c_adap.name, "B2C2 FlexCop I2C to eeprom",
- sizeof(fc->fc_i2c_adap[1].i2c_adap.name));
- strlcpy(fc->fc_i2c_adap[2].i2c_adap.name, "B2C2 FlexCop I2C to tuner",
- sizeof(fc->fc_i2c_adap[2].i2c_adap.name));
+ strscpy(fc->fc_i2c_adap[0].i2c_adap.name, "B2C2 FlexCop I2C to demod",
+ sizeof(fc->fc_i2c_adap[0].i2c_adap.name));
+ strscpy(fc->fc_i2c_adap[1].i2c_adap.name, "B2C2 FlexCop I2C to eeprom",
+ sizeof(fc->fc_i2c_adap[1].i2c_adap.name));
+ strscpy(fc->fc_i2c_adap[2].i2c_adap.name, "B2C2 FlexCop I2C to tuner",
+ sizeof(fc->fc_i2c_adap[2].i2c_adap.name));
i2c_set_adapdata(&fc->fc_i2c_adap[0].i2c_adap, &fc->fc_i2c_adap[0]);
i2c_set_adapdata(&fc->fc_i2c_adap[1].i2c_adap, &fc->fc_i2c_adap[1]);
diff --git a/drivers/media/dvb/b2c2/flexcop-misc.c b/drivers/media/common/b2c2/flexcop-misc.c
index f06f3a9070f5..251c4f731ed1 100644
--- a/drivers/media/dvb/b2c2/flexcop-misc.c
+++ b/drivers/media/common/b2c2/flexcop-misc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
* flexcop-misc.c - miscellaneous functions
@@ -23,18 +24,15 @@ void flexcop_determine_revision(struct flexcop_device *fc)
fc->rev = FLEXCOP_III;
break;
default:
- err("unknown FlexCop Revision: %x. Please report this to "
- "linux-dvb@linuxtv.org.",
+ err("unknown FlexCop Revision: %x. Please report this to linux-dvb@linuxtv.org.",
v.misc_204.Rev_N_sig_revision_hi);
break;
}
if ((fc->has_32_hw_pid_filter = v.misc_204.Rev_N_sig_caps))
- deb_info("this FlexCop has "
- "the additional 32 hardware pid filter.\n");
+ deb_info("this FlexCop has the additional 32 hardware pid filter.\n");
else
- deb_info("this FlexCop has "
- "the 6 basic main hardware pid filter.\n");
+ deb_info("this FlexCop has the 6 basic main hardware pid filter.\n");
/* bus parts have to decide if hw pid filtering is used or not. */
}
@@ -56,6 +54,7 @@ static const char *flexcop_device_names[] = {
[FC_SKY_REV26] = "Sky2PC/SkyStar 2 DVB-S rev 2.6",
[FC_SKY_REV27] = "Sky2PC/SkyStar 2 DVB-S rev 2.7a/u",
[FC_SKY_REV28] = "Sky2PC/SkyStar 2 DVB-S rev 2.8",
+ [FC_SKYS2_REV33] = "Sky2PC/SkyStar S2 DVB-S/S2 rev 3.3",
};
static const char *flexcop_bus_names[] = {
@@ -71,16 +70,3 @@ void flexcop_device_name(struct flexcop_device *fc,
flexcop_bus_names[fc->bus_type],
flexcop_revision_names[fc->rev], suffix);
}
-
-void flexcop_dump_reg(struct flexcop_device *fc,
- flexcop_ibi_register reg, int num)
-{
- flexcop_ibi_value v;
- int i;
- for (i = 0; i < num; i++) {
- v = fc->read_ibi_reg(fc, reg+4*i);
- deb_rdump("0x%03x: %08x, ", reg+4*i, v.raw);
- }
- deb_rdump("\n");
-}
-EXPORT_SYMBOL(flexcop_dump_reg);
diff --git a/drivers/media/dvb/b2c2/flexcop-reg.h b/drivers/media/common/b2c2/flexcop-reg.h
index dc4528dcbb98..dd7c962db565 100644
--- a/drivers/media/dvb/b2c2/flexcop-reg.h
+++ b/drivers/media/common/b2c2/flexcop-reg.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
* flexcop-reg.h - register abstraction for FlexCopII, FlexCopIIb and FlexCopIII
@@ -24,6 +25,7 @@ typedef enum {
FC_SKY_REV26,
FC_SKY_REV27,
FC_SKY_REV28,
+ FC_SKYS2_REV33,
} flexcop_device_type_t;
typedef enum {
diff --git a/drivers/media/dvb/b2c2/flexcop-sram.c b/drivers/media/common/b2c2/flexcop-sram.c
index f2199e43e803..dba03769f263 100644
--- a/drivers/media/dvb/b2c2/flexcop-sram.c
+++ b/drivers/media/common/b2c2/flexcop-sram.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
* flexcop-sram.c - functions for controlling the SRAM
@@ -85,7 +86,7 @@ static void flexcop_sram_write(struct adapter *adapter, u32 bank, u32 addr, u8 *
while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
mdelay(1);
retries--;
- };
+ }
if (retries == 0)
printk("%s: SRAM timeout\n", __func__);
@@ -110,7 +111,7 @@ static void flex_sram_read(struct adapter *adapter, u32 bank, u32 addr, u8 *buf,
while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
mdelay(1);
retries--;
- };
+ }
if (retries == 0)
printk("%s: SRAM timeout\n", __func__);
@@ -122,7 +123,7 @@ static void flex_sram_read(struct adapter *adapter, u32 bank, u32 addr, u8 *buf,
while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
mdelay(1);
retries--;
- };
+ }
if (retries == 0)
printk("%s: SRAM timeout\n", __func__);
@@ -351,7 +352,7 @@ static int flexcop_sram_detect(struct flexcop_device *fc)
sram_set_size(adapter, 0x10000);
sram_init(adapter);
write_reg_dw(adapter, 0x208, tmp);
- dprintk("%s: SRAM detection failed. Set to 32K \n", __func__);
+ dprintk("%s: SRAM detection failed. Set to 32K\n", __func__);
return 0;
}
diff --git a/drivers/media/dvb/b2c2/flexcop.c b/drivers/media/common/b2c2/flexcop.c
index b1e8c99f469b..8506de48ba45 100644
--- a/drivers/media/dvb/b2c2/flexcop.c
+++ b/drivers/media/common/b2c2/flexcop.c
@@ -1,7 +1,8 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
* flexcop.c - main module part
- * Copyright (C) 2004-9 Patrick Boettcher <patrick.boettcher@desy.de>
+ * Copyright (C) 2004-9 Patrick Boettcher <patrick.boettcher@posteo.de>
* based on skystar2-driver Copyright (C) 2003 Vadim Catana, skystar@moldova.cc
*
* Acknowledgements:
@@ -15,26 +16,12 @@
* Uwe Bugla, uwe.bugla at gmx.de (doing tests, restyling code, writing docu)
* Niklas Peinecke, peinecke at gdv.uni-hannover.de (hardware pid/mac
* filtering)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "flexcop.h"
#define DRIVER_NAME "B2C2 FlexcopII/II(b)/III digital TV receiver chip"
-#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de"
+#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@posteo.de"
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
#define DEBSTATUS ""
@@ -43,10 +30,10 @@
#endif
int b2c2_flexcop_debug;
+EXPORT_SYMBOL_GPL(b2c2_flexcop_debug);
module_param_named(debug, b2c2_flexcop_debug, int, 0644);
MODULE_PARM_DESC(debug,
- "set debug level (1=info,2=tuner,4=i2c,8=ts,"
- "16=sram,32=reg (|-able))."
+ "set debug level (1=info,2=tuner,4=i2c,8=ts,16=sram,32=reg,64=i2cdump (|-able))."
DEBSTATUS);
#undef DEBSTATUS
@@ -183,7 +170,7 @@ static void flexcop_reset(struct flexcop_device *fc)
flexcop_ibi_value v210, v204;
/* reset the flexcop itself */
- fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
+ fc->write_ibi_reg(fc, ctrl_208, ibi_zero);
v210.raw = 0;
v210.sw_reset_210.reset_block_000 = 1;
@@ -196,17 +183,17 @@ static void flexcop_reset(struct flexcop_device *fc)
v210.sw_reset_210.reset_block_700 = 1;
v210.sw_reset_210.Block_reset_enable = 0xb2;
v210.sw_reset_210.Special_controls = 0xc259;
- fc->write_ibi_reg(fc,sw_reset_210,v210);
+ fc->write_ibi_reg(fc, sw_reset_210, v210);
msleep(1);
/* reset the periphical devices */
- v204 = fc->read_ibi_reg(fc,misc_204);
+ v204 = fc->read_ibi_reg(fc, misc_204);
v204.misc_204.Per_reset_sig = 0;
- fc->write_ibi_reg(fc,misc_204,v204);
+ fc->write_ibi_reg(fc, misc_204, v204);
msleep(1);
v204.misc_204.Per_reset_sig = 1;
- fc->write_ibi_reg(fc,misc_204,v204);
+ fc->write_ibi_reg(fc, misc_204, v204);
}
void flexcop_reset_block_300(struct flexcop_device *fc)
@@ -215,13 +202,13 @@ void flexcop_reset_block_300(struct flexcop_device *fc)
v210 = fc->read_ibi_reg(fc, sw_reset_210);
deb_rdump("208: %08x, 210: %08x\n", v208_save.raw, v210.raw);
- fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
+ fc->write_ibi_reg(fc, ctrl_208, ibi_zero);
v210.sw_reset_210.reset_block_300 = 1;
v210.sw_reset_210.Block_reset_enable = 0xb2;
- fc->write_ibi_reg(fc,sw_reset_210,v210);
- fc->write_ibi_reg(fc,ctrl_208,v208_save);
+ fc->write_ibi_reg(fc, sw_reset_210, v210);
+ fc->write_ibi_reg(fc, ctrl_208, v208_save);
}
struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len)
@@ -279,8 +266,8 @@ int flexcop_device_initialize(struct flexcop_device *fc)
if (fc->get_mac_addr(fc, 0) == 0) {
u8 *b = fc->dvb_adapter.proposed_mac;
info("MAC address = %pM", b);
- flexcop_set_mac_filter(fc,b);
- flexcop_mac_filter_ctrl(fc,1);
+ flexcop_set_mac_filter(fc, b);
+ flexcop_mac_filter_ctrl(fc, 1);
} else
warn("reading of MAC address failed.\n");
@@ -288,7 +275,7 @@ int flexcop_device_initialize(struct flexcop_device *fc)
if (ret)
goto error;
- flexcop_device_name(fc,"initialization of","complete");
+ flexcop_device_name(fc, "initialization of", "complete");
return 0;
error:
diff --git a/drivers/media/common/b2c2/flexcop.h b/drivers/media/common/b2c2/flexcop.h
new file mode 100644
index 000000000000..05e595f896dc
--- /dev/null
+++ b/drivers/media/common/b2c2/flexcop.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop.h - private header file for all flexcop-chip-source files
+ * see flexcop.c for copyright information
+ */
+#ifndef __FLEXCOP_H__
+#define __FLEXCOP_H__
+
+#define FC_LOG_PREFIX "b2c2-flexcop"
+#include "flexcop-common.h"
+
+extern int b2c2_flexcop_debug;
+
+/* debug */
+#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
+#define dprintk(level, args...) \
+ do { if ((b2c2_flexcop_debug & (level))) printk(args); } while (0)
+#else
+#define dprintk(level, args...) no_printk(args)
+#endif
+
+#define deb_info(args...) dprintk(0x01, args)
+#define deb_tuner(args...) dprintk(0x02, args)
+#define deb_i2c(args...) dprintk(0x04, args)
+#define deb_ts(args...) dprintk(0x08, args)
+#define deb_sram(args...) dprintk(0x10, args)
+#define deb_rdump(args...) dprintk(0x20, args)
+#define deb_i2c_dump(args...) dprintk(0x40, args)
+
+#endif
diff --git a/drivers/media/dvb/b2c2/flexcop_ibi_value_be.h b/drivers/media/common/b2c2/flexcop_ibi_value_be.h
index 8f64bdbd72bb..c97a0d6d7b3a 100644
--- a/drivers/media/dvb/b2c2/flexcop_ibi_value_be.h
+++ b/drivers/media/common/b2c2/flexcop_ibi_value_be.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
* register descriptions
* see flexcop.c for copyright information
diff --git a/drivers/media/dvb/b2c2/flexcop_ibi_value_le.h b/drivers/media/common/b2c2/flexcop_ibi_value_le.h
index c75830d7d942..5db3b46f21ee 100644
--- a/drivers/media/dvb/b2c2/flexcop_ibi_value_le.h
+++ b/drivers/media/common/b2c2/flexcop_ibi_value_le.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
* register descriptions
* see flexcop.c for copyright information
diff --git a/drivers/media/video/cx2341x.c b/drivers/media/common/cx2341x.c
index 103ef6bad2e2..1ee159ef7f38 100644
--- a/drivers/media/video/cx2341x.c
+++ b/drivers/media/common/cx2341x.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* cx2341x - generic code for cx23415/6/8 based devices
*
- * Copyright (C) 2006 Hans Verkuil <hverkuil@xs4all.nl>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Copyright (C) 2006 Hans Verkuil <hverkuil@kernel.org>
*/
@@ -27,7 +14,7 @@
#include <linux/videodev2.h>
#include <media/tuner.h>
-#include <media/cx2341x.h>
+#include <media/drv-intf/cx2341x.h>
#include <media/v4l2-common.h>
MODULE_DESCRIPTION("cx23415/6/8 driver");
@@ -179,7 +166,7 @@ static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *ty
/* Must be sorted from low to high control ID! */
const u32 cx2341x_mpeg_ctrls[] = {
- V4L2_CID_MPEG_CLASS,
+ V4L2_CID_CODEC_CLASS,
V4L2_CID_MPEG_STREAM_TYPE,
V4L2_CID_MPEG_STREAM_VBI_FMT,
V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
@@ -573,7 +560,7 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl,
qctrl->step = step;
qctrl->default_value = def;
qctrl->reserved[0] = qctrl->reserved[1] = 0;
- strlcpy(qctrl->name, name, sizeof(qctrl->name));
+ strscpy(qctrl->name, name, sizeof(qctrl->name));
return 0;
default:
@@ -587,7 +574,7 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params,
int err;
switch (qctrl->id) {
- case V4L2_CID_MPEG_CLASS:
+ case V4L2_CID_CODEC_CLASS:
return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0);
case V4L2_CID_MPEG_STREAM_TYPE:
return v4l2_ctrl_query_fill(qctrl,
@@ -931,6 +918,35 @@ static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
}
}
+/* Check for correctness of the ctrl's value based on the data from
+ struct v4l2_queryctrl and the available menu items. Note that
+ menu_items may be NULL, in that case it is ignored. */
+static int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
+ const char * const *menu_items)
+{
+ if (qctrl->flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+ if (qctrl->flags & V4L2_CTRL_FLAG_GRABBED)
+ return -EBUSY;
+ if (qctrl->type == V4L2_CTRL_TYPE_STRING)
+ return 0;
+ if (qctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+ qctrl->type == V4L2_CTRL_TYPE_INTEGER64 ||
+ qctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ return 0;
+ if (ctrl->value < qctrl->minimum || ctrl->value > qctrl->maximum)
+ return -ERANGE;
+ if (qctrl->type == V4L2_CTRL_TYPE_MENU && menu_items != NULL) {
+ if (menu_items[ctrl->value] == NULL ||
+ menu_items[ctrl->value][0] == '\0')
+ return -EINVAL;
+ }
+ if (qctrl->type == V4L2_CTRL_TYPE_BITMASK &&
+ (ctrl->value & ~qctrl->maximum))
+ return -ERANGE;
+ return 0;
+}
+
int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy,
struct v4l2_ext_controls *ctrls, unsigned int cmd)
{
@@ -1003,7 +1019,7 @@ static int cx2341x_api(void *priv, cx2341x_mbox_func func,
return func(priv, cmd, args, 0, data);
}
-#define NEQ(field) (old->field != new->field)
+#define CMP_FIELD(__old, __new, __field) (__old->__field != __new->__field)
int cx2341x_update(void *priv, cx2341x_mbox_func func,
const struct cx2341x_mpeg_params *old,
@@ -1017,20 +1033,22 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
11, /* VCD */
12, /* SVCD */
};
-
- int err = 0;
- int force = (old == NULL);
- u16 temporal = new->video_temporal_filter;
+ int err;
cx2341x_api(priv, func, CX2341X_ENC_SET_OUTPUT_PORT, 2, new->port, 0);
- if (force || NEQ(is_50hz)) {
+ if (!old ||
+ CMP_FIELD(old, new, is_50hz)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_RATE, 1,
new->is_50hz);
- if (err) return err;
+ if (err)
+ return err;
}
- if (force || NEQ(width) || NEQ(height) || NEQ(video_encoding)) {
+ if (!old ||
+ CMP_FIELD(old, new, width) ||
+ CMP_FIELD(old, new, height) ||
+ CMP_FIELD(old, new, video_encoding)) {
u16 w = new->width;
u16 h = new->height;
@@ -1040,94 +1058,127 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
}
err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2,
h, w);
- if (err) return err;
+ if (err)
+ return err;
}
- if (force || NEQ(stream_type)) {
+ if (!old ||
+ CMP_FIELD(old, new, stream_type)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1,
mpeg_stream_type[new->stream_type]);
- if (err) return err;
+ if (err)
+ return err;
}
- if (force || NEQ(video_aspect)) {
+ if (!old ||
+ CMP_FIELD(old, new, video_aspect)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_ASPECT_RATIO, 1,
1 + new->video_aspect);
- if (err) return err;
+ if (err)
+ return err;
}
- if (force || NEQ(video_b_frames) || NEQ(video_gop_size)) {
+ if (!old ||
+ CMP_FIELD(old, new, video_b_frames) ||
+ CMP_FIELD(old, new, video_gop_size)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_PROPERTIES, 2,
- new->video_gop_size, new->video_b_frames + 1);
- if (err) return err;
+ new->video_gop_size, new->video_b_frames + 1);
+ if (err)
+ return err;
}
- if (force || NEQ(video_gop_closure)) {
+ if (!old ||
+ CMP_FIELD(old, new, video_gop_closure)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1,
new->video_gop_closure);
- if (err) return err;
+ if (err)
+ return err;
}
- if (force || NEQ(audio_properties)) {
+ if (!old ||
+ CMP_FIELD(old, new, audio_properties)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES,
1, new->audio_properties);
- if (err) return err;
+ if (err)
+ return err;
}
- if (force || NEQ(audio_mute)) {
+ if (!old ||
+ CMP_FIELD(old, new, audio_mute)) {
err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1,
new->audio_mute);
- if (err) return err;
+ if (err)
+ return err;
}
- if (force || NEQ(video_bitrate_mode) || NEQ(video_bitrate) ||
- NEQ(video_bitrate_peak)) {
+ if (!old ||
+ CMP_FIELD(old, new, video_bitrate_mode) ||
+ CMP_FIELD(old, new, video_bitrate) ||
+ CMP_FIELD(old, new, video_bitrate_peak)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_BIT_RATE, 5,
- new->video_bitrate_mode, new->video_bitrate,
- new->video_bitrate_peak / 400, 0, 0);
- if (err) return err;
+ new->video_bitrate_mode, new->video_bitrate,
+ new->video_bitrate_peak / 400, 0, 0);
+ if (err)
+ return err;
}
- if (force || NEQ(video_spatial_filter_mode) ||
- NEQ(video_temporal_filter_mode) ||
- NEQ(video_median_filter_type)) {
+ if (!old ||
+ CMP_FIELD(old, new, video_spatial_filter_mode) ||
+ CMP_FIELD(old, new, video_temporal_filter_mode) ||
+ CMP_FIELD(old, new, video_median_filter_type)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_MODE,
- 2, new->video_spatial_filter_mode |
+ 2,
+ new->video_spatial_filter_mode |
(new->video_temporal_filter_mode << 1),
- new->video_median_filter_type);
- if (err) return err;
+ new->video_median_filter_type);
+ if (err)
+ return err;
}
- if (force || NEQ(video_luma_median_filter_bottom) ||
- NEQ(video_luma_median_filter_top) ||
- NEQ(video_chroma_median_filter_bottom) ||
- NEQ(video_chroma_median_filter_top)) {
+ if (!old ||
+ CMP_FIELD(old, new, video_luma_median_filter_bottom) ||
+ CMP_FIELD(old, new, video_luma_median_filter_top) ||
+ CMP_FIELD(old, new, video_chroma_median_filter_bottom) ||
+ CMP_FIELD(old, new, video_chroma_median_filter_top)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_CORING_LEVELS, 4,
- new->video_luma_median_filter_bottom,
- new->video_luma_median_filter_top,
- new->video_chroma_median_filter_bottom,
- new->video_chroma_median_filter_top);
- if (err) return err;
+ new->video_luma_median_filter_bottom,
+ new->video_luma_median_filter_top,
+ new->video_chroma_median_filter_bottom,
+ new->video_chroma_median_filter_top);
+ if (err)
+ return err;
}
- if (force || NEQ(video_luma_spatial_filter_type) ||
- NEQ(video_chroma_spatial_filter_type)) {
+ if (!old ||
+ CMP_FIELD(old, new, video_luma_spatial_filter_type) ||
+ CMP_FIELD(old, new, video_chroma_spatial_filter_type)) {
err = cx2341x_api(priv, func,
CX2341X_ENC_SET_SPATIAL_FILTER_TYPE,
2, new->video_luma_spatial_filter_type,
new->video_chroma_spatial_filter_type);
- if (err) return err;
+ if (err)
+ return err;
}
- if (force || NEQ(video_spatial_filter) ||
- old->video_temporal_filter != temporal) {
+ if (!old ||
+ CMP_FIELD(old, new, video_spatial_filter) ||
+ CMP_FIELD(old, new, video_temporal_filter)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS,
- 2, new->video_spatial_filter, temporal);
- if (err) return err;
+ 2, new->video_spatial_filter,
+ new->video_temporal_filter);
+ if (err)
+ return err;
}
- if (force || NEQ(video_temporal_decimation)) {
+ if (!old ||
+ CMP_FIELD(old, new, video_temporal_decimation)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_DROP_RATE,
1, new->video_temporal_decimation);
- if (err) return err;
+ if (err)
+ return err;
}
- if (force || NEQ(video_mute) ||
- (new->video_mute && NEQ(video_mute_yuv))) {
+ if (!old ||
+ CMP_FIELD(old, new, video_mute) ||
+ (new->video_mute && CMP_FIELD(old, new, video_mute_yuv))) {
err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1,
- new->video_mute | (new->video_mute_yuv << 8));
- if (err) return err;
+ new->video_mute | (new->video_mute_yuv << 8));
+ if (err)
+ return err;
}
- if (force || NEQ(stream_insert_nav_packets)) {
+ if (!old ||
+ CMP_FIELD(old, new, stream_insert_nav_packets)) {
err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2,
- 7, new->stream_insert_nav_packets);
- if (err) return err;
+ 7, new->stream_insert_nav_packets);
+ if (err)
+ return err;
}
return 0;
}
@@ -1161,8 +1212,8 @@ void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix)
prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE));
if (p->stream_insert_nav_packets)
- printk(" (with navigation packets)");
- printk("\n");
+ printk(KERN_CONT " (with navigation packets)");
+ printk(KERN_CONT "\n");
printk(KERN_INFO "%s: VBI Format: %s\n",
prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT));
@@ -1180,8 +1231,8 @@ void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix)
cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE),
p->video_bitrate);
if (p->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
- printk(", Peak %d", p->video_bitrate_peak);
- printk("\n");
+ printk(KERN_CONT ", Peak %d", p->video_bitrate_peak);
+ printk(KERN_CONT "\n");
printk(KERN_INFO
"%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n",
prefix,
@@ -1203,9 +1254,9 @@ void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix)
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE),
p->audio_mute ? " (muted)" : "");
if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO)
- printk(", %s", cx2341x_menu_item(p,
+ printk(KERN_CONT ", %s", cx2341x_menu_item(p,
V4L2_CID_MPEG_AUDIO_MODE_EXTENSION));
- printk(", %s, %s\n",
+ printk(KERN_CONT ", %s, %s\n",
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_EMPHASIS),
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC));
@@ -1490,6 +1541,7 @@ static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
{
struct v4l2_ctrl_config cfg;
+ memset(&cfg, 0, sizeof(cfg));
cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags);
cfg.ops = &cx2341x_ops;
cfg.id = id;
diff --git a/drivers/media/common/cypress_firmware.c b/drivers/media/common/cypress_firmware.c
new file mode 100644
index 000000000000..cdc7050ed3ac
--- /dev/null
+++ b/drivers/media/common/cypress_firmware.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* cypress_firmware.c is part of the DVB USB library.
+ *
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
+ * see dvb-usb-init.c for copyright information.
+ *
+ * This file contains functions for downloading the firmware to Cypress FX 1
+ * and 2 based devices.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include "cypress_firmware.h"
+
+struct usb_cypress_controller {
+ u8 id;
+ const char *name; /* name of the usb controller */
+ u16 cs_reg; /* needs to be restarted,
+ * when the firmware has been downloaded */
+};
+
+static const struct usb_cypress_controller cypress[] = {
+ { .id = CYPRESS_AN2135, .name = "Cypress AN2135", .cs_reg = 0x7f92 },
+ { .id = CYPRESS_AN2235, .name = "Cypress AN2235", .cs_reg = 0x7f92 },
+ { .id = CYPRESS_FX2, .name = "Cypress FX2", .cs_reg = 0xe600 },
+};
+
+/*
+ * load a firmware packet to the device
+ */
+static int usb_cypress_writemem(struct usb_device *udev, u16 addr, u8 *data,
+ u8 len)
+{
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000);
+}
+
+static int cypress_get_hexline(const struct firmware *fw,
+ struct hexline *hx, int *pos)
+{
+ u8 *b = (u8 *) &fw->data[*pos];
+ int data_offs = 4;
+
+ if (*pos >= fw->size)
+ return 0;
+
+ memset(hx, 0, sizeof(struct hexline));
+ hx->len = b[0];
+
+ if ((*pos + hx->len + 4) >= fw->size)
+ return -EINVAL;
+
+ hx->addr = b[1] | (b[2] << 8);
+ hx->type = b[3];
+
+ if (hx->type == 0x04) {
+ /* b[4] and b[5] are the Extended linear address record data
+ * field */
+ hx->addr |= (b[4] << 24) | (b[5] << 16);
+ }
+
+ memcpy(hx->data, &b[data_offs], hx->len);
+ hx->chk = b[hx->len + data_offs];
+ *pos += hx->len + 5;
+
+ return *pos;
+}
+
+int cypress_load_firmware(struct usb_device *udev,
+ const struct firmware *fw, int type)
+{
+ struct hexline *hx;
+ int ret, pos = 0;
+
+ hx = kmalloc(sizeof(*hx), GFP_KERNEL);
+ if (!hx)
+ return -ENOMEM;
+
+ /* stop the CPU */
+ hx->data[0] = 1;
+ ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1);
+ if (ret != 1) {
+ dev_err(&udev->dev, "%s: CPU stop failed=%d\n",
+ KBUILD_MODNAME, ret);
+ ret = -EIO;
+ goto err_kfree;
+ }
+
+ /* write firmware to memory */
+ for (;;) {
+ ret = cypress_get_hexline(fw, hx, &pos);
+ if (ret < 0)
+ goto err_kfree;
+ else if (ret == 0)
+ break;
+
+ ret = usb_cypress_writemem(udev, hx->addr, hx->data, hx->len);
+ if (ret < 0) {
+ goto err_kfree;
+ } else if (ret != hx->len) {
+ dev_err(&udev->dev,
+ "%s: error while transferring firmware (transferred size=%d, block size=%d)\n",
+ KBUILD_MODNAME, ret, hx->len);
+ ret = -EIO;
+ goto err_kfree;
+ }
+ }
+
+ /* start the CPU */
+ hx->data[0] = 0;
+ ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1);
+ if (ret != 1) {
+ dev_err(&udev->dev, "%s: CPU start failed=%d\n",
+ KBUILD_MODNAME, ret);
+ ret = -EIO;
+ goto err_kfree;
+ }
+
+ ret = 0;
+err_kfree:
+ kfree(hx);
+ return ret;
+}
+EXPORT_SYMBOL(cypress_load_firmware);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Cypress firmware download");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/cypress_firmware.h b/drivers/media/common/cypress_firmware.h
new file mode 100644
index 000000000000..0aa46e602b07
--- /dev/null
+++ b/drivers/media/common/cypress_firmware.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@posteo.de)
+ * see dvb-usb-init.c for copyright information.
+ *
+ * This file contains functions for downloading the firmware to Cypress FX 1
+ * and 2 based devices.
+ *
+ */
+
+#ifndef CYPRESS_FIRMWARE_H
+#define CYPRESS_FIRMWARE_H
+
+#define CYPRESS_AN2135 0
+#define CYPRESS_AN2235 1
+#define CYPRESS_FX2 2
+
+/* commonly used firmware download types and function */
+struct hexline {
+ u8 len;
+ u32 addr;
+ u8 type;
+ u8 data[255];
+ u8 chk;
+};
+
+int cypress_load_firmware(struct usb_device *, const struct firmware *, int);
+
+#endif
diff --git a/drivers/media/common/saa7146/Kconfig b/drivers/media/common/saa7146/Kconfig
new file mode 100644
index 000000000000..dfec86e50dff
--- /dev/null
+++ b/drivers/media/common/saa7146/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_SAA7146
+ tristate
+ depends on I2C && PCI
+
+config VIDEO_SAA7146_VV
+ tristate
+ depends on VIDEO_DEV
+ select VIDEOBUF2_DMA_SG
+ select VIDEO_SAA7146
diff --git a/drivers/media/common/saa7146/Makefile b/drivers/media/common/saa7146/Makefile
new file mode 100644
index 000000000000..2a6337feaec8
--- /dev/null
+++ b/drivers/media/common/saa7146/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+saa7146-objs := saa7146_i2c.o saa7146_core.o
+saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o
+
+obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o
+obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o
diff --git a/drivers/media/common/saa7146_core.c b/drivers/media/common/saa7146/saa7146_core.c
index d6b1cf66042d..27c53eed8fe3 100644
--- a/drivers/media/common/saa7146_core.c
+++ b/drivers/media/common/saa7146/saa7146_core.c
@@ -1,31 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
saa7146.o - driver for generic saa7146-based hardware
Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <media/saa7146.h>
+#include <media/drv-intf/saa7146.h>
#include <linux/module.h>
-LIST_HEAD(saa7146_devices);
-DEFINE_MUTEX(saa7146_devices_lock);
-
static int saa7146_num;
unsigned int saa7146_debug;
@@ -52,7 +37,8 @@ void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data)
{
u32 value = 0;
- BUG_ON(port > 3);
+ if (WARN_ON(port > 3))
+ return;
value = saa7146_read(dev, GPIO_CTRL);
value &= ~(0xff << (8*port));
@@ -74,7 +60,7 @@ static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev,
if (saa7146_read(dev, MC2) & 2)
break;
if (err) {
- pr_err("%s: %s timed out while waiting for registers getting programmed\n",
+ pr_debug("%s: %s timed out while waiting for registers getting programmed\n",
dev->name, __func__);
return -ETIMEDOUT;
}
@@ -147,15 +133,15 @@ int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop)
****************************************************************************/
/* this is videobuf_vmalloc_to_sg() from videobuf-dma-sg.c
- make sure virt has been allocated with vmalloc_32(), otherwise the BUG()
- may be triggered on highmem machines */
+ make sure virt has been allocated with vmalloc_32(), otherwise return NULL
+ on highmem machines */
static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages)
{
struct scatterlist *sglist;
struct page *pg;
int i;
- sglist = kcalloc(nr_pages, sizeof(struct scatterlist), GFP_KERNEL);
+ sglist = kmalloc_array(nr_pages, sizeof(struct scatterlist), GFP_KERNEL);
if (NULL == sglist)
return NULL;
sg_init_table(sglist, nr_pages);
@@ -163,7 +149,8 @@ static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages)
pg = vmalloc_to_page(virt);
if (NULL == pg)
goto err;
- BUG_ON(PageHighMem(pg));
+ if (WARN_ON(PageHighMem(pg)))
+ goto err;
sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
}
return sglist;
@@ -192,7 +179,7 @@ void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa
goto err_free_slist;
pt->nents = pages;
- slen = pci_map_sg(pci,pt->slist,pt->nents,PCI_DMA_FROMDEVICE);
+ slen = dma_map_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE);
if (0 == slen)
goto err_free_pgtable;
@@ -202,7 +189,7 @@ void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa
return mem;
err_unmap_sg:
- pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);
+ dma_unmap_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE);
err_free_pgtable:
saa7146_pgtable_free(pci, pt);
err_free_slist:
@@ -216,7 +203,7 @@ err_null:
void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt)
{
- pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);
+ dma_unmap_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE);
saa7146_pgtable_free(pci, pt);
kfree(pt->slist);
pt->slist = NULL;
@@ -227,7 +214,7 @@ void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)
{
if (NULL == pt->cpu)
return;
- pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
+ dma_free_coherent(&pci->dev, pt->size, pt->cpu, pt->dma);
pt->cpu = NULL;
}
@@ -236,7 +223,7 @@ int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
__le32 *cpu;
dma_addr_t dma_addr = 0;
- cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr);
+ cpu = dma_alloc_coherent(&pci->dev, PAGE_SIZE, &dma_addr, GFP_KERNEL);
if (NULL == cpu) {
return -ENOMEM;
}
@@ -248,46 +235,32 @@ int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
}
int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt,
- struct scatterlist *list, int sglen )
+ struct scatterlist *list, int sglen)
{
+ struct sg_dma_page_iter dma_iter;
__le32 *ptr, fill;
int nr_pages = 0;
- int i,p;
+ int i;
- BUG_ON(0 == sglen);
- BUG_ON(list->offset > PAGE_SIZE);
+ if (WARN_ON(!sglen) ||
+ WARN_ON(list->offset > PAGE_SIZE))
+ return -EIO;
/* if we have a user buffer, the first page may not be
aligned to a page boundary. */
pt->offset = list->offset;
ptr = pt->cpu;
- for (i = 0; i < sglen; i++, list++) {
-/*
- pr_debug("i:%d, adr:0x%08x, len:%d, offset:%d\n",
- i, sg_dma_address(list), sg_dma_len(list),
- list->offset);
-*/
- for (p = 0; p * 4096 < list->length; p++, ptr++) {
- *ptr = cpu_to_le32(sg_dma_address(list) + p * 4096);
- nr_pages++;
- }
+ for_each_sg_dma_page(list, &dma_iter, sglen, 0) {
+ *ptr++ = cpu_to_le32(sg_page_iter_dma_address(&dma_iter));
+ nr_pages++;
}
/* safety; fill the page table up with the last valid page */
fill = *(ptr-1);
- for(i=nr_pages;i<1024;i++) {
+ for (i = nr_pages; i < 1024; i++)
*ptr++ = fill;
- }
-
-/*
- ptr = pt->cpu;
- pr_debug("offset: %d\n", pt->offset);
- for(i=0;i<5;i++) {
- pr_debug("ptr1 %d: 0x%08x\n", i, ptr[i]);
- }
-*/
return 0;
}
@@ -367,6 +340,9 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
goto out;
}
+ /* create a nice device name */
+ sprintf(dev->name, "saa7146 (%d)", saa7146_num);
+
DEB_EE("pci:%p\n", pci);
err = pci_enable_device(pci);
@@ -414,7 +390,7 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
saa7146_write(dev, MC2, 0xf8000000);
/* request an interrupt for the saa7146 */
- err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED | IRQF_DISABLED,
+ err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED,
dev->name, dev);
if (err < 0) {
ERR("request_irq() failed\n");
@@ -424,29 +400,25 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
err = -ENOMEM;
/* get memory for various stuff */
- dev->d_rps0.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
- &dev->d_rps0.dma_handle);
+ dev->d_rps0.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM,
+ &dev->d_rps0.dma_handle,
+ GFP_KERNEL);
if (!dev->d_rps0.cpu_addr)
goto err_free_irq;
- memset(dev->d_rps0.cpu_addr, 0x0, SAA7146_RPS_MEM);
- dev->d_rps1.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
- &dev->d_rps1.dma_handle);
+ dev->d_rps1.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM,
+ &dev->d_rps1.dma_handle,
+ GFP_KERNEL);
if (!dev->d_rps1.cpu_addr)
goto err_free_rps0;
- memset(dev->d_rps1.cpu_addr, 0x0, SAA7146_RPS_MEM);
- dev->d_i2c.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
- &dev->d_i2c.dma_handle);
+ dev->d_i2c.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM,
+ &dev->d_i2c.dma_handle, GFP_KERNEL);
if (!dev->d_i2c.cpu_addr)
goto err_free_rps1;
- memset(dev->d_i2c.cpu_addr, 0x0, SAA7146_RPS_MEM);
/* the rest + print status message */
- /* create a nice device name */
- sprintf(dev->name, "saa7146 (%d)", saa7146_num);
-
pr_info("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x)\n",
dev->mem, dev->revision, pci->irq,
pci->subsystem_vendor, pci->subsystem_device);
@@ -482,8 +454,6 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
set it explicitly. */
pci_set_drvdata(pci, &dev->v4l2_dev);
- INIT_LIST_HEAD(&dev->item);
- list_add_tail(&dev->item,&saa7146_devices);
saa7146_num++;
err = 0;
@@ -491,14 +461,14 @@ out:
return err;
err_free_i2c:
- pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr,
- dev->d_i2c.dma_handle);
+ dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr,
+ dev->d_i2c.dma_handle);
err_free_rps1:
- pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr,
- dev->d_rps1.dma_handle);
+ dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr,
+ dev->d_rps1.dma_handle);
err_free_rps0:
- pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr,
- dev->d_rps0.dma_handle);
+ dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr,
+ dev->d_rps0.dma_handle);
err_free_irq:
free_irq(pci->irq, (void *)dev);
err_unmap:
@@ -529,8 +499,6 @@ static void saa7146_remove_one(struct pci_dev *pdev)
DEB_EE("dev:%p\n", dev);
dev->ext->detach(dev);
- /* Zero the PCI drvdata after use. */
- pci_set_drvdata(pdev, NULL);
/* shut down all video dma transfers */
saa7146_write(dev, MC1, 0x00ff0000);
@@ -541,11 +509,11 @@ static void saa7146_remove_one(struct pci_dev *pdev)
free_irq(pdev->irq, dev);
for (p = dev_map; p->addr; p++)
- pci_free_consistent(pdev, SAA7146_RPS_MEM, p->addr, p->dma);
+ dma_free_coherent(&pdev->dev, SAA7146_RPS_MEM, p->addr,
+ p->dma);
iounmap(dev->mem);
pci_release_region(pdev, 0);
- list_del(&dev->item);
pci_disable_device(pdev);
kfree(dev);
@@ -592,8 +560,6 @@ EXPORT_SYMBOL_GPL(saa7146_setgpio);
EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare);
EXPORT_SYMBOL_GPL(saa7146_debug);
-EXPORT_SYMBOL_GPL(saa7146_devices);
-EXPORT_SYMBOL_GPL(saa7146_devices_lock);
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("driver for generic saa7146-based hardware");
diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c
new file mode 100644
index 000000000000..a9e3bad76d54
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_fops.c
@@ -0,0 +1,435 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <media/drv-intf/saa7146_vv.h>
+#include <linux/module.h>
+
+/****************************************************************************/
+/* resource management functions, shamelessly stolen from saa7134 driver */
+
+int saa7146_res_get(struct saa7146_dev *dev, unsigned int bit)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ if (vv->resources & bit) {
+ DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n",
+ bit, vv->resources);
+ /* have it already allocated */
+ return 1;
+ }
+
+ /* is it free? */
+ if (vv->resources & bit) {
+ DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n",
+ vv->resources, bit);
+ /* no, someone else uses it */
+ return 0;
+ }
+ /* it's free, grab it */
+ vv->resources |= bit;
+ DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources);
+ return 1;
+}
+
+void saa7146_res_free(struct saa7146_dev *dev, unsigned int bits)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ WARN_ON((vv->resources & bits) != bits);
+
+ vv->resources &= ~bits;
+ DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources);
+}
+
+
+/********************************************************************************/
+/* common buffer functions */
+
+int saa7146_buffer_queue(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q,
+ struct saa7146_buf *buf)
+{
+ assert_spin_locked(&dev->slock);
+ DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf);
+
+ if (WARN_ON(!q))
+ return -EIO;
+
+ if (NULL == q->curr) {
+ q->curr = buf;
+ DEB_D("immediately activating buffer %p\n", buf);
+ buf->activate(dev,buf,NULL);
+ } else {
+ list_add_tail(&buf->list, &q->queue);
+ DEB_D("adding buffer %p to queue. (active buffer present)\n",
+ buf);
+ }
+ return 0;
+}
+
+void saa7146_buffer_finish(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q,
+ int state)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_buf *buf = q->curr;
+
+ assert_spin_locked(&dev->slock);
+ DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state);
+ DEB_EE("q->curr:%p\n", q->curr);
+
+ /* finish current buffer */
+ if (!buf) {
+ DEB_D("aiii. no current buffer\n");
+ return;
+ }
+
+ q->curr = NULL;
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
+ if (vv->video_fmt.field == V4L2_FIELD_ALTERNATE)
+ buf->vb.field = vv->last_field;
+ else if (vv->video_fmt.field == V4L2_FIELD_ANY)
+ buf->vb.field = (vv->video_fmt.height > vv->standard->v_max_out / 2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ else
+ buf->vb.field = vv->video_fmt.field;
+ buf->vb.sequence = vv->seqnr++;
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+}
+
+void saa7146_buffer_next(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q, int vbi)
+{
+ struct saa7146_buf *buf,*next = NULL;
+
+ if (WARN_ON(!q))
+ return;
+
+ DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi);
+
+ assert_spin_locked(&dev->slock);
+ if (!list_empty(&q->queue)) {
+ /* activate next one from queue */
+ buf = list_entry(q->queue.next, struct saa7146_buf, list);
+ list_del(&buf->list);
+ if (!list_empty(&q->queue))
+ next = list_entry(q->queue.next, struct saa7146_buf, list);
+ q->curr = buf;
+ DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n",
+ buf, q->queue.prev, q->queue.next);
+ buf->activate(dev,buf,next);
+ } else {
+ DEB_INT("no next buffer. stopping.\n");
+ if( 0 != vbi ) {
+ /* turn off video-dma3 */
+ saa7146_write(dev,MC1, MASK_20);
+ } else {
+ /* nothing to do -- just prevent next video-dma1 transfer
+ by lowering the protection address */
+
+ // fixme: fix this for vflip != 0
+
+ saa7146_write(dev, PROT_ADDR1, 0);
+ saa7146_write(dev, MC2, (MASK_02|MASK_18));
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_12 | MASK_28));
+
+/*
+ printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
+ printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
+ printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
+ printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
+ printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
+ printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
+*/
+ }
+ timer_delete(&q->timeout);
+ }
+}
+
+void saa7146_buffer_timeout(struct timer_list *t)
+{
+ struct saa7146_dmaqueue *q = timer_container_of(q, t, timeout);
+ struct saa7146_dev *dev = q->dev;
+ unsigned long flags;
+
+ DEB_EE("dev:%p, dmaq:%p\n", dev, q);
+
+ spin_lock_irqsave(&dev->slock,flags);
+ if (q->curr) {
+ DEB_D("timeout on %p\n", q->curr);
+ saa7146_buffer_finish(dev, q, VB2_BUF_STATE_ERROR);
+ }
+
+ /* we don't restart the transfer here like other drivers do. when
+ a streaming capture is disabled, the timeout function will be
+ called for the current buffer. if we activate the next buffer now,
+ we mess up our capture logic. if a timeout occurs on another buffer,
+ then something is seriously broken before, so no need to buffer the
+ next capture IMHO... */
+
+ saa7146_buffer_next(dev, q, 0);
+
+ spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+/********************************************************************************/
+/* file operations */
+
+static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_dev *dev = video_drvdata(file);
+ int ret;
+
+ if (vdev->vfl_type != VFL_TYPE_VBI || !dev->ext_vv_data->vbi_write)
+ return -EINVAL;
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ ret = dev->ext_vv_data->vbi_write(file, data, count, ppos);
+ mutex_unlock(vdev->lock);
+ return ret;
+}
+
+static const struct v4l2_file_operations video_fops =
+{
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .write = fops_write,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static void vv_callback(struct saa7146_dev *dev, unsigned long status)
+{
+ u32 isr = status;
+
+ DEB_INT("dev:%p, isr:0x%08x\n", dev, (u32)status);
+
+ if (0 != (isr & (MASK_27))) {
+ DEB_INT("irq: RPS0 (0x%08x)\n", isr);
+ saa7146_video_uops.irq_done(dev,isr);
+ }
+
+ if (0 != (isr & (MASK_28))) {
+ u32 mc2 = saa7146_read(dev, MC2);
+ if( 0 != (mc2 & MASK_15)) {
+ DEB_INT("irq: RPS1 vbi workaround (0x%08x)\n", isr);
+ wake_up(&dev->vv_data->vbi_wq);
+ saa7146_write(dev,MC2, MASK_31);
+ return;
+ }
+ DEB_INT("irq: RPS1 (0x%08x)\n", isr);
+ saa7146_vbi_uops.irq_done(dev,isr);
+ }
+}
+
+static const struct v4l2_ctrl_ops saa7146_ctrl_ops = {
+ .s_ctrl = saa7146_s_ctrl,
+};
+
+int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
+{
+ struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
+ struct v4l2_pix_format *fmt;
+ struct v4l2_vbi_format *vbi;
+ struct saa7146_vv *vv;
+ int err;
+
+ err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);
+ if (err)
+ return err;
+
+ v4l2_ctrl_handler_init(hdl, 6);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ if (hdl->error) {
+ err = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return err;
+ }
+ dev->v4l2_dev.ctrl_handler = hdl;
+
+ vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
+ if (vv == NULL) {
+ ERR("out of memory. aborting.\n");
+ v4l2_ctrl_handler_free(hdl);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return -ENOMEM;
+ }
+ ext_vv->vid_ops = saa7146_video_ioctl_ops;
+ ext_vv->vbi_ops = saa7146_vbi_ioctl_ops;
+ ext_vv->core_ops = &saa7146_video_ioctl_ops;
+
+ DEB_EE("dev:%p\n", dev);
+
+ /* set default values for video parts of the saa7146 */
+ saa7146_write(dev, BCS_CTRL, 0x80400040);
+
+ /* enable video-port pins */
+ saa7146_write(dev, MC1, (MASK_10 | MASK_26));
+
+ /* save per-device extension data (one extension can
+ handle different devices that might need different
+ configuration data) */
+ dev->ext_vv_data = ext_vv;
+
+ saa7146_video_uops.init(dev,vv);
+ if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
+ saa7146_vbi_uops.init(dev,vv);
+
+ fmt = &vv->video_fmt;
+ fmt->width = 384;
+ fmt->height = 288;
+ fmt->pixelformat = V4L2_PIX_FMT_BGR24;
+ fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->bytesperline = 3 * fmt->width;
+ fmt->sizeimage = fmt->bytesperline * fmt->height;
+
+ vbi = &vv->vbi_fmt;
+ vbi->sampling_rate = 27000000;
+ vbi->offset = 248; /* todo */
+ vbi->samples_per_line = 720 * 2;
+ vbi->sample_format = V4L2_PIX_FMT_GREY;
+
+ /* fixme: this only works for PAL */
+ vbi->start[0] = 5;
+ vbi->count[0] = 16;
+ vbi->start[1] = 312;
+ vbi->count[1] = 16;
+
+ timer_setup(&vv->vbi_read_timeout, NULL, 0);
+
+ dev->vv_data = vv;
+ dev->vv_callback = &vv_callback;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_vv_init);
+
+int saa7146_vv_release(struct saa7146_dev* dev)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ DEB_EE("dev:%p\n", dev);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+ kfree(vv);
+ dev->vv_data = NULL;
+ dev->vv_callback = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_vv_release);
+
+int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev,
+ char *name, int type)
+{
+ struct vb2_queue *q;
+ int err;
+ int i;
+
+ DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type);
+
+ vfd->fops = &video_fops;
+ if (type == VFL_TYPE_VIDEO) {
+ vfd->ioctl_ops = &dev->ext_vv_data->vid_ops;
+ q = &dev->vv_data->video_dmaq.q;
+ } else {
+ vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops;
+ q = &dev->vv_data->vbi_dmaq.q;
+ }
+ vfd->release = video_device_release_empty;
+ vfd->lock = &dev->v4l2_lock;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->tvnorms = 0;
+ for (i = 0; i < dev->ext_vv_data->num_stds; i++)
+ vfd->tvnorms |= dev->ext_vv_data->stds[i].id;
+ strscpy(vfd->name, name, sizeof(vfd->name));
+ vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ vfd->device_caps |= dev->ext_vv_data->capabilities;
+ if (type == VFL_TYPE_VIDEO) {
+ vfd->device_caps &=
+ ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT);
+ } else if (vfd->device_caps & V4L2_CAP_SLICED_VBI_OUTPUT) {
+ vfd->vfl_dir = VFL_DIR_TX;
+ vfd->device_caps &= ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_AUDIO | V4L2_CAP_TUNER);
+ } else {
+ vfd->device_caps &= ~V4L2_CAP_VIDEO_CAPTURE;
+ }
+
+ q->type = type == VFL_TYPE_VIDEO ? V4L2_BUF_TYPE_VIDEO_CAPTURE : V4L2_BUF_TYPE_VBI_CAPTURE;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF;
+ q->ops = type == VFL_TYPE_VIDEO ? &video_qops : &vbi_qops;
+ q->mem_ops = &vb2_dma_sg_memops;
+ q->drv_priv = dev;
+ q->gfp_flags = __GFP_DMA32;
+ q->buf_struct_size = sizeof(struct saa7146_buf);
+ q->lock = &dev->v4l2_lock;
+ q->min_queued_buffers = 2;
+ q->dev = &dev->pci->dev;
+ err = vb2_queue_init(q);
+ if (err)
+ return err;
+ vfd->queue = q;
+
+ video_set_drvdata(vfd, dev);
+
+ err = video_register_device(vfd, type, -1);
+ if (err < 0) {
+ ERR("cannot register v4l2 device. skipping.\n");
+ return err;
+ }
+
+ pr_info("%s: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(vfd));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_register_device);
+
+int saa7146_unregister_device(struct video_device *vfd, struct saa7146_dev *dev)
+{
+ DEB_EE("dev:%p\n", dev);
+
+ video_unregister_device(vfd);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_unregister_device);
+
+static int __init saa7146_vv_init_module(void)
+{
+ return 0;
+}
+
+
+static void __exit saa7146_vv_cleanup_module(void)
+{
+}
+
+module_init(saa7146_vv_init_module);
+module_exit(saa7146_vv_cleanup_module);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/saa7146/saa7146_hlp.c b/drivers/media/common/saa7146/saa7146_hlp.c
new file mode 100644
index 000000000000..fe3348af543e
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_hlp.c
@@ -0,0 +1,771 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <media/drv-intf/saa7146_vv.h>
+
+static void calculate_output_format_register(struct saa7146_dev* saa, u32 palette, u32* clip_format)
+{
+ /* clear out the necessary bits */
+ *clip_format &= 0x0000ffff;
+ /* set these bits new */
+ *clip_format |= (( ((palette&0xf00)>>8) << 30) | ((palette&0x00f) << 24) | (((palette&0x0f0)>>4) << 16));
+}
+
+static void calculate_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync, u32* hps_ctrl)
+{
+ *hps_ctrl &= ~(MASK_30 | MASK_31 | MASK_28);
+ *hps_ctrl |= (source << 30) | (sync << 28);
+}
+
+static void calculate_hxo_and_hyo(struct saa7146_vv *vv, u32* hps_h_scale, u32* hps_ctrl)
+{
+ int hyo = 0, hxo = 0;
+
+ hyo = vv->standard->v_offset;
+ hxo = vv->standard->h_offset;
+
+ *hps_h_scale &= ~(MASK_B0 | 0xf00);
+ *hps_h_scale |= (hxo << 0);
+
+ *hps_ctrl &= ~(MASK_W0 | MASK_B2);
+ *hps_ctrl |= (hyo << 12);
+}
+
+/* helper functions for the calculation of the horizontal- and vertical
+ scaling registers, clip-format-register etc ...
+ these functions take pointers to the (most-likely read-out
+ original-values) and manipulate them according to the requested
+ changes.
+*/
+
+/* hps_coeff used for CXY and CXUV; scale 1/1 -> scale 1/64 */
+static struct {
+ u16 hps_coeff;
+ u16 weight_sum;
+} hps_h_coeff_tab [] = {
+ {0x00, 2}, {0x02, 4}, {0x00, 4}, {0x06, 8}, {0x02, 8},
+ {0x08, 8}, {0x00, 8}, {0x1E, 16}, {0x0E, 8}, {0x26, 8},
+ {0x06, 8}, {0x42, 8}, {0x02, 8}, {0x80, 8}, {0x00, 8},
+ {0xFE, 16}, {0xFE, 8}, {0x7E, 8}, {0x7E, 8}, {0x3E, 8},
+ {0x3E, 8}, {0x1E, 8}, {0x1E, 8}, {0x0E, 8}, {0x0E, 8},
+ {0x06, 8}, {0x06, 8}, {0x02, 8}, {0x02, 8}, {0x00, 8},
+ {0x00, 8}, {0xFE, 16}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0x7E, 8},
+ {0x7E, 8}, {0x3E, 8}, {0x3E, 8}, {0x1E, 8}, {0x1E, 8},
+ {0x0E, 8}, {0x0E, 8}, {0x06, 8}, {0x06, 8}, {0x02, 8},
+ {0x02, 8}, {0x00, 8}, {0x00, 8}, {0xFE, 16}
+};
+
+/* table of attenuation values for horizontal scaling */
+static u8 h_attenuation[] = { 1, 2, 4, 8, 2, 4, 8, 16, 0};
+
+/* calculate horizontal scale registers */
+static int calculate_h_scale_registers(struct saa7146_dev *dev,
+ int in_x, int out_x, int flip_lr,
+ u32* hps_ctrl, u32* hps_v_gain, u32* hps_h_prescale, u32* hps_h_scale)
+{
+ /* horizontal prescaler */
+ u32 dcgx = 0, xpsc = 0, xacm = 0, cxy = 0, cxuv = 0;
+ /* horizontal scaler */
+ u32 xim = 0, xp = 0, xsci =0;
+ /* vertical scale & gain */
+ u32 pfuv = 0;
+
+ /* helper variables */
+ u32 h_atten = 0, i = 0;
+
+ if ( 0 == out_x ) {
+ return -EINVAL;
+ }
+
+ /* mask out vanity-bit */
+ *hps_ctrl &= ~MASK_29;
+
+ /* calculate prescale-(xspc)-value: [n .. 1/2) : 1
+ [1/2 .. 1/3) : 2
+ [1/3 .. 1/4) : 3
+ ... */
+ if (in_x > out_x) {
+ xpsc = in_x / out_x;
+ }
+ else {
+ /* zooming */
+ xpsc = 1;
+ }
+
+ /* if flip_lr-bit is set, number of pixels after
+ horizontal prescaling must be < 384 */
+ if ( 0 != flip_lr ) {
+
+ /* set vanity bit */
+ *hps_ctrl |= MASK_29;
+
+ while (in_x / xpsc >= 384 )
+ xpsc++;
+ }
+ /* if zooming is wanted, number of pixels after
+ horizontal prescaling must be < 768 */
+ else {
+ while ( in_x / xpsc >= 768 )
+ xpsc++;
+ }
+
+ /* maximum prescale is 64 (p.69) */
+ if ( xpsc > 64 )
+ xpsc = 64;
+
+ /* keep xacm clear*/
+ xacm = 0;
+
+ /* set horizontal filter parameters (CXY = CXUV) */
+ cxy = hps_h_coeff_tab[min(xpsc - 1, 63)].hps_coeff;
+ cxuv = cxy;
+
+ /* calculate and set horizontal fine scale (xsci) */
+
+ /* bypass the horizontal scaler ? */
+ if ( (in_x == out_x) && ( 1 == xpsc ) )
+ xsci = 0x400;
+ else
+ xsci = ( (1024 * in_x) / (out_x * xpsc) ) + xpsc;
+
+ /* set start phase for horizontal fine scale (xp) to 0 */
+ xp = 0;
+
+ /* set xim, if we bypass the horizontal scaler */
+ if ( 0x400 == xsci )
+ xim = 1;
+ else
+ xim = 0;
+
+ /* if the prescaler is bypassed, enable horizontal
+ accumulation mode (xacm) and clear dcgx */
+ if( 1 == xpsc ) {
+ xacm = 1;
+ dcgx = 0;
+ } else {
+ xacm = 0;
+ /* get best match in the table of attenuations
+ for horizontal scaling */
+ h_atten = hps_h_coeff_tab[min(xpsc - 1, 63)].weight_sum;
+
+ for (i = 0; h_attenuation[i] != 0; i++) {
+ if (h_attenuation[i] >= h_atten)
+ break;
+ }
+
+ dcgx = i;
+ }
+
+ /* the horizontal scaling increment controls the UV filter
+ to reduce the bandwidth to improve the display quality,
+ so set it ... */
+ if ( xsci == 0x400)
+ pfuv = 0x00;
+ else if ( xsci < 0x600)
+ pfuv = 0x01;
+ else if ( xsci < 0x680)
+ pfuv = 0x11;
+ else if ( xsci < 0x700)
+ pfuv = 0x22;
+ else
+ pfuv = 0x33;
+
+
+ *hps_v_gain &= MASK_W0|MASK_B2;
+ *hps_v_gain |= (pfuv << 24);
+
+ *hps_h_scale &= ~(MASK_W1 | 0xf000);
+ *hps_h_scale |= (xim << 31) | (xp << 24) | (xsci << 12);
+
+ *hps_h_prescale |= (dcgx << 27) | ((xpsc-1) << 18) | (xacm << 17) | (cxy << 8) | (cxuv << 0);
+
+ return 0;
+}
+
+static struct {
+ u16 hps_coeff;
+ u16 weight_sum;
+} hps_v_coeff_tab [] = {
+ {0x0100, 2}, {0x0102, 4}, {0x0300, 4}, {0x0106, 8}, {0x0502, 8},
+ {0x0708, 8}, {0x0F00, 8}, {0x011E, 16}, {0x110E, 16}, {0x1926, 16},
+ {0x3906, 16}, {0x3D42, 16}, {0x7D02, 16}, {0x7F80, 16}, {0xFF00, 16},
+ {0x01FE, 32}, {0x01FE, 32}, {0x817E, 32}, {0x817E, 32}, {0xC13E, 32},
+ {0xC13E, 32}, {0xE11E, 32}, {0xE11E, 32}, {0xF10E, 32}, {0xF10E, 32},
+ {0xF906, 32}, {0xF906, 32}, {0xFD02, 32}, {0xFD02, 32}, {0xFF00, 32},
+ {0xFF00, 32}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x817E, 64},
+ {0x817E, 64}, {0xC13E, 64}, {0xC13E, 64}, {0xE11E, 64}, {0xE11E, 64},
+ {0xF10E, 64}, {0xF10E, 64}, {0xF906, 64}, {0xF906, 64}, {0xFD02, 64},
+ {0xFD02, 64}, {0xFF00, 64}, {0xFF00, 64}, {0x01FE, 128}
+};
+
+/* table of attenuation values for vertical scaling */
+static u16 v_attenuation[] = { 2, 4, 8, 16, 32, 64, 128, 256, 0};
+
+/* calculate vertical scale registers */
+static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field field,
+ int in_y, int out_y, u32* hps_v_scale, u32* hps_v_gain)
+{
+ int lpi = 0;
+
+ /* vertical scaling */
+ u32 yacm = 0, ysci = 0, yacl = 0, ypo = 0, ype = 0;
+ /* vertical scale & gain */
+ u32 dcgy = 0, cya_cyb = 0;
+
+ /* helper variables */
+ u32 v_atten = 0, i = 0;
+
+ /* error, if vertical zooming */
+ if ( in_y < out_y ) {
+ return -EINVAL;
+ }
+
+ /* linear phase interpolation may be used
+ if scaling is between 1 and 1/2 (both fields used)
+ or scaling is between 1/2 and 1/4 (if only one field is used) */
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ if( 2*out_y >= in_y) {
+ lpi = 1;
+ }
+ } else if (field == V4L2_FIELD_TOP
+ || field == V4L2_FIELD_ALTERNATE
+ || field == V4L2_FIELD_BOTTOM) {
+ if( 4*out_y >= in_y ) {
+ lpi = 1;
+ }
+ out_y *= 2;
+ }
+ if( 0 != lpi ) {
+
+ yacm = 0;
+ yacl = 0;
+ cya_cyb = 0x00ff;
+
+ /* calculate scaling increment */
+ if ( in_y > out_y )
+ ysci = ((1024 * in_y) / (out_y + 1)) - 1024;
+ else
+ ysci = 0;
+
+ dcgy = 0;
+
+ /* calculate ype and ypo */
+ ype = ysci / 16;
+ ypo = ype + (ysci / 64);
+
+ } else {
+ yacm = 1;
+
+ /* calculate scaling increment */
+ ysci = (((10 * 1024 * (in_y - out_y - 1)) / in_y) + 9) / 10;
+
+ /* calculate ype and ypo */
+ ypo = ype = ((ysci + 15) / 16);
+
+ /* the sequence length interval (yacl) has to be set according
+ to the prescale value, e.g. [n .. 1/2) : 0
+ [1/2 .. 1/3) : 1
+ [1/3 .. 1/4) : 2
+ ... */
+ if ( ysci < 512) {
+ yacl = 0;
+ } else {
+ yacl = ( ysci / (1024 - ysci) );
+ }
+
+ /* get filter coefficients for cya, cyb from table hps_v_coeff_tab */
+ cya_cyb = hps_v_coeff_tab[min(yacl, 63)].hps_coeff;
+
+ /* get best match in the table of attenuations for vertical scaling */
+ v_atten = hps_v_coeff_tab[min(yacl, 63)].weight_sum;
+
+ for (i = 0; v_attenuation[i] != 0; i++) {
+ if (v_attenuation[i] >= v_atten)
+ break;
+ }
+
+ dcgy = i;
+ }
+
+ /* ypo and ype swapped in spec ? */
+ *hps_v_scale |= (yacm << 31) | (ysci << 21) | (yacl << 15) | (ypo << 8 ) | (ype << 1);
+
+ *hps_v_gain &= ~(MASK_W0|MASK_B2);
+ *hps_v_gain |= (dcgy << 16) | (cya_cyb << 0);
+
+ return 0;
+}
+
+/* simple bubble-sort algorithm with duplicate elimination */
+static void saa7146_set_window(struct saa7146_dev *dev, int width, int height, enum v4l2_field field)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ int source = vv->current_hps_source;
+ int sync = vv->current_hps_sync;
+
+ u32 hps_v_scale = 0, hps_v_gain = 0, hps_ctrl = 0, hps_h_prescale = 0, hps_h_scale = 0;
+
+ /* set vertical scale */
+ hps_v_scale = 0; /* all bits get set by the function-call */
+ hps_v_gain = 0; /* fixme: saa7146_read(dev, HPS_V_GAIN);*/
+ calculate_v_scale_registers(dev, field, vv->standard->v_field*2, height, &hps_v_scale, &hps_v_gain);
+
+ /* set horizontal scale */
+ hps_ctrl = 0;
+ hps_h_prescale = 0; /* all bits get set in the function */
+ hps_h_scale = 0;
+ calculate_h_scale_registers(dev, vv->standard->h_pixels, width, vv->hflip, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale);
+
+ /* set hyo and hxo */
+ calculate_hxo_and_hyo(vv, &hps_h_scale, &hps_ctrl);
+ calculate_hps_source_and_sync(dev, source, sync, &hps_ctrl);
+
+ /* write out new register contents */
+ saa7146_write(dev, HPS_V_SCALE, hps_v_scale);
+ saa7146_write(dev, HPS_V_GAIN, hps_v_gain);
+ saa7146_write(dev, HPS_CTRL, hps_ctrl);
+ saa7146_write(dev, HPS_H_PRESCALE,hps_h_prescale);
+ saa7146_write(dev, HPS_H_SCALE, hps_h_scale);
+
+ /* upload shadow-ram registers */
+ saa7146_write(dev, MC2, (MASK_05 | MASK_06 | MASK_21 | MASK_22) );
+}
+
+static void saa7146_set_output_format(struct saa7146_dev *dev, unsigned long palette)
+{
+ u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+
+ /* call helper function */
+ calculate_output_format_register(dev,palette,&clip_format);
+
+ /* update the hps registers */
+ saa7146_write(dev, CLIP_FORMAT_CTRL, clip_format);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+}
+
+/* select input-source */
+void saa7146_set_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ u32 hps_ctrl = 0;
+
+ /* read old state */
+ hps_ctrl = saa7146_read(dev, HPS_CTRL);
+
+ hps_ctrl &= ~( MASK_31 | MASK_30 | MASK_28 );
+ hps_ctrl |= (source << 30) | (sync << 28);
+
+ /* write back & upload register */
+ saa7146_write(dev, HPS_CTRL, hps_ctrl);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+
+ vv->current_hps_source = source;
+ vv->current_hps_sync = sync;
+}
+EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync);
+
+void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma)
+{
+ int where = 0;
+
+ if( which < 1 || which > 3) {
+ return;
+ }
+
+ /* calculate starting address */
+ where = (which-1)*0x18;
+
+ saa7146_write(dev, where, vdma->base_odd);
+ saa7146_write(dev, where+0x04, vdma->base_even);
+ saa7146_write(dev, where+0x08, vdma->prot_addr);
+ saa7146_write(dev, where+0x0c, vdma->pitch);
+ saa7146_write(dev, where+0x10, vdma->base_page);
+ saa7146_write(dev, where+0x14, vdma->num_line_byte);
+
+ /* upload */
+ saa7146_write(dev, MC2, (MASK_02<<(which-1))|(MASK_18<<(which-1)));
+/*
+ printk("vdma%d.base_even: 0x%08x\n", which,vdma->base_even);
+ printk("vdma%d.base_odd: 0x%08x\n", which,vdma->base_odd);
+ printk("vdma%d.prot_addr: 0x%08x\n", which,vdma->prot_addr);
+ printk("vdma%d.base_page: 0x%08x\n", which,vdma->base_page);
+ printk("vdma%d.pitch: 0x%08x\n", which,vdma->pitch);
+ printk("vdma%d.num_line_byte: 0x%08x\n", which,vdma->num_line_byte);
+*/
+}
+
+static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa7146_buf *buf)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct v4l2_pix_format *pix = &vv->video_fmt;
+ struct saa7146_video_dma vdma1;
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pix->pixelformat);
+
+ int width = pix->width;
+ int height = pix->height;
+ int bytesperline = pix->bytesperline;
+ enum v4l2_field field = pix->field;
+
+ int depth = sfmt->depth;
+
+ DEB_CAP("[size=%dx%d,fields=%s]\n",
+ width, height, v4l2_field_names[field]);
+
+ if( bytesperline != 0) {
+ vdma1.pitch = bytesperline*2;
+ } else {
+ vdma1.pitch = (width*depth*2)/8;
+ }
+ vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels);
+ vdma1.base_page = buf->pt[0].dma | ME1 | sfmt->swap;
+
+ if( 0 != vv->vflip ) {
+ vdma1.prot_addr = buf->pt[0].offset;
+ vdma1.base_even = buf->pt[0].offset+(vdma1.pitch/2)*height;
+ vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2);
+ } else {
+ vdma1.base_even = buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2);
+ vdma1.prot_addr = buf->pt[0].offset+(vdma1.pitch/2)*height;
+ }
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ } else if (field == V4L2_FIELD_ALTERNATE) {
+ /* fixme */
+ if ( vv->last_field == V4L2_FIELD_TOP ) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ }
+ } else if (field == V4L2_FIELD_TOP) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ }
+
+ if( 0 != vv->vflip ) {
+ vdma1.pitch *= -1;
+ }
+
+ saa7146_write_out_dma(dev, 1, &vdma1);
+ return 0;
+}
+
+static int calc_planar_422(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3)
+{
+ struct v4l2_pix_format *pix = &vv->video_fmt;
+ int height = pix->height;
+ int width = pix->width;
+
+ vdma2->pitch = width;
+ vdma3->pitch = width;
+
+ /* fixme: look at bytesperline! */
+
+ if( 0 != vv->vflip ) {
+ vdma2->prot_addr = buf->pt[1].offset;
+ vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[1].offset;
+ vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2);
+
+ vdma3->prot_addr = buf->pt[2].offset;
+ vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[2].offset;
+ vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2);
+ } else {
+ vdma3->base_even = buf->pt[2].offset;
+ vdma3->base_odd = vdma3->base_even + (vdma3->pitch/2);
+ vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset;
+
+ vdma2->base_even = buf->pt[1].offset;
+ vdma2->base_odd = vdma2->base_even + (vdma2->pitch/2);
+ vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset;
+ }
+
+ return 0;
+}
+
+static int calc_planar_420(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3)
+{
+ struct v4l2_pix_format *pix = &vv->video_fmt;
+ int height = pix->height;
+ int width = pix->width;
+
+ vdma2->pitch = width/2;
+ vdma3->pitch = width/2;
+
+ if( 0 != vv->vflip ) {
+ vdma2->prot_addr = buf->pt[2].offset;
+ vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[2].offset;
+ vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2);
+
+ vdma3->prot_addr = buf->pt[1].offset;
+ vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[1].offset;
+ vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2);
+
+ } else {
+ vdma3->base_even = buf->pt[2].offset;
+ vdma3->base_odd = vdma3->base_even + (vdma3->pitch);
+ vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset;
+
+ vdma2->base_even = buf->pt[1].offset;
+ vdma2->base_odd = vdma2->base_even + (vdma2->pitch);
+ vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset;
+ }
+ return 0;
+}
+
+static int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa7146_buf *buf)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct v4l2_pix_format *pix = &vv->video_fmt;
+ struct saa7146_video_dma vdma1;
+ struct saa7146_video_dma vdma2;
+ struct saa7146_video_dma vdma3;
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pix->pixelformat);
+
+ int width = pix->width;
+ int height = pix->height;
+ enum v4l2_field field = pix->field;
+
+ if (WARN_ON(!buf->pt[0].dma) ||
+ WARN_ON(!buf->pt[1].dma) ||
+ WARN_ON(!buf->pt[2].dma))
+ return -1;
+
+ DEB_CAP("[size=%dx%d,fields=%s]\n",
+ width, height, v4l2_field_names[field]);
+
+ /* fixme: look at bytesperline! */
+
+ /* fixme: what happens for user space buffers here?. The offsets are
+ most likely wrong, this version here only works for page-aligned
+ buffers, modifications to the pagetable-functions are necessary...*/
+
+ vdma1.pitch = width*2;
+ vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels);
+ vdma1.base_page = buf->pt[0].dma | ME1;
+
+ if( 0 != vv->vflip ) {
+ vdma1.prot_addr = buf->pt[0].offset;
+ vdma1.base_even = ((vdma1.pitch/2)*height)+buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2);
+ } else {
+ vdma1.base_even = buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2);
+ vdma1.prot_addr = (vdma1.pitch/2)*height+buf->pt[0].offset;
+ }
+
+ vdma2.num_line_byte = 0; /* unused */
+ vdma2.base_page = buf->pt[1].dma | ME1;
+
+ vdma3.num_line_byte = 0; /* unused */
+ vdma3.base_page = buf->pt[2].dma | ME1;
+
+ switch( sfmt->depth ) {
+ case 12: {
+ calc_planar_420(vv,buf,&vdma2,&vdma3);
+ break;
+ }
+ case 16: {
+ calc_planar_422(vv,buf,&vdma2,&vdma3);
+ break;
+ }
+ default: {
+ return -1;
+ }
+ }
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ } else if (field == V4L2_FIELD_ALTERNATE) {
+ /* fixme */
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ vdma2.base_odd = vdma2.prot_addr;
+ vdma2.pitch /= 2;
+ vdma3.base_odd = vdma3.prot_addr;
+ vdma3.pitch /= 2;
+ } else if (field == V4L2_FIELD_TOP) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ vdma2.base_odd = vdma2.prot_addr;
+ vdma2.pitch /= 2;
+ vdma3.base_odd = vdma3.prot_addr;
+ vdma3.pitch /= 2;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ vdma2.base_odd = vdma2.base_even;
+ vdma2.base_even = vdma2.prot_addr;
+ vdma2.pitch /= 2;
+ vdma3.base_odd = vdma3.base_even;
+ vdma3.base_even = vdma3.prot_addr;
+ vdma3.pitch /= 2;
+ }
+
+ if( 0 != vv->vflip ) {
+ vdma1.pitch *= -1;
+ vdma2.pitch *= -1;
+ vdma3.pitch *= -1;
+ }
+
+ saa7146_write_out_dma(dev, 1, &vdma1);
+ if( (sfmt->flags & FORMAT_BYTE_SWAP) != 0 ) {
+ saa7146_write_out_dma(dev, 3, &vdma2);
+ saa7146_write_out_dma(dev, 2, &vdma3);
+ } else {
+ saa7146_write_out_dma(dev, 2, &vdma2);
+ saa7146_write_out_dma(dev, 3, &vdma3);
+ }
+ return 0;
+}
+
+static void program_capture_engine(struct saa7146_dev *dev, int planar)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ int count = 0;
+
+ unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
+ unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle only if rps register 0 is not set*/
+ WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | o_wait);
+ WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | e_wait);
+
+ /* set rps register 0 */
+ WRITE_RPS0(CMD_WR_REG | (1 << 8) | (MC2/4));
+ WRITE_RPS0(MASK_27 | MASK_11);
+
+ /* turn on video-dma1 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_06 | MASK_22); /* => mask */
+ WRITE_RPS0(MASK_06 | MASK_22); /* => values */
+ if( 0 != planar ) {
+ /* turn on video-dma2 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_05 | MASK_21); /* => mask */
+ WRITE_RPS0(MASK_05 | MASK_21); /* => values */
+
+ /* turn on video-dma3 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_04 | MASK_20); /* => mask */
+ WRITE_RPS0(MASK_04 | MASK_20); /* => values */
+ }
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle */
+ if ( vv->last_field == V4L2_FIELD_INTERLACED ) {
+ WRITE_RPS0(CMD_PAUSE | o_wait);
+ WRITE_RPS0(CMD_PAUSE | e_wait);
+ } else if ( vv->last_field == V4L2_FIELD_TOP ) {
+ WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09));
+ WRITE_RPS0(CMD_PAUSE | o_wait);
+ } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
+ WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09));
+ WRITE_RPS0(CMD_PAUSE | e_wait);
+ }
+
+ /* turn off video-dma1 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_22 | MASK_06); /* => mask */
+ WRITE_RPS0(MASK_22); /* => values */
+ if( 0 != planar ) {
+ /* turn off video-dma2 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_05 | MASK_21); /* => mask */
+ WRITE_RPS0(MASK_21); /* => values */
+
+ /* turn off video-dma3 */
+ WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS0(MASK_04 | MASK_20); /* => mask */
+ WRITE_RPS0(MASK_20); /* => values */
+ }
+
+ /* generate interrupt */
+ WRITE_RPS0(CMD_INTERRUPT);
+
+ /* stop */
+ WRITE_RPS0(CMD_STOP);
+}
+
+/* disable clipping */
+static void saa7146_disable_clipping(struct saa7146_dev *dev)
+{
+ u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+
+ /* mask out relevant bits (=lower word)*/
+ clip_format &= MASK_W1;
+
+ /* upload clipping-registers*/
+ saa7146_write(dev, CLIP_FORMAT_CTRL, clip_format);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+
+ /* disable video dma2 */
+ saa7146_write(dev, MC1, MASK_21);
+}
+
+void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct v4l2_pix_format *pix = &vv->video_fmt;
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pix->pixelformat);
+ u32 vdma1_prot_addr;
+
+ DEB_CAP("buf:%p, next:%p\n", buf, next);
+
+ vdma1_prot_addr = saa7146_read(dev, PROT_ADDR1);
+ if( 0 == vdma1_prot_addr ) {
+ /* clear out beginning of streaming bit (rps register 0)*/
+ DEB_CAP("forcing sync to new frame\n");
+ saa7146_write(dev, MC2, MASK_27 );
+ }
+
+ saa7146_set_window(dev, pix->width, pix->height, pix->field);
+ saa7146_set_output_format(dev, sfmt->trans);
+ saa7146_disable_clipping(dev);
+
+ if ( vv->last_field == V4L2_FIELD_INTERLACED ) {
+ } else if ( vv->last_field == V4L2_FIELD_TOP ) {
+ vv->last_field = V4L2_FIELD_BOTTOM;
+ } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
+ vv->last_field = V4L2_FIELD_TOP;
+ }
+
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ calculate_video_dma_grab_planar(dev, buf);
+ program_capture_engine(dev,1);
+ } else {
+ calculate_video_dma_grab_packed(dev, buf);
+ program_capture_engine(dev,0);
+ }
+
+/*
+ printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
+ printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
+ printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
+ printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
+ printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
+ printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
+ printk("vdma%d => vptr : 0x%08x\n", 1,saa7146_read(dev,PCI_VDP1));
+*/
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
+
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_12 | MASK_28));
+}
diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146/saa7146_i2c.c
index 22027198129d..df9ebe2a168c 100644
--- a/drivers/media/common/saa7146_i2c.c
+++ b/drivers/media/common/saa7146/saa7146_i2c.c
@@ -1,6 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <media/saa7146_vv.h>
+#include <media/drv-intf/saa7146_vv.h>
static u32 saa7146_i2c_func(struct i2c_adapter *adapter)
{
@@ -53,10 +54,7 @@ static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op)
/* loop through all messages */
for(i = 0; i < num; i++) {
- /* insert the address of the i2c-slave.
- note: we get 7 bit i2c-addresses,
- so we have to perform a translation */
- addr = (m[i].addr*2) + ( (0 != (m[i].flags & I2C_M_RD)) ? 1 : 0);
+ addr = i2c_8bit_addr_from_msg(&m[i]);
h1 = op_count/3; h2 = op_count%3;
op[h1] |= cpu_to_le32( (u8)addr << ((3-h2)*8));
op[h1] |= cpu_to_le32(SAA7146_I2C_START << ((3-h2)*2));
@@ -307,7 +305,7 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
/* prepare the message(s), get number of u32s to transfer */
count = saa7146_i2c_msg_prepare(msgs, num, buffer);
if ( 0 > count ) {
- err = -1;
+ err = -EIO;
goto out;
}
@@ -359,7 +357,7 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
/* if any things had to be read, get the results */
if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) {
DEB_I2C("could not cleanup i2c-message\n");
- err = -1;
+ err = -EIO;
goto out;
}
@@ -395,7 +393,7 @@ static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, in
/* i2c-adapter helper functions */
/* exported algorithm data */
-static struct i2c_algorithm saa7146_algo = {
+static const struct i2c_algorithm saa7146_algo = {
.master_xfer = saa7146_i2c_xfer,
.functionality = saa7146_i2c_func,
};
diff --git a/drivers/media/common/saa7146/saa7146_vbi.c b/drivers/media/common/saa7146/saa7146_vbi.c
new file mode 100644
index 000000000000..1ffcc025d1a4
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_vbi.c
@@ -0,0 +1,445 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <media/drv-intf/saa7146_vv.h>
+
+static int vbi_pixel_to_capture = 720 * 2;
+
+static int vbi_workaround(struct saa7146_dev *dev)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ u32 *cpu;
+ dma_addr_t dma_addr;
+
+ int count = 0;
+ int i;
+
+ DECLARE_WAITQUEUE(wait, current);
+
+ DEB_VBI("dev:%p\n", dev);
+
+ /* once again, a bug in the saa7146: the brs acquisition
+ is buggy and especially the BXO-counter does not work
+ as specified. there is this workaround, but please
+ don't let me explain it. ;-) */
+
+ cpu = dma_alloc_coherent(&dev->pci->dev, 4096, &dma_addr, GFP_KERNEL);
+ if (NULL == cpu)
+ return -ENOMEM;
+
+ /* setup some basic programming, just for the workaround */
+ saa7146_write(dev, BASE_EVEN3, dma_addr);
+ saa7146_write(dev, BASE_ODD3, dma_addr+vbi_pixel_to_capture);
+ saa7146_write(dev, PROT_ADDR3, dma_addr+4096);
+ saa7146_write(dev, PITCH3, vbi_pixel_to_capture);
+ saa7146_write(dev, BASE_PAGE3, 0x0);
+ saa7146_write(dev, NUM_LINE_BYTE3, (2<<16)|((vbi_pixel_to_capture)<<0));
+ saa7146_write(dev, MC2, MASK_04|MASK_20);
+
+ /* load brs-control register */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
+ /* BXO = 1h, BRS to outbound */
+ WRITE_RPS1(0xc000008c);
+ /* wait for vbi_a or vbi_b*/
+ if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
+ DEB_D("...using port b\n");
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_E_FID_B);
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_O_FID_B);
+/*
+ WRITE_RPS1(CMD_PAUSE | MASK_09);
+*/
+ } else {
+ DEB_D("...using port a\n");
+ WRITE_RPS1(CMD_PAUSE | MASK_10);
+ }
+ /* upload brs */
+ WRITE_RPS1(CMD_UPLOAD | MASK_08);
+ /* load brs-control register */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
+ /* BYO = 1, BXO = NQBIL (=1728 for PAL, for NTSC this is 858*2) - NumByte3 (=1440) = 288 */
+ WRITE_RPS1(((1728-(vbi_pixel_to_capture)) << 7) | MASK_19);
+ /* wait for brs_done */
+ WRITE_RPS1(CMD_PAUSE | MASK_08);
+ /* upload brs */
+ WRITE_RPS1(CMD_UPLOAD | MASK_08);
+ /* load video-dma3 NumLines3 and NumBytes3 */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (NUM_LINE_BYTE3/4));
+ /* dev->vbi_count*2 lines, 720 pixel (= 1440 Bytes) */
+ WRITE_RPS1((2 << 16) | (vbi_pixel_to_capture));
+ /* load brs-control register */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
+ /* Set BRS right: note: this is an experimental value for BXO (=> PAL!) */
+ WRITE_RPS1((540 << 7) | (5 << 19)); // 5 == vbi_start
+ /* wait for brs_done */
+ WRITE_RPS1(CMD_PAUSE | MASK_08);
+ /* upload brs and video-dma3*/
+ WRITE_RPS1(CMD_UPLOAD | MASK_08 | MASK_04);
+ /* load mc2 register: enable dma3 */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC1/4));
+ WRITE_RPS1(MASK_20 | MASK_04);
+ /* generate interrupt */
+ WRITE_RPS1(CMD_INTERRUPT);
+ /* stop rps1 */
+ WRITE_RPS1(CMD_STOP);
+
+ /* we have to do the workaround twice to be sure that
+ everything is ok */
+ for(i = 0; i < 2; i++) {
+
+ /* indicate to the irq handler that we do the workaround */
+ saa7146_write(dev, MC2, MASK_31|MASK_15);
+
+ saa7146_write(dev, NUM_LINE_BYTE3, (1<<16)|(2<<0));
+ saa7146_write(dev, MC2, MASK_04|MASK_20);
+
+ /* enable rps1 irqs */
+ SAA7146_IER_ENABLE(dev,MASK_28);
+
+ /* prepare to wait to be woken up by the irq-handler */
+ add_wait_queue(&vv->vbi_wq, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* start rps1 to enable workaround */
+ saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+ saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+
+ schedule();
+
+ DEB_VBI("brs bug workaround %d/1\n", i);
+
+ remove_wait_queue(&vv->vbi_wq, &wait);
+ __set_current_state(TASK_RUNNING);
+
+ /* disable rps1 irqs */
+ SAA7146_IER_DISABLE(dev,MASK_28);
+
+ /* stop video-dma3 */
+ saa7146_write(dev, MC1, MASK_20);
+
+ if(signal_pending(current)) {
+
+ DEB_VBI("aborted (rps:0x%08x)\n",
+ saa7146_read(dev, RPS_ADDR1));
+
+ /* stop rps1 for sure */
+ saa7146_write(dev, MC1, MASK_29);
+
+ dma_free_coherent(&dev->pci->dev, 4096, cpu, dma_addr);
+ return -EINTR;
+ }
+ }
+
+ dma_free_coherent(&dev->pci->dev, 4096, cpu, dma_addr);
+ return 0;
+}
+
+static void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ struct saa7146_video_dma vdma3;
+
+ int count = 0;
+ unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
+ unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
+
+/*
+ vdma3.base_even = 0xc8000000+2560*70;
+ vdma3.base_odd = 0xc8000000;
+ vdma3.prot_addr = 0xc8000000+2560*164;
+ vdma3.pitch = 2560;
+ vdma3.base_page = 0;
+ vdma3.num_line_byte = (64<<16)|((vbi_pixel_to_capture)<<0); // set above!
+*/
+ vdma3.base_even = buf->pt[2].offset;
+ vdma3.base_odd = buf->pt[2].offset + 16 * vbi_pixel_to_capture;
+ vdma3.prot_addr = buf->pt[2].offset + 16 * 2 * vbi_pixel_to_capture;
+ vdma3.pitch = vbi_pixel_to_capture;
+ vdma3.base_page = buf->pt[2].dma | ME1;
+ vdma3.num_line_byte = (16 << 16) | vbi_pixel_to_capture;
+
+ saa7146_write_out_dma(dev, 3, &vdma3);
+
+ /* write beginning of rps-program */
+ count = 0;
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle only if bit 1 is not set */
+
+ /* we don't wait here for the first field anymore. this is different from the video
+ capture and might cause that the first buffer is only half filled (with only
+ one field). but since this is some sort of streaming data, this is not that negative.
+ but by doing this, we can use the whole engine from videobuf-dma-sg.c... */
+
+/*
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | e_wait);
+ WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | o_wait);
+*/
+ /* set bit 1 */
+ WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC2/4));
+ WRITE_RPS1(MASK_28 | MASK_12);
+
+ /* turn on video-dma3 */
+ WRITE_RPS1(CMD_WR_REG_MASK | (MC1/4));
+ WRITE_RPS1(MASK_04 | MASK_20); /* => mask */
+ WRITE_RPS1(MASK_04 | MASK_20); /* => values */
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle */
+ WRITE_RPS1(CMD_PAUSE | o_wait);
+ WRITE_RPS1(CMD_PAUSE | e_wait);
+
+ /* generate interrupt */
+ WRITE_RPS1(CMD_INTERRUPT);
+
+ /* stop */
+ WRITE_RPS1(CMD_STOP);
+
+ /* enable rps1 irqs */
+ SAA7146_IER_ENABLE(dev, MASK_28);
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+}
+
+static int buffer_activate(struct saa7146_dev *dev,
+ struct saa7146_buf *buf,
+ struct saa7146_buf *next)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next);
+ saa7146_set_vbi_capture(dev,buf,next);
+
+ mod_timer(&vv->vbi_dmaq.timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ unsigned int size = 16 * 2 * vbi_pixel_to_capture;
+
+ if (*num_planes)
+ return sizes[0] < size ? -EINVAL : 0;
+ *num_planes = 1;
+ sizes[0] = size;
+
+ return 0;
+}
+
+static void buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct saa7146_dev *dev = vb2_get_drv_priv(vq);
+ struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->slock, flags);
+
+ saa7146_buffer_queue(dev, &dev->vv_data->vbi_dmaq, buf);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static int buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb);
+ struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0);
+ struct scatterlist *list = sgt->sgl;
+ int length = sgt->nents;
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct saa7146_dev *dev = vb2_get_drv_priv(vq);
+ int ret;
+
+ buf->activate = buffer_activate;
+
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
+
+ ret = saa7146_pgtable_build_single(dev->pci, &buf->pt[2],
+ list, length);
+ if (ret)
+ saa7146_pgtable_free(dev->pci, &buf->pt[2]);
+ return ret;
+}
+
+static int buf_prepare(struct vb2_buffer *vb)
+{
+ unsigned int size = 16 * 2 * vbi_pixel_to_capture;
+
+ if (vb2_plane_size(vb, 0) < size)
+ return -EINVAL;
+ vb2_set_plane_payload(vb, 0, size);
+ return 0;
+}
+
+static void buf_cleanup(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb);
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct saa7146_dev *dev = vb2_get_drv_priv(vq);
+
+ saa7146_pgtable_free(dev->pci, &buf->pt[2]);
+}
+
+static void return_buffers(struct vb2_queue *q, int state)
+{
+ struct saa7146_dev *dev = vb2_get_drv_priv(q);
+ struct saa7146_dmaqueue *dq = &dev->vv_data->vbi_dmaq;
+ struct saa7146_buf *buf;
+
+ if (dq->curr) {
+ buf = dq->curr;
+ dq->curr = NULL;
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+ while (!list_empty(&dq->queue)) {
+ buf = list_entry(dq->queue.next, struct saa7146_buf, list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+}
+
+static void vbi_stop(struct saa7146_dev *dev)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ unsigned long flags;
+ DEB_VBI("dev:%p\n", dev);
+
+ spin_lock_irqsave(&dev->slock,flags);
+
+ /* disable rps1 */
+ saa7146_write(dev, MC1, MASK_29);
+
+ /* disable rps1 irqs */
+ SAA7146_IER_DISABLE(dev, MASK_28);
+
+ /* shut down dma 3 transfers */
+ saa7146_write(dev, MC1, MASK_20);
+
+ timer_delete(&vv->vbi_dmaq.timeout);
+ timer_delete(&vv->vbi_read_timeout);
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static void vbi_read_timeout(struct timer_list *t)
+{
+ struct saa7146_vv *vv = timer_container_of(vv, t, vbi_read_timeout);
+ struct saa7146_dev *dev = vv->vbi_dmaq.dev;
+
+ DEB_VBI("dev:%p\n", dev);
+
+ vbi_stop(dev);
+}
+
+static int vbi_begin(struct saa7146_dev *dev)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
+ int ret = 0;
+
+ DEB_VBI("dev:%p\n", dev);
+
+ ret = saa7146_res_get(dev, RESOURCE_DMA3_BRS);
+ if (0 == ret) {
+ DEB_S("cannot get vbi RESOURCE_DMA3_BRS resource\n");
+ return -EBUSY;
+ }
+
+ /* adjust arbitrition control for video dma 3 */
+ arbtr_ctrl &= ~0x1f0000;
+ arbtr_ctrl |= 0x1d0000;
+ saa7146_write(dev, PCI_BT_V1, arbtr_ctrl);
+ saa7146_write(dev, MC2, (MASK_04|MASK_20));
+
+ vv->vbi_read_timeout.function = vbi_read_timeout;
+
+ /* initialize the brs */
+ if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
+ saa7146_write(dev, BRS_CTRL, MASK_30|MASK_29 | (7 << 19));
+ } else {
+ saa7146_write(dev, BRS_CTRL, 0x00000001);
+
+ if (0 != (ret = vbi_workaround(dev))) {
+ DEB_VBI("vbi workaround failed!\n");
+ /* return ret;*/
+ }
+ }
+
+ /* upload brs register */
+ saa7146_write(dev, MC2, (MASK_08|MASK_24));
+ return 0;
+}
+
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct saa7146_dev *dev = vb2_get_drv_priv(q);
+ int ret;
+
+ if (!vb2_is_streaming(&dev->vv_data->vbi_dmaq.q))
+ dev->vv_data->seqnr = 0;
+ ret = vbi_begin(dev);
+ if (ret)
+ return_buffers(q, VB2_BUF_STATE_QUEUED);
+ return ret;
+}
+
+static void stop_streaming(struct vb2_queue *q)
+{
+ struct saa7146_dev *dev = vb2_get_drv_priv(q);
+
+ vbi_stop(dev);
+ return_buffers(q, VB2_BUF_STATE_ERROR);
+ saa7146_res_free(dev, RESOURCE_DMA3_BRS);
+}
+
+const struct vb2_ops vbi_qops = {
+ .queue_setup = queue_setup,
+ .buf_queue = buf_queue,
+ .buf_init = buf_init,
+ .buf_prepare = buf_prepare,
+ .buf_cleanup = buf_cleanup,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+};
+
+/* ------------------------------------------------------------------ */
+
+static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
+{
+ DEB_VBI("dev:%p\n", dev);
+
+ INIT_LIST_HEAD(&vv->vbi_dmaq.queue);
+
+ timer_setup(&vv->vbi_dmaq.timeout, saa7146_buffer_timeout, 0);
+ vv->vbi_dmaq.dev = dev;
+
+ init_waitqueue_head(&vv->vbi_wq);
+}
+
+static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ spin_lock(&dev->slock);
+
+ if (vv->vbi_dmaq.curr) {
+ DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr);
+ saa7146_buffer_finish(dev, &vv->vbi_dmaq, VB2_BUF_STATE_DONE);
+ } else {
+ DEB_VBI("dev:%p\n", dev);
+ }
+ saa7146_buffer_next(dev, &vv->vbi_dmaq, 1);
+
+ spin_unlock(&dev->slock);
+}
+
+const struct saa7146_use_ops saa7146_vbi_uops = {
+ .init = vbi_init,
+ .irq_done = vbi_irq_done,
+};
diff --git a/drivers/media/common/saa7146/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c
new file mode 100644
index 000000000000..733e18001d0d
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_video.c
@@ -0,0 +1,723 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <media/drv-intf/saa7146_vv.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+/* format descriptions for capture and preview */
+static struct saa7146_format formats[] = {
+ {
+ .pixelformat = V4L2_PIX_FMT_RGB332,
+ .trans = RGB08_COMPOSED,
+ .depth = 8,
+ .flags = 0,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ .trans = RGB16_COMPOSED,
+ .depth = 16,
+ .flags = 0,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_BGR24,
+ .trans = RGB24_COMPOSED,
+ .depth = 24,
+ .flags = 0,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_BGR32,
+ .trans = RGB32_COMPOSED,
+ .depth = 32,
+ .flags = 0,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .trans = RGB32_COMPOSED,
+ .depth = 32,
+ .flags = 0,
+ .swap = 0x2,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ .trans = Y8,
+ .depth = 8,
+ .flags = 0,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_YUV422P,
+ .trans = YUV422_DECOMPOSED,
+ .depth = 16,
+ .flags = FORMAT_IS_PLANAR,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_YVU420,
+ .trans = YUV420_DECOMPOSED,
+ .depth = 12,
+ .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_YUV420,
+ .trans = YUV420_DECOMPOSED,
+ .depth = 12,
+ .flags = FORMAT_IS_PLANAR,
+ }, {
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .trans = YUV422_COMPOSED,
+ .depth = 16,
+ .flags = 0,
+ }
+};
+
+/* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps.
+ due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped
+ (like V4L2_PIX_FMT_YUYV) ... 8-( */
+
+struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ if (formats[i].pixelformat == fourcc) {
+ return formats+i;
+ }
+ }
+
+ DEB_D("unknown pixelformat:'%4.4s'\n", (char *)&fourcc);
+ return NULL;
+}
+
+/********************************************************************************/
+/* common pagetable functions */
+
+static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct pci_dev *pci = dev->pci;
+ struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0);
+ struct scatterlist *list = sgt->sgl;
+ int length = sgt->nents;
+ struct v4l2_pix_format *pix = &vv->video_fmt;
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pix->pixelformat);
+
+ DEB_EE("dev:%p, buf:%p, sg_len:%d\n", dev, buf, length);
+
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ struct saa7146_pgtable *pt1 = &buf->pt[0];
+ struct saa7146_pgtable *pt2 = &buf->pt[1];
+ struct saa7146_pgtable *pt3 = &buf->pt[2];
+ struct sg_dma_page_iter dma_iter;
+ __le32 *ptr1, *ptr2, *ptr3;
+ __le32 fill;
+
+ int size = pix->width * pix->height;
+ int i, m1, m2, m3, o1, o2;
+
+ switch( sfmt->depth ) {
+ case 12: {
+ /* create some offsets inside the page table */
+ m1 = ((size + PAGE_SIZE) / PAGE_SIZE) - 1;
+ m2 = ((size + (size / 4) + PAGE_SIZE) / PAGE_SIZE) - 1;
+ m3 = ((size + (size / 2) + PAGE_SIZE) / PAGE_SIZE) - 1;
+ o1 = size % PAGE_SIZE;
+ o2 = (size + (size / 4)) % PAGE_SIZE;
+ DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
+ size, m1, m2, m3, o1, o2);
+ break;
+ }
+ case 16: {
+ /* create some offsets inside the page table */
+ m1 = ((size + PAGE_SIZE) / PAGE_SIZE) - 1;
+ m2 = ((size + (size / 2) + PAGE_SIZE) / PAGE_SIZE) - 1;
+ m3 = ((2 * size + PAGE_SIZE) / PAGE_SIZE) - 1;
+ o1 = size % PAGE_SIZE;
+ o2 = (size + (size / 2)) % PAGE_SIZE;
+ DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
+ size, m1, m2, m3, o1, o2);
+ break;
+ }
+ default: {
+ return -1;
+ }
+ }
+
+ ptr1 = pt1->cpu;
+ ptr2 = pt2->cpu;
+ ptr3 = pt3->cpu;
+
+ for_each_sg_dma_page(list, &dma_iter, length, 0)
+ *ptr1++ = cpu_to_le32(sg_page_iter_dma_address(&dma_iter) - list->offset);
+
+ /* if we have a user buffer, the first page may not be
+ aligned to a page boundary. */
+ pt1->offset = sgt->sgl->offset;
+ pt2->offset = pt1->offset + o1;
+ pt3->offset = pt1->offset + o2;
+
+ /* create video-dma2 page table */
+ ptr1 = pt1->cpu;
+ for (i = m1; i <= m2; i++, ptr2++)
+ *ptr2 = ptr1[i];
+ fill = *(ptr2 - 1);
+ for (; i < 1024; i++, ptr2++)
+ *ptr2 = fill;
+ /* create video-dma3 page table */
+ ptr1 = pt1->cpu;
+ for (i = m2; i <= m3; i++, ptr3++)
+ *ptr3 = ptr1[i];
+ fill = *(ptr3 - 1);
+ for (; i < 1024; i++, ptr3++)
+ *ptr3 = fill;
+ /* finally: finish up video-dma1 page table */
+ ptr1 = pt1->cpu + m1;
+ fill = pt1->cpu[m1];
+ for (i = m1; i < 1024; i++, ptr1++)
+ *ptr1 = fill;
+ } else {
+ struct saa7146_pgtable *pt = &buf->pt[0];
+
+ return saa7146_pgtable_build_single(pci, pt, list, length);
+ }
+
+ return 0;
+}
+
+
+/********************************************************************************/
+/* file operations */
+
+static int video_begin(struct saa7146_dev *dev)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt = NULL;
+ unsigned int resource;
+ int ret = 0;
+
+ DEB_EE("dev:%p\n", dev);
+
+ fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
+ /* we need to have a valid format set here */
+ if (!fmt)
+ return -EINVAL;
+
+ if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+ resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
+ } else {
+ resource = RESOURCE_DMA1_HPS;
+ }
+
+ ret = saa7146_res_get(dev, resource);
+ if (0 == ret) {
+ DEB_S("cannot get capture resource %d\n", resource);
+ return -EBUSY;
+ }
+
+ /* clear out beginning of streaming bit (rps register 0)*/
+ saa7146_write(dev, MC2, MASK_27 );
+
+ /* enable rps0 irqs */
+ SAA7146_IER_ENABLE(dev, MASK_27);
+
+ return 0;
+}
+
+static void video_end(struct saa7146_dev *dev)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt = NULL;
+ unsigned long flags;
+ unsigned int resource;
+ u32 dmas = 0;
+ DEB_EE("dev:%p\n", dev);
+
+ fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
+ /* we need to have a valid format set here */
+ if (!fmt)
+ return;
+
+ if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+ resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
+ dmas = MASK_22 | MASK_21 | MASK_20;
+ } else {
+ resource = RESOURCE_DMA1_HPS;
+ dmas = MASK_22;
+ }
+ spin_lock_irqsave(&dev->slock,flags);
+
+ /* disable rps0 */
+ saa7146_write(dev, MC1, MASK_28);
+
+ /* disable rps0 irqs */
+ SAA7146_IER_DISABLE(dev, MASK_27);
+
+ /* shut down all used video dma transfers */
+ saa7146_write(dev, MC1, dmas);
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ saa7146_res_free(dev, resource);
+}
+
+static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ struct saa7146_dev *dev = video_drvdata(file);
+
+ strscpy((char *)cap->driver, "saa7146 v4l2", sizeof(cap->driver));
+ strscpy((char *)cap->card, dev->ext->name, sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
+ V4L2_CAP_DEVICE_CAPS;
+ cap->capabilities |= dev->ext_vv_data->capabilities;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ if (f->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+ f->pixelformat = formats[f->index].pixelformat;
+ return 0;
+}
+
+int saa7146_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct saa7146_dev *dev = container_of(ctrl->handler,
+ struct saa7146_dev, ctrl_handler);
+ struct saa7146_vv *vv = dev->vv_data;
+ u32 val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0x00ffffff;
+ val |= (ctrl->val << 24);
+ saa7146_write(dev, BCS_CTRL, val);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0xff00ffff;
+ val |= (ctrl->val << 16);
+ saa7146_write(dev, BCS_CTRL, val);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06);
+ break;
+
+ case V4L2_CID_SATURATION:
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0xffffff00;
+ val |= (ctrl->val << 0);
+ saa7146_write(dev, BCS_CTRL, val);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06);
+ break;
+
+ case V4L2_CID_HFLIP:
+ /* fixme: we can support changing VFLIP and HFLIP here... */
+ if (vb2_is_busy(&vv->video_dmaq.q))
+ return -EBUSY;
+ vv->hflip = ctrl->val;
+ break;
+
+ case V4L2_CID_VFLIP:
+ if (vb2_is_busy(&vv->video_dmaq.q))
+ return -EBUSY;
+ vv->vflip = ctrl->val;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ struct saa7146_dev *dev = video_drvdata(file);
+ struct saa7146_vv *vv = dev->vv_data;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ parm->parm.capture.readbuffers = 1;
+ v4l2_video_std_frame_period(vv->standard->id,
+ &parm->parm.capture.timeperframe);
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = video_drvdata(file);
+ struct saa7146_vv *vv = dev->vv_data;
+
+ f->fmt.pix = vv->video_fmt;
+ return 0;
+}
+
+static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = video_drvdata(file);
+ struct saa7146_vv *vv = dev->vv_data;
+
+ f->fmt.vbi = vv->vbi_fmt;
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = video_drvdata(file);
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt;
+ enum v4l2_field field;
+ int maxw, maxh;
+ int calc_bpl;
+
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
+
+ fmt = saa7146_format_by_fourcc(dev, f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = vv->standard->h_max_out;
+ maxh = vv->standard->v_max_out;
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh / 2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+ switch (field) {
+ case V4L2_FIELD_ALTERNATE:
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ default:
+ field = V4L2_FIELD_INTERLACED;
+ break;
+ }
+
+ f->fmt.pix.field = field;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ if (f->fmt.pix.width < 48)
+ f->fmt.pix.width = 48;
+ if (f->fmt.pix.height < 32)
+ f->fmt.pix.height = 32;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+
+ calc_bpl = (f->fmt.pix.width * fmt->depth) / 8;
+
+ if (f->fmt.pix.bytesperline < calc_bpl)
+ f->fmt.pix.bytesperline = calc_bpl;
+
+ if (f->fmt.pix.bytesperline > (2 * PAGE_SIZE * fmt->depth) / 8) /* arbitrary constraint */
+ f->fmt.pix.bytesperline = calc_bpl;
+
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+ DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",
+ f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = video_drvdata(file);
+ struct saa7146_vv *vv = dev->vv_data;
+ int err;
+
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p\n", dev);
+ if (vb2_is_busy(&vv->video_dmaq.q)) {
+ DEB_EE("streaming capture is active\n");
+ return -EBUSY;
+ }
+ err = vidioc_try_fmt_vid_cap(file, fh, f);
+ if (0 != err)
+ return err;
+ switch (f->fmt.pix.field) {
+ case V4L2_FIELD_ALTERNATE:
+ vv->last_field = V4L2_FIELD_TOP;
+ break;
+ default:
+ vv->last_field = V4L2_FIELD_INTERLACED;
+ break;
+ }
+ vv->video_fmt = f->fmt.pix;
+ DEB_EE("set to pixelformat '%4.4s'\n",
+ (char *)&vv->video_fmt.pixelformat);
+ return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct saa7146_dev *dev = video_drvdata(file);
+ struct saa7146_vv *vv = dev->vv_data;
+
+ *norm = vv->standard->id;
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id)
+{
+ struct saa7146_dev *dev = video_drvdata(file);
+ struct saa7146_vv *vv = dev->vv_data;
+ int found = 0;
+ int i;
+
+ DEB_EE("VIDIOC_S_STD\n");
+
+ for (i = 0; i < dev->ext_vv_data->num_stds; i++)
+ if (id & dev->ext_vv_data->stds[i].id)
+ break;
+
+ if (i != dev->ext_vv_data->num_stds &&
+ vv->standard == &dev->ext_vv_data->stds[i])
+ return 0;
+
+ if (vb2_is_busy(&vv->video_dmaq.q) || vb2_is_busy(&vv->vbi_dmaq.q)) {
+ DEB_D("cannot change video standard while streaming capture is active\n");
+ return -EBUSY;
+ }
+
+ if (i != dev->ext_vv_data->num_stds) {
+ vv->standard = &dev->ext_vv_data->stds[i];
+ if (NULL != dev->ext_vv_data->std_callback)
+ dev->ext_vv_data->std_callback(dev, vv->standard);
+ found = 1;
+ }
+
+ if (!found) {
+ DEB_EE("VIDIOC_S_STD: standard not found\n");
+ return -EINVAL;
+ }
+
+ DEB_EE("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name);
+ return 0;
+}
+
+const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+ .vidioc_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/*********************************************************************************/
+/* buffer handling functions */
+
+static int buffer_activate (struct saa7146_dev *dev,
+ struct saa7146_buf *buf,
+ struct saa7146_buf *next)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ saa7146_set_capture(dev,buf,next);
+
+ mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf)
+{
+ saa7146_pgtable_free(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_free(dev->pci, &buf->pt[1]);
+ saa7146_pgtable_free(dev->pci, &buf->pt[2]);
+}
+
+static int queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct saa7146_dev *dev = vb2_get_drv_priv(q);
+ unsigned int size = dev->vv_data->video_fmt.sizeimage;
+
+ if (*num_planes)
+ return sizes[0] < size ? -EINVAL : 0;
+ *num_planes = 1;
+ sizes[0] = size;
+
+ return 0;
+}
+
+static void buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct saa7146_dev *dev = vb2_get_drv_priv(vq);
+ struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->slock, flags);
+
+ saa7146_buffer_queue(dev, &dev->vv_data->video_dmaq, buf);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static int buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb);
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct saa7146_dev *dev = vb2_get_drv_priv(vq);
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *sfmt;
+ int ret;
+
+ buf->activate = buffer_activate;
+ sfmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
+
+ if (IS_PLANAR(sfmt->trans)) {
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[1]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
+ } else {
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
+ }
+
+ ret = saa7146_pgtable_build(dev, buf);
+ if (ret)
+ release_all_pagetables(dev, buf);
+ return ret;
+}
+
+static int buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct saa7146_dev *dev = vb2_get_drv_priv(vq);
+ struct saa7146_vv *vv = dev->vv_data;
+ unsigned int size = vv->video_fmt.sizeimage;
+
+ if (vb2_plane_size(vb, 0) < size)
+ return -EINVAL;
+ vb2_set_plane_payload(vb, 0, size);
+ return 0;
+}
+
+static void buf_cleanup(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb);
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct saa7146_dev *dev = vb2_get_drv_priv(vq);
+
+ release_all_pagetables(dev, buf);
+}
+
+static void return_buffers(struct vb2_queue *q, int state)
+{
+ struct saa7146_dev *dev = vb2_get_drv_priv(q);
+ struct saa7146_dmaqueue *dq = &dev->vv_data->video_dmaq;
+ struct saa7146_buf *buf;
+
+ if (dq->curr) {
+ buf = dq->curr;
+ dq->curr = NULL;
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+ while (!list_empty(&dq->queue)) {
+ buf = list_entry(dq->queue.next, struct saa7146_buf, list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+}
+
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct saa7146_dev *dev = vb2_get_drv_priv(q);
+ int ret;
+
+ if (!vb2_is_streaming(&dev->vv_data->video_dmaq.q))
+ dev->vv_data->seqnr = 0;
+ ret = video_begin(dev);
+ if (ret)
+ return_buffers(q, VB2_BUF_STATE_QUEUED);
+ return ret;
+}
+
+static void stop_streaming(struct vb2_queue *q)
+{
+ struct saa7146_dev *dev = vb2_get_drv_priv(q);
+ struct saa7146_dmaqueue *dq = &dev->vv_data->video_dmaq;
+
+ timer_delete(&dq->timeout);
+ video_end(dev);
+ return_buffers(q, VB2_BUF_STATE_ERROR);
+}
+
+const struct vb2_ops video_qops = {
+ .queue_setup = queue_setup,
+ .buf_queue = buf_queue,
+ .buf_init = buf_init,
+ .buf_prepare = buf_prepare,
+ .buf_cleanup = buf_cleanup,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+};
+
+/********************************************************************************/
+/* file operations */
+
+static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
+{
+ INIT_LIST_HEAD(&vv->video_dmaq.queue);
+
+ timer_setup(&vv->video_dmaq.timeout, saa7146_buffer_timeout, 0);
+ vv->video_dmaq.dev = dev;
+
+ /* set some default values */
+ vv->standard = &dev->ext_vv_data->stds[0];
+
+ /* FIXME: what's this? */
+ vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A;
+ vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A;
+}
+
+static void video_irq_done(struct saa7146_dev *dev, unsigned long st)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_dmaqueue *q = &vv->video_dmaq;
+
+ spin_lock(&dev->slock);
+ DEB_CAP("called\n");
+
+ /* only finish the buffer if we have one... */
+ if (q->curr)
+ saa7146_buffer_finish(dev, q, VB2_BUF_STATE_DONE);
+ saa7146_buffer_next(dev,q,0);
+
+ spin_unlock(&dev->slock);
+}
+
+const struct saa7146_use_ops saa7146_video_uops = {
+ .init = video_init,
+ .irq_done = video_irq_done,
+};
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
deleted file mode 100644
index 0cdbd742974a..000000000000
--- a/drivers/media/common/saa7146_fops.c
+++ /dev/null
@@ -1,641 +0,0 @@
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <media/saa7146_vv.h>
-#include <linux/module.h>
-
-/****************************************************************************/
-/* resource management functions, shamelessly stolen from saa7134 driver */
-
-int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- if (fh->resources & bit) {
- DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n",
- bit, vv->resources);
- /* have it already allocated */
- return 1;
- }
-
- /* is it free? */
- if (vv->resources & bit) {
- DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n",
- vv->resources, bit);
- /* no, someone else uses it */
- return 0;
- }
- /* it's free, grab it */
- fh->resources |= bit;
- vv->resources |= bit;
- DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources);
- return 1;
-}
-
-void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- BUG_ON((fh->resources & bits) != bits);
-
- fh->resources &= ~bits;
- vv->resources &= ~bits;
- DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources);
-}
-
-
-/********************************************************************************/
-/* common dma functions */
-
-void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q,
- struct saa7146_buf *buf)
-{
- struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
- DEB_EE("dev:%p, buf:%p\n", dev, buf);
-
- BUG_ON(in_interrupt());
-
- videobuf_waiton(q, &buf->vb, 0, 0);
- videobuf_dma_unmap(q->dev, dma);
- videobuf_dma_free(dma);
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-
-/********************************************************************************/
-/* common buffer functions */
-
-int saa7146_buffer_queue(struct saa7146_dev *dev,
- struct saa7146_dmaqueue *q,
- struct saa7146_buf *buf)
-{
- assert_spin_locked(&dev->slock);
- DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf);
-
- BUG_ON(!q);
-
- if (NULL == q->curr) {
- q->curr = buf;
- DEB_D("immediately activating buffer %p\n", buf);
- buf->activate(dev,buf,NULL);
- } else {
- list_add_tail(&buf->vb.queue,&q->queue);
- buf->vb.state = VIDEOBUF_QUEUED;
- DEB_D("adding buffer %p to queue. (active buffer present)\n",
- buf);
- }
- return 0;
-}
-
-void saa7146_buffer_finish(struct saa7146_dev *dev,
- struct saa7146_dmaqueue *q,
- int state)
-{
- assert_spin_locked(&dev->slock);
- DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state);
- DEB_EE("q->curr:%p\n", q->curr);
-
- BUG_ON(!q->curr);
-
- /* finish current buffer */
- if (NULL == q->curr) {
- DEB_D("aiii. no current buffer\n");
- return;
- }
-
- q->curr->vb.state = state;
- do_gettimeofday(&q->curr->vb.ts);
- wake_up(&q->curr->vb.done);
-
- q->curr = NULL;
-}
-
-void saa7146_buffer_next(struct saa7146_dev *dev,
- struct saa7146_dmaqueue *q, int vbi)
-{
- struct saa7146_buf *buf,*next = NULL;
-
- BUG_ON(!q);
-
- DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi);
-
- assert_spin_locked(&dev->slock);
- if (!list_empty(&q->queue)) {
- /* activate next one from queue */
- buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue);
- list_del(&buf->vb.queue);
- if (!list_empty(&q->queue))
- next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);
- q->curr = buf;
- DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n",
- buf, q->queue.prev, q->queue.next);
- buf->activate(dev,buf,next);
- } else {
- DEB_INT("no next buffer. stopping.\n");
- if( 0 != vbi ) {
- /* turn off video-dma3 */
- saa7146_write(dev,MC1, MASK_20);
- } else {
- /* nothing to do -- just prevent next video-dma1 transfer
- by lowering the protection address */
-
- // fixme: fix this for vflip != 0
-
- saa7146_write(dev, PROT_ADDR1, 0);
- saa7146_write(dev, MC2, (MASK_02|MASK_18));
-
- /* write the address of the rps-program */
- saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
- /* turn on rps */
- saa7146_write(dev, MC1, (MASK_12 | MASK_28));
-
-/*
- printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
- printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
- printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
- printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
- printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
- printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
-*/
- }
- del_timer(&q->timeout);
- }
-}
-
-void saa7146_buffer_timeout(unsigned long data)
-{
- struct saa7146_dmaqueue *q = (struct saa7146_dmaqueue*)data;
- struct saa7146_dev *dev = q->dev;
- unsigned long flags;
-
- DEB_EE("dev:%p, dmaq:%p\n", dev, q);
-
- spin_lock_irqsave(&dev->slock,flags);
- if (q->curr) {
- DEB_D("timeout on %p\n", q->curr);
- saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR);
- }
-
- /* we don't restart the transfer here like other drivers do. when
- a streaming capture is disabled, the timeout function will be
- called for the current buffer. if we activate the next buffer now,
- we mess up our capture logic. if a timeout occurs on another buffer,
- then something is seriously broken before, so no need to buffer the
- next capture IMHO... */
-/*
- saa7146_buffer_next(dev,q);
-*/
- spin_unlock_irqrestore(&dev->slock,flags);
-}
-
-/********************************************************************************/
-/* file operations */
-
-static int fops_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7146_dev *dev = video_drvdata(file);
- struct saa7146_fh *fh = NULL;
- int result = 0;
-
- DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev));
-
- if (mutex_lock_interruptible(&saa7146_devices_lock))
- return -ERESTARTSYS;
-
- DEB_D("using: %p\n", dev);
-
- /* check if an extension is registered */
- if( NULL == dev->ext ) {
- DEB_S("no extension registered for this device\n");
- result = -ENODEV;
- goto out;
- }
-
- /* allocate per open data */
- fh = kzalloc(sizeof(*fh),GFP_KERNEL);
- if (NULL == fh) {
- DEB_S("cannot allocate memory for per open data\n");
- result = -ENOMEM;
- goto out;
- }
-
- v4l2_fh_init(&fh->fh, vdev);
-
- file->private_data = &fh->fh;
- fh->dev = dev;
-
- if (vdev->vfl_type == VFL_TYPE_VBI) {
- DEB_S("initializing vbi...\n");
- if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
- result = saa7146_vbi_uops.open(dev,file);
- if (dev->ext_vv_data->vbi_fops.open)
- dev->ext_vv_data->vbi_fops.open(file);
- } else {
- DEB_S("initializing video...\n");
- result = saa7146_video_uops.open(dev,file);
- }
-
- if (0 != result) {
- goto out;
- }
-
- if( 0 == try_module_get(dev->ext->module)) {
- result = -EINVAL;
- goto out;
- }
-
- result = 0;
- v4l2_fh_add(&fh->fh);
-out:
- if (fh && result != 0) {
- kfree(fh);
- file->private_data = NULL;
- }
- mutex_unlock(&saa7146_devices_lock);
- return result;
-}
-
-static int fops_release(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
-
- DEB_EE("file:%p\n", file);
-
- if (mutex_lock_interruptible(&saa7146_devices_lock))
- return -ERESTARTSYS;
-
- if (vdev->vfl_type == VFL_TYPE_VBI) {
- if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
- saa7146_vbi_uops.release(dev,file);
- if (dev->ext_vv_data->vbi_fops.release)
- dev->ext_vv_data->vbi_fops.release(file);
- } else {
- saa7146_video_uops.release(dev,file);
- }
-
- v4l2_fh_del(&fh->fh);
- v4l2_fh_exit(&fh->fh);
- module_put(dev->ext->module);
- file->private_data = NULL;
- kfree(fh);
-
- mutex_unlock(&saa7146_devices_lock);
-
- return 0;
-}
-
-static int fops_mmap(struct file *file, struct vm_area_struct * vma)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7146_fh *fh = file->private_data;
- struct videobuf_queue *q;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER: {
- DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",
- file, vma);
- q = &fh->video_q;
- break;
- }
- case VFL_TYPE_VBI: {
- DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",
- file, vma);
- if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
- return -ENODEV;
- q = &fh->vbi_q;
- break;
- }
- default:
- BUG();
- return 0;
- }
-
- return videobuf_mmap_mapper(q,vma);
-}
-
-static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7146_fh *fh = file->private_data;
- struct videobuf_buffer *buf = NULL;
- struct videobuf_queue *q;
- unsigned int res = v4l2_ctrl_poll(file, wait);
-
- DEB_EE("file:%p, poll:%p\n", file, wait);
-
- if (vdev->vfl_type == VFL_TYPE_VBI) {
- if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
- return res | POLLOUT | POLLWRNORM;
- if( 0 == fh->vbi_q.streaming )
- return res | videobuf_poll_stream(file, &fh->vbi_q, wait);
- q = &fh->vbi_q;
- } else {
- DEB_D("using video queue\n");
- q = &fh->video_q;
- }
-
- if (!list_empty(&q->stream))
- buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
-
- if (!buf) {
- DEB_D("buf == NULL!\n");
- return res | POLLERR;
- }
-
- poll_wait(file, &buf->done, wait);
- if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {
- DEB_D("poll succeeded!\n");
- return res | POLLIN | POLLRDNORM;
- }
-
- DEB_D("nothing to poll for, buf->state:%d\n", buf->state);
- return res;
-}
-
-static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7146_fh *fh = file->private_data;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
-/*
- DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun",
- file, data, (unsigned long)count);
-*/
- return saa7146_video_uops.read(file,data,count,ppos);
- case VFL_TYPE_VBI:
-/*
- DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n",
- file, data, (unsigned long)count);
-*/
- if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
- return saa7146_vbi_uops.read(file,data,count,ppos);
- return -EINVAL;
- default:
- BUG();
- return 0;
- }
-}
-
-static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7146_fh *fh = file->private_data;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- return -EINVAL;
- case VFL_TYPE_VBI:
- if (fh->dev->ext_vv_data->vbi_fops.write)
- return fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos);
- else
- return -EINVAL;
- default:
- BUG();
- return -EINVAL;
- }
-}
-
-static const struct v4l2_file_operations video_fops =
-{
- .owner = THIS_MODULE,
- .open = fops_open,
- .release = fops_release,
- .read = fops_read,
- .write = fops_write,
- .poll = fops_poll,
- .mmap = fops_mmap,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static void vv_callback(struct saa7146_dev *dev, unsigned long status)
-{
- u32 isr = status;
-
- DEB_INT("dev:%p, isr:0x%08x\n", dev, (u32)status);
-
- if (0 != (isr & (MASK_27))) {
- DEB_INT("irq: RPS0 (0x%08x)\n", isr);
- saa7146_video_uops.irq_done(dev,isr);
- }
-
- if (0 != (isr & (MASK_28))) {
- u32 mc2 = saa7146_read(dev, MC2);
- if( 0 != (mc2 & MASK_15)) {
- DEB_INT("irq: RPS1 vbi workaround (0x%08x)\n", isr);
- wake_up(&dev->vv_data->vbi_wq);
- saa7146_write(dev,MC2, MASK_31);
- return;
- }
- DEB_INT("irq: RPS1 (0x%08x)\n", isr);
- saa7146_vbi_uops.irq_done(dev,isr);
- }
-}
-
-static const struct v4l2_ctrl_ops saa7146_ctrl_ops = {
- .s_ctrl = saa7146_s_ctrl,
-};
-
-int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
-{
- struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
- struct v4l2_pix_format *fmt;
- struct v4l2_vbi_format *vbi;
- struct saa7146_vv *vv;
- int err;
-
- err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);
- if (err)
- return err;
-
- v4l2_ctrl_handler_init(hdl, 6);
- v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
- v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 127, 1, 64);
- v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
- V4L2_CID_SATURATION, 0, 127, 1, 64);
- v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- if (hdl->error) {
- err = hdl->error;
- v4l2_ctrl_handler_free(hdl);
- return err;
- }
- dev->v4l2_dev.ctrl_handler = hdl;
-
- vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
- if (vv == NULL) {
- ERR("out of memory. aborting.\n");
- v4l2_ctrl_handler_free(hdl);
- return -ENOMEM;
- }
- ext_vv->vid_ops = saa7146_video_ioctl_ops;
- ext_vv->vbi_ops = saa7146_vbi_ioctl_ops;
- ext_vv->core_ops = &saa7146_video_ioctl_ops;
-
- DEB_EE("dev:%p\n", dev);
-
- /* set default values for video parts of the saa7146 */
- saa7146_write(dev, BCS_CTRL, 0x80400040);
-
- /* enable video-port pins */
- saa7146_write(dev, MC1, (MASK_10 | MASK_26));
-
- /* save per-device extension data (one extension can
- handle different devices that might need different
- configuration data) */
- dev->ext_vv_data = ext_vv;
-
- vv->d_clipping.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_CLIPPING_MEM, &vv->d_clipping.dma_handle);
- if( NULL == vv->d_clipping.cpu_addr ) {
- ERR("out of memory. aborting.\n");
- kfree(vv);
- v4l2_ctrl_handler_free(hdl);
- return -1;
- }
- memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM);
-
- saa7146_video_uops.init(dev,vv);
- if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
- saa7146_vbi_uops.init(dev,vv);
-
- fmt = &vv->ov_fb.fmt;
- fmt->width = vv->standard->h_max_out;
- fmt->height = vv->standard->v_max_out;
- fmt->pixelformat = V4L2_PIX_FMT_RGB565;
- fmt->bytesperline = 2 * fmt->width;
- fmt->sizeimage = fmt->bytesperline * fmt->height;
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
-
- fmt = &vv->video_fmt;
- fmt->width = 384;
- fmt->height = 288;
- fmt->pixelformat = V4L2_PIX_FMT_BGR24;
- fmt->field = V4L2_FIELD_ANY;
- fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
- fmt->bytesperline = 3 * fmt->width;
- fmt->sizeimage = fmt->bytesperline * fmt->height;
-
- vbi = &vv->vbi_fmt;
- vbi->sampling_rate = 27000000;
- vbi->offset = 248; /* todo */
- vbi->samples_per_line = 720 * 2;
- vbi->sample_format = V4L2_PIX_FMT_GREY;
-
- /* fixme: this only works for PAL */
- vbi->start[0] = 5;
- vbi->count[0] = 16;
- vbi->start[1] = 312;
- vbi->count[1] = 16;
-
- init_timer(&vv->vbi_read_timeout);
-
- vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING;
- vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY;
- dev->vv_data = vv;
- dev->vv_callback = &vv_callback;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7146_vv_init);
-
-int saa7146_vv_release(struct saa7146_dev* dev)
-{
- struct saa7146_vv *vv = dev->vv_data;
-
- DEB_EE("dev:%p\n", dev);
-
- v4l2_device_unregister(&dev->v4l2_dev);
- pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
- v4l2_ctrl_handler_free(&dev->ctrl_handler);
- kfree(vv);
- dev->vv_data = NULL;
- dev->vv_callback = NULL;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7146_vv_release);
-
-int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
- char *name, int type)
-{
- struct video_device *vfd;
- int err;
- int i;
-
- DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type);
-
- // released by vfd->release
- vfd = video_device_alloc();
- if (vfd == NULL)
- return -ENOMEM;
-
- vfd->fops = &video_fops;
- if (type == VFL_TYPE_GRABBER)
- vfd->ioctl_ops = &dev->ext_vv_data->vid_ops;
- else
- vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops;
- vfd->release = video_device_release;
- /* Locking in file operations other than ioctl should be done by
- the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
- vfd->lock = &dev->v4l2_lock;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->tvnorms = 0;
- set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
- for (i = 0; i < dev->ext_vv_data->num_stds; i++)
- vfd->tvnorms |= dev->ext_vv_data->stds[i].id;
- strlcpy(vfd->name, name, sizeof(vfd->name));
- video_set_drvdata(vfd, dev);
-
- err = video_register_device(vfd, type, -1);
- if (err < 0) {
- ERR("cannot register v4l2 device. skipping.\n");
- video_device_release(vfd);
- return err;
- }
-
- pr_info("%s: registered device %s [v4l2]\n",
- dev->name, video_device_node_name(vfd));
-
- *vid = vfd;
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7146_register_device);
-
-int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev)
-{
- DEB_EE("dev:%p\n", dev);
-
- video_unregister_device(*vid);
- *vid = NULL;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7146_unregister_device);
-
-static int __init saa7146_vv_init_module(void)
-{
- return 0;
-}
-
-
-static void __exit saa7146_vv_cleanup_module(void)
-{
-}
-
-module_init(saa7146_vv_init_module);
-module_exit(saa7146_vv_cleanup_module);
-
-MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
-MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/saa7146_hlp.c b/drivers/media/common/saa7146_hlp.c
deleted file mode 100644
index be746d1aee9a..000000000000
--- a/drivers/media/common/saa7146_hlp.c
+++ /dev/null
@@ -1,1048 +0,0 @@
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/export.h>
-#include <media/saa7146_vv.h>
-
-static void calculate_output_format_register(struct saa7146_dev* saa, u32 palette, u32* clip_format)
-{
- /* clear out the necessary bits */
- *clip_format &= 0x0000ffff;
- /* set these bits new */
- *clip_format |= (( ((palette&0xf00)>>8) << 30) | ((palette&0x00f) << 24) | (((palette&0x0f0)>>4) << 16));
-}
-
-static void calculate_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync, u32* hps_ctrl)
-{
- *hps_ctrl &= ~(MASK_30 | MASK_31 | MASK_28);
- *hps_ctrl |= (source << 30) | (sync << 28);
-}
-
-static void calculate_hxo_and_hyo(struct saa7146_vv *vv, u32* hps_h_scale, u32* hps_ctrl)
-{
- int hyo = 0, hxo = 0;
-
- hyo = vv->standard->v_offset;
- hxo = vv->standard->h_offset;
-
- *hps_h_scale &= ~(MASK_B0 | 0xf00);
- *hps_h_scale |= (hxo << 0);
-
- *hps_ctrl &= ~(MASK_W0 | MASK_B2);
- *hps_ctrl |= (hyo << 12);
-}
-
-/* helper functions for the calculation of the horizontal- and vertical
- scaling registers, clip-format-register etc ...
- these functions take pointers to the (most-likely read-out
- original-values) and manipulate them according to the requested
- changes.
-*/
-
-/* hps_coeff used for CXY and CXUV; scale 1/1 -> scale 1/64 */
-static struct {
- u16 hps_coeff;
- u16 weight_sum;
-} hps_h_coeff_tab [] = {
- {0x00, 2}, {0x02, 4}, {0x00, 4}, {0x06, 8}, {0x02, 8},
- {0x08, 8}, {0x00, 8}, {0x1E, 16}, {0x0E, 8}, {0x26, 8},
- {0x06, 8}, {0x42, 8}, {0x02, 8}, {0x80, 8}, {0x00, 8},
- {0xFE, 16}, {0xFE, 8}, {0x7E, 8}, {0x7E, 8}, {0x3E, 8},
- {0x3E, 8}, {0x1E, 8}, {0x1E, 8}, {0x0E, 8}, {0x0E, 8},
- {0x06, 8}, {0x06, 8}, {0x02, 8}, {0x02, 8}, {0x00, 8},
- {0x00, 8}, {0xFE, 16}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
- {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
- {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
- {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0x7E, 8},
- {0x7E, 8}, {0x3E, 8}, {0x3E, 8}, {0x1E, 8}, {0x1E, 8},
- {0x0E, 8}, {0x0E, 8}, {0x06, 8}, {0x06, 8}, {0x02, 8},
- {0x02, 8}, {0x00, 8}, {0x00, 8}, {0xFE, 16}
-};
-
-/* table of attenuation values for horizontal scaling */
-static u8 h_attenuation[] = { 1, 2, 4, 8, 2, 4, 8, 16, 0};
-
-/* calculate horizontal scale registers */
-static int calculate_h_scale_registers(struct saa7146_dev *dev,
- int in_x, int out_x, int flip_lr,
- u32* hps_ctrl, u32* hps_v_gain, u32* hps_h_prescale, u32* hps_h_scale)
-{
- /* horizontal prescaler */
- u32 dcgx = 0, xpsc = 0, xacm = 0, cxy = 0, cxuv = 0;
- /* horizontal scaler */
- u32 xim = 0, xp = 0, xsci =0;
- /* vertical scale & gain */
- u32 pfuv = 0;
-
- /* helper variables */
- u32 h_atten = 0, i = 0;
-
- if ( 0 == out_x ) {
- return -EINVAL;
- }
-
- /* mask out vanity-bit */
- *hps_ctrl &= ~MASK_29;
-
- /* calculate prescale-(xspc)-value: [n .. 1/2) : 1
- [1/2 .. 1/3) : 2
- [1/3 .. 1/4) : 3
- ... */
- if (in_x > out_x) {
- xpsc = in_x / out_x;
- }
- else {
- /* zooming */
- xpsc = 1;
- }
-
- /* if flip_lr-bit is set, number of pixels after
- horizontal prescaling must be < 384 */
- if ( 0 != flip_lr ) {
-
- /* set vanity bit */
- *hps_ctrl |= MASK_29;
-
- while (in_x / xpsc >= 384 )
- xpsc++;
- }
- /* if zooming is wanted, number of pixels after
- horizontal prescaling must be < 768 */
- else {
- while ( in_x / xpsc >= 768 )
- xpsc++;
- }
-
- /* maximum prescale is 64 (p.69) */
- if ( xpsc > 64 )
- xpsc = 64;
-
- /* keep xacm clear*/
- xacm = 0;
-
- /* set horizontal filter parameters (CXY = CXUV) */
- cxy = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].hps_coeff;
- cxuv = cxy;
-
- /* calculate and set horizontal fine scale (xsci) */
-
- /* bypass the horizontal scaler ? */
- if ( (in_x == out_x) && ( 1 == xpsc ) )
- xsci = 0x400;
- else
- xsci = ( (1024 * in_x) / (out_x * xpsc) ) + xpsc;
-
- /* set start phase for horizontal fine scale (xp) to 0 */
- xp = 0;
-
- /* set xim, if we bypass the horizontal scaler */
- if ( 0x400 == xsci )
- xim = 1;
- else
- xim = 0;
-
- /* if the prescaler is bypassed, enable horizontal
- accumulation mode (xacm) and clear dcgx */
- if( 1 == xpsc ) {
- xacm = 1;
- dcgx = 0;
- } else {
- xacm = 0;
- /* get best match in the table of attenuations
- for horizontal scaling */
- h_atten = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].weight_sum;
-
- for (i = 0; h_attenuation[i] != 0; i++) {
- if (h_attenuation[i] >= h_atten)
- break;
- }
-
- dcgx = i;
- }
-
- /* the horizontal scaling increment controls the UV filter
- to reduce the bandwidth to improve the display quality,
- so set it ... */
- if ( xsci == 0x400)
- pfuv = 0x00;
- else if ( xsci < 0x600)
- pfuv = 0x01;
- else if ( xsci < 0x680)
- pfuv = 0x11;
- else if ( xsci < 0x700)
- pfuv = 0x22;
- else
- pfuv = 0x33;
-
-
- *hps_v_gain &= MASK_W0|MASK_B2;
- *hps_v_gain |= (pfuv << 24);
-
- *hps_h_scale &= ~(MASK_W1 | 0xf000);
- *hps_h_scale |= (xim << 31) | (xp << 24) | (xsci << 12);
-
- *hps_h_prescale |= (dcgx << 27) | ((xpsc-1) << 18) | (xacm << 17) | (cxy << 8) | (cxuv << 0);
-
- return 0;
-}
-
-static struct {
- u16 hps_coeff;
- u16 weight_sum;
-} hps_v_coeff_tab [] = {
- {0x0100, 2}, {0x0102, 4}, {0x0300, 4}, {0x0106, 8}, {0x0502, 8},
- {0x0708, 8}, {0x0F00, 8}, {0x011E, 16}, {0x110E, 16}, {0x1926, 16},
- {0x3906, 16}, {0x3D42, 16}, {0x7D02, 16}, {0x7F80, 16}, {0xFF00, 16},
- {0x01FE, 32}, {0x01FE, 32}, {0x817E, 32}, {0x817E, 32}, {0xC13E, 32},
- {0xC13E, 32}, {0xE11E, 32}, {0xE11E, 32}, {0xF10E, 32}, {0xF10E, 32},
- {0xF906, 32}, {0xF906, 32}, {0xFD02, 32}, {0xFD02, 32}, {0xFF00, 32},
- {0xFF00, 32}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
- {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
- {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
- {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x817E, 64},
- {0x817E, 64}, {0xC13E, 64}, {0xC13E, 64}, {0xE11E, 64}, {0xE11E, 64},
- {0xF10E, 64}, {0xF10E, 64}, {0xF906, 64}, {0xF906, 64}, {0xFD02, 64},
- {0xFD02, 64}, {0xFF00, 64}, {0xFF00, 64}, {0x01FE, 128}
-};
-
-/* table of attenuation values for vertical scaling */
-static u16 v_attenuation[] = { 2, 4, 8, 16, 32, 64, 128, 256, 0};
-
-/* calculate vertical scale registers */
-static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field field,
- int in_y, int out_y, u32* hps_v_scale, u32* hps_v_gain)
-{
- int lpi = 0;
-
- /* vertical scaling */
- u32 yacm = 0, ysci = 0, yacl = 0, ypo = 0, ype = 0;
- /* vertical scale & gain */
- u32 dcgy = 0, cya_cyb = 0;
-
- /* helper variables */
- u32 v_atten = 0, i = 0;
-
- /* error, if vertical zooming */
- if ( in_y < out_y ) {
- return -EINVAL;
- }
-
- /* linear phase interpolation may be used
- if scaling is between 1 and 1/2 (both fields used)
- or scaling is between 1/2 and 1/4 (if only one field is used) */
-
- if (V4L2_FIELD_HAS_BOTH(field)) {
- if( 2*out_y >= in_y) {
- lpi = 1;
- }
- } else if (field == V4L2_FIELD_TOP
- || field == V4L2_FIELD_ALTERNATE
- || field == V4L2_FIELD_BOTTOM) {
- if( 4*out_y >= in_y ) {
- lpi = 1;
- }
- out_y *= 2;
- }
- if( 0 != lpi ) {
-
- yacm = 0;
- yacl = 0;
- cya_cyb = 0x00ff;
-
- /* calculate scaling increment */
- if ( in_y > out_y )
- ysci = ((1024 * in_y) / (out_y + 1)) - 1024;
- else
- ysci = 0;
-
- dcgy = 0;
-
- /* calculate ype and ypo */
- ype = ysci / 16;
- ypo = ype + (ysci / 64);
-
- } else {
- yacm = 1;
-
- /* calculate scaling increment */
- ysci = (((10 * 1024 * (in_y - out_y - 1)) / in_y) + 9) / 10;
-
- /* calculate ype and ypo */
- ypo = ype = ((ysci + 15) / 16);
-
- /* the sequence length interval (yacl) has to be set according
- to the prescale value, e.g. [n .. 1/2) : 0
- [1/2 .. 1/3) : 1
- [1/3 .. 1/4) : 2
- ... */
- if ( ysci < 512) {
- yacl = 0;
- } else {
- yacl = ( ysci / (1024 - ysci) );
- }
-
- /* get filter coefficients for cya, cyb from table hps_v_coeff_tab */
- cya_cyb = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].hps_coeff;
-
- /* get best match in the table of attenuations for vertical scaling */
- v_atten = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].weight_sum;
-
- for (i = 0; v_attenuation[i] != 0; i++) {
- if (v_attenuation[i] >= v_atten)
- break;
- }
-
- dcgy = i;
- }
-
- /* ypo and ype swapped in spec ? */
- *hps_v_scale |= (yacm << 31) | (ysci << 21) | (yacl << 15) | (ypo << 8 ) | (ype << 1);
-
- *hps_v_gain &= ~(MASK_W0|MASK_B2);
- *hps_v_gain |= (dcgy << 16) | (cya_cyb << 0);
-
- return 0;
-}
-
-/* simple bubble-sort algorithm with duplicate elimination */
-static int sort_and_eliminate(u32* values, int* count)
-{
- int low = 0, high = 0, top = 0, temp = 0;
- int cur = 0, next = 0;
-
- /* sanity checks */
- if( (0 > *count) || (NULL == values) ) {
- return -EINVAL;
- }
-
- /* bubble sort the first @count items of the array @values */
- for( top = *count; top > 0; top--) {
- for( low = 0, high = 1; high < top; low++, high++) {
- if( values[low] > values[high] ) {
- temp = values[low];
- values[low] = values[high];
- values[high] = temp;
- }
- }
- }
-
- /* remove duplicate items */
- for( cur = 0, next = 1; next < *count; next++) {
- if( values[cur] != values[next])
- values[++cur] = values[next];
- }
-
- *count = cur + 1;
-
- return 0;
-}
-
-static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct saa7146_fh *fh,
- struct saa7146_video_dma *vdma2, u32* clip_format, u32* arbtr_ctrl, enum v4l2_field field)
-{
- struct saa7146_vv *vv = dev->vv_data;
- __le32 *clipping = vv->d_clipping.cpu_addr;
-
- int width = vv->ov.win.w.width;
- int height = vv->ov.win.w.height;
- int clipcount = vv->ov.nclips;
-
- u32 line_list[32];
- u32 pixel_list[32];
- int numdwords = 0;
-
- int i = 0, j = 0;
- int cnt_line = 0, cnt_pixel = 0;
-
- int x[32], y[32], w[32], h[32];
-
- /* clear out memory */
- memset(&line_list[0], 0x00, sizeof(u32)*32);
- memset(&pixel_list[0], 0x00, sizeof(u32)*32);
- memset(clipping, 0x00, SAA7146_CLIPPING_MEM);
-
- /* fill the line and pixel-lists */
- for(i = 0; i < clipcount; i++) {
- int l = 0, r = 0, t = 0, b = 0;
-
- x[i] = vv->ov.clips[i].c.left;
- y[i] = vv->ov.clips[i].c.top;
- w[i] = vv->ov.clips[i].c.width;
- h[i] = vv->ov.clips[i].c.height;
-
- if( w[i] < 0) {
- x[i] += w[i]; w[i] = -w[i];
- }
- if( h[i] < 0) {
- y[i] += h[i]; h[i] = -h[i];
- }
- if( x[i] < 0) {
- w[i] += x[i]; x[i] = 0;
- }
- if( y[i] < 0) {
- h[i] += y[i]; y[i] = 0;
- }
- if( 0 != vv->vflip ) {
- y[i] = height - y[i] - h[i];
- }
-
- l = x[i];
- r = x[i]+w[i];
- t = y[i];
- b = y[i]+h[i];
-
- /* insert left/right coordinates */
- pixel_list[ 2*i ] = min_t(int, l, width);
- pixel_list[(2*i)+1] = min_t(int, r, width);
- /* insert top/bottom coordinates */
- line_list[ 2*i ] = min_t(int, t, height);
- line_list[(2*i)+1] = min_t(int, b, height);
- }
-
- /* sort and eliminate lists */
- cnt_line = cnt_pixel = 2*clipcount;
- sort_and_eliminate( &pixel_list[0], &cnt_pixel );
- sort_and_eliminate( &line_list[0], &cnt_line );
-
- /* calculate the number of used u32s */
- numdwords = max_t(int, (cnt_line+1), (cnt_pixel+1))*2;
- numdwords = max_t(int, 4, numdwords);
- numdwords = min_t(int, 64, numdwords);
-
- /* fill up cliptable */
- for(i = 0; i < cnt_pixel; i++) {
- clipping[2*i] |= cpu_to_le32(pixel_list[i] << 16);
- }
- for(i = 0; i < cnt_line; i++) {
- clipping[(2*i)+1] |= cpu_to_le32(line_list[i] << 16);
- }
-
- /* fill up cliptable with the display infos */
- for(j = 0; j < clipcount; j++) {
-
- for(i = 0; i < cnt_pixel; i++) {
-
- if( x[j] < 0)
- x[j] = 0;
-
- if( pixel_list[i] < (x[j] + w[j])) {
-
- if ( pixel_list[i] >= x[j] ) {
- clipping[2*i] |= cpu_to_le32(1 << j);
- }
- }
- }
- for(i = 0; i < cnt_line; i++) {
-
- if( y[j] < 0)
- y[j] = 0;
-
- if( line_list[i] < (y[j] + h[j]) ) {
-
- if( line_list[i] >= y[j] ) {
- clipping[(2*i)+1] |= cpu_to_le32(1 << j);
- }
- }
- }
- }
-
- /* adjust arbitration control register */
- *arbtr_ctrl &= 0xffff00ff;
- *arbtr_ctrl |= 0x00001c00;
-
- vdma2->base_even = vv->d_clipping.dma_handle;
- vdma2->base_odd = vv->d_clipping.dma_handle;
- vdma2->prot_addr = vv->d_clipping.dma_handle+((sizeof(u32))*(numdwords));
- vdma2->base_page = 0x04;
- vdma2->pitch = 0x00;
- vdma2->num_line_byte = (0 << 16 | (sizeof(u32))*(numdwords-1) );
-
- /* set clipping-mode. this depends on the field(s) used */
- *clip_format &= 0xfffffff7;
- if (V4L2_FIELD_HAS_BOTH(field)) {
- *clip_format |= 0x00000008;
- } else {
- *clip_format |= 0x00000000;
- }
-}
-
-/* disable clipping */
-static void saa7146_disable_clipping(struct saa7146_dev *dev)
-{
- u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
-
- /* mask out relevant bits (=lower word)*/
- clip_format &= MASK_W1;
-
- /* upload clipping-registers*/
- saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format);
- saa7146_write(dev, MC2, (MASK_05 | MASK_21));
-
- /* disable video dma2 */
- saa7146_write(dev, MC1, MASK_21);
-}
-
-static void saa7146_set_clipping_rect(struct saa7146_fh *fh)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- enum v4l2_field field = vv->ov.win.field;
- struct saa7146_video_dma vdma2;
- u32 clip_format;
- u32 arbtr_ctrl;
-
- /* check clipcount, disable clipping if clipcount == 0*/
- if (vv->ov.nclips == 0) {
- saa7146_disable_clipping(dev);
- return;
- }
-
- clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
- arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
-
- calculate_clipping_registers_rect(dev, fh, &vdma2, &clip_format, &arbtr_ctrl, field);
-
- /* set clipping format */
- clip_format &= 0xffff0008;
- clip_format |= (SAA7146_CLIPPING_RECT << 4);
-
- /* prepare video dma2 */
- saa7146_write(dev, BASE_EVEN2, vdma2.base_even);
- saa7146_write(dev, BASE_ODD2, vdma2.base_odd);
- saa7146_write(dev, PROT_ADDR2, vdma2.prot_addr);
- saa7146_write(dev, BASE_PAGE2, vdma2.base_page);
- saa7146_write(dev, PITCH2, vdma2.pitch);
- saa7146_write(dev, NUM_LINE_BYTE2, vdma2.num_line_byte);
-
- /* prepare the rest */
- saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format);
- saa7146_write(dev, PCI_BT_V1, arbtr_ctrl);
-
- /* upload clip_control-register, clipping-registers, enable video dma2 */
- saa7146_write(dev, MC2, (MASK_05 | MASK_21 | MASK_03 | MASK_19));
- saa7146_write(dev, MC1, (MASK_05 | MASK_21));
-}
-
-static void saa7146_set_window(struct saa7146_dev *dev, int width, int height, enum v4l2_field field)
-{
- struct saa7146_vv *vv = dev->vv_data;
-
- int source = vv->current_hps_source;
- int sync = vv->current_hps_sync;
-
- u32 hps_v_scale = 0, hps_v_gain = 0, hps_ctrl = 0, hps_h_prescale = 0, hps_h_scale = 0;
-
- /* set vertical scale */
- hps_v_scale = 0; /* all bits get set by the function-call */
- hps_v_gain = 0; /* fixme: saa7146_read(dev, HPS_V_GAIN);*/
- calculate_v_scale_registers(dev, field, vv->standard->v_field*2, height, &hps_v_scale, &hps_v_gain);
-
- /* set horizontal scale */
- hps_ctrl = 0;
- hps_h_prescale = 0; /* all bits get set in the function */
- hps_h_scale = 0;
- calculate_h_scale_registers(dev, vv->standard->h_pixels, width, vv->hflip, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale);
-
- /* set hyo and hxo */
- calculate_hxo_and_hyo(vv, &hps_h_scale, &hps_ctrl);
- calculate_hps_source_and_sync(dev, source, sync, &hps_ctrl);
-
- /* write out new register contents */
- saa7146_write(dev, HPS_V_SCALE, hps_v_scale);
- saa7146_write(dev, HPS_V_GAIN, hps_v_gain);
- saa7146_write(dev, HPS_CTRL, hps_ctrl);
- saa7146_write(dev, HPS_H_PRESCALE,hps_h_prescale);
- saa7146_write(dev, HPS_H_SCALE, hps_h_scale);
-
- /* upload shadow-ram registers */
- saa7146_write(dev, MC2, (MASK_05 | MASK_06 | MASK_21 | MASK_22) );
-}
-
-/* calculate the new memory offsets for a desired position */
-static void saa7146_set_position(struct saa7146_dev *dev, int w_x, int w_y, int w_height, enum v4l2_field field, u32 pixelformat)
-{
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pixelformat);
-
- int b_depth = vv->ov_fmt->depth;
- int b_bpl = vv->ov_fb.fmt.bytesperline;
- /* The unsigned long cast is to remove a 64-bit compile warning since
- it looks like a 64-bit address is cast to a 32-bit value, even
- though the base pointer is really a 32-bit physical address that
- goes into a 32-bit DMA register.
- FIXME: might not work on some 64-bit platforms, but see the FIXME
- in struct v4l2_framebuffer (videodev2.h) for that.
- */
- u32 base = (u32)(unsigned long)vv->ov_fb.base;
-
- struct saa7146_video_dma vdma1;
-
- /* calculate memory offsets for picture, look if we shall top-down-flip */
- vdma1.pitch = 2*b_bpl;
- if ( 0 == vv->vflip ) {
- vdma1.base_even = base + (w_y * (vdma1.pitch/2)) + (w_x * (b_depth / 8));
- vdma1.base_odd = vdma1.base_even + (vdma1.pitch / 2);
- vdma1.prot_addr = vdma1.base_even + (w_height * (vdma1.pitch / 2));
- }
- else {
- vdma1.base_even = base + ((w_y+w_height) * (vdma1.pitch/2)) + (w_x * (b_depth / 8));
- vdma1.base_odd = vdma1.base_even - (vdma1.pitch / 2);
- vdma1.prot_addr = vdma1.base_odd - (w_height * (vdma1.pitch / 2));
- }
-
- if (V4L2_FIELD_HAS_BOTH(field)) {
- } else if (field == V4L2_FIELD_ALTERNATE) {
- /* fixme */
- vdma1.base_odd = vdma1.prot_addr;
- vdma1.pitch /= 2;
- } else if (field == V4L2_FIELD_TOP) {
- vdma1.base_odd = vdma1.prot_addr;
- vdma1.pitch /= 2;
- } else if (field == V4L2_FIELD_BOTTOM) {
- vdma1.base_odd = vdma1.base_even;
- vdma1.base_even = vdma1.prot_addr;
- vdma1.pitch /= 2;
- }
-
- if ( 0 != vv->vflip ) {
- vdma1.pitch *= -1;
- }
-
- vdma1.base_page = sfmt->swap;
- vdma1.num_line_byte = (vv->standard->v_field<<16)+vv->standard->h_pixels;
-
- saa7146_write_out_dma(dev, 1, &vdma1);
-}
-
-static void saa7146_set_output_format(struct saa7146_dev *dev, unsigned long palette)
-{
- u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
-
- /* call helper function */
- calculate_output_format_register(dev,palette,&clip_format);
-
- /* update the hps registers */
- saa7146_write(dev, CLIP_FORMAT_CTRL, clip_format);
- saa7146_write(dev, MC2, (MASK_05 | MASK_21));
-}
-
-/* select input-source */
-void saa7146_set_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync)
-{
- struct saa7146_vv *vv = dev->vv_data;
- u32 hps_ctrl = 0;
-
- /* read old state */
- hps_ctrl = saa7146_read(dev, HPS_CTRL);
-
- hps_ctrl &= ~( MASK_31 | MASK_30 | MASK_28 );
- hps_ctrl |= (source << 30) | (sync << 28);
-
- /* write back & upload register */
- saa7146_write(dev, HPS_CTRL, hps_ctrl);
- saa7146_write(dev, MC2, (MASK_05 | MASK_21));
-
- vv->current_hps_source = source;
- vv->current_hps_sync = sync;
-}
-EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync);
-
-int saa7146_enable_overlay(struct saa7146_fh *fh)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- saa7146_set_window(dev, vv->ov.win.w.width, vv->ov.win.w.height, vv->ov.win.field);
- saa7146_set_position(dev, vv->ov.win.w.left, vv->ov.win.w.top, vv->ov.win.w.height, vv->ov.win.field, vv->ov_fmt->pixelformat);
- saa7146_set_output_format(dev, vv->ov_fmt->trans);
- saa7146_set_clipping_rect(fh);
-
- /* enable video dma1 */
- saa7146_write(dev, MC1, (MASK_06 | MASK_22));
- return 0;
-}
-
-void saa7146_disable_overlay(struct saa7146_fh *fh)
-{
- struct saa7146_dev *dev = fh->dev;
-
- /* disable clipping + video dma1 */
- saa7146_disable_clipping(dev);
- saa7146_write(dev, MC1, MASK_22);
-}
-
-void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma)
-{
- int where = 0;
-
- if( which < 1 || which > 3) {
- return;
- }
-
- /* calculate starting address */
- where = (which-1)*0x18;
-
- saa7146_write(dev, where, vdma->base_odd);
- saa7146_write(dev, where+0x04, vdma->base_even);
- saa7146_write(dev, where+0x08, vdma->prot_addr);
- saa7146_write(dev, where+0x0c, vdma->pitch);
- saa7146_write(dev, where+0x10, vdma->base_page);
- saa7146_write(dev, where+0x14, vdma->num_line_byte);
-
- /* upload */
- saa7146_write(dev, MC2, (MASK_02<<(which-1))|(MASK_18<<(which-1)));
-/*
- printk("vdma%d.base_even: 0x%08x\n", which,vdma->base_even);
- printk("vdma%d.base_odd: 0x%08x\n", which,vdma->base_odd);
- printk("vdma%d.prot_addr: 0x%08x\n", which,vdma->prot_addr);
- printk("vdma%d.base_page: 0x%08x\n", which,vdma->base_page);
- printk("vdma%d.pitch: 0x%08x\n", which,vdma->pitch);
- printk("vdma%d.num_line_byte: 0x%08x\n", which,vdma->num_line_byte);
-*/
-}
-
-static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa7146_buf *buf)
-{
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_video_dma vdma1;
-
- struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
-
- int width = buf->fmt->width;
- int height = buf->fmt->height;
- int bytesperline = buf->fmt->bytesperline;
- enum v4l2_field field = buf->fmt->field;
-
- int depth = sfmt->depth;
-
- DEB_CAP("[size=%dx%d,fields=%s]\n",
- width, height, v4l2_field_names[field]);
-
- if( bytesperline != 0) {
- vdma1.pitch = bytesperline*2;
- } else {
- vdma1.pitch = (width*depth*2)/8;
- }
- vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels);
- vdma1.base_page = buf->pt[0].dma | ME1 | sfmt->swap;
-
- if( 0 != vv->vflip ) {
- vdma1.prot_addr = buf->pt[0].offset;
- vdma1.base_even = buf->pt[0].offset+(vdma1.pitch/2)*height;
- vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2);
- } else {
- vdma1.base_even = buf->pt[0].offset;
- vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2);
- vdma1.prot_addr = buf->pt[0].offset+(vdma1.pitch/2)*height;
- }
-
- if (V4L2_FIELD_HAS_BOTH(field)) {
- } else if (field == V4L2_FIELD_ALTERNATE) {
- /* fixme */
- if ( vv->last_field == V4L2_FIELD_TOP ) {
- vdma1.base_odd = vdma1.prot_addr;
- vdma1.pitch /= 2;
- } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
- vdma1.base_odd = vdma1.base_even;
- vdma1.base_even = vdma1.prot_addr;
- vdma1.pitch /= 2;
- }
- } else if (field == V4L2_FIELD_TOP) {
- vdma1.base_odd = vdma1.prot_addr;
- vdma1.pitch /= 2;
- } else if (field == V4L2_FIELD_BOTTOM) {
- vdma1.base_odd = vdma1.base_even;
- vdma1.base_even = vdma1.prot_addr;
- vdma1.pitch /= 2;
- }
-
- if( 0 != vv->vflip ) {
- vdma1.pitch *= -1;
- }
-
- saa7146_write_out_dma(dev, 1, &vdma1);
- return 0;
-}
-
-static int calc_planar_422(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3)
-{
- int height = buf->fmt->height;
- int width = buf->fmt->width;
-
- vdma2->pitch = width;
- vdma3->pitch = width;
-
- /* fixme: look at bytesperline! */
-
- if( 0 != vv->vflip ) {
- vdma2->prot_addr = buf->pt[1].offset;
- vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[1].offset;
- vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2);
-
- vdma3->prot_addr = buf->pt[2].offset;
- vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[2].offset;
- vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2);
- } else {
- vdma3->base_even = buf->pt[2].offset;
- vdma3->base_odd = vdma3->base_even + (vdma3->pitch/2);
- vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset;
-
- vdma2->base_even = buf->pt[1].offset;
- vdma2->base_odd = vdma2->base_even + (vdma2->pitch/2);
- vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset;
- }
-
- return 0;
-}
-
-static int calc_planar_420(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3)
-{
- int height = buf->fmt->height;
- int width = buf->fmt->width;
-
- vdma2->pitch = width/2;
- vdma3->pitch = width/2;
-
- if( 0 != vv->vflip ) {
- vdma2->prot_addr = buf->pt[2].offset;
- vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[2].offset;
- vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2);
-
- vdma3->prot_addr = buf->pt[1].offset;
- vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[1].offset;
- vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2);
-
- } else {
- vdma3->base_even = buf->pt[2].offset;
- vdma3->base_odd = vdma3->base_even + (vdma3->pitch);
- vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset;
-
- vdma2->base_even = buf->pt[1].offset;
- vdma2->base_odd = vdma2->base_even + (vdma2->pitch);
- vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset;
- }
- return 0;
-}
-
-static int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa7146_buf *buf)
-{
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_video_dma vdma1;
- struct saa7146_video_dma vdma2;
- struct saa7146_video_dma vdma3;
-
- struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
-
- int width = buf->fmt->width;
- int height = buf->fmt->height;
- enum v4l2_field field = buf->fmt->field;
-
- BUG_ON(0 == buf->pt[0].dma);
- BUG_ON(0 == buf->pt[1].dma);
- BUG_ON(0 == buf->pt[2].dma);
-
- DEB_CAP("[size=%dx%d,fields=%s]\n",
- width, height, v4l2_field_names[field]);
-
- /* fixme: look at bytesperline! */
-
- /* fixme: what happens for user space buffers here?. The offsets are
- most likely wrong, this version here only works for page-aligned
- buffers, modifications to the pagetable-functions are necessary...*/
-
- vdma1.pitch = width*2;
- vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels);
- vdma1.base_page = buf->pt[0].dma | ME1;
-
- if( 0 != vv->vflip ) {
- vdma1.prot_addr = buf->pt[0].offset;
- vdma1.base_even = ((vdma1.pitch/2)*height)+buf->pt[0].offset;
- vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2);
- } else {
- vdma1.base_even = buf->pt[0].offset;
- vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2);
- vdma1.prot_addr = (vdma1.pitch/2)*height+buf->pt[0].offset;
- }
-
- vdma2.num_line_byte = 0; /* unused */
- vdma2.base_page = buf->pt[1].dma | ME1;
-
- vdma3.num_line_byte = 0; /* unused */
- vdma3.base_page = buf->pt[2].dma | ME1;
-
- switch( sfmt->depth ) {
- case 12: {
- calc_planar_420(vv,buf,&vdma2,&vdma3);
- break;
- }
- case 16: {
- calc_planar_422(vv,buf,&vdma2,&vdma3);
- break;
- }
- default: {
- return -1;
- }
- }
-
- if (V4L2_FIELD_HAS_BOTH(field)) {
- } else if (field == V4L2_FIELD_ALTERNATE) {
- /* fixme */
- vdma1.base_odd = vdma1.prot_addr;
- vdma1.pitch /= 2;
- vdma2.base_odd = vdma2.prot_addr;
- vdma2.pitch /= 2;
- vdma3.base_odd = vdma3.prot_addr;
- vdma3.pitch /= 2;
- } else if (field == V4L2_FIELD_TOP) {
- vdma1.base_odd = vdma1.prot_addr;
- vdma1.pitch /= 2;
- vdma2.base_odd = vdma2.prot_addr;
- vdma2.pitch /= 2;
- vdma3.base_odd = vdma3.prot_addr;
- vdma3.pitch /= 2;
- } else if (field == V4L2_FIELD_BOTTOM) {
- vdma1.base_odd = vdma1.base_even;
- vdma1.base_even = vdma1.prot_addr;
- vdma1.pitch /= 2;
- vdma2.base_odd = vdma2.base_even;
- vdma2.base_even = vdma2.prot_addr;
- vdma2.pitch /= 2;
- vdma3.base_odd = vdma3.base_even;
- vdma3.base_even = vdma3.prot_addr;
- vdma3.pitch /= 2;
- }
-
- if( 0 != vv->vflip ) {
- vdma1.pitch *= -1;
- vdma2.pitch *= -1;
- vdma3.pitch *= -1;
- }
-
- saa7146_write_out_dma(dev, 1, &vdma1);
- if( (sfmt->flags & FORMAT_BYTE_SWAP) != 0 ) {
- saa7146_write_out_dma(dev, 3, &vdma2);
- saa7146_write_out_dma(dev, 2, &vdma3);
- } else {
- saa7146_write_out_dma(dev, 2, &vdma2);
- saa7146_write_out_dma(dev, 3, &vdma3);
- }
- return 0;
-}
-
-static void program_capture_engine(struct saa7146_dev *dev, int planar)
-{
- struct saa7146_vv *vv = dev->vv_data;
- int count = 0;
-
- unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
- unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
-
- /* wait for o_fid_a/b / e_fid_a/b toggle only if rps register 0 is not set*/
- WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | o_wait);
- WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | e_wait);
-
- /* set rps register 0 */
- WRITE_RPS0(CMD_WR_REG | (1 << 8) | (MC2/4));
- WRITE_RPS0(MASK_27 | MASK_11);
-
- /* turn on video-dma1 */
- WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
- WRITE_RPS0(MASK_06 | MASK_22); /* => mask */
- WRITE_RPS0(MASK_06 | MASK_22); /* => values */
- if( 0 != planar ) {
- /* turn on video-dma2 */
- WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
- WRITE_RPS0(MASK_05 | MASK_21); /* => mask */
- WRITE_RPS0(MASK_05 | MASK_21); /* => values */
-
- /* turn on video-dma3 */
- WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
- WRITE_RPS0(MASK_04 | MASK_20); /* => mask */
- WRITE_RPS0(MASK_04 | MASK_20); /* => values */
- }
-
- /* wait for o_fid_a/b / e_fid_a/b toggle */
- if ( vv->last_field == V4L2_FIELD_INTERLACED ) {
- WRITE_RPS0(CMD_PAUSE | o_wait);
- WRITE_RPS0(CMD_PAUSE | e_wait);
- } else if ( vv->last_field == V4L2_FIELD_TOP ) {
- WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09));
- WRITE_RPS0(CMD_PAUSE | o_wait);
- } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
- WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09));
- WRITE_RPS0(CMD_PAUSE | e_wait);
- }
-
- /* turn off video-dma1 */
- WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
- WRITE_RPS0(MASK_22 | MASK_06); /* => mask */
- WRITE_RPS0(MASK_22); /* => values */
- if( 0 != planar ) {
- /* turn off video-dma2 */
- WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
- WRITE_RPS0(MASK_05 | MASK_21); /* => mask */
- WRITE_RPS0(MASK_21); /* => values */
-
- /* turn off video-dma3 */
- WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4));
- WRITE_RPS0(MASK_04 | MASK_20); /* => mask */
- WRITE_RPS0(MASK_20); /* => values */
- }
-
- /* generate interrupt */
- WRITE_RPS0(CMD_INTERRUPT);
-
- /* stop */
- WRITE_RPS0(CMD_STOP);
-}
-
-void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
-{
- struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
- struct saa7146_vv *vv = dev->vv_data;
- u32 vdma1_prot_addr;
-
- DEB_CAP("buf:%p, next:%p\n", buf, next);
-
- vdma1_prot_addr = saa7146_read(dev, PROT_ADDR1);
- if( 0 == vdma1_prot_addr ) {
- /* clear out beginning of streaming bit (rps register 0)*/
- DEB_CAP("forcing sync to new frame\n");
- saa7146_write(dev, MC2, MASK_27 );
- }
-
- saa7146_set_window(dev, buf->fmt->width, buf->fmt->height, buf->fmt->field);
- saa7146_set_output_format(dev, sfmt->trans);
- saa7146_disable_clipping(dev);
-
- if ( vv->last_field == V4L2_FIELD_INTERLACED ) {
- } else if ( vv->last_field == V4L2_FIELD_TOP ) {
- vv->last_field = V4L2_FIELD_BOTTOM;
- } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) {
- vv->last_field = V4L2_FIELD_TOP;
- }
-
- if( 0 != IS_PLANAR(sfmt->trans)) {
- calculate_video_dma_grab_planar(dev, buf);
- program_capture_engine(dev,1);
- } else {
- calculate_video_dma_grab_packed(dev, buf);
- program_capture_engine(dev,0);
- }
-
-/*
- printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
- printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
- printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
- printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
- printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
- printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
- printk("vdma%d => vptr : 0x%08x\n", 1,saa7146_read(dev,PCI_VDP1));
-*/
-
- /* write the address of the rps-program */
- saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
-
- /* turn on rps */
- saa7146_write(dev, MC1, (MASK_12 | MASK_28));
-}
diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c
deleted file mode 100644
index 1e71e374bbfe..000000000000
--- a/drivers/media/common/saa7146_vbi.c
+++ /dev/null
@@ -1,498 +0,0 @@
-#include <media/saa7146_vv.h>
-
-static int vbi_pixel_to_capture = 720 * 2;
-
-static int vbi_workaround(struct saa7146_dev *dev)
-{
- struct saa7146_vv *vv = dev->vv_data;
-
- u32 *cpu;
- dma_addr_t dma_addr;
-
- int count = 0;
- int i;
-
- DECLARE_WAITQUEUE(wait, current);
-
- DEB_VBI("dev:%p\n", dev);
-
- /* once again, a bug in the saa7146: the brs acquisition
- is buggy and especially the BXO-counter does not work
- as specified. there is this workaround, but please
- don't let me explain it. ;-) */
-
- cpu = pci_alloc_consistent(dev->pci, 4096, &dma_addr);
- if (NULL == cpu)
- return -ENOMEM;
-
- /* setup some basic programming, just for the workaround */
- saa7146_write(dev, BASE_EVEN3, dma_addr);
- saa7146_write(dev, BASE_ODD3, dma_addr+vbi_pixel_to_capture);
- saa7146_write(dev, PROT_ADDR3, dma_addr+4096);
- saa7146_write(dev, PITCH3, vbi_pixel_to_capture);
- saa7146_write(dev, BASE_PAGE3, 0x0);
- saa7146_write(dev, NUM_LINE_BYTE3, (2<<16)|((vbi_pixel_to_capture)<<0));
- saa7146_write(dev, MC2, MASK_04|MASK_20);
-
- /* load brs-control register */
- WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
- /* BXO = 1h, BRS to outbound */
- WRITE_RPS1(0xc000008c);
- /* wait for vbi_a or vbi_b*/
- if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
- DEB_D("...using port b\n");
- WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_E_FID_B);
- WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_O_FID_B);
-/*
- WRITE_RPS1(CMD_PAUSE | MASK_09);
-*/
- } else {
- DEB_D("...using port a\n");
- WRITE_RPS1(CMD_PAUSE | MASK_10);
- }
- /* upload brs */
- WRITE_RPS1(CMD_UPLOAD | MASK_08);
- /* load brs-control register */
- WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
- /* BYO = 1, BXO = NQBIL (=1728 for PAL, for NTSC this is 858*2) - NumByte3 (=1440) = 288 */
- WRITE_RPS1(((1728-(vbi_pixel_to_capture)) << 7) | MASK_19);
- /* wait for brs_done */
- WRITE_RPS1(CMD_PAUSE | MASK_08);
- /* upload brs */
- WRITE_RPS1(CMD_UPLOAD | MASK_08);
- /* load video-dma3 NumLines3 and NumBytes3 */
- WRITE_RPS1(CMD_WR_REG | (1 << 8) | (NUM_LINE_BYTE3/4));
- /* dev->vbi_count*2 lines, 720 pixel (= 1440 Bytes) */
- WRITE_RPS1((2 << 16) | (vbi_pixel_to_capture));
- /* load brs-control register */
- WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
- /* Set BRS right: note: this is an experimental value for BXO (=> PAL!) */
- WRITE_RPS1((540 << 7) | (5 << 19)); // 5 == vbi_start
- /* wait for brs_done */
- WRITE_RPS1(CMD_PAUSE | MASK_08);
- /* upload brs and video-dma3*/
- WRITE_RPS1(CMD_UPLOAD | MASK_08 | MASK_04);
- /* load mc2 register: enable dma3 */
- WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC1/4));
- WRITE_RPS1(MASK_20 | MASK_04);
- /* generate interrupt */
- WRITE_RPS1(CMD_INTERRUPT);
- /* stop rps1 */
- WRITE_RPS1(CMD_STOP);
-
- /* we have to do the workaround twice to be sure that
- everything is ok */
- for(i = 0; i < 2; i++) {
-
- /* indicate to the irq handler that we do the workaround */
- saa7146_write(dev, MC2, MASK_31|MASK_15);
-
- saa7146_write(dev, NUM_LINE_BYTE3, (1<<16)|(2<<0));
- saa7146_write(dev, MC2, MASK_04|MASK_20);
-
- /* enable rps1 irqs */
- SAA7146_IER_ENABLE(dev,MASK_28);
-
- /* prepare to wait to be woken up by the irq-handler */
- add_wait_queue(&vv->vbi_wq, &wait);
- current->state = TASK_INTERRUPTIBLE;
-
- /* start rps1 to enable workaround */
- saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
- saa7146_write(dev, MC1, (MASK_13 | MASK_29));
-
- schedule();
-
- DEB_VBI("brs bug workaround %d/1\n", i);
-
- remove_wait_queue(&vv->vbi_wq, &wait);
- current->state = TASK_RUNNING;
-
- /* disable rps1 irqs */
- SAA7146_IER_DISABLE(dev,MASK_28);
-
- /* stop video-dma3 */
- saa7146_write(dev, MC1, MASK_20);
-
- if(signal_pending(current)) {
-
- DEB_VBI("aborted (rps:0x%08x)\n",
- saa7146_read(dev, RPS_ADDR1));
-
- /* stop rps1 for sure */
- saa7146_write(dev, MC1, MASK_29);
-
- pci_free_consistent(dev->pci, 4096, cpu, dma_addr);
- return -EINTR;
- }
- }
-
- pci_free_consistent(dev->pci, 4096, cpu, dma_addr);
- return 0;
-}
-
-static void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
-{
- struct saa7146_vv *vv = dev->vv_data;
-
- struct saa7146_video_dma vdma3;
-
- int count = 0;
- unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
- unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
-
-/*
- vdma3.base_even = 0xc8000000+2560*70;
- vdma3.base_odd = 0xc8000000;
- vdma3.prot_addr = 0xc8000000+2560*164;
- vdma3.pitch = 2560;
- vdma3.base_page = 0;
- vdma3.num_line_byte = (64<<16)|((vbi_pixel_to_capture)<<0); // set above!
-*/
- vdma3.base_even = buf->pt[2].offset;
- vdma3.base_odd = buf->pt[2].offset + 16 * vbi_pixel_to_capture;
- vdma3.prot_addr = buf->pt[2].offset + 16 * 2 * vbi_pixel_to_capture;
- vdma3.pitch = vbi_pixel_to_capture;
- vdma3.base_page = buf->pt[2].dma | ME1;
- vdma3.num_line_byte = (16 << 16) | vbi_pixel_to_capture;
-
- saa7146_write_out_dma(dev, 3, &vdma3);
-
- /* write beginning of rps-program */
- count = 0;
-
- /* wait for o_fid_a/b / e_fid_a/b toggle only if bit 1 is not set */
-
- /* we don't wait here for the first field anymore. this is different from the video
- capture and might cause that the first buffer is only half filled (with only
- one field). but since this is some sort of streaming data, this is not that negative.
- but by doing this, we can use the whole engine from videobuf-dma-sg.c... */
-
-/*
- WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | e_wait);
- WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | o_wait);
-*/
- /* set bit 1 */
- WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC2/4));
- WRITE_RPS1(MASK_28 | MASK_12);
-
- /* turn on video-dma3 */
- WRITE_RPS1(CMD_WR_REG_MASK | (MC1/4));
- WRITE_RPS1(MASK_04 | MASK_20); /* => mask */
- WRITE_RPS1(MASK_04 | MASK_20); /* => values */
-
- /* wait for o_fid_a/b / e_fid_a/b toggle */
- WRITE_RPS1(CMD_PAUSE | o_wait);
- WRITE_RPS1(CMD_PAUSE | e_wait);
-
- /* generate interrupt */
- WRITE_RPS1(CMD_INTERRUPT);
-
- /* stop */
- WRITE_RPS1(CMD_STOP);
-
- /* enable rps1 irqs */
- SAA7146_IER_ENABLE(dev, MASK_28);
-
- /* write the address of the rps-program */
- saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
-
- /* turn on rps */
- saa7146_write(dev, MC1, (MASK_13 | MASK_29));
-}
-
-static int buffer_activate(struct saa7146_dev *dev,
- struct saa7146_buf *buf,
- struct saa7146_buf *next)
-{
- struct saa7146_vv *vv = dev->vv_data;
- buf->vb.state = VIDEOBUF_ACTIVE;
-
- DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next);
- saa7146_set_vbi_capture(dev,buf,next);
-
- mod_timer(&vv->vbi_dmaq.timeout, jiffies+BUFFER_TIMEOUT);
- return 0;
-}
-
-static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,enum v4l2_field field)
-{
- struct file *file = q->priv_data;
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_buf *buf = (struct saa7146_buf *)vb;
-
- int err = 0;
- int lines, llength, size;
-
- lines = 16 * 2 ; /* 2 fields */
- llength = vbi_pixel_to_capture;
- size = lines * llength;
-
- DEB_VBI("vb:%p\n", vb);
-
- if (0 != buf->vb.baddr && buf->vb.bsize < size) {
- DEB_VBI("size mismatch\n");
- return -EINVAL;
- }
-
- if (buf->vb.size != size)
- saa7146_dma_free(dev,q,buf);
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
-
- buf->vb.width = llength;
- buf->vb.height = lines;
- buf->vb.size = size;
- buf->vb.field = field; // FIXME: check this
-
- saa7146_pgtable_free(dev->pci, &buf->pt[2]);
- saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
-
- err = videobuf_iolock(q,&buf->vb, NULL);
- if (err)
- goto oops;
- err = saa7146_pgtable_build_single(dev->pci, &buf->pt[2],
- dma->sglist, dma->sglen);
- if (0 != err)
- return err;
- }
- buf->vb.state = VIDEOBUF_PREPARED;
- buf->activate = buffer_activate;
-
- return 0;
-
- oops:
- DEB_VBI("error out\n");
- saa7146_dma_free(dev,q,buf);
-
- return err;
-}
-
-static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
-{
- int llength,lines;
-
- lines = 16 * 2 ; /* 2 fields */
- llength = vbi_pixel_to_capture;
-
- *size = lines * llength;
- *count = 2;
-
- DEB_VBI("count:%d, size:%d\n", *count, *size);
-
- return 0;
-}
-
-static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct file *file = q->priv_data;
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_buf *buf = (struct saa7146_buf *)vb;
-
- DEB_VBI("vb:%p\n", vb);
- saa7146_buffer_queue(dev, &vv->vbi_dmaq, buf);
-}
-
-static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct file *file = q->priv_data;
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_buf *buf = (struct saa7146_buf *)vb;
-
- DEB_VBI("vb:%p\n", vb);
- saa7146_dma_free(dev,q,buf);
-}
-
-static struct videobuf_queue_ops vbi_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-/* ------------------------------------------------------------------ */
-
-static void vbi_stop(struct saa7146_fh *fh, struct file *file)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- unsigned long flags;
- DEB_VBI("dev:%p, fh:%p\n", dev, fh);
-
- spin_lock_irqsave(&dev->slock,flags);
-
- /* disable rps1 */
- saa7146_write(dev, MC1, MASK_29);
-
- /* disable rps1 irqs */
- SAA7146_IER_DISABLE(dev, MASK_28);
-
- /* shut down dma 3 transfers */
- saa7146_write(dev, MC1, MASK_20);
-
- if (vv->vbi_dmaq.curr)
- saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE);
-
- videobuf_queue_cancel(&fh->vbi_q);
-
- vv->vbi_streaming = NULL;
-
- del_timer(&vv->vbi_dmaq.timeout);
- del_timer(&vv->vbi_read_timeout);
-
- spin_unlock_irqrestore(&dev->slock, flags);
-}
-
-static void vbi_read_timeout(unsigned long data)
-{
- struct file *file = (struct file*)data;
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
-
- DEB_VBI("dev:%p, fh:%p\n", dev, fh);
-
- vbi_stop(fh, file);
-}
-
-static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
-{
- DEB_VBI("dev:%p\n", dev);
-
- INIT_LIST_HEAD(&vv->vbi_dmaq.queue);
-
- init_timer(&vv->vbi_dmaq.timeout);
- vv->vbi_dmaq.timeout.function = saa7146_buffer_timeout;
- vv->vbi_dmaq.timeout.data = (unsigned long)(&vv->vbi_dmaq);
- vv->vbi_dmaq.dev = dev;
-
- init_waitqueue_head(&vv->vbi_wq);
-}
-
-static int vbi_open(struct saa7146_dev *dev, struct file *file)
-{
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_vv *vv = fh->dev->vv_data;
-
- u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
- int ret = 0;
-
- DEB_VBI("dev:%p, fh:%p\n", dev, fh);
-
- ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS);
- if (0 == ret) {
- DEB_S("cannot get vbi RESOURCE_DMA3_BRS resource\n");
- return -EBUSY;
- }
-
- /* adjust arbitrition control for video dma 3 */
- arbtr_ctrl &= ~0x1f0000;
- arbtr_ctrl |= 0x1d0000;
- saa7146_write(dev, PCI_BT_V1, arbtr_ctrl);
- saa7146_write(dev, MC2, (MASK_04|MASK_20));
-
- videobuf_queue_sg_init(&fh->vbi_q, &vbi_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VBI_CAPTURE,
- V4L2_FIELD_SEQ_TB, // FIXME: does this really work?
- sizeof(struct saa7146_buf),
- file, &dev->v4l2_lock);
-
- vv->vbi_read_timeout.function = vbi_read_timeout;
- vv->vbi_read_timeout.data = (unsigned long)file;
-
- /* initialize the brs */
- if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
- saa7146_write(dev, BRS_CTRL, MASK_30|MASK_29 | (7 << 19));
- } else {
- saa7146_write(dev, BRS_CTRL, 0x00000001);
-
- if (0 != (ret = vbi_workaround(dev))) {
- DEB_VBI("vbi workaround failed!\n");
- /* return ret;*/
- }
- }
-
- /* upload brs register */
- saa7146_write(dev, MC2, (MASK_08|MASK_24));
- return 0;
-}
-
-static void vbi_close(struct saa7146_dev *dev, struct file *file)
-{
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_vv *vv = dev->vv_data;
- DEB_VBI("dev:%p, fh:%p\n", dev, fh);
-
- if( fh == vv->vbi_streaming ) {
- vbi_stop(fh, file);
- }
- saa7146_res_free(fh, RESOURCE_DMA3_BRS);
-}
-
-static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status)
-{
- struct saa7146_vv *vv = dev->vv_data;
- spin_lock(&dev->slock);
-
- if (vv->vbi_dmaq.curr) {
- DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr);
- /* this must be += 2, one count for each field */
- vv->vbi_fieldcount+=2;
- vv->vbi_dmaq.curr->vb.field_count = vv->vbi_fieldcount;
- saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE);
- } else {
- DEB_VBI("dev:%p\n", dev);
- }
- saa7146_buffer_next(dev, &vv->vbi_dmaq, 1);
-
- spin_unlock(&dev->slock);
-}
-
-static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- ssize_t ret = 0;
-
- DEB_VBI("dev:%p, fh:%p\n", dev, fh);
-
- if( NULL == vv->vbi_streaming ) {
- // fixme: check if dma3 is available
- // fixme: activate vbi engine here if necessary. (really?)
- vv->vbi_streaming = fh;
- }
-
- if( fh != vv->vbi_streaming ) {
- DEB_VBI("open %p is already using vbi capture\n",
- vv->vbi_streaming);
- return -EBUSY;
- }
-
- mod_timer(&vv->vbi_read_timeout, jiffies+BUFFER_TIMEOUT);
- ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1,
- file->f_flags & O_NONBLOCK);
-/*
- printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3));
- printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3));
- printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3));
- printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3));
- printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3));
- printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3));
- printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL));
-*/
- return ret;
-}
-
-struct saa7146_use_ops saa7146_vbi_uops = {
- .init = vbi_init,
- .open = vbi_open,
- .release = vbi_close,
- .irq_done = vbi_irq_done,
- .read = vbi_read,
-};
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
deleted file mode 100644
index 6d14785d4747..000000000000
--- a/drivers/media/common/saa7146_video.c
+++ /dev/null
@@ -1,1332 +0,0 @@
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <media/saa7146_vv.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-ctrls.h>
-#include <linux/module.h>
-
-static int max_memory = 32;
-
-module_param(max_memory, int, 0644);
-MODULE_PARM_DESC(max_memory, "maximum memory usage for capture buffers (default: 32Mb)");
-
-#define IS_CAPTURE_ACTIVE(fh) \
- (((vv->video_status & STATUS_CAPTURE) != 0) && (vv->video_fh == fh))
-
-#define IS_OVERLAY_ACTIVE(fh) \
- (((vv->video_status & STATUS_OVERLAY) != 0) && (vv->video_fh == fh))
-
-/* format descriptions for capture and preview */
-static struct saa7146_format formats[] = {
- {
- .name = "RGB-8 (3-3-2)",
- .pixelformat = V4L2_PIX_FMT_RGB332,
- .trans = RGB08_COMPOSED,
- .depth = 8,
- .flags = 0,
- }, {
- .name = "RGB-16 (5/B-6/G-5/R)",
- .pixelformat = V4L2_PIX_FMT_RGB565,
- .trans = RGB16_COMPOSED,
- .depth = 16,
- .flags = 0,
- }, {
- .name = "RGB-24 (B-G-R)",
- .pixelformat = V4L2_PIX_FMT_BGR24,
- .trans = RGB24_COMPOSED,
- .depth = 24,
- .flags = 0,
- }, {
- .name = "RGB-32 (B-G-R)",
- .pixelformat = V4L2_PIX_FMT_BGR32,
- .trans = RGB32_COMPOSED,
- .depth = 32,
- .flags = 0,
- }, {
- .name = "RGB-32 (R-G-B)",
- .pixelformat = V4L2_PIX_FMT_RGB32,
- .trans = RGB32_COMPOSED,
- .depth = 32,
- .flags = 0,
- .swap = 0x2,
- }, {
- .name = "Greyscale-8",
- .pixelformat = V4L2_PIX_FMT_GREY,
- .trans = Y8,
- .depth = 8,
- .flags = 0,
- }, {
- .name = "YUV 4:2:2 planar (Y-Cb-Cr)",
- .pixelformat = V4L2_PIX_FMT_YUV422P,
- .trans = YUV422_DECOMPOSED,
- .depth = 16,
- .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
- }, {
- .name = "YVU 4:2:0 planar (Y-Cb-Cr)",
- .pixelformat = V4L2_PIX_FMT_YVU420,
- .trans = YUV420_DECOMPOSED,
- .depth = 12,
- .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
- }, {
- .name = "YUV 4:2:0 planar (Y-Cb-Cr)",
- .pixelformat = V4L2_PIX_FMT_YUV420,
- .trans = YUV420_DECOMPOSED,
- .depth = 12,
- .flags = FORMAT_IS_PLANAR,
- }, {
- .name = "YUV 4:2:2 (U-Y-V-Y)",
- .pixelformat = V4L2_PIX_FMT_UYVY,
- .trans = YUV422_COMPOSED,
- .depth = 16,
- .flags = 0,
- }
-};
-
-/* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps.
- due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped
- (like V4L2_PIX_FMT_YUYV) ... 8-( */
-
-static int NUM_FORMATS = sizeof(formats)/sizeof(struct saa7146_format);
-
-struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc)
-{
- int i, j = NUM_FORMATS;
-
- for (i = 0; i < j; i++) {
- if (formats[i].pixelformat == fourcc) {
- return formats+i;
- }
- }
-
- DEB_D("unknown pixelformat:'%4.4s'\n", (char *)&fourcc);
- return NULL;
-}
-
-static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f);
-
-int saa7146_start_preview(struct saa7146_fh *fh)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct v4l2_format fmt;
- int ret = 0, err = 0;
-
- DEB_EE("dev:%p, fh:%p\n", dev, fh);
-
- /* check if we have overlay information */
- if (vv->ov.fh == NULL) {
- DEB_D("no overlay data available. try S_FMT first.\n");
- return -EAGAIN;
- }
-
- /* check if streaming capture is running */
- if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_D("streaming capture is active\n");
- return -EBUSY;
- }
-
- /* check if overlay is running */
- if (IS_OVERLAY_ACTIVE(fh) != 0) {
- if (vv->video_fh == fh) {
- DEB_D("overlay is already active\n");
- return 0;
- }
- DEB_D("overlay is already active in another open\n");
- return -EBUSY;
- }
-
- if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
- DEB_D("cannot get necessary overlay resources\n");
- return -EBUSY;
- }
-
- fmt.fmt.win = vv->ov.win;
- err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt);
- if (0 != err) {
- saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
- return -EBUSY;
- }
- vv->ov.win = fmt.fmt.win;
-
- DEB_D("%dx%d+%d+%d %s field=%s\n",
- vv->ov.win.w.width, vv->ov.win.w.height,
- vv->ov.win.w.left, vv->ov.win.w.top,
- vv->ov_fmt->name, v4l2_field_names[vv->ov.win.field]);
-
- if (0 != (ret = saa7146_enable_overlay(fh))) {
- DEB_D("enabling overlay failed: %d\n", ret);
- saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
- return ret;
- }
-
- vv->video_status = STATUS_OVERLAY;
- vv->video_fh = fh;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7146_start_preview);
-
-int saa7146_stop_preview(struct saa7146_fh *fh)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- DEB_EE("dev:%p, fh:%p\n", dev, fh);
-
- /* check if streaming capture is running */
- if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_D("streaming capture is active\n");
- return -EBUSY;
- }
-
- /* check if overlay is running at all */
- if ((vv->video_status & STATUS_OVERLAY) == 0) {
- DEB_D("no active overlay\n");
- return 0;
- }
-
- if (vv->video_fh != fh) {
- DEB_D("overlay is active, but in another open\n");
- return -EBUSY;
- }
-
- vv->video_status = 0;
- vv->video_fh = NULL;
-
- saa7146_disable_overlay(fh);
-
- saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7146_stop_preview);
-
-/********************************************************************************/
-/* common pagetable functions */
-
-static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf)
-{
- struct pci_dev *pci = dev->pci;
- struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
- struct scatterlist *list = dma->sglist;
- int length = dma->sglen;
- struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
-
- DEB_EE("dev:%p, buf:%p, sg_len:%d\n", dev, buf, length);
-
- if( 0 != IS_PLANAR(sfmt->trans)) {
- struct saa7146_pgtable *pt1 = &buf->pt[0];
- struct saa7146_pgtable *pt2 = &buf->pt[1];
- struct saa7146_pgtable *pt3 = &buf->pt[2];
- __le32 *ptr1, *ptr2, *ptr3;
- __le32 fill;
-
- int size = buf->fmt->width*buf->fmt->height;
- int i,p,m1,m2,m3,o1,o2;
-
- switch( sfmt->depth ) {
- case 12: {
- /* create some offsets inside the page table */
- m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1;
- m2 = ((size+(size/4)+PAGE_SIZE)/PAGE_SIZE)-1;
- m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
- o1 = size%PAGE_SIZE;
- o2 = (size+(size/4))%PAGE_SIZE;
- DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
- size, m1, m2, m3, o1, o2);
- break;
- }
- case 16: {
- /* create some offsets inside the page table */
- m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1;
- m2 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
- m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1;
- o1 = size%PAGE_SIZE;
- o2 = (size+(size/2))%PAGE_SIZE;
- DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
- size, m1, m2, m3, o1, o2);
- break;
- }
- default: {
- return -1;
- }
- }
-
- ptr1 = pt1->cpu;
- ptr2 = pt2->cpu;
- ptr3 = pt3->cpu;
-
- /* walk all pages, copy all page addresses to ptr1 */
- for (i = 0; i < length; i++, list++) {
- for (p = 0; p * 4096 < list->length; p++, ptr1++) {
- *ptr1 = cpu_to_le32(sg_dma_address(list) - list->offset);
- }
- }
-/*
- ptr1 = pt1->cpu;
- for(j=0;j<40;j++) {
- printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
- }
-*/
-
- /* if we have a user buffer, the first page may not be
- aligned to a page boundary. */
- pt1->offset = dma->sglist->offset;
- pt2->offset = pt1->offset+o1;
- pt3->offset = pt1->offset+o2;
-
- /* create video-dma2 page table */
- ptr1 = pt1->cpu;
- for(i = m1; i <= m2 ; i++, ptr2++) {
- *ptr2 = ptr1[i];
- }
- fill = *(ptr2-1);
- for(;i<1024;i++,ptr2++) {
- *ptr2 = fill;
- }
- /* create video-dma3 page table */
- ptr1 = pt1->cpu;
- for(i = m2; i <= m3; i++,ptr3++) {
- *ptr3 = ptr1[i];
- }
- fill = *(ptr3-1);
- for(;i<1024;i++,ptr3++) {
- *ptr3 = fill;
- }
- /* finally: finish up video-dma1 page table */
- ptr1 = pt1->cpu+m1;
- fill = pt1->cpu[m1];
- for(i=m1;i<1024;i++,ptr1++) {
- *ptr1 = fill;
- }
-/*
- ptr1 = pt1->cpu;
- ptr2 = pt2->cpu;
- ptr3 = pt3->cpu;
- for(j=0;j<40;j++) {
- printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
- }
- for(j=0;j<40;j++) {
- printk("ptr2 %d: 0x%08x\n",j,ptr2[j]);
- }
- for(j=0;j<40;j++) {
- printk("ptr3 %d: 0x%08x\n",j,ptr3[j]);
- }
-*/
- } else {
- struct saa7146_pgtable *pt = &buf->pt[0];
- return saa7146_pgtable_build_single(pci, pt, list, length);
- }
-
- return 0;
-}
-
-
-/********************************************************************************/
-/* file operations */
-
-static int video_begin(struct saa7146_fh *fh)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_format *fmt = NULL;
- unsigned int resource;
- int ret = 0, err = 0;
-
- DEB_EE("dev:%p, fh:%p\n", dev, fh);
-
- if ((vv->video_status & STATUS_CAPTURE) != 0) {
- if (vv->video_fh == fh) {
- DEB_S("already capturing\n");
- return 0;
- }
- DEB_S("already capturing in another open\n");
- return -EBUSY;
- }
-
- if ((vv->video_status & STATUS_OVERLAY) != 0) {
- DEB_S("warning: suspending overlay video for streaming capture\n");
- vv->ov_suspend = vv->video_fh;
- err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
- if (0 != err) {
- DEB_D("suspending video failed. aborting\n");
- return err;
- }
- }
-
- fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
- /* we need to have a valid format set here */
- BUG_ON(NULL == fmt);
-
- if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
- resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
- } else {
- resource = RESOURCE_DMA1_HPS;
- }
-
- ret = saa7146_res_get(fh, resource);
- if (0 == ret) {
- DEB_S("cannot get capture resource %d\n", resource);
- if (vv->ov_suspend != NULL) {
- saa7146_start_preview(vv->ov_suspend);
- vv->ov_suspend = NULL;
- }
- return -EBUSY;
- }
-
- /* clear out beginning of streaming bit (rps register 0)*/
- saa7146_write(dev, MC2, MASK_27 );
-
- /* enable rps0 irqs */
- SAA7146_IER_ENABLE(dev, MASK_27);
-
- vv->video_fh = fh;
- vv->video_status = STATUS_CAPTURE;
-
- return 0;
-}
-
-static int video_end(struct saa7146_fh *fh, struct file *file)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_format *fmt = NULL;
- unsigned long flags;
- unsigned int resource;
- u32 dmas = 0;
- DEB_EE("dev:%p, fh:%p\n", dev, fh);
-
- if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
- DEB_S("not capturing\n");
- return 0;
- }
-
- if (vv->video_fh != fh) {
- DEB_S("capturing, but in another open\n");
- return -EBUSY;
- }
-
- fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
- /* we need to have a valid format set here */
- BUG_ON(NULL == fmt);
-
- if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
- resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
- dmas = MASK_22 | MASK_21 | MASK_20;
- } else {
- resource = RESOURCE_DMA1_HPS;
- dmas = MASK_22;
- }
- spin_lock_irqsave(&dev->slock,flags);
-
- /* disable rps0 */
- saa7146_write(dev, MC1, MASK_28);
-
- /* disable rps0 irqs */
- SAA7146_IER_DISABLE(dev, MASK_27);
-
- /* shut down all used video dma transfers */
- saa7146_write(dev, MC1, dmas);
-
- spin_unlock_irqrestore(&dev->slock, flags);
-
- vv->video_fh = NULL;
- vv->video_status = 0;
-
- saa7146_res_free(fh, resource);
-
- if (vv->ov_suspend != NULL) {
- saa7146_start_preview(vv->ov_suspend);
- vv->ov_suspend = NULL;
- }
-
- return 0;
-}
-
-static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
-
- strcpy((char *)cap->driver, "saa7146 v4l2");
- strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card));
- sprintf((char *)cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cap->device_caps =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VIDEO_OVERLAY |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
- cap->device_caps |= dev->ext_vv_data->capabilities;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
- if (vdev->vfl_type == VFL_TYPE_GRABBER)
- cap->device_caps &=
- ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT);
- else
- cap->device_caps &=
- ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO);
- return 0;
-}
-
-static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- *fb = vv->ov_fb;
- fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
- fb->flags = V4L2_FBUF_FLAG_PRIMARY;
- return 0;
-}
-
-static int vidioc_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_format *fmt;
-
- DEB_EE("VIDIOC_S_FBUF\n");
-
- if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
- return -EPERM;
-
- /* check args */
- fmt = saa7146_format_by_fourcc(dev, fb->fmt.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
-
- /* planar formats are not allowed for overlay video, clipping and video dma would clash */
- if (fmt->flags & FORMAT_IS_PLANAR)
- DEB_S("planar pixelformat '%4.4s' not allowed for overlay\n",
- (char *)&fmt->pixelformat);
-
- /* check if overlay is running */
- if (IS_OVERLAY_ACTIVE(fh) != 0) {
- if (vv->video_fh != fh) {
- DEB_D("refusing to change framebuffer informations while overlay is active in another open\n");
- return -EBUSY;
- }
- }
-
- /* ok, accept it */
- vv->ov_fb = *fb;
- vv->ov_fmt = fmt;
-
- if (vv->ov_fb.fmt.bytesperline < vv->ov_fb.fmt.width) {
- vv->ov_fb.fmt.bytesperline = vv->ov_fb.fmt.width * fmt->depth / 8;
- DEB_D("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline);
- }
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
-{
- if (f->index >= NUM_FORMATS)
- return -EINVAL;
- strlcpy((char *)f->description, formats[f->index].name,
- sizeof(f->description));
- f->pixelformat = formats[f->index].pixelformat;
- return 0;
-}
-
-int saa7146_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct saa7146_dev *dev = container_of(ctrl->handler,
- struct saa7146_dev, ctrl_handler);
- struct saa7146_vv *vv = dev->vv_data;
- u32 val;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- val = saa7146_read(dev, BCS_CTRL);
- val &= 0x00ffffff;
- val |= (ctrl->val << 24);
- saa7146_write(dev, BCS_CTRL, val);
- saa7146_write(dev, MC2, MASK_22 | MASK_06);
- break;
-
- case V4L2_CID_CONTRAST:
- val = saa7146_read(dev, BCS_CTRL);
- val &= 0xff00ffff;
- val |= (ctrl->val << 16);
- saa7146_write(dev, BCS_CTRL, val);
- saa7146_write(dev, MC2, MASK_22 | MASK_06);
- break;
-
- case V4L2_CID_SATURATION:
- val = saa7146_read(dev, BCS_CTRL);
- val &= 0xffffff00;
- val |= (ctrl->val << 0);
- saa7146_write(dev, BCS_CTRL, val);
- saa7146_write(dev, MC2, MASK_22 | MASK_06);
- break;
-
- case V4L2_CID_HFLIP:
- /* fixme: we can support changing VFLIP and HFLIP here... */
- if ((vv->video_status & STATUS_CAPTURE))
- return -EBUSY;
- vv->hflip = ctrl->val;
- break;
-
- case V4L2_CID_VFLIP:
- if ((vv->video_status & STATUS_CAPTURE))
- return -EBUSY;
- vv->vflip = ctrl->val;
- break;
-
- default:
- return -EINVAL;
- }
-
- if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */
- struct saa7146_fh *fh = vv->video_fh;
-
- saa7146_stop_preview(fh);
- saa7146_start_preview(fh);
- }
- return 0;
-}
-
-static int vidioc_g_parm(struct file *file, void *fh,
- struct v4l2_streamparm *parm)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- parm->parm.capture.readbuffers = 1;
- v4l2_video_std_frame_period(vv->standard->id,
- &parm->parm.capture.timeperframe);
- return 0;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- f->fmt.pix = vv->video_fmt;
- return 0;
-}
-
-static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- f->fmt.win = vv->ov.win;
- return 0;
-}
-
-static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- f->fmt.vbi = vv->vbi_fmt;
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_format *fmt;
- enum v4l2_field field;
- int maxw, maxh;
- int calc_bpl;
-
- DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
-
- fmt = saa7146_format_by_fourcc(dev, f->fmt.pix.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
-
- field = f->fmt.pix.field;
- maxw = vv->standard->h_max_out;
- maxh = vv->standard->v_max_out;
-
- if (V4L2_FIELD_ANY == field) {
- field = (f->fmt.pix.height > maxh / 2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_BOTTOM;
- }
- switch (field) {
- case V4L2_FIELD_ALTERNATE:
- vv->last_field = V4L2_FIELD_TOP;
- maxh = maxh / 2;
- break;
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- vv->last_field = V4L2_FIELD_INTERLACED;
- maxh = maxh / 2;
- break;
- case V4L2_FIELD_INTERLACED:
- vv->last_field = V4L2_FIELD_INTERLACED;
- break;
- default:
- DEB_D("no known field mode '%d'\n", field);
- return -EINVAL;
- }
-
- f->fmt.pix.field = field;
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- if (f->fmt.pix.width > maxw)
- f->fmt.pix.width = maxw;
- if (f->fmt.pix.height > maxh)
- f->fmt.pix.height = maxh;
-
- calc_bpl = (f->fmt.pix.width * fmt->depth) / 8;
-
- if (f->fmt.pix.bytesperline < calc_bpl)
- f->fmt.pix.bytesperline = calc_bpl;
-
- if (f->fmt.pix.bytesperline > (2 * PAGE_SIZE * fmt->depth) / 8) /* arbitrary constraint */
- f->fmt.pix.bytesperline = calc_bpl;
-
- f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
- DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",
- f->fmt.pix.width, f->fmt.pix.height,
- f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
-
- return 0;
-}
-
-
-static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct v4l2_window *win = &f->fmt.win;
- enum v4l2_field field;
- int maxw, maxh;
-
- DEB_EE("dev:%p\n", dev);
-
- if (NULL == vv->ov_fb.base) {
- DEB_D("no fb base set\n");
- return -EINVAL;
- }
- if (NULL == vv->ov_fmt) {
- DEB_D("no fb fmt set\n");
- return -EINVAL;
- }
- if (win->w.width < 48 || win->w.height < 32) {
- DEB_D("min width/height. (%d,%d)\n",
- win->w.width, win->w.height);
- return -EINVAL;
- }
- if (win->clipcount > 16) {
- DEB_D("clipcount too big\n");
- return -EINVAL;
- }
-
- field = win->field;
- maxw = vv->standard->h_max_out;
- maxh = vv->standard->v_max_out;
-
- if (V4L2_FIELD_ANY == field) {
- field = (win->w.height > maxh / 2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_TOP;
- }
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- case V4L2_FIELD_ALTERNATE:
- maxh = maxh / 2;
- break;
- case V4L2_FIELD_INTERLACED:
- break;
- default:
- DEB_D("no known field mode '%d'\n", field);
- return -EINVAL;
- }
-
- win->field = field;
- if (win->w.width > maxw)
- win->w.width = maxw;
- if (win->w.height > maxh)
- win->w.height = maxh;
-
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f)
-{
- struct saa7146_fh *fh = __fh;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- int err;
-
- DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
- if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_EE("streaming capture is active\n");
- return -EBUSY;
- }
- err = vidioc_try_fmt_vid_cap(file, fh, f);
- if (0 != err)
- return err;
- vv->video_fmt = f->fmt.pix;
- DEB_EE("set to pixelformat '%4.4s'\n",
- (char *)&vv->video_fmt.pixelformat);
- return 0;
-}
-
-static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *f)
-{
- struct saa7146_fh *fh = __fh;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- int err;
-
- DEB_EE("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh);
- err = vidioc_try_fmt_vid_overlay(file, fh, f);
- if (0 != err)
- return err;
- vv->ov.win = f->fmt.win;
- vv->ov.nclips = f->fmt.win.clipcount;
- if (vv->ov.nclips > 16)
- vv->ov.nclips = 16;
- if (copy_from_user(vv->ov.clips, f->fmt.win.clips,
- sizeof(struct v4l2_clip) * vv->ov.nclips)) {
- return -EFAULT;
- }
-
- /* vv->ov.fh is used to indicate that we have valid overlay informations, too */
- vv->ov.fh = fh;
-
- /* check if our current overlay is active */
- if (IS_OVERLAY_ACTIVE(fh) != 0) {
- saa7146_stop_preview(fh);
- saa7146_start_preview(fh);
- }
- return 0;
-}
-
-static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- *norm = vv->standard->id;
- return 0;
-}
-
- /* the saa7146 supfhrts (used in conjunction with the saa7111a for example)
- PAL / NTSC / SECAM. if your hardware does not (or does more)
- -- override this function in your extension */
-/*
- case VIDIOC_ENUMSTD:
- {
- struct v4l2_standard *e = arg;
- if (e->index < 0 )
- return -EINVAL;
- if( e->index < dev->ext_vv_data->num_stds ) {
- DEB_EE("VIDIOC_ENUMSTD: index:%d\n", e->index);
- v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name);
- return 0;
- }
- return -EINVAL;
- }
- */
-
-static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
- int found = 0;
- int err, i;
-
- DEB_EE("VIDIOC_S_STD\n");
-
- if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) {
- DEB_D("cannot change video standard while streaming capture is active\n");
- return -EBUSY;
- }
-
- if ((vv->video_status & STATUS_OVERLAY) != 0) {
- vv->ov_suspend = vv->video_fh;
- err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
- if (0 != err) {
- DEB_D("suspending video failed. aborting\n");
- return err;
- }
- }
-
- for (i = 0; i < dev->ext_vv_data->num_stds; i++)
- if (*id & dev->ext_vv_data->stds[i].id)
- break;
- if (i != dev->ext_vv_data->num_stds) {
- vv->standard = &dev->ext_vv_data->stds[i];
- if (NULL != dev->ext_vv_data->std_callback)
- dev->ext_vv_data->std_callback(dev, vv->standard);
- found = 1;
- }
-
- if (vv->ov_suspend != NULL) {
- saa7146_start_preview(vv->ov_suspend);
- vv->ov_suspend = NULL;
- }
-
- if (!found) {
- DEB_EE("VIDIOC_S_STD: standard not found\n");
- return -EINVAL;
- }
-
- DEB_EE("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name);
- return 0;
-}
-
-static int vidioc_overlay(struct file *file, void *fh, unsigned int on)
-{
- int err;
-
- DEB_D("VIDIOC_OVERLAY on:%d\n", on);
- if (on)
- err = saa7146_start_preview(fh);
- else
- err = saa7146_stop_preview(fh);
- return err;
-}
-
-static int vidioc_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *b)
-{
- struct saa7146_fh *fh = __fh;
-
- if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return videobuf_reqbufs(&fh->video_q, b);
- if (b->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- return videobuf_reqbufs(&fh->vbi_q, b);
- return -EINVAL;
-}
-
-static int vidioc_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
-{
- struct saa7146_fh *fh = __fh;
-
- if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return videobuf_querybuf(&fh->video_q, buf);
- if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- return videobuf_querybuf(&fh->vbi_q, buf);
- return -EINVAL;
-}
-
-static int vidioc_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
-{
- struct saa7146_fh *fh = __fh;
-
- if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return videobuf_qbuf(&fh->video_q, buf);
- if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- return videobuf_qbuf(&fh->vbi_q, buf);
- return -EINVAL;
-}
-
-static int vidioc_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
-{
- struct saa7146_fh *fh = __fh;
-
- if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return videobuf_dqbuf(&fh->video_q, buf, file->f_flags & O_NONBLOCK);
- if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- return videobuf_dqbuf(&fh->vbi_q, buf, file->f_flags & O_NONBLOCK);
- return -EINVAL;
-}
-
-static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type type)
-{
- struct saa7146_fh *fh = __fh;
- int err;
-
- DEB_D("VIDIOC_STREAMON, type:%d\n", type);
-
- err = video_begin(fh);
- if (err)
- return err;
- if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return videobuf_streamon(&fh->video_q);
- if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
- return videobuf_streamon(&fh->vbi_q);
- return -EINVAL;
-}
-
-static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type)
-{
- struct saa7146_fh *fh = __fh;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- int err;
-
- DEB_D("VIDIOC_STREAMOFF, type:%d\n", type);
-
- /* ugly: we need to copy some checks from video_end(),
- because videobuf_streamoff() relies on the capture running.
- check and fix this */
- if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
- DEB_S("not capturing\n");
- return 0;
- }
-
- if (vv->video_fh != fh) {
- DEB_S("capturing, but in another open\n");
- return -EBUSY;
- }
-
- err = -EINVAL;
- if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- err = videobuf_streamoff(&fh->video_q);
- else if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
- err = videobuf_streamoff(&fh->vbi_q);
- if (0 != err) {
- DEB_D("warning: videobuf_streamoff() failed\n");
- video_end(fh, file);
- } else {
- err = video_end(fh, file);
- }
- return err;
-}
-
-static int vidioc_g_chip_ident(struct file *file, void *__fh,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct saa7146_fh *fh = __fh;
- struct saa7146_dev *dev = fh->dev;
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type == V4L2_CHIP_MATCH_HOST) {
- if (v4l2_chip_match_host(&chip->match))
- chip->ident = V4L2_IDENT_SAA7146;
- return 0;
- }
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
- return v4l2_device_call_until_err(&dev->v4l2_dev, 0,
- core, g_chip_ident, chip);
-}
-
-const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay,
- .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay,
- .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
-
- .vidioc_overlay = vidioc_overlay,
- .vidioc_g_fbuf = vidioc_g_fbuf,
- .vidioc_s_fbuf = vidioc_s_fbuf,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_parm = vidioc_g_parm,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
-
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_parm = vidioc_g_parm,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-/*********************************************************************************/
-/* buffer handling functions */
-
-static int buffer_activate (struct saa7146_dev *dev,
- struct saa7146_buf *buf,
- struct saa7146_buf *next)
-{
- struct saa7146_vv *vv = dev->vv_data;
-
- buf->vb.state = VIDEOBUF_ACTIVE;
- saa7146_set_capture(dev,buf,next);
-
- mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT);
- return 0;
-}
-
-static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf)
-{
- saa7146_pgtable_free(dev->pci, &buf->pt[0]);
- saa7146_pgtable_free(dev->pci, &buf->pt[1]);
- saa7146_pgtable_free(dev->pci, &buf->pt[2]);
-}
-
-static int buffer_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb, enum v4l2_field field)
-{
- struct file *file = q->priv_data;
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_buf *buf = (struct saa7146_buf *)vb;
- int size,err = 0;
-
- DEB_CAP("vbuf:%p\n", vb);
-
- /* sanity checks */
- if (vv->video_fmt.width < 48 ||
- vv->video_fmt.height < 32 ||
- vv->video_fmt.width > vv->standard->h_max_out ||
- vv->video_fmt.height > vv->standard->v_max_out) {
- DEB_D("w (%d) / h (%d) out of bounds\n",
- vv->video_fmt.width, vv->video_fmt.height);
- return -EINVAL;
- }
-
- size = vv->video_fmt.sizeimage;
- if (0 != buf->vb.baddr && buf->vb.bsize < size) {
- DEB_D("size mismatch\n");
- return -EINVAL;
- }
-
- DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n",
- vv->video_fmt.width, vv->video_fmt.height,
- size, v4l2_field_names[vv->video_fmt.field]);
- if (buf->vb.width != vv->video_fmt.width ||
- buf->vb.bytesperline != vv->video_fmt.bytesperline ||
- buf->vb.height != vv->video_fmt.height ||
- buf->vb.size != size ||
- buf->vb.field != field ||
- buf->vb.field != vv->video_fmt.field ||
- buf->fmt != &vv->video_fmt) {
- saa7146_dma_free(dev,q,buf);
- }
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- struct saa7146_format *sfmt;
-
- buf->vb.bytesperline = vv->video_fmt.bytesperline;
- buf->vb.width = vv->video_fmt.width;
- buf->vb.height = vv->video_fmt.height;
- buf->vb.size = size;
- buf->vb.field = field;
- buf->fmt = &vv->video_fmt;
- buf->vb.field = vv->video_fmt.field;
-
- sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
-
- release_all_pagetables(dev, buf);
- if( 0 != IS_PLANAR(sfmt->trans)) {
- saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
- saa7146_pgtable_alloc(dev->pci, &buf->pt[1]);
- saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
- } else {
- saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
- }
-
- err = videobuf_iolock(q,&buf->vb, &vv->ov_fb);
- if (err)
- goto oops;
- err = saa7146_pgtable_build(dev,buf);
- if (err)
- goto oops;
- }
- buf->vb.state = VIDEOBUF_PREPARED;
- buf->activate = buffer_activate;
-
- return 0;
-
- oops:
- DEB_D("error out\n");
- saa7146_dma_free(dev,q,buf);
-
- return err;
-}
-
-static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
-{
- struct file *file = q->priv_data;
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_vv *vv = fh->dev->vv_data;
-
- if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS)
- *count = MAX_SAA7146_CAPTURE_BUFFERS;
-
- *size = vv->video_fmt.sizeimage;
-
- /* check if we exceed the "max_memory" parameter */
- if( (*count * *size) > (max_memory*1048576) ) {
- *count = (max_memory*1048576) / *size;
- }
-
- DEB_CAP("%d buffers, %d bytes each\n", *count, *size);
-
- return 0;
-}
-
-static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct file *file = q->priv_data;
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_buf *buf = (struct saa7146_buf *)vb;
-
- DEB_CAP("vbuf:%p\n", vb);
- saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf);
-}
-
-static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct file *file = q->priv_data;
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_buf *buf = (struct saa7146_buf *)vb;
-
- DEB_CAP("vbuf:%p\n", vb);
-
- saa7146_dma_free(dev,q,buf);
-
- release_all_pagetables(dev, buf);
-}
-
-static struct videobuf_queue_ops video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-/********************************************************************************/
-/* file operations */
-
-static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
-{
- INIT_LIST_HEAD(&vv->video_dmaq.queue);
-
- init_timer(&vv->video_dmaq.timeout);
- vv->video_dmaq.timeout.function = saa7146_buffer_timeout;
- vv->video_dmaq.timeout.data = (unsigned long)(&vv->video_dmaq);
- vv->video_dmaq.dev = dev;
-
- /* set some default values */
- vv->standard = &dev->ext_vv_data->stds[0];
-
- /* FIXME: what's this? */
- vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A;
- vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A;
-}
-
-
-static int video_open(struct saa7146_dev *dev, struct file *file)
-{
- struct saa7146_fh *fh = file->private_data;
-
- videobuf_queue_sg_init(&fh->video_q, &video_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct saa7146_buf),
- file, &dev->v4l2_lock);
-
- return 0;
-}
-
-
-static void video_close(struct saa7146_dev *dev, struct file *file)
-{
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_vv *vv = dev->vv_data;
- struct videobuf_queue *q = &fh->video_q;
-
- if (IS_CAPTURE_ACTIVE(fh) != 0)
- video_end(fh, file);
- else if (IS_OVERLAY_ACTIVE(fh) != 0)
- saa7146_stop_preview(fh);
-
- videobuf_stop(q);
- /* hmm, why is this function declared void? */
-}
-
-
-static void video_irq_done(struct saa7146_dev *dev, unsigned long st)
-{
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_dmaqueue *q = &vv->video_dmaq;
-
- spin_lock(&dev->slock);
- DEB_CAP("called\n");
-
- /* only finish the buffer if we have one... */
- if( NULL != q->curr ) {
- saa7146_buffer_finish(dev,q,VIDEOBUF_DONE);
- }
- saa7146_buffer_next(dev,q,0);
-
- spin_unlock(&dev->slock);
-}
-
-static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- ssize_t ret = 0;
-
- DEB_EE("called\n");
-
- if ((vv->video_status & STATUS_CAPTURE) != 0) {
- /* fixme: should we allow read() captures while streaming capture? */
- if (vv->video_fh == fh) {
- DEB_S("already capturing\n");
- return -EBUSY;
- }
- DEB_S("already capturing in another open\n");
- return -EBUSY;
- }
-
- ret = video_begin(fh);
- if( 0 != ret) {
- goto out;
- }
-
- ret = videobuf_read_one(&fh->video_q , data, count, ppos,
- file->f_flags & O_NONBLOCK);
- if (ret != 0) {
- video_end(fh, file);
- } else {
- ret = video_end(fh, file);
- }
-out:
- /* restart overlay if it was active before */
- if (vv->ov_suspend != NULL) {
- saa7146_start_preview(vv->ov_suspend);
- vv->ov_suspend = NULL;
- }
-
- return ret;
-}
-
-struct saa7146_use_ops saa7146_video_uops = {
- .init = video_init,
- .open = video_open,
- .release = video_close,
- .irq_done = video_irq_done,
- .read = video_read,
-};
diff --git a/drivers/media/common/siano/Kconfig b/drivers/media/common/siano/Kconfig
new file mode 100644
index 000000000000..37fa6597b407
--- /dev/null
+++ b/drivers/media/common/siano/Kconfig
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Siano Mobile Silicon Digital TV device configuration
+#
+
+config SMS_SIANO_MDTV
+ tristate
+ depends on DVB_CORE && HAS_DMA
+ depends on !RC_CORE || RC_CORE
+ depends on SMS_USB_DRV || SMS_SDIO_DRV
+ default y
+
+config SMS_SIANO_RC
+ bool "Enable Remote Controller support for Siano devices"
+ depends on SMS_SIANO_MDTV && RC_CORE
+ depends on SMS_USB_DRV || SMS_SDIO_DRV
+ depends on MEDIA_COMMON_OPTIONS
+ default y
+ help
+ Choose Y to select Remote Controller support for Siano driver.
+
+config SMS_SIANO_DEBUGFS
+ bool "Enable debugfs for smsdvb"
+ depends on SMS_SIANO_MDTV
+ depends on DEBUG_FS
+ depends on SMS_USB_DRV = SMS_SDIO_DRV
+
+ help
+ Choose Y to enable visualizing a dump of the frontend
+ statistics response packets via debugfs. Currently, works
+ only with Siano USB devices.
+
+ Useful only for developers. In doubt, say N.
diff --git a/drivers/media/common/siano/Makefile b/drivers/media/common/siano/Makefile
new file mode 100644
index 000000000000..b33022e0be56
--- /dev/null
+++ b/drivers/media/common/siano/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o
+smsdvb-objs := smsdvb-main.o
+
+obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o
+
+ifeq ($(CONFIG_SMS_SIANO_RC),y)
+ smsmdtv-objs += smsir.o
+endif
+
+ifeq ($(CONFIG_SMS_SIANO_DEBUGFS),y)
+ smsdvb-objs += smsdvb-debugfs.o
+endif
diff --git a/drivers/media/common/siano/sms-cards.c b/drivers/media/common/siano/sms-cards.c
new file mode 100644
index 000000000000..d4a116ab6c88
--- /dev/null
+++ b/drivers/media/common/siano/sms-cards.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Card-specific functions for the Siano SMS1xxx USB dongle
+ *
+ * Copyright (c) 2008 Michael Krufky <mkrufky@linuxtv.org>
+ */
+
+#include "sms-cards.h"
+#include "smsir.h"
+#include <linux/module.h>
+
+static struct sms_board sms_boards[] = {
+ [SMS_BOARD_UNKNOWN] = {
+ .name = "Unknown board",
+ .type = SMS_UNKNOWN_TYPE,
+ .default_mode = DEVICE_MODE_NONE,
+ },
+ [SMS1XXX_BOARD_SIANO_STELLAR] = {
+ .name = "Siano Stellar Digital Receiver",
+ .type = SMS_STELLAR,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+ [SMS1XXX_BOARD_SIANO_NOVA_A] = {
+ .name = "Siano Nova A Digital Receiver",
+ .type = SMS_NOVA_A0,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+ [SMS1XXX_BOARD_SIANO_NOVA_B] = {
+ .name = "Siano Nova B Digital Receiver",
+ .type = SMS_NOVA_B0,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+ [SMS1XXX_BOARD_SIANO_VEGA] = {
+ .name = "Siano Vega Digital Receiver",
+ .type = SMS_VEGA,
+ .default_mode = DEVICE_MODE_CMMB,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT] = {
+ .name = "Hauppauge Catamount",
+ .type = SMS_STELLAR,
+ .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_STELLAR,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A] = {
+ .name = "Hauppauge Okemo-A",
+ .type = SMS_NOVA_A0,
+ .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_NOVA_A,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B] = {
+ .name = "Hauppauge Okemo-B",
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_NOVA_B,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = {
+ .name = "Hauppauge WinTV MiniStick",
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_HCW_55XXX,
+ .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_HCW_55XXX,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ .rc_codes = RC_MAP_HAUPPAUGE,
+ .board_cfg.leds_power = 26,
+ .board_cfg.led0 = 27,
+ .board_cfg.led1 = 28,
+ .board_cfg.ir = 9,
+ .led_power = 26,
+ .led_lo = 27,
+ .led_hi = 28,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD] = {
+ .name = "Hauppauge WinTV MiniCard",
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_HCW_55XXX,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ .lna_ctrl = 29,
+ .board_cfg.foreign_lna0_ctrl = 29,
+ .rf_switch = 17,
+ .board_cfg.rf_switch_uhf = 17,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2] = {
+ .name = "Hauppauge WinTV MiniCard Rev 2",
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_HCW_55XXX,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ .lna_ctrl = -1,
+ },
+ [SMS1XXX_BOARD_SIANO_NICE] = {
+ .name = "Siano Nice Digital Receiver",
+ .type = SMS_NOVA_B0,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+ [SMS1XXX_BOARD_SIANO_VENICE] = {
+ .name = "Siano Venice Digital Receiver",
+ .type = SMS_VEGA,
+ .default_mode = DEVICE_MODE_CMMB,
+ },
+ [SMS1XXX_BOARD_SIANO_STELLAR_ROM] = {
+ .name = "Siano Stellar Digital Receiver ROM",
+ .type = SMS_STELLAR,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ .intf_num = 1,
+ },
+ [SMS1XXX_BOARD_ZTE_DVB_DATA_CARD] = {
+ .name = "ZTE Data Card Digital Receiver",
+ .type = SMS_NOVA_B0,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ .intf_num = 5,
+ .mtu = 15792,
+ },
+ [SMS1XXX_BOARD_ONDA_MDTV_DATA_CARD] = {
+ .name = "ONDA Data Card Digital Receiver",
+ .type = SMS_NOVA_B0,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ .intf_num = 6,
+ .mtu = 15792,
+ },
+ [SMS1XXX_BOARD_SIANO_MING] = {
+ .name = "Siano Ming Digital Receiver",
+ .type = SMS_MING,
+ .default_mode = DEVICE_MODE_CMMB,
+ },
+ [SMS1XXX_BOARD_SIANO_PELE] = {
+ .name = "Siano Pele Digital Receiver",
+ .type = SMS_PELE,
+ .default_mode = DEVICE_MODE_ISDBT_BDA,
+ },
+ [SMS1XXX_BOARD_SIANO_RIO] = {
+ .name = "Siano Rio Digital Receiver",
+ .type = SMS_RIO,
+ .default_mode = DEVICE_MODE_ISDBT_BDA,
+ },
+ [SMS1XXX_BOARD_SIANO_DENVER_1530] = {
+ .name = "Siano Denver (ATSC-M/H) Digital Receiver",
+ .type = SMS_DENVER_1530,
+ .default_mode = DEVICE_MODE_ATSC,
+ .crystal = 2400,
+ },
+ [SMS1XXX_BOARD_SIANO_DENVER_2160] = {
+ .name = "Siano Denver (TDMB) Digital Receiver",
+ .type = SMS_DENVER_2160,
+ .default_mode = DEVICE_MODE_DAB_TDMB,
+ },
+ [SMS1XXX_BOARD_PCTV_77E] = {
+ .name = "Hauppauge microStick 77e",
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVB_NOVA_12MHZ_B0,
+ .default_mode = DEVICE_MODE_DVBT_BDA,
+ },
+};
+
+struct sms_board *sms_get_board(unsigned id)
+{
+ BUG_ON(id >= ARRAY_SIZE(sms_boards));
+
+ return &sms_boards[id];
+}
+EXPORT_SYMBOL_GPL(sms_get_board);
+static inline void sms_gpio_assign_11xx_default_led_config(
+ struct smscore_config_gpio *p_gpio_config) {
+ p_gpio_config->direction = SMS_GPIO_DIRECTION_OUTPUT;
+ p_gpio_config->inputcharacteristics =
+ SMS_GPIO_INPUTCHARACTERISTICS_NORMAL;
+ p_gpio_config->outputdriving = SMS_GPIO_OUTPUTDRIVING_4mA;
+ p_gpio_config->outputslewrate = SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS;
+ p_gpio_config->pullupdown = SMS_GPIO_PULLUPDOWN_NONE;
+}
+
+int sms_board_event(struct smscore_device_t *coredev,
+ enum SMS_BOARD_EVENTS gevent)
+{
+ struct smscore_config_gpio my_gpio_config;
+
+ sms_gpio_assign_11xx_default_led_config(&my_gpio_config);
+
+ switch (gevent) {
+ case BOARD_EVENT_POWER_INIT: /* including hotplug */
+ break; /* BOARD_EVENT_BIND */
+
+ case BOARD_EVENT_POWER_SUSPEND:
+ break; /* BOARD_EVENT_POWER_SUSPEND */
+
+ case BOARD_EVENT_POWER_RESUME:
+ break; /* BOARD_EVENT_POWER_RESUME */
+
+ case BOARD_EVENT_BIND:
+ break; /* BOARD_EVENT_BIND */
+
+ case BOARD_EVENT_SCAN_PROG:
+ break; /* BOARD_EVENT_SCAN_PROG */
+ case BOARD_EVENT_SCAN_COMP:
+ break; /* BOARD_EVENT_SCAN_COMP */
+ case BOARD_EVENT_EMERGENCY_WARNING_SIGNAL:
+ break; /* BOARD_EVENT_EMERGENCY_WARNING_SIGNAL */
+ case BOARD_EVENT_FE_LOCK:
+ break; /* BOARD_EVENT_FE_LOCK */
+ case BOARD_EVENT_FE_UNLOCK:
+ break; /* BOARD_EVENT_FE_UNLOCK */
+ case BOARD_EVENT_DEMOD_LOCK:
+ break; /* BOARD_EVENT_DEMOD_LOCK */
+ case BOARD_EVENT_DEMOD_UNLOCK:
+ break; /* BOARD_EVENT_DEMOD_UNLOCK */
+ case BOARD_EVENT_RECEPTION_MAX_4:
+ break; /* BOARD_EVENT_RECEPTION_MAX_4 */
+ case BOARD_EVENT_RECEPTION_3:
+ break; /* BOARD_EVENT_RECEPTION_3 */
+ case BOARD_EVENT_RECEPTION_2:
+ break; /* BOARD_EVENT_RECEPTION_2 */
+ case BOARD_EVENT_RECEPTION_1:
+ break; /* BOARD_EVENT_RECEPTION_1 */
+ case BOARD_EVENT_RECEPTION_LOST_0:
+ break; /* BOARD_EVENT_RECEPTION_LOST_0 */
+ case BOARD_EVENT_MULTIPLEX_OK:
+ break; /* BOARD_EVENT_MULTIPLEX_OK */
+ case BOARD_EVENT_MULTIPLEX_ERRORS:
+ break; /* BOARD_EVENT_MULTIPLEX_ERRORS */
+
+ default:
+ pr_err("Unknown SMS board event\n");
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sms_board_event);
+
+static int sms_set_gpio(struct smscore_device_t *coredev, int pin, int enable)
+{
+ int lvl, ret;
+ u32 gpio;
+ struct smscore_config_gpio gpioconfig = {
+ .direction = SMS_GPIO_DIRECTION_OUTPUT,
+ .pullupdown = SMS_GPIO_PULLUPDOWN_NONE,
+ .inputcharacteristics = SMS_GPIO_INPUTCHARACTERISTICS_NORMAL,
+ .outputslewrate = SMS_GPIO_OUTPUT_SLEW_RATE_FAST,
+ .outputdriving = SMS_GPIO_OUTPUTDRIVING_S_4mA,
+ };
+
+ if (pin == 0)
+ return -EINVAL;
+
+ if (pin < 0) {
+ /* inverted gpio */
+ gpio = pin * -1;
+ lvl = enable ? 0 : 1;
+ } else {
+ gpio = pin;
+ lvl = enable ? 1 : 0;
+ }
+
+ ret = smscore_configure_gpio(coredev, gpio, &gpioconfig);
+ if (ret < 0)
+ return ret;
+
+ return smscore_set_gpio(coredev, gpio, lvl);
+}
+
+int sms_board_setup(struct smscore_device_t *coredev)
+{
+ int board_id = smscore_get_board_id(coredev);
+ struct sms_board *board = sms_get_board(board_id);
+
+ switch (board_id) {
+ case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
+ /* turn off all LEDs */
+ sms_set_gpio(coredev, board->led_power, 0);
+ sms_set_gpio(coredev, board->led_hi, 0);
+ sms_set_gpio(coredev, board->led_lo, 0);
+ break;
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
+ /* turn off LNA */
+ sms_set_gpio(coredev, board->lna_ctrl, 0);
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sms_board_setup);
+
+int sms_board_power(struct smscore_device_t *coredev, int onoff)
+{
+ int board_id = smscore_get_board_id(coredev);
+ struct sms_board *board = sms_get_board(board_id);
+
+ switch (board_id) {
+ case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
+ /* power LED */
+ sms_set_gpio(coredev,
+ board->led_power, onoff ? 1 : 0);
+ break;
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
+ /* LNA */
+ if (!onoff)
+ sms_set_gpio(coredev, board->lna_ctrl, 0);
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sms_board_power);
+
+int sms_board_led_feedback(struct smscore_device_t *coredev, int led)
+{
+ int board_id = smscore_get_board_id(coredev);
+ struct sms_board *board = sms_get_board(board_id);
+
+ /* don't touch GPIO if LEDs are already set */
+ if (smscore_led_state(coredev, -1) == led)
+ return 0;
+
+ switch (board_id) {
+ case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
+ sms_set_gpio(coredev,
+ board->led_lo, (led & SMS_LED_LO) ? 1 : 0);
+ sms_set_gpio(coredev,
+ board->led_hi, (led & SMS_LED_HI) ? 1 : 0);
+
+ smscore_led_state(coredev, led);
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sms_board_led_feedback);
+
+int sms_board_lna_control(struct smscore_device_t *coredev, int onoff)
+{
+ int board_id = smscore_get_board_id(coredev);
+ struct sms_board *board = sms_get_board(board_id);
+
+ pr_debug("%s: LNA %s\n", __func__, onoff ? "enabled" : "disabled");
+
+ switch (board_id) {
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
+ sms_set_gpio(coredev,
+ board->rf_switch, onoff ? 1 : 0);
+ return sms_set_gpio(coredev,
+ board->lna_ctrl, onoff ? 1 : 0);
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(sms_board_lna_control);
+
+int sms_board_load_modules(int id)
+{
+ request_module("smsdvb");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sms_board_load_modules);
diff --git a/drivers/media/dvb/siano/sms-cards.h b/drivers/media/common/siano/sms-cards.h
index d8cdf756f7cf..028c5cb435bc 100644
--- a/drivers/media/dvb/siano/sms-cards.h
+++ b/drivers/media/common/siano/sms-cards.h
@@ -1,27 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Card-specific functions for the Siano SMS1xxx USB dongle
*
* Copyright (c) 2008 Michael Krufky <mkrufky@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation;
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- *
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __SMS_CARDS_H__
#define __SMS_CARDS_H__
-#include <linux/usb.h>
#include "smscoreapi.h"
+
+#include <linux/usb.h>
#include "smsir.h"
#define SMS_BOARD_UNKNOWN 0
@@ -37,6 +26,15 @@
#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 10
#define SMS1XXX_BOARD_SIANO_NICE 11
#define SMS1XXX_BOARD_SIANO_VENICE 12
+#define SMS1XXX_BOARD_SIANO_STELLAR_ROM 13
+#define SMS1XXX_BOARD_ZTE_DVB_DATA_CARD 14
+#define SMS1XXX_BOARD_ONDA_MDTV_DATA_CARD 15
+#define SMS1XXX_BOARD_SIANO_MING 16
+#define SMS1XXX_BOARD_SIANO_PELE 17
+#define SMS1XXX_BOARD_SIANO_RIO 18
+#define SMS1XXX_BOARD_SIANO_DENVER_1530 19
+#define SMS1XXX_BOARD_SIANO_DENVER_2160 20
+#define SMS1XXX_BOARD_PCTV_77E 21
struct sms_board_gpio_cfg {
int lna_vhf_exist;
@@ -79,6 +77,12 @@ struct sms_board {
/* gpios */
int led_power, led_hi, led_lo, lna_ctrl, rf_switch;
+
+ char intf_num;
+ int default_mode;
+ unsigned int mtu;
+ unsigned int crystal;
+ struct sms_antenna_config_ST *antenna_config;
};
struct sms_board *sms_get_board(unsigned id);
diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c
new file mode 100644
index 000000000000..3732367e0c62
--- /dev/null
+++ b/drivers/media/common/siano/smscoreapi.c
@@ -0,0 +1,2178 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Siano core API module
+ *
+ * This file contains implementation for the interface to sms core component
+ *
+ * author: Uri Shkolnik
+ *
+ * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
+ */
+
+#include "smscoreapi.h"
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <linux/firmware.h>
+#include <linux/wait.h>
+#include <asm/byteorder.h>
+
+#include "sms-cards.h"
+#include "smsir.h"
+
+struct smscore_device_notifyee_t {
+ struct list_head entry;
+ hotplug_t hotplug;
+};
+
+struct smscore_idlist_t {
+ struct list_head entry;
+ int id;
+ int data_type;
+};
+
+struct smscore_client_t {
+ struct list_head entry;
+ struct smscore_device_t *coredev;
+ void *context;
+ struct list_head idlist;
+ onresponse_t onresponse_handler;
+ onremove_t onremove_handler;
+};
+
+static char *siano_msgs[] = {
+ [MSG_TYPE_BASE_VAL - MSG_TYPE_BASE_VAL] = "MSG_TYPE_BASE_VAL",
+ [MSG_SMS_GET_VERSION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_VERSION_REQ",
+ [MSG_SMS_GET_VERSION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_VERSION_RES",
+ [MSG_SMS_MULTI_BRIDGE_CFG - MSG_TYPE_BASE_VAL] = "MSG_SMS_MULTI_BRIDGE_CFG",
+ [MSG_SMS_GPIO_CONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_CONFIG_REQ",
+ [MSG_SMS_GPIO_CONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_CONFIG_RES",
+ [MSG_SMS_GPIO_SET_LEVEL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_SET_LEVEL_REQ",
+ [MSG_SMS_GPIO_SET_LEVEL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_SET_LEVEL_RES",
+ [MSG_SMS_GPIO_GET_LEVEL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_GET_LEVEL_REQ",
+ [MSG_SMS_GPIO_GET_LEVEL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_GET_LEVEL_RES",
+ [MSG_SMS_EEPROM_BURN_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_EEPROM_BURN_IND",
+ [MSG_SMS_LOG_ENABLE_CHANGE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOG_ENABLE_CHANGE_REQ",
+ [MSG_SMS_LOG_ENABLE_CHANGE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOG_ENABLE_CHANGE_RES",
+ [MSG_SMS_SET_MAX_TX_MSG_LEN_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_MAX_TX_MSG_LEN_REQ",
+ [MSG_SMS_SET_MAX_TX_MSG_LEN_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_MAX_TX_MSG_LEN_RES",
+ [MSG_SMS_SPI_HALFDUPLEX_TOKEN_HOST_TO_DEVICE - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_HALFDUPLEX_TOKEN_HOST_TO_DEVICE",
+ [MSG_SMS_SPI_HALFDUPLEX_TOKEN_DEVICE_TO_HOST - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_HALFDUPLEX_TOKEN_DEVICE_TO_HOST",
+ [MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_REQ",
+ [MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_RES",
+ [MSG_SMS_BACKGROUND_SCAN_SIGNAL_DETECTED_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_BACKGROUND_SCAN_SIGNAL_DETECTED_IND",
+ [MSG_SMS_BACKGROUND_SCAN_NO_SIGNAL_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_BACKGROUND_SCAN_NO_SIGNAL_IND",
+ [MSG_SMS_CONFIGURE_RF_SWITCH_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CONFIGURE_RF_SWITCH_REQ",
+ [MSG_SMS_CONFIGURE_RF_SWITCH_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CONFIGURE_RF_SWITCH_RES",
+ [MSG_SMS_MRC_PATH_DISCONNECT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_PATH_DISCONNECT_REQ",
+ [MSG_SMS_MRC_PATH_DISCONNECT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_PATH_DISCONNECT_RES",
+ [MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_REQ",
+ [MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_RES",
+ [MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_REQ",
+ [MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_RES",
+ [MSG_WR_REG_RFT_REQ - MSG_TYPE_BASE_VAL] = "MSG_WR_REG_RFT_REQ",
+ [MSG_WR_REG_RFT_RES - MSG_TYPE_BASE_VAL] = "MSG_WR_REG_RFT_RES",
+ [MSG_RD_REG_RFT_REQ - MSG_TYPE_BASE_VAL] = "MSG_RD_REG_RFT_REQ",
+ [MSG_RD_REG_RFT_RES - MSG_TYPE_BASE_VAL] = "MSG_RD_REG_RFT_RES",
+ [MSG_RD_REG_ALL_RFT_REQ - MSG_TYPE_BASE_VAL] = "MSG_RD_REG_ALL_RFT_REQ",
+ [MSG_RD_REG_ALL_RFT_RES - MSG_TYPE_BASE_VAL] = "MSG_RD_REG_ALL_RFT_RES",
+ [MSG_HELP_INT - MSG_TYPE_BASE_VAL] = "MSG_HELP_INT",
+ [MSG_RUN_SCRIPT_INT - MSG_TYPE_BASE_VAL] = "MSG_RUN_SCRIPT_INT",
+ [MSG_SMS_EWS_INBAND_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EWS_INBAND_REQ",
+ [MSG_SMS_EWS_INBAND_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EWS_INBAND_RES",
+ [MSG_SMS_RFS_SELECT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RFS_SELECT_REQ",
+ [MSG_SMS_RFS_SELECT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RFS_SELECT_RES",
+ [MSG_SMS_MB_GET_VER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_GET_VER_REQ",
+ [MSG_SMS_MB_GET_VER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_GET_VER_RES",
+ [MSG_SMS_MB_WRITE_CFGFILE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_WRITE_CFGFILE_REQ",
+ [MSG_SMS_MB_WRITE_CFGFILE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_WRITE_CFGFILE_RES",
+ [MSG_SMS_MB_READ_CFGFILE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_READ_CFGFILE_REQ",
+ [MSG_SMS_MB_READ_CFGFILE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_READ_CFGFILE_RES",
+ [MSG_SMS_RD_MEM_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RD_MEM_REQ",
+ [MSG_SMS_RD_MEM_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RD_MEM_RES",
+ [MSG_SMS_WR_MEM_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_WR_MEM_REQ",
+ [MSG_SMS_WR_MEM_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_WR_MEM_RES",
+ [MSG_SMS_UPDATE_MEM_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_UPDATE_MEM_REQ",
+ [MSG_SMS_UPDATE_MEM_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_UPDATE_MEM_RES",
+ [MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_REQ",
+ [MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_RES",
+ [MSG_SMS_RF_TUNE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RF_TUNE_REQ",
+ [MSG_SMS_RF_TUNE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RF_TUNE_RES",
+ [MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_REQ",
+ [MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_RES",
+ [MSG_SMS_ISDBT_SB_RECEPTION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_SB_RECEPTION_REQ",
+ [MSG_SMS_ISDBT_SB_RECEPTION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_SB_RECEPTION_RES",
+ [MSG_SMS_GENERIC_EPROM_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_EPROM_WRITE_REQ",
+ [MSG_SMS_GENERIC_EPROM_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_EPROM_WRITE_RES",
+ [MSG_SMS_GENERIC_EPROM_READ_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_EPROM_READ_REQ",
+ [MSG_SMS_GENERIC_EPROM_READ_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_EPROM_READ_RES",
+ [MSG_SMS_EEPROM_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EEPROM_WRITE_REQ",
+ [MSG_SMS_EEPROM_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EEPROM_WRITE_RES",
+ [MSG_SMS_CUSTOM_READ_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CUSTOM_READ_REQ",
+ [MSG_SMS_CUSTOM_READ_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CUSTOM_READ_RES",
+ [MSG_SMS_CUSTOM_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CUSTOM_WRITE_REQ",
+ [MSG_SMS_CUSTOM_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CUSTOM_WRITE_RES",
+ [MSG_SMS_INIT_DEVICE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_INIT_DEVICE_REQ",
+ [MSG_SMS_INIT_DEVICE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_INIT_DEVICE_RES",
+ [MSG_SMS_ATSC_SET_ALL_IP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_SET_ALL_IP_REQ",
+ [MSG_SMS_ATSC_SET_ALL_IP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_SET_ALL_IP_RES",
+ [MSG_SMS_ATSC_START_ENSEMBLE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_START_ENSEMBLE_REQ",
+ [MSG_SMS_ATSC_START_ENSEMBLE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_START_ENSEMBLE_RES",
+ [MSG_SMS_SET_OUTPUT_MODE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_OUTPUT_MODE_REQ",
+ [MSG_SMS_SET_OUTPUT_MODE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_OUTPUT_MODE_RES",
+ [MSG_SMS_ATSC_IP_FILTER_GET_LIST_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_GET_LIST_REQ",
+ [MSG_SMS_ATSC_IP_FILTER_GET_LIST_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_GET_LIST_RES",
+ [MSG_SMS_SUB_CHANNEL_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SUB_CHANNEL_START_REQ",
+ [MSG_SMS_SUB_CHANNEL_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SUB_CHANNEL_START_RES",
+ [MSG_SMS_SUB_CHANNEL_STOP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SUB_CHANNEL_STOP_REQ",
+ [MSG_SMS_SUB_CHANNEL_STOP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SUB_CHANNEL_STOP_RES",
+ [MSG_SMS_ATSC_IP_FILTER_ADD_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_ADD_REQ",
+ [MSG_SMS_ATSC_IP_FILTER_ADD_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_ADD_RES",
+ [MSG_SMS_ATSC_IP_FILTER_REMOVE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_REMOVE_REQ",
+ [MSG_SMS_ATSC_IP_FILTER_REMOVE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_REMOVE_RES",
+ [MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_REQ",
+ [MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_RES",
+ [MSG_SMS_WAIT_CMD - MSG_TYPE_BASE_VAL] = "MSG_SMS_WAIT_CMD",
+ [MSG_SMS_ADD_PID_FILTER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ADD_PID_FILTER_REQ",
+ [MSG_SMS_ADD_PID_FILTER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ADD_PID_FILTER_RES",
+ [MSG_SMS_REMOVE_PID_FILTER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_REMOVE_PID_FILTER_REQ",
+ [MSG_SMS_REMOVE_PID_FILTER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_REMOVE_PID_FILTER_RES",
+ [MSG_SMS_FAST_INFORMATION_CHANNEL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_FAST_INFORMATION_CHANNEL_REQ",
+ [MSG_SMS_FAST_INFORMATION_CHANNEL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_FAST_INFORMATION_CHANNEL_RES",
+ [MSG_SMS_DAB_CHANNEL - MSG_TYPE_BASE_VAL] = "MSG_SMS_DAB_CHANNEL",
+ [MSG_SMS_GET_PID_FILTER_LIST_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_PID_FILTER_LIST_REQ",
+ [MSG_SMS_GET_PID_FILTER_LIST_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_PID_FILTER_LIST_RES",
+ [MSG_SMS_POWER_DOWN_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_DOWN_REQ",
+ [MSG_SMS_POWER_DOWN_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_DOWN_RES",
+ [MSG_SMS_ATSC_SLT_EXIST_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_SLT_EXIST_IND",
+ [MSG_SMS_ATSC_NO_SLT_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_ATSC_NO_SLT_IND",
+ [MSG_SMS_GET_STATISTICS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_STATISTICS_REQ",
+ [MSG_SMS_GET_STATISTICS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_STATISTICS_RES",
+ [MSG_SMS_SEND_DUMP - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_DUMP",
+ [MSG_SMS_SCAN_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_START_REQ",
+ [MSG_SMS_SCAN_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_START_RES",
+ [MSG_SMS_SCAN_STOP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_STOP_REQ",
+ [MSG_SMS_SCAN_STOP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_STOP_RES",
+ [MSG_SMS_SCAN_PROGRESS_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_PROGRESS_IND",
+ [MSG_SMS_SCAN_COMPLETE_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SCAN_COMPLETE_IND",
+ [MSG_SMS_LOG_ITEM - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOG_ITEM",
+ [MSG_SMS_DAB_SUBCHANNEL_RECONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DAB_SUBCHANNEL_RECONFIG_REQ",
+ [MSG_SMS_DAB_SUBCHANNEL_RECONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DAB_SUBCHANNEL_RECONFIG_RES",
+ [MSG_SMS_HO_PER_SLICES_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_PER_SLICES_IND",
+ [MSG_SMS_HO_INBAND_POWER_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_INBAND_POWER_IND",
+ [MSG_SMS_MANUAL_DEMOD_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MANUAL_DEMOD_REQ",
+ [MSG_SMS_HO_TUNE_ON_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_TUNE_ON_REQ",
+ [MSG_SMS_HO_TUNE_ON_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_TUNE_ON_RES",
+ [MSG_SMS_HO_TUNE_OFF_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_TUNE_OFF_REQ",
+ [MSG_SMS_HO_TUNE_OFF_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_TUNE_OFF_RES",
+ [MSG_SMS_HO_PEEK_FREQ_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_PEEK_FREQ_REQ",
+ [MSG_SMS_HO_PEEK_FREQ_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_PEEK_FREQ_RES",
+ [MSG_SMS_HO_PEEK_FREQ_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_HO_PEEK_FREQ_IND",
+ [MSG_SMS_MB_ATTEN_SET_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_ATTEN_SET_REQ",
+ [MSG_SMS_MB_ATTEN_SET_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MB_ATTEN_SET_RES",
+ [MSG_SMS_ENABLE_STAT_IN_I2C_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENABLE_STAT_IN_I2C_REQ",
+ [MSG_SMS_ENABLE_STAT_IN_I2C_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENABLE_STAT_IN_I2C_RES",
+ [MSG_SMS_SET_ANTENNA_CONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_ANTENNA_CONFIG_REQ",
+ [MSG_SMS_SET_ANTENNA_CONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_ANTENNA_CONFIG_RES",
+ [MSG_SMS_GET_STATISTICS_EX_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_STATISTICS_EX_REQ",
+ [MSG_SMS_GET_STATISTICS_EX_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_STATISTICS_EX_RES",
+ [MSG_SMS_SLEEP_RESUME_COMP_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SLEEP_RESUME_COMP_IND",
+ [MSG_SMS_SWITCH_HOST_INTERFACE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWITCH_HOST_INTERFACE_REQ",
+ [MSG_SMS_SWITCH_HOST_INTERFACE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWITCH_HOST_INTERFACE_RES",
+ [MSG_SMS_DATA_DOWNLOAD_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_DOWNLOAD_REQ",
+ [MSG_SMS_DATA_DOWNLOAD_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_DOWNLOAD_RES",
+ [MSG_SMS_DATA_VALIDITY_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_VALIDITY_REQ",
+ [MSG_SMS_DATA_VALIDITY_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_VALIDITY_RES",
+ [MSG_SMS_SWDOWNLOAD_TRIGGER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWDOWNLOAD_TRIGGER_REQ",
+ [MSG_SMS_SWDOWNLOAD_TRIGGER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWDOWNLOAD_TRIGGER_RES",
+ [MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ",
+ [MSG_SMS_SWDOWNLOAD_BACKDOOR_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SWDOWNLOAD_BACKDOOR_RES",
+ [MSG_SMS_GET_VERSION_EX_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_VERSION_EX_REQ",
+ [MSG_SMS_GET_VERSION_EX_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_VERSION_EX_RES",
+ [MSG_SMS_CLOCK_OUTPUT_CONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CLOCK_OUTPUT_CONFIG_REQ",
+ [MSG_SMS_CLOCK_OUTPUT_CONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CLOCK_OUTPUT_CONFIG_RES",
+ [MSG_SMS_I2C_SET_FREQ_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_I2C_SET_FREQ_REQ",
+ [MSG_SMS_I2C_SET_FREQ_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_I2C_SET_FREQ_RES",
+ [MSG_SMS_GENERIC_I2C_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_I2C_REQ",
+ [MSG_SMS_GENERIC_I2C_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GENERIC_I2C_RES",
+ [MSG_SMS_DVBT_BDA_DATA - MSG_TYPE_BASE_VAL] = "MSG_SMS_DVBT_BDA_DATA",
+ [MSG_SW_RELOAD_REQ - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_REQ",
+ [MSG_SMS_DATA_MSG - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_MSG",
+ [MSG_TABLE_UPLOAD_REQ - MSG_TYPE_BASE_VAL] = "MSG_TABLE_UPLOAD_REQ",
+ [MSG_TABLE_UPLOAD_RES - MSG_TYPE_BASE_VAL] = "MSG_TABLE_UPLOAD_RES",
+ [MSG_SW_RELOAD_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_START_REQ",
+ [MSG_SW_RELOAD_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_START_RES",
+ [MSG_SW_RELOAD_EXEC_REQ - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_EXEC_REQ",
+ [MSG_SW_RELOAD_EXEC_RES - MSG_TYPE_BASE_VAL] = "MSG_SW_RELOAD_EXEC_RES",
+ [MSG_SMS_SPI_INT_LINE_SET_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_INT_LINE_SET_REQ",
+ [MSG_SMS_SPI_INT_LINE_SET_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_INT_LINE_SET_RES",
+ [MSG_SMS_GPIO_CONFIG_EX_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_CONFIG_EX_REQ",
+ [MSG_SMS_GPIO_CONFIG_EX_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GPIO_CONFIG_EX_RES",
+ [MSG_SMS_WATCHDOG_ACT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_WATCHDOG_ACT_REQ",
+ [MSG_SMS_WATCHDOG_ACT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_WATCHDOG_ACT_RES",
+ [MSG_SMS_LOOPBACK_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOOPBACK_REQ",
+ [MSG_SMS_LOOPBACK_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOOPBACK_RES",
+ [MSG_SMS_RAW_CAPTURE_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_START_REQ",
+ [MSG_SMS_RAW_CAPTURE_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_START_RES",
+ [MSG_SMS_RAW_CAPTURE_ABORT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_ABORT_REQ",
+ [MSG_SMS_RAW_CAPTURE_ABORT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_ABORT_RES",
+ [MSG_SMS_RAW_CAPTURE_COMPLETE_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_RAW_CAPTURE_COMPLETE_IND",
+ [MSG_SMS_DATA_PUMP_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_PUMP_IND",
+ [MSG_SMS_DATA_PUMP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_PUMP_REQ",
+ [MSG_SMS_DATA_PUMP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DATA_PUMP_RES",
+ [MSG_SMS_FLASH_DL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_FLASH_DL_REQ",
+ [MSG_SMS_EXEC_TEST_1_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXEC_TEST_1_REQ",
+ [MSG_SMS_EXEC_TEST_1_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXEC_TEST_1_RES",
+ [MSG_SMS_ENABLE_TS_INTERFACE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENABLE_TS_INTERFACE_REQ",
+ [MSG_SMS_ENABLE_TS_INTERFACE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ENABLE_TS_INTERFACE_RES",
+ [MSG_SMS_SPI_SET_BUS_WIDTH_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_SET_BUS_WIDTH_REQ",
+ [MSG_SMS_SPI_SET_BUS_WIDTH_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SPI_SET_BUS_WIDTH_RES",
+ [MSG_SMS_SEND_EMM_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_EMM_REQ",
+ [MSG_SMS_SEND_EMM_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_EMM_RES",
+ [MSG_SMS_DISABLE_TS_INTERFACE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DISABLE_TS_INTERFACE_REQ",
+ [MSG_SMS_DISABLE_TS_INTERFACE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DISABLE_TS_INTERFACE_RES",
+ [MSG_SMS_IS_BUF_FREE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_IS_BUF_FREE_REQ",
+ [MSG_SMS_IS_BUF_FREE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_IS_BUF_FREE_RES",
+ [MSG_SMS_EXT_ANTENNA_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXT_ANTENNA_REQ",
+ [MSG_SMS_EXT_ANTENNA_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXT_ANTENNA_RES",
+ [MSG_SMS_CMMB_GET_NET_OF_FREQ_REQ_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_NET_OF_FREQ_REQ_OBSOLETE",
+ [MSG_SMS_CMMB_GET_NET_OF_FREQ_RES_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_NET_OF_FREQ_RES_OBSOLETE",
+ [MSG_SMS_BATTERY_LEVEL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_BATTERY_LEVEL_REQ",
+ [MSG_SMS_BATTERY_LEVEL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_BATTERY_LEVEL_RES",
+ [MSG_SMS_CMMB_INJECT_TABLE_REQ_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_INJECT_TABLE_REQ_OBSOLETE",
+ [MSG_SMS_CMMB_INJECT_TABLE_RES_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_INJECT_TABLE_RES_OBSOLETE",
+ [MSG_SMS_FM_RADIO_BLOCK_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_FM_RADIO_BLOCK_IND",
+ [MSG_SMS_HOST_NOTIFICATION_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_HOST_NOTIFICATION_IND",
+ [MSG_SMS_CMMB_GET_CONTROL_TABLE_REQ_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_CONTROL_TABLE_REQ_OBSOLETE",
+ [MSG_SMS_CMMB_GET_CONTROL_TABLE_RES_OBSOLETE - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_CONTROL_TABLE_RES_OBSOLETE",
+ [MSG_SMS_CMMB_GET_NETWORKS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_NETWORKS_REQ",
+ [MSG_SMS_CMMB_GET_NETWORKS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_NETWORKS_RES",
+ [MSG_SMS_CMMB_START_SERVICE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_START_SERVICE_REQ",
+ [MSG_SMS_CMMB_START_SERVICE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_START_SERVICE_RES",
+ [MSG_SMS_CMMB_STOP_SERVICE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_STOP_SERVICE_REQ",
+ [MSG_SMS_CMMB_STOP_SERVICE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_STOP_SERVICE_RES",
+ [MSG_SMS_CMMB_ADD_CHANNEL_FILTER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_ADD_CHANNEL_FILTER_REQ",
+ [MSG_SMS_CMMB_ADD_CHANNEL_FILTER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_ADD_CHANNEL_FILTER_RES",
+ [MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_REQ",
+ [MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_RES",
+ [MSG_SMS_CMMB_START_CONTROL_INFO_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_START_CONTROL_INFO_REQ",
+ [MSG_SMS_CMMB_START_CONTROL_INFO_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_START_CONTROL_INFO_RES",
+ [MSG_SMS_CMMB_STOP_CONTROL_INFO_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_STOP_CONTROL_INFO_REQ",
+ [MSG_SMS_CMMB_STOP_CONTROL_INFO_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_STOP_CONTROL_INFO_RES",
+ [MSG_SMS_ISDBT_TUNE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_TUNE_REQ",
+ [MSG_SMS_ISDBT_TUNE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_ISDBT_TUNE_RES",
+ [MSG_SMS_TRANSMISSION_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_TRANSMISSION_IND",
+ [MSG_SMS_PID_STATISTICS_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_PID_STATISTICS_IND",
+ [MSG_SMS_POWER_DOWN_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_DOWN_IND",
+ [MSG_SMS_POWER_DOWN_CONF - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_DOWN_CONF",
+ [MSG_SMS_POWER_UP_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_UP_IND",
+ [MSG_SMS_POWER_UP_CONF - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_UP_CONF",
+ [MSG_SMS_POWER_MODE_SET_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_MODE_SET_REQ",
+ [MSG_SMS_POWER_MODE_SET_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_POWER_MODE_SET_RES",
+ [MSG_SMS_DEBUG_HOST_EVENT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DEBUG_HOST_EVENT_REQ",
+ [MSG_SMS_DEBUG_HOST_EVENT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DEBUG_HOST_EVENT_RES",
+ [MSG_SMS_NEW_CRYSTAL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_NEW_CRYSTAL_REQ",
+ [MSG_SMS_NEW_CRYSTAL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_NEW_CRYSTAL_RES",
+ [MSG_SMS_CONFIG_SPI_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CONFIG_SPI_REQ",
+ [MSG_SMS_CONFIG_SPI_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CONFIG_SPI_RES",
+ [MSG_SMS_I2C_SHORT_STAT_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_I2C_SHORT_STAT_IND",
+ [MSG_SMS_START_IR_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_START_IR_REQ",
+ [MSG_SMS_START_IR_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_START_IR_RES",
+ [MSG_SMS_IR_SAMPLES_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_IR_SAMPLES_IND",
+ [MSG_SMS_CMMB_CA_SERVICE_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_CA_SERVICE_IND",
+ [MSG_SMS_SLAVE_DEVICE_DETECTED - MSG_TYPE_BASE_VAL] = "MSG_SMS_SLAVE_DEVICE_DETECTED",
+ [MSG_SMS_INTERFACE_LOCK_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_INTERFACE_LOCK_IND",
+ [MSG_SMS_INTERFACE_UNLOCK_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_INTERFACE_UNLOCK_IND",
+ [MSG_SMS_SEND_ROSUM_BUFF_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_ROSUM_BUFF_REQ",
+ [MSG_SMS_SEND_ROSUM_BUFF_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_ROSUM_BUFF_RES",
+ [MSG_SMS_ROSUM_BUFF - MSG_TYPE_BASE_VAL] = "MSG_SMS_ROSUM_BUFF",
+ [MSG_SMS_SET_AES128_KEY_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_AES128_KEY_REQ",
+ [MSG_SMS_SET_AES128_KEY_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_AES128_KEY_RES",
+ [MSG_SMS_MBBMS_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MBBMS_WRITE_REQ",
+ [MSG_SMS_MBBMS_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MBBMS_WRITE_RES",
+ [MSG_SMS_MBBMS_READ_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_MBBMS_READ_IND",
+ [MSG_SMS_IQ_STREAM_START_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_START_REQ",
+ [MSG_SMS_IQ_STREAM_START_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_START_RES",
+ [MSG_SMS_IQ_STREAM_STOP_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_STOP_REQ",
+ [MSG_SMS_IQ_STREAM_STOP_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_STOP_RES",
+ [MSG_SMS_IQ_STREAM_DATA_BLOCK - MSG_TYPE_BASE_VAL] = "MSG_SMS_IQ_STREAM_DATA_BLOCK",
+ [MSG_SMS_GET_EEPROM_VERSION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_EEPROM_VERSION_REQ",
+ [MSG_SMS_GET_EEPROM_VERSION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_GET_EEPROM_VERSION_RES",
+ [MSG_SMS_SIGNAL_DETECTED_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SIGNAL_DETECTED_IND",
+ [MSG_SMS_NO_SIGNAL_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_NO_SIGNAL_IND",
+ [MSG_SMS_MRC_SHUTDOWN_SLAVE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_SHUTDOWN_SLAVE_REQ",
+ [MSG_SMS_MRC_SHUTDOWN_SLAVE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_SHUTDOWN_SLAVE_RES",
+ [MSG_SMS_MRC_BRINGUP_SLAVE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_BRINGUP_SLAVE_REQ",
+ [MSG_SMS_MRC_BRINGUP_SLAVE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_BRINGUP_SLAVE_RES",
+ [MSG_SMS_EXTERNAL_LNA_CTRL_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXTERNAL_LNA_CTRL_REQ",
+ [MSG_SMS_EXTERNAL_LNA_CTRL_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_EXTERNAL_LNA_CTRL_RES",
+ [MSG_SMS_SET_PERIODIC_STATISTICS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_PERIODIC_STATISTICS_REQ",
+ [MSG_SMS_SET_PERIODIC_STATISTICS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SET_PERIODIC_STATISTICS_RES",
+ [MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_REQ",
+ [MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_RES",
+ [LOCAL_TUNE - MSG_TYPE_BASE_VAL] = "LOCAL_TUNE",
+ [LOCAL_IFFT_H_ICI - MSG_TYPE_BASE_VAL] = "LOCAL_IFFT_H_ICI",
+ [MSG_RESYNC_REQ - MSG_TYPE_BASE_VAL] = "MSG_RESYNC_REQ",
+ [MSG_SMS_CMMB_GET_MRC_STATISTICS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_MRC_STATISTICS_REQ",
+ [MSG_SMS_CMMB_GET_MRC_STATISTICS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_MRC_STATISTICS_RES",
+ [MSG_SMS_LOG_EX_ITEM - MSG_TYPE_BASE_VAL] = "MSG_SMS_LOG_EX_ITEM",
+ [MSG_SMS_DEVICE_DATA_LOSS_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_DEVICE_DATA_LOSS_IND",
+ [MSG_SMS_MRC_WATCHDOG_TRIGGERED_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_MRC_WATCHDOG_TRIGGERED_IND",
+ [MSG_SMS_USER_MSG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_USER_MSG_REQ",
+ [MSG_SMS_USER_MSG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_USER_MSG_RES",
+ [MSG_SMS_SMART_CARD_INIT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_INIT_REQ",
+ [MSG_SMS_SMART_CARD_INIT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_INIT_RES",
+ [MSG_SMS_SMART_CARD_WRITE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_WRITE_REQ",
+ [MSG_SMS_SMART_CARD_WRITE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_WRITE_RES",
+ [MSG_SMS_SMART_CARD_READ_IND - MSG_TYPE_BASE_VAL] = "MSG_SMS_SMART_CARD_READ_IND",
+ [MSG_SMS_TSE_ENABLE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_TSE_ENABLE_REQ",
+ [MSG_SMS_TSE_ENABLE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_TSE_ENABLE_RES",
+ [MSG_SMS_CMMB_GET_SHORT_STATISTICS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_SHORT_STATISTICS_REQ",
+ [MSG_SMS_CMMB_GET_SHORT_STATISTICS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_GET_SHORT_STATISTICS_RES",
+ [MSG_SMS_LED_CONFIG_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_LED_CONFIG_REQ",
+ [MSG_SMS_LED_CONFIG_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_LED_CONFIG_RES",
+ [MSG_PWM_ANTENNA_REQ - MSG_TYPE_BASE_VAL] = "MSG_PWM_ANTENNA_REQ",
+ [MSG_PWM_ANTENNA_RES - MSG_TYPE_BASE_VAL] = "MSG_PWM_ANTENNA_RES",
+ [MSG_SMS_CMMB_SMD_SN_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SMD_SN_REQ",
+ [MSG_SMS_CMMB_SMD_SN_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SMD_SN_RES",
+ [MSG_SMS_CMMB_SET_CA_CW_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_CA_CW_REQ",
+ [MSG_SMS_CMMB_SET_CA_CW_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_CA_CW_RES",
+ [MSG_SMS_CMMB_SET_CA_SALT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_CA_SALT_REQ",
+ [MSG_SMS_CMMB_SET_CA_SALT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_CMMB_SET_CA_SALT_RES",
+ [MSG_SMS_NSCD_INIT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_INIT_REQ",
+ [MSG_SMS_NSCD_INIT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_INIT_RES",
+ [MSG_SMS_NSCD_PROCESS_SECTION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_PROCESS_SECTION_REQ",
+ [MSG_SMS_NSCD_PROCESS_SECTION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_PROCESS_SECTION_RES",
+ [MSG_SMS_DBD_CREATE_OBJECT_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_CREATE_OBJECT_REQ",
+ [MSG_SMS_DBD_CREATE_OBJECT_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_CREATE_OBJECT_RES",
+ [MSG_SMS_DBD_CONFIGURE_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_CONFIGURE_REQ",
+ [MSG_SMS_DBD_CONFIGURE_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_CONFIGURE_RES",
+ [MSG_SMS_DBD_SET_KEYS_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_SET_KEYS_REQ",
+ [MSG_SMS_DBD_SET_KEYS_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_SET_KEYS_RES",
+ [MSG_SMS_DBD_PROCESS_HEADER_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_HEADER_REQ",
+ [MSG_SMS_DBD_PROCESS_HEADER_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_HEADER_RES",
+ [MSG_SMS_DBD_PROCESS_DATA_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_DATA_REQ",
+ [MSG_SMS_DBD_PROCESS_DATA_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_DATA_RES",
+ [MSG_SMS_DBD_PROCESS_GET_DATA_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_GET_DATA_REQ",
+ [MSG_SMS_DBD_PROCESS_GET_DATA_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_DBD_PROCESS_GET_DATA_RES",
+ [MSG_SMS_NSCD_OPEN_SESSION_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_OPEN_SESSION_REQ",
+ [MSG_SMS_NSCD_OPEN_SESSION_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_NSCD_OPEN_SESSION_RES",
+ [MSG_SMS_SEND_HOST_DATA_TO_DEMUX_REQ - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_HOST_DATA_TO_DEMUX_REQ",
+ [MSG_SMS_SEND_HOST_DATA_TO_DEMUX_RES - MSG_TYPE_BASE_VAL] = "MSG_SMS_SEND_HOST_DATA_TO_DEMUX_RES",
+ [MSG_LAST_MSG_TYPE - MSG_TYPE_BASE_VAL] = "MSG_LAST_MSG_TYPE",
+};
+
+char *smscore_translate_msg(enum msg_types msgtype)
+{
+ int i = msgtype - MSG_TYPE_BASE_VAL;
+ char *msg;
+
+ if (i < 0 || i >= ARRAY_SIZE(siano_msgs))
+ return "Unknown msg type";
+
+ msg = siano_msgs[i];
+
+ if (!*msg)
+ return "Unknown msg type";
+
+ return msg;
+}
+EXPORT_SYMBOL_GPL(smscore_translate_msg);
+
+void smscore_set_board_id(struct smscore_device_t *core, int id)
+{
+ core->board_id = id;
+}
+
+int smscore_led_state(struct smscore_device_t *core, int led)
+{
+ if (led >= 0)
+ core->led_state = led;
+ return core->led_state;
+}
+EXPORT_SYMBOL_GPL(smscore_set_board_id);
+
+int smscore_get_board_id(struct smscore_device_t *core)
+{
+ return core->board_id;
+}
+EXPORT_SYMBOL_GPL(smscore_get_board_id);
+
+struct smscore_registry_entry_t {
+ struct list_head entry;
+ char devpath[32];
+ int mode;
+ enum sms_device_type_st type;
+};
+
+static struct list_head g_smscore_notifyees;
+static struct list_head g_smscore_devices;
+static DEFINE_MUTEX(g_smscore_deviceslock);
+
+static struct list_head g_smscore_registry;
+static DEFINE_MUTEX(g_smscore_registrylock);
+
+static int default_mode = DEVICE_MODE_NONE;
+
+module_param(default_mode, int, 0644);
+MODULE_PARM_DESC(default_mode, "default firmware id (device mode)");
+
+static struct smscore_registry_entry_t *smscore_find_registry(char *devpath)
+{
+ struct smscore_registry_entry_t *entry;
+ struct list_head *next;
+
+ mutex_lock(&g_smscore_registrylock);
+ for (next = g_smscore_registry.next;
+ next != &g_smscore_registry;
+ next = next->next) {
+ entry = (struct smscore_registry_entry_t *) next;
+ if (!strncmp(entry->devpath, devpath, sizeof(entry->devpath))) {
+ mutex_unlock(&g_smscore_registrylock);
+ return entry;
+ }
+ }
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (entry) {
+ entry->mode = default_mode;
+ strscpy(entry->devpath, devpath, sizeof(entry->devpath));
+ list_add(&entry->entry, &g_smscore_registry);
+ } else
+ pr_err("failed to create smscore_registry.\n");
+ mutex_unlock(&g_smscore_registrylock);
+ return entry;
+}
+
+int smscore_registry_getmode(char *devpath)
+{
+ struct smscore_registry_entry_t *entry;
+
+ entry = smscore_find_registry(devpath);
+ if (entry)
+ return entry->mode;
+ else
+ pr_err("No registry found.\n");
+
+ return default_mode;
+}
+EXPORT_SYMBOL_GPL(smscore_registry_getmode);
+
+static enum sms_device_type_st smscore_registry_gettype(char *devpath)
+{
+ struct smscore_registry_entry_t *entry;
+
+ entry = smscore_find_registry(devpath);
+ if (entry)
+ return entry->type;
+ else
+ pr_err("No registry found.\n");
+
+ return -EINVAL;
+}
+
+static void smscore_registry_setmode(char *devpath, int mode)
+{
+ struct smscore_registry_entry_t *entry;
+
+ entry = smscore_find_registry(devpath);
+ if (entry)
+ entry->mode = mode;
+ else
+ pr_err("No registry found.\n");
+}
+
+static void smscore_registry_settype(char *devpath,
+ enum sms_device_type_st type)
+{
+ struct smscore_registry_entry_t *entry;
+
+ entry = smscore_find_registry(devpath);
+ if (entry)
+ entry->type = type;
+ else
+ pr_err("No registry found.\n");
+}
+
+
+static void list_add_locked(struct list_head *new, struct list_head *head,
+ spinlock_t *lock)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(lock, flags);
+
+ list_add(new, head);
+
+ spin_unlock_irqrestore(lock, flags);
+}
+
+/*
+ * register a client callback that called when device plugged in/unplugged
+ * NOTE: if devices exist callback is called immediately for each device
+ *
+ * @param hotplug callback
+ *
+ * return: 0 on success, <0 on error.
+ */
+int smscore_register_hotplug(hotplug_t hotplug)
+{
+ struct smscore_device_notifyee_t *notifyee;
+ struct list_head *next, *first;
+ int rc = 0;
+
+ mutex_lock(&g_smscore_deviceslock);
+ notifyee = kmalloc(sizeof(*notifyee), GFP_KERNEL);
+ if (notifyee) {
+ /* now notify callback about existing devices */
+ first = &g_smscore_devices;
+ for (next = first->next;
+ next != first && !rc;
+ next = next->next) {
+ struct smscore_device_t *coredev =
+ (struct smscore_device_t *) next;
+ rc = hotplug(coredev, coredev->device, 1);
+ }
+
+ if (rc >= 0) {
+ notifyee->hotplug = hotplug;
+ list_add(&notifyee->entry, &g_smscore_notifyees);
+ } else
+ kfree(notifyee);
+ } else
+ rc = -ENOMEM;
+
+ mutex_unlock(&g_smscore_deviceslock);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(smscore_register_hotplug);
+
+/*
+ * unregister a client callback that called when device plugged in/unplugged
+ *
+ * @param hotplug callback
+ *
+ */
+void smscore_unregister_hotplug(hotplug_t hotplug)
+{
+ struct list_head *next, *first;
+
+ mutex_lock(&g_smscore_deviceslock);
+
+ first = &g_smscore_notifyees;
+
+ for (next = first->next; next != first;) {
+ struct smscore_device_notifyee_t *notifyee =
+ (struct smscore_device_notifyee_t *) next;
+ next = next->next;
+
+ if (notifyee->hotplug == hotplug) {
+ list_del(&notifyee->entry);
+ kfree(notifyee);
+ }
+ }
+
+ mutex_unlock(&g_smscore_deviceslock);
+}
+EXPORT_SYMBOL_GPL(smscore_unregister_hotplug);
+
+static void smscore_notify_clients(struct smscore_device_t *coredev)
+{
+ struct smscore_client_t *client;
+
+ /* the client must call smscore_unregister_client from remove handler */
+ while (!list_empty(&coredev->clients)) {
+ client = (struct smscore_client_t *) coredev->clients.next;
+ client->onremove_handler(client->context);
+ }
+}
+
+static int smscore_notify_callbacks(struct smscore_device_t *coredev,
+ struct device *device, int arrival)
+{
+ struct smscore_device_notifyee_t *elem;
+ int rc = 0;
+
+ /* note: must be called under g_deviceslock */
+
+ list_for_each_entry(elem, &g_smscore_notifyees, entry) {
+ rc = elem->hotplug(coredev, device, arrival);
+ if (rc < 0)
+ break;
+ }
+
+ return rc;
+}
+
+static struct
+smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer,
+ dma_addr_t common_buffer_phys)
+{
+ struct smscore_buffer_t *cb;
+
+ cb = kzalloc(sizeof(*cb), GFP_KERNEL);
+ if (!cb)
+ return NULL;
+
+ cb->p = buffer;
+ cb->offset_in_common = buffer - (u8 *) common_buffer;
+ if (common_buffer_phys)
+ cb->phys = common_buffer_phys + cb->offset_in_common;
+
+ return cb;
+}
+
+/*
+ * creates coredev object for a device, prepares buffers,
+ * creates buffer mappings, notifies registered hotplugs about new device.
+ *
+ * @param params device pointer to struct with device specific parameters
+ * and handlers
+ * @param coredev pointer to a value that receives created coredev object
+ *
+ * return: 0 on success, <0 on error.
+ */
+int smscore_register_device(struct smsdevice_params_t *params,
+ struct smscore_device_t **coredev,
+ gfp_t gfp_buf_flags,
+ void *mdev)
+{
+ struct smscore_device_t *dev;
+ u8 *buffer;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ dev->media_dev = mdev;
+#endif
+ dev->gfp_buf_flags = gfp_buf_flags;
+
+ /* init list entry so it could be safe in smscore_unregister_device */
+ INIT_LIST_HEAD(&dev->entry);
+
+ /* init queues */
+ INIT_LIST_HEAD(&dev->clients);
+ INIT_LIST_HEAD(&dev->buffers);
+
+ /* init locks */
+ spin_lock_init(&dev->clientslock);
+ spin_lock_init(&dev->bufferslock);
+
+ /* init completion events */
+ init_completion(&dev->version_ex_done);
+ init_completion(&dev->data_download_done);
+ init_completion(&dev->data_validity_done);
+ init_completion(&dev->trigger_done);
+ init_completion(&dev->init_device_done);
+ init_completion(&dev->reload_start_done);
+ init_completion(&dev->resume_done);
+ init_completion(&dev->gpio_configuration_done);
+ init_completion(&dev->gpio_set_level_done);
+ init_completion(&dev->gpio_get_level_done);
+ init_completion(&dev->ir_init_done);
+
+ /* Buffer management */
+ init_waitqueue_head(&dev->buffer_mng_waitq);
+
+ /* alloc common buffer */
+ dev->common_buffer_size = params->buffer_size * params->num_buffers;
+ if (params->usb_device)
+ buffer = kzalloc(dev->common_buffer_size, GFP_KERNEL);
+ else
+ buffer = dma_alloc_coherent(params->device,
+ dev->common_buffer_size,
+ &dev->common_buffer_phys,
+ GFP_KERNEL | dev->gfp_buf_flags);
+ if (!buffer) {
+ smscore_unregister_device(dev);
+ return -ENOMEM;
+ }
+ dev->common_buffer = buffer;
+
+ /* prepare dma buffers */
+ for (; dev->num_buffers < params->num_buffers;
+ dev->num_buffers++, buffer += params->buffer_size) {
+ struct smscore_buffer_t *cb;
+
+ cb = smscore_createbuffer(buffer, dev->common_buffer,
+ dev->common_buffer_phys);
+ if (!cb) {
+ smscore_unregister_device(dev);
+ return -ENOMEM;
+ }
+
+ smscore_putbuffer(dev, cb);
+ }
+
+ pr_debug("allocated %d buffers\n", dev->num_buffers);
+
+ dev->mode = DEVICE_MODE_NONE;
+ dev->board_id = SMS_BOARD_UNKNOWN;
+ dev->context = params->context;
+ dev->device = params->device;
+ dev->usb_device = params->usb_device;
+ dev->setmode_handler = params->setmode_handler;
+ dev->detectmode_handler = params->detectmode_handler;
+ dev->sendrequest_handler = params->sendrequest_handler;
+ dev->preload_handler = params->preload_handler;
+ dev->postload_handler = params->postload_handler;
+
+ dev->device_flags = params->flags;
+ strscpy(dev->devpath, params->devpath, sizeof(dev->devpath));
+
+ smscore_registry_settype(dev->devpath, params->device_type);
+
+ /* add device to devices list */
+ mutex_lock(&g_smscore_deviceslock);
+ list_add(&dev->entry, &g_smscore_devices);
+ mutex_unlock(&g_smscore_deviceslock);
+
+ *coredev = dev;
+
+ pr_debug("device %p created\n", dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(smscore_register_device);
+
+
+static int smscore_sendrequest_and_wait(struct smscore_device_t *coredev,
+ void *buffer, size_t size, struct completion *completion) {
+ int rc;
+
+ if (!completion)
+ return -EINVAL;
+ init_completion(completion);
+
+ rc = coredev->sendrequest_handler(coredev->context, buffer, size);
+ if (rc < 0) {
+ pr_info("sendrequest returned error %d\n", rc);
+ return rc;
+ }
+
+ return wait_for_completion_timeout(completion,
+ msecs_to_jiffies(SMS_PROTOCOL_MAX_RAOUNDTRIP_MS)) ?
+ 0 : -ETIME;
+}
+
+/*
+ * Starts & enables IR operations
+ *
+ * return: 0 on success, < 0 on error.
+ */
+static int smscore_init_ir(struct smscore_device_t *coredev)
+{
+ int ir_io;
+ int rc;
+ void *buffer;
+
+ coredev->ir.dev = NULL;
+ ir_io = sms_get_board(smscore_get_board_id(coredev))->board_cfg.ir;
+ if (ir_io) {/* only if IR port exist we use IR sub-module */
+ pr_debug("IR loading\n");
+ rc = sms_ir_init(coredev);
+
+ if (rc != 0)
+ pr_err("Error initialization DTV IR sub-module\n");
+ else {
+ buffer = kmalloc(sizeof(struct sms_msg_data2) +
+ SMS_DMA_ALIGNMENT,
+ GFP_KERNEL | coredev->gfp_buf_flags);
+ if (buffer) {
+ struct sms_msg_data2 *msg =
+ (struct sms_msg_data2 *)
+ SMS_ALIGN_ADDRESS(buffer);
+
+ SMS_INIT_MSG(&msg->x_msg_header,
+ MSG_SMS_START_IR_REQ,
+ sizeof(struct sms_msg_data2));
+ msg->msg_data[0] = coredev->ir.controller;
+ msg->msg_data[1] = coredev->ir.timeout;
+
+ rc = smscore_sendrequest_and_wait(coredev, msg,
+ msg->x_msg_header. msg_length,
+ &coredev->ir_init_done);
+
+ kfree(buffer);
+ } else
+ pr_err("Sending IR initialization message failed\n");
+ }
+ } else
+ pr_info("IR port has not been detected\n");
+
+ return 0;
+}
+
+/*
+ * configures device features according to board configuration structure.
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ *
+ * return: 0 on success, <0 on error.
+ */
+static int smscore_configure_board(struct smscore_device_t *coredev)
+{
+ struct sms_board *board;
+
+ board = sms_get_board(coredev->board_id);
+ if (!board) {
+ pr_err("no board configuration exist.\n");
+ return -EINVAL;
+ }
+
+ if (board->mtu) {
+ struct sms_msg_data mtu_msg;
+ pr_debug("set max transmit unit %d\n", board->mtu);
+
+ mtu_msg.x_msg_header.msg_src_id = 0;
+ mtu_msg.x_msg_header.msg_dst_id = HIF_TASK;
+ mtu_msg.x_msg_header.msg_flags = 0;
+ mtu_msg.x_msg_header.msg_type = MSG_SMS_SET_MAX_TX_MSG_LEN_REQ;
+ mtu_msg.x_msg_header.msg_length = sizeof(mtu_msg);
+ mtu_msg.msg_data = board->mtu;
+
+ coredev->sendrequest_handler(coredev->context, &mtu_msg,
+ sizeof(mtu_msg));
+ }
+
+ if (board->crystal) {
+ struct sms_msg_data crys_msg;
+ pr_debug("set crystal value %d\n", board->crystal);
+
+ SMS_INIT_MSG(&crys_msg.x_msg_header,
+ MSG_SMS_NEW_CRYSTAL_REQ,
+ sizeof(crys_msg));
+ crys_msg.msg_data = board->crystal;
+
+ coredev->sendrequest_handler(coredev->context, &crys_msg,
+ sizeof(crys_msg));
+ }
+
+ return 0;
+}
+
+/*
+ * sets initial device mode and notifies client hotplugs that device is ready
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ *
+ * return: 0 on success, <0 on error.
+ */
+int smscore_start_device(struct smscore_device_t *coredev)
+{
+ int rc;
+ int board_id = smscore_get_board_id(coredev);
+ int mode = smscore_registry_getmode(coredev->devpath);
+
+ /* Device is initialized as DEVICE_MODE_NONE */
+ if (board_id != SMS_BOARD_UNKNOWN && mode == DEVICE_MODE_NONE)
+ mode = sms_get_board(board_id)->default_mode;
+
+ rc = smscore_set_device_mode(coredev, mode);
+ if (rc < 0) {
+ pr_info("set device mode failed , rc %d\n", rc);
+ return rc;
+ }
+ rc = smscore_configure_board(coredev);
+ if (rc < 0) {
+ pr_info("configure board failed , rc %d\n", rc);
+ return rc;
+ }
+
+ mutex_lock(&g_smscore_deviceslock);
+
+ rc = smscore_notify_callbacks(coredev, coredev->device, 1);
+ smscore_init_ir(coredev);
+
+ pr_debug("device %p started, rc %d\n", coredev, rc);
+
+ mutex_unlock(&g_smscore_deviceslock);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(smscore_start_device);
+
+
+static int smscore_load_firmware_family2(struct smscore_device_t *coredev,
+ void *buffer, size_t size)
+{
+ struct sms_firmware *firmware = (struct sms_firmware *) buffer;
+ struct sms_msg_data5 *msg;
+ u32 mem_address, calc_checksum = 0;
+ u32 i, *ptr;
+ u8 *payload = firmware->payload;
+ int rc = 0;
+ firmware->start_address = le32_to_cpup((__le32 *)&firmware->start_address);
+ firmware->length = le32_to_cpup((__le32 *)&firmware->length);
+
+ mem_address = firmware->start_address;
+
+ pr_debug("loading FW to addr 0x%x size %d\n",
+ mem_address, firmware->length);
+ if (coredev->preload_handler) {
+ rc = coredev->preload_handler(coredev->context);
+ if (rc < 0)
+ return rc;
+ }
+
+ /* PAGE_SIZE buffer shall be enough and dma aligned */
+ msg = kmalloc(PAGE_SIZE, GFP_KERNEL | coredev->gfp_buf_flags);
+ if (!msg)
+ return -ENOMEM;
+
+ if (coredev->mode != DEVICE_MODE_NONE) {
+ pr_debug("sending reload command.\n");
+ SMS_INIT_MSG(&msg->x_msg_header, MSG_SW_RELOAD_START_REQ,
+ sizeof(struct sms_msg_hdr));
+ rc = smscore_sendrequest_and_wait(coredev, msg,
+ msg->x_msg_header.msg_length,
+ &coredev->reload_start_done);
+ if (rc < 0) {
+ pr_err("device reload failed, rc %d\n", rc);
+ goto exit_fw_download;
+ }
+ mem_address = *(u32 *) &payload[20];
+ }
+
+ for (i = 0, ptr = (u32 *)firmware->payload; i < firmware->length/4 ;
+ i++, ptr++)
+ calc_checksum += *ptr;
+
+ while (size && rc >= 0) {
+ struct sms_data_download *data_msg =
+ (struct sms_data_download *) msg;
+ int payload_size = min_t(int, size, SMS_MAX_PAYLOAD_SIZE);
+
+ SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_DATA_DOWNLOAD_REQ,
+ (u16)(sizeof(struct sms_msg_hdr) +
+ sizeof(u32) + payload_size));
+
+ data_msg->mem_addr = mem_address;
+ memcpy(data_msg->payload, payload, payload_size);
+
+ rc = smscore_sendrequest_and_wait(coredev, data_msg,
+ data_msg->x_msg_header.msg_length,
+ &coredev->data_download_done);
+
+ payload += payload_size;
+ size -= payload_size;
+ mem_address += payload_size;
+ }
+
+ if (rc < 0)
+ goto exit_fw_download;
+
+ pr_debug("sending MSG_SMS_DATA_VALIDITY_REQ expecting 0x%x\n",
+ calc_checksum);
+ SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_DATA_VALIDITY_REQ,
+ sizeof(msg->x_msg_header) +
+ sizeof(u32) * 3);
+ msg->msg_data[0] = firmware->start_address;
+ /* Entry point */
+ msg->msg_data[1] = firmware->length;
+ msg->msg_data[2] = 0; /* Regular checksum*/
+ rc = smscore_sendrequest_and_wait(coredev, msg,
+ msg->x_msg_header.msg_length,
+ &coredev->data_validity_done);
+ if (rc < 0)
+ goto exit_fw_download;
+
+ if (coredev->mode == DEVICE_MODE_NONE) {
+ pr_debug("sending MSG_SMS_SWDOWNLOAD_TRIGGER_REQ\n");
+ SMS_INIT_MSG(&msg->x_msg_header,
+ MSG_SMS_SWDOWNLOAD_TRIGGER_REQ,
+ sizeof(*msg));
+
+ msg->msg_data[0] = firmware->start_address;
+ /* Entry point */
+ msg->msg_data[1] = 6; /* Priority */
+ msg->msg_data[2] = 0x200; /* Stack size */
+ msg->msg_data[3] = 0; /* Parameter */
+ msg->msg_data[4] = 4; /* Task ID */
+
+ rc = smscore_sendrequest_and_wait(coredev, msg,
+ msg->x_msg_header.msg_length,
+ &coredev->trigger_done);
+ } else {
+ SMS_INIT_MSG(&msg->x_msg_header, MSG_SW_RELOAD_EXEC_REQ,
+ sizeof(struct sms_msg_hdr));
+ rc = coredev->sendrequest_handler(coredev->context, msg,
+ msg->x_msg_header.msg_length);
+ }
+
+ if (rc < 0)
+ goto exit_fw_download;
+
+ /*
+ * backward compatibility - wait to device_ready_done for
+ * not more than 400 ms
+ */
+ msleep(400);
+
+exit_fw_download:
+ kfree(msg);
+
+ if (coredev->postload_handler) {
+ pr_debug("rc=%d, postload=0x%p\n",
+ rc, coredev->postload_handler);
+ if (rc >= 0)
+ return coredev->postload_handler(coredev->context);
+ }
+
+ pr_debug("rc=%d\n", rc);
+ return rc;
+}
+
+static char *smscore_fw_lkup[][DEVICE_MODE_MAX] = {
+ [SMS_NOVA_A0] = {
+ [DEVICE_MODE_DVBT] = SMS_FW_DVB_NOVA_12MHZ,
+ [DEVICE_MODE_DVBH] = SMS_FW_DVB_NOVA_12MHZ,
+ [DEVICE_MODE_DAB_TDMB] = SMS_FW_TDMB_NOVA_12MHZ,
+ [DEVICE_MODE_DVBT_BDA] = SMS_FW_DVB_NOVA_12MHZ,
+ [DEVICE_MODE_ISDBT] = SMS_FW_ISDBT_NOVA_12MHZ,
+ [DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_NOVA_12MHZ,
+ },
+ [SMS_NOVA_B0] = {
+ [DEVICE_MODE_DVBT] = SMS_FW_DVB_NOVA_12MHZ_B0,
+ [DEVICE_MODE_DVBH] = SMS_FW_DVB_NOVA_12MHZ_B0,
+ [DEVICE_MODE_DAB_TDMB] = SMS_FW_TDMB_NOVA_12MHZ_B0,
+ [DEVICE_MODE_DVBT_BDA] = SMS_FW_DVB_NOVA_12MHZ_B0,
+ [DEVICE_MODE_ISDBT] = SMS_FW_ISDBT_NOVA_12MHZ_B0,
+ [DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_NOVA_12MHZ_B0,
+ [DEVICE_MODE_FM_RADIO] = SMS_FW_FM_RADIO,
+ [DEVICE_MODE_FM_RADIO_BDA] = SMS_FW_FM_RADIO,
+ },
+ [SMS_VEGA] = {
+ [DEVICE_MODE_CMMB] = SMS_FW_CMMB_VEGA_12MHZ,
+ },
+ [SMS_VENICE] = {
+ [DEVICE_MODE_CMMB] = SMS_FW_CMMB_VENICE_12MHZ,
+ },
+ [SMS_MING] = {
+ [DEVICE_MODE_CMMB] = SMS_FW_CMMB_MING_APP,
+ },
+ [SMS_PELE] = {
+ [DEVICE_MODE_ISDBT] = SMS_FW_ISDBT_PELE,
+ [DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_PELE,
+ },
+ [SMS_RIO] = {
+ [DEVICE_MODE_DVBT] = SMS_FW_DVB_RIO,
+ [DEVICE_MODE_DVBH] = SMS_FW_DVBH_RIO,
+ [DEVICE_MODE_DVBT_BDA] = SMS_FW_DVB_RIO,
+ [DEVICE_MODE_ISDBT] = SMS_FW_ISDBT_RIO,
+ [DEVICE_MODE_ISDBT_BDA] = SMS_FW_ISDBT_RIO,
+ [DEVICE_MODE_FM_RADIO] = SMS_FW_FM_RADIO_RIO,
+ [DEVICE_MODE_FM_RADIO_BDA] = SMS_FW_FM_RADIO_RIO,
+ },
+ [SMS_DENVER_1530] = {
+ [DEVICE_MODE_ATSC] = SMS_FW_ATSC_DENVER,
+ },
+ [SMS_DENVER_2160] = {
+ [DEVICE_MODE_DAB_TDMB] = SMS_FW_TDMB_DENVER,
+ },
+};
+
+/*
+ * get firmware file name from one of the two mechanisms : sms_boards or
+ * smscore_fw_lkup.
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ * @param mode requested mode of operation
+ * @param lookup if 1, always get the fw filename from smscore_fw_lkup
+ * table. if 0, try first to get from sms_boards
+ *
+ * return: 0 on success, <0 on error.
+ */
+static char *smscore_get_fw_filename(struct smscore_device_t *coredev,
+ int mode)
+{
+ char **fw;
+ int board_id = smscore_get_board_id(coredev);
+ enum sms_device_type_st type;
+
+ type = smscore_registry_gettype(coredev->devpath);
+
+ /* Prevent looking outside the smscore_fw_lkup table */
+ if (type <= SMS_UNKNOWN_TYPE || type >= SMS_NUM_OF_DEVICE_TYPES)
+ return NULL;
+ if (mode <= DEVICE_MODE_NONE || mode >= DEVICE_MODE_MAX)
+ return NULL;
+
+ pr_debug("trying to get fw name from sms_boards board_id %d mode %d\n",
+ board_id, mode);
+ fw = sms_get_board(board_id)->fw;
+ if (!fw || !fw[mode]) {
+ pr_debug("cannot find fw name in sms_boards, getting from lookup table mode %d type %d\n",
+ mode, type);
+ return smscore_fw_lkup[type][mode];
+ }
+
+ return fw[mode];
+}
+
+/*
+ * loads specified firmware into a buffer and calls device loadfirmware_handler
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ * @param filename null-terminated string specifies firmware file name
+ * @param loadfirmware_handler device handler that loads firmware
+ *
+ * return: 0 on success, <0 on error.
+ */
+static int smscore_load_firmware_from_file(struct smscore_device_t *coredev,
+ int mode)
+{
+ int rc = -ENOENT;
+ u8 *fw_buf;
+ u32 fw_buf_size;
+ const struct firmware *fw;
+
+ char *fw_filename = smscore_get_fw_filename(coredev, mode);
+ if (!fw_filename) {
+ pr_err("mode %d not supported on this device\n", mode);
+ return -ENOENT;
+ }
+ pr_debug("Firmware name: %s\n", fw_filename);
+
+ if (!(coredev->device_flags & SMS_DEVICE_FAMILY2))
+ return -EINVAL;
+
+ rc = request_firmware(&fw, fw_filename, coredev->device);
+ if (rc < 0) {
+ pr_err("failed to open firmware file '%s'\n", fw_filename);
+ return rc;
+ }
+ pr_debug("read fw %s, buffer size=0x%zx\n", fw_filename, fw->size);
+ fw_buf = kmalloc(ALIGN(fw->size + sizeof(struct sms_firmware),
+ SMS_ALLOC_ALIGNMENT), GFP_KERNEL | coredev->gfp_buf_flags);
+ if (!fw_buf) {
+ pr_err("failed to allocate firmware buffer\n");
+ rc = -ENOMEM;
+ } else {
+ memcpy(fw_buf, fw->data, fw->size);
+ fw_buf_size = fw->size;
+
+ rc = smscore_load_firmware_family2(coredev, fw_buf,
+ fw_buf_size);
+ }
+
+ kfree(fw_buf);
+ release_firmware(fw);
+
+ return rc;
+}
+
+/*
+ * notifies all clients registered with the device, notifies hotplugs,
+ * frees all buffers and coredev object
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ *
+ * return: 0 on success, <0 on error.
+ */
+void smscore_unregister_device(struct smscore_device_t *coredev)
+{
+ struct smscore_buffer_t *cb;
+ int num_buffers = 0;
+ int retry = 0;
+
+ mutex_lock(&g_smscore_deviceslock);
+
+ /* Release input device (IR) resources */
+ sms_ir_exit(coredev);
+
+ smscore_notify_clients(coredev);
+ smscore_notify_callbacks(coredev, NULL, 0);
+
+ /* at this point all buffers should be back
+ * onresponse must no longer be called */
+
+ while (1) {
+ while (!list_empty(&coredev->buffers)) {
+ cb = (struct smscore_buffer_t *) coredev->buffers.next;
+ list_del(&cb->entry);
+ kfree(cb);
+ num_buffers++;
+ }
+ if (num_buffers == coredev->num_buffers)
+ break;
+ if (++retry > 10) {
+ pr_info("exiting although not all buffers released.\n");
+ break;
+ }
+
+ pr_debug("waiting for %d buffer(s)\n",
+ coredev->num_buffers - num_buffers);
+ mutex_unlock(&g_smscore_deviceslock);
+ msleep(100);
+ mutex_lock(&g_smscore_deviceslock);
+ }
+
+ pr_debug("freed %d buffers\n", num_buffers);
+
+ if (coredev->common_buffer) {
+ if (coredev->usb_device)
+ kfree(coredev->common_buffer);
+ else
+ dma_free_coherent(coredev->device,
+ coredev->common_buffer_size,
+ coredev->common_buffer,
+ coredev->common_buffer_phys);
+ }
+ kfree(coredev->fw_buf);
+
+ list_del(&coredev->entry);
+ kfree(coredev);
+
+ mutex_unlock(&g_smscore_deviceslock);
+
+ pr_debug("device %p destroyed\n", coredev);
+}
+EXPORT_SYMBOL_GPL(smscore_unregister_device);
+
+static int smscore_detect_mode(struct smscore_device_t *coredev)
+{
+ void *buffer = kmalloc(sizeof(struct sms_msg_hdr) + SMS_DMA_ALIGNMENT,
+ GFP_KERNEL | coredev->gfp_buf_flags);
+ struct sms_msg_hdr *msg =
+ (struct sms_msg_hdr *) SMS_ALIGN_ADDRESS(buffer);
+ int rc;
+
+ if (!buffer)
+ return -ENOMEM;
+
+ SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ,
+ sizeof(struct sms_msg_hdr));
+
+ rc = smscore_sendrequest_and_wait(coredev, msg, msg->msg_length,
+ &coredev->version_ex_done);
+ if (rc == -ETIME) {
+ pr_err("MSG_SMS_GET_VERSION_EX_REQ failed first try\n");
+
+ if (wait_for_completion_timeout(&coredev->resume_done,
+ msecs_to_jiffies(5000))) {
+ rc = smscore_sendrequest_and_wait(
+ coredev, msg, msg->msg_length,
+ &coredev->version_ex_done);
+ if (rc < 0)
+ pr_err("MSG_SMS_GET_VERSION_EX_REQ failed second try, rc %d\n",
+ rc);
+ } else
+ rc = -ETIME;
+ }
+
+ kfree(buffer);
+
+ return rc;
+}
+
+/*
+ * send init device request and wait for response
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ * @param mode requested mode of operation
+ *
+ * return: 0 on success, <0 on error.
+ */
+static int smscore_init_device(struct smscore_device_t *coredev, int mode)
+{
+ void *buffer;
+ struct sms_msg_data *msg;
+ int rc = 0;
+
+ buffer = kmalloc(sizeof(struct sms_msg_data) +
+ SMS_DMA_ALIGNMENT, GFP_KERNEL | coredev->gfp_buf_flags);
+ if (!buffer)
+ return -ENOMEM;
+
+ msg = (struct sms_msg_data *)SMS_ALIGN_ADDRESS(buffer);
+ SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_INIT_DEVICE_REQ,
+ sizeof(struct sms_msg_data));
+ msg->msg_data = mode;
+
+ rc = smscore_sendrequest_and_wait(coredev, msg,
+ msg->x_msg_header. msg_length,
+ &coredev->init_device_done);
+
+ kfree(buffer);
+ return rc;
+}
+
+/*
+ * calls device handler to change mode of operation
+ * NOTE: stellar/usb may disconnect when changing mode
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ * @param mode requested mode of operation
+ *
+ * return: 0 on success, <0 on error.
+ */
+int smscore_set_device_mode(struct smscore_device_t *coredev, int mode)
+{
+ int rc = 0;
+
+ pr_debug("set device mode to %d\n", mode);
+ if (coredev->device_flags & SMS_DEVICE_FAMILY2) {
+ if (mode <= DEVICE_MODE_NONE || mode >= DEVICE_MODE_MAX) {
+ pr_err("invalid mode specified %d\n", mode);
+ return -EINVAL;
+ }
+
+ smscore_registry_setmode(coredev->devpath, mode);
+
+ if (!(coredev->device_flags & SMS_DEVICE_NOT_READY)) {
+ rc = smscore_detect_mode(coredev);
+ if (rc < 0) {
+ pr_err("mode detect failed %d\n", rc);
+ return rc;
+ }
+ }
+
+ if (coredev->mode == mode) {
+ pr_debug("device mode %d already set\n", mode);
+ return 0;
+ }
+
+ if (!(coredev->modes_supported & (1 << mode))) {
+ rc = smscore_load_firmware_from_file(coredev, mode);
+ if (rc >= 0)
+ pr_debug("firmware download success\n");
+ } else {
+ pr_debug("mode %d is already supported by running firmware\n",
+ mode);
+ }
+ if (coredev->fw_version >= 0x800) {
+ rc = smscore_init_device(coredev, mode);
+ if (rc < 0)
+ pr_err("device init failed, rc %d.\n", rc);
+ }
+ } else {
+ if (mode <= DEVICE_MODE_NONE || mode >= DEVICE_MODE_MAX) {
+ pr_err("invalid mode specified %d\n", mode);
+ return -EINVAL;
+ }
+
+ smscore_registry_setmode(coredev->devpath, mode);
+
+ if (coredev->detectmode_handler)
+ coredev->detectmode_handler(coredev->context,
+ &coredev->mode);
+
+ if (coredev->mode != mode && coredev->setmode_handler)
+ rc = coredev->setmode_handler(coredev->context, mode);
+ }
+
+ if (rc >= 0) {
+ char *buffer;
+ coredev->mode = mode;
+ coredev->device_flags &= ~SMS_DEVICE_NOT_READY;
+
+ buffer = kmalloc(sizeof(struct sms_msg_data) +
+ SMS_DMA_ALIGNMENT, GFP_KERNEL | coredev->gfp_buf_flags);
+ if (buffer) {
+ struct sms_msg_data *msg = (struct sms_msg_data *) SMS_ALIGN_ADDRESS(buffer);
+
+ SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_INIT_DEVICE_REQ,
+ sizeof(struct sms_msg_data));
+ msg->msg_data = mode;
+
+ rc = smscore_sendrequest_and_wait(
+ coredev, msg, msg->x_msg_header.msg_length,
+ &coredev->init_device_done);
+
+ kfree(buffer);
+ }
+ }
+
+ if (rc < 0)
+ pr_err("return error code %d.\n", rc);
+ else
+ pr_debug("Success setting device mode.\n");
+
+ return rc;
+}
+
+/*
+ * calls device handler to get current mode of operation
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ *
+ * return: current mode
+ */
+int smscore_get_device_mode(struct smscore_device_t *coredev)
+{
+ return coredev->mode;
+}
+EXPORT_SYMBOL_GPL(smscore_get_device_mode);
+
+/*
+ * find client by response id & type within the clients list.
+ * return client handle or NULL.
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ * @param data_type client data type (SMS_DONT_CARE for all types)
+ * @param id client id (SMS_DONT_CARE for all id)
+ *
+ */
+static struct
+smscore_client_t *smscore_find_client(struct smscore_device_t *coredev,
+ int data_type, int id)
+{
+ struct list_head *first;
+ struct smscore_client_t *client;
+ unsigned long flags;
+ struct list_head *firstid;
+ struct smscore_idlist_t *client_id;
+
+ spin_lock_irqsave(&coredev->clientslock, flags);
+ first = &coredev->clients;
+ list_for_each_entry(client, first, entry) {
+ firstid = &client->idlist;
+ list_for_each_entry(client_id, firstid, entry) {
+ if ((client_id->id == id) &&
+ (client_id->data_type == data_type ||
+ (client_id->data_type == 0)))
+ goto found;
+ }
+ }
+ client = NULL;
+found:
+ spin_unlock_irqrestore(&coredev->clientslock, flags);
+ return client;
+}
+
+/*
+ * find client by response id/type, call clients onresponse handler
+ * return buffer to pool on error
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ * @param cb pointer to response buffer descriptor
+ *
+ */
+void smscore_onresponse(struct smscore_device_t *coredev,
+ struct smscore_buffer_t *cb) {
+ struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) ((u8 *) cb->p
+ + cb->offset);
+ struct smscore_client_t *client;
+ int rc = -EBUSY;
+ static unsigned long last_sample_time; /* = 0; */
+ static int data_total; /* = 0; */
+ unsigned long time_now = jiffies_to_msecs(jiffies);
+
+ if (!last_sample_time)
+ last_sample_time = time_now;
+
+ if (time_now - last_sample_time > 10000) {
+ pr_debug("data rate %d bytes/secs\n",
+ (int)((data_total * 1000) /
+ (time_now - last_sample_time)));
+
+ last_sample_time = time_now;
+ data_total = 0;
+ }
+
+ data_total += cb->size;
+ /* Do we need to re-route? */
+ if ((phdr->msg_type == MSG_SMS_HO_PER_SLICES_IND) ||
+ (phdr->msg_type == MSG_SMS_TRANSMISSION_IND)) {
+ if (coredev->mode == DEVICE_MODE_DVBT_BDA)
+ phdr->msg_dst_id = DVBT_BDA_CONTROL_MSG_ID;
+ }
+
+
+ client = smscore_find_client(coredev, phdr->msg_type, phdr->msg_dst_id);
+
+ /* If no client registered for type & id,
+ * check for control client where type is not registered */
+ if (client)
+ rc = client->onresponse_handler(client->context, cb);
+
+ if (rc < 0) {
+ switch (phdr->msg_type) {
+ case MSG_SMS_ISDBT_TUNE_RES:
+ break;
+ case MSG_SMS_RF_TUNE_RES:
+ break;
+ case MSG_SMS_SIGNAL_DETECTED_IND:
+ break;
+ case MSG_SMS_NO_SIGNAL_IND:
+ break;
+ case MSG_SMS_SPI_INT_LINE_SET_RES:
+ break;
+ case MSG_SMS_INTERFACE_LOCK_IND:
+ break;
+ case MSG_SMS_INTERFACE_UNLOCK_IND:
+ break;
+ case MSG_SMS_GET_VERSION_EX_RES:
+ {
+ struct sms_version_res *ver =
+ (struct sms_version_res *) phdr;
+ pr_debug("Firmware id %d prots 0x%x ver %d.%d\n",
+ ver->firmware_id, ver->supported_protocols,
+ ver->rom_ver_major, ver->rom_ver_minor);
+
+ coredev->mode = ver->firmware_id == 255 ?
+ DEVICE_MODE_NONE : ver->firmware_id;
+ coredev->modes_supported = ver->supported_protocols;
+ coredev->fw_version = ver->rom_ver_major << 8 |
+ ver->rom_ver_minor;
+
+ complete(&coredev->version_ex_done);
+ break;
+ }
+ case MSG_SMS_INIT_DEVICE_RES:
+ complete(&coredev->init_device_done);
+ break;
+ case MSG_SW_RELOAD_START_RES:
+ complete(&coredev->reload_start_done);
+ break;
+ case MSG_SMS_DATA_VALIDITY_RES:
+ {
+ struct sms_msg_data *validity = (struct sms_msg_data *) phdr;
+
+ pr_debug("MSG_SMS_DATA_VALIDITY_RES, checksum = 0x%x\n",
+ validity->msg_data);
+ complete(&coredev->data_validity_done);
+ break;
+ }
+ case MSG_SMS_DATA_DOWNLOAD_RES:
+ complete(&coredev->data_download_done);
+ break;
+ case MSG_SW_RELOAD_EXEC_RES:
+ break;
+ case MSG_SMS_SWDOWNLOAD_TRIGGER_RES:
+ complete(&coredev->trigger_done);
+ break;
+ case MSG_SMS_SLEEP_RESUME_COMP_IND:
+ complete(&coredev->resume_done);
+ break;
+ case MSG_SMS_GPIO_CONFIG_EX_RES:
+ complete(&coredev->gpio_configuration_done);
+ break;
+ case MSG_SMS_GPIO_SET_LEVEL_RES:
+ complete(&coredev->gpio_set_level_done);
+ break;
+ case MSG_SMS_GPIO_GET_LEVEL_RES:
+ {
+ u32 *msgdata = (u32 *) phdr;
+ coredev->gpio_get_res = msgdata[1];
+ pr_debug("gpio level %d\n",
+ coredev->gpio_get_res);
+ complete(&coredev->gpio_get_level_done);
+ break;
+ }
+ case MSG_SMS_START_IR_RES:
+ complete(&coredev->ir_init_done);
+ break;
+ case MSG_SMS_IR_SAMPLES_IND:
+ sms_ir_event(coredev,
+ (const char *)
+ ((char *)phdr
+ + sizeof(struct sms_msg_hdr)),
+ (int)phdr->msg_length
+ - sizeof(struct sms_msg_hdr));
+ break;
+
+ case MSG_SMS_DVBT_BDA_DATA:
+ /*
+ * It can be received here, if the frontend is
+ * tuned into a valid channel and the proper firmware
+ * is loaded. That happens when the module got removed
+ * and re-inserted, without powering the device off
+ */
+ break;
+
+ default:
+ pr_debug("message %s(%d) not handled.\n",
+ smscore_translate_msg(phdr->msg_type),
+ phdr->msg_type);
+ break;
+ }
+ smscore_putbuffer(coredev, cb);
+ }
+}
+EXPORT_SYMBOL_GPL(smscore_onresponse);
+
+/*
+ * return pointer to next free buffer descriptor from core pool
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ *
+ * return: pointer to descriptor on success, NULL on error.
+ */
+
+static struct smscore_buffer_t *get_entry(struct smscore_device_t *coredev)
+{
+ struct smscore_buffer_t *cb = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&coredev->bufferslock, flags);
+ if (!list_empty(&coredev->buffers)) {
+ cb = (struct smscore_buffer_t *) coredev->buffers.next;
+ list_del(&cb->entry);
+ }
+ spin_unlock_irqrestore(&coredev->bufferslock, flags);
+ return cb;
+}
+
+struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev)
+{
+ struct smscore_buffer_t *cb = NULL;
+
+ wait_event(coredev->buffer_mng_waitq, (cb = get_entry(coredev)));
+
+ return cb;
+}
+EXPORT_SYMBOL_GPL(smscore_getbuffer);
+
+/*
+ * return buffer descriptor to a pool
+ *
+ * @param coredev pointer to a coredev object returned by
+ * smscore_register_device
+ * @param cb pointer buffer descriptor
+ *
+ */
+void smscore_putbuffer(struct smscore_device_t *coredev,
+ struct smscore_buffer_t *cb) {
+ wake_up_interruptible(&coredev->buffer_mng_waitq);
+ list_add_locked(&cb->entry, &coredev->buffers, &coredev->bufferslock);
+}
+EXPORT_SYMBOL_GPL(smscore_putbuffer);
+
+static int smscore_validate_client(struct smscore_device_t *coredev,
+ struct smscore_client_t *client,
+ int data_type, int id)
+{
+ struct smscore_idlist_t *listentry;
+ struct smscore_client_t *registered_client;
+
+ if (!client) {
+ pr_err("bad parameter.\n");
+ return -EINVAL;
+ }
+ registered_client = smscore_find_client(coredev, data_type, id);
+ if (registered_client == client)
+ return 0;
+
+ if (registered_client) {
+ pr_err("The msg ID already registered to another client.\n");
+ return -EEXIST;
+ }
+ listentry = kzalloc(sizeof(*listentry), GFP_KERNEL);
+ if (!listentry)
+ return -ENOMEM;
+
+ listentry->id = id;
+ listentry->data_type = data_type;
+ list_add_locked(&listentry->entry, &client->idlist,
+ &coredev->clientslock);
+ return 0;
+}
+
+/*
+ * creates smsclient object, check that id is taken by another client
+ *
+ * @param coredev pointer to a coredev object from clients hotplug
+ * @param initial_id all messages with this id would be sent to this client
+ * @param data_type all messages of this type would be sent to this client
+ * @param onresponse_handler client handler that is called to
+ * process incoming messages
+ * @param onremove_handler client handler that is called when device is removed
+ * @param context client-specific context
+ * @param client pointer to a value that receives created smsclient object
+ *
+ * return: 0 on success, <0 on error.
+ */
+int smscore_register_client(struct smscore_device_t *coredev,
+ struct smsclient_params_t *params,
+ struct smscore_client_t **client)
+{
+ struct smscore_client_t *newclient;
+ /* check that no other channel with same parameters exists */
+ if (smscore_find_client(coredev, params->data_type,
+ params->initial_id)) {
+ pr_err("Client already exist.\n");
+ return -EEXIST;
+ }
+
+ newclient = kzalloc(sizeof(*newclient), GFP_KERNEL);
+ if (!newclient)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&newclient->idlist);
+ newclient->coredev = coredev;
+ newclient->onresponse_handler = params->onresponse_handler;
+ newclient->onremove_handler = params->onremove_handler;
+ newclient->context = params->context;
+ list_add_locked(&newclient->entry, &coredev->clients,
+ &coredev->clientslock);
+ smscore_validate_client(coredev, newclient, params->data_type,
+ params->initial_id);
+ *client = newclient;
+ pr_debug("%p %d %d\n", params->context, params->data_type,
+ params->initial_id);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(smscore_register_client);
+
+/*
+ * frees smsclient object and all subclients associated with it
+ *
+ * @param client pointer to smsclient object returned by
+ * smscore_register_client
+ *
+ */
+void smscore_unregister_client(struct smscore_client_t *client)
+{
+ struct smscore_device_t *coredev = client->coredev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&coredev->clientslock, flags);
+
+
+ while (!list_empty(&client->idlist)) {
+ struct smscore_idlist_t *identry =
+ (struct smscore_idlist_t *) client->idlist.next;
+ list_del(&identry->entry);
+ kfree(identry);
+ }
+
+ pr_debug("%p\n", client->context);
+
+ list_del(&client->entry);
+ kfree(client);
+
+ spin_unlock_irqrestore(&coredev->clientslock, flags);
+}
+EXPORT_SYMBOL_GPL(smscore_unregister_client);
+
+/*
+ * verifies that source id is not taken by another client,
+ * calls device handler to send requests to the device
+ *
+ * @param client pointer to smsclient object returned by
+ * smscore_register_client
+ * @param buffer pointer to a request buffer
+ * @param size size (in bytes) of request buffer
+ *
+ * return: 0 on success, <0 on error.
+ */
+int smsclient_sendrequest(struct smscore_client_t *client,
+ void *buffer, size_t size)
+{
+ struct smscore_device_t *coredev;
+ struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) buffer;
+ int rc;
+
+ if (!client) {
+ pr_err("Got NULL client\n");
+ return -EINVAL;
+ }
+
+ coredev = client->coredev;
+
+ /* check that no other channel with same id exists */
+ if (!coredev) {
+ pr_err("Got NULL coredev\n");
+ return -EINVAL;
+ }
+
+ rc = smscore_validate_client(client->coredev, client, 0,
+ phdr->msg_src_id);
+ if (rc < 0)
+ return rc;
+
+ return coredev->sendrequest_handler(coredev->context, buffer, size);
+}
+EXPORT_SYMBOL_GPL(smsclient_sendrequest);
+
+
+/* old GPIO managements implementation */
+int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin,
+ struct smscore_config_gpio *pinconfig)
+{
+ struct {
+ struct sms_msg_hdr hdr;
+ u32 data[6];
+ } msg;
+
+ if (coredev->device_flags & SMS_DEVICE_FAMILY2) {
+ msg.hdr.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ msg.hdr.msg_dst_id = HIF_TASK;
+ msg.hdr.msg_flags = 0;
+ msg.hdr.msg_type = MSG_SMS_GPIO_CONFIG_EX_REQ;
+ msg.hdr.msg_length = sizeof(msg);
+
+ msg.data[0] = pin;
+ msg.data[1] = pinconfig->pullupdown;
+
+ /* Convert slew rate for Nova: Fast(0) = 3 / Slow(1) = 0; */
+ msg.data[2] = pinconfig->outputslewrate == 0 ? 3 : 0;
+
+ switch (pinconfig->outputdriving) {
+ case SMS_GPIO_OUTPUTDRIVING_S_16mA:
+ msg.data[3] = 7; /* Nova - 16mA */
+ break;
+ case SMS_GPIO_OUTPUTDRIVING_S_12mA:
+ msg.data[3] = 5; /* Nova - 11mA */
+ break;
+ case SMS_GPIO_OUTPUTDRIVING_S_8mA:
+ msg.data[3] = 3; /* Nova - 7mA */
+ break;
+ case SMS_GPIO_OUTPUTDRIVING_S_4mA:
+ default:
+ msg.data[3] = 2; /* Nova - 4mA */
+ break;
+ }
+
+ msg.data[4] = pinconfig->direction;
+ msg.data[5] = 0;
+ } else /* TODO: SMS_DEVICE_FAMILY1 */
+ return -EINVAL;
+
+ return coredev->sendrequest_handler(coredev->context,
+ &msg, sizeof(msg));
+}
+
+int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level)
+{
+ struct {
+ struct sms_msg_hdr hdr;
+ u32 data[3];
+ } msg;
+
+ if (pin > MAX_GPIO_PIN_NUMBER)
+ return -EINVAL;
+
+ msg.hdr.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ msg.hdr.msg_dst_id = HIF_TASK;
+ msg.hdr.msg_flags = 0;
+ msg.hdr.msg_type = MSG_SMS_GPIO_SET_LEVEL_REQ;
+ msg.hdr.msg_length = sizeof(msg);
+
+ msg.data[0] = pin;
+ msg.data[1] = level ? 1 : 0;
+ msg.data[2] = 0;
+
+ return coredev->sendrequest_handler(coredev->context,
+ &msg, sizeof(msg));
+}
+
+/* new GPIO management implementation */
+static int get_gpio_pin_params(u32 pin_num, u32 *p_translatedpin_num,
+ u32 *p_group_num, u32 *p_group_cfg) {
+
+ *p_group_cfg = 1;
+
+ if (pin_num <= 1) {
+ *p_translatedpin_num = 0;
+ *p_group_num = 9;
+ *p_group_cfg = 2;
+ } else if (pin_num >= 2 && pin_num <= 6) {
+ *p_translatedpin_num = 2;
+ *p_group_num = 0;
+ *p_group_cfg = 2;
+ } else if (pin_num >= 7 && pin_num <= 11) {
+ *p_translatedpin_num = 7;
+ *p_group_num = 1;
+ } else if (pin_num >= 12 && pin_num <= 15) {
+ *p_translatedpin_num = 12;
+ *p_group_num = 2;
+ *p_group_cfg = 3;
+ } else if (pin_num == 16) {
+ *p_translatedpin_num = 16;
+ *p_group_num = 23;
+ } else if (pin_num >= 17 && pin_num <= 24) {
+ *p_translatedpin_num = 17;
+ *p_group_num = 3;
+ } else if (pin_num == 25) {
+ *p_translatedpin_num = 25;
+ *p_group_num = 6;
+ } else if (pin_num >= 26 && pin_num <= 28) {
+ *p_translatedpin_num = 26;
+ *p_group_num = 4;
+ } else if (pin_num == 29) {
+ *p_translatedpin_num = 29;
+ *p_group_num = 5;
+ *p_group_cfg = 2;
+ } else if (pin_num == 30) {
+ *p_translatedpin_num = 30;
+ *p_group_num = 8;
+ } else if (pin_num == 31) {
+ *p_translatedpin_num = 31;
+ *p_group_num = 17;
+ } else
+ return -1;
+
+ *p_group_cfg <<= 24;
+
+ return 0;
+}
+
+int smscore_gpio_configure(struct smscore_device_t *coredev, u8 pin_num,
+ struct smscore_config_gpio *p_gpio_config) {
+
+ u32 total_len;
+ u32 translatedpin_num = 0;
+ u32 group_num = 0;
+ u32 electric_char;
+ u32 group_cfg;
+ void *buffer;
+ int rc;
+
+ struct set_gpio_msg {
+ struct sms_msg_hdr x_msg_header;
+ u32 msg_data[6];
+ } *p_msg;
+
+
+ if (pin_num > MAX_GPIO_PIN_NUMBER)
+ return -EINVAL;
+
+ if (!p_gpio_config)
+ return -EINVAL;
+
+ total_len = sizeof(struct sms_msg_hdr) + (sizeof(u32) * 6);
+
+ buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT,
+ GFP_KERNEL | coredev->gfp_buf_flags);
+ if (!buffer)
+ return -ENOMEM;
+
+ p_msg = (struct set_gpio_msg *) SMS_ALIGN_ADDRESS(buffer);
+
+ p_msg->x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ p_msg->x_msg_header.msg_dst_id = HIF_TASK;
+ p_msg->x_msg_header.msg_flags = 0;
+ p_msg->x_msg_header.msg_length = (u16) total_len;
+ p_msg->msg_data[0] = pin_num;
+
+ if (!(coredev->device_flags & SMS_DEVICE_FAMILY2)) {
+ p_msg->x_msg_header.msg_type = MSG_SMS_GPIO_CONFIG_REQ;
+ if (get_gpio_pin_params(pin_num, &translatedpin_num, &group_num,
+ &group_cfg) != 0) {
+ rc = -EINVAL;
+ goto free;
+ }
+
+ p_msg->msg_data[1] = translatedpin_num;
+ p_msg->msg_data[2] = group_num;
+ electric_char = (p_gpio_config->pullupdown)
+ | (p_gpio_config->inputcharacteristics << 2)
+ | (p_gpio_config->outputslewrate << 3)
+ | (p_gpio_config->outputdriving << 4);
+ p_msg->msg_data[3] = electric_char;
+ p_msg->msg_data[4] = p_gpio_config->direction;
+ p_msg->msg_data[5] = group_cfg;
+ } else {
+ p_msg->x_msg_header.msg_type = MSG_SMS_GPIO_CONFIG_EX_REQ;
+ p_msg->msg_data[1] = p_gpio_config->pullupdown;
+ p_msg->msg_data[2] = p_gpio_config->outputslewrate;
+ p_msg->msg_data[3] = p_gpio_config->outputdriving;
+ p_msg->msg_data[4] = p_gpio_config->direction;
+ p_msg->msg_data[5] = 0;
+ }
+
+ rc = smscore_sendrequest_and_wait(coredev, p_msg, total_len,
+ &coredev->gpio_configuration_done);
+
+ if (rc != 0) {
+ if (rc == -ETIME)
+ pr_err("smscore_gpio_configure timeout\n");
+ else
+ pr_err("smscore_gpio_configure error\n");
+ }
+free:
+ kfree(buffer);
+
+ return rc;
+}
+
+int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 pin_num,
+ u8 new_level) {
+
+ u32 total_len;
+ int rc;
+ void *buffer;
+
+ struct set_gpio_msg {
+ struct sms_msg_hdr x_msg_header;
+ u32 msg_data[3]; /* keep it 3 ! */
+ } *p_msg;
+
+ if ((new_level > 1) || (pin_num > MAX_GPIO_PIN_NUMBER))
+ return -EINVAL;
+
+ total_len = sizeof(struct sms_msg_hdr) +
+ (3 * sizeof(u32)); /* keep it 3 ! */
+
+ buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT,
+ GFP_KERNEL | coredev->gfp_buf_flags);
+ if (!buffer)
+ return -ENOMEM;
+
+ p_msg = (struct set_gpio_msg *) SMS_ALIGN_ADDRESS(buffer);
+
+ p_msg->x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ p_msg->x_msg_header.msg_dst_id = HIF_TASK;
+ p_msg->x_msg_header.msg_flags = 0;
+ p_msg->x_msg_header.msg_type = MSG_SMS_GPIO_SET_LEVEL_REQ;
+ p_msg->x_msg_header.msg_length = (u16) total_len;
+ p_msg->msg_data[0] = pin_num;
+ p_msg->msg_data[1] = new_level;
+
+ /* Send message to SMS */
+ rc = smscore_sendrequest_and_wait(coredev, p_msg, total_len,
+ &coredev->gpio_set_level_done);
+
+ if (rc != 0) {
+ if (rc == -ETIME)
+ pr_err("smscore_gpio_set_level timeout\n");
+ else
+ pr_err("smscore_gpio_set_level error\n");
+ }
+ kfree(buffer);
+
+ return rc;
+}
+
+int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 pin_num,
+ u8 *level) {
+
+ u32 total_len;
+ int rc;
+ void *buffer;
+
+ struct set_gpio_msg {
+ struct sms_msg_hdr x_msg_header;
+ u32 msg_data[2];
+ } *p_msg;
+
+
+ if (pin_num > MAX_GPIO_PIN_NUMBER)
+ return -EINVAL;
+
+ total_len = sizeof(struct sms_msg_hdr) + (2 * sizeof(u32));
+
+ buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT,
+ GFP_KERNEL | coredev->gfp_buf_flags);
+ if (!buffer)
+ return -ENOMEM;
+
+ p_msg = (struct set_gpio_msg *) SMS_ALIGN_ADDRESS(buffer);
+
+ p_msg->x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ p_msg->x_msg_header.msg_dst_id = HIF_TASK;
+ p_msg->x_msg_header.msg_flags = 0;
+ p_msg->x_msg_header.msg_type = MSG_SMS_GPIO_GET_LEVEL_REQ;
+ p_msg->x_msg_header.msg_length = (u16) total_len;
+ p_msg->msg_data[0] = pin_num;
+ p_msg->msg_data[1] = 0;
+
+ /* Send message to SMS */
+ rc = smscore_sendrequest_and_wait(coredev, p_msg, total_len,
+ &coredev->gpio_get_level_done);
+
+ if (rc != 0) {
+ if (rc == -ETIME)
+ pr_err("smscore_gpio_get_level timeout\n");
+ else
+ pr_err("smscore_gpio_get_level error\n");
+ }
+ kfree(buffer);
+
+ /* Its a race between other gpio_get_level() and the copy of the single
+ * global 'coredev->gpio_get_res' to the function's variable 'level'
+ */
+ *level = coredev->gpio_get_res;
+
+ return rc;
+}
+
+static int __init smscore_module_init(void)
+{
+ INIT_LIST_HEAD(&g_smscore_notifyees);
+ INIT_LIST_HEAD(&g_smscore_devices);
+ INIT_LIST_HEAD(&g_smscore_registry);
+
+ return 0;
+}
+
+static void __exit smscore_module_exit(void)
+{
+ mutex_lock(&g_smscore_deviceslock);
+ while (!list_empty(&g_smscore_notifyees)) {
+ struct smscore_device_notifyee_t *notifyee =
+ (struct smscore_device_notifyee_t *)
+ g_smscore_notifyees.next;
+
+ list_del(&notifyee->entry);
+ kfree(notifyee);
+ }
+ mutex_unlock(&g_smscore_deviceslock);
+
+ mutex_lock(&g_smscore_registrylock);
+ while (!list_empty(&g_smscore_registry)) {
+ struct smscore_registry_entry_t *entry =
+ (struct smscore_registry_entry_t *)
+ g_smscore_registry.next;
+
+ list_del(&entry->entry);
+ kfree(entry);
+ }
+ mutex_unlock(&g_smscore_registrylock);
+
+ pr_debug("\n");
+}
+
+module_init(smscore_module_init);
+module_exit(smscore_module_exit);
+
+MODULE_DESCRIPTION("Siano MDTV Core module");
+MODULE_AUTHOR("Siano Mobile Silicon, Inc. <uris@siano-ms.com>");
+MODULE_LICENSE("GPL");
+
+/* This should match what's defined at smscoreapi.h */
+MODULE_FIRMWARE(SMS_FW_ATSC_DENVER);
+MODULE_FIRMWARE(SMS_FW_CMMB_MING_APP);
+MODULE_FIRMWARE(SMS_FW_CMMB_VEGA_12MHZ);
+MODULE_FIRMWARE(SMS_FW_CMMB_VENICE_12MHZ);
+MODULE_FIRMWARE(SMS_FW_DVBH_RIO);
+MODULE_FIRMWARE(SMS_FW_DVB_NOVA_12MHZ_B0);
+MODULE_FIRMWARE(SMS_FW_DVB_NOVA_12MHZ);
+MODULE_FIRMWARE(SMS_FW_DVB_RIO);
+MODULE_FIRMWARE(SMS_FW_FM_RADIO);
+MODULE_FIRMWARE(SMS_FW_FM_RADIO_RIO);
+MODULE_FIRMWARE(SMS_FW_DVBT_HCW_55XXX);
+MODULE_FIRMWARE(SMS_FW_ISDBT_HCW_55XXX);
+MODULE_FIRMWARE(SMS_FW_ISDBT_NOVA_12MHZ_B0);
+MODULE_FIRMWARE(SMS_FW_ISDBT_NOVA_12MHZ);
+MODULE_FIRMWARE(SMS_FW_ISDBT_PELE);
+MODULE_FIRMWARE(SMS_FW_ISDBT_RIO);
+MODULE_FIRMWARE(SMS_FW_DVBT_NOVA_A);
+MODULE_FIRMWARE(SMS_FW_DVBT_NOVA_B);
+MODULE_FIRMWARE(SMS_FW_DVBT_STELLAR);
+MODULE_FIRMWARE(SMS_FW_TDMB_DENVER);
+MODULE_FIRMWARE(SMS_FW_TDMB_NOVA_12MHZ_B0);
+MODULE_FIRMWARE(SMS_FW_TDMB_NOVA_12MHZ);
diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h
new file mode 100644
index 000000000000..d945a2d6d624
--- /dev/null
+++ b/drivers/media/common/siano/smscoreapi.h
@@ -0,0 +1,1144 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/****************************************************************
+
+Siano Mobile Silicon, Inc.
+MDTV receiver kernel modules.
+Copyright (C) 2006-2008, Uri Shkolnik, Anatoly Greenblat
+
+
+****************************************************************/
+
+#ifndef __SMS_CORE_API_H__
+#define __SMS_CORE_API_H__
+
+#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/timer.h>
+
+#include <media/media-device.h>
+
+#include <asm/page.h>
+
+#include "smsir.h"
+
+/*
+ * Define the firmware names used by the driver.
+ * Those should match what's used at smscoreapi.c and sms-cards.c
+ * including the MODULE_FIRMWARE() macros at the end of smscoreapi.c
+ */
+#define SMS_FW_ATSC_DENVER "atsc_denver.inp"
+#define SMS_FW_CMMB_MING_APP "cmmb_ming_app.inp"
+#define SMS_FW_CMMB_VEGA_12MHZ "cmmb_vega_12mhz.inp"
+#define SMS_FW_CMMB_VENICE_12MHZ "cmmb_venice_12mhz.inp"
+#define SMS_FW_DVBH_RIO "dvbh_rio.inp"
+#define SMS_FW_DVB_NOVA_12MHZ_B0 "dvb_nova_12mhz_b0.inp"
+#define SMS_FW_DVB_NOVA_12MHZ "dvb_nova_12mhz.inp"
+#define SMS_FW_DVB_RIO "dvb_rio.inp"
+#define SMS_FW_FM_RADIO "fm_radio.inp"
+#define SMS_FW_FM_RADIO_RIO "fm_radio_rio.inp"
+#define SMS_FW_DVBT_HCW_55XXX "sms1xxx-hcw-55xxx-dvbt-02.fw"
+#define SMS_FW_ISDBT_HCW_55XXX "sms1xxx-hcw-55xxx-isdbt-02.fw"
+#define SMS_FW_ISDBT_NOVA_12MHZ_B0 "isdbt_nova_12mhz_b0.inp"
+#define SMS_FW_ISDBT_NOVA_12MHZ "isdbt_nova_12mhz.inp"
+#define SMS_FW_ISDBT_PELE "isdbt_pele.inp"
+#define SMS_FW_ISDBT_RIO "isdbt_rio.inp"
+#define SMS_FW_DVBT_NOVA_A "sms1xxx-nova-a-dvbt-01.fw"
+#define SMS_FW_DVBT_NOVA_B "sms1xxx-nova-b-dvbt-01.fw"
+#define SMS_FW_DVBT_STELLAR "sms1xxx-stellar-dvbt-01.fw"
+#define SMS_FW_TDMB_DENVER "tdmb_denver.inp"
+#define SMS_FW_TDMB_NOVA_12MHZ_B0 "tdmb_nova_12mhz_b0.inp"
+#define SMS_FW_TDMB_NOVA_12MHZ "tdmb_nova_12mhz.inp"
+
+#define SMS_PROTOCOL_MAX_RAOUNDTRIP_MS (10000)
+#define SMS_ALLOC_ALIGNMENT 128
+#define SMS_DMA_ALIGNMENT 16
+#define SMS_ALIGN_ADDRESS(addr) \
+ ((((uintptr_t)(addr)) + (SMS_DMA_ALIGNMENT-1)) & ~(SMS_DMA_ALIGNMENT-1))
+
+#define SMS_DEVICE_FAMILY1 0
+#define SMS_DEVICE_FAMILY2 1
+#define SMS_ROM_NO_RESPONSE 2
+#define SMS_DEVICE_NOT_READY 0x8000000
+
+enum sms_device_type_st {
+ SMS_UNKNOWN_TYPE = -1,
+ SMS_STELLAR = 0,
+ SMS_NOVA_A0,
+ SMS_NOVA_B0,
+ SMS_VEGA,
+ SMS_VENICE,
+ SMS_MING,
+ SMS_PELE,
+ SMS_RIO,
+ SMS_DENVER_1530,
+ SMS_DENVER_2160,
+ SMS_NUM_OF_DEVICE_TYPES
+};
+
+enum sms_power_mode_st {
+ SMS_POWER_MODE_ACTIVE,
+ SMS_POWER_MODE_SUSPENDED
+};
+
+struct smscore_device_t;
+struct smscore_client_t;
+struct smscore_buffer_t;
+
+typedef int (*hotplug_t)(struct smscore_device_t *coredev,
+ struct device *device, int arrival);
+
+typedef int (*setmode_t)(void *context, int mode);
+typedef void (*detectmode_t)(void *context, int *mode);
+typedef int (*sendrequest_t)(void *context, void *buffer, size_t size);
+typedef int (*preload_t)(void *context);
+typedef int (*postload_t)(void *context);
+
+typedef int (*onresponse_t)(void *context, struct smscore_buffer_t *cb);
+typedef void (*onremove_t)(void *context);
+
+struct smscore_buffer_t {
+ /* public members, once passed to clients can be changed freely */
+ struct list_head entry;
+ int size;
+ int offset;
+
+ /* private members, read-only for clients */
+ void *p;
+ dma_addr_t phys;
+ unsigned long offset_in_common;
+};
+
+struct smsdevice_params_t {
+ struct device *device;
+ struct usb_device *usb_device;
+
+ int buffer_size;
+ int num_buffers;
+
+ char devpath[32];
+ unsigned long flags;
+
+ setmode_t setmode_handler;
+ detectmode_t detectmode_handler;
+ sendrequest_t sendrequest_handler;
+ preload_t preload_handler;
+ postload_t postload_handler;
+
+ void *context;
+ enum sms_device_type_st device_type;
+};
+
+struct smsclient_params_t {
+ int initial_id;
+ int data_type;
+ onresponse_t onresponse_handler;
+ onremove_t onremove_handler;
+ void *context;
+};
+
+struct smscore_device_t {
+ struct list_head entry;
+
+ struct list_head clients;
+ struct list_head subclients;
+ spinlock_t clientslock;
+
+ struct list_head buffers;
+ spinlock_t bufferslock;
+ int num_buffers;
+
+ void *common_buffer;
+ int common_buffer_size;
+ dma_addr_t common_buffer_phys;
+
+ void *context;
+ struct device *device;
+ struct usb_device *usb_device;
+
+ char devpath[32];
+ unsigned long device_flags;
+
+ setmode_t setmode_handler;
+ detectmode_t detectmode_handler;
+ sendrequest_t sendrequest_handler;
+ preload_t preload_handler;
+ postload_t postload_handler;
+
+ int mode, modes_supported;
+
+ gfp_t gfp_buf_flags;
+
+ /* host <--> device messages */
+ struct completion version_ex_done, data_download_done, trigger_done;
+ struct completion data_validity_done, device_ready_done;
+ struct completion init_device_done, reload_start_done, resume_done;
+ struct completion gpio_configuration_done, gpio_set_level_done;
+ struct completion gpio_get_level_done, ir_init_done;
+
+ /* Buffer management */
+ wait_queue_head_t buffer_mng_waitq;
+
+ /* GPIO */
+ int gpio_get_res;
+
+ /* Target hardware board */
+ int board_id;
+
+ /* Firmware */
+ u8 *fw_buf;
+ u32 fw_buf_size;
+ u16 fw_version;
+
+ /* Infrared (IR) */
+ struct ir_t ir;
+
+ /*
+ * Identify if device is USB or not.
+ * Used by smsdvb-sysfs to know the root node for debugfs
+ */
+ bool is_usb_device;
+
+ int led_state;
+
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+ struct media_device *media_dev;
+#endif
+};
+
+/* GPIO definitions for antenna frequency domain control (SMS8021) */
+#define SMS_ANTENNA_GPIO_0 1
+#define SMS_ANTENNA_GPIO_1 0
+
+enum sms_bandwidth_mode {
+ BW_8_MHZ = 0,
+ BW_7_MHZ = 1,
+ BW_6_MHZ = 2,
+ BW_5_MHZ = 3,
+ BW_ISDBT_1SEG = 4,
+ BW_ISDBT_3SEG = 5,
+ BW_2_MHZ = 6,
+ BW_FM_RADIO = 7,
+ BW_ISDBT_13SEG = 8,
+ BW_1_5_MHZ = 15,
+ BW_UNKNOWN = 0xffff
+};
+
+
+#define MSG_HDR_FLAG_SPLIT_MSG 4
+
+#define MAX_GPIO_PIN_NUMBER 31
+
+#define HIF_TASK 11
+#define HIF_TASK_SLAVE 22
+#define HIF_TASK_SLAVE2 33
+#define HIF_TASK_SLAVE3 44
+#define SMS_HOST_LIB 150
+#define DVBT_BDA_CONTROL_MSG_ID 201
+
+#define SMS_MAX_PAYLOAD_SIZE 240
+#define SMS_TUNE_TIMEOUT 500
+
+enum msg_types {
+ MSG_TYPE_BASE_VAL = 500,
+ MSG_SMS_GET_VERSION_REQ = 503,
+ MSG_SMS_GET_VERSION_RES = 504,
+ MSG_SMS_MULTI_BRIDGE_CFG = 505,
+ MSG_SMS_GPIO_CONFIG_REQ = 507,
+ MSG_SMS_GPIO_CONFIG_RES = 508,
+ MSG_SMS_GPIO_SET_LEVEL_REQ = 509,
+ MSG_SMS_GPIO_SET_LEVEL_RES = 510,
+ MSG_SMS_GPIO_GET_LEVEL_REQ = 511,
+ MSG_SMS_GPIO_GET_LEVEL_RES = 512,
+ MSG_SMS_EEPROM_BURN_IND = 513,
+ MSG_SMS_LOG_ENABLE_CHANGE_REQ = 514,
+ MSG_SMS_LOG_ENABLE_CHANGE_RES = 515,
+ MSG_SMS_SET_MAX_TX_MSG_LEN_REQ = 516,
+ MSG_SMS_SET_MAX_TX_MSG_LEN_RES = 517,
+ MSG_SMS_SPI_HALFDUPLEX_TOKEN_HOST_TO_DEVICE = 518,
+ MSG_SMS_SPI_HALFDUPLEX_TOKEN_DEVICE_TO_HOST = 519,
+ MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_REQ = 520,
+ MSG_SMS_BACKGROUND_SCAN_FLAG_CHANGE_RES = 521,
+ MSG_SMS_BACKGROUND_SCAN_SIGNAL_DETECTED_IND = 522,
+ MSG_SMS_BACKGROUND_SCAN_NO_SIGNAL_IND = 523,
+ MSG_SMS_CONFIGURE_RF_SWITCH_REQ = 524,
+ MSG_SMS_CONFIGURE_RF_SWITCH_RES = 525,
+ MSG_SMS_MRC_PATH_DISCONNECT_REQ = 526,
+ MSG_SMS_MRC_PATH_DISCONNECT_RES = 527,
+ MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_REQ = 528,
+ MSG_SMS_RECEIVE_1SEG_THROUGH_FULLSEG_RES = 529,
+ MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_REQ = 530,
+ MSG_SMS_RECEIVE_VHF_VIA_VHF_INPUT_RES = 531,
+ MSG_WR_REG_RFT_REQ = 533,
+ MSG_WR_REG_RFT_RES = 534,
+ MSG_RD_REG_RFT_REQ = 535,
+ MSG_RD_REG_RFT_RES = 536,
+ MSG_RD_REG_ALL_RFT_REQ = 537,
+ MSG_RD_REG_ALL_RFT_RES = 538,
+ MSG_HELP_INT = 539,
+ MSG_RUN_SCRIPT_INT = 540,
+ MSG_SMS_EWS_INBAND_REQ = 541,
+ MSG_SMS_EWS_INBAND_RES = 542,
+ MSG_SMS_RFS_SELECT_REQ = 543,
+ MSG_SMS_RFS_SELECT_RES = 544,
+ MSG_SMS_MB_GET_VER_REQ = 545,
+ MSG_SMS_MB_GET_VER_RES = 546,
+ MSG_SMS_MB_WRITE_CFGFILE_REQ = 547,
+ MSG_SMS_MB_WRITE_CFGFILE_RES = 548,
+ MSG_SMS_MB_READ_CFGFILE_REQ = 549,
+ MSG_SMS_MB_READ_CFGFILE_RES = 550,
+ MSG_SMS_RD_MEM_REQ = 552,
+ MSG_SMS_RD_MEM_RES = 553,
+ MSG_SMS_WR_MEM_REQ = 554,
+ MSG_SMS_WR_MEM_RES = 555,
+ MSG_SMS_UPDATE_MEM_REQ = 556,
+ MSG_SMS_UPDATE_MEM_RES = 557,
+ MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_REQ = 558,
+ MSG_SMS_ISDBT_ENABLE_FULL_PARAMS_SET_RES = 559,
+ MSG_SMS_RF_TUNE_REQ = 561,
+ MSG_SMS_RF_TUNE_RES = 562,
+ MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_REQ = 563,
+ MSG_SMS_ISDBT_ENABLE_HIGH_MOBILITY_RES = 564,
+ MSG_SMS_ISDBT_SB_RECEPTION_REQ = 565,
+ MSG_SMS_ISDBT_SB_RECEPTION_RES = 566,
+ MSG_SMS_GENERIC_EPROM_WRITE_REQ = 567,
+ MSG_SMS_GENERIC_EPROM_WRITE_RES = 568,
+ MSG_SMS_GENERIC_EPROM_READ_REQ = 569,
+ MSG_SMS_GENERIC_EPROM_READ_RES = 570,
+ MSG_SMS_EEPROM_WRITE_REQ = 571,
+ MSG_SMS_EEPROM_WRITE_RES = 572,
+ MSG_SMS_CUSTOM_READ_REQ = 574,
+ MSG_SMS_CUSTOM_READ_RES = 575,
+ MSG_SMS_CUSTOM_WRITE_REQ = 576,
+ MSG_SMS_CUSTOM_WRITE_RES = 577,
+ MSG_SMS_INIT_DEVICE_REQ = 578,
+ MSG_SMS_INIT_DEVICE_RES = 579,
+ MSG_SMS_ATSC_SET_ALL_IP_REQ = 580,
+ MSG_SMS_ATSC_SET_ALL_IP_RES = 581,
+ MSG_SMS_ATSC_START_ENSEMBLE_REQ = 582,
+ MSG_SMS_ATSC_START_ENSEMBLE_RES = 583,
+ MSG_SMS_SET_OUTPUT_MODE_REQ = 584,
+ MSG_SMS_SET_OUTPUT_MODE_RES = 585,
+ MSG_SMS_ATSC_IP_FILTER_GET_LIST_REQ = 586,
+ MSG_SMS_ATSC_IP_FILTER_GET_LIST_RES = 587,
+ MSG_SMS_SUB_CHANNEL_START_REQ = 589,
+ MSG_SMS_SUB_CHANNEL_START_RES = 590,
+ MSG_SMS_SUB_CHANNEL_STOP_REQ = 591,
+ MSG_SMS_SUB_CHANNEL_STOP_RES = 592,
+ MSG_SMS_ATSC_IP_FILTER_ADD_REQ = 593,
+ MSG_SMS_ATSC_IP_FILTER_ADD_RES = 594,
+ MSG_SMS_ATSC_IP_FILTER_REMOVE_REQ = 595,
+ MSG_SMS_ATSC_IP_FILTER_REMOVE_RES = 596,
+ MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_REQ = 597,
+ MSG_SMS_ATSC_IP_FILTER_REMOVE_ALL_RES = 598,
+ MSG_SMS_WAIT_CMD = 599,
+ MSG_SMS_ADD_PID_FILTER_REQ = 601,
+ MSG_SMS_ADD_PID_FILTER_RES = 602,
+ MSG_SMS_REMOVE_PID_FILTER_REQ = 603,
+ MSG_SMS_REMOVE_PID_FILTER_RES = 604,
+ MSG_SMS_FAST_INFORMATION_CHANNEL_REQ = 605,
+ MSG_SMS_FAST_INFORMATION_CHANNEL_RES = 606,
+ MSG_SMS_DAB_CHANNEL = 607,
+ MSG_SMS_GET_PID_FILTER_LIST_REQ = 608,
+ MSG_SMS_GET_PID_FILTER_LIST_RES = 609,
+ MSG_SMS_POWER_DOWN_REQ = 610,
+ MSG_SMS_POWER_DOWN_RES = 611,
+ MSG_SMS_ATSC_SLT_EXIST_IND = 612,
+ MSG_SMS_ATSC_NO_SLT_IND = 613,
+ MSG_SMS_GET_STATISTICS_REQ = 615,
+ MSG_SMS_GET_STATISTICS_RES = 616,
+ MSG_SMS_SEND_DUMP = 617,
+ MSG_SMS_SCAN_START_REQ = 618,
+ MSG_SMS_SCAN_START_RES = 619,
+ MSG_SMS_SCAN_STOP_REQ = 620,
+ MSG_SMS_SCAN_STOP_RES = 621,
+ MSG_SMS_SCAN_PROGRESS_IND = 622,
+ MSG_SMS_SCAN_COMPLETE_IND = 623,
+ MSG_SMS_LOG_ITEM = 624,
+ MSG_SMS_DAB_SUBCHANNEL_RECONFIG_REQ = 628,
+ MSG_SMS_DAB_SUBCHANNEL_RECONFIG_RES = 629,
+ MSG_SMS_HO_PER_SLICES_IND = 630,
+ MSG_SMS_HO_INBAND_POWER_IND = 631,
+ MSG_SMS_MANUAL_DEMOD_REQ = 632,
+ MSG_SMS_HO_TUNE_ON_REQ = 636,
+ MSG_SMS_HO_TUNE_ON_RES = 637,
+ MSG_SMS_HO_TUNE_OFF_REQ = 638,
+ MSG_SMS_HO_TUNE_OFF_RES = 639,
+ MSG_SMS_HO_PEEK_FREQ_REQ = 640,
+ MSG_SMS_HO_PEEK_FREQ_RES = 641,
+ MSG_SMS_HO_PEEK_FREQ_IND = 642,
+ MSG_SMS_MB_ATTEN_SET_REQ = 643,
+ MSG_SMS_MB_ATTEN_SET_RES = 644,
+ MSG_SMS_ENABLE_STAT_IN_I2C_REQ = 649,
+ MSG_SMS_ENABLE_STAT_IN_I2C_RES = 650,
+ MSG_SMS_SET_ANTENNA_CONFIG_REQ = 651,
+ MSG_SMS_SET_ANTENNA_CONFIG_RES = 652,
+ MSG_SMS_GET_STATISTICS_EX_REQ = 653,
+ MSG_SMS_GET_STATISTICS_EX_RES = 654,
+ MSG_SMS_SLEEP_RESUME_COMP_IND = 655,
+ MSG_SMS_SWITCH_HOST_INTERFACE_REQ = 656,
+ MSG_SMS_SWITCH_HOST_INTERFACE_RES = 657,
+ MSG_SMS_DATA_DOWNLOAD_REQ = 660,
+ MSG_SMS_DATA_DOWNLOAD_RES = 661,
+ MSG_SMS_DATA_VALIDITY_REQ = 662,
+ MSG_SMS_DATA_VALIDITY_RES = 663,
+ MSG_SMS_SWDOWNLOAD_TRIGGER_REQ = 664,
+ MSG_SMS_SWDOWNLOAD_TRIGGER_RES = 665,
+ MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ = 666,
+ MSG_SMS_SWDOWNLOAD_BACKDOOR_RES = 667,
+ MSG_SMS_GET_VERSION_EX_REQ = 668,
+ MSG_SMS_GET_VERSION_EX_RES = 669,
+ MSG_SMS_CLOCK_OUTPUT_CONFIG_REQ = 670,
+ MSG_SMS_CLOCK_OUTPUT_CONFIG_RES = 671,
+ MSG_SMS_I2C_SET_FREQ_REQ = 685,
+ MSG_SMS_I2C_SET_FREQ_RES = 686,
+ MSG_SMS_GENERIC_I2C_REQ = 687,
+ MSG_SMS_GENERIC_I2C_RES = 688,
+ MSG_SMS_DVBT_BDA_DATA = 693,
+ MSG_SW_RELOAD_REQ = 697,
+ MSG_SMS_DATA_MSG = 699,
+ MSG_TABLE_UPLOAD_REQ = 700,
+ MSG_TABLE_UPLOAD_RES = 701,
+ MSG_SW_RELOAD_START_REQ = 702,
+ MSG_SW_RELOAD_START_RES = 703,
+ MSG_SW_RELOAD_EXEC_REQ = 704,
+ MSG_SW_RELOAD_EXEC_RES = 705,
+ MSG_SMS_SPI_INT_LINE_SET_REQ = 710,
+ MSG_SMS_SPI_INT_LINE_SET_RES = 711,
+ MSG_SMS_GPIO_CONFIG_EX_REQ = 712,
+ MSG_SMS_GPIO_CONFIG_EX_RES = 713,
+ MSG_SMS_WATCHDOG_ACT_REQ = 716,
+ MSG_SMS_WATCHDOG_ACT_RES = 717,
+ MSG_SMS_LOOPBACK_REQ = 718,
+ MSG_SMS_LOOPBACK_RES = 719,
+ MSG_SMS_RAW_CAPTURE_START_REQ = 720,
+ MSG_SMS_RAW_CAPTURE_START_RES = 721,
+ MSG_SMS_RAW_CAPTURE_ABORT_REQ = 722,
+ MSG_SMS_RAW_CAPTURE_ABORT_RES = 723,
+ MSG_SMS_RAW_CAPTURE_COMPLETE_IND = 728,
+ MSG_SMS_DATA_PUMP_IND = 729,
+ MSG_SMS_DATA_PUMP_REQ = 730,
+ MSG_SMS_DATA_PUMP_RES = 731,
+ MSG_SMS_FLASH_DL_REQ = 732,
+ MSG_SMS_EXEC_TEST_1_REQ = 734,
+ MSG_SMS_EXEC_TEST_1_RES = 735,
+ MSG_SMS_ENABLE_TS_INTERFACE_REQ = 736,
+ MSG_SMS_ENABLE_TS_INTERFACE_RES = 737,
+ MSG_SMS_SPI_SET_BUS_WIDTH_REQ = 738,
+ MSG_SMS_SPI_SET_BUS_WIDTH_RES = 739,
+ MSG_SMS_SEND_EMM_REQ = 740,
+ MSG_SMS_SEND_EMM_RES = 741,
+ MSG_SMS_DISABLE_TS_INTERFACE_REQ = 742,
+ MSG_SMS_DISABLE_TS_INTERFACE_RES = 743,
+ MSG_SMS_IS_BUF_FREE_REQ = 744,
+ MSG_SMS_IS_BUF_FREE_RES = 745,
+ MSG_SMS_EXT_ANTENNA_REQ = 746,
+ MSG_SMS_EXT_ANTENNA_RES = 747,
+ MSG_SMS_CMMB_GET_NET_OF_FREQ_REQ_OBSOLETE = 748,
+ MSG_SMS_CMMB_GET_NET_OF_FREQ_RES_OBSOLETE = 749,
+ MSG_SMS_BATTERY_LEVEL_REQ = 750,
+ MSG_SMS_BATTERY_LEVEL_RES = 751,
+ MSG_SMS_CMMB_INJECT_TABLE_REQ_OBSOLETE = 752,
+ MSG_SMS_CMMB_INJECT_TABLE_RES_OBSOLETE = 753,
+ MSG_SMS_FM_RADIO_BLOCK_IND = 754,
+ MSG_SMS_HOST_NOTIFICATION_IND = 755,
+ MSG_SMS_CMMB_GET_CONTROL_TABLE_REQ_OBSOLETE = 756,
+ MSG_SMS_CMMB_GET_CONTROL_TABLE_RES_OBSOLETE = 757,
+ MSG_SMS_CMMB_GET_NETWORKS_REQ = 760,
+ MSG_SMS_CMMB_GET_NETWORKS_RES = 761,
+ MSG_SMS_CMMB_START_SERVICE_REQ = 762,
+ MSG_SMS_CMMB_START_SERVICE_RES = 763,
+ MSG_SMS_CMMB_STOP_SERVICE_REQ = 764,
+ MSG_SMS_CMMB_STOP_SERVICE_RES = 765,
+ MSG_SMS_CMMB_ADD_CHANNEL_FILTER_REQ = 768,
+ MSG_SMS_CMMB_ADD_CHANNEL_FILTER_RES = 769,
+ MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_REQ = 770,
+ MSG_SMS_CMMB_REMOVE_CHANNEL_FILTER_RES = 771,
+ MSG_SMS_CMMB_START_CONTROL_INFO_REQ = 772,
+ MSG_SMS_CMMB_START_CONTROL_INFO_RES = 773,
+ MSG_SMS_CMMB_STOP_CONTROL_INFO_REQ = 774,
+ MSG_SMS_CMMB_STOP_CONTROL_INFO_RES = 775,
+ MSG_SMS_ISDBT_TUNE_REQ = 776,
+ MSG_SMS_ISDBT_TUNE_RES = 777,
+ MSG_SMS_TRANSMISSION_IND = 782,
+ MSG_SMS_PID_STATISTICS_IND = 783,
+ MSG_SMS_POWER_DOWN_IND = 784,
+ MSG_SMS_POWER_DOWN_CONF = 785,
+ MSG_SMS_POWER_UP_IND = 786,
+ MSG_SMS_POWER_UP_CONF = 787,
+ MSG_SMS_POWER_MODE_SET_REQ = 790,
+ MSG_SMS_POWER_MODE_SET_RES = 791,
+ MSG_SMS_DEBUG_HOST_EVENT_REQ = 792,
+ MSG_SMS_DEBUG_HOST_EVENT_RES = 793,
+ MSG_SMS_NEW_CRYSTAL_REQ = 794,
+ MSG_SMS_NEW_CRYSTAL_RES = 795,
+ MSG_SMS_CONFIG_SPI_REQ = 796,
+ MSG_SMS_CONFIG_SPI_RES = 797,
+ MSG_SMS_I2C_SHORT_STAT_IND = 798,
+ MSG_SMS_START_IR_REQ = 800,
+ MSG_SMS_START_IR_RES = 801,
+ MSG_SMS_IR_SAMPLES_IND = 802,
+ MSG_SMS_CMMB_CA_SERVICE_IND = 803,
+ MSG_SMS_SLAVE_DEVICE_DETECTED = 804,
+ MSG_SMS_INTERFACE_LOCK_IND = 805,
+ MSG_SMS_INTERFACE_UNLOCK_IND = 806,
+ MSG_SMS_SEND_ROSUM_BUFF_REQ = 810,
+ MSG_SMS_SEND_ROSUM_BUFF_RES = 811,
+ MSG_SMS_ROSUM_BUFF = 812,
+ MSG_SMS_SET_AES128_KEY_REQ = 815,
+ MSG_SMS_SET_AES128_KEY_RES = 816,
+ MSG_SMS_MBBMS_WRITE_REQ = 817,
+ MSG_SMS_MBBMS_WRITE_RES = 818,
+ MSG_SMS_MBBMS_READ_IND = 819,
+ MSG_SMS_IQ_STREAM_START_REQ = 820,
+ MSG_SMS_IQ_STREAM_START_RES = 821,
+ MSG_SMS_IQ_STREAM_STOP_REQ = 822,
+ MSG_SMS_IQ_STREAM_STOP_RES = 823,
+ MSG_SMS_IQ_STREAM_DATA_BLOCK = 824,
+ MSG_SMS_GET_EEPROM_VERSION_REQ = 825,
+ MSG_SMS_GET_EEPROM_VERSION_RES = 826,
+ MSG_SMS_SIGNAL_DETECTED_IND = 827,
+ MSG_SMS_NO_SIGNAL_IND = 828,
+ MSG_SMS_MRC_SHUTDOWN_SLAVE_REQ = 830,
+ MSG_SMS_MRC_SHUTDOWN_SLAVE_RES = 831,
+ MSG_SMS_MRC_BRINGUP_SLAVE_REQ = 832,
+ MSG_SMS_MRC_BRINGUP_SLAVE_RES = 833,
+ MSG_SMS_EXTERNAL_LNA_CTRL_REQ = 834,
+ MSG_SMS_EXTERNAL_LNA_CTRL_RES = 835,
+ MSG_SMS_SET_PERIODIC_STATISTICS_REQ = 836,
+ MSG_SMS_SET_PERIODIC_STATISTICS_RES = 837,
+ MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_REQ = 838,
+ MSG_SMS_CMMB_SET_AUTO_OUTPUT_TS0_RES = 839,
+ LOCAL_TUNE = 850,
+ LOCAL_IFFT_H_ICI = 851,
+ MSG_RESYNC_REQ = 852,
+ MSG_SMS_CMMB_GET_MRC_STATISTICS_REQ = 853,
+ MSG_SMS_CMMB_GET_MRC_STATISTICS_RES = 854,
+ MSG_SMS_LOG_EX_ITEM = 855,
+ MSG_SMS_DEVICE_DATA_LOSS_IND = 856,
+ MSG_SMS_MRC_WATCHDOG_TRIGGERED_IND = 857,
+ MSG_SMS_USER_MSG_REQ = 858,
+ MSG_SMS_USER_MSG_RES = 859,
+ MSG_SMS_SMART_CARD_INIT_REQ = 860,
+ MSG_SMS_SMART_CARD_INIT_RES = 861,
+ MSG_SMS_SMART_CARD_WRITE_REQ = 862,
+ MSG_SMS_SMART_CARD_WRITE_RES = 863,
+ MSG_SMS_SMART_CARD_READ_IND = 864,
+ MSG_SMS_TSE_ENABLE_REQ = 866,
+ MSG_SMS_TSE_ENABLE_RES = 867,
+ MSG_SMS_CMMB_GET_SHORT_STATISTICS_REQ = 868,
+ MSG_SMS_CMMB_GET_SHORT_STATISTICS_RES = 869,
+ MSG_SMS_LED_CONFIG_REQ = 870,
+ MSG_SMS_LED_CONFIG_RES = 871,
+ MSG_PWM_ANTENNA_REQ = 872,
+ MSG_PWM_ANTENNA_RES = 873,
+ MSG_SMS_CMMB_SMD_SN_REQ = 874,
+ MSG_SMS_CMMB_SMD_SN_RES = 875,
+ MSG_SMS_CMMB_SET_CA_CW_REQ = 876,
+ MSG_SMS_CMMB_SET_CA_CW_RES = 877,
+ MSG_SMS_CMMB_SET_CA_SALT_REQ = 878,
+ MSG_SMS_CMMB_SET_CA_SALT_RES = 879,
+ MSG_SMS_NSCD_INIT_REQ = 880,
+ MSG_SMS_NSCD_INIT_RES = 881,
+ MSG_SMS_NSCD_PROCESS_SECTION_REQ = 882,
+ MSG_SMS_NSCD_PROCESS_SECTION_RES = 883,
+ MSG_SMS_DBD_CREATE_OBJECT_REQ = 884,
+ MSG_SMS_DBD_CREATE_OBJECT_RES = 885,
+ MSG_SMS_DBD_CONFIGURE_REQ = 886,
+ MSG_SMS_DBD_CONFIGURE_RES = 887,
+ MSG_SMS_DBD_SET_KEYS_REQ = 888,
+ MSG_SMS_DBD_SET_KEYS_RES = 889,
+ MSG_SMS_DBD_PROCESS_HEADER_REQ = 890,
+ MSG_SMS_DBD_PROCESS_HEADER_RES = 891,
+ MSG_SMS_DBD_PROCESS_DATA_REQ = 892,
+ MSG_SMS_DBD_PROCESS_DATA_RES = 893,
+ MSG_SMS_DBD_PROCESS_GET_DATA_REQ = 894,
+ MSG_SMS_DBD_PROCESS_GET_DATA_RES = 895,
+ MSG_SMS_NSCD_OPEN_SESSION_REQ = 896,
+ MSG_SMS_NSCD_OPEN_SESSION_RES = 897,
+ MSG_SMS_SEND_HOST_DATA_TO_DEMUX_REQ = 898,
+ MSG_SMS_SEND_HOST_DATA_TO_DEMUX_RES = 899,
+ MSG_LAST_MSG_TYPE = 900,
+};
+
+#define SMS_INIT_MSG_EX(ptr, type, src, dst, len) do { \
+ (ptr)->msg_type = type; \
+ (ptr)->msg_src_id = src; \
+ (ptr)->msg_dst_id = dst; \
+ (ptr)->msg_length = len; \
+ (ptr)->msg_flags = 0; \
+} while (0)
+
+#define SMS_INIT_MSG(ptr, type, len) \
+ SMS_INIT_MSG_EX(ptr, type, 0, HIF_TASK, len)
+
+enum SMS_DVB3_EVENTS {
+ DVB3_EVENT_INIT = 0,
+ DVB3_EVENT_SLEEP,
+ DVB3_EVENT_HOTPLUG,
+ DVB3_EVENT_FE_LOCK,
+ DVB3_EVENT_FE_UNLOCK,
+ DVB3_EVENT_UNC_OK,
+ DVB3_EVENT_UNC_ERR
+};
+
+enum SMS_DEVICE_MODE {
+ DEVICE_MODE_NONE = -1,
+ DEVICE_MODE_DVBT = 0,
+ DEVICE_MODE_DVBH,
+ DEVICE_MODE_DAB_TDMB,
+ DEVICE_MODE_DAB_TDMB_DABIP,
+ DEVICE_MODE_DVBT_BDA,
+ DEVICE_MODE_ISDBT,
+ DEVICE_MODE_ISDBT_BDA,
+ DEVICE_MODE_CMMB,
+ DEVICE_MODE_RAW_TUNER,
+ DEVICE_MODE_FM_RADIO,
+ DEVICE_MODE_FM_RADIO_BDA,
+ DEVICE_MODE_ATSC,
+ DEVICE_MODE_MAX,
+};
+
+struct sms_msg_hdr {
+ u16 msg_type;
+ u8 msg_src_id;
+ u8 msg_dst_id;
+ u16 msg_length; /* length of entire message, including header */
+ u16 msg_flags;
+};
+
+struct sms_msg_data {
+ struct sms_msg_hdr x_msg_header;
+ u32 msg_data;
+};
+
+struct sms_msg_data2 {
+ struct sms_msg_hdr x_msg_header;
+ u32 msg_data[2];
+};
+
+struct sms_msg_data5 {
+ struct sms_msg_hdr x_msg_header;
+ u32 msg_data[5];
+};
+
+struct sms_data_download {
+ struct sms_msg_hdr x_msg_header;
+ u32 mem_addr;
+ u8 payload[SMS_MAX_PAYLOAD_SIZE];
+};
+
+struct sms_version_res {
+ struct sms_msg_hdr x_msg_header;
+
+ u16 chip_model; /* e.g. 0x1102 for SMS-1102 "Nova" */
+ u8 step; /* 0 - step A */
+ u8 metal_fix; /* 0 - Metal 0 */
+
+ /* firmware_id 0xFF if ROM, otherwise the
+ * value indicated by SMSHOSTLIB_DEVICE_MODES_E */
+ u8 firmware_id;
+ /* supported_protocols Bitwise OR combination of
+ * supported protocols */
+ u8 supported_protocols;
+
+ u8 version_major;
+ u8 version_minor;
+ u8 version_patch;
+ u8 version_field_patch;
+
+ u8 rom_ver_major;
+ u8 rom_ver_minor;
+ u8 rom_ver_patch;
+ u8 rom_ver_field_patch;
+
+ u8 TextLabel[34];
+};
+
+struct sms_firmware {
+ u32 check_sum;
+ u32 length;
+ u32 start_address;
+ u8 payload[];
+};
+
+/* statistics information returned as response for
+ * SmsHostApiGetstatistics_Req */
+struct sms_stats {
+ u32 reserved; /* reserved */
+
+ /* Common parameters */
+ u32 is_rf_locked; /* 0 - not locked, 1 - locked */
+ u32 is_demod_locked; /* 0 - not locked, 1 - locked */
+ u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */
+
+ /* Reception quality */
+ s32 SNR; /* dB */
+ u32 ber; /* Post Viterbi ber [1E-5] */
+ u32 FIB_CRC; /* CRC errors percentage, valid only for DAB */
+ u32 ts_per; /* Transport stream PER,
+ 0xFFFFFFFF indicate N/A, valid only for DVB-T/H */
+ u32 MFER; /* DVB-H frame error rate in percentage,
+ 0xFFFFFFFF indicate N/A, valid only for DVB-H */
+ s32 RSSI; /* dBm */
+ s32 in_band_pwr; /* In band power in dBM */
+ s32 carrier_offset; /* Carrier Offset in bin/1024 */
+
+ /* Transmission parameters */
+ u32 frequency; /* frequency in Hz */
+ u32 bandwidth; /* bandwidth in MHz, valid only for DVB-T/H */
+ u32 transmission_mode; /* Transmission Mode, for DAB modes 1-4,
+ for DVB-T/H FFT mode carriers in Kilos */
+ u32 modem_state; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET,
+ valid only for DVB-T/H */
+ u32 guard_interval; /* Guard Interval from
+ SMSHOSTLIB_GUARD_INTERVALS_ET, valid only for DVB-T/H */
+ u32 code_rate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET,
+ valid only for DVB-T/H */
+ u32 lp_code_rate; /* Low Priority Code Rate from
+ SMSHOSTLIB_CODE_RATE_ET, valid only for DVB-T/H */
+ u32 hierarchy; /* hierarchy from SMSHOSTLIB_HIERARCHY_ET,
+ valid only for DVB-T/H */
+ u32 constellation; /* constellation from
+ SMSHOSTLIB_CONSTELLATION_ET, valid only for DVB-T/H */
+
+ /* Burst parameters, valid only for DVB-H */
+ u32 burst_size; /* Current burst size in bytes,
+ valid only for DVB-H */
+ u32 burst_duration; /* Current burst duration in mSec,
+ valid only for DVB-H */
+ u32 burst_cycle_time; /* Current burst cycle time in mSec,
+ valid only for DVB-H */
+ u32 calc_burst_cycle_time;/* Current burst cycle time in mSec,
+ as calculated by demodulator, valid only for DVB-H */
+ u32 num_of_rows; /* Number of rows in MPE table,
+ valid only for DVB-H */
+ u32 num_of_padd_cols; /* Number of padding columns in MPE table,
+ valid only for DVB-H */
+ u32 num_of_punct_cols; /* Number of puncturing columns in MPE table,
+ valid only for DVB-H */
+ u32 error_ts_packets; /* Number of erroneous
+ transport-stream packets */
+ u32 total_ts_packets; /* Total number of transport-stream packets */
+ u32 num_of_valid_mpe_tlbs; /* Number of MPE tables which do not include
+ errors after MPE RS decoding */
+ u32 num_of_invalid_mpe_tlbs;/* Number of MPE tables which include errors
+ after MPE RS decoding */
+ u32 num_of_corrected_mpe_tlbs;/* Number of MPE tables which were
+ corrected by MPE RS decoding */
+ /* Common params */
+ u32 ber_error_count; /* Number of erroneous SYNC bits. */
+ u32 ber_bit_count; /* Total number of SYNC bits. */
+
+ /* Interface information */
+ u32 sms_to_host_tx_errors; /* Total number of transmission errors. */
+
+ /* DAB/T-DMB */
+ u32 pre_ber; /* DAB/T-DMB only: Pre Viterbi ber [1E-5] */
+
+ /* DVB-H TPS parameters */
+ u32 cell_id; /* TPS Cell ID in bits 15..0, bits 31..16 zero;
+ if set to 0xFFFFFFFF cell_id not yet recovered */
+ u32 dvbh_srv_ind_hp; /* DVB-H service indication info, bit 1 -
+ Time Slicing indicator, bit 0 - MPE-FEC indicator */
+ u32 dvbh_srv_ind_lp; /* DVB-H service indication info, bit 1 -
+ Time Slicing indicator, bit 0 - MPE-FEC indicator */
+
+ u32 num_mpe_received; /* DVB-H, Num MPE section received */
+
+ u32 reservedFields[10]; /* reserved */
+};
+
+struct sms_msg_statistics_info {
+ u32 request_result;
+
+ struct sms_stats stat;
+
+ /* Split the calc of the SNR in DAB */
+ u32 signal; /* dB */
+ u32 noise; /* dB */
+
+};
+
+struct sms_isdbt_layer_stats {
+ /* Per-layer information */
+ u32 code_rate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET,
+ * 255 means layer does not exist */
+ u32 constellation; /* constellation from SMSHOSTLIB_CONSTELLATION_ET,
+ * 255 means layer does not exist */
+ u32 ber; /* Post Viterbi ber [1E-5], 0xFFFFFFFF indicate N/A */
+ u32 ber_error_count; /* Post Viterbi Error Bits Count */
+ u32 ber_bit_count; /* Post Viterbi Total Bits Count */
+ u32 pre_ber; /* Pre Viterbi ber [1E-5], 0xFFFFFFFF indicate N/A */
+ u32 ts_per; /* Transport stream PER [%], 0xFFFFFFFF indicate N/A */
+ u32 error_ts_packets; /* Number of erroneous transport-stream packets */
+ u32 total_ts_packets; /* Total number of transport-stream packets */
+ u32 ti_ldepth_i; /* Time interleaver depth I parameter,
+ * 255 means layer does not exist */
+ u32 number_of_segments; /* Number of segments in layer A,
+ * 255 means layer does not exist */
+ u32 tmcc_errors; /* TMCC errors */
+};
+
+struct sms_isdbt_stats {
+ u32 statistics_type; /* Enumerator identifying the type of the
+ * structure. Values are the same as
+ * SMSHOSTLIB_DEVICE_MODES_E
+ *
+ * This field MUST always be first in any
+ * statistics structure */
+
+ u32 full_size; /* Total size of the structure returned by the modem.
+ * If the size requested by the host is smaller than
+ * full_size, the struct will be truncated */
+
+ /* Common parameters */
+ u32 is_rf_locked; /* 0 - not locked, 1 - locked */
+ u32 is_demod_locked; /* 0 - not locked, 1 - locked */
+ u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */
+
+ /* Reception quality */
+ s32 SNR; /* dB */
+ s32 RSSI; /* dBm */
+ s32 in_band_pwr; /* In band power in dBM */
+ s32 carrier_offset; /* Carrier Offset in Hz */
+
+ /* Transmission parameters */
+ u32 frequency; /* frequency in Hz */
+ u32 bandwidth; /* bandwidth in MHz */
+ u32 transmission_mode; /* ISDB-T transmission mode */
+ u32 modem_state; /* 0 - Acquisition, 1 - Locked */
+ u32 guard_interval; /* Guard Interval, 1 divided by value */
+ u32 system_type; /* ISDB-T system type (ISDB-T / ISDB-Tsb) */
+ u32 partial_reception; /* TRUE - partial reception, FALSE otherwise */
+ u32 num_of_layers; /* Number of ISDB-T layers in the network */
+
+ /* Per-layer information */
+ /* Layers A, B and C */
+ struct sms_isdbt_layer_stats layer_info[3];
+ /* Per-layer statistics, see sms_isdbt_layer_stats */
+
+ /* Interface information */
+ u32 sms_to_host_tx_errors; /* Total number of transmission errors. */
+};
+
+struct sms_isdbt_stats_ex {
+ u32 statistics_type; /* Enumerator identifying the type of the
+ * structure. Values are the same as
+ * SMSHOSTLIB_DEVICE_MODES_E
+ *
+ * This field MUST always be first in any
+ * statistics structure */
+
+ u32 full_size; /* Total size of the structure returned by the modem.
+ * If the size requested by the host is smaller than
+ * full_size, the struct will be truncated */
+
+ /* Common parameters */
+ u32 is_rf_locked; /* 0 - not locked, 1 - locked */
+ u32 is_demod_locked; /* 0 - not locked, 1 - locked */
+ u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */
+
+ /* Reception quality */
+ s32 SNR; /* dB */
+ s32 RSSI; /* dBm */
+ s32 in_band_pwr; /* In band power in dBM */
+ s32 carrier_offset; /* Carrier Offset in Hz */
+
+ /* Transmission parameters */
+ u32 frequency; /* frequency in Hz */
+ u32 bandwidth; /* bandwidth in MHz */
+ u32 transmission_mode; /* ISDB-T transmission mode */
+ u32 modem_state; /* 0 - Acquisition, 1 - Locked */
+ u32 guard_interval; /* Guard Interval, 1 divided by value */
+ u32 system_type; /* ISDB-T system type (ISDB-T / ISDB-Tsb) */
+ u32 partial_reception; /* TRUE - partial reception, FALSE otherwise */
+ u32 num_of_layers; /* Number of ISDB-T layers in the network */
+
+ u32 segment_number; /* Segment number for ISDB-Tsb */
+ u32 tune_bw; /* Tuned bandwidth - BW_ISDBT_1SEG / BW_ISDBT_3SEG */
+
+ /* Per-layer information */
+ /* Layers A, B and C */
+ struct sms_isdbt_layer_stats layer_info[3];
+ /* Per-layer statistics, see sms_isdbt_layer_stats */
+
+ /* Interface information */
+ u32 reserved1; /* Was sms_to_host_tx_errors - obsolete . */
+ /* Proprietary information */
+ u32 ext_antenna; /* Obsolete field. */
+ u32 reception_quality;
+ u32 ews_alert_active; /* signals if EWS alert is currently on */
+ u32 lna_on_off; /* Internal LNA state: 0: OFF, 1: ON */
+
+ u32 rf_agc_level; /* RF AGC Level [linear units], full gain = 65535 (20dB) */
+ u32 bb_agc_level; /* Baseband AGC level [linear units], full gain = 65535 (71.5dB) */
+ u32 fw_errors_counter; /* Application errors - should be always zero */
+ u8 FwErrorsHistoryArr[8]; /* Last FW errors IDs - first is most recent, last is oldest */
+
+ s32 MRC_SNR; /* dB */
+ u32 snr_full_res; /* dB x 65536 */
+ u32 reserved4[4];
+};
+
+
+struct sms_pid_stats_data {
+ struct PID_BURST_S {
+ u32 size;
+ u32 padding_cols;
+ u32 punct_cols;
+ u32 duration;
+ u32 cycle;
+ u32 calc_cycle;
+ } burst;
+
+ u32 tot_tbl_cnt;
+ u32 invalid_tbl_cnt;
+ u32 tot_cor_tbl;
+};
+
+struct sms_pid_data {
+ u32 pid;
+ u32 num_rows;
+ struct sms_pid_stats_data pid_statistics;
+};
+
+#define CORRECT_STAT_RSSI(_stat) ((_stat).RSSI *= -1)
+#define CORRECT_STAT_BANDWIDTH(_stat) (_stat.bandwidth = 8 - _stat.bandwidth)
+#define CORRECT_STAT_TRANSMISSON_MODE(_stat) \
+ if (_stat.transmission_mode == 0) \
+ _stat.transmission_mode = 2; \
+ else if (_stat.transmission_mode == 1) \
+ _stat.transmission_mode = 8; \
+ else \
+ _stat.transmission_mode = 4;
+
+struct sms_tx_stats {
+ u32 frequency; /* frequency in Hz */
+ u32 bandwidth; /* bandwidth in MHz */
+ u32 transmission_mode; /* FFT mode carriers in Kilos */
+ u32 guard_interval; /* Guard Interval from
+ SMSHOSTLIB_GUARD_INTERVALS_ET */
+ u32 code_rate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET */
+ u32 lp_code_rate; /* Low Priority Code Rate from
+ SMSHOSTLIB_CODE_RATE_ET */
+ u32 hierarchy; /* hierarchy from SMSHOSTLIB_HIERARCHY_ET */
+ u32 constellation; /* constellation from
+ SMSHOSTLIB_CONSTELLATION_ET */
+
+ /* DVB-H TPS parameters */
+ u32 cell_id; /* TPS Cell ID in bits 15..0, bits 31..16 zero;
+ if set to 0xFFFFFFFF cell_id not yet recovered */
+ u32 dvbh_srv_ind_hp; /* DVB-H service indication info, bit 1 -
+ Time Slicing indicator, bit 0 - MPE-FEC indicator */
+ u32 dvbh_srv_ind_lp; /* DVB-H service indication info, bit 1 -
+ Time Slicing indicator, bit 0 - MPE-FEC indicator */
+ u32 is_demod_locked; /* 0 - not locked, 1 - locked */
+};
+
+struct sms_rx_stats {
+ u32 is_rf_locked; /* 0 - not locked, 1 - locked */
+ u32 is_demod_locked; /* 0 - not locked, 1 - locked */
+ u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */
+
+ u32 modem_state; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */
+ s32 SNR; /* dB */
+ u32 ber; /* Post Viterbi ber [1E-5] */
+ u32 ber_error_count; /* Number of erroneous SYNC bits. */
+ u32 ber_bit_count; /* Total number of SYNC bits. */
+ u32 ts_per; /* Transport stream PER,
+ 0xFFFFFFFF indicate N/A */
+ u32 MFER; /* DVB-H frame error rate in percentage,
+ 0xFFFFFFFF indicate N/A, valid only for DVB-H */
+ s32 RSSI; /* dBm */
+ s32 in_band_pwr; /* In band power in dBM */
+ s32 carrier_offset; /* Carrier Offset in bin/1024 */
+ u32 error_ts_packets; /* Number of erroneous
+ transport-stream packets */
+ u32 total_ts_packets; /* Total number of transport-stream packets */
+
+ s32 MRC_SNR; /* dB */
+ s32 MRC_RSSI; /* dBm */
+ s32 mrc_in_band_pwr; /* In band power in dBM */
+};
+
+struct sms_rx_stats_ex {
+ u32 is_rf_locked; /* 0 - not locked, 1 - locked */
+ u32 is_demod_locked; /* 0 - not locked, 1 - locked */
+ u32 is_external_lna_on; /* 0 - external LNA off, 1 - external LNA on */
+
+ u32 modem_state; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */
+ s32 SNR; /* dB */
+ u32 ber; /* Post Viterbi ber [1E-5] */
+ u32 ber_error_count; /* Number of erroneous SYNC bits. */
+ u32 ber_bit_count; /* Total number of SYNC bits. */
+ u32 ts_per; /* Transport stream PER,
+ 0xFFFFFFFF indicate N/A */
+ u32 MFER; /* DVB-H frame error rate in percentage,
+ 0xFFFFFFFF indicate N/A, valid only for DVB-H */
+ s32 RSSI; /* dBm */
+ s32 in_band_pwr; /* In band power in dBM */
+ s32 carrier_offset; /* Carrier Offset in bin/1024 */
+ u32 error_ts_packets; /* Number of erroneous
+ transport-stream packets */
+ u32 total_ts_packets; /* Total number of transport-stream packets */
+
+ s32 ref_dev_ppm;
+ s32 freq_dev_hz;
+
+ s32 MRC_SNR; /* dB */
+ s32 MRC_RSSI; /* dBm */
+ s32 mrc_in_band_pwr; /* In band power in dBM */
+};
+
+#define SRVM_MAX_PID_FILTERS 8
+
+/* statistics information returned as response for
+ * SmsHostApiGetstatisticsEx_Req for DVB applications, SMS1100 and up */
+struct sms_stats_dvb {
+ /* Reception */
+ struct sms_rx_stats reception_data;
+
+ /* Transmission parameters */
+ struct sms_tx_stats transmission_data;
+
+ /* Burst parameters, valid only for DVB-H */
+ struct sms_pid_data pid_data[SRVM_MAX_PID_FILTERS];
+};
+
+/* statistics information returned as response for
+ * SmsHostApiGetstatisticsEx_Req for DVB applications, SMS1100 and up */
+struct sms_stats_dvb_ex {
+ /* Reception */
+ struct sms_rx_stats_ex reception_data;
+
+ /* Transmission parameters */
+ struct sms_tx_stats transmission_data;
+
+ /* Burst parameters, valid only for DVB-H */
+ struct sms_pid_data pid_data[SRVM_MAX_PID_FILTERS];
+};
+
+struct sms_srvm_signal_status {
+ u32 result;
+ u32 snr;
+ u32 ts_packets;
+ u32 ets_packets;
+ u32 constellation;
+ u32 hp_code;
+ u32 tps_srv_ind_lp;
+ u32 tps_srv_ind_hp;
+ u32 cell_id;
+ u32 reason;
+
+ s32 in_band_power;
+ u32 request_id;
+};
+
+struct smscore_config_gpio {
+#define SMS_GPIO_DIRECTION_INPUT 0
+#define SMS_GPIO_DIRECTION_OUTPUT 1
+ u8 direction;
+
+#define SMS_GPIO_PULLUPDOWN_NONE 0
+#define SMS_GPIO_PULLUPDOWN_PULLDOWN 1
+#define SMS_GPIO_PULLUPDOWN_PULLUP 2
+#define SMS_GPIO_PULLUPDOWN_KEEPER 3
+ u8 pullupdown;
+
+#define SMS_GPIO_INPUTCHARACTERISTICS_NORMAL 0
+#define SMS_GPIO_INPUTCHARACTERISTICS_SCHMITT 1
+ u8 inputcharacteristics;
+
+ /* 10xx */
+#define SMS_GPIO_OUTPUT_SLEW_RATE_FAST 0
+#define SMS_GPIO_OUTPUT_SLEW_WRATE_SLOW 1
+
+ /* 11xx */
+#define SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS 0
+#define SMS_GPIO_OUTPUT_SLEW_RATE_0_9_V_NS 1
+#define SMS_GPIO_OUTPUT_SLEW_RATE_1_7_V_NS 2
+#define SMS_GPIO_OUTPUT_SLEW_RATE_3_3_V_NS 3
+
+ u8 outputslewrate;
+
+ /* 10xx */
+#define SMS_GPIO_OUTPUTDRIVING_S_4mA 0
+#define SMS_GPIO_OUTPUTDRIVING_S_8mA 1
+#define SMS_GPIO_OUTPUTDRIVING_S_12mA 2
+#define SMS_GPIO_OUTPUTDRIVING_S_16mA 3
+
+ /* 11xx*/
+#define SMS_GPIO_OUTPUTDRIVING_1_5mA 0
+#define SMS_GPIO_OUTPUTDRIVING_2_8mA 1
+#define SMS_GPIO_OUTPUTDRIVING_4mA 2
+#define SMS_GPIO_OUTPUTDRIVING_7mA 3
+#define SMS_GPIO_OUTPUTDRIVING_10mA 4
+#define SMS_GPIO_OUTPUTDRIVING_11mA 5
+#define SMS_GPIO_OUTPUTDRIVING_14mA 6
+#define SMS_GPIO_OUTPUTDRIVING_16mA 7
+
+ u8 outputdriving;
+};
+
+char *smscore_translate_msg(enum msg_types msgtype);
+
+extern int smscore_registry_getmode(char *devpath);
+
+extern int smscore_register_hotplug(hotplug_t hotplug);
+extern void smscore_unregister_hotplug(hotplug_t hotplug);
+
+extern int smscore_register_device(struct smsdevice_params_t *params,
+ struct smscore_device_t **coredev,
+ gfp_t gfp_buf_flags,
+ void *mdev);
+extern void smscore_unregister_device(struct smscore_device_t *coredev);
+
+extern int smscore_start_device(struct smscore_device_t *coredev);
+
+extern int smscore_set_device_mode(struct smscore_device_t *coredev, int mode);
+extern int smscore_get_device_mode(struct smscore_device_t *coredev);
+
+extern int smscore_register_client(struct smscore_device_t *coredev,
+ struct smsclient_params_t *params,
+ struct smscore_client_t **client);
+extern void smscore_unregister_client(struct smscore_client_t *client);
+
+extern int smsclient_sendrequest(struct smscore_client_t *client,
+ void *buffer, size_t size);
+extern void smscore_onresponse(struct smscore_device_t *coredev,
+ struct smscore_buffer_t *cb);
+
+extern
+struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev);
+extern void smscore_putbuffer(struct smscore_device_t *coredev,
+ struct smscore_buffer_t *cb);
+
+/* old GPIO management */
+int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin,
+ struct smscore_config_gpio *pinconfig);
+int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level);
+
+/* new GPIO management */
+extern int smscore_gpio_configure(struct smscore_device_t *coredev, u8 pin_num,
+ struct smscore_config_gpio *p_gpio_config);
+extern int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 pin_num,
+ u8 new_level);
+extern int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 pin_num,
+ u8 *level);
+
+void smscore_set_board_id(struct smscore_device_t *core, int id);
+int smscore_get_board_id(struct smscore_device_t *core);
+
+int smscore_led_state(struct smscore_device_t *core, int led);
+
+
+/* ------------------------------------------------------------------------ */
+
+#endif /* __SMS_CORE_API_H__ */
diff --git a/drivers/media/common/siano/smsdvb-debugfs.c b/drivers/media/common/siano/smsdvb-debugfs.c
new file mode 100644
index 000000000000..d14ba271db50
--- /dev/null
+++ b/drivers/media/common/siano/smsdvb-debugfs.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright(c) 2013 Mauro Carvalho Chehab
+
+#include "smscoreapi.h"
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+
+#include <media/dmxdev.h>
+#include <media/dvbdev.h>
+#include <media/dvb_demux.h>
+#include <media/dvb_frontend.h>
+
+#include "smsdvb.h"
+
+static struct dentry *smsdvb_debugfs_usb_root;
+
+struct smsdvb_debugfs {
+ struct kref refcount;
+ spinlock_t lock;
+
+ char stats_data[PAGE_SIZE];
+ unsigned stats_count;
+ bool stats_was_read;
+
+ wait_queue_head_t stats_queue;
+};
+
+static void smsdvb_print_dvb_stats(struct smsdvb_debugfs *debug_data,
+ struct sms_stats *p)
+{
+ int n = 0;
+ char *buf;
+
+ spin_lock(&debug_data->lock);
+ if (debug_data->stats_count) {
+ spin_unlock(&debug_data->lock);
+ return;
+ }
+
+ buf = debug_data->stats_data;
+
+ n += sysfs_emit_at(buf, n, "is_rf_locked = %d\n", p->is_rf_locked);
+ n += sysfs_emit_at(buf, n, "is_demod_locked = %d\n", p->is_demod_locked);
+ n += sysfs_emit_at(buf, n, "is_external_lna_on = %d\n", p->is_external_lna_on);
+ n += sysfs_emit_at(buf, n, "SNR = %d\n", p->SNR);
+ n += sysfs_emit_at(buf, n, "ber = %d\n", p->ber);
+ n += sysfs_emit_at(buf, n, "FIB_CRC = %d\n", p->FIB_CRC);
+ n += sysfs_emit_at(buf, n, "ts_per = %d\n", p->ts_per);
+ n += sysfs_emit_at(buf, n, "MFER = %d\n", p->MFER);
+ n += sysfs_emit_at(buf, n, "RSSI = %d\n", p->RSSI);
+ n += sysfs_emit_at(buf, n, "in_band_pwr = %d\n", p->in_band_pwr);
+ n += sysfs_emit_at(buf, n, "carrier_offset = %d\n", p->carrier_offset);
+ n += sysfs_emit_at(buf, n, "modem_state = %d\n", p->modem_state);
+ n += sysfs_emit_at(buf, n, "frequency = %d\n", p->frequency);
+ n += sysfs_emit_at(buf, n, "bandwidth = %d\n", p->bandwidth);
+ n += sysfs_emit_at(buf, n, "transmission_mode = %d\n", p->transmission_mode);
+ n += sysfs_emit_at(buf, n, "modem_state = %d\n", p->modem_state);
+ n += sysfs_emit_at(buf, n, "guard_interval = %d\n", p->guard_interval);
+ n += sysfs_emit_at(buf, n, "code_rate = %d\n", p->code_rate);
+ n += sysfs_emit_at(buf, n, "lp_code_rate = %d\n", p->lp_code_rate);
+ n += sysfs_emit_at(buf, n, "hierarchy = %d\n", p->hierarchy);
+ n += sysfs_emit_at(buf, n, "constellation = %d\n", p->constellation);
+ n += sysfs_emit_at(buf, n, "burst_size = %d\n", p->burst_size);
+ n += sysfs_emit_at(buf, n, "burst_duration = %d\n", p->burst_duration);
+ n += sysfs_emit_at(buf, n, "burst_cycle_time = %d\n", p->burst_cycle_time);
+ n += sysfs_emit_at(buf, n, "calc_burst_cycle_time = %d\n", p->calc_burst_cycle_time);
+ n += sysfs_emit_at(buf, n, "num_of_rows = %d\n", p->num_of_rows);
+ n += sysfs_emit_at(buf, n, "num_of_padd_cols = %d\n", p->num_of_padd_cols);
+ n += sysfs_emit_at(buf, n, "num_of_punct_cols = %d\n", p->num_of_punct_cols);
+ n += sysfs_emit_at(buf, n, "error_ts_packets = %d\n", p->error_ts_packets);
+ n += sysfs_emit_at(buf, n, "total_ts_packets = %d\n", p->total_ts_packets);
+ n += sysfs_emit_at(buf, n, "num_of_valid_mpe_tlbs = %d\n", p->num_of_valid_mpe_tlbs);
+ n += sysfs_emit_at(buf, n, "num_of_invalid_mpe_tlbs = %d\n", p->num_of_invalid_mpe_tlbs);
+ n += sysfs_emit_at(buf, n, "num_of_corrected_mpe_tlbs = %d\n",
+ p->num_of_corrected_mpe_tlbs);
+ n += sysfs_emit_at(buf, n, "ber_error_count = %d\n", p->ber_error_count);
+ n += sysfs_emit_at(buf, n, "ber_bit_count = %d\n", p->ber_bit_count);
+ n += sysfs_emit_at(buf, n, "sms_to_host_tx_errors = %d\n", p->sms_to_host_tx_errors);
+ n += sysfs_emit_at(buf, n, "pre_ber = %d\n", p->pre_ber);
+ n += sysfs_emit_at(buf, n, "cell_id = %d\n", p->cell_id);
+ n += sysfs_emit_at(buf, n, "dvbh_srv_ind_hp = %d\n", p->dvbh_srv_ind_hp);
+ n += sysfs_emit_at(buf, n, "dvbh_srv_ind_lp = %d\n", p->dvbh_srv_ind_lp);
+ n += sysfs_emit_at(buf, n, "num_mpe_received = %d\n", p->num_mpe_received);
+
+ debug_data->stats_count = n;
+ spin_unlock(&debug_data->lock);
+ wake_up(&debug_data->stats_queue);
+}
+
+static void smsdvb_print_isdb_stats(struct smsdvb_debugfs *debug_data,
+ struct sms_isdbt_stats *p)
+{
+ int i, n = 0;
+ char *buf;
+
+ spin_lock(&debug_data->lock);
+ if (debug_data->stats_count) {
+ spin_unlock(&debug_data->lock);
+ return;
+ }
+
+ buf = debug_data->stats_data;
+
+ n += sysfs_emit_at(buf, n, "statistics_type = %d\t", p->statistics_type);
+ n += sysfs_emit_at(buf, n, "full_size = %d\n", p->full_size);
+
+ n += sysfs_emit_at(buf, n, "is_rf_locked = %d\t\t", p->is_rf_locked);
+ n += sysfs_emit_at(buf, n, "is_demod_locked = %d\t", p->is_demod_locked);
+ n += sysfs_emit_at(buf, n, "is_external_lna_on = %d\n", p->is_external_lna_on);
+ n += sysfs_emit_at(buf, n, "SNR = %d dB\t\t", p->SNR);
+ n += sysfs_emit_at(buf, n, "RSSI = %d dBm\t\t", p->RSSI);
+ n += sysfs_emit_at(buf, n, "in_band_pwr = %d dBm\n", p->in_band_pwr);
+ n += sysfs_emit_at(buf, n, "carrier_offset = %d\t", p->carrier_offset);
+ n += sysfs_emit_at(buf, n, "bandwidth = %d\t\t", p->bandwidth);
+ n += sysfs_emit_at(buf, n, "frequency = %d Hz\n", p->frequency);
+ n += sysfs_emit_at(buf, n, "transmission_mode = %d\t", p->transmission_mode);
+ n += sysfs_emit_at(buf, n, "modem_state = %d\t\t", p->modem_state);
+ n += sysfs_emit_at(buf, n, "guard_interval = %d\n", p->guard_interval);
+ n += sysfs_emit_at(buf, n, "system_type = %d\t\t", p->system_type);
+ n += sysfs_emit_at(buf, n, "partial_reception = %d\t", p->partial_reception);
+ n += sysfs_emit_at(buf, n, "num_of_layers = %d\n", p->num_of_layers);
+ n += sysfs_emit_at(buf, n, "sms_to_host_tx_errors = %d\n", p->sms_to_host_tx_errors);
+
+ for (i = 0; i < 3; i++) {
+ if (p->layer_info[i].number_of_segments < 1 ||
+ p->layer_info[i].number_of_segments > 13)
+ continue;
+
+ n += sysfs_emit_at(buf, n, "\nLayer %d\n", i);
+ n += sysfs_emit_at(buf, n, "\tcode_rate = %d\t", p->layer_info[i].code_rate);
+ n += sysfs_emit_at(buf, n, "constellation = %d\n", p->layer_info[i].constellation);
+ n += sysfs_emit_at(buf, n, "\tber = %-5d\t", p->layer_info[i].ber);
+ n += sysfs_emit_at(buf, n, "\tber_error_count = %-5d\t",
+ p->layer_info[i].ber_error_count);
+ n += sysfs_emit_at(buf, n, "ber_bit_count = %-5d\n",
+ p->layer_info[i].ber_bit_count);
+ n += sysfs_emit_at(buf, n, "\tpre_ber = %-5d\t", p->layer_info[i].pre_ber);
+ n += sysfs_emit_at(buf, n, "\tts_per = %-5d\n", p->layer_info[i].ts_per);
+ n += sysfs_emit_at(buf, n, "\terror_ts_packets = %-5d\t",
+ p->layer_info[i].error_ts_packets);
+ n += sysfs_emit_at(buf, n, "total_ts_packets = %-5d\t",
+ p->layer_info[i].total_ts_packets);
+ n += sysfs_emit_at(buf, n, "ti_ldepth_i = %d\n", p->layer_info[i].ti_ldepth_i);
+ n += sysfs_emit_at(buf, n, "\tnumber_of_segments = %d\t",
+ p->layer_info[i].number_of_segments);
+ n += sysfs_emit_at(buf, n, "tmcc_errors = %d\n", p->layer_info[i].tmcc_errors);
+ }
+
+ debug_data->stats_count = n;
+ spin_unlock(&debug_data->lock);
+ wake_up(&debug_data->stats_queue);
+}
+
+static void smsdvb_print_isdb_stats_ex(struct smsdvb_debugfs *debug_data,
+ struct sms_isdbt_stats_ex *p)
+{
+ int i, n = 0;
+ char *buf;
+
+ spin_lock(&debug_data->lock);
+ if (debug_data->stats_count) {
+ spin_unlock(&debug_data->lock);
+ return;
+ }
+
+ buf = debug_data->stats_data;
+
+ n += sysfs_emit_at(buf, n, "statistics_type = %d\t", p->statistics_type);
+ n += sysfs_emit_at(buf, n, "full_size = %d\n", p->full_size);
+
+ n += sysfs_emit_at(buf, n, "is_rf_locked = %d\t\t", p->is_rf_locked);
+ n += sysfs_emit_at(buf, n, "is_demod_locked = %d\t", p->is_demod_locked);
+ n += sysfs_emit_at(buf, n, "is_external_lna_on = %d\n", p->is_external_lna_on);
+ n += sysfs_emit_at(buf, n, "SNR = %d dB\t\t", p->SNR);
+ n += sysfs_emit_at(buf, n, "RSSI = %d dBm\t\t", p->RSSI);
+ n += sysfs_emit_at(buf, n, "in_band_pwr = %d dBm\n", p->in_band_pwr);
+ n += sysfs_emit_at(buf, n, "carrier_offset = %d\t", p->carrier_offset);
+ n += sysfs_emit_at(buf, n, "bandwidth = %d\t\t", p->bandwidth);
+ n += sysfs_emit_at(buf, n, "frequency = %d Hz\n", p->frequency);
+ n += sysfs_emit_at(buf, n, "transmission_mode = %d\t", p->transmission_mode);
+ n += sysfs_emit_at(buf, n, "modem_state = %d\t\t", p->modem_state);
+ n += sysfs_emit_at(buf, n, "guard_interval = %d\n", p->guard_interval);
+ n += sysfs_emit_at(buf, n, "system_type = %d\t\t", p->system_type);
+ n += sysfs_emit_at(buf, n, "partial_reception = %d\t", p->partial_reception);
+ n += sysfs_emit_at(buf, n, "num_of_layers = %d\n", p->num_of_layers);
+ n += sysfs_emit_at(buf, n, "segment_number = %d\t", p->segment_number);
+ n += sysfs_emit_at(buf, n, "tune_bw = %d\n", p->tune_bw);
+
+ for (i = 0; i < 3; i++) {
+ if (p->layer_info[i].number_of_segments < 1 ||
+ p->layer_info[i].number_of_segments > 13)
+ continue;
+
+ n += sysfs_emit_at(buf, n, "\nLayer %d\n", i);
+ n += sysfs_emit_at(buf, n, "\tcode_rate = %d\t", p->layer_info[i].code_rate);
+ n += sysfs_emit_at(buf, n, "constellation = %d\n", p->layer_info[i].constellation);
+ n += sysfs_emit_at(buf, n, "\tber = %-5d\t", p->layer_info[i].ber);
+ n += sysfs_emit_at(buf, n, "\tber_error_count = %-5d\t",
+ p->layer_info[i].ber_error_count);
+ n += sysfs_emit_at(buf, n, "ber_bit_count = %-5d\n",
+ p->layer_info[i].ber_bit_count);
+ n += sysfs_emit_at(buf, n, "\tpre_ber = %-5d\t", p->layer_info[i].pre_ber);
+ n += sysfs_emit_at(buf, n, "\tts_per = %-5d\n", p->layer_info[i].ts_per);
+ n += sysfs_emit_at(buf, n, "\terror_ts_packets = %-5d\t",
+ p->layer_info[i].error_ts_packets);
+ n += sysfs_emit_at(buf, n, "total_ts_packets = %-5d\t",
+ p->layer_info[i].total_ts_packets);
+ n += sysfs_emit_at(buf, n, "ti_ldepth_i = %d\n", p->layer_info[i].ti_ldepth_i);
+ n += sysfs_emit_at(buf, n, "\tnumber_of_segments = %d\t",
+ p->layer_info[i].number_of_segments);
+ n += sysfs_emit_at(buf, n, "tmcc_errors = %d\n", p->layer_info[i].tmcc_errors);
+ }
+
+
+ debug_data->stats_count = n;
+ spin_unlock(&debug_data->lock);
+
+ wake_up(&debug_data->stats_queue);
+}
+
+static int smsdvb_stats_open(struct inode *inode, struct file *file)
+{
+ struct smsdvb_client_t *client = inode->i_private;
+ struct smsdvb_debugfs *debug_data = client->debug_data;
+
+ kref_get(&debug_data->refcount);
+
+ spin_lock(&debug_data->lock);
+ debug_data->stats_count = 0;
+ debug_data->stats_was_read = false;
+ spin_unlock(&debug_data->lock);
+
+ file->private_data = debug_data;
+
+ return 0;
+}
+
+static void smsdvb_debugfs_data_release(struct kref *ref)
+{
+ struct smsdvb_debugfs *debug_data;
+
+ debug_data = container_of(ref, struct smsdvb_debugfs, refcount);
+ kfree(debug_data);
+}
+
+static int smsdvb_stats_wait_read(struct smsdvb_debugfs *debug_data)
+{
+ int rc = 1;
+
+ spin_lock(&debug_data->lock);
+
+ if (debug_data->stats_was_read)
+ goto exit;
+
+ rc = debug_data->stats_count;
+
+exit:
+ spin_unlock(&debug_data->lock);
+ return rc;
+}
+
+static __poll_t smsdvb_stats_poll(struct file *file, poll_table *wait)
+{
+ struct smsdvb_debugfs *debug_data = file->private_data;
+ int rc;
+
+ kref_get(&debug_data->refcount);
+
+ poll_wait(file, &debug_data->stats_queue, wait);
+
+ rc = smsdvb_stats_wait_read(debug_data);
+ kref_put(&debug_data->refcount, smsdvb_debugfs_data_release);
+
+ return rc > 0 ? EPOLLIN | EPOLLRDNORM : 0;
+}
+
+static ssize_t smsdvb_stats_read(struct file *file, char __user *user_buf,
+ size_t nbytes, loff_t *ppos)
+{
+ int rc = 0, len;
+ struct smsdvb_debugfs *debug_data = file->private_data;
+
+ kref_get(&debug_data->refcount);
+
+ if (file->f_flags & O_NONBLOCK) {
+ rc = smsdvb_stats_wait_read(debug_data);
+ if (!rc) {
+ rc = -EWOULDBLOCK;
+ goto ret;
+ }
+ } else {
+ rc = wait_event_interruptible(debug_data->stats_queue,
+ smsdvb_stats_wait_read(debug_data));
+ if (rc < 0)
+ goto ret;
+ }
+
+ if (debug_data->stats_was_read) {
+ rc = 0; /* EOF */
+ goto ret;
+ }
+
+ len = debug_data->stats_count - *ppos;
+ if (len >= 0)
+ rc = simple_read_from_buffer(user_buf, nbytes, ppos,
+ debug_data->stats_data, len);
+ else
+ rc = 0;
+
+ if (*ppos >= debug_data->stats_count) {
+ spin_lock(&debug_data->lock);
+ debug_data->stats_was_read = true;
+ spin_unlock(&debug_data->lock);
+ }
+ret:
+ kref_put(&debug_data->refcount, smsdvb_debugfs_data_release);
+ return rc;
+}
+
+static int smsdvb_stats_release(struct inode *inode, struct file *file)
+{
+ struct smsdvb_debugfs *debug_data = file->private_data;
+
+ spin_lock(&debug_data->lock);
+ debug_data->stats_was_read = true; /* return EOF to read() */
+ spin_unlock(&debug_data->lock);
+ wake_up_interruptible_sync(&debug_data->stats_queue);
+
+ kref_put(&debug_data->refcount, smsdvb_debugfs_data_release);
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static const struct file_operations debugfs_stats_ops = {
+ .open = smsdvb_stats_open,
+ .poll = smsdvb_stats_poll,
+ .read = smsdvb_stats_read,
+ .release = smsdvb_stats_release,
+ .llseek = generic_file_llseek,
+};
+
+/*
+ * Functions used by smsdvb, in order to create the interfaces
+ */
+
+int smsdvb_debugfs_create(struct smsdvb_client_t *client)
+{
+ struct smscore_device_t *coredev = client->coredev;
+ struct smsdvb_debugfs *debug_data;
+
+ if (!smsdvb_debugfs_usb_root || !coredev->is_usb_device)
+ return -ENODEV;
+
+ debug_data = kzalloc(sizeof(*client->debug_data), GFP_KERNEL);
+ if (!debug_data)
+ return -ENOMEM;
+
+ client->debugfs = debugfs_create_dir(coredev->devpath,
+ smsdvb_debugfs_usb_root);
+
+ debugfs_create_file("stats", S_IRUGO | S_IWUSR, client->debugfs,
+ client, &debugfs_stats_ops);
+
+ client->debug_data = debug_data;
+ client->prt_dvb_stats = smsdvb_print_dvb_stats;
+ client->prt_isdb_stats = smsdvb_print_isdb_stats;
+ client->prt_isdb_stats_ex = smsdvb_print_isdb_stats_ex;
+
+ init_waitqueue_head(&debug_data->stats_queue);
+ spin_lock_init(&debug_data->lock);
+ kref_init(&debug_data->refcount);
+
+ return 0;
+}
+
+void smsdvb_debugfs_release(struct smsdvb_client_t *client)
+{
+ if (!client->debugfs)
+ return;
+
+ client->prt_dvb_stats = NULL;
+ client->prt_isdb_stats = NULL;
+ client->prt_isdb_stats_ex = NULL;
+
+ debugfs_remove_recursive(client->debugfs);
+ kref_put(&client->debug_data->refcount, smsdvb_debugfs_data_release);
+
+ client->debug_data = NULL;
+ client->debugfs = NULL;
+}
+
+void smsdvb_debugfs_register(void)
+{
+ /*
+ * FIXME: This was written to debug Siano USB devices. So, it creates
+ * the debugfs node under <debugfs>/usb.
+ * A similar logic would be needed for Siano sdio devices, but, in that
+ * case, usb_debug_root is not a good choice.
+ *
+ * Perhaps the right fix here would be to create another sysfs root
+ * node for sdio-based boards, but this may need some logic at sdio
+ * subsystem.
+ */
+ smsdvb_debugfs_usb_root = debugfs_create_dir("smsdvb", usb_debug_root);
+}
+
+void smsdvb_debugfs_unregister(void)
+{
+ if (!smsdvb_debugfs_usb_root)
+ return;
+ debugfs_remove_recursive(smsdvb_debugfs_usb_root);
+ smsdvb_debugfs_usb_root = NULL;
+}
diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c
new file mode 100644
index 000000000000..9b1a650ed055
--- /dev/null
+++ b/drivers/media/common/siano/smsdvb-main.c
@@ -0,0 +1,1273 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/****************************************************************
+
+Siano Mobile Silicon, Inc.
+MDTV receiver kernel modules.
+Copyright (C) 2006-2008, Uri Shkolnik
+
+
+****************************************************************/
+
+#include "smscoreapi.h"
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/div64.h>
+
+#include <media/dmxdev.h>
+#include <media/dvbdev.h>
+#include <media/dvb_demux.h>
+#include <media/dvb_frontend.h>
+
+#include "sms-cards.h"
+
+#include "smsdvb.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static LIST_HEAD(g_smsdvb_clients);
+static DEFINE_MUTEX(g_smsdvb_clientslock);
+
+static u32 sms_to_guard_interval_table[] = {
+ [0] = GUARD_INTERVAL_1_32,
+ [1] = GUARD_INTERVAL_1_16,
+ [2] = GUARD_INTERVAL_1_8,
+ [3] = GUARD_INTERVAL_1_4,
+};
+
+static u32 sms_to_code_rate_table[] = {
+ [0] = FEC_1_2,
+ [1] = FEC_2_3,
+ [2] = FEC_3_4,
+ [3] = FEC_5_6,
+ [4] = FEC_7_8,
+};
+
+
+static u32 sms_to_hierarchy_table[] = {
+ [0] = HIERARCHY_NONE,
+ [1] = HIERARCHY_1,
+ [2] = HIERARCHY_2,
+ [3] = HIERARCHY_4,
+};
+
+static u32 sms_to_modulation_table[] = {
+ [0] = QPSK,
+ [1] = QAM_16,
+ [2] = QAM_64,
+ [3] = DQPSK,
+};
+
+
+/* Events that may come from DVB v3 adapter */
+static void sms_board_dvb3_event(struct smsdvb_client_t *client,
+ enum SMS_DVB3_EVENTS event) {
+
+ struct smscore_device_t *coredev = client->coredev;
+ switch (event) {
+ case DVB3_EVENT_INIT:
+ pr_debug("DVB3_EVENT_INIT\n");
+ sms_board_event(coredev, BOARD_EVENT_BIND);
+ break;
+ case DVB3_EVENT_SLEEP:
+ pr_debug("DVB3_EVENT_SLEEP\n");
+ sms_board_event(coredev, BOARD_EVENT_POWER_SUSPEND);
+ break;
+ case DVB3_EVENT_HOTPLUG:
+ pr_debug("DVB3_EVENT_HOTPLUG\n");
+ sms_board_event(coredev, BOARD_EVENT_POWER_INIT);
+ break;
+ case DVB3_EVENT_FE_LOCK:
+ if (client->event_fe_state != DVB3_EVENT_FE_LOCK) {
+ client->event_fe_state = DVB3_EVENT_FE_LOCK;
+ pr_debug("DVB3_EVENT_FE_LOCK\n");
+ sms_board_event(coredev, BOARD_EVENT_FE_LOCK);
+ }
+ break;
+ case DVB3_EVENT_FE_UNLOCK:
+ if (client->event_fe_state != DVB3_EVENT_FE_UNLOCK) {
+ client->event_fe_state = DVB3_EVENT_FE_UNLOCK;
+ pr_debug("DVB3_EVENT_FE_UNLOCK\n");
+ sms_board_event(coredev, BOARD_EVENT_FE_UNLOCK);
+ }
+ break;
+ case DVB3_EVENT_UNC_OK:
+ if (client->event_unc_state != DVB3_EVENT_UNC_OK) {
+ client->event_unc_state = DVB3_EVENT_UNC_OK;
+ pr_debug("DVB3_EVENT_UNC_OK\n");
+ sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_OK);
+ }
+ break;
+ case DVB3_EVENT_UNC_ERR:
+ if (client->event_unc_state != DVB3_EVENT_UNC_ERR) {
+ client->event_unc_state = DVB3_EVENT_UNC_ERR;
+ pr_debug("DVB3_EVENT_UNC_ERR\n");
+ sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_ERRORS);
+ }
+ break;
+
+ default:
+ pr_err("Unknown dvb3 api event\n");
+ break;
+ }
+}
+
+static void smsdvb_stats_not_ready(struct dvb_frontend *fe)
+{
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+ struct smscore_device_t *coredev = client->coredev;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int i, n_layers;
+
+ switch (smscore_get_device_mode(coredev)) {
+ case DEVICE_MODE_ISDBT:
+ case DEVICE_MODE_ISDBT_BDA:
+ n_layers = 4;
+ break;
+ default:
+ n_layers = 1;
+ }
+
+ /* Global stats */
+ c->strength.len = 1;
+ c->cnr.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+
+ /* Per-layer stats */
+ c->post_bit_error.len = n_layers;
+ c->post_bit_count.len = n_layers;
+ c->block_error.len = n_layers;
+ c->block_count.len = n_layers;
+
+ /*
+ * Put all of them at FE_SCALE_NOT_AVAILABLE. They're dynamically
+ * changed when the stats become available.
+ */
+ for (i = 0; i < n_layers; i++) {
+ c->post_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+}
+
+static inline int sms_to_mode(u32 mode)
+{
+ switch (mode) {
+ case 2:
+ return TRANSMISSION_MODE_2K;
+ case 4:
+ return TRANSMISSION_MODE_4K;
+ case 8:
+ return TRANSMISSION_MODE_8K;
+ }
+ return TRANSMISSION_MODE_AUTO;
+}
+
+static inline int sms_to_isdbt_mode(u32 mode)
+{
+ switch (mode) {
+ case 1:
+ return TRANSMISSION_MODE_2K;
+ case 2:
+ return TRANSMISSION_MODE_4K;
+ case 3:
+ return TRANSMISSION_MODE_8K;
+ }
+ return TRANSMISSION_MODE_AUTO;
+}
+
+static inline int sms_to_isdbt_guard_interval(u32 interval)
+{
+ switch (interval) {
+ case 4:
+ return GUARD_INTERVAL_1_4;
+ case 8:
+ return GUARD_INTERVAL_1_8;
+ case 16:
+ return GUARD_INTERVAL_1_16;
+ case 32:
+ return GUARD_INTERVAL_1_32;
+ }
+ return GUARD_INTERVAL_AUTO;
+}
+
+static inline int sms_to_status(u32 is_demod_locked, u32 is_rf_locked)
+{
+ if (is_demod_locked)
+ return FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
+ FE_HAS_SYNC | FE_HAS_LOCK;
+
+ if (is_rf_locked)
+ return FE_HAS_SIGNAL | FE_HAS_CARRIER;
+
+ return 0;
+}
+
+static inline u32 sms_to_bw(u32 value)
+{
+ return value * 1000000;
+}
+
+#define convert_from_table(value, table, defval) ({ \
+ u32 __ret; \
+ if (value < ARRAY_SIZE(table)) \
+ __ret = table[value]; \
+ else \
+ __ret = defval; \
+ __ret; \
+})
+
+#define sms_to_guard_interval(value) \
+ convert_from_table(value, sms_to_guard_interval_table, \
+ GUARD_INTERVAL_AUTO);
+
+#define sms_to_code_rate(value) \
+ convert_from_table(value, sms_to_code_rate_table, \
+ FEC_NONE);
+
+#define sms_to_hierarchy(value) \
+ convert_from_table(value, sms_to_hierarchy_table, \
+ FEC_NONE);
+
+#define sms_to_modulation(value) \
+ convert_from_table(value, sms_to_modulation_table, \
+ FEC_NONE);
+
+static void smsdvb_update_tx_params(struct smsdvb_client_t *client,
+ struct sms_tx_stats *p)
+{
+ struct dvb_frontend *fe = &client->frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ c->frequency = p->frequency;
+ client->fe_status = sms_to_status(p->is_demod_locked, 0);
+ c->bandwidth_hz = sms_to_bw(p->bandwidth);
+ c->transmission_mode = sms_to_mode(p->transmission_mode);
+ c->guard_interval = sms_to_guard_interval(p->guard_interval);
+ c->code_rate_HP = sms_to_code_rate(p->code_rate);
+ c->code_rate_LP = sms_to_code_rate(p->lp_code_rate);
+ c->hierarchy = sms_to_hierarchy(p->hierarchy);
+ c->modulation = sms_to_modulation(p->constellation);
+}
+
+static void smsdvb_update_per_slices(struct smsdvb_client_t *client,
+ struct RECEPTION_STATISTICS_PER_SLICES_S *p)
+{
+ struct dvb_frontend *fe = &client->frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u64 tmp;
+
+ client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);
+ c->modulation = sms_to_modulation(p->constellation);
+
+ /* signal Strength, in DBm */
+ c->strength.stat[0].uvalue = p->in_band_power * 1000;
+
+ /* Carrier to noise ratio, in DB */
+ c->cnr.stat[0].svalue = p->snr * 1000;
+
+ /* PER/BER requires demod lock */
+ if (!p->is_demod_locked)
+ return;
+
+ /* TS PER */
+ client->last_per = c->block_error.stat[0].uvalue;
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue += p->ets_packets;
+ c->block_count.stat[0].uvalue += p->ets_packets + p->ts_packets;
+
+ /* ber */
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += p->ber_error_count;
+ c->post_bit_count.stat[0].uvalue += p->ber_bit_count;
+
+ /* Legacy PER/BER */
+ tmp = p->ets_packets * 65535ULL;
+ if (p->ts_packets + p->ets_packets)
+ do_div(tmp, p->ts_packets + p->ets_packets);
+ client->legacy_per = tmp;
+}
+
+static void smsdvb_update_dvb_stats(struct smsdvb_client_t *client,
+ struct sms_stats *p)
+{
+ struct dvb_frontend *fe = &client->frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (client->prt_dvb_stats)
+ client->prt_dvb_stats(client->debug_data, p);
+
+ client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);
+
+ /* Update DVB modulation parameters */
+ c->frequency = p->frequency;
+ client->fe_status = sms_to_status(p->is_demod_locked, 0);
+ c->bandwidth_hz = sms_to_bw(p->bandwidth);
+ c->transmission_mode = sms_to_mode(p->transmission_mode);
+ c->guard_interval = sms_to_guard_interval(p->guard_interval);
+ c->code_rate_HP = sms_to_code_rate(p->code_rate);
+ c->code_rate_LP = sms_to_code_rate(p->lp_code_rate);
+ c->hierarchy = sms_to_hierarchy(p->hierarchy);
+ c->modulation = sms_to_modulation(p->constellation);
+
+ /* update reception data */
+ c->lna = p->is_external_lna_on ? 1 : 0;
+
+ /* Carrier to noise ratio, in DB */
+ c->cnr.stat[0].svalue = p->SNR * 1000;
+
+ /* signal Strength, in DBm */
+ c->strength.stat[0].uvalue = p->in_band_pwr * 1000;
+
+ /* PER/BER requires demod lock */
+ if (!p->is_demod_locked)
+ return;
+
+ /* TS PER */
+ client->last_per = c->block_error.stat[0].uvalue;
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue += p->error_ts_packets;
+ c->block_count.stat[0].uvalue += p->total_ts_packets;
+
+ /* ber */
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += p->ber_error_count;
+ c->post_bit_count.stat[0].uvalue += p->ber_bit_count;
+
+ /* Legacy PER/BER */
+ client->legacy_ber = p->ber;
+};
+
+static void smsdvb_update_isdbt_stats(struct smsdvb_client_t *client,
+ struct sms_isdbt_stats *p)
+{
+ struct dvb_frontend *fe = &client->frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct sms_isdbt_layer_stats *lr;
+ int i, n_layers;
+
+ if (client->prt_isdb_stats)
+ client->prt_isdb_stats(client->debug_data, p);
+
+ client->fe_status = sms_to_status(p->is_demod_locked, p->is_rf_locked);
+
+ /*
+ * Firmware 2.1 seems to report only lock status and
+ * signal strength. The signal strength indicator is at the
+ * wrong field.
+ */
+ if (p->statistics_type == 0) {
+ c->strength.stat[0].uvalue = ((s32)p->transmission_mode) * 1000;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return;
+ }
+
+ /* Update ISDB-T transmission parameters */
+ c->frequency = p->frequency;
+ c->bandwidth_hz = sms_to_bw(p->bandwidth);
+ c->transmission_mode = sms_to_isdbt_mode(p->transmission_mode);
+ c->guard_interval = sms_to_isdbt_guard_interval(p->guard_interval);
+ c->isdbt_partial_reception = p->partial_reception ? 1 : 0;
+ n_layers = p->num_of_layers;
+ if (n_layers < 1)
+ n_layers = 1;
+ if (n_layers > 3)
+ n_layers = 3;
+ c->isdbt_layer_enabled = 0;
+
+ /* update reception data */
+ c->lna = p->is_external_lna_on ? 1 : 0;
+
+ /* Carrier to noise ratio, in DB */
+ c->cnr.stat[0].svalue = p->SNR * 1000;
+
+ /* signal Strength, in DBm */
+ c->strength.stat[0].uvalue = p->in_band_pwr * 1000;
+
+ /* PER/BER and per-layer stats require demod lock */
+ if (!p->is_demod_locked)
+ return;
+
+ client->last_per = c->block_error.stat[0].uvalue;
+
+ /* Clears global counters, as the code below will sum it again */
+ c->block_error.stat[0].uvalue = 0;
+ c->block_count.stat[0].uvalue = 0;
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = 0;
+ c->post_bit_count.stat[0].uvalue = 0;
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+
+ for (i = 0; i < n_layers; i++) {
+ lr = &p->layer_info[i];
+
+ /* Update per-layer transmission parameters */
+ if (lr->number_of_segments > 0 && lr->number_of_segments < 13) {
+ c->isdbt_layer_enabled |= 1 << i;
+ c->layer[i].segment_count = lr->number_of_segments;
+ } else {
+ continue;
+ }
+ c->layer[i].modulation = sms_to_modulation(lr->constellation);
+ c->layer[i].fec = sms_to_code_rate(lr->code_rate);
+
+ /* Time interleaving */
+ c->layer[i].interleaving = (u8)lr->ti_ldepth_i;
+
+ /* TS PER */
+ c->block_error.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[i + 1].uvalue += lr->error_ts_packets;
+ c->block_count.stat[i + 1].uvalue += lr->total_ts_packets;
+
+ /* Update global PER counter */
+ c->block_error.stat[0].uvalue += lr->error_ts_packets;
+ c->block_count.stat[0].uvalue += lr->total_ts_packets;
+
+ /* BER */
+ c->post_bit_error.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[i + 1].uvalue += lr->ber_error_count;
+ c->post_bit_count.stat[i + 1].uvalue += lr->ber_bit_count;
+
+ /* Update global BER counter */
+ c->post_bit_error.stat[0].uvalue += lr->ber_error_count;
+ c->post_bit_count.stat[0].uvalue += lr->ber_bit_count;
+ }
+}
+
+static void smsdvb_update_isdbt_stats_ex(struct smsdvb_client_t *client,
+ struct sms_isdbt_stats_ex *p)
+{
+ struct dvb_frontend *fe = &client->frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct sms_isdbt_layer_stats *lr;
+ int i, n_layers;
+
+ if (client->prt_isdb_stats_ex)
+ client->prt_isdb_stats_ex(client->debug_data, p);
+
+ /* Update ISDB-T transmission parameters */
+ c->frequency = p->frequency;
+ client->fe_status = sms_to_status(p->is_demod_locked, 0);
+ c->bandwidth_hz = sms_to_bw(p->bandwidth);
+ c->transmission_mode = sms_to_isdbt_mode(p->transmission_mode);
+ c->guard_interval = sms_to_isdbt_guard_interval(p->guard_interval);
+ c->isdbt_partial_reception = p->partial_reception ? 1 : 0;
+ n_layers = p->num_of_layers;
+ if (n_layers < 1)
+ n_layers = 1;
+ if (n_layers > 3)
+ n_layers = 3;
+ c->isdbt_layer_enabled = 0;
+
+ /* update reception data */
+ c->lna = p->is_external_lna_on ? 1 : 0;
+
+ /* Carrier to noise ratio, in DB */
+ c->cnr.stat[0].svalue = p->SNR * 1000;
+
+ /* signal Strength, in DBm */
+ c->strength.stat[0].uvalue = p->in_band_pwr * 1000;
+
+ /* PER/BER and per-layer stats require demod lock */
+ if (!p->is_demod_locked)
+ return;
+
+ client->last_per = c->block_error.stat[0].uvalue;
+
+ /* Clears global counters, as the code below will sum it again */
+ c->block_error.stat[0].uvalue = 0;
+ c->block_count.stat[0].uvalue = 0;
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = 0;
+ c->post_bit_count.stat[0].uvalue = 0;
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+
+ c->post_bit_error.len = n_layers + 1;
+ c->post_bit_count.len = n_layers + 1;
+ c->block_error.len = n_layers + 1;
+ c->block_count.len = n_layers + 1;
+ for (i = 0; i < n_layers; i++) {
+ lr = &p->layer_info[i];
+
+ /* Update per-layer transmission parameters */
+ if (lr->number_of_segments > 0 && lr->number_of_segments < 13) {
+ c->isdbt_layer_enabled |= 1 << i;
+ c->layer[i].segment_count = lr->number_of_segments;
+ } else {
+ continue;
+ }
+ c->layer[i].modulation = sms_to_modulation(lr->constellation);
+ c->layer[i].fec = sms_to_code_rate(lr->code_rate);
+
+ /* Time interleaving */
+ c->layer[i].interleaving = (u8)lr->ti_ldepth_i;
+
+ /* TS PER */
+ c->block_error.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[i + 1].uvalue += lr->error_ts_packets;
+ c->block_count.stat[i + 1].uvalue += lr->total_ts_packets;
+
+ /* Update global PER counter */
+ c->block_error.stat[0].uvalue += lr->error_ts_packets;
+ c->block_count.stat[0].uvalue += lr->total_ts_packets;
+
+ /* ber */
+ c->post_bit_error.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[i + 1].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[i + 1].uvalue += lr->ber_error_count;
+ c->post_bit_count.stat[i + 1].uvalue += lr->ber_bit_count;
+
+ /* Update global ber counter */
+ c->post_bit_error.stat[0].uvalue += lr->ber_error_count;
+ c->post_bit_count.stat[0].uvalue += lr->ber_bit_count;
+ }
+}
+
+static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)
+{
+ struct smsdvb_client_t *client = (struct smsdvb_client_t *) context;
+ struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) (((u8 *) cb->p)
+ + cb->offset);
+ void *p = phdr + 1;
+ struct dvb_frontend *fe = &client->frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ bool is_status_update = false;
+
+ switch (phdr->msg_type) {
+ case MSG_SMS_DVBT_BDA_DATA:
+ /*
+ * Only feed data to dvb demux if are there any feed listening
+ * to it and if the device has tuned
+ */
+ if (client->feed_users && client->has_tuned)
+ dvb_dmx_swfilter(&client->demux, p,
+ cb->size - sizeof(struct sms_msg_hdr));
+ break;
+
+ case MSG_SMS_RF_TUNE_RES:
+ case MSG_SMS_ISDBT_TUNE_RES:
+ complete(&client->tune_done);
+ break;
+
+ case MSG_SMS_SIGNAL_DETECTED_IND:
+ client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+
+ is_status_update = true;
+ break;
+
+ case MSG_SMS_NO_SIGNAL_IND:
+ client->fe_status = 0;
+
+ is_status_update = true;
+ break;
+
+ case MSG_SMS_TRANSMISSION_IND:
+ smsdvb_update_tx_params(client, p);
+
+ is_status_update = true;
+ break;
+
+ case MSG_SMS_HO_PER_SLICES_IND:
+ smsdvb_update_per_slices(client, p);
+
+ is_status_update = true;
+ break;
+
+ case MSG_SMS_GET_STATISTICS_RES:
+ switch (smscore_get_device_mode(client->coredev)) {
+ case DEVICE_MODE_ISDBT:
+ case DEVICE_MODE_ISDBT_BDA:
+ smsdvb_update_isdbt_stats(client, p);
+ break;
+ default:
+ /* Skip sms_msg_statistics_info:request_result field */
+ smsdvb_update_dvb_stats(client, p + sizeof(u32));
+ }
+
+ is_status_update = true;
+ break;
+
+ /* Only for ISDB-T */
+ case MSG_SMS_GET_STATISTICS_EX_RES:
+ /* Skip sms_msg_statistics_info:request_result field? */
+ smsdvb_update_isdbt_stats_ex(client, p + sizeof(u32));
+ is_status_update = true;
+ break;
+ default:
+ pr_debug("message not handled\n");
+ }
+ smscore_putbuffer(client->coredev, cb);
+
+ if (is_status_update) {
+ if (client->fe_status & FE_HAS_LOCK) {
+ sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK);
+ if (client->last_per == c->block_error.stat[0].uvalue)
+ sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK);
+ else
+ sms_board_dvb3_event(client, DVB3_EVENT_UNC_ERR);
+ client->has_tuned = true;
+ } else {
+ smsdvb_stats_not_ready(fe);
+ client->has_tuned = false;
+ sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK);
+ }
+ complete(&client->stats_done);
+ }
+
+ return 0;
+}
+
+static void smsdvb_media_device_unregister(struct smsdvb_client_t *client)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ struct smscore_device_t *coredev = client->coredev;
+
+ if (!coredev->media_dev)
+ return;
+ media_device_unregister(coredev->media_dev);
+ media_device_cleanup(coredev->media_dev);
+ kfree(coredev->media_dev);
+ coredev->media_dev = NULL;
+#endif
+}
+
+static void smsdvb_unregister_client(struct smsdvb_client_t *client)
+{
+ /* must be called under clientslock */
+
+ list_del(&client->entry);
+
+ smsdvb_debugfs_release(client);
+ smscore_unregister_client(client->smsclient);
+ dvb_unregister_frontend(&client->frontend);
+ dvb_dmxdev_release(&client->dmxdev);
+ dvb_dmx_release(&client->demux);
+ smsdvb_media_device_unregister(client);
+ dvb_unregister_adapter(&client->adapter);
+ kfree(client);
+}
+
+static void smsdvb_onremove(void *context)
+{
+ mutex_lock(&g_smsdvb_clientslock);
+
+ smsdvb_unregister_client((struct smsdvb_client_t *) context);
+
+ mutex_unlock(&g_smsdvb_clientslock);
+}
+
+static int smsdvb_start_feed(struct dvb_demux_feed *feed)
+{
+ struct smsdvb_client_t *client =
+ container_of(feed->demux, struct smsdvb_client_t, demux);
+ struct sms_msg_data pid_msg;
+
+ pr_debug("add pid %d(%x)\n",
+ feed->pid, feed->pid);
+
+ client->feed_users++;
+
+ pid_msg.x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ pid_msg.x_msg_header.msg_dst_id = HIF_TASK;
+ pid_msg.x_msg_header.msg_flags = 0;
+ pid_msg.x_msg_header.msg_type = MSG_SMS_ADD_PID_FILTER_REQ;
+ pid_msg.x_msg_header.msg_length = sizeof(pid_msg);
+ pid_msg.msg_data = feed->pid;
+
+ return smsclient_sendrequest(client->smsclient,
+ &pid_msg, sizeof(pid_msg));
+}
+
+static int smsdvb_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct smsdvb_client_t *client =
+ container_of(feed->demux, struct smsdvb_client_t, demux);
+ struct sms_msg_data pid_msg;
+
+ pr_debug("remove pid %d(%x)\n",
+ feed->pid, feed->pid);
+
+ client->feed_users--;
+
+ pid_msg.x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ pid_msg.x_msg_header.msg_dst_id = HIF_TASK;
+ pid_msg.x_msg_header.msg_flags = 0;
+ pid_msg.x_msg_header.msg_type = MSG_SMS_REMOVE_PID_FILTER_REQ;
+ pid_msg.x_msg_header.msg_length = sizeof(pid_msg);
+ pid_msg.msg_data = feed->pid;
+
+ return smsclient_sendrequest(client->smsclient,
+ &pid_msg, sizeof(pid_msg));
+}
+
+static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client,
+ void *buffer, size_t size,
+ struct completion *completion)
+{
+ int rc;
+
+ rc = smsclient_sendrequest(client->smsclient, buffer, size);
+ if (rc < 0)
+ return rc;
+
+ return wait_for_completion_timeout(completion,
+ msecs_to_jiffies(2000)) ?
+ 0 : -ETIME;
+}
+
+static int smsdvb_send_statistics_request(struct smsdvb_client_t *client)
+{
+ int rc;
+ struct sms_msg_hdr msg;
+
+ /* Don't request stats too fast */
+ if (client->get_stats_jiffies &&
+ (!time_after(jiffies, client->get_stats_jiffies)))
+ return 0;
+ client->get_stats_jiffies = jiffies + msecs_to_jiffies(100);
+
+ msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ msg.msg_dst_id = HIF_TASK;
+ msg.msg_flags = 0;
+ msg.msg_length = sizeof(msg);
+
+ switch (smscore_get_device_mode(client->coredev)) {
+ case DEVICE_MODE_ISDBT:
+ case DEVICE_MODE_ISDBT_BDA:
+ /*
+ * Check for firmware version, to avoid breaking for old cards
+ */
+ if (client->coredev->fw_version >= 0x800)
+ msg.msg_type = MSG_SMS_GET_STATISTICS_EX_REQ;
+ else
+ msg.msg_type = MSG_SMS_GET_STATISTICS_REQ;
+ break;
+ default:
+ msg.msg_type = MSG_SMS_GET_STATISTICS_REQ;
+ }
+
+ rc = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
+ &client->stats_done);
+
+ return rc;
+}
+
+static inline int led_feedback(struct smsdvb_client_t *client)
+{
+ if (!(client->fe_status & FE_HAS_LOCK))
+ return sms_board_led_feedback(client->coredev, SMS_LED_OFF);
+
+ return sms_board_led_feedback(client->coredev,
+ (client->legacy_ber == 0) ?
+ SMS_LED_HI : SMS_LED_LO);
+}
+
+static int smsdvb_read_status(struct dvb_frontend *fe, enum fe_status *stat)
+{
+ int rc;
+ struct smsdvb_client_t *client;
+ client = container_of(fe, struct smsdvb_client_t, frontend);
+
+ rc = smsdvb_send_statistics_request(client);
+
+ *stat = client->fe_status;
+
+ led_feedback(client);
+
+ return rc;
+}
+
+static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ int rc;
+ struct smsdvb_client_t *client;
+
+ client = container_of(fe, struct smsdvb_client_t, frontend);
+
+ rc = smsdvb_send_statistics_request(client);
+
+ *ber = client->legacy_ber;
+
+ led_feedback(client);
+
+ return rc;
+}
+
+static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int rc;
+ s32 power = (s32) c->strength.stat[0].uvalue;
+ struct smsdvb_client_t *client;
+
+ client = container_of(fe, struct smsdvb_client_t, frontend);
+
+ rc = smsdvb_send_statistics_request(client);
+
+ if (power < -95)
+ *strength = 0;
+ else if (power > -29)
+ *strength = 65535;
+ else
+ *strength = (power + 95) * 65535 / 66;
+
+ led_feedback(client);
+
+ return rc;
+}
+
+static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int rc;
+ struct smsdvb_client_t *client;
+
+ client = container_of(fe, struct smsdvb_client_t, frontend);
+
+ rc = smsdvb_send_statistics_request(client);
+
+ /* Preferred scale for SNR with legacy API: 0.1 dB */
+ *snr = ((u32)c->cnr.stat[0].svalue) / 100;
+
+ led_feedback(client);
+
+ return rc;
+}
+
+static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ int rc;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct smsdvb_client_t *client;
+
+ client = container_of(fe, struct smsdvb_client_t, frontend);
+
+ rc = smsdvb_send_statistics_request(client);
+
+ *ucblocks = c->block_error.stat[0].uvalue;
+
+ led_feedback(client);
+
+ return rc;
+}
+
+static int smsdvb_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *tune)
+{
+ pr_debug("\n");
+
+ tune->min_delay_ms = 400;
+ tune->step_size = 250000;
+ tune->max_drift = 0;
+ return 0;
+}
+
+static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+
+ struct {
+ struct sms_msg_hdr msg;
+ u32 Data[3];
+ } msg;
+
+ int ret;
+
+ client->fe_status = 0;
+ client->event_fe_state = -1;
+ client->event_unc_state = -1;
+ fe->dtv_property_cache.delivery_system = SYS_DVBT;
+
+ msg.msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ msg.msg.msg_dst_id = HIF_TASK;
+ msg.msg.msg_flags = 0;
+ msg.msg.msg_type = MSG_SMS_RF_TUNE_REQ;
+ msg.msg.msg_length = sizeof(msg);
+ msg.Data[0] = c->frequency;
+ msg.Data[2] = 12000000;
+
+ pr_debug("%s: freq %d band %d\n", __func__, c->frequency,
+ c->bandwidth_hz);
+
+ switch (c->bandwidth_hz / 1000000) {
+ case 8:
+ msg.Data[1] = BW_8_MHZ;
+ break;
+ case 7:
+ msg.Data[1] = BW_7_MHZ;
+ break;
+ case 6:
+ msg.Data[1] = BW_6_MHZ;
+ break;
+ case 0:
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+ /* Disable LNA, if any. An error is returned if no LNA is present */
+ ret = sms_board_lna_control(client->coredev, 0);
+ if (ret == 0) {
+ enum fe_status status;
+
+ /* tune with LNA off at first */
+ ret = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
+ &client->tune_done);
+
+ smsdvb_read_status(fe, &status);
+
+ if (status & FE_HAS_LOCK)
+ return ret;
+
+ /* previous tune didn't lock - enable LNA and tune again */
+ sms_board_lna_control(client->coredev, 1);
+ }
+
+ return smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
+ &client->tune_done);
+}
+
+static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+ int board_id = smscore_get_board_id(client->coredev);
+ struct sms_board *board = sms_get_board(board_id);
+ enum sms_device_type_st type = board->type;
+ int ret;
+
+ struct {
+ struct sms_msg_hdr msg;
+ u32 Data[4];
+ } msg;
+
+ fe->dtv_property_cache.delivery_system = SYS_ISDBT;
+
+ msg.msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID;
+ msg.msg.msg_dst_id = HIF_TASK;
+ msg.msg.msg_flags = 0;
+ msg.msg.msg_type = MSG_SMS_ISDBT_TUNE_REQ;
+ msg.msg.msg_length = sizeof(msg);
+
+ if (c->isdbt_sb_segment_idx == -1)
+ c->isdbt_sb_segment_idx = 0;
+
+ if (!c->isdbt_layer_enabled)
+ c->isdbt_layer_enabled = 7;
+
+ msg.Data[0] = c->frequency;
+ msg.Data[1] = BW_ISDBT_1SEG;
+ msg.Data[2] = 12000000;
+ msg.Data[3] = c->isdbt_sb_segment_idx;
+
+ if (c->isdbt_partial_reception) {
+ if ((type == SMS_PELE || type == SMS_RIO) &&
+ c->isdbt_sb_segment_count > 3)
+ msg.Data[1] = BW_ISDBT_13SEG;
+ else if (c->isdbt_sb_segment_count > 1)
+ msg.Data[1] = BW_ISDBT_3SEG;
+ } else if (type == SMS_PELE || type == SMS_RIO)
+ msg.Data[1] = BW_ISDBT_13SEG;
+
+ c->bandwidth_hz = 6000000;
+
+ pr_debug("freq %d segwidth %d segindex %d\n",
+ c->frequency, c->isdbt_sb_segment_count,
+ c->isdbt_sb_segment_idx);
+
+ /* Disable LNA, if any. An error is returned if no LNA is present */
+ ret = sms_board_lna_control(client->coredev, 0);
+ if (ret == 0) {
+ enum fe_status status;
+
+ /* tune with LNA off at first */
+ ret = smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
+ &client->tune_done);
+
+ smsdvb_read_status(fe, &status);
+
+ if (status & FE_HAS_LOCK)
+ return ret;
+
+ /* previous tune didn't lock - enable LNA and tune again */
+ sms_board_lna_control(client->coredev, 1);
+ }
+ return smsdvb_sendrequest_and_wait(client, &msg, sizeof(msg),
+ &client->tune_done);
+}
+
+static int smsdvb_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+ struct smscore_device_t *coredev = client->coredev;
+
+ smsdvb_stats_not_ready(fe);
+ c->strength.stat[0].uvalue = 0;
+ c->cnr.stat[0].uvalue = 0;
+
+ client->has_tuned = false;
+
+ switch (smscore_get_device_mode(coredev)) {
+ case DEVICE_MODE_DVBT:
+ case DEVICE_MODE_DVBT_BDA:
+ return smsdvb_dvbt_set_frontend(fe);
+ case DEVICE_MODE_ISDBT:
+ case DEVICE_MODE_ISDBT_BDA:
+ return smsdvb_isdbt_set_frontend(fe);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int smsdvb_init(struct dvb_frontend *fe)
+{
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+
+ sms_board_power(client->coredev, 1);
+
+ sms_board_dvb3_event(client, DVB3_EVENT_INIT);
+ return 0;
+}
+
+static int smsdvb_sleep(struct dvb_frontend *fe)
+{
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+
+ sms_board_led_feedback(client->coredev, SMS_LED_OFF);
+ sms_board_power(client->coredev, 0);
+
+ sms_board_dvb3_event(client, DVB3_EVENT_SLEEP);
+
+ return 0;
+}
+
+static void smsdvb_release(struct dvb_frontend *fe)
+{
+ /* do nothing */
+}
+
+static const struct dvb_frontend_ops smsdvb_fe_ops = {
+ .info = {
+ .name = "Siano Mobile Digital MDTV Receiver",
+ .frequency_min_hz = 44250 * kHz,
+ .frequency_max_hz = 867250 * kHz,
+ .frequency_stepsize_hz = 250 * kHz,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+ FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = smsdvb_release,
+
+ .set_frontend = smsdvb_set_frontend,
+ .get_tune_settings = smsdvb_get_tune_settings,
+
+ .read_status = smsdvb_read_status,
+ .read_ber = smsdvb_read_ber,
+ .read_signal_strength = smsdvb_read_signal_strength,
+ .read_snr = smsdvb_read_snr,
+ .read_ucblocks = smsdvb_read_ucblocks,
+
+ .init = smsdvb_init,
+ .sleep = smsdvb_sleep,
+};
+
+static int smsdvb_hotplug(struct smscore_device_t *coredev,
+ struct device *device, int arrival)
+{
+ struct smsclient_params_t params;
+ struct smsdvb_client_t *client;
+ int rc;
+
+ /* device removal handled by onremove callback */
+ if (!arrival)
+ return 0;
+ client = kzalloc(sizeof(struct smsdvb_client_t), GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+
+ /* register dvb adapter */
+ rc = dvb_register_adapter(&client->adapter,
+ sms_get_board(
+ smscore_get_board_id(coredev))->name,
+ THIS_MODULE, device, adapter_nr);
+ if (rc < 0) {
+ pr_err("dvb_register_adapter() failed %d\n", rc);
+ goto adapter_error;
+ }
+ dvb_register_media_controller(&client->adapter, coredev->media_dev);
+
+ /* init dvb demux */
+ client->demux.dmx.capabilities = DMX_TS_FILTERING;
+ client->demux.filternum = 32; /* todo: nova ??? */
+ client->demux.feednum = 32;
+ client->demux.start_feed = smsdvb_start_feed;
+ client->demux.stop_feed = smsdvb_stop_feed;
+
+ rc = dvb_dmx_init(&client->demux);
+ if (rc < 0) {
+ pr_err("dvb_dmx_init failed %d\n", rc);
+ goto dvbdmx_error;
+ }
+
+ /* init dmxdev */
+ client->dmxdev.filternum = 32;
+ client->dmxdev.demux = &client->demux.dmx;
+ client->dmxdev.capabilities = 0;
+
+ rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter);
+ if (rc < 0) {
+ pr_err("dvb_dmxdev_init failed %d\n", rc);
+ goto dmxdev_error;
+ }
+
+ /* init and register frontend */
+ memcpy(&client->frontend.ops, &smsdvb_fe_ops,
+ sizeof(struct dvb_frontend_ops));
+
+ switch (smscore_get_device_mode(coredev)) {
+ case DEVICE_MODE_DVBT:
+ case DEVICE_MODE_DVBT_BDA:
+ client->frontend.ops.delsys[0] = SYS_DVBT;
+ break;
+ case DEVICE_MODE_ISDBT:
+ case DEVICE_MODE_ISDBT_BDA:
+ client->frontend.ops.delsys[0] = SYS_ISDBT;
+ break;
+ }
+
+ rc = dvb_register_frontend(&client->adapter, &client->frontend);
+ if (rc < 0) {
+ pr_err("frontend registration failed %d\n", rc);
+ goto frontend_error;
+ }
+
+ params.initial_id = 1;
+ params.data_type = MSG_SMS_DVBT_BDA_DATA;
+ params.onresponse_handler = smsdvb_onresponse;
+ params.onremove_handler = smsdvb_onremove;
+ params.context = client;
+
+ rc = smscore_register_client(coredev, &params, &client->smsclient);
+ if (rc < 0) {
+ pr_err("smscore_register_client() failed %d\n", rc);
+ goto client_error;
+ }
+
+ client->coredev = coredev;
+
+ init_completion(&client->tune_done);
+ init_completion(&client->stats_done);
+
+ mutex_lock(&g_smsdvb_clientslock);
+
+ list_add(&client->entry, &g_smsdvb_clients);
+
+ mutex_unlock(&g_smsdvb_clientslock);
+
+ client->event_fe_state = -1;
+ client->event_unc_state = -1;
+ sms_board_dvb3_event(client, DVB3_EVENT_HOTPLUG);
+
+ sms_board_setup(coredev);
+
+ if (smsdvb_debugfs_create(client) < 0)
+ pr_info("failed to create debugfs node\n");
+
+ rc = dvb_create_media_graph(&client->adapter, true);
+ if (rc < 0) {
+ pr_err("dvb_create_media_graph failed %d\n", rc);
+ goto media_graph_error;
+ }
+
+ pr_info("DVB interface registered.\n");
+ return 0;
+
+media_graph_error:
+ mutex_lock(&g_smsdvb_clientslock);
+ list_del(&client->entry);
+ mutex_unlock(&g_smsdvb_clientslock);
+
+ smsdvb_debugfs_release(client);
+
+client_error:
+ dvb_unregister_frontend(&client->frontend);
+
+frontend_error:
+ dvb_dmxdev_release(&client->dmxdev);
+
+dmxdev_error:
+ dvb_dmx_release(&client->demux);
+
+dvbdmx_error:
+ smsdvb_media_device_unregister(client);
+ dvb_unregister_adapter(&client->adapter);
+
+adapter_error:
+ kfree(client);
+ return rc;
+}
+
+static int __init smsdvb_module_init(void)
+{
+ int rc;
+
+ smsdvb_debugfs_register();
+
+ rc = smscore_register_hotplug(smsdvb_hotplug);
+ if (rc)
+ smsdvb_debugfs_unregister();
+
+ pr_debug("\n");
+
+ return rc;
+}
+
+static void __exit smsdvb_module_exit(void)
+{
+ smscore_unregister_hotplug(smsdvb_hotplug);
+
+ mutex_lock(&g_smsdvb_clientslock);
+
+ while (!list_empty(&g_smsdvb_clients))
+ smsdvb_unregister_client((struct smsdvb_client_t *)g_smsdvb_clients.next);
+
+ smsdvb_debugfs_unregister();
+
+ mutex_unlock(&g_smsdvb_clientslock);
+}
+
+module_init(smsdvb_module_init);
+module_exit(smsdvb_module_exit);
+
+MODULE_DESCRIPTION("SMS DVB subsystem adaptation module");
+MODULE_AUTHOR("Siano Mobile Silicon, Inc. <uris@siano-ms.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/siano/smsdvb.h b/drivers/media/common/siano/smsdvb.h
new file mode 100644
index 000000000000..b43cbb5c0dee
--- /dev/null
+++ b/drivers/media/common/siano/smsdvb.h
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/***********************************************************************
+ *
+ ***********************************************************************/
+
+struct smsdvb_debugfs;
+struct smsdvb_client_t;
+
+typedef void (*sms_prt_dvb_stats_t)(struct smsdvb_debugfs *debug_data,
+ struct sms_stats *p);
+
+typedef void (*sms_prt_isdb_stats_t)(struct smsdvb_debugfs *debug_data,
+ struct sms_isdbt_stats *p);
+
+typedef void (*sms_prt_isdb_stats_ex_t)
+ (struct smsdvb_debugfs *debug_data,
+ struct sms_isdbt_stats_ex *p);
+
+
+struct smsdvb_client_t {
+ struct list_head entry;
+
+ struct smscore_device_t *coredev;
+ struct smscore_client_t *smsclient;
+
+ struct dvb_adapter adapter;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dvb_frontend frontend;
+
+ enum fe_status fe_status;
+
+ struct completion tune_done;
+ struct completion stats_done;
+
+ int last_per;
+
+ int legacy_ber, legacy_per;
+
+ int event_fe_state;
+ int event_unc_state;
+
+ unsigned long get_stats_jiffies;
+
+ int feed_users;
+ bool has_tuned;
+
+ /* stats debugfs data */
+ struct dentry *debugfs;
+
+ struct smsdvb_debugfs *debug_data;
+
+ sms_prt_dvb_stats_t prt_dvb_stats;
+ sms_prt_isdb_stats_t prt_isdb_stats;
+ sms_prt_isdb_stats_ex_t prt_isdb_stats_ex;
+};
+
+/*
+ * This struct is a mix of struct sms_rx_stats_ex and
+ * struct sms_srvm_signal_status.
+ * It was obtained by comparing the way it was filled by the original code
+ */
+struct RECEPTION_STATISTICS_PER_SLICES_S {
+ u32 result;
+ u32 snr;
+ s32 in_band_power;
+ u32 ts_packets;
+ u32 ets_packets;
+ u32 constellation;
+ u32 hp_code;
+ u32 tps_srv_ind_lp;
+ u32 tps_srv_ind_hp;
+ u32 cell_id;
+ u32 reason;
+ u32 request_id;
+ u32 modem_state; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */
+
+ u32 ber; /* Post Viterbi BER [1E-5] */
+ s32 RSSI; /* dBm */
+ s32 carrier_offset; /* Carrier Offset in bin/1024 */
+
+ u32 is_rf_locked; /* 0 - not locked, 1 - locked */
+ u32 is_demod_locked; /* 0 - not locked, 1 - locked */
+
+ u32 ber_bit_count; /* Total number of SYNC bits. */
+ u32 ber_error_count; /* Number of erroneous SYNC bits. */
+
+ s32 MRC_SNR; /* dB */
+ s32 mrc_in_band_pwr; /* In band power in dBM */
+ s32 MRC_RSSI; /* dBm */
+};
+
+/* From smsdvb-debugfs.c */
+#ifdef CONFIG_SMS_SIANO_DEBUGFS
+
+int smsdvb_debugfs_create(struct smsdvb_client_t *client);
+void smsdvb_debugfs_release(struct smsdvb_client_t *client);
+void smsdvb_debugfs_register(void);
+void smsdvb_debugfs_unregister(void);
+
+#else
+
+static inline int smsdvb_debugfs_create(struct smsdvb_client_t *client)
+{
+ return 0;
+}
+
+static inline void smsdvb_debugfs_release(struct smsdvb_client_t *client) {}
+
+static inline void smsdvb_debugfs_register(void) {}
+
+static inline void smsdvb_debugfs_unregister(void) {};
+
+#endif
+
diff --git a/drivers/media/common/siano/smsendian.c b/drivers/media/common/siano/smsendian.c
new file mode 100644
index 000000000000..b957970c7d97
--- /dev/null
+++ b/drivers/media/common/siano/smsendian.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/****************************************************************
+
+ Siano Mobile Silicon, Inc.
+ MDTV receiver kernel modules.
+ Copyright (C) 2006-2009, Uri Shkolnik
+
+
+ ****************************************************************/
+
+#include <linux/export.h>
+#include <asm/byteorder.h>
+
+#include "smsendian.h"
+#include "smscoreapi.h"
+
+void smsendian_handle_tx_message(void *buffer)
+{
+#ifdef __BIG_ENDIAN
+ struct sms_msg_data *msg = buffer;
+ int i;
+ int msg_words;
+ u32 *msg_data = &msg->msg_data;
+
+ switch (msg->x_msg_header.msg_type) {
+ case MSG_SMS_DATA_DOWNLOAD_REQ:
+ {
+ msg->msg_data = le32_to_cpu((__force __le32)(msg->msg_data));
+ break;
+ }
+
+ default:
+ msg_words = (msg->x_msg_header.msg_length -
+ sizeof(struct sms_msg_hdr))/4;
+
+ for (i = 0; i < msg_words; i++)
+ msg_data[i] = le32_to_cpu((__force __le32)msg_data[i]);
+
+ break;
+ }
+#endif /* __BIG_ENDIAN */
+}
+EXPORT_SYMBOL_GPL(smsendian_handle_tx_message);
+
+void smsendian_handle_rx_message(void *buffer)
+{
+#ifdef __BIG_ENDIAN
+ struct sms_msg_data *msg = (struct sms_msg_data *)buffer;
+ int i;
+ int msg_words;
+
+ switch (msg->x_msg_header.msg_type) {
+ case MSG_SMS_GET_VERSION_EX_RES:
+ {
+ struct sms_version_res *ver =
+ (struct sms_version_res *) msg;
+ ver->chip_model = le16_to_cpu((__force __le16)ver->chip_model);
+ break;
+ }
+
+ case MSG_SMS_DVBT_BDA_DATA:
+ case MSG_SMS_DAB_CHANNEL:
+ case MSG_SMS_DATA_MSG:
+ {
+ break;
+ }
+
+ default:
+ {
+ u32 *msg_data = &msg->msg_data;
+ msg_words = (msg->x_msg_header.msg_length -
+ sizeof(struct sms_msg_hdr))/4;
+
+ for (i = 0; i < msg_words; i++)
+ msg_data[i] = le32_to_cpu((__force __le32)msg_data[i]);
+
+ break;
+ }
+ }
+#endif /* __BIG_ENDIAN */
+}
+EXPORT_SYMBOL_GPL(smsendian_handle_rx_message);
+
+void smsendian_handle_message_header(void *msg)
+{
+#ifdef __BIG_ENDIAN
+ struct sms_msg_hdr *phdr = (struct sms_msg_hdr *)msg;
+
+ phdr->msg_type = le16_to_cpu((__force __le16)phdr->msg_type);
+ phdr->msg_length = le16_to_cpu((__force __le16)phdr->msg_length);
+ phdr->msg_flags = le16_to_cpu((__force __le16)phdr->msg_flags);
+#endif /* __BIG_ENDIAN */
+}
+EXPORT_SYMBOL_GPL(smsendian_handle_message_header);
diff --git a/drivers/media/common/siano/smsendian.h b/drivers/media/common/siano/smsendian.h
new file mode 100644
index 000000000000..f64215c47a1c
--- /dev/null
+++ b/drivers/media/common/siano/smsendian.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/****************************************************************
+
+Siano Mobile Silicon, Inc.
+MDTV receiver kernel modules.
+Copyright (C) 2006-2009, Uri Shkolnik
+
+
+****************************************************************/
+
+#ifndef __SMS_ENDIAN_H__
+#define __SMS_ENDIAN_H__
+
+#include <asm/byteorder.h>
+
+extern void smsendian_handle_tx_message(void *buffer);
+extern void smsendian_handle_rx_message(void *buffer);
+extern void smsendian_handle_message_header(void *msg);
+
+#endif /* __SMS_ENDIAN_H__ */
+
diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c
new file mode 100644
index 000000000000..af07fed21ae1
--- /dev/null
+++ b/drivers/media/common/siano/smsir.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Siano Mobile Silicon, Inc.
+// MDTV receiver kernel modules.
+// Copyright (C) 2006-2009, Uri Shkolnik
+//
+// Copyright (c) 2010 - Mauro Carvalho Chehab
+// - Ported the driver to use rc-core
+// - IR raw event decoding is now done at rc-core
+// - Code almost re-written
+
+
+#include "smscoreapi.h"
+
+#include <linux/types.h>
+#include <linux/input.h>
+
+#include "smsir.h"
+#include "sms-cards.h"
+
+#define MODULE_NAME "smsmdtv"
+
+void sms_ir_event(struct smscore_device_t *coredev, const char *buf, int len)
+{
+ int i;
+ const s32 *samples = (const void *)buf;
+
+ for (i = 0; i < len >> 2; i++) {
+ struct ir_raw_event ev = {
+ .duration = abs(samples[i]),
+ .pulse = samples[i] <= 0
+ };
+
+ ir_raw_event_store(coredev->ir.dev, &ev);
+ }
+ ir_raw_event_handle(coredev->ir.dev);
+}
+
+int sms_ir_init(struct smscore_device_t *coredev)
+{
+ int err;
+ int board_id = smscore_get_board_id(coredev);
+ struct rc_dev *dev;
+
+ pr_debug("Allocating rc device\n");
+ dev = rc_allocate_device(RC_DRIVER_IR_RAW);
+ if (!dev)
+ return -ENOMEM;
+
+ coredev->ir.controller = 0; /* Todo: vega/nova SPI number */
+ coredev->ir.timeout = US_TO_NS(IR_DEFAULT_TIMEOUT);
+ pr_debug("IR port %d, timeout %d ms\n",
+ coredev->ir.controller, coredev->ir.timeout);
+
+ snprintf(coredev->ir.name, sizeof(coredev->ir.name),
+ "SMS IR (%s)", sms_get_board(board_id)->name);
+
+ strscpy(coredev->ir.phys, coredev->devpath, sizeof(coredev->ir.phys));
+ strlcat(coredev->ir.phys, "/ir0", sizeof(coredev->ir.phys));
+
+ dev->device_name = coredev->ir.name;
+ dev->input_phys = coredev->ir.phys;
+ dev->dev.parent = coredev->device;
+
+#if 0
+ /* TODO: properly initialize the parameters below */
+ dev->input_id.bustype = BUS_USB;
+ dev->input_id.version = 1;
+ dev->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+ dev->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
+#endif
+
+ dev->priv = coredev;
+ dev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
+ dev->map_name = sms_get_board(board_id)->rc_codes;
+ dev->driver_name = MODULE_NAME;
+
+ pr_debug("Input device (IR) %s is set for key events\n",
+ dev->device_name);
+
+ err = rc_register_device(dev);
+ if (err < 0) {
+ pr_err("Failed to register device\n");
+ rc_free_device(dev);
+ return err;
+ }
+
+ coredev->ir.dev = dev;
+ return 0;
+}
+
+void sms_ir_exit(struct smscore_device_t *coredev)
+{
+ rc_unregister_device(coredev->ir.dev);
+
+ pr_debug("\n");
+}
diff --git a/drivers/media/common/siano/smsir.h b/drivers/media/common/siano/smsir.h
new file mode 100644
index 000000000000..ada41d5c4e83
--- /dev/null
+++ b/drivers/media/common/siano/smsir.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ *
+ * Siano Mobile Silicon, Inc.
+ * MDTV receiver kernel modules.
+ * Copyright (C) 2006-2009, Uri Shkolnik
+ *
+ * Copyright (c) 2010 - Mauro Carvalho Chehab
+ * - Ported the driver to use rc-core
+ * - IR raw event decoding is now done at rc-core
+ * - Code almost re-written
+ */
+
+#ifndef __SMS_IR_H__
+#define __SMS_IR_H__
+
+#include <linux/input.h>
+#include <media/rc-core.h>
+
+struct smscore_device_t;
+
+struct ir_t {
+ struct rc_dev *dev;
+ char name[40];
+ char phys[32];
+
+ char *rc_codes;
+
+ u32 timeout;
+ u32 controller;
+};
+
+#ifdef CONFIG_SMS_SIANO_RC
+int sms_ir_init(struct smscore_device_t *coredev);
+void sms_ir_exit(struct smscore_device_t *coredev);
+void sms_ir_event(struct smscore_device_t *coredev,
+ const char *buf, int len);
+#else
+inline static int sms_ir_init(struct smscore_device_t *coredev) {
+ return 0;
+}
+inline static void sms_ir_exit(struct smscore_device_t *coredev) {};
+inline static void sms_ir_event(struct smscore_device_t *coredev,
+ const char *buf, int len) {};
+#endif
+
+#endif /* __SMS_IR_H__ */
+
diff --git a/drivers/media/dvb/ttpci/ttpci-eeprom.c b/drivers/media/common/ttpci-eeprom.c
index 32d43156c548..ef8746684d31 100644
--- a/drivers/media/dvb/ttpci/ttpci-eeprom.c
+++ b/drivers/media/common/ttpci-eeprom.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Retrieve encoded MAC address from 24C16 serial 2-wire EEPROM,
decode it and store it in the associated adapter struct for
@@ -15,19 +16,6 @@
Copyright (C) 2002-2003 Ralph Metzler <rjkm@metzlerbros.de>
Metzler Brothers Systementwicklung GbR
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
@@ -36,6 +24,7 @@
#include <linux/module.h>
#include <linux/string.h>
#include <linux/i2c.h>
+#include <linux/etherdevice.h>
#include "ttpci-eeprom.h"
@@ -137,7 +126,7 @@ static int ttpci_eeprom_read_encodedMAC(struct i2c_adapter *adapter, u8 * encode
int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac)
{
- int ret, i;
+ int ret;
u8 encodedMAC[20];
u8 decodedMAC[6];
@@ -145,26 +134,21 @@ int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac)
if (ret != 0) { /* Will only be -ENODEV */
dprintk("Couldn't read from EEPROM: not there?\n");
- memset(proposed_mac, 0, 6);
+ eth_zero_addr(proposed_mac);
return ret;
}
ret = getmac_tt(decodedMAC, encodedMAC);
if( ret != 0 ) {
dprintk("adapter failed MAC signature check\n");
- dprintk("encoded MAC from EEPROM was " );
- for(i=0; i<19; i++) {
- dprintk( "%.2x:", encodedMAC[i]);
- }
- dprintk("%.2x\n", encodedMAC[19]);
- memset(proposed_mac, 0, 6);
+ dprintk("encoded MAC from EEPROM was %*phC",
+ (int)sizeof(encodedMAC), &encodedMAC);
+ eth_zero_addr(proposed_mac);
return ret;
}
memcpy(proposed_mac, decodedMAC, 6);
- dprintk("adapter has MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
- decodedMAC[0], decodedMAC[1], decodedMAC[2],
- decodedMAC[3], decodedMAC[4], decodedMAC[5]);
+ dprintk("adapter has MAC addr = %pM\n", decodedMAC);
return 0;
}
@@ -172,5 +156,4 @@ EXPORT_SYMBOL(ttpci_eeprom_parse_mac);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others");
-MODULE_DESCRIPTION("Decode dvb_net MAC address from EEPROM of PCI DVB cards "
- "made by Siemens, Technotrend, Hauppauge");
+MODULE_DESCRIPTION("Decode dvb_net MAC address from EEPROM of PCI DVB cards made by Siemens, Technotrend, Hauppauge");
diff --git a/drivers/media/common/ttpci-eeprom.h b/drivers/media/common/ttpci-eeprom.h
new file mode 100644
index 000000000000..ee741867ba47
--- /dev/null
+++ b/drivers/media/common/ttpci-eeprom.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Retrieve encoded MAC address from ATMEL ttpci_eeprom serial 2-wire EEPROM,
+ decode it and store it in associated adapter net device
+
+ Robert Schlabbach GMX
+ Michael Glaum KVH Industries
+ Holger Waechtler Convergence
+
+
+*/
+
+#ifndef __TTPCI_EEPROM_H__
+#define __TTPCI_EEPROM_H__
+
+#include <linux/types.h>
+#include <linux/i2c.h>
+
+extern int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC);
+extern int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *propsed_mac);
+
+#endif
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig
deleted file mode 100644
index bbf4945149a9..000000000000
--- a/drivers/media/common/tuners/Kconfig
+++ /dev/null
@@ -1,241 +0,0 @@
-config MEDIA_ATTACH
- bool "Load and attach frontend and tuner driver modules as needed"
- depends on VIDEO_MEDIA
- depends on MODULES
- help
- Remove the static dependency of DVB card drivers on all
- frontend modules for all possible card variants. Instead,
- allow the card drivers to only load the frontend modules
- they require.
-
- Also, tuner module will automatically load a tuner driver
- when needed, for analog mode.
-
- This saves several KBytes of memory.
-
- Note: You will need module-init-tools v3.2 or later for this feature.
-
- If unsure say Y.
-
-config MEDIA_TUNER
- tristate
- default VIDEO_MEDIA && I2C
- depends on VIDEO_MEDIA && I2C
- select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_XC4000 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMISE && EXPERIMENTAL
- select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA9887 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE
-
-config MEDIA_TUNER_CUSTOMISE
- bool "Customize analog and hybrid tuner modules to build"
- depends on MEDIA_TUNER
- default y if EXPERT
- help
- This allows the user to deselect tuner drivers unnecessary
- for their hardware from the build. Use this option with care
- as deselecting tuner drivers which are in fact necessary will
- result in V4L/DVB devices which cannot be tuned due to lack of
- driver support
-
- If unsure say N.
-
-menu "Customize TV tuners"
- visible if MEDIA_TUNER_CUSTOMISE
-
-config MEDIA_TUNER_SIMPLE
- tristate "Simple tuner support"
- depends on VIDEO_MEDIA && I2C
- select MEDIA_TUNER_TDA9887
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to include support for various simple tuners.
-
-config MEDIA_TUNER_TDA8290
- tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo"
- depends on VIDEO_MEDIA && I2C
- select MEDIA_TUNER_TDA827X
- select MEDIA_TUNER_TDA18271
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to include support for Philips TDA8290+8275(a) tuner.
-
-config MEDIA_TUNER_TDA827X
- tristate "Philips TDA827X silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A DVB-T silicon tuner module. Say Y when you want to support this tuner.
-
-config MEDIA_TUNER_TDA18271
- tristate "NXP TDA18271 silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A silicon tuner module. Say Y when you want to support this tuner.
-
-config MEDIA_TUNER_TDA9887
- tristate "TDA 9885/6/7 analog IF demodulator"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to include support for Philips TDA9885/6/7
- analog IF demodulator.
-
-config MEDIA_TUNER_TEA5761
- tristate "TEA 5761 radio tuner (EXPERIMENTAL)"
- depends on VIDEO_MEDIA && I2C
- depends on EXPERIMENTAL
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to include support for the Philips TEA5761 radio tuner.
-
-config MEDIA_TUNER_TEA5767
- tristate "TEA 5767 radio tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to include support for the Philips TEA5767 radio tuner.
-
-config MEDIA_TUNER_MT20XX
- tristate "Microtune 2032 / 2050 tuners"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to include support for the MT2032 / MT2050 tuner.
-
-config MEDIA_TUNER_MT2060
- tristate "Microtune MT2060 silicon IF tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon IF tuner MT2060 from Microtune.
-
-config MEDIA_TUNER_MT2063
- tristate "Microtune MT2063 silicon IF tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon IF tuner MT2063 from Microtune.
-
-config MEDIA_TUNER_MT2266
- tristate "Microtune MT2266 silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon baseband tuner MT2266 from Microtune.
-
-config MEDIA_TUNER_MT2131
- tristate "Microtune MT2131 silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon baseband tuner MT2131 from Microtune.
-
-config MEDIA_TUNER_QT1010
- tristate "Quantek QT1010 silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon tuner QT1010 from Quantek.
-
-config MEDIA_TUNER_XC2028
- tristate "XCeive xc2028/xc3028 tuners"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to include support for the xc2028/xc3028 tuners.
-
-config MEDIA_TUNER_XC5000
- tristate "Xceive XC5000 silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon tuner XC5000 from Xceive.
- This device is only used inside a SiP called together with a
- demodulator for now.
-
-config MEDIA_TUNER_XC4000
- tristate "Xceive XC4000 silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon tuner XC4000 from Xceive.
- This device is only used inside a SiP called together with a
- demodulator for now.
-
-config MEDIA_TUNER_MXL5005S
- tristate "MaxLinear MSL5005S silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon tuner MXL5005S from MaxLinear.
-
-config MEDIA_TUNER_MXL5007T
- tristate "MaxLinear MxL5007T silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon tuner MxL5007T from MaxLinear.
-
-config MEDIA_TUNER_MC44S803
- tristate "Freescale MC44S803 Low Power CMOS Broadband tuners"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the Freescale MC44S803 based tuners
-
-config MEDIA_TUNER_MAX2165
- tristate "Maxim MAX2165 silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon tuner MAX2165 from Maxim.
-
-config MEDIA_TUNER_TDA18218
- tristate "NXP TDA18218 silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- NXP TDA18218 silicon tuner driver.
-
-config MEDIA_TUNER_FC0011
- tristate "Fitipower FC0011 silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Fitipower FC0011 silicon tuner driver.
-
-config MEDIA_TUNER_FC0012
- tristate "Fitipower FC0012 silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Fitipower FC0012 silicon tuner driver.
-
-config MEDIA_TUNER_FC0013
- tristate "Fitipower FC0013 silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Fitipower FC0013 silicon tuner driver.
-
-config MEDIA_TUNER_TDA18212
- tristate "NXP TDA18212 silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- NXP TDA18212 silicon tuner driver.
-
-config MEDIA_TUNER_TUA9001
- tristate "Infineon TUA 9001 silicon tuner"
- depends on VIDEO_MEDIA && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Infineon TUA 9001 silicon tuner driver.
-endmenu
diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile
deleted file mode 100644
index 891b80e60808..000000000000
--- a/drivers/media/common/tuners/Makefile
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Makefile for common V4L/DVB tuners
-#
-
-tda18271-objs := tda18271-maps.o tda18271-common.o tda18271-fe.o
-
-obj-$(CONFIG_MEDIA_TUNER_XC2028) += tuner-xc2028.o
-obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-simple.o
-# tuner-types will be merged into tuner-simple, in the future
-obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-types.o
-obj-$(CONFIG_MEDIA_TUNER_MT20XX) += mt20xx.o
-obj-$(CONFIG_MEDIA_TUNER_TDA8290) += tda8290.o
-obj-$(CONFIG_MEDIA_TUNER_TEA5767) += tea5767.o
-obj-$(CONFIG_MEDIA_TUNER_TEA5761) += tea5761.o
-obj-$(CONFIG_MEDIA_TUNER_TDA9887) += tda9887.o
-obj-$(CONFIG_MEDIA_TUNER_TDA827X) += tda827x.o
-obj-$(CONFIG_MEDIA_TUNER_TDA18271) += tda18271.o
-obj-$(CONFIG_MEDIA_TUNER_XC5000) += xc5000.o
-obj-$(CONFIG_MEDIA_TUNER_XC4000) += xc4000.o
-obj-$(CONFIG_MEDIA_TUNER_MT2060) += mt2060.o
-obj-$(CONFIG_MEDIA_TUNER_MT2063) += mt2063.o
-obj-$(CONFIG_MEDIA_TUNER_MT2266) += mt2266.o
-obj-$(CONFIG_MEDIA_TUNER_QT1010) += qt1010.o
-obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o
-obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o
-obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o
-obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o
-obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
-obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
-obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o
-obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o
-obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o
-obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o
-obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o
-
-ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
-ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
diff --git a/drivers/media/common/tuners/fc0012-priv.h b/drivers/media/common/tuners/fc0012-priv.h
deleted file mode 100644
index 4577c917e616..000000000000
--- a/drivers/media/common/tuners/fc0012-priv.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Fitipower FC0012 tuner driver - private includes
- *
- * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef _FC0012_PRIV_H_
-#define _FC0012_PRIV_H_
-
-#define LOG_PREFIX "fc0012"
-
-#undef err
-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
-#undef info
-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef warn
-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
-
-struct fc0012_priv {
- struct i2c_adapter *i2c;
- u8 addr;
- u8 dual_master;
- u8 xtal_freq;
-
- u32 frequency;
- u32 bandwidth;
-};
-
-#endif
diff --git a/drivers/media/common/tuners/fc0012.h b/drivers/media/common/tuners/fc0012.h
deleted file mode 100644
index 4dbd5efe8845..000000000000
--- a/drivers/media/common/tuners/fc0012.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Fitipower FC0012 tuner driver - include
- *
- * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef _FC0012_H_
-#define _FC0012_H_
-
-#include "dvb_frontend.h"
-#include "fc001x-common.h"
-
-#if defined(CONFIG_MEDIA_TUNER_FC0012) || \
- (defined(CONFIG_MEDIA_TUNER_FC0012_MODULE) && defined(MODULE))
-extern struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- u8 i2c_address, int dual_master,
- enum fc001x_xtal_freq xtal_freq);
-#else
-static inline struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- u8 i2c_address, int dual_master,
- enum fc001x_xtal_freq xtal_freq)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif
diff --git a/drivers/media/common/tuners/fc0013-priv.h b/drivers/media/common/tuners/fc0013-priv.h
deleted file mode 100644
index bfd49dedea22..000000000000
--- a/drivers/media/common/tuners/fc0013-priv.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Fitipower FC0013 tuner driver
- *
- * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#ifndef _FC0013_PRIV_H_
-#define _FC0013_PRIV_H_
-
-#define LOG_PREFIX "fc0013"
-
-#undef err
-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
-#undef info
-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef warn
-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
-
-struct fc0013_priv {
- struct i2c_adapter *i2c;
- u8 addr;
- u8 dual_master;
- u8 xtal_freq;
-
- u32 frequency;
- u32 bandwidth;
-};
-
-#endif
diff --git a/drivers/media/common/tuners/fc0013.h b/drivers/media/common/tuners/fc0013.h
deleted file mode 100644
index 594efd64aeec..000000000000
--- a/drivers/media/common/tuners/fc0013.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Fitipower FC0013 tuner driver
- *
- * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#ifndef _FC0013_H_
-#define _FC0013_H_
-
-#include "dvb_frontend.h"
-#include "fc001x-common.h"
-
-#if defined(CONFIG_MEDIA_TUNER_FC0013) || \
- (defined(CONFIG_MEDIA_TUNER_FC0013_MODULE) && defined(MODULE))
-extern struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- u8 i2c_address, int dual_master,
- enum fc001x_xtal_freq xtal_freq);
-extern int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val);
-extern int fc0013_rc_cal_reset(struct dvb_frontend *fe);
-#else
-static inline struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- u8 i2c_address, int dual_master,
- enum fc001x_xtal_freq xtal_freq)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-
-static inline int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val)
-{
- return 0;
-}
-
-static inline int fc0013_rc_cal_reset(struct dvb_frontend *fe)
-{
- return 0;
-}
-#endif
-
-#endif
diff --git a/drivers/media/common/tuners/fc001x-common.h b/drivers/media/common/tuners/fc001x-common.h
deleted file mode 100644
index 718818156934..000000000000
--- a/drivers/media/common/tuners/fc001x-common.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Fitipower FC0012 & FC0013 tuner driver - common defines
- *
- * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef _FC001X_COMMON_H_
-#define _FC001X_COMMON_H_
-
-enum fc001x_xtal_freq {
- FC_XTAL_27_MHZ, /* 27000000 */
- FC_XTAL_28_8_MHZ, /* 28800000 */
- FC_XTAL_36_MHZ, /* 36000000 */
-};
-
-/*
- * enum fc001x_fe_callback_commands - Frontend callbacks
- *
- * @FC_FE_CALLBACK_VHF_ENABLE: enable VHF or UHF
- */
-enum fc001x_fe_callback_commands {
- FC_FE_CALLBACK_VHF_ENABLE,
-};
-
-#endif
diff --git a/drivers/media/common/tuners/max2165.h b/drivers/media/common/tuners/max2165.h
deleted file mode 100644
index c063c36a93d3..000000000000
--- a/drivers/media/common/tuners/max2165.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Driver for Maxim MAX2165 silicon tuner
- *
- * Copyright (c) 2009 David T. L. Wong <davidtlwong@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef __MAX2165_H__
-#define __MAX2165_H__
-
-struct dvb_frontend;
-struct i2c_adapter;
-
-struct max2165_config {
- u8 i2c_address;
- u8 osc_clk; /* in MHz, selectable values: 4,16,18,20,22,24,26,28 */
-};
-
-#if defined(CONFIG_MEDIA_TUNER_MAX2165) || \
- (defined(CONFIG_MEDIA_TUNER_MAX2165_MODULE) && defined(MODULE))
-extern struct dvb_frontend *max2165_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- struct max2165_config *cfg);
-#else
-static inline struct dvb_frontend *max2165_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- struct max2165_config *cfg)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif
diff --git a/drivers/media/common/tuners/max2165_priv.h b/drivers/media/common/tuners/max2165_priv.h
deleted file mode 100644
index 91bbe021a08d..000000000000
--- a/drivers/media/common/tuners/max2165_priv.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Driver for Maxim MAX2165 silicon tuner
- *
- * Copyright (c) 2009 David T. L. Wong <davidtlwong@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef __MAX2165_PRIV_H__
-#define __MAX2165_PRIV_H__
-
-#define REG_NDIV_INT 0x00
-#define REG_NDIV_FRAC2 0x01
-#define REG_NDIV_FRAC1 0x02
-#define REG_NDIV_FRAC0 0x03
-#define REG_TRACK_FILTER 0x04
-#define REG_LNA 0x05
-#define REG_PLL_CFG 0x06
-#define REG_TEST 0x07
-#define REG_SHUTDOWN 0x08
-#define REG_VCO_CTRL 0x09
-#define REG_BASEBAND_CTRL 0x0A
-#define REG_DC_OFFSET_CTRL 0x0B
-#define REG_DC_OFFSET_DAC 0x0C
-#define REG_ROM_TABLE_ADDR 0x0D
-
-/* Read Only Registers */
-#define REG_ROM_TABLE_DATA 0x10
-#define REG_STATUS 0x11
-#define REG_AUTOTUNE 0x12
-
-struct max2165_priv {
- struct max2165_config *config;
- struct i2c_adapter *i2c;
-
- u32 frequency;
- u32 bandwidth;
-
- u8 tf_ntch_low_cfg;
- u8 tf_ntch_hi_cfg;
- u8 tf_balun_low_ref;
- u8 tf_balun_hi_ref;
- u8 bb_filter_7mhz_cfg;
- u8 bb_filter_8mhz_cfg;
-};
-
-#endif
diff --git a/drivers/media/common/tuners/mc44s803.h b/drivers/media/common/tuners/mc44s803.h
deleted file mode 100644
index 34f3892d3f6d..000000000000
--- a/drivers/media/common/tuners/mc44s803.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner
- *
- * Copyright (c) 2009 Jochen Friedrich <jochen@scram.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
- */
-
-#ifndef MC44S803_H
-#define MC44S803_H
-
-struct dvb_frontend;
-struct i2c_adapter;
-
-struct mc44s803_config {
- u8 i2c_address;
- u8 dig_out;
-};
-
-#if defined(CONFIG_MEDIA_TUNER_MC44S803) || \
- (defined(CONFIG_MEDIA_TUNER_MC44S803_MODULE) && defined(MODULE))
-extern struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct mc44s803_config *cfg);
-#else
-static inline struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct mc44s803_config *cfg)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif /* CONFIG_MEDIA_TUNER_MC44S803 */
-
-#endif
diff --git a/drivers/media/common/tuners/mt2060.c b/drivers/media/common/tuners/mt2060.c
deleted file mode 100644
index 13381de58a84..000000000000
--- a/drivers/media/common/tuners/mt2060.c
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner"
- *
- * Copyright (c) 2006 Olivier DANET <odanet@caramail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
- */
-
-/* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/dvb/frontend.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-
-#include "dvb_frontend.h"
-
-#include "mt2060.h"
-#include "mt2060_priv.h"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-
-#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2060: " args); printk("\n"); }} while (0)
-
-// Reads a single register
-static int mt2060_readreg(struct mt2060_priv *priv, u8 reg, u8 *val)
-{
- struct i2c_msg msg[2] = {
- { .addr = priv->cfg->i2c_address, .flags = 0, .buf = &reg, .len = 1 },
- { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 },
- };
-
- if (i2c_transfer(priv->i2c, msg, 2) != 2) {
- printk(KERN_WARNING "mt2060 I2C read failed\n");
- return -EREMOTEIO;
- }
- return 0;
-}
-
-// Writes a single register
-static int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val)
-{
- u8 buf[2] = { reg, val };
- struct i2c_msg msg = {
- .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2
- };
-
- if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
- printk(KERN_WARNING "mt2060 I2C write failed\n");
- return -EREMOTEIO;
- }
- return 0;
-}
-
-// Writes a set of consecutive registers
-static int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len)
-{
- struct i2c_msg msg = {
- .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len
- };
- if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
- printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n",(int)len);
- return -EREMOTEIO;
- }
- return 0;
-}
-
-// Initialisation sequences
-// LNABAND=3, NUM1=0x3C, DIV1=0x74, NUM2=0x1080, DIV2=0x49
-static u8 mt2060_config1[] = {
- REG_LO1C1,
- 0x3F, 0x74, 0x00, 0x08, 0x93
-};
-
-// FMCG=2, GP2=0, GP1=0
-static u8 mt2060_config2[] = {
- REG_MISC_CTRL,
- 0x20, 0x1E, 0x30, 0xff, 0x80, 0xff, 0x00, 0x2c, 0x42
-};
-
-// VGAG=3, V1CSE=1
-
-#ifdef MT2060_SPURCHECK
-/* The function below calculates the frequency offset between the output frequency if2
- and the closer cross modulation subcarrier between lo1 and lo2 up to the tenth harmonic */
-static int mt2060_spurcalc(u32 lo1,u32 lo2,u32 if2)
-{
- int I,J;
- int dia,diamin,diff;
- diamin=1000000;
- for (I = 1; I < 10; I++) {
- J = ((2*I*lo1)/lo2+1)/2;
- diff = I*(int)lo1-J*(int)lo2;
- if (diff < 0) diff=-diff;
- dia = (diff-(int)if2);
- if (dia < 0) dia=-dia;
- if (diamin > dia) diamin=dia;
- }
- return diamin;
-}
-
-#define BANDWIDTH 4000 // kHz
-
-/* Calculates the frequency offset to add to avoid spurs. Returns 0 if no offset is needed */
-static int mt2060_spurcheck(u32 lo1,u32 lo2,u32 if2)
-{
- u32 Spur,Sp1,Sp2;
- int I,J;
- I=0;
- J=1000;
-
- Spur=mt2060_spurcalc(lo1,lo2,if2);
- if (Spur < BANDWIDTH) {
- /* Potential spurs detected */
- dprintk("Spurs before : f_lo1: %d f_lo2: %d (kHz)",
- (int)lo1,(int)lo2);
- I=1000;
- Sp1 = mt2060_spurcalc(lo1+I,lo2+I,if2);
- Sp2 = mt2060_spurcalc(lo1-I,lo2-I,if2);
-
- if (Sp1 < Sp2) {
- J=-J; I=-I; Spur=Sp2;
- } else
- Spur=Sp1;
-
- while (Spur < BANDWIDTH) {
- I += J;
- Spur = mt2060_spurcalc(lo1+I,lo2+I,if2);
- }
- dprintk("Spurs after : f_lo1: %d f_lo2: %d (kHz)",
- (int)(lo1+I),(int)(lo2+I));
- }
- return I;
-}
-#endif
-
-#define IF2 36150 // IF2 frequency = 36.150 MHz
-#define FREF 16000 // Quartz oscillator 16 MHz
-
-static int mt2060_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct mt2060_priv *priv;
- int ret=0;
- int i=0;
- u32 freq;
- u8 lnaband;
- u32 f_lo1,f_lo2;
- u32 div1,num1,div2,num2;
- u8 b[8];
- u32 if1;
-
- priv = fe->tuner_priv;
-
- if1 = priv->if1_freq;
- b[0] = REG_LO1B1;
- b[1] = 0xFF;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
-
- mt2060_writeregs(priv,b,2);
-
- freq = c->frequency / 1000; /* Hz -> kHz */
-
- f_lo1 = freq + if1 * 1000;
- f_lo1 = (f_lo1 / 250) * 250;
- f_lo2 = f_lo1 - freq - IF2;
- // From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise
- f_lo2 = ((f_lo2 + 25) / 50) * 50;
- priv->frequency = (f_lo1 - f_lo2 - IF2) * 1000,
-
-#ifdef MT2060_SPURCHECK
- // LO-related spurs detection and correction
- num1 = mt2060_spurcheck(f_lo1,f_lo2,IF2);
- f_lo1 += num1;
- f_lo2 += num1;
-#endif
- //Frequency LO1 = 16MHz * (DIV1 + NUM1/64 )
- num1 = f_lo1 / (FREF / 64);
- div1 = num1 / 64;
- num1 &= 0x3f;
-
- // Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 )
- num2 = f_lo2 * 64 / (FREF / 128);
- div2 = num2 / 8192;
- num2 &= 0x1fff;
-
- if (freq <= 95000) lnaband = 0xB0; else
- if (freq <= 180000) lnaband = 0xA0; else
- if (freq <= 260000) lnaband = 0x90; else
- if (freq <= 335000) lnaband = 0x80; else
- if (freq <= 425000) lnaband = 0x70; else
- if (freq <= 480000) lnaband = 0x60; else
- if (freq <= 570000) lnaband = 0x50; else
- if (freq <= 645000) lnaband = 0x40; else
- if (freq <= 730000) lnaband = 0x30; else
- if (freq <= 810000) lnaband = 0x20; else lnaband = 0x10;
-
- b[0] = REG_LO1C1;
- b[1] = lnaband | ((num1 >>2) & 0x0F);
- b[2] = div1;
- b[3] = (num2 & 0x0F) | ((num1 & 3) << 4);
- b[4] = num2 >> 4;
- b[5] = ((num2 >>12) & 1) | (div2 << 1);
-
- dprintk("IF1: %dMHz",(int)if1);
- dprintk("PLL freq=%dkHz f_lo1=%dkHz f_lo2=%dkHz",(int)freq,(int)f_lo1,(int)f_lo2);
- dprintk("PLL div1=%d num1=%d div2=%d num2=%d",(int)div1,(int)num1,(int)div2,(int)num2);
- dprintk("PLL [1..5]: %2x %2x %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3],(int)b[4],(int)b[5]);
-
- mt2060_writeregs(priv,b,6);
-
- //Waits for pll lock or timeout
- i = 0;
- do {
- mt2060_readreg(priv,REG_LO_STATUS,b);
- if ((b[0] & 0x88)==0x88)
- break;
- msleep(4);
- i++;
- } while (i<10);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
-
- return ret;
-}
-
-static void mt2060_calibrate(struct mt2060_priv *priv)
-{
- u8 b = 0;
- int i = 0;
-
- if (mt2060_writeregs(priv,mt2060_config1,sizeof(mt2060_config1)))
- return;
- if (mt2060_writeregs(priv,mt2060_config2,sizeof(mt2060_config2)))
- return;
-
- /* initialize the clock output */
- mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30);
-
- do {
- b |= (1 << 6); // FM1SS;
- mt2060_writereg(priv, REG_LO2C1,b);
- msleep(20);
-
- if (i == 0) {
- b |= (1 << 7); // FM1CA;
- mt2060_writereg(priv, REG_LO2C1,b);
- b &= ~(1 << 7); // FM1CA;
- msleep(20);
- }
-
- b &= ~(1 << 6); // FM1SS
- mt2060_writereg(priv, REG_LO2C1,b);
-
- msleep(20);
- i++;
- } while (i < 9);
-
- i = 0;
- while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0)
- msleep(20);
-
- if (i <= 10) {
- mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :)
- dprintk("calibration was successful: %d", (int)priv->fmfreq);
- } else
- dprintk("FMCAL timed out");
-}
-
-static int mt2060_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct mt2060_priv *priv = fe->tuner_priv;
- *frequency = priv->frequency;
- return 0;
-}
-
-static int mt2060_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- *frequency = IF2 * 1000;
- return 0;
-}
-
-static int mt2060_init(struct dvb_frontend *fe)
-{
- struct mt2060_priv *priv = fe->tuner_priv;
- int ret;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
-
- ret = mt2060_writereg(priv, REG_VGAG,
- (priv->cfg->clock_out << 6) | 0x33);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
-
- return ret;
-}
-
-static int mt2060_sleep(struct dvb_frontend *fe)
-{
- struct mt2060_priv *priv = fe->tuner_priv;
- int ret;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
-
- ret = mt2060_writereg(priv, REG_VGAG,
- (priv->cfg->clock_out << 6) | 0x30);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
-
- return ret;
-}
-
-static int mt2060_release(struct dvb_frontend *fe)
-{
- kfree(fe->tuner_priv);
- fe->tuner_priv = NULL;
- return 0;
-}
-
-static const struct dvb_tuner_ops mt2060_tuner_ops = {
- .info = {
- .name = "Microtune MT2060",
- .frequency_min = 48000000,
- .frequency_max = 860000000,
- .frequency_step = 50000,
- },
-
- .release = mt2060_release,
-
- .init = mt2060_init,
- .sleep = mt2060_sleep,
-
- .set_params = mt2060_set_params,
- .get_frequency = mt2060_get_frequency,
- .get_if_frequency = mt2060_get_if_frequency,
-};
-
-/* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */
-struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1)
-{
- struct mt2060_priv *priv = NULL;
- u8 id = 0;
-
- priv = kzalloc(sizeof(struct mt2060_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
-
- priv->cfg = cfg;
- priv->i2c = i2c;
- priv->if1_freq = if1;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
-
- if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) {
- kfree(priv);
- return NULL;
- }
-
- if (id != PART_REV) {
- kfree(priv);
- return NULL;
- }
- printk(KERN_INFO "MT2060: successfully identified (IF1 = %d)\n", if1);
- memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops));
-
- fe->tuner_priv = priv;
-
- mt2060_calibrate(priv);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
-
- return fe;
-}
-EXPORT_SYMBOL(mt2060_attach);
-
-MODULE_AUTHOR("Olivier DANET");
-MODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/mt2060.h b/drivers/media/common/tuners/mt2060.h
deleted file mode 100644
index cb60caffb6b6..000000000000
--- a/drivers/media/common/tuners/mt2060.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner"
- *
- * Copyright (c) 2006 Olivier DANET <odanet@caramail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
- */
-
-#ifndef MT2060_H
-#define MT2060_H
-
-struct dvb_frontend;
-struct i2c_adapter;
-
-struct mt2060_config {
- u8 i2c_address;
- u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */
-};
-
-#if defined(CONFIG_MEDIA_TUNER_MT2060) || (defined(CONFIG_MEDIA_TUNER_MT2060_MODULE) && defined(MODULE))
-extern struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1);
-#else
-static inline struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif // CONFIG_MEDIA_TUNER_MT2060
-
-#endif
diff --git a/drivers/media/common/tuners/mt2063.h b/drivers/media/common/tuners/mt2063.h
deleted file mode 100644
index 3f5cfd93713f..000000000000
--- a/drivers/media/common/tuners/mt2063.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef __MT2063_H__
-#define __MT2063_H__
-
-#include "dvb_frontend.h"
-
-struct mt2063_config {
- u8 tuner_address;
- u32 refclock;
-};
-
-#if defined(CONFIG_MEDIA_TUNER_MT2063) || (defined(CONFIG_MEDIA_TUNER_MT2063_MODULE) && defined(MODULE))
-struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe,
- struct mt2063_config *config,
- struct i2c_adapter *i2c);
-
-#else
-
-static inline struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe,
- struct mt2063_config *config,
- struct i2c_adapter *i2c)
-{
- printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-
-/* FIXME: Should use the standard DVB attachment interfaces */
-unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe);
-unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe);
-
-#endif /* CONFIG_DVB_MT2063 */
-
-#endif /* __MT2063_H__ */
diff --git a/drivers/media/common/tuners/mt20xx.h b/drivers/media/common/tuners/mt20xx.h
deleted file mode 100644
index 259553a24903..000000000000
--- a/drivers/media/common/tuners/mt20xx.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __MT20XX_H__
-#define __MT20XX_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-#if defined(CONFIG_MEDIA_TUNER_MT20XX) || (defined(CONFIG_MEDIA_TUNER_MT20XX_MODULE) && defined(MODULE))
-extern struct dvb_frontend *microtune_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr);
-#else
-static inline struct dvb_frontend *microtune_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* __MT20XX_H__ */
diff --git a/drivers/media/common/tuners/mt2131.h b/drivers/media/common/tuners/mt2131.h
deleted file mode 100644
index 6632de640df0..000000000000
--- a/drivers/media/common/tuners/mt2131.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Driver for Microtune MT2131 "QAM/8VSB single chip tuner"
- *
- * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef __MT2131_H__
-#define __MT2131_H__
-
-struct dvb_frontend;
-struct i2c_adapter;
-
-struct mt2131_config {
- u8 i2c_address;
- u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */
-};
-
-#if defined(CONFIG_MEDIA_TUNER_MT2131) || (defined(CONFIG_MEDIA_TUNER_MT2131_MODULE) && defined(MODULE))
-extern struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- struct mt2131_config *cfg,
- u16 if1);
-#else
-static inline struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- struct mt2131_config *cfg,
- u16 if1)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif /* CONFIG_MEDIA_TUNER_MT2131 */
-
-#endif /* __MT2131_H__ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- */
diff --git a/drivers/media/common/tuners/mt2131_priv.h b/drivers/media/common/tuners/mt2131_priv.h
deleted file mode 100644
index 62aeedf5c550..000000000000
--- a/drivers/media/common/tuners/mt2131_priv.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Driver for Microtune MT2131 "QAM/8VSB single chip tuner"
- *
- * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef __MT2131_PRIV_H__
-#define __MT2131_PRIV_H__
-
-/* Regs */
-#define MT2131_PWR 0x07
-#define MT2131_UPC_1 0x0b
-#define MT2131_AGC_RL 0x10
-#define MT2131_MISC_2 0x15
-
-/* frequency values in KHz */
-#define MT2131_IF1 1220
-#define MT2131_IF2 44000
-#define MT2131_FREF 16000
-
-struct mt2131_priv {
- struct mt2131_config *cfg;
- struct i2c_adapter *i2c;
-
- u32 frequency;
-};
-
-#endif /* __MT2131_PRIV_H__ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- */
diff --git a/drivers/media/common/tuners/mt2266.h b/drivers/media/common/tuners/mt2266.h
deleted file mode 100644
index 4d083882d044..000000000000
--- a/drivers/media/common/tuners/mt2266.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Driver for Microtune MT2266 "Direct conversion low power broadband tuner"
- *
- * Copyright (c) 2007 Olivier DANET <odanet@caramail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef MT2266_H
-#define MT2266_H
-
-struct dvb_frontend;
-struct i2c_adapter;
-
-struct mt2266_config {
- u8 i2c_address;
-};
-
-#if defined(CONFIG_MEDIA_TUNER_MT2266) || (defined(CONFIG_MEDIA_TUNER_MT2266_MODULE) && defined(MODULE))
-extern struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg);
-#else
-static inline struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif // CONFIG_MEDIA_TUNER_MT2266
-
-#endif
diff --git a/drivers/media/common/tuners/mxl5007t.h b/drivers/media/common/tuners/mxl5007t.h
deleted file mode 100644
index aa3eea0b5262..000000000000
--- a/drivers/media/common/tuners/mxl5007t.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * mxl5007t.h - driver for the MaxLinear MxL5007T silicon tuner
- *
- * Copyright (C) 2008 Michael Krufky <mkrufky@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef __MXL5007T_H__
-#define __MXL5007T_H__
-
-#include "dvb_frontend.h"
-
-/* ------------------------------------------------------------------------- */
-
-enum mxl5007t_if_freq {
- MxL_IF_4_MHZ, /* 4000000 */
- MxL_IF_4_5_MHZ, /* 4500000 */
- MxL_IF_4_57_MHZ, /* 4570000 */
- MxL_IF_5_MHZ, /* 5000000 */
- MxL_IF_5_38_MHZ, /* 5380000 */
- MxL_IF_6_MHZ, /* 6000000 */
- MxL_IF_6_28_MHZ, /* 6280000 */
- MxL_IF_9_1915_MHZ, /* 9191500 */
- MxL_IF_35_25_MHZ, /* 35250000 */
- MxL_IF_36_15_MHZ, /* 36150000 */
- MxL_IF_44_MHZ, /* 44000000 */
-};
-
-enum mxl5007t_xtal_freq {
- MxL_XTAL_16_MHZ, /* 16000000 */
- MxL_XTAL_20_MHZ, /* 20000000 */
- MxL_XTAL_20_25_MHZ, /* 20250000 */
- MxL_XTAL_20_48_MHZ, /* 20480000 */
- MxL_XTAL_24_MHZ, /* 24000000 */
- MxL_XTAL_25_MHZ, /* 25000000 */
- MxL_XTAL_25_14_MHZ, /* 25140000 */
- MxL_XTAL_27_MHZ, /* 27000000 */
- MxL_XTAL_28_8_MHZ, /* 28800000 */
- MxL_XTAL_32_MHZ, /* 32000000 */
- MxL_XTAL_40_MHZ, /* 40000000 */
- MxL_XTAL_44_MHZ, /* 44000000 */
- MxL_XTAL_48_MHZ, /* 48000000 */
- MxL_XTAL_49_3811_MHZ, /* 49381100 */
-};
-
-enum mxl5007t_clkout_amp {
- MxL_CLKOUT_AMP_0_94V = 0,
- MxL_CLKOUT_AMP_0_53V = 1,
- MxL_CLKOUT_AMP_0_37V = 2,
- MxL_CLKOUT_AMP_0_28V = 3,
- MxL_CLKOUT_AMP_0_23V = 4,
- MxL_CLKOUT_AMP_0_20V = 5,
- MxL_CLKOUT_AMP_0_17V = 6,
- MxL_CLKOUT_AMP_0_15V = 7,
-};
-
-struct mxl5007t_config {
- s32 if_diff_out_level;
- enum mxl5007t_clkout_amp clk_out_amp;
- enum mxl5007t_xtal_freq xtal_freq_hz;
- enum mxl5007t_if_freq if_freq_hz;
- unsigned int invert_if:1;
- unsigned int loop_thru_enable:1;
- unsigned int clk_out_enable:1;
-};
-
-#if defined(CONFIG_MEDIA_TUNER_MXL5007T) || (defined(CONFIG_MEDIA_TUNER_MXL5007T_MODULE) && defined(MODULE))
-extern struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, u8 addr,
- struct mxl5007t_config *cfg);
-#else
-static inline struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- u8 addr,
- struct mxl5007t_config *cfg)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* __MXL5007T_H__ */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
-
diff --git a/drivers/media/common/tuners/qt1010.h b/drivers/media/common/tuners/qt1010.h
deleted file mode 100644
index 807fb7b6146b..000000000000
--- a/drivers/media/common/tuners/qt1010.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Driver for Quantek QT1010 silicon tuner
- *
- * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
- * Aapo Tahkola <aet@rasterburn.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef QT1010_H
-#define QT1010_H
-
-#include "dvb_frontend.h"
-
-struct qt1010_config {
- u8 i2c_address;
-};
-
-/**
- * Attach a qt1010 tuner to the supplied frontend structure.
- *
- * @param fe frontend to attach to
- * @param i2c i2c adapter to use
- * @param cfg tuner hw based configuration
- * @return fe pointer on success, NULL on failure
- */
-#if defined(CONFIG_MEDIA_TUNER_QT1010) || (defined(CONFIG_MEDIA_TUNER_QT1010_MODULE) && defined(MODULE))
-extern struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- struct qt1010_config *cfg);
-#else
-static inline struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- struct qt1010_config *cfg)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif // CONFIG_MEDIA_TUNER_QT1010
-
-#endif
diff --git a/drivers/media/common/tuners/qt1010_priv.h b/drivers/media/common/tuners/qt1010_priv.h
deleted file mode 100644
index 2c42d3f01636..000000000000
--- a/drivers/media/common/tuners/qt1010_priv.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Driver for Quantek QT1010 silicon tuner
- *
- * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
- * Aapo Tahkola <aet@rasterburn.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef QT1010_PRIV_H
-#define QT1010_PRIV_H
-
-/*
-reg def meaning
-=== === =======
-00 00 ?
-01 a0 ? operation start/stop; start=80, stop=00
-02 00 ?
-03 19 ?
-04 00 ?
-05 00 ? maybe band selection
-06 00 ?
-07 2b set frequency: 32 MHz scale, n*32 MHz
-08 0b ?
-09 10 ? changes every 8/24 MHz; values 1d/1c
-0a 08 set frequency: 4 MHz scale, n*4 MHz
-0b 41 ? changes every 2/2 MHz; values 45/45
-0c e1 ?
-0d 94 ?
-0e b6 ?
-0f 2c ?
-10 10 ?
-11 f1 ? maybe device specified adjustment
-12 11 ? maybe device specified adjustment
-13 3f ?
-14 1f ?
-15 3f ?
-16 ff ?
-17 ff ?
-18 f7 ?
-19 80 ?
-1a d0 set frequency: 125 kHz scale, n*125 kHz
-1b 00 ?
-1c 89 ?
-1d 00 ?
-1e 00 ? looks like operation register; write cmd here, read result from 1f-26
-1f 20 ? chip initialization
-20 e0 ? chip initialization
-21 20 ?
-22 d0 ?
-23 d0 ?
-24 d0 ?
-25 40 ? chip initialization
-26 08 ?
-27 29 ?
-28 55 ?
-29 39 ?
-2a 13 ?
-2b 01 ?
-2c ea ?
-2d 00 ?
-2e 00 ? not used?
-2f 00 ? not used?
-*/
-
-#define QT1010_STEP 125000 /* 125 kHz used by Windows drivers,
- hw could be more precise but we don't
- know how to use */
-#define QT1010_MIN_FREQ 48000000 /* 48 MHz */
-#define QT1010_MAX_FREQ 860000000 /* 860 MHz */
-#define QT1010_OFFSET 1246000000 /* 1246 MHz */
-
-#define QT1010_WR 0
-#define QT1010_RD 1
-#define QT1010_M1 3
-
-typedef struct {
- u8 oper, reg, val;
-} qt1010_i2c_oper_t;
-
-struct qt1010_priv {
- struct qt1010_config *cfg;
- struct i2c_adapter *i2c;
-
- u8 reg1f_init_val;
- u8 reg20_init_val;
- u8 reg25_init_val;
-
- u32 frequency;
-};
-
-#endif
diff --git a/drivers/media/common/tuners/tda18212.c b/drivers/media/common/tuners/tda18212.c
deleted file mode 100644
index 602c2e392b17..000000000000
--- a/drivers/media/common/tuners/tda18212.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * NXP TDA18212HN silicon tuner driver
- *
- * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include "tda18212.h"
-
-struct tda18212_priv {
- struct tda18212_config *cfg;
- struct i2c_adapter *i2c;
-
- u32 if_frequency;
-};
-
-#define dbg(fmt, arg...) \
-do { \
- if (debug) \
- pr_info("%s: " fmt, __func__, ##arg); \
-} while (0)
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-
-/* write multiple registers */
-static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
- int len)
-{
- int ret;
- u8 buf[len+1];
- struct i2c_msg msg[1] = {
- {
- .addr = priv->cfg->i2c_address,
- .flags = 0,
- .len = sizeof(buf),
- .buf = buf,
- }
- };
-
- buf[0] = reg;
- memcpy(&buf[1], val, len);
-
- ret = i2c_transfer(priv->i2c, msg, 1);
- if (ret == 1) {
- ret = 0;
- } else {
- pr_warn("i2c wr failed ret:%d reg:%02x len:%d\n",
- ret, reg, len);
- ret = -EREMOTEIO;
- }
- return ret;
-}
-
-/* read multiple registers */
-static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
- int len)
-{
- int ret;
- u8 buf[len];
- struct i2c_msg msg[2] = {
- {
- .addr = priv->cfg->i2c_address,
- .flags = 0,
- .len = 1,
- .buf = &reg,
- }, {
- .addr = priv->cfg->i2c_address,
- .flags = I2C_M_RD,
- .len = sizeof(buf),
- .buf = buf,
- }
- };
-
- ret = i2c_transfer(priv->i2c, msg, 2);
- if (ret == 2) {
- memcpy(val, buf, len);
- ret = 0;
- } else {
- pr_warn("i2c rd failed ret:%d reg:%02x len:%d\n",
- ret, reg, len);
- ret = -EREMOTEIO;
- }
-
- return ret;
-}
-
-/* write single register */
-static int tda18212_wr_reg(struct tda18212_priv *priv, u8 reg, u8 val)
-{
- return tda18212_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register */
-static int tda18212_rd_reg(struct tda18212_priv *priv, u8 reg, u8 *val)
-{
- return tda18212_rd_regs(priv, reg, val, 1);
-}
-
-#if 0 /* keep, useful when developing driver */
-static void tda18212_dump_regs(struct tda18212_priv *priv)
-{
- int i;
- u8 buf[256];
-
- #define TDA18212_RD_LEN 32
- for (i = 0; i < sizeof(buf); i += TDA18212_RD_LEN)
- tda18212_rd_regs(priv, i, &buf[i], TDA18212_RD_LEN);
-
- print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 32, 1, buf,
- sizeof(buf), true);
-
- return;
-}
-#endif
-
-static int tda18212_set_params(struct dvb_frontend *fe)
-{
- struct tda18212_priv *priv = fe->tuner_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i;
- u32 if_khz;
- u8 buf[9];
- #define DVBT_6 0
- #define DVBT_7 1
- #define DVBT_8 2
- #define DVBT2_6 3
- #define DVBT2_7 4
- #define DVBT2_8 5
- #define DVBC_6 6
- #define DVBC_8 7
- static const u8 bw_params[][3] = {
- /* reg: 0f 13 23 */
- [DVBT_6] = { 0xb3, 0x20, 0x03 },
- [DVBT_7] = { 0xb3, 0x31, 0x01 },
- [DVBT_8] = { 0xb3, 0x22, 0x01 },
- [DVBT2_6] = { 0xbc, 0x20, 0x03 },
- [DVBT2_7] = { 0xbc, 0x72, 0x03 },
- [DVBT2_8] = { 0xbc, 0x22, 0x01 },
- [DVBC_6] = { 0x92, 0x50, 0x03 },
- [DVBC_8] = { 0x92, 0x53, 0x03 },
- };
-
- dbg("delsys=%d RF=%d BW=%d\n",
- c->delivery_system, c->frequency, c->bandwidth_hz);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
-
- switch (c->delivery_system) {
- case SYS_DVBT:
- switch (c->bandwidth_hz) {
- case 6000000:
- if_khz = priv->cfg->if_dvbt_6;
- i = DVBT_6;
- break;
- case 7000000:
- if_khz = priv->cfg->if_dvbt_7;
- i = DVBT_7;
- break;
- case 8000000:
- if_khz = priv->cfg->if_dvbt_8;
- i = DVBT_8;
- break;
- default:
- ret = -EINVAL;
- goto error;
- }
- break;
- case SYS_DVBT2:
- switch (c->bandwidth_hz) {
- case 6000000:
- if_khz = priv->cfg->if_dvbt2_6;
- i = DVBT2_6;
- break;
- case 7000000:
- if_khz = priv->cfg->if_dvbt2_7;
- i = DVBT2_7;
- break;
- case 8000000:
- if_khz = priv->cfg->if_dvbt2_8;
- i = DVBT2_8;
- break;
- default:
- ret = -EINVAL;
- goto error;
- }
- break;
- case SYS_DVBC_ANNEX_A:
- case SYS_DVBC_ANNEX_C:
- if_khz = priv->cfg->if_dvbc;
- i = DVBC_8;
- break;
- default:
- ret = -EINVAL;
- goto error;
- }
-
- ret = tda18212_wr_reg(priv, 0x23, bw_params[i][2]);
- if (ret)
- goto error;
-
- ret = tda18212_wr_reg(priv, 0x06, 0x00);
- if (ret)
- goto error;
-
- ret = tda18212_wr_reg(priv, 0x0f, bw_params[i][0]);
- if (ret)
- goto error;
-
- buf[0] = 0x02;
- buf[1] = bw_params[i][1];
- buf[2] = 0x03; /* default value */
- buf[3] = DIV_ROUND_CLOSEST(if_khz, 50);
- buf[4] = ((c->frequency / 1000) >> 16) & 0xff;
- buf[5] = ((c->frequency / 1000) >> 8) & 0xff;
- buf[6] = ((c->frequency / 1000) >> 0) & 0xff;
- buf[7] = 0xc1;
- buf[8] = 0x01;
- ret = tda18212_wr_regs(priv, 0x12, buf, sizeof(buf));
- if (ret)
- goto error;
-
- /* actual IF rounded as it is on register */
- priv->if_frequency = buf[3] * 50 * 1000;
-
-exit:
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
-
- return ret;
-
-error:
- dbg("failed:%d\n", ret);
- goto exit;
-}
-
-static int tda18212_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct tda18212_priv *priv = fe->tuner_priv;
-
- *frequency = priv->if_frequency;
-
- return 0;
-}
-
-static int tda18212_release(struct dvb_frontend *fe)
-{
- kfree(fe->tuner_priv);
- fe->tuner_priv = NULL;
- return 0;
-}
-
-static const struct dvb_tuner_ops tda18212_tuner_ops = {
- .info = {
- .name = "NXP TDA18212",
-
- .frequency_min = 48000000,
- .frequency_max = 864000000,
- .frequency_step = 1000,
- },
-
- .release = tda18212_release,
-
- .set_params = tda18212_set_params,
- .get_if_frequency = tda18212_get_if_frequency,
-};
-
-struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct tda18212_config *cfg)
-{
- struct tda18212_priv *priv = NULL;
- int ret;
- u8 val;
-
- priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
-
- priv->cfg = cfg;
- priv->i2c = i2c;
- fe->tuner_priv = priv;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
-
- /* check if the tuner is there */
- ret = tda18212_rd_reg(priv, 0x00, &val);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
-
- dbg("ret:%d chip ID:%02x\n", ret, val);
- if (ret || val != 0xc7) {
- kfree(priv);
- return NULL;
- }
-
- pr_info("NXP TDA18212HN successfully identified\n");
-
- memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- return fe;
-}
-EXPORT_SYMBOL(tda18212_attach);
-
-MODULE_DESCRIPTION("NXP TDA18212HN silicon tuner driver");
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tda18212.h b/drivers/media/common/tuners/tda18212.h
deleted file mode 100644
index 9bd5da4aabb7..000000000000
--- a/drivers/media/common/tuners/tda18212.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * NXP TDA18212HN silicon tuner driver
- *
- * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef TDA18212_H
-#define TDA18212_H
-
-#include "dvb_frontend.h"
-
-struct tda18212_config {
- u8 i2c_address;
-
- u16 if_dvbt_6;
- u16 if_dvbt_7;
- u16 if_dvbt_8;
- u16 if_dvbt2_5;
- u16 if_dvbt2_6;
- u16 if_dvbt2_7;
- u16 if_dvbt2_8;
- u16 if_dvbc;
-};
-
-#if defined(CONFIG_MEDIA_TUNER_TDA18212) || \
- (defined(CONFIG_MEDIA_TUNER_TDA18212_MODULE) && defined(MODULE))
-extern struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct tda18212_config *cfg);
-#else
-static inline struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct tda18212_config *cfg)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif
diff --git a/drivers/media/common/tuners/tda18218.h b/drivers/media/common/tuners/tda18218.h
deleted file mode 100644
index b4180d180029..000000000000
--- a/drivers/media/common/tuners/tda18218.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * NXP TDA18218HN silicon tuner driver
- *
- * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef TDA18218_H
-#define TDA18218_H
-
-#include "dvb_frontend.h"
-
-struct tda18218_config {
- u8 i2c_address;
- u8 i2c_wr_max;
- u8 loop_through:1;
-};
-
-#if defined(CONFIG_MEDIA_TUNER_TDA18218) || \
- (defined(CONFIG_MEDIA_TUNER_TDA18218_MODULE) && defined(MODULE))
-extern struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct tda18218_config *cfg);
-#else
-static inline struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct tda18218_config *cfg)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif
diff --git a/drivers/media/common/tuners/tda18218_priv.h b/drivers/media/common/tuners/tda18218_priv.h
deleted file mode 100644
index dc52b72e1407..000000000000
--- a/drivers/media/common/tuners/tda18218_priv.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * NXP TDA18218HN silicon tuner driver
- *
- * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef TDA18218_PRIV_H
-#define TDA18218_PRIV_H
-
-#define LOG_PREFIX "tda18218"
-
-#undef dbg
-#define dbg(f, arg...) \
- if (debug) \
- printk(KERN_DEBUG LOG_PREFIX": " f "\n" , ## arg)
-#undef err
-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
-#undef info
-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef warn
-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
-
-#define R00_ID 0x00 /* ID byte */
-#define R01_R1 0x01 /* Read byte 1 */
-#define R02_R2 0x02 /* Read byte 2 */
-#define R03_R3 0x03 /* Read byte 3 */
-#define R04_R4 0x04 /* Read byte 4 */
-#define R05_R5 0x05 /* Read byte 5 */
-#define R06_R6 0x06 /* Read byte 6 */
-#define R07_MD1 0x07 /* Main divider byte 1 */
-#define R08_PSM1 0x08 /* PSM byte 1 */
-#define R09_MD2 0x09 /* Main divider byte 2 */
-#define R0A_MD3 0x0a /* Main divider byte 1 */
-#define R0B_MD4 0x0b /* Main divider byte 4 */
-#define R0C_MD5 0x0c /* Main divider byte 5 */
-#define R0D_MD6 0x0d /* Main divider byte 6 */
-#define R0E_MD7 0x0e /* Main divider byte 7 */
-#define R0F_MD8 0x0f /* Main divider byte 8 */
-#define R10_CD1 0x10 /* Call divider byte 1 */
-#define R11_CD2 0x11 /* Call divider byte 2 */
-#define R12_CD3 0x12 /* Call divider byte 3 */
-#define R13_CD4 0x13 /* Call divider byte 4 */
-#define R14_CD5 0x14 /* Call divider byte 5 */
-#define R15_CD6 0x15 /* Call divider byte 6 */
-#define R16_CD7 0x16 /* Call divider byte 7 */
-#define R17_PD1 0x17 /* Power-down byte 1 */
-#define R18_PD2 0x18 /* Power-down byte 2 */
-#define R19_XTOUT 0x19 /* XTOUT byte */
-#define R1A_IF1 0x1a /* IF byte 1 */
-#define R1B_IF2 0x1b /* IF byte 2 */
-#define R1C_AGC2B 0x1c /* AGC2b byte */
-#define R1D_PSM2 0x1d /* PSM byte 2 */
-#define R1E_PSM3 0x1e /* PSM byte 3 */
-#define R1F_PSM4 0x1f /* PSM byte 4 */
-#define R20_AGC11 0x20 /* AGC1 byte 1 */
-#define R21_AGC12 0x21 /* AGC1 byte 2 */
-#define R22_AGC13 0x22 /* AGC1 byte 3 */
-#define R23_AGC21 0x23 /* AGC2 byte 1 */
-#define R24_AGC22 0x24 /* AGC2 byte 2 */
-#define R25_AAGC 0x25 /* Analog AGC byte */
-#define R26_RC 0x26 /* RC byte */
-#define R27_RSSI 0x27 /* RSSI byte */
-#define R28_IRCAL1 0x28 /* IR CAL byte 1 */
-#define R29_IRCAL2 0x29 /* IR CAL byte 2 */
-#define R2A_IRCAL3 0x2a /* IR CAL byte 3 */
-#define R2B_IRCAL4 0x2b /* IR CAL byte 4 */
-#define R2C_RFCAL1 0x2c /* RF CAL byte 1 */
-#define R2D_RFCAL2 0x2d /* RF CAL byte 2 */
-#define R2E_RFCAL3 0x2e /* RF CAL byte 3 */
-#define R2F_RFCAL4 0x2f /* RF CAL byte 4 */
-#define R30_RFCAL5 0x30 /* RF CAL byte 5 */
-#define R31_RFCAL6 0x31 /* RF CAL byte 6 */
-#define R32_RFCAL7 0x32 /* RF CAL byte 7 */
-#define R33_RFCAL8 0x33 /* RF CAL byte 8 */
-#define R34_RFCAL9 0x34 /* RF CAL byte 9 */
-#define R35_RFCAL10 0x35 /* RF CAL byte 10 */
-#define R36_RFCALRAM1 0x36 /* RF CAL RAM byte 1 */
-#define R37_RFCALRAM2 0x37 /* RF CAL RAM byte 2 */
-#define R38_MARGIN 0x38 /* Margin byte */
-#define R39_FMAX1 0x39 /* Fmax byte 1 */
-#define R3A_FMAX2 0x3a /* Fmax byte 2 */
-
-#define TDA18218_NUM_REGS 59
-
-struct tda18218_priv {
- struct tda18218_config *cfg;
- struct i2c_adapter *i2c;
-
- u32 if_frequency;
-
- u8 regs[TDA18218_NUM_REGS];
-};
-
-#endif
diff --git a/drivers/media/common/tuners/tda18271.h b/drivers/media/common/tuners/tda18271.h
deleted file mode 100644
index 640bae4e6a5a..000000000000
--- a/drivers/media/common/tuners/tda18271.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- tda18271.h - header for the Philips / NXP TDA18271 silicon tuner
-
- Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __TDA18271_H__
-#define __TDA18271_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-struct tda18271_std_map_item {
- u16 if_freq;
-
- /* EP3[4:3] */
- unsigned int agc_mode:2;
- /* EP3[2:0] */
- unsigned int std:3;
- /* EP4[7] */
- unsigned int fm_rfn:1;
- /* EP4[4:2] */
- unsigned int if_lvl:3;
- /* EB22[6:0] */
- unsigned int rfagc_top:7;
-};
-
-struct tda18271_std_map {
- struct tda18271_std_map_item fm_radio;
- struct tda18271_std_map_item atv_b;
- struct tda18271_std_map_item atv_dk;
- struct tda18271_std_map_item atv_gh;
- struct tda18271_std_map_item atv_i;
- struct tda18271_std_map_item atv_l;
- struct tda18271_std_map_item atv_lc;
- struct tda18271_std_map_item atv_mn;
- struct tda18271_std_map_item atsc_6;
- struct tda18271_std_map_item dvbt_6;
- struct tda18271_std_map_item dvbt_7;
- struct tda18271_std_map_item dvbt_8;
- struct tda18271_std_map_item qam_6;
- struct tda18271_std_map_item qam_7;
- struct tda18271_std_map_item qam_8;
-};
-
-enum tda18271_role {
- TDA18271_MASTER = 0,
- TDA18271_SLAVE,
-};
-
-enum tda18271_i2c_gate {
- TDA18271_GATE_AUTO = 0,
- TDA18271_GATE_ANALOG,
- TDA18271_GATE_DIGITAL,
-};
-
-enum tda18271_output_options {
- /* slave tuner output & loop thru & xtal oscillator always on */
- TDA18271_OUTPUT_LT_XT_ON = 0,
-
- /* slave tuner output loop thru off */
- TDA18271_OUTPUT_LT_OFF = 1,
-
- /* xtal oscillator off */
- TDA18271_OUTPUT_XT_OFF = 2,
-};
-
-enum tda18271_small_i2c {
- TDA18271_39_BYTE_CHUNK_INIT = 0,
- TDA18271_16_BYTE_CHUNK_INIT = 16,
- TDA18271_08_BYTE_CHUNK_INIT = 8,
- TDA18271_03_BYTE_CHUNK_INIT = 3,
-};
-
-struct tda18271_config {
- /* override default if freq / std settings (optional) */
- struct tda18271_std_map *std_map;
-
- /* master / slave tuner: master uses main pll, slave uses cal pll */
- enum tda18271_role role;
-
- /* use i2c gate provided by analog or digital demod */
- enum tda18271_i2c_gate gate;
-
- /* output options that can be disabled */
- enum tda18271_output_options output_opt;
-
- /* some i2c providers can't write all 39 registers at once */
- enum tda18271_small_i2c small_i2c;
-
- /* force rf tracking filter calibration on startup */
- unsigned int rf_cal_on_startup:1;
-
- /* interface to saa713x / tda829x */
- unsigned int config;
-};
-
-#define TDA18271_CALLBACK_CMD_AGC_ENABLE 0
-
-enum tda18271_mode {
- TDA18271_ANALOG = 0,
- TDA18271_DIGITAL,
-};
-
-#if defined(CONFIG_MEDIA_TUNER_TDA18271) || (defined(CONFIG_MEDIA_TUNER_TDA18271_MODULE) && defined(MODULE))
-extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
- struct i2c_adapter *i2c,
- struct tda18271_config *cfg);
-#else
-static inline struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe,
- u8 addr,
- struct i2c_adapter *i2c,
- struct tda18271_config *cfg)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* __TDA18271_H__ */
diff --git a/drivers/media/common/tuners/tda827x.h b/drivers/media/common/tuners/tda827x.h
deleted file mode 100644
index 7d72ce0a0c2d..000000000000
--- a/drivers/media/common/tuners/tda827x.h
+++ /dev/null
@@ -1,68 +0,0 @@
- /*
- DVB Driver for Philips tda827x / tda827xa Silicon tuners
-
- (c) 2005 Hartmut Hackmann
- (c) 2007 Michael Krufky
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- */
-
-#ifndef __DVB_TDA827X_H__
-#define __DVB_TDA827X_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-struct tda827x_config
-{
- /* saa7134 - provided callbacks */
- int (*init) (struct dvb_frontend *fe);
- int (*sleep) (struct dvb_frontend *fe);
-
- /* interface to tda829x driver */
- unsigned int config;
- int switch_addr;
-
- void (*agcf)(struct dvb_frontend *fe);
-};
-
-
-/**
- * Attach a tda827x tuner to the supplied frontend structure.
- *
- * @param fe Frontend to attach to.
- * @param addr i2c address of the tuner.
- * @param i2c i2c adapter to use.
- * @param cfg optional callback function pointers.
- * @return FE pointer on success, NULL on failure.
- */
-#if defined(CONFIG_MEDIA_TUNER_TDA827X) || (defined(CONFIG_MEDIA_TUNER_TDA827X_MODULE) && defined(MODULE))
-extern struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, int addr,
- struct i2c_adapter *i2c,
- struct tda827x_config *cfg);
-#else
-static inline struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe,
- int addr,
- struct i2c_adapter *i2c,
- struct tda827x_config *cfg)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif // CONFIG_MEDIA_TUNER_TDA827X
-
-#endif // __DVB_TDA827X_H__
diff --git a/drivers/media/common/tuners/tda8290.h b/drivers/media/common/tuners/tda8290.h
deleted file mode 100644
index 7e288b26fcc3..000000000000
--- a/drivers/media/common/tuners/tda8290.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __TDA8290_H__
-#define __TDA8290_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-struct tda829x_config {
- unsigned int lna_cfg;
-
- unsigned int probe_tuner:1;
-#define TDA829X_PROBE_TUNER 0
-#define TDA829X_DONT_PROBE 1
-};
-
-#if defined(CONFIG_MEDIA_TUNER_TDA8290) || (defined(CONFIG_MEDIA_TUNER_TDA8290_MODULE) && defined(MODULE))
-extern int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr);
-
-extern struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr,
- struct tda829x_config *cfg);
-#else
-static inline int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return -EINVAL;
-}
-
-static inline struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr,
- struct tda829x_config *cfg)
-{
- printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
- __func__);
- return NULL;
-}
-#endif
-
-#endif /* __TDA8290_H__ */
diff --git a/drivers/media/common/tuners/tda9887.h b/drivers/media/common/tuners/tda9887.h
deleted file mode 100644
index acc419e8c4fc..000000000000
--- a/drivers/media/common/tuners/tda9887.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __TDA9887_H__
-#define __TDA9887_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-/* ------------------------------------------------------------------------ */
-#if defined(CONFIG_MEDIA_TUNER_TDA9887) || (defined(CONFIG_MEDIA_TUNER_TDA9887_MODULE) && defined(MODULE))
-extern struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr);
-#else
-static inline struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* __TDA9887_H__ */
diff --git a/drivers/media/common/tuners/tea5761.h b/drivers/media/common/tuners/tea5761.h
deleted file mode 100644
index 2e2ff82c95a4..000000000000
--- a/drivers/media/common/tuners/tea5761.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __TEA5761_H__
-#define __TEA5761_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE))
-extern int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);
-
-extern struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr);
-#else
-static inline int tea5761_autodetection(struct i2c_adapter* i2c_adap,
- u8 i2c_addr)
-{
- printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
- __func__);
- return -EINVAL;
-}
-
-static inline struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* __TEA5761_H__ */
diff --git a/drivers/media/common/tuners/tea5767.h b/drivers/media/common/tuners/tea5767.h
deleted file mode 100644
index d30ab1b483de..000000000000
--- a/drivers/media/common/tuners/tea5767.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __TEA5767_H__
-#define __TEA5767_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-enum tea5767_xtal {
- TEA5767_LOW_LO_32768 = 0,
- TEA5767_HIGH_LO_32768 = 1,
- TEA5767_LOW_LO_13MHz = 2,
- TEA5767_HIGH_LO_13MHz = 3,
-};
-
-struct tea5767_ctrl {
- unsigned int port1:1;
- unsigned int port2:1;
- unsigned int high_cut:1;
- unsigned int st_noise:1;
- unsigned int soft_mute:1;
- unsigned int japan_band:1;
- unsigned int deemph_75:1;
- unsigned int pllref:1;
- enum tea5767_xtal xtal_freq;
-};
-
-#if defined(CONFIG_MEDIA_TUNER_TEA5767) || (defined(CONFIG_MEDIA_TUNER_TEA5767_MODULE) && defined(MODULE))
-extern int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);
-
-extern struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr);
-#else
-static inline int tea5767_autodetection(struct i2c_adapter* i2c_adap,
- u8 i2c_addr)
-{
- printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
- __func__);
- return -EINVAL;
-}
-
-static inline struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* __TEA5767_H__ */
diff --git a/drivers/media/common/tuners/tua9001.c b/drivers/media/common/tuners/tua9001.c
deleted file mode 100644
index de2607084672..000000000000
--- a/drivers/media/common/tuners/tua9001.c
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Infineon TUA 9001 silicon tuner driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "tua9001.h"
-#include "tua9001_priv.h"
-
-/* write register */
-static int tua9001_wr_reg(struct tua9001_priv *priv, u8 reg, u16 val)
-{
- int ret;
- u8 buf[3] = { reg, (val >> 8) & 0xff, (val >> 0) & 0xff };
- struct i2c_msg msg[1] = {
- {
- .addr = priv->cfg->i2c_addr,
- .flags = 0,
- .len = sizeof(buf),
- .buf = buf,
- }
- };
-
- ret = i2c_transfer(priv->i2c, msg, 1);
- if (ret == 1) {
- ret = 0;
- } else {
- printk(KERN_WARNING "%s: I2C wr failed=%d reg=%02x\n",
- __func__, ret, reg);
- ret = -EREMOTEIO;
- }
-
- return ret;
-}
-
-static int tua9001_release(struct dvb_frontend *fe)
-{
- kfree(fe->tuner_priv);
- fe->tuner_priv = NULL;
-
- return 0;
-}
-
-static int tua9001_init(struct dvb_frontend *fe)
-{
- struct tua9001_priv *priv = fe->tuner_priv;
- int ret = 0;
- u8 i;
- struct reg_val data[] = {
- { 0x1e, 0x6512 },
- { 0x25, 0xb888 },
- { 0x39, 0x5460 },
- { 0x3b, 0x00c0 },
- { 0x3a, 0xf000 },
- { 0x08, 0x0000 },
- { 0x32, 0x0030 },
- { 0x41, 0x703a },
- { 0x40, 0x1c78 },
- { 0x2c, 0x1c00 },
- { 0x36, 0xc013 },
- { 0x37, 0x6f18 },
- { 0x27, 0x0008 },
- { 0x2a, 0x0001 },
- { 0x34, 0x0a40 },
- };
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */
-
- for (i = 0; i < ARRAY_SIZE(data); i++) {
- ret = tua9001_wr_reg(priv, data[i].reg, data[i].val);
- if (ret)
- break;
- }
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */
-
- if (ret < 0)
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int tua9001_set_params(struct dvb_frontend *fe)
-{
- struct tua9001_priv *priv = fe->tuner_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i;
- u16 val;
- u32 frequency;
- struct reg_val data[2];
-
- pr_debug("%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n",
- __func__, c->delivery_system, c->frequency,
- c->bandwidth_hz);
-
- switch (c->delivery_system) {
- case SYS_DVBT:
- switch (c->bandwidth_hz) {
- case 8000000:
- val = 0x0000;
- break;
- case 7000000:
- val = 0x1000;
- break;
- case 6000000:
- val = 0x2000;
- break;
- case 5000000:
- val = 0x3000;
- break;
- default:
- ret = -EINVAL;
- goto err;
- }
- break;
- default:
- ret = -EINVAL;
- goto err;
- }
-
- data[0].reg = 0x04;
- data[0].val = val;
-
- frequency = (c->frequency - 150000000);
- frequency /= 100;
- frequency *= 48;
- frequency /= 10000;
-
- data[1].reg = 0x1f;
- data[1].val = frequency;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */
-
- for (i = 0; i < ARRAY_SIZE(data); i++) {
- ret = tua9001_wr_reg(priv, data[i].reg, data[i].val);
- if (ret < 0)
- break;
- }
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */
-
-err:
- if (ret < 0)
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- *frequency = 0; /* Zero-IF */
-
- return 0;
-}
-
-static const struct dvb_tuner_ops tua9001_tuner_ops = {
- .info = {
- .name = "Infineon TUA 9001",
-
- .frequency_min = 170000000,
- .frequency_max = 862000000,
- .frequency_step = 0,
- },
-
- .release = tua9001_release,
-
- .init = tua9001_init,
- .set_params = tua9001_set_params,
-
- .get_if_frequency = tua9001_get_if_frequency,
-};
-
-struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct tua9001_config *cfg)
-{
- struct tua9001_priv *priv = NULL;
-
- priv = kzalloc(sizeof(struct tua9001_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
-
- priv->cfg = cfg;
- priv->i2c = i2c;
-
- printk(KERN_INFO "Infineon TUA 9001 successfully attached.");
-
- memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- fe->tuner_priv = priv;
- return fe;
-}
-EXPORT_SYMBOL(tua9001_attach);
-
-MODULE_DESCRIPTION("Infineon TUA 9001 silicon tuner driver");
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tua9001.h b/drivers/media/common/tuners/tua9001.h
deleted file mode 100644
index 38d6ae76b1d6..000000000000
--- a/drivers/media/common/tuners/tua9001.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Infineon TUA 9001 silicon tuner driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef TUA9001_H
-#define TUA9001_H
-
-#include "dvb_frontend.h"
-
-struct tua9001_config {
- /*
- * I2C address
- */
- u8 i2c_addr;
-};
-
-#if defined(CONFIG_MEDIA_TUNER_TUA9001) || \
- (defined(CONFIG_MEDIA_TUNER_TUA9001_MODULE) && defined(MODULE))
-extern struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct tua9001_config *cfg);
-#else
-static inline struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct tua9001_config *cfg)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif
diff --git a/drivers/media/common/tuners/tua9001_priv.h b/drivers/media/common/tuners/tua9001_priv.h
deleted file mode 100644
index 73cc1ce0575c..000000000000
--- a/drivers/media/common/tuners/tua9001_priv.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Infineon TUA 9001 silicon tuner driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef TUA9001_PRIV_H
-#define TUA9001_PRIV_H
-
-struct reg_val {
- u8 reg;
- u16 val;
-};
-
-struct tua9001_priv {
- struct tua9001_config *cfg;
- struct i2c_adapter *i2c;
-};
-
-#endif
diff --git a/drivers/media/common/tuners/tuner-simple.h b/drivers/media/common/tuners/tuner-simple.h
deleted file mode 100644
index 381fa5d35a9b..000000000000
--- a/drivers/media/common/tuners/tuner-simple.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __TUNER_SIMPLE_H__
-#define __TUNER_SIMPLE_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-#if defined(CONFIG_MEDIA_TUNER_SIMPLE) || (defined(CONFIG_MEDIA_TUNER_SIMPLE_MODULE) && defined(MODULE))
-extern struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr,
- unsigned int type);
-#else
-static inline struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr,
- unsigned int type)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* __TUNER_SIMPLE_H__ */
diff --git a/drivers/media/common/tuners/tuner-xc2028-types.h b/drivers/media/common/tuners/tuner-xc2028-types.h
deleted file mode 100644
index 74dc46a71f64..000000000000
--- a/drivers/media/common/tuners/tuner-xc2028-types.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/* tuner-xc2028_types
- *
- * This file includes internal tipes to be used inside tuner-xc2028.
- * Shouldn't be included outside tuner-xc2028
- *
- * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org)
- * This code is placed under the terms of the GNU General Public License v2
- */
-
-/* xc3028 firmware types */
-
-/* BASE firmware should be loaded before any other firmware */
-#define BASE (1<<0)
-#define BASE_TYPES (BASE|F8MHZ|MTS|FM|INPUT1|INPUT2|INIT1)
-
-/* F8MHZ marks BASE firmwares for 8 MHz Bandwidth */
-#define F8MHZ (1<<1)
-
-/* Multichannel Television Sound (MTS)
- Those firmwares are capable of using xc2038 DSP to decode audio and
- produce a baseband audio output on some pins of the chip.
- There are MTS firmwares for the most used video standards. It should be
- required to use MTS firmwares, depending on the way audio is routed into
- the bridge chip
- */
-#define MTS (1<<2)
-
-/* FIXME: I have no idea what's the difference between
- D2620 and D2633 firmwares
- */
-#define D2620 (1<<3)
-#define D2633 (1<<4)
-
-/* DTV firmwares for 6, 7 and 8 MHz
- DTV6 - 6MHz - ATSC/DVB-C/DVB-T/ISDB-T/DOCSIS
- DTV8 - 8MHz - DVB-C/DVB-T
- */
-#define DTV6 (1 << 5)
-#define QAM (1 << 6)
-#define DTV7 (1<<7)
-#define DTV78 (1<<8)
-#define DTV8 (1<<9)
-
-#define DTV_TYPES (D2620|D2633|DTV6|QAM|DTV7|DTV78|DTV8|ATSC)
-
-/* There's a FM | BASE firmware + FM specific firmware (std=0) */
-#define FM (1<<10)
-
-#define STD_SPECIFIC_TYPES (MTS|FM|LCD|NOGD)
-
-/* Applies only for FM firmware
- Makes it use RF input 1 (pin #2) instead of input 2 (pin #4)
- */
-#define INPUT1 (1<<11)
-
-
-/* LCD firmwares exist only for MTS STD/MN (PAL or NTSC/M)
- and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr)
- There are variants both with and without NOGD
- Those firmwares produce better result with LCD displays
- */
-#define LCD (1<<12)
-
-/* NOGD firmwares exist only for MTS STD/MN (PAL or NTSC/M)
- and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr)
- The NOGD firmwares don't have group delay compensation filter
- */
-#define NOGD (1<<13)
-
-/* Old firmwares were broken into init0 and init1 */
-#define INIT1 (1<<14)
-
-/* SCODE firmware selects particular behaviours */
-#define MONO (1 << 15)
-#define ATSC (1 << 16)
-#define IF (1 << 17)
-#define LG60 (1 << 18)
-#define ATI638 (1 << 19)
-#define OREN538 (1 << 20)
-#define OREN36 (1 << 21)
-#define TOYOTA388 (1 << 22)
-#define TOYOTA794 (1 << 23)
-#define DIBCOM52 (1 << 24)
-#define ZARLINK456 (1 << 25)
-#define CHINA (1 << 26)
-#define F6MHZ (1 << 27)
-#define INPUT2 (1 << 28)
-#define SCODE (1 << 29)
-
-/* This flag identifies that the scode table has a new format */
-#define HAS_IF (1 << 30)
-
-/* There are different scode tables for MTS and non-MTS.
- The MTS firmwares support mono only
- */
-#define SCODE_TYPES (SCODE | MTS)
-
-
-/* Newer types not defined on videodev2.h.
- The original idea were to move all those types to videodev2.h, but
- it seemed overkill, since, with the exception of SECAM/K3, the other
- types seem to be autodetected.
- It is not clear where secam/k3 is used, nor we have a feedback of this
- working or being autodetected by the standard secam firmware.
- */
-
-#define V4L2_STD_SECAM_K3 (0x04000000)
-
-/* Audio types */
-
-#define V4L2_STD_A2_A (1LL<<32)
-#define V4L2_STD_A2_B (1LL<<33)
-#define V4L2_STD_NICAM_A (1LL<<34)
-#define V4L2_STD_NICAM_B (1LL<<35)
-#define V4L2_STD_AM (1LL<<36)
-#define V4L2_STD_BTSC (1LL<<37)
-#define V4L2_STD_EIAJ (1LL<<38)
-
-#define V4L2_STD_A2 (V4L2_STD_A2_A | V4L2_STD_A2_B)
-#define V4L2_STD_NICAM (V4L2_STD_NICAM_A | V4L2_STD_NICAM_B)
-
-/* To preserve backward compatibilty,
- (std & V4L2_STD_AUDIO) = 0 means that ALL audio stds are supported
- */
-
-#define V4L2_STD_AUDIO (V4L2_STD_A2 | \
- V4L2_STD_NICAM | \
- V4L2_STD_AM | \
- V4L2_STD_BTSC | \
- V4L2_STD_EIAJ)
-
-/* Used standards with audio restrictions */
-
-#define V4L2_STD_PAL_BG_A2_A (V4L2_STD_PAL_BG | V4L2_STD_A2_A)
-#define V4L2_STD_PAL_BG_A2_B (V4L2_STD_PAL_BG | V4L2_STD_A2_B)
-#define V4L2_STD_PAL_BG_NICAM_A (V4L2_STD_PAL_BG | V4L2_STD_NICAM_A)
-#define V4L2_STD_PAL_BG_NICAM_B (V4L2_STD_PAL_BG | V4L2_STD_NICAM_B)
-#define V4L2_STD_PAL_DK_A2 (V4L2_STD_PAL_DK | V4L2_STD_A2)
-#define V4L2_STD_PAL_DK_NICAM (V4L2_STD_PAL_DK | V4L2_STD_NICAM)
-#define V4L2_STD_SECAM_L_NICAM (V4L2_STD_SECAM_L | V4L2_STD_NICAM)
-#define V4L2_STD_SECAM_L_AM (V4L2_STD_SECAM_L | V4L2_STD_AM)
diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
deleted file mode 100644
index b5ee3ebfcfca..000000000000
--- a/drivers/media/common/tuners/tuner-xc2028.c
+++ /dev/null
@@ -1,1377 +0,0 @@
-/* tuner-xc2028
- *
- * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org)
- *
- * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com)
- * - frontend interface
- *
- * This code is placed under the terms of the GNU General Public License v2
- */
-
-#include <linux/i2c.h>
-#include <asm/div64.h>
-#include <linux/firmware.h>
-#include <linux/videodev2.h>
-#include <linux/delay.h>
-#include <media/tuner.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <asm/unaligned.h>
-#include "tuner-i2c.h"
-#include "tuner-xc2028.h"
-#include "tuner-xc2028-types.h"
-
-#include <linux/dvb/frontend.h>
-#include "dvb_frontend.h"
-
-/* Registers (Write-only) */
-#define XREG_INIT 0x00
-#define XREG_RF_FREQ 0x02
-#define XREG_POWER_DOWN 0x08
-
-/* Registers (Read-only) */
-#define XREG_FREQ_ERROR 0x01
-#define XREG_LOCK 0x02
-#define XREG_VERSION 0x04
-#define XREG_PRODUCT_ID 0x08
-#define XREG_HSYNC_FREQ 0x10
-#define XREG_FRAME_LINES 0x20
-#define XREG_SNR 0x40
-
-#define XREG_ADC_ENV 0x0100
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable verbose debug messages");
-
-static int no_poweroff;
-module_param(no_poweroff, int, 0644);
-MODULE_PARM_DESC(no_poweroff, "0 (default) powers device off when not used.\n"
- "1 keep device energized and with tuner ready all the times.\n"
- " Faster, but consumes more power and keeps the device hotter\n");
-
-static char audio_std[8];
-module_param_string(audio_std, audio_std, sizeof(audio_std), 0);
-MODULE_PARM_DESC(audio_std,
- "Audio standard. XC3028 audio decoder explicitly "
- "needs to know what audio\n"
- "standard is needed for some video standards with audio A2 or NICAM.\n"
- "The valid values are:\n"
- "A2\n"
- "A2/A\n"
- "A2/B\n"
- "NICAM\n"
- "NICAM/A\n"
- "NICAM/B\n");
-
-static char firmware_name[30];
-module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);
-MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the "
- "default firmware name\n");
-
-static LIST_HEAD(hybrid_tuner_instance_list);
-static DEFINE_MUTEX(xc2028_list_mutex);
-
-/* struct for storing firmware table */
-struct firmware_description {
- unsigned int type;
- v4l2_std_id id;
- __u16 int_freq;
- unsigned char *ptr;
- unsigned int size;
-};
-
-struct firmware_properties {
- unsigned int type;
- v4l2_std_id id;
- v4l2_std_id std_req;
- __u16 int_freq;
- unsigned int scode_table;
- int scode_nr;
-};
-
-struct xc2028_data {
- struct list_head hybrid_tuner_instance_list;
- struct tuner_i2c_props i2c_props;
- __u32 frequency;
-
- struct firmware_description *firm;
- int firm_size;
- __u16 firm_version;
-
- __u16 hwmodel;
- __u16 hwvers;
-
- struct xc2028_ctrl ctrl;
-
- struct firmware_properties cur_fw;
-
- struct mutex lock;
-};
-
-#define i2c_send(priv, buf, size) ({ \
- int _rc; \
- _rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size); \
- if (size != _rc) \
- tuner_info("i2c output error: rc = %d (should be %d)\n",\
- _rc, (int)size); \
- if (priv->ctrl.msleep) \
- msleep(priv->ctrl.msleep); \
- _rc; \
-})
-
-#define i2c_rcv(priv, buf, size) ({ \
- int _rc; \
- _rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size); \
- if (size != _rc) \
- tuner_err("i2c input error: rc = %d (should be %d)\n", \
- _rc, (int)size); \
- _rc; \
-})
-
-#define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({ \
- int _rc; \
- _rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize, \
- ibuf, isize); \
- if (isize != _rc) \
- tuner_err("i2c input error: rc = %d (should be %d)\n", \
- _rc, (int)isize); \
- if (priv->ctrl.msleep) \
- msleep(priv->ctrl.msleep); \
- _rc; \
-})
-
-#define send_seq(priv, data...) ({ \
- static u8 _val[] = data; \
- int _rc; \
- if (sizeof(_val) != \
- (_rc = tuner_i2c_xfer_send(&priv->i2c_props, \
- _val, sizeof(_val)))) { \
- tuner_err("Error on line %d: %d\n", __LINE__, _rc); \
- } else if (priv->ctrl.msleep) \
- msleep(priv->ctrl.msleep); \
- _rc; \
-})
-
-static int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val)
-{
- unsigned char buf[2];
- unsigned char ibuf[2];
-
- tuner_dbg("%s %04x called\n", __func__, reg);
-
- buf[0] = reg >> 8;
- buf[1] = (unsigned char) reg;
-
- if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2)
- return -EIO;
-
- *val = (ibuf[1]) | (ibuf[0] << 8);
- return 0;
-}
-
-#define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0)
-static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
-{
- if (type & BASE)
- printk("BASE ");
- if (type & INIT1)
- printk("INIT1 ");
- if (type & F8MHZ)
- printk("F8MHZ ");
- if (type & MTS)
- printk("MTS ");
- if (type & D2620)
- printk("D2620 ");
- if (type & D2633)
- printk("D2633 ");
- if (type & DTV6)
- printk("DTV6 ");
- if (type & QAM)
- printk("QAM ");
- if (type & DTV7)
- printk("DTV7 ");
- if (type & DTV78)
- printk("DTV78 ");
- if (type & DTV8)
- printk("DTV8 ");
- if (type & FM)
- printk("FM ");
- if (type & INPUT1)
- printk("INPUT1 ");
- if (type & LCD)
- printk("LCD ");
- if (type & NOGD)
- printk("NOGD ");
- if (type & MONO)
- printk("MONO ");
- if (type & ATSC)
- printk("ATSC ");
- if (type & IF)
- printk("IF ");
- if (type & LG60)
- printk("LG60 ");
- if (type & ATI638)
- printk("ATI638 ");
- if (type & OREN538)
- printk("OREN538 ");
- if (type & OREN36)
- printk("OREN36 ");
- if (type & TOYOTA388)
- printk("TOYOTA388 ");
- if (type & TOYOTA794)
- printk("TOYOTA794 ");
- if (type & DIBCOM52)
- printk("DIBCOM52 ");
- if (type & ZARLINK456)
- printk("ZARLINK456 ");
- if (type & CHINA)
- printk("CHINA ");
- if (type & F6MHZ)
- printk("F6MHZ ");
- if (type & INPUT2)
- printk("INPUT2 ");
- if (type & SCODE)
- printk("SCODE ");
- if (type & HAS_IF)
- printk("HAS_IF_%d ", int_freq);
-}
-
-static v4l2_std_id parse_audio_std_option(void)
-{
- if (strcasecmp(audio_std, "A2") == 0)
- return V4L2_STD_A2;
- if (strcasecmp(audio_std, "A2/A") == 0)
- return V4L2_STD_A2_A;
- if (strcasecmp(audio_std, "A2/B") == 0)
- return V4L2_STD_A2_B;
- if (strcasecmp(audio_std, "NICAM") == 0)
- return V4L2_STD_NICAM;
- if (strcasecmp(audio_std, "NICAM/A") == 0)
- return V4L2_STD_NICAM_A;
- if (strcasecmp(audio_std, "NICAM/B") == 0)
- return V4L2_STD_NICAM_B;
-
- return 0;
-}
-
-static void free_firmware(struct xc2028_data *priv)
-{
- int i;
- tuner_dbg("%s called\n", __func__);
-
- if (!priv->firm)
- return;
-
- for (i = 0; i < priv->firm_size; i++)
- kfree(priv->firm[i].ptr);
-
- kfree(priv->firm);
-
- priv->firm = NULL;
- priv->firm_size = 0;
-
- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
-}
-
-static int load_all_firmwares(struct dvb_frontend *fe)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- const struct firmware *fw = NULL;
- const unsigned char *p, *endp;
- int rc = 0;
- int n, n_array;
- char name[33];
- char *fname;
-
- tuner_dbg("%s called\n", __func__);
-
- if (!firmware_name[0])
- fname = priv->ctrl.fname;
- else
- fname = firmware_name;
-
- tuner_dbg("Reading firmware %s\n", fname);
- rc = request_firmware(&fw, fname, priv->i2c_props.adap->dev.parent);
- if (rc < 0) {
- if (rc == -ENOENT)
- tuner_err("Error: firmware %s not found.\n",
- fname);
- else
- tuner_err("Error %d while requesting firmware %s \n",
- rc, fname);
-
- return rc;
- }
- p = fw->data;
- endp = p + fw->size;
-
- if (fw->size < sizeof(name) - 1 + 2 + 2) {
- tuner_err("Error: firmware file %s has invalid size!\n",
- fname);
- goto corrupt;
- }
-
- memcpy(name, p, sizeof(name) - 1);
- name[sizeof(name) - 1] = 0;
- p += sizeof(name) - 1;
-
- priv->firm_version = get_unaligned_le16(p);
- p += 2;
-
- n_array = get_unaligned_le16(p);
- p += 2;
-
- tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n",
- n_array, fname, name,
- priv->firm_version >> 8, priv->firm_version & 0xff);
-
- priv->firm = kcalloc(n_array, sizeof(*priv->firm), GFP_KERNEL);
- if (priv->firm == NULL) {
- tuner_err("Not enough memory to load firmware file.\n");
- rc = -ENOMEM;
- goto err;
- }
- priv->firm_size = n_array;
-
- n = -1;
- while (p < endp) {
- __u32 type, size;
- v4l2_std_id id;
- __u16 int_freq = 0;
-
- n++;
- if (n >= n_array) {
- tuner_err("More firmware images in file than "
- "were expected!\n");
- goto corrupt;
- }
-
- /* Checks if there's enough bytes to read */
- if (endp - p < sizeof(type) + sizeof(id) + sizeof(size))
- goto header;
-
- type = get_unaligned_le32(p);
- p += sizeof(type);
-
- id = get_unaligned_le64(p);
- p += sizeof(id);
-
- if (type & HAS_IF) {
- int_freq = get_unaligned_le16(p);
- p += sizeof(int_freq);
- if (endp - p < sizeof(size))
- goto header;
- }
-
- size = get_unaligned_le32(p);
- p += sizeof(size);
-
- if (!size || size > endp - p) {
- tuner_err("Firmware type ");
- dump_firm_type(type);
- printk("(%x), id %llx is corrupted "
- "(size=%d, expected %d)\n",
- type, (unsigned long long)id,
- (unsigned)(endp - p), size);
- goto corrupt;
- }
-
- priv->firm[n].ptr = kzalloc(size, GFP_KERNEL);
- if (priv->firm[n].ptr == NULL) {
- tuner_err("Not enough memory to load firmware file.\n");
- rc = -ENOMEM;
- goto err;
- }
- tuner_dbg("Reading firmware type ");
- if (debug) {
- dump_firm_type_and_int_freq(type, int_freq);
- printk("(%x), id %llx, size=%d.\n",
- type, (unsigned long long)id, size);
- }
-
- memcpy(priv->firm[n].ptr, p, size);
- priv->firm[n].type = type;
- priv->firm[n].id = id;
- priv->firm[n].size = size;
- priv->firm[n].int_freq = int_freq;
-
- p += size;
- }
-
- if (n + 1 != priv->firm_size) {
- tuner_err("Firmware file is incomplete!\n");
- goto corrupt;
- }
-
- goto done;
-
-header:
- tuner_err("Firmware header is incomplete!\n");
-corrupt:
- rc = -EINVAL;
- tuner_err("Error: firmware file is corrupted!\n");
-
-err:
- tuner_info("Releasing partially loaded firmware file.\n");
- free_firmware(priv);
-
-done:
- release_firmware(fw);
- if (rc == 0)
- tuner_dbg("Firmware files loaded.\n");
-
- return rc;
-}
-
-static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id *id)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int i, best_i = -1, best_nr_matches = 0;
- unsigned int type_mask = 0;
-
- tuner_dbg("%s called, want type=", __func__);
- if (debug) {
- dump_firm_type(type);
- printk("(%x), id %016llx.\n", type, (unsigned long long)*id);
- }
-
- if (!priv->firm) {
- tuner_err("Error! firmware not loaded\n");
- return -EINVAL;
- }
-
- if (((type & ~SCODE) == 0) && (*id == 0))
- *id = V4L2_STD_PAL;
-
- if (type & BASE)
- type_mask = BASE_TYPES;
- else if (type & SCODE) {
- type &= SCODE_TYPES;
- type_mask = SCODE_TYPES & ~HAS_IF;
- } else if (type & DTV_TYPES)
- type_mask = DTV_TYPES;
- else if (type & STD_SPECIFIC_TYPES)
- type_mask = STD_SPECIFIC_TYPES;
-
- type &= type_mask;
-
- if (!(type & SCODE))
- type_mask = ~0;
-
- /* Seek for exact match */
- for (i = 0; i < priv->firm_size; i++) {
- if ((type == (priv->firm[i].type & type_mask)) &&
- (*id == priv->firm[i].id))
- goto found;
- }
-
- /* Seek for generic video standard match */
- for (i = 0; i < priv->firm_size; i++) {
- v4l2_std_id match_mask;
- int nr_matches;
-
- if (type != (priv->firm[i].type & type_mask))
- continue;
-
- match_mask = *id & priv->firm[i].id;
- if (!match_mask)
- continue;
-
- if ((*id & match_mask) == *id)
- goto found; /* Supports all the requested standards */
-
- nr_matches = hweight64(match_mask);
- if (nr_matches > best_nr_matches) {
- best_nr_matches = nr_matches;
- best_i = i;
- }
- }
-
- if (best_nr_matches > 0) {
- tuner_dbg("Selecting best matching firmware (%d bits) for "
- "type=", best_nr_matches);
- dump_firm_type(type);
- printk("(%x), id %016llx:\n", type, (unsigned long long)*id);
- i = best_i;
- goto found;
- }
-
- /*FIXME: Would make sense to seek for type "hint" match ? */
-
- i = -ENOENT;
- goto ret;
-
-found:
- *id = priv->firm[i].id;
-
-ret:
- tuner_dbg("%s firmware for type=", (i < 0) ? "Can't find" : "Found");
- if (debug) {
- dump_firm_type(type);
- printk("(%x), id %016llx.\n", type, (unsigned long long)*id);
- }
- return i;
-}
-
-static inline int do_tuner_callback(struct dvb_frontend *fe, int cmd, int arg)
-{
- struct xc2028_data *priv = fe->tuner_priv;
-
- /* analog side (tuner-core) uses i2c_adap->algo_data.
- * digital side is not guaranteed to have algo_data defined.
- *
- * digital side will always have fe->dvb defined.
- * analog side (tuner-core) doesn't (yet) define fe->dvb.
- */
-
- return (!fe->callback) ? -EINVAL :
- fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
- fe->dvb->priv : priv->i2c_props.adap->algo_data,
- DVB_FRONTEND_COMPONENT_TUNER, cmd, arg);
-}
-
-static int load_firmware(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id *id)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int pos, rc;
- unsigned char *p, *endp, buf[priv->ctrl.max_len];
-
- tuner_dbg("%s called\n", __func__);
-
- pos = seek_firmware(fe, type, id);
- if (pos < 0)
- return pos;
-
- tuner_info("Loading firmware for type=");
- dump_firm_type(priv->firm[pos].type);
- printk("(%x), id %016llx.\n", priv->firm[pos].type,
- (unsigned long long)*id);
-
- p = priv->firm[pos].ptr;
- endp = p + priv->firm[pos].size;
-
- while (p < endp) {
- __u16 size;
-
- /* Checks if there's enough bytes to read */
- if (p + sizeof(size) > endp) {
- tuner_err("Firmware chunk size is wrong\n");
- return -EINVAL;
- }
-
- size = le16_to_cpu(*(__u16 *) p);
- p += sizeof(size);
-
- if (size == 0xffff)
- return 0;
-
- if (!size) {
- /* Special callback command received */
- rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
- if (rc < 0) {
- tuner_err("Error at RESET code %d\n",
- (*p) & 0x7f);
- return -EINVAL;
- }
- continue;
- }
- if (size >= 0xff00) {
- switch (size) {
- case 0xff00:
- rc = do_tuner_callback(fe, XC2028_RESET_CLK, 0);
- if (rc < 0) {
- tuner_err("Error at RESET code %d\n",
- (*p) & 0x7f);
- return -EINVAL;
- }
- break;
- default:
- tuner_info("Invalid RESET code %d\n",
- size & 0x7f);
- return -EINVAL;
-
- }
- continue;
- }
-
- /* Checks for a sleep command */
- if (size & 0x8000) {
- msleep(size & 0x7fff);
- continue;
- }
-
- if ((size + p > endp)) {
- tuner_err("missing bytes: need %d, have %d\n",
- size, (int)(endp - p));
- return -EINVAL;
- }
-
- buf[0] = *p;
- p++;
- size--;
-
- /* Sends message chunks */
- while (size > 0) {
- int len = (size < priv->ctrl.max_len - 1) ?
- size : priv->ctrl.max_len - 1;
-
- memcpy(buf + 1, p, len);
-
- rc = i2c_send(priv, buf, len + 1);
- if (rc < 0) {
- tuner_err("%d returned from send\n", rc);
- return -EINVAL;
- }
-
- p += len;
- size -= len;
- }
-
- /* silently fail if the frontend doesn't support I2C flush */
- rc = do_tuner_callback(fe, XC2028_I2C_FLUSH, 0);
- if ((rc < 0) && (rc != -EINVAL)) {
- tuner_err("error executing flush: %d\n", rc);
- return rc;
- }
- }
- return 0;
-}
-
-static int load_scode(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id *id, __u16 int_freq, int scode)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int pos, rc;
- unsigned char *p;
-
- tuner_dbg("%s called\n", __func__);
-
- if (!int_freq) {
- pos = seek_firmware(fe, type, id);
- if (pos < 0)
- return pos;
- } else {
- for (pos = 0; pos < priv->firm_size; pos++) {
- if ((priv->firm[pos].int_freq == int_freq) &&
- (priv->firm[pos].type & HAS_IF))
- break;
- }
- if (pos == priv->firm_size)
- return -ENOENT;
- }
-
- p = priv->firm[pos].ptr;
-
- if (priv->firm[pos].type & HAS_IF) {
- if (priv->firm[pos].size != 12 * 16 || scode >= 16)
- return -EINVAL;
- p += 12 * scode;
- } else {
- /* 16 SCODE entries per file; each SCODE entry is 12 bytes and
- * has a 2-byte size header in the firmware format. */
- if (priv->firm[pos].size != 14 * 16 || scode >= 16 ||
- le16_to_cpu(*(__u16 *)(p + 14 * scode)) != 12)
- return -EINVAL;
- p += 14 * scode + 2;
- }
-
- tuner_info("Loading SCODE for type=");
- dump_firm_type_and_int_freq(priv->firm[pos].type,
- priv->firm[pos].int_freq);
- printk("(%x), id %016llx.\n", priv->firm[pos].type,
- (unsigned long long)*id);
-
- if (priv->firm_version < 0x0202)
- rc = send_seq(priv, {0x20, 0x00, 0x00, 0x00});
- else
- rc = send_seq(priv, {0xa0, 0x00, 0x00, 0x00});
- if (rc < 0)
- return -EIO;
-
- rc = i2c_send(priv, p, 12);
- if (rc < 0)
- return -EIO;
-
- rc = send_seq(priv, {0x00, 0x8c});
- if (rc < 0)
- return -EIO;
-
- return 0;
-}
-
-static int check_firmware(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id std, __u16 int_freq)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- struct firmware_properties new_fw;
- int rc = 0, retry_count = 0;
- u16 version, hwmodel;
- v4l2_std_id std0;
-
- tuner_dbg("%s called\n", __func__);
-
- if (!priv->firm) {
- if (!priv->ctrl.fname) {
- tuner_info("xc2028/3028 firmware name not set!\n");
- return -EINVAL;
- }
-
- rc = load_all_firmwares(fe);
- if (rc < 0)
- return rc;
- }
-
- if (priv->ctrl.mts && !(type & FM))
- type |= MTS;
-
-retry:
- new_fw.type = type;
- new_fw.id = std;
- new_fw.std_req = std;
- new_fw.scode_table = SCODE | priv->ctrl.scode_table;
- new_fw.scode_nr = 0;
- new_fw.int_freq = int_freq;
-
- tuner_dbg("checking firmware, user requested type=");
- if (debug) {
- dump_firm_type(new_fw.type);
- printk("(%x), id %016llx, ", new_fw.type,
- (unsigned long long)new_fw.std_req);
- if (!int_freq) {
- printk("scode_tbl ");
- dump_firm_type(priv->ctrl.scode_table);
- printk("(%x), ", priv->ctrl.scode_table);
- } else
- printk("int_freq %d, ", new_fw.int_freq);
- printk("scode_nr %d\n", new_fw.scode_nr);
- }
-
- /* No need to reload base firmware if it matches */
- if (((BASE | new_fw.type) & BASE_TYPES) ==
- (priv->cur_fw.type & BASE_TYPES)) {
- tuner_dbg("BASE firmware not changed.\n");
- goto skip_base;
- }
-
- /* Updating BASE - forget about all currently loaded firmware */
- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
-
- /* Reset is needed before loading firmware */
- rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
- if (rc < 0)
- goto fail;
-
- /* BASE firmwares are all std0 */
- std0 = 0;
- rc = load_firmware(fe, BASE | new_fw.type, &std0);
- if (rc < 0) {
- tuner_err("Error %d while loading base firmware\n",
- rc);
- goto fail;
- }
-
- /* Load INIT1, if needed */
- tuner_dbg("Load init1 firmware, if exists\n");
-
- rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &std0);
- if (rc == -ENOENT)
- rc = load_firmware(fe, (BASE | INIT1 | new_fw.type) & ~F8MHZ,
- &std0);
- if (rc < 0 && rc != -ENOENT) {
- tuner_err("Error %d while loading init1 firmware\n",
- rc);
- goto fail;
- }
-
-skip_base:
- /*
- * No need to reload standard specific firmware if base firmware
- * was not reloaded and requested video standards have not changed.
- */
- if (priv->cur_fw.type == (BASE | new_fw.type) &&
- priv->cur_fw.std_req == std) {
- tuner_dbg("Std-specific firmware already loaded.\n");
- goto skip_std_specific;
- }
-
- /* Reloading std-specific firmware forces a SCODE update */
- priv->cur_fw.scode_table = 0;
-
- rc = load_firmware(fe, new_fw.type, &new_fw.id);
- if (rc == -ENOENT)
- rc = load_firmware(fe, new_fw.type & ~F8MHZ, &new_fw.id);
-
- if (rc < 0)
- goto fail;
-
-skip_std_specific:
- if (priv->cur_fw.scode_table == new_fw.scode_table &&
- priv->cur_fw.scode_nr == new_fw.scode_nr) {
- tuner_dbg("SCODE firmware already loaded.\n");
- goto check_device;
- }
-
- if (new_fw.type & FM)
- goto check_device;
-
- /* Load SCODE firmware, if exists */
- tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr);
-
- rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id,
- new_fw.int_freq, new_fw.scode_nr);
-
-check_device:
- if (xc2028_get_reg(priv, 0x0004, &version) < 0 ||
- xc2028_get_reg(priv, 0x0008, &hwmodel) < 0) {
- tuner_err("Unable to read tuner registers.\n");
- goto fail;
- }
-
- tuner_dbg("Device is Xceive %d version %d.%d, "
- "firmware version %d.%d\n",
- hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8,
- (version & 0xf0) >> 4, version & 0xf);
-
-
- if (priv->ctrl.read_not_reliable)
- goto read_not_reliable;
-
- /* Check firmware version against what we downloaded. */
- if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) {
- if (!priv->ctrl.read_not_reliable) {
- tuner_err("Incorrect readback of firmware version.\n");
- goto fail;
- } else {
- tuner_err("Returned an incorrect version. However, "
- "read is not reliable enough. Ignoring it.\n");
- hwmodel = 3028;
- }
- }
-
- /* Check that the tuner hardware model remains consistent over time. */
- if (priv->hwmodel == 0 && (hwmodel == 2028 || hwmodel == 3028)) {
- priv->hwmodel = hwmodel;
- priv->hwvers = version & 0xff00;
- } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel ||
- priv->hwvers != (version & 0xff00)) {
- tuner_err("Read invalid device hardware information - tuner "
- "hung?\n");
- goto fail;
- }
-
-read_not_reliable:
- memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));
-
- /*
- * By setting BASE in cur_fw.type only after successfully loading all
- * firmwares, we can:
- * 1. Identify that BASE firmware with type=0 has been loaded;
- * 2. Tell whether BASE firmware was just changed the next time through.
- */
- priv->cur_fw.type |= BASE;
-
- return 0;
-
-fail:
- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
- if (retry_count < 8) {
- msleep(50);
- retry_count++;
- tuner_dbg("Retrying firmware load\n");
- goto retry;
- }
-
- if (rc == -ENOENT)
- rc = -EINVAL;
- return rc;
-}
-
-static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- u16 frq_lock, signal = 0;
- int rc;
-
- tuner_dbg("%s called\n", __func__);
-
- mutex_lock(&priv->lock);
-
- /* Sync Lock Indicator */
- rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
- if (rc < 0)
- goto ret;
-
- /* Frequency is locked */
- if (frq_lock == 1)
- signal = 1 << 11;
-
- /* Get SNR of the video signal */
- rc = xc2028_get_reg(priv, XREG_SNR, &signal);
- if (rc < 0)
- goto ret;
-
- /* Use both frq_lock and signal to generate the result */
- signal = signal || ((signal & 0x07) << 12);
-
-ret:
- mutex_unlock(&priv->lock);
-
- *strength = signal;
-
- tuner_dbg("signal strength is %d\n", signal);
-
- return rc;
-}
-
-#define DIV 15625
-
-static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
- enum v4l2_tuner_type new_type,
- unsigned int type,
- v4l2_std_id std,
- u16 int_freq)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int rc = -EINVAL;
- unsigned char buf[4];
- u32 div, offset = 0;
-
- tuner_dbg("%s called\n", __func__);
-
- mutex_lock(&priv->lock);
-
- tuner_dbg("should set frequency %d kHz\n", freq / 1000);
-
- if (check_firmware(fe, type, std, int_freq) < 0)
- goto ret;
-
- /* On some cases xc2028 can disable video output, if
- * very weak signals are received. By sending a soft
- * reset, this is re-enabled. So, it is better to always
- * send a soft reset before changing channels, to be sure
- * that xc2028 will be in a safe state.
- * Maybe this might also be needed for DTV.
- */
- switch (new_type) {
- case V4L2_TUNER_ANALOG_TV:
- rc = send_seq(priv, {0x00, 0x00});
-
- /* Analog mode requires offset = 0 */
- break;
- case V4L2_TUNER_RADIO:
- /* Radio mode requires offset = 0 */
- break;
- case V4L2_TUNER_DIGITAL_TV:
- /*
- * Digital modes require an offset to adjust to the
- * proper frequency. The offset depends on what
- * firmware version is used.
- */
-
- /*
- * Adjust to the center frequency. This is calculated by the
- * formula: offset = 1.25MHz - BW/2
- * For DTV 7/8, the firmware uses BW = 8000, so it needs a
- * further adjustment to get the frequency center on VHF
- */
-
- /*
- * The firmware DTV78 used to work fine in UHF band (8 MHz
- * bandwidth) but not at all in VHF band (7 MHz bandwidth).
- * The real problem was connected to the formula used to
- * calculate the center frequency offset in VHF band.
- * In fact, removing the 500KHz adjustment fixed the problem.
- * This is coherent to what was implemented for the DTV7
- * firmware.
- * In the end, now the center frequency is the same for all 3
- * firmwares (DTV7, DTV8, DTV78) and doesn't depend on channel
- * bandwidth.
- */
-
- if (priv->cur_fw.type & DTV6)
- offset = 1750000;
- else /* DTV7 or DTV8 or DTV78 */
- offset = 2750000;
-
- /*
- * xc3028 additional "magic"
- * Depending on the firmware version, it needs some adjustments
- * to properly centralize the frequency. This seems to be
- * needed to compensate the SCODE table adjustments made by
- * newer firmwares
- */
-
- /*
- * The proper adjustment would be to do it at s-code table.
- * However, this didn't work, as reported by
- * Robert Lowery <rglowery@exemail.com.au>
- */
-
-#if 0
- /*
- * Still need tests for XC3028L (firmware 3.2 or upper)
- * So, for now, let's just comment the per-firmware
- * version of this change. Reports with xc3028l working
- * with and without the lines bellow are welcome
- */
-
- if (priv->firm_version < 0x0302) {
- if (priv->cur_fw.type & DTV7)
- offset += 500000;
- } else {
- if (priv->cur_fw.type & DTV7)
- offset -= 300000;
- else if (type != ATSC) /* DVB @6MHz, DTV 8 and DTV 7/8 */
- offset += 200000;
- }
-#endif
- }
-
- div = (freq - offset + DIV / 2) / DIV;
-
- /* CMD= Set frequency */
- if (priv->firm_version < 0x0202)
- rc = send_seq(priv, {0x00, XREG_RF_FREQ, 0x00, 0x00});
- else
- rc = send_seq(priv, {0x80, XREG_RF_FREQ, 0x00, 0x00});
- if (rc < 0)
- goto ret;
-
- /* Return code shouldn't be checked.
- The reset CLK is needed only with tm6000.
- Driver should work fine even if this fails.
- */
- if (priv->ctrl.msleep)
- msleep(priv->ctrl.msleep);
- do_tuner_callback(fe, XC2028_RESET_CLK, 1);
-
- msleep(10);
-
- buf[0] = 0xff & (div >> 24);
- buf[1] = 0xff & (div >> 16);
- buf[2] = 0xff & (div >> 8);
- buf[3] = 0xff & (div);
-
- rc = i2c_send(priv, buf, sizeof(buf));
- if (rc < 0)
- goto ret;
- msleep(100);
-
- priv->frequency = freq;
-
- tuner_dbg("divisor= %02x %02x %02x %02x (freq=%d.%03d)\n",
- buf[0], buf[1], buf[2], buf[3],
- freq / 1000000, (freq % 1000000) / 1000);
-
- rc = 0;
-
-ret:
- mutex_unlock(&priv->lock);
-
- return rc;
-}
-
-static int xc2028_set_analog_freq(struct dvb_frontend *fe,
- struct analog_parameters *p)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- unsigned int type=0;
-
- tuner_dbg("%s called\n", __func__);
-
- if (p->mode == V4L2_TUNER_RADIO) {
- type |= FM;
- if (priv->ctrl.input1)
- type |= INPUT1;
- return generic_set_freq(fe, (625l * p->frequency) / 10,
- V4L2_TUNER_RADIO, type, 0, 0);
- }
-
- /* if std is not defined, choose one */
- if (!p->std)
- p->std = V4L2_STD_MN;
-
- /* PAL/M, PAL/N, PAL/Nc and NTSC variants should use 6MHz firmware */
- if (!(p->std & V4L2_STD_MN))
- type |= F8MHZ;
-
- /* Add audio hack to std mask */
- p->std |= parse_audio_std_option();
-
- return generic_set_freq(fe, 62500l * p->frequency,
- V4L2_TUNER_ANALOG_TV, type, p->std, 0);
-}
-
-static int xc2028_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- u32 delsys = c->delivery_system;
- u32 bw = c->bandwidth_hz;
- struct xc2028_data *priv = fe->tuner_priv;
- unsigned int type=0;
- u16 demod = 0;
-
- tuner_dbg("%s called\n", __func__);
-
- switch (delsys) {
- case SYS_DVBT:
- case SYS_DVBT2:
- /*
- * The only countries with 6MHz seem to be Taiwan/Uruguay.
- * Both seem to require QAM firmware for OFDM decoding
- * Tested in Taiwan by Terry Wu <terrywu2009@gmail.com>
- */
- if (bw <= 6000000)
- type |= QAM;
-
- switch (priv->ctrl.type) {
- case XC2028_D2633:
- type |= D2633;
- break;
- case XC2028_D2620:
- type |= D2620;
- break;
- case XC2028_AUTO:
- default:
- /* Zarlink seems to need D2633 */
- if (priv->ctrl.demod == XC3028_FE_ZARLINK456)
- type |= D2633;
- else
- type |= D2620;
- }
- break;
- case SYS_ATSC:
- /* The only ATSC firmware (at least on v2.7) is D2633 */
- type |= ATSC | D2633;
- break;
- /* DVB-S and pure QAM (FE_QAM) are not supported */
- default:
- return -EINVAL;
- }
-
- if (bw <= 6000000) {
- type |= DTV6;
- priv->ctrl.vhfbw7 = 0;
- priv->ctrl.uhfbw8 = 0;
- } else if (bw <= 7000000) {
- if (c->frequency < 470000000)
- priv->ctrl.vhfbw7 = 1;
- else
- priv->ctrl.uhfbw8 = 0;
- type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV7;
- type |= F8MHZ;
- } else {
- if (c->frequency < 470000000)
- priv->ctrl.vhfbw7 = 0;
- else
- priv->ctrl.uhfbw8 = 1;
- type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV8;
- type |= F8MHZ;
- }
-
- /* All S-code tables need a 200kHz shift */
- if (priv->ctrl.demod) {
- demod = priv->ctrl.demod;
-
- /*
- * Newer firmwares require a 200 kHz offset only for ATSC
- */
- if (type == ATSC || priv->firm_version < 0x0302)
- demod += 200;
- /*
- * The DTV7 S-code table needs a 700 kHz shift.
- *
- * DTV7 is only used in Australia. Germany or Italy may also
- * use this firmware after initialization, but a tune to a UHF
- * channel should then cause DTV78 to be used.
- *
- * Unfortunately, on real-field tests, the s-code offset
- * didn't work as expected, as reported by
- * Robert Lowery <rglowery@exemail.com.au>
- */
- }
-
- return generic_set_freq(fe, c->frequency,
- V4L2_TUNER_DIGITAL_TV, type, 0, demod);
-}
-
-static int xc2028_sleep(struct dvb_frontend *fe)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int rc = 0;
-
- /* Avoid firmware reload on slow devices or if PM disabled */
- if (no_poweroff || priv->ctrl.disable_power_mgmt)
- return 0;
-
- tuner_dbg("Putting xc2028/3028 into poweroff mode.\n");
- if (debug > 1) {
- tuner_dbg("Printing sleep stack trace:\n");
- dump_stack();
- }
-
- mutex_lock(&priv->lock);
-
- if (priv->firm_version < 0x0202)
- rc = send_seq(priv, {0x00, XREG_POWER_DOWN, 0x00, 0x00});
- else
- rc = send_seq(priv, {0x80, XREG_POWER_DOWN, 0x00, 0x00});
-
- priv->cur_fw.type = 0; /* need firmware reload */
-
- mutex_unlock(&priv->lock);
-
- return rc;
-}
-
-static int xc2028_dvb_release(struct dvb_frontend *fe)
-{
- struct xc2028_data *priv = fe->tuner_priv;
-
- tuner_dbg("%s called\n", __func__);
-
- mutex_lock(&xc2028_list_mutex);
-
- /* only perform final cleanup if this is the last instance */
- if (hybrid_tuner_report_instance_count(priv) == 1) {
- kfree(priv->ctrl.fname);
- free_firmware(priv);
- }
-
- if (priv)
- hybrid_tuner_release_state(priv);
-
- mutex_unlock(&xc2028_list_mutex);
-
- fe->tuner_priv = NULL;
-
- return 0;
-}
-
-static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct xc2028_data *priv = fe->tuner_priv;
-
- tuner_dbg("%s called\n", __func__);
-
- *frequency = priv->frequency;
-
- return 0;
-}
-
-static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- struct xc2028_ctrl *p = priv_cfg;
- int rc = 0;
-
- tuner_dbg("%s called\n", __func__);
-
- mutex_lock(&priv->lock);
-
- memcpy(&priv->ctrl, p, sizeof(priv->ctrl));
- if (priv->ctrl.max_len < 9)
- priv->ctrl.max_len = 13;
-
- if (p->fname) {
- if (priv->ctrl.fname && strcmp(p->fname, priv->ctrl.fname)) {
- kfree(priv->ctrl.fname);
- free_firmware(priv);
- }
-
- priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL);
- if (priv->ctrl.fname == NULL)
- rc = -ENOMEM;
- }
-
- mutex_unlock(&priv->lock);
-
- return rc;
-}
-
-static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
- .info = {
- .name = "Xceive XC3028",
- .frequency_min = 42000000,
- .frequency_max = 864000000,
- .frequency_step = 50000,
- },
-
- .set_config = xc2028_set_config,
- .set_analog_params = xc2028_set_analog_freq,
- .release = xc2028_dvb_release,
- .get_frequency = xc2028_get_frequency,
- .get_rf_strength = xc2028_signal,
- .set_params = xc2028_set_params,
- .sleep = xc2028_sleep,
-};
-
-struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
- struct xc2028_config *cfg)
-{
- struct xc2028_data *priv;
- int instance;
-
- if (debug)
- printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n");
-
- if (NULL == cfg)
- return NULL;
-
- if (!fe) {
- printk(KERN_ERR "xc2028: No frontend!\n");
- return NULL;
- }
-
- mutex_lock(&xc2028_list_mutex);
-
- instance = hybrid_tuner_request_state(struct xc2028_data, priv,
- hybrid_tuner_instance_list,
- cfg->i2c_adap, cfg->i2c_addr,
- "xc2028");
- switch (instance) {
- case 0:
- /* memory allocation failure */
- goto fail;
- break;
- case 1:
- /* new tuner instance */
- priv->ctrl.max_len = 13;
-
- mutex_init(&priv->lock);
-
- fe->tuner_priv = priv;
- break;
- case 2:
- /* existing tuner instance */
- fe->tuner_priv = priv;
- break;
- }
-
- memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
- sizeof(xc2028_dvb_tuner_ops));
-
- tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner");
-
- if (cfg->ctrl)
- xc2028_set_config(fe, cfg->ctrl);
-
- mutex_unlock(&xc2028_list_mutex);
-
- return fe;
-fail:
- mutex_unlock(&xc2028_list_mutex);
-
- xc2028_dvb_release(fe);
- return NULL;
-}
-
-EXPORT_SYMBOL(xc2028_attach);
-
-MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
-MODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tuner-xc2028.h b/drivers/media/common/tuners/tuner-xc2028.h
deleted file mode 100644
index 9ebfb2d0ff14..000000000000
--- a/drivers/media/common/tuners/tuner-xc2028.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/* tuner-xc2028
- *
- * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org)
- * This code is placed under the terms of the GNU General Public License v2
- */
-
-#ifndef __TUNER_XC2028_H__
-#define __TUNER_XC2028_H__
-
-#include "dvb_frontend.h"
-
-#define XC2028_DEFAULT_FIRMWARE "xc3028-v27.fw"
-#define XC3028L_DEFAULT_FIRMWARE "xc3028L-v36.fw"
-
-/* Dmoduler IF (kHz) */
-#define XC3028_FE_DEFAULT 0 /* Don't load SCODE */
-#define XC3028_FE_LG60 6000
-#define XC3028_FE_ATI638 6380
-#define XC3028_FE_OREN538 5380
-#define XC3028_FE_OREN36 3600
-#define XC3028_FE_TOYOTA388 3880
-#define XC3028_FE_TOYOTA794 7940
-#define XC3028_FE_DIBCOM52 5200
-#define XC3028_FE_ZARLINK456 4560
-#define XC3028_FE_CHINA 5200
-
-enum firmware_type {
- XC2028_AUTO = 0, /* By default, auto-detects */
- XC2028_D2633,
- XC2028_D2620,
-};
-
-struct xc2028_ctrl {
- char *fname;
- int max_len;
- int msleep;
- unsigned int scode_table;
- unsigned int mts :1;
- unsigned int input1:1;
- unsigned int vhfbw7:1;
- unsigned int uhfbw8:1;
- unsigned int disable_power_mgmt:1;
- unsigned int read_not_reliable:1;
- unsigned int demod;
- enum firmware_type type:2;
-};
-
-struct xc2028_config {
- struct i2c_adapter *i2c_adap;
- u8 i2c_addr;
- struct xc2028_ctrl *ctrl;
-};
-
-/* xc2028 commands for callback */
-#define XC2028_TUNER_RESET 0
-#define XC2028_RESET_CLK 1
-#define XC2028_I2C_FLUSH 2
-
-#if defined(CONFIG_MEDIA_TUNER_XC2028) || (defined(CONFIG_MEDIA_TUNER_XC2028_MODULE) && defined(MODULE))
-extern struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
- struct xc2028_config *cfg);
-#else
-static inline struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
- struct xc2028_config *cfg)
-{
- printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
- __func__);
- return NULL;
-}
-#endif
-
-#endif /* __TUNER_XC2028_H__ */
diff --git a/drivers/media/common/tuners/xc4000.h b/drivers/media/common/tuners/xc4000.h
deleted file mode 100644
index e6a44d151cbd..000000000000
--- a/drivers/media/common/tuners/xc4000.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Driver for Xceive XC4000 "QAM/8VSB single chip tuner"
- *
- * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef __XC4000_H__
-#define __XC4000_H__
-
-#include <linux/firmware.h>
-
-struct dvb_frontend;
-struct i2c_adapter;
-
-struct xc4000_config {
- u8 i2c_address;
- /* if non-zero, power management is enabled by default */
- u8 default_pm;
- /* value to be written to XREG_AMPLITUDE in DVB-T mode (0: no write) */
- u8 dvb_amplitude;
- /* if non-zero, register 0x0E is set to filter analog TV video output */
- u8 set_smoothedcvbs;
- /* IF for DVB-T */
- u32 if_khz;
-};
-
-/* xc4000 callback command */
-#define XC4000_TUNER_RESET 0
-
-/* For each bridge framework, when it attaches either analog or digital,
- * it has to store a reference back to its _core equivalent structure,
- * so that it can service the hardware by steering gpio's etc.
- * Each bridge implementation is different so cast devptr accordingly.
- * The xc4000 driver cares not for this value, other than ensuring
- * it's passed back to a bridge during tuner_callback().
- */
-
-#if defined(CONFIG_MEDIA_TUNER_XC4000) || (defined(CONFIG_MEDIA_TUNER_XC4000_MODULE) && defined(MODULE))
-extern struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- struct xc4000_config *cfg);
-#else
-static inline struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- struct xc4000_config *cfg)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
deleted file mode 100644
index dcca42ca57be..000000000000
--- a/drivers/media/common/tuners/xc5000.c
+++ /dev/null
@@ -1,1255 +0,0 @@
-/*
- * Driver for Xceive XC5000 "QAM/8VSB single chip tuner"
- *
- * Copyright (c) 2007 Xceive Corporation
- * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
- * Copyright (c) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/videodev2.h>
-#include <linux/delay.h>
-#include <linux/dvb/frontend.h>
-#include <linux/i2c.h>
-
-#include "dvb_frontend.h"
-
-#include "xc5000.h"
-#include "tuner-i2c.h"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-
-static int no_poweroff;
-module_param(no_poweroff, int, 0644);
-MODULE_PARM_DESC(no_poweroff, "0 (default) powers device off when not used.\n"
- "\t\t1 keep device energized and with tuner ready all the times.\n"
- "\t\tFaster, but consumes more power and keeps the device hotter");
-
-static DEFINE_MUTEX(xc5000_list_mutex);
-static LIST_HEAD(hybrid_tuner_instance_list);
-
-#define dprintk(level, fmt, arg...) if (debug >= level) \
- printk(KERN_INFO "%s: " fmt, "xc5000", ## arg)
-
-struct xc5000_priv {
- struct tuner_i2c_props i2c_props;
- struct list_head hybrid_tuner_instance_list;
-
- u32 if_khz;
- u16 xtal_khz;
- u32 freq_hz;
- u32 bandwidth;
- u8 video_standard;
- u8 rf_mode;
- u8 radio_input;
-
- int chip_id;
-};
-
-/* Misc Defines */
-#define MAX_TV_STANDARD 24
-#define XC_MAX_I2C_WRITE_LENGTH 64
-
-/* Signal Types */
-#define XC_RF_MODE_AIR 0
-#define XC_RF_MODE_CABLE 1
-
-/* Result codes */
-#define XC_RESULT_SUCCESS 0
-#define XC_RESULT_RESET_FAILURE 1
-#define XC_RESULT_I2C_WRITE_FAILURE 2
-#define XC_RESULT_I2C_READ_FAILURE 3
-#define XC_RESULT_OUT_OF_RANGE 5
-
-/* Product id */
-#define XC_PRODUCT_ID_FW_NOT_LOADED 0x2000
-#define XC_PRODUCT_ID_FW_LOADED 0x1388
-
-/* Registers */
-#define XREG_INIT 0x00
-#define XREG_VIDEO_MODE 0x01
-#define XREG_AUDIO_MODE 0x02
-#define XREG_RF_FREQ 0x03
-#define XREG_D_CODE 0x04
-#define XREG_IF_OUT 0x05
-#define XREG_SEEK_MODE 0x07
-#define XREG_POWER_DOWN 0x0A /* Obsolete */
-/* Set the output amplitude - SIF for analog, DTVP/DTVN for digital */
-#define XREG_OUTPUT_AMP 0x0B
-#define XREG_SIGNALSOURCE 0x0D /* 0=Air, 1=Cable */
-#define XREG_SMOOTHEDCVBS 0x0E
-#define XREG_XTALFREQ 0x0F
-#define XREG_FINERFREQ 0x10
-#define XREG_DDIMODE 0x11
-
-#define XREG_ADC_ENV 0x00
-#define XREG_QUALITY 0x01
-#define XREG_FRAME_LINES 0x02
-#define XREG_HSYNC_FREQ 0x03
-#define XREG_LOCK 0x04
-#define XREG_FREQ_ERROR 0x05
-#define XREG_SNR 0x06
-#define XREG_VERSION 0x07
-#define XREG_PRODUCT_ID 0x08
-#define XREG_BUSY 0x09
-#define XREG_BUILD 0x0D
-
-/*
- Basic firmware description. This will remain with
- the driver for documentation purposes.
-
- This represents an I2C firmware file encoded as a
- string of unsigned char. Format is as follows:
-
- char[0 ]=len0_MSB -> len = len_MSB * 256 + len_LSB
- char[1 ]=len0_LSB -> length of first write transaction
- char[2 ]=data0 -> first byte to be sent
- char[3 ]=data1
- char[4 ]=data2
- char[ ]=...
- char[M ]=dataN -> last byte to be sent
- char[M+1]=len1_MSB -> len = len_MSB * 256 + len_LSB
- char[M+2]=len1_LSB -> length of second write transaction
- char[M+3]=data0
- char[M+4]=data1
- ...
- etc.
-
- The [len] value should be interpreted as follows:
-
- len= len_MSB _ len_LSB
- len=1111_1111_1111_1111 : End of I2C_SEQUENCE
- len=0000_0000_0000_0000 : Reset command: Do hardware reset
- len=0NNN_NNNN_NNNN_NNNN : Normal transaction: number of bytes = {1:32767)
- len=1WWW_WWWW_WWWW_WWWW : Wait command: wait for {1:32767} ms
-
- For the RESET and WAIT commands, the two following bytes will contain
- immediately the length of the following transaction.
-
-*/
-struct XC_TV_STANDARD {
- char *Name;
- u16 AudioMode;
- u16 VideoMode;
-};
-
-/* Tuner standards */
-#define MN_NTSC_PAL_BTSC 0
-#define MN_NTSC_PAL_A2 1
-#define MN_NTSC_PAL_EIAJ 2
-#define MN_NTSC_PAL_Mono 3
-#define BG_PAL_A2 4
-#define BG_PAL_NICAM 5
-#define BG_PAL_MONO 6
-#define I_PAL_NICAM 7
-#define I_PAL_NICAM_MONO 8
-#define DK_PAL_A2 9
-#define DK_PAL_NICAM 10
-#define DK_PAL_MONO 11
-#define DK_SECAM_A2DK1 12
-#define DK_SECAM_A2LDK3 13
-#define DK_SECAM_A2MONO 14
-#define L_SECAM_NICAM 15
-#define LC_SECAM_NICAM 16
-#define DTV6 17
-#define DTV8 18
-#define DTV7_8 19
-#define DTV7 20
-#define FM_Radio_INPUT2 21
-#define FM_Radio_INPUT1 22
-#define FM_Radio_INPUT1_MONO 23
-
-static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = {
- {"M/N-NTSC/PAL-BTSC", 0x0400, 0x8020},
- {"M/N-NTSC/PAL-A2", 0x0600, 0x8020},
- {"M/N-NTSC/PAL-EIAJ", 0x0440, 0x8020},
- {"M/N-NTSC/PAL-Mono", 0x0478, 0x8020},
- {"B/G-PAL-A2", 0x0A00, 0x8049},
- {"B/G-PAL-NICAM", 0x0C04, 0x8049},
- {"B/G-PAL-MONO", 0x0878, 0x8059},
- {"I-PAL-NICAM", 0x1080, 0x8009},
- {"I-PAL-NICAM-MONO", 0x0E78, 0x8009},
- {"D/K-PAL-A2", 0x1600, 0x8009},
- {"D/K-PAL-NICAM", 0x0E80, 0x8009},
- {"D/K-PAL-MONO", 0x1478, 0x8009},
- {"D/K-SECAM-A2 DK1", 0x1200, 0x8009},
- {"D/K-SECAM-A2 L/DK3", 0x0E00, 0x8009},
- {"D/K-SECAM-A2 MONO", 0x1478, 0x8009},
- {"L-SECAM-NICAM", 0x8E82, 0x0009},
- {"L'-SECAM-NICAM", 0x8E82, 0x4009},
- {"DTV6", 0x00C0, 0x8002},
- {"DTV8", 0x00C0, 0x800B},
- {"DTV7/8", 0x00C0, 0x801B},
- {"DTV7", 0x00C0, 0x8007},
- {"FM Radio-INPUT2", 0x9802, 0x9002},
- {"FM Radio-INPUT1", 0x0208, 0x9002},
- {"FM Radio-INPUT1_MONO", 0x0278, 0x9002}
-};
-
-
-struct xc5000_fw_cfg {
- char *name;
- u16 size;
-};
-
-static const struct xc5000_fw_cfg xc5000a_1_6_114 = {
- .name = "dvb-fe-xc5000-1.6.114.fw",
- .size = 12401,
-};
-
-static const struct xc5000_fw_cfg xc5000c_41_024_5 = {
- .name = "dvb-fe-xc5000c-41.024.5.fw",
- .size = 16497,
-};
-
-static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id)
-{
- switch (chip_id) {
- default:
- case XC5000A:
- return &xc5000a_1_6_114;
- case XC5000C:
- return &xc5000c_41_024_5;
- }
-}
-
-static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe);
-static int xc5000_is_firmware_loaded(struct dvb_frontend *fe);
-static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val);
-static int xc5000_TunerReset(struct dvb_frontend *fe);
-
-static int xc_send_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)
-{
- struct i2c_msg msg = { .addr = priv->i2c_props.addr,
- .flags = 0, .buf = buf, .len = len };
-
- if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
- printk(KERN_ERR "xc5000: I2C write failed (len=%i)\n", len);
- return XC_RESULT_I2C_WRITE_FAILURE;
- }
- return XC_RESULT_SUCCESS;
-}
-
-#if 0
-/* This routine is never used because the only time we read data from the
- i2c bus is when we read registers, and we want that to be an atomic i2c
- transaction in case we are on a multi-master bus */
-static int xc_read_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)
-{
- struct i2c_msg msg = { .addr = priv->i2c_props.addr,
- .flags = I2C_M_RD, .buf = buf, .len = len };
-
- if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
- printk(KERN_ERR "xc5000 I2C read failed (len=%i)\n", len);
- return -EREMOTEIO;
- }
- return 0;
-}
-#endif
-
-static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val)
-{
- u8 buf[2] = { reg >> 8, reg & 0xff };
- u8 bval[2] = { 0, 0 };
- struct i2c_msg msg[2] = {
- { .addr = priv->i2c_props.addr,
- .flags = 0, .buf = &buf[0], .len = 2 },
- { .addr = priv->i2c_props.addr,
- .flags = I2C_M_RD, .buf = &bval[0], .len = 2 },
- };
-
- if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) {
- printk(KERN_WARNING "xc5000: I2C read failed\n");
- return -EREMOTEIO;
- }
-
- *val = (bval[0] << 8) | bval[1];
- return XC_RESULT_SUCCESS;
-}
-
-static void xc_wait(int wait_ms)
-{
- msleep(wait_ms);
-}
-
-static int xc5000_TunerReset(struct dvb_frontend *fe)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- int ret;
-
- dprintk(1, "%s()\n", __func__);
-
- if (fe->callback) {
- ret = fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
- fe->dvb->priv :
- priv->i2c_props.adap->algo_data,
- DVB_FRONTEND_COMPONENT_TUNER,
- XC5000_TUNER_RESET, 0);
- if (ret) {
- printk(KERN_ERR "xc5000: reset failed\n");
- return XC_RESULT_RESET_FAILURE;
- }
- } else {
- printk(KERN_ERR "xc5000: no tuner reset callback function, fatal\n");
- return XC_RESULT_RESET_FAILURE;
- }
- return XC_RESULT_SUCCESS;
-}
-
-static int xc_write_reg(struct xc5000_priv *priv, u16 regAddr, u16 i2cData)
-{
- u8 buf[4];
- int WatchDogTimer = 100;
- int result;
-
- buf[0] = (regAddr >> 8) & 0xFF;
- buf[1] = regAddr & 0xFF;
- buf[2] = (i2cData >> 8) & 0xFF;
- buf[3] = i2cData & 0xFF;
- result = xc_send_i2c_data(priv, buf, 4);
- if (result == XC_RESULT_SUCCESS) {
- /* wait for busy flag to clear */
- while ((WatchDogTimer > 0) && (result == XC_RESULT_SUCCESS)) {
- result = xc5000_readreg(priv, XREG_BUSY, (u16 *)buf);
- if (result == XC_RESULT_SUCCESS) {
- if ((buf[0] == 0) && (buf[1] == 0)) {
- /* busy flag cleared */
- break;
- } else {
- xc_wait(5); /* wait 5 ms */
- WatchDogTimer--;
- }
- }
- }
- }
- if (WatchDogTimer < 0)
- result = XC_RESULT_I2C_WRITE_FAILURE;
-
- return result;
-}
-
-static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
-
- int i, nbytes_to_send, result;
- unsigned int len, pos, index;
- u8 buf[XC_MAX_I2C_WRITE_LENGTH];
-
- index = 0;
- while ((i2c_sequence[index] != 0xFF) ||
- (i2c_sequence[index + 1] != 0xFF)) {
- len = i2c_sequence[index] * 256 + i2c_sequence[index+1];
- if (len == 0x0000) {
- /* RESET command */
- result = xc5000_TunerReset(fe);
- index += 2;
- if (result != XC_RESULT_SUCCESS)
- return result;
- } else if (len & 0x8000) {
- /* WAIT command */
- xc_wait(len & 0x7FFF);
- index += 2;
- } else {
- /* Send i2c data whilst ensuring individual transactions
- * do not exceed XC_MAX_I2C_WRITE_LENGTH bytes.
- */
- index += 2;
- buf[0] = i2c_sequence[index];
- buf[1] = i2c_sequence[index + 1];
- pos = 2;
- while (pos < len) {
- if ((len - pos) > XC_MAX_I2C_WRITE_LENGTH - 2)
- nbytes_to_send =
- XC_MAX_I2C_WRITE_LENGTH;
- else
- nbytes_to_send = (len - pos + 2);
- for (i = 2; i < nbytes_to_send; i++) {
- buf[i] = i2c_sequence[index + pos +
- i - 2];
- }
- result = xc_send_i2c_data(priv, buf,
- nbytes_to_send);
-
- if (result != XC_RESULT_SUCCESS)
- return result;
-
- pos += nbytes_to_send - 2;
- }
- index += len;
- }
- }
- return XC_RESULT_SUCCESS;
-}
-
-static int xc_initialize(struct xc5000_priv *priv)
-{
- dprintk(1, "%s()\n", __func__);
- return xc_write_reg(priv, XREG_INIT, 0);
-}
-
-static int xc_SetTVStandard(struct xc5000_priv *priv,
- u16 VideoMode, u16 AudioMode)
-{
- int ret;
- dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, VideoMode, AudioMode);
- dprintk(1, "%s() Standard = %s\n",
- __func__,
- XC5000_Standard[priv->video_standard].Name);
-
- ret = xc_write_reg(priv, XREG_VIDEO_MODE, VideoMode);
- if (ret == XC_RESULT_SUCCESS)
- ret = xc_write_reg(priv, XREG_AUDIO_MODE, AudioMode);
-
- return ret;
-}
-
-static int xc_SetSignalSource(struct xc5000_priv *priv, u16 rf_mode)
-{
- dprintk(1, "%s(%d) Source = %s\n", __func__, rf_mode,
- rf_mode == XC_RF_MODE_AIR ? "ANTENNA" : "CABLE");
-
- if ((rf_mode != XC_RF_MODE_AIR) && (rf_mode != XC_RF_MODE_CABLE)) {
- rf_mode = XC_RF_MODE_CABLE;
- printk(KERN_ERR
- "%s(), Invalid mode, defaulting to CABLE",
- __func__);
- }
- return xc_write_reg(priv, XREG_SIGNALSOURCE, rf_mode);
-}
-
-static const struct dvb_tuner_ops xc5000_tuner_ops;
-
-static int xc_set_RF_frequency(struct xc5000_priv *priv, u32 freq_hz)
-{
- u16 freq_code;
-
- dprintk(1, "%s(%u)\n", __func__, freq_hz);
-
- if ((freq_hz > xc5000_tuner_ops.info.frequency_max) ||
- (freq_hz < xc5000_tuner_ops.info.frequency_min))
- return XC_RESULT_OUT_OF_RANGE;
-
- freq_code = (u16)(freq_hz / 15625);
-
- /* Starting in firmware version 1.1.44, Xceive recommends using the
- FINERFREQ for all normal tuning (the doc indicates reg 0x03 should
- only be used for fast scanning for channel lock) */
- return xc_write_reg(priv, XREG_FINERFREQ, freq_code);
-}
-
-
-static int xc_set_IF_frequency(struct xc5000_priv *priv, u32 freq_khz)
-{
- u32 freq_code = (freq_khz * 1024)/1000;
- dprintk(1, "%s(freq_khz = %d) freq_code = 0x%x\n",
- __func__, freq_khz, freq_code);
-
- return xc_write_reg(priv, XREG_IF_OUT, freq_code);
-}
-
-
-static int xc_get_ADC_Envelope(struct xc5000_priv *priv, u16 *adc_envelope)
-{
- return xc5000_readreg(priv, XREG_ADC_ENV, adc_envelope);
-}
-
-static int xc_get_frequency_error(struct xc5000_priv *priv, u32 *freq_error_hz)
-{
- int result;
- u16 regData;
- u32 tmp;
-
- result = xc5000_readreg(priv, XREG_FREQ_ERROR, &regData);
- if (result != XC_RESULT_SUCCESS)
- return result;
-
- tmp = (u32)regData;
- (*freq_error_hz) = (tmp * 15625) / 1000;
- return result;
-}
-
-static int xc_get_lock_status(struct xc5000_priv *priv, u16 *lock_status)
-{
- return xc5000_readreg(priv, XREG_LOCK, lock_status);
-}
-
-static int xc_get_version(struct xc5000_priv *priv,
- u8 *hw_majorversion, u8 *hw_minorversion,
- u8 *fw_majorversion, u8 *fw_minorversion)
-{
- u16 data;
- int result;
-
- result = xc5000_readreg(priv, XREG_VERSION, &data);
- if (result != XC_RESULT_SUCCESS)
- return result;
-
- (*hw_majorversion) = (data >> 12) & 0x0F;
- (*hw_minorversion) = (data >> 8) & 0x0F;
- (*fw_majorversion) = (data >> 4) & 0x0F;
- (*fw_minorversion) = data & 0x0F;
-
- return 0;
-}
-
-static int xc_get_buildversion(struct xc5000_priv *priv, u16 *buildrev)
-{
- return xc5000_readreg(priv, XREG_BUILD, buildrev);
-}
-
-static int xc_get_hsync_freq(struct xc5000_priv *priv, u32 *hsync_freq_hz)
-{
- u16 regData;
- int result;
-
- result = xc5000_readreg(priv, XREG_HSYNC_FREQ, &regData);
- if (result != XC_RESULT_SUCCESS)
- return result;
-
- (*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100;
- return result;
-}
-
-static int xc_get_frame_lines(struct xc5000_priv *priv, u16 *frame_lines)
-{
- return xc5000_readreg(priv, XREG_FRAME_LINES, frame_lines);
-}
-
-static int xc_get_quality(struct xc5000_priv *priv, u16 *quality)
-{
- return xc5000_readreg(priv, XREG_QUALITY, quality);
-}
-
-static u16 WaitForLock(struct xc5000_priv *priv)
-{
- u16 lockState = 0;
- int watchDogCount = 40;
-
- while ((lockState == 0) && (watchDogCount > 0)) {
- xc_get_lock_status(priv, &lockState);
- if (lockState != 1) {
- xc_wait(5);
- watchDogCount--;
- }
- }
- return lockState;
-}
-
-#define XC_TUNE_ANALOG 0
-#define XC_TUNE_DIGITAL 1
-static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode)
-{
- int found = 0;
-
- dprintk(1, "%s(%u)\n", __func__, freq_hz);
-
- if (xc_set_RF_frequency(priv, freq_hz) != XC_RESULT_SUCCESS)
- return 0;
-
- if (mode == XC_TUNE_ANALOG) {
- if (WaitForLock(priv) == 1)
- found = 1;
- }
-
- return found;
-}
-
-static int xc_set_xtal(struct dvb_frontend *fe)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- int ret = XC_RESULT_SUCCESS;
-
- switch (priv->chip_id) {
- default:
- case XC5000A:
- /* 32.000 MHz xtal is default */
- break;
- case XC5000C:
- switch (priv->xtal_khz) {
- default:
- case 32000:
- /* 32.000 MHz xtal is default */
- break;
- case 31875:
- /* 31.875 MHz xtal configuration */
- ret = xc_write_reg(priv, 0x000f, 0x8081);
- break;
- }
- break;
- }
- return ret;
-}
-
-static int xc5000_fwupload(struct dvb_frontend *fe)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- const struct firmware *fw;
- int ret;
- const struct xc5000_fw_cfg *desired_fw =
- xc5000_assign_firmware(priv->chip_id);
-
- /* request the firmware, this will block and timeout */
- printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n",
- desired_fw->name);
-
- ret = request_firmware(&fw, desired_fw->name,
- priv->i2c_props.adap->dev.parent);
- if (ret) {
- printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n");
- ret = XC_RESULT_RESET_FAILURE;
- goto out;
- } else {
- printk(KERN_DEBUG "xc5000: firmware read %Zu bytes.\n",
- fw->size);
- ret = XC_RESULT_SUCCESS;
- }
-
- if (fw->size != desired_fw->size) {
- printk(KERN_ERR "xc5000: firmware incorrect size\n");
- ret = XC_RESULT_RESET_FAILURE;
- } else {
- printk(KERN_INFO "xc5000: firmware uploading...\n");
- ret = xc_load_i2c_sequence(fe, fw->data);
- if (XC_RESULT_SUCCESS == ret)
- ret = xc_set_xtal(fe);
- if (XC_RESULT_SUCCESS == ret)
- printk(KERN_INFO "xc5000: firmware upload complete...\n");
- else
- printk(KERN_ERR "xc5000: firmware upload failed...\n");
- }
-
-out:
- release_firmware(fw);
- return ret;
-}
-
-static void xc_debug_dump(struct xc5000_priv *priv)
-{
- u16 adc_envelope;
- u32 freq_error_hz = 0;
- u16 lock_status;
- u32 hsync_freq_hz = 0;
- u16 frame_lines;
- u16 quality;
- u8 hw_majorversion = 0, hw_minorversion = 0;
- u8 fw_majorversion = 0, fw_minorversion = 0;
- u16 fw_buildversion = 0;
-
- /* Wait for stats to stabilize.
- * Frame Lines needs two frame times after initial lock
- * before it is valid.
- */
- xc_wait(100);
-
- xc_get_ADC_Envelope(priv, &adc_envelope);
- dprintk(1, "*** ADC envelope (0-1023) = %d\n", adc_envelope);
-
- xc_get_frequency_error(priv, &freq_error_hz);
- dprintk(1, "*** Frequency error = %d Hz\n", freq_error_hz);
-
- xc_get_lock_status(priv, &lock_status);
- dprintk(1, "*** Lock status (0-Wait, 1-Locked, 2-No-signal) = %d\n",
- lock_status);
-
- xc_get_version(priv, &hw_majorversion, &hw_minorversion,
- &fw_majorversion, &fw_minorversion);
- xc_get_buildversion(priv, &fw_buildversion);
- dprintk(1, "*** HW: V%02x.%02x, FW: V%02x.%02x.%04x\n",
- hw_majorversion, hw_minorversion,
- fw_majorversion, fw_minorversion, fw_buildversion);
-
- xc_get_hsync_freq(priv, &hsync_freq_hz);
- dprintk(1, "*** Horizontal sync frequency = %d Hz\n", hsync_freq_hz);
-
- xc_get_frame_lines(priv, &frame_lines);
- dprintk(1, "*** Frame lines = %d\n", frame_lines);
-
- xc_get_quality(priv, &quality);
- dprintk(1, "*** Quality (0:<8dB, 7:>56dB) = %d\n", quality);
-}
-
-static int xc5000_set_params(struct dvb_frontend *fe)
-{
- int ret, b;
- struct xc5000_priv *priv = fe->tuner_priv;
- u32 bw = fe->dtv_property_cache.bandwidth_hz;
- u32 freq = fe->dtv_property_cache.frequency;
- u32 delsys = fe->dtv_property_cache.delivery_system;
-
- if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
- if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
- dprintk(1, "Unable to load firmware and init tuner\n");
- return -EINVAL;
- }
- }
-
- dprintk(1, "%s() frequency=%d (Hz)\n", __func__, freq);
-
- switch (delsys) {
- case SYS_ATSC:
- dprintk(1, "%s() VSB modulation\n", __func__);
- priv->rf_mode = XC_RF_MODE_AIR;
- priv->freq_hz = freq - 1750000;
- priv->video_standard = DTV6;
- break;
- case SYS_DVBC_ANNEX_B:
- dprintk(1, "%s() QAM modulation\n", __func__);
- priv->rf_mode = XC_RF_MODE_CABLE;
- priv->freq_hz = freq - 1750000;
- priv->video_standard = DTV6;
- break;
- case SYS_DVBT:
- case SYS_DVBT2:
- dprintk(1, "%s() OFDM\n", __func__);
- switch (bw) {
- case 6000000:
- priv->video_standard = DTV6;
- priv->freq_hz = freq - 1750000;
- break;
- case 7000000:
- priv->video_standard = DTV7;
- priv->freq_hz = freq - 2250000;
- break;
- case 8000000:
- priv->video_standard = DTV8;
- priv->freq_hz = freq - 2750000;
- break;
- default:
- printk(KERN_ERR "xc5000 bandwidth not set!\n");
- return -EINVAL;
- }
- priv->rf_mode = XC_RF_MODE_AIR;
- case SYS_DVBC_ANNEX_A:
- case SYS_DVBC_ANNEX_C:
- dprintk(1, "%s() QAM modulation\n", __func__);
- priv->rf_mode = XC_RF_MODE_CABLE;
- if (bw <= 6000000) {
- priv->video_standard = DTV6;
- priv->freq_hz = freq - 1750000;
- b = 6;
- } else if (bw <= 7000000) {
- priv->video_standard = DTV7;
- priv->freq_hz = freq - 2250000;
- b = 7;
- } else {
- priv->video_standard = DTV7_8;
- priv->freq_hz = freq - 2750000;
- b = 8;
- }
- dprintk(1, "%s() Bandwidth %dMHz (%d)\n", __func__,
- b, bw);
- break;
- default:
- printk(KERN_ERR "xc5000: delivery system is not supported!\n");
- return -EINVAL;
- }
-
- dprintk(1, "%s() frequency=%d (compensated to %d)\n",
- __func__, freq, priv->freq_hz);
-
- ret = xc_SetSignalSource(priv, priv->rf_mode);
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR
- "xc5000: xc_SetSignalSource(%d) failed\n",
- priv->rf_mode);
- return -EREMOTEIO;
- }
-
- ret = xc_SetTVStandard(priv,
- XC5000_Standard[priv->video_standard].VideoMode,
- XC5000_Standard[priv->video_standard].AudioMode);
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
- return -EREMOTEIO;
- }
-
- ret = xc_set_IF_frequency(priv, priv->if_khz);
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n",
- priv->if_khz);
- return -EIO;
- }
-
- xc_write_reg(priv, XREG_OUTPUT_AMP, 0x8a);
-
- xc_tune_channel(priv, priv->freq_hz, XC_TUNE_DIGITAL);
-
- if (debug)
- xc_debug_dump(priv);
-
- priv->bandwidth = bw;
-
- return 0;
-}
-
-static int xc5000_is_firmware_loaded(struct dvb_frontend *fe)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- int ret;
- u16 id;
-
- ret = xc5000_readreg(priv, XREG_PRODUCT_ID, &id);
- if (ret == XC_RESULT_SUCCESS) {
- if (id == XC_PRODUCT_ID_FW_NOT_LOADED)
- ret = XC_RESULT_RESET_FAILURE;
- else
- ret = XC_RESULT_SUCCESS;
- }
-
- dprintk(1, "%s() returns %s id = 0x%x\n", __func__,
- ret == XC_RESULT_SUCCESS ? "True" : "False", id);
- return ret;
-}
-
-static int xc5000_set_tv_freq(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- int ret;
-
- dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
- __func__, params->frequency);
-
- /* Fix me: it could be air. */
- priv->rf_mode = params->mode;
- if (params->mode > XC_RF_MODE_CABLE)
- priv->rf_mode = XC_RF_MODE_CABLE;
-
- /* params->frequency is in units of 62.5khz */
- priv->freq_hz = params->frequency * 62500;
-
- /* FIX ME: Some video standards may have several possible audio
- standards. We simply default to one of them here.
- */
- if (params->std & V4L2_STD_MN) {
- /* default to BTSC audio standard */
- priv->video_standard = MN_NTSC_PAL_BTSC;
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_PAL_BG) {
- /* default to NICAM audio standard */
- priv->video_standard = BG_PAL_NICAM;
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_PAL_I) {
- /* default to NICAM audio standard */
- priv->video_standard = I_PAL_NICAM;
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_PAL_DK) {
- /* default to NICAM audio standard */
- priv->video_standard = DK_PAL_NICAM;
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_SECAM_DK) {
- /* default to A2 DK1 audio standard */
- priv->video_standard = DK_SECAM_A2DK1;
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_SECAM_L) {
- priv->video_standard = L_SECAM_NICAM;
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_SECAM_LC) {
- priv->video_standard = LC_SECAM_NICAM;
- goto tune_channel;
- }
-
-tune_channel:
- ret = xc_SetSignalSource(priv, priv->rf_mode);
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR
- "xc5000: xc_SetSignalSource(%d) failed\n",
- priv->rf_mode);
- return -EREMOTEIO;
- }
-
- ret = xc_SetTVStandard(priv,
- XC5000_Standard[priv->video_standard].VideoMode,
- XC5000_Standard[priv->video_standard].AudioMode);
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
- return -EREMOTEIO;
- }
-
- xc_write_reg(priv, XREG_OUTPUT_AMP, 0x09);
-
- xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG);
-
- if (debug)
- xc_debug_dump(priv);
-
- return 0;
-}
-
-static int xc5000_set_radio_freq(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- int ret = -EINVAL;
- u8 radio_input;
-
- dprintk(1, "%s() frequency=%d (in units of khz)\n",
- __func__, params->frequency);
-
- if (priv->radio_input == XC5000_RADIO_NOT_CONFIGURED) {
- dprintk(1, "%s() radio input not configured\n", __func__);
- return -EINVAL;
- }
-
- if (priv->radio_input == XC5000_RADIO_FM1)
- radio_input = FM_Radio_INPUT1;
- else if (priv->radio_input == XC5000_RADIO_FM2)
- radio_input = FM_Radio_INPUT2;
- else if (priv->radio_input == XC5000_RADIO_FM1_MONO)
- radio_input = FM_Radio_INPUT1_MONO;
- else {
- dprintk(1, "%s() unknown radio input %d\n", __func__,
- priv->radio_input);
- return -EINVAL;
- }
-
- priv->freq_hz = params->frequency * 125 / 2;
-
- priv->rf_mode = XC_RF_MODE_AIR;
-
- ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode,
- XC5000_Standard[radio_input].AudioMode);
-
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
- return -EREMOTEIO;
- }
-
- ret = xc_SetSignalSource(priv, priv->rf_mode);
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR
- "xc5000: xc_SetSignalSource(%d) failed\n",
- priv->rf_mode);
- return -EREMOTEIO;
- }
-
- if ((priv->radio_input == XC5000_RADIO_FM1) ||
- (priv->radio_input == XC5000_RADIO_FM2))
- xc_write_reg(priv, XREG_OUTPUT_AMP, 0x09);
- else if (priv->radio_input == XC5000_RADIO_FM1_MONO)
- xc_write_reg(priv, XREG_OUTPUT_AMP, 0x06);
-
- xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG);
-
- return 0;
-}
-
-static int xc5000_set_analog_params(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- int ret = -EINVAL;
-
- if (priv->i2c_props.adap == NULL)
- return -EINVAL;
-
- if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
- if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
- dprintk(1, "Unable to load firmware and init tuner\n");
- return -EINVAL;
- }
- }
-
- switch (params->mode) {
- case V4L2_TUNER_RADIO:
- ret = xc5000_set_radio_freq(fe, params);
- break;
- case V4L2_TUNER_ANALOG_TV:
- case V4L2_TUNER_DIGITAL_TV:
- ret = xc5000_set_tv_freq(fe, params);
- break;
- }
-
- return ret;
-}
-
-
-static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- dprintk(1, "%s()\n", __func__);
- *freq = priv->freq_hz;
- return 0;
-}
-
-static int xc5000_get_if_frequency(struct dvb_frontend *fe, u32 *freq)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- dprintk(1, "%s()\n", __func__);
- *freq = priv->if_khz * 1000;
- return 0;
-}
-
-static int xc5000_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- dprintk(1, "%s()\n", __func__);
-
- *bw = priv->bandwidth;
- return 0;
-}
-
-static int xc5000_get_status(struct dvb_frontend *fe, u32 *status)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- u16 lock_status = 0;
-
- xc_get_lock_status(priv, &lock_status);
-
- dprintk(1, "%s() lock_status = 0x%08x\n", __func__, lock_status);
-
- *status = lock_status;
-
- return 0;
-}
-
-static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- int ret = 0;
-
- if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
- ret = xc5000_fwupload(fe);
- if (ret != XC_RESULT_SUCCESS)
- return ret;
- }
-
- /* Start the tuner self-calibration process */
- ret |= xc_initialize(priv);
-
- /* Wait for calibration to complete.
- * We could continue but XC5000 will clock stretch subsequent
- * I2C transactions until calibration is complete. This way we
- * don't have to rely on clock stretching working.
- */
- xc_wait(100);
-
- /* Default to "CABLE" mode */
- ret |= xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE);
-
- return ret;
-}
-
-static int xc5000_sleep(struct dvb_frontend *fe)
-{
- int ret;
-
- dprintk(1, "%s()\n", __func__);
-
- /* Avoid firmware reload on slow devices */
- if (no_poweroff)
- return 0;
-
- /* According to Xceive technical support, the "powerdown" register
- was removed in newer versions of the firmware. The "supported"
- way to sleep the tuner is to pull the reset pin low for 10ms */
- ret = xc5000_TunerReset(fe);
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR
- "xc5000: %s() unable to shutdown tuner\n",
- __func__);
- return -EREMOTEIO;
- } else
- return XC_RESULT_SUCCESS;
-}
-
-static int xc5000_init(struct dvb_frontend *fe)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- dprintk(1, "%s()\n", __func__);
-
- if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
- printk(KERN_ERR "xc5000: Unable to initialise tuner\n");
- return -EREMOTEIO;
- }
-
- if (debug)
- xc_debug_dump(priv);
-
- return 0;
-}
-
-static int xc5000_release(struct dvb_frontend *fe)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
-
- dprintk(1, "%s()\n", __func__);
-
- mutex_lock(&xc5000_list_mutex);
-
- if (priv)
- hybrid_tuner_release_state(priv);
-
- mutex_unlock(&xc5000_list_mutex);
-
- fe->tuner_priv = NULL;
-
- return 0;
-}
-
-static int xc5000_set_config(struct dvb_frontend *fe, void *priv_cfg)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- struct xc5000_config *p = priv_cfg;
-
- dprintk(1, "%s()\n", __func__);
-
- if (p->if_khz)
- priv->if_khz = p->if_khz;
-
- if (p->radio_input)
- priv->radio_input = p->radio_input;
-
- return 0;
-}
-
-
-static const struct dvb_tuner_ops xc5000_tuner_ops = {
- .info = {
- .name = "Xceive XC5000",
- .frequency_min = 1000000,
- .frequency_max = 1023000000,
- .frequency_step = 50000,
- },
-
- .release = xc5000_release,
- .init = xc5000_init,
- .sleep = xc5000_sleep,
-
- .set_config = xc5000_set_config,
- .set_params = xc5000_set_params,
- .set_analog_params = xc5000_set_analog_params,
- .get_frequency = xc5000_get_frequency,
- .get_if_frequency = xc5000_get_if_frequency,
- .get_bandwidth = xc5000_get_bandwidth,
- .get_status = xc5000_get_status
-};
-
-struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- const struct xc5000_config *cfg)
-{
- struct xc5000_priv *priv = NULL;
- int instance;
- u16 id = 0;
-
- dprintk(1, "%s(%d-%04x)\n", __func__,
- i2c ? i2c_adapter_id(i2c) : -1,
- cfg ? cfg->i2c_address : -1);
-
- mutex_lock(&xc5000_list_mutex);
-
- instance = hybrid_tuner_request_state(struct xc5000_priv, priv,
- hybrid_tuner_instance_list,
- i2c, cfg->i2c_address, "xc5000");
- switch (instance) {
- case 0:
- goto fail;
- break;
- case 1:
- /* new tuner instance */
- priv->bandwidth = 6000000;
- fe->tuner_priv = priv;
- break;
- default:
- /* existing tuner instance */
- fe->tuner_priv = priv;
- break;
- }
-
- if (priv->if_khz == 0) {
- /* If the IF hasn't been set yet, use the value provided by
- the caller (occurs in hybrid devices where the analog
- call to xc5000_attach occurs before the digital side) */
- priv->if_khz = cfg->if_khz;
- }
-
- if (priv->xtal_khz == 0)
- priv->xtal_khz = cfg->xtal_khz;
-
- if (priv->radio_input == 0)
- priv->radio_input = cfg->radio_input;
-
- /* don't override chip id if it's already been set
- unless explicitly specified */
- if ((priv->chip_id == 0) || (cfg->chip_id))
- /* use default chip id if none specified, set to 0 so
- it can be overridden if this is a hybrid driver */
- priv->chip_id = (cfg->chip_id) ? cfg->chip_id : 0;
-
- /* Check if firmware has been loaded. It is possible that another
- instance of the driver has loaded the firmware.
- */
- if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != XC_RESULT_SUCCESS)
- goto fail;
-
- switch (id) {
- case XC_PRODUCT_ID_FW_LOADED:
- printk(KERN_INFO
- "xc5000: Successfully identified at address 0x%02x\n",
- cfg->i2c_address);
- printk(KERN_INFO
- "xc5000: Firmware has been loaded previously\n");
- break;
- case XC_PRODUCT_ID_FW_NOT_LOADED:
- printk(KERN_INFO
- "xc5000: Successfully identified at address 0x%02x\n",
- cfg->i2c_address);
- printk(KERN_INFO
- "xc5000: Firmware has not been loaded previously\n");
- break;
- default:
- printk(KERN_ERR
- "xc5000: Device not found at addr 0x%02x (0x%x)\n",
- cfg->i2c_address, id);
- goto fail;
- }
-
- mutex_unlock(&xc5000_list_mutex);
-
- memcpy(&fe->ops.tuner_ops, &xc5000_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- return fe;
-fail:
- mutex_unlock(&xc5000_list_mutex);
-
- xc5000_release(fe);
- return NULL;
-}
-EXPORT_SYMBOL(xc5000_attach);
-
-MODULE_AUTHOR("Steven Toth");
-MODULE_DESCRIPTION("Xceive xc5000 silicon tuner driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h
deleted file mode 100644
index b1a547494625..000000000000
--- a/drivers/media/common/tuners/xc5000.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Driver for Xceive XC5000 "QAM/8VSB single chip tuner"
- *
- * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef __XC5000_H__
-#define __XC5000_H__
-
-#include <linux/firmware.h>
-
-struct dvb_frontend;
-struct i2c_adapter;
-
-#define XC5000A 1
-#define XC5000C 2
-
-struct xc5000_config {
- u8 i2c_address;
- u32 if_khz;
- u8 radio_input;
- u16 xtal_khz;
-
- int chip_id;
-};
-
-/* xc5000 callback command */
-#define XC5000_TUNER_RESET 0
-
-/* Possible Radio inputs */
-#define XC5000_RADIO_NOT_CONFIGURED 0
-#define XC5000_RADIO_FM1 1
-#define XC5000_RADIO_FM2 2
-#define XC5000_RADIO_FM1_MONO 3
-
-/* For each bridge framework, when it attaches either analog or digital,
- * it has to store a reference back to its _core equivalent structure,
- * so that it can service the hardware by steering gpio's etc.
- * Each bridge implementation is different so cast devptr accordingly.
- * The xc5000 driver cares not for this value, other than ensuring
- * it's passed back to a bridge during tuner_callback().
- */
-
-#if defined(CONFIG_MEDIA_TUNER_XC5000) || \
- (defined(CONFIG_MEDIA_TUNER_XC5000_MODULE) && defined(MODULE))
-extern struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- const struct xc5000_config *cfg);
-#else
-static inline struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- const struct xc5000_config *cfg)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif
diff --git a/drivers/media/common/tveeprom.c b/drivers/media/common/tveeprom.c
new file mode 100644
index 000000000000..b5b9d6de6d9e
--- /dev/null
+++ b/drivers/media/common/tveeprom.c
@@ -0,0 +1,759 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * tveeprom - eeprom decoder for tvcard configuration eeproms
+ *
+ * Data and decoding routines shamelessly borrowed from bttv-cards.c
+ * eeprom access routine shamelessly borrowed from bttv-if.c
+ * which are:
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ & Marcus Metzler (mocm@thp.uni-koeln.de)
+ (c) 1999-2001 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+
+ * Adjustments to fit a more general model and all bugs:
+
+ Copyright (C) 2003 John Klar <linpvr at projectplasma.com>
+
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-common.h>
+
+MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver");
+MODULE_AUTHOR("John Klar");
+MODULE_LICENSE("GPL");
+
+#define STRM(array, i) \
+ (i < sizeof(array) / sizeof(char *) ? array[i] : "unknown")
+
+
+/*
+ * The Hauppauge eeprom uses an 8bit field to determine which
+ * tuner formats the tuner supports.
+ */
+static const struct {
+ int id;
+ const char * const name;
+} hauppauge_tuner_fmt[] = {
+ { V4L2_STD_UNKNOWN, " UNKNOWN" },
+ { V4L2_STD_UNKNOWN, " FM" },
+ { V4L2_STD_B|V4L2_STD_GH, " PAL(B/G)" },
+ { V4L2_STD_MN, " NTSC(M)" },
+ { V4L2_STD_PAL_I, " PAL(I)" },
+ { V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC, " SECAM(L/L')" },
+ { V4L2_STD_DK, " PAL(D/D1/K)" },
+ { V4L2_STD_ATSC, " ATSC/DVB Digital" },
+};
+
+/* This is the full list of possible tuners. Many thanks to Hauppauge for
+ supplying this information. Note that many tuners where only used for
+ testing and never made it to the outside world. So you will only see
+ a subset in actual produced cards. */
+static const struct {
+ int id;
+ const char * const name;
+} hauppauge_tuner[] = {
+ /* 0-9 */
+ { TUNER_ABSENT, "None" },
+ { TUNER_ABSENT, "External" },
+ { TUNER_ABSENT, "Unspecified" },
+ { TUNER_PHILIPS_PAL, "Philips FI1216" },
+ { TUNER_PHILIPS_SECAM, "Philips FI1216MF" },
+ { TUNER_PHILIPS_NTSC, "Philips FI1236" },
+ { TUNER_PHILIPS_PAL_I, "Philips FI1246" },
+ { TUNER_PHILIPS_PAL_DK, "Philips FI1256" },
+ { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" },
+ { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" },
+ /* 10-19 */
+ { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" },
+ { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" },
+ { TUNER_PHILIPS_PAL_DK, "Philips FI1256 MK2" },
+ { TUNER_TEMIC_NTSC, "Temic 4032FY5" },
+ { TUNER_TEMIC_PAL, "Temic 4002FH5" },
+ { TUNER_TEMIC_PAL_I, "Temic 4062FY5" },
+ { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" },
+ { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" },
+ { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" },
+ { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" },
+ /* 20-29 */
+ { TUNER_PHILIPS_PAL_DK, "Philips FR1256 MK2" },
+ { TUNER_PHILIPS_PAL, "Philips FM1216" },
+ { TUNER_PHILIPS_SECAM, "Philips FM1216MF" },
+ { TUNER_PHILIPS_NTSC, "Philips FM1236" },
+ { TUNER_PHILIPS_PAL_I, "Philips FM1246" },
+ { TUNER_PHILIPS_PAL_DK, "Philips FM1256" },
+ { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" },
+ { TUNER_ABSENT, "Samsung TCPN9082D" },
+ { TUNER_ABSENT, "Samsung TCPM9092P" },
+ { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" },
+ /* 30-39 */
+ { TUNER_ABSENT, "Samsung TCPN9085D" },
+ { TUNER_ABSENT, "Samsung TCPB9085P" },
+ { TUNER_ABSENT, "Samsung TCPL9091P" },
+ { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" },
+ { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" },
+ { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" },
+ { TUNER_PHILIPS_NTSC, "Philips TD1536" },
+ { TUNER_PHILIPS_NTSC, "Philips TD1536D" },
+ { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */
+ { TUNER_ABSENT, "Philips FI1256MP" },
+ /* 40-49 */
+ { TUNER_ABSENT, "Samsung TCPQ9091P" },
+ { TUNER_TEMIC_4006FN5_MULTI_PAL,"Temic 4006FN5" },
+ { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" },
+ { TUNER_TEMIC_4046FM5, "Temic 4046FM5" },
+ { TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5" },
+ { TUNER_ABSENT, "Philips TD1536D FH 44"},
+ { TUNER_LG_NTSC_FM, "LG TP18NSR01F"},
+ { TUNER_LG_PAL_FM, "LG TP18PSB01D"},
+ { TUNER_LG_PAL, "LG TP18PSB11D"},
+ { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"},
+ /* 50-59 */
+ { TUNER_LG_PAL_I, "LG TAPC-I701D"},
+ { TUNER_ABSENT, "Temic 4042FI5"},
+ { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"},
+ { TUNER_ABSENT, "LG TPI8NSR11F"},
+ { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"},
+ { TUNER_ABSENT, "Philips FI1236 MK3"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"},
+ { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"},
+ { TUNER_ABSENT, "Philips FM1216MP MK3"},
+ /* 60-69 */
+ { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"},
+ { TUNER_ABSENT, "LG M001D MK3"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"},
+ { TUNER_ABSENT, "LG M701D MK3"},
+ { TUNER_ABSENT, "Temic 4146FM5"},
+ { TUNER_ABSENT, "Temic 4136FY5"},
+ { TUNER_ABSENT, "Temic 4106FH5"},
+ { TUNER_ABSENT, "Philips FQ1216LMP MK3"},
+ { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"},
+ { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"},
+ /* 70-79 */
+ { TUNER_ABSENT, "LG TALN H200T"},
+ { TUNER_ABSENT, "LG TALN H250T"},
+ { TUNER_ABSENT, "LG TALN M200T"},
+ { TUNER_ABSENT, "LG TALN Z200T"},
+ { TUNER_ABSENT, "LG TALN S200T"},
+ { TUNER_ABSENT, "Thompson DTT7595"},
+ { TUNER_ABSENT, "Thompson DTT7592"},
+ { TUNER_ABSENT, "Silicon TDA8275C1 8290"},
+ { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"},
+ { TUNER_ABSENT, "Thompson DTT757"},
+ /* 80-89 */
+ { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK3"},
+ { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"},
+ { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"},
+ { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"},
+ { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"},
+ { TUNER_TCL_2002N, "TCL 2002N 6A"},
+ { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"},
+ { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"},
+ { TUNER_ABSENT, "Samsung TCPE 4121P30A"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"},
+ /* 90-99 */
+ { TUNER_ABSENT, "LG TALN H202T"},
+ { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"},
+ { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"},
+ { TUNER_ABSENT, "Philips FQ1286A MK4"},
+ { TUNER_ABSENT, "Philips FQ1216ME MK5"},
+ { TUNER_ABSENT, "Philips FQ1236 MK5"},
+ { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"},
+ { TUNER_TCL_2002MB, "TCL 2002MB_3H"},
+ { TUNER_ABSENT, "TCL 2002MI_3H"},
+ { TUNER_TCL_2002N, "TCL 2002N 5H"},
+ /* 100-109 */
+ { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"},
+ { TUNER_TEA5767, "Philips TEA5768HL FM Radio"},
+ { TUNER_ABSENT, "Panasonic ENV57H12D5"},
+ { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"},
+ { TUNER_PHILIPS_FM1236_MK3, "TCL MNM05-4"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"},
+ { TUNER_ABSENT, "TCL MQNM05-4"},
+ { TUNER_ABSENT, "LG TAPC-W701D"},
+ { TUNER_ABSENT, "TCL 9886P-WM"},
+ { TUNER_ABSENT, "TCL 1676NM-WM"},
+ /* 110-119 */
+ { TUNER_ABSENT, "Thompson DTT75105"},
+ { TUNER_ABSENT, "Conexant_CX24109"},
+ { TUNER_TCL_2002N, "TCL M2523_5N_E"},
+ { TUNER_TCL_2002MB, "TCL M2523_3DB_E"},
+ { TUNER_ABSENT, "Philips 8275A"},
+ { TUNER_ABSENT, "Microtune MT2060"},
+ { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"},
+ { TUNER_ABSENT, "TCL M2523_3DI_E"},
+ { TUNER_ABSENT, "Samsung THPD5222FG30A"},
+ /* 120-129 */
+ { TUNER_XC2028, "Xceive XC3028"},
+ { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK5"},
+ { TUNER_ABSENT, "Philips FQD1216LME"},
+ { TUNER_ABSENT, "Conexant CX24118A"},
+ { TUNER_ABSENT, "TCL DMF11WIP"},
+ { TUNER_ABSENT, "TCL MFNM05_4H_E"},
+ { TUNER_ABSENT, "TCL MNM05_4H_E"},
+ { TUNER_ABSENT, "TCL MPE05_2H_E"},
+ { TUNER_ABSENT, "TCL MQNM05_4_U"},
+ { TUNER_ABSENT, "TCL M2523_5NH_E"},
+ /* 130-139 */
+ { TUNER_ABSENT, "TCL M2523_3DBH_E"},
+ { TUNER_ABSENT, "TCL M2523_3DIH_E"},
+ { TUNER_ABSENT, "TCL MFPE05_2_U"},
+ { TUNER_PHILIPS_FMD1216MEX_MK3, "Philips FMD1216MEX"},
+ { TUNER_ABSENT, "Philips FRH2036B"},
+ { TUNER_ABSENT, "Panasonic ENGF75_01GF"},
+ { TUNER_ABSENT, "MaxLinear MXL5005"},
+ { TUNER_ABSENT, "MaxLinear MXL5003"},
+ { TUNER_ABSENT, "Xceive XC2028"},
+ { TUNER_ABSENT, "Microtune MT2131"},
+ /* 140-149 */
+ { TUNER_ABSENT, "Philips 8275A_8295"},
+ { TUNER_ABSENT, "TCL MF02GIP_5N_E"},
+ { TUNER_ABSENT, "TCL MF02GIP_3DB_E"},
+ { TUNER_ABSENT, "TCL MF02GIP_3DI_E"},
+ { TUNER_ABSENT, "Microtune MT2266"},
+ { TUNER_ABSENT, "TCL MF10WPP_4N_E"},
+ { TUNER_ABSENT, "LG TAPQ_H702F"},
+ { TUNER_ABSENT, "TCL M09WPP_4N_E"},
+ { TUNER_ABSENT, "MaxLinear MXL5005_v2"},
+ { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"},
+ /* 150-159 */
+ { TUNER_XC5000, "Xceive XC5000"},
+ { TUNER_ABSENT, "Xceive XC3028L"},
+ { TUNER_ABSENT, "NXP 18271C2_716x"},
+ { TUNER_ABSENT, "Xceive XC4000"},
+ { TUNER_ABSENT, "Dibcom 7070"},
+ { TUNER_PHILIPS_TDA8290, "NXP 18271C2"},
+ { TUNER_ABSENT, "Siano SMS1010"},
+ { TUNER_ABSENT, "Siano SMS1150"},
+ { TUNER_ABSENT, "MaxLinear 5007"},
+ { TUNER_ABSENT, "TCL M09WPP_2P_E"},
+ /* 160-169 */
+ { TUNER_ABSENT, "Siano SMS1180"},
+ { TUNER_ABSENT, "Maxim_MAX2165"},
+ { TUNER_ABSENT, "Siano SMS1140"},
+ { TUNER_ABSENT, "Siano SMS1150 B1"},
+ { TUNER_ABSENT, "MaxLinear 111"},
+ { TUNER_ABSENT, "Dibcom 7770"},
+ { TUNER_ABSENT, "Siano SMS1180VNS"},
+ { TUNER_ABSENT, "Siano SMS1184"},
+ { TUNER_PHILIPS_FQ1236_MK5, "TCL M30WTP-4N-E"},
+ { TUNER_ABSENT, "TCL_M11WPP_2PN_E"},
+ /* 170-179 */
+ { TUNER_ABSENT, "MaxLinear 301"},
+ { TUNER_ABSENT, "Mirics MSi001"},
+ { TUNER_ABSENT, "MaxLinear MxL241SF"},
+ { TUNER_XC5000C, "Xceive XC5000C"},
+ { TUNER_ABSENT, "Montage M68TS2020"},
+ { TUNER_ABSENT, "Siano SMS1530"},
+ { TUNER_ABSENT, "Dibcom 7090"},
+ { TUNER_ABSENT, "Xceive XC5200C"},
+ { TUNER_ABSENT, "NXP 18273"},
+ { TUNER_ABSENT, "Montage M88TS2022"},
+ /* 180-188 */
+ { TUNER_ABSENT, "NXP 18272M"},
+ { TUNER_ABSENT, "NXP 18272S"},
+
+ { TUNER_ABSENT, "Mirics MSi003"},
+ { TUNER_ABSENT, "MaxLinear MxL256"},
+ { TUNER_ABSENT, "SiLabs Si2158"},
+ { TUNER_ABSENT, "SiLabs Si2178"},
+ { TUNER_ABSENT, "SiLabs Si2157"},
+ { TUNER_ABSENT, "SiLabs Si2177"},
+ { TUNER_ABSENT, "ITE IT9137FN"},
+};
+
+/* Use TVEEPROM_AUDPROC_INTERNAL for those audio 'chips' that are
+ * internal to a video chip, i.e. not a separate audio chip. */
+static const struct {
+ u32 id;
+ const char * const name;
+} audio_ic[] = {
+ /* 0-4 */
+ { TVEEPROM_AUDPROC_NONE, "None" },
+ { TVEEPROM_AUDPROC_OTHER, "TEA6300" },
+ { TVEEPROM_AUDPROC_OTHER, "TEA6320" },
+ { TVEEPROM_AUDPROC_OTHER, "TDA9850" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3400C" },
+ /* 5-9 */
+ { TVEEPROM_AUDPROC_MSP, "MSP3410D" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3415" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3430" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3438" },
+ { TVEEPROM_AUDPROC_OTHER, "CS5331" },
+ /* 10-14 */
+ { TVEEPROM_AUDPROC_MSP, "MSP3435" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3440" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3445" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3411" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3416" },
+ /* 15-19 */
+ { TVEEPROM_AUDPROC_MSP, "MSP3425" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3451" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3418" },
+ { TVEEPROM_AUDPROC_OTHER, "Type 0x12" },
+ { TVEEPROM_AUDPROC_OTHER, "OKI7716" },
+ /* 20-24 */
+ { TVEEPROM_AUDPROC_MSP, "MSP4410" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4420" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4440" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4450" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4408" },
+ /* 25-29 */
+ { TVEEPROM_AUDPROC_MSP, "MSP4418" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4428" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4448" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4458" },
+ { TVEEPROM_AUDPROC_MSP, "Type 0x1d" },
+ /* 30-34 */
+ { TVEEPROM_AUDPROC_INTERNAL, "CX880" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX881" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX883" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX882" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX25840" },
+ /* 35-39 */
+ { TVEEPROM_AUDPROC_INTERNAL, "CX25841" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX25842" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX25843" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23418" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23885" },
+ /* 40-44 */
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23888" },
+ { TVEEPROM_AUDPROC_INTERNAL, "SAA7131" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23887" },
+ { TVEEPROM_AUDPROC_INTERNAL, "SAA7164" },
+ { TVEEPROM_AUDPROC_INTERNAL, "AU8522" },
+ /* 45-49 */
+ { TVEEPROM_AUDPROC_INTERNAL, "AVF4910B" },
+ { TVEEPROM_AUDPROC_INTERNAL, "SAA7231" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23102" },
+ { TVEEPROM_AUDPROC_INTERNAL, "SAA7163" },
+ { TVEEPROM_AUDPROC_OTHER, "AK4113" },
+ /* 50-52 */
+ { TVEEPROM_AUDPROC_OTHER, "CS5340" },
+ { TVEEPROM_AUDPROC_OTHER, "CS8416" },
+ { TVEEPROM_AUDPROC_OTHER, "CX20810" },
+};
+
+/* This list is supplied by Hauppauge. Thanks! */
+static const char *decoderIC[] = {
+ /* 0-4 */
+ "None", "BT815", "BT817", "BT819", "BT815A",
+ /* 5-9 */
+ "BT817A", "BT819A", "BT827", "BT829", "BT848",
+ /* 10-14 */
+ "BT848A", "BT849A", "BT829A", "BT827A", "BT878",
+ /* 15-19 */
+ "BT879", "BT880", "VPX3226E", "SAA7114", "SAA7115",
+ /* 20-24 */
+ "CX880", "CX881", "CX883", "SAA7111", "SAA7113",
+ /* 25-29 */
+ "CX882", "TVP5150A", "CX25840", "CX25841", "CX25842",
+ /* 30-34 */
+ "CX25843", "CX23418", "NEC61153", "CX23885", "CX23888",
+ /* 35-39 */
+ "SAA7131", "CX25837", "CX23887", "CX23885A", "CX23887A",
+ /* 40-44 */
+ "SAA7164", "CX23885B", "AU8522", "ADV7401", "AVF4910B",
+ /* 45-49 */
+ "SAA7231", "CX23102", "SAA7163", "ADV7441A", "ADV7181C",
+ /* 50-53 */
+ "CX25836", "TDA9955", "TDA19977", "ADV7842"
+};
+
+static int hasRadioTuner(int tunerType)
+{
+ switch (tunerType) {
+ case 18: /* PNPEnv_TUNER_FR1236_MK2 */
+ case 23: /* PNPEnv_TUNER_FM1236 */
+ case 38: /* PNPEnv_TUNER_FMR1236 */
+ case 16: /* PNPEnv_TUNER_FR1216_MK2 */
+ case 19: /* PNPEnv_TUNER_FR1246_MK2 */
+ case 21: /* PNPEnv_TUNER_FM1216 */
+ case 24: /* PNPEnv_TUNER_FM1246 */
+ case 17: /* PNPEnv_TUNER_FR1216MF_MK2 */
+ case 22: /* PNPEnv_TUNER_FM1216MF */
+ case 20: /* PNPEnv_TUNER_FR1256_MK2 */
+ case 25: /* PNPEnv_TUNER_FM1256 */
+ case 33: /* PNPEnv_TUNER_4039FR5 */
+ case 42: /* PNPEnv_TUNER_4009FR5 */
+ case 52: /* PNPEnv_TUNER_4049FM5 */
+ case 54: /* PNPEnv_TUNER_4049FM5_AltI2C */
+ case 44: /* PNPEnv_TUNER_4009FN5 */
+ case 31: /* PNPEnv_TUNER_TCPB9085P */
+ case 30: /* PNPEnv_TUNER_TCPN9085D */
+ case 46: /* PNPEnv_TUNER_TP18NSR01F */
+ case 47: /* PNPEnv_TUNER_TP18PSB01D */
+ case 49: /* PNPEnv_TUNER_TAPC_I001D */
+ case 60: /* PNPEnv_TUNER_TAPE_S001D_MK3 */
+ case 57: /* PNPEnv_TUNER_FM1216ME_MK3 */
+ case 59: /* PNPEnv_TUNER_FM1216MP_MK3 */
+ case 58: /* PNPEnv_TUNER_FM1236_MK3 */
+ case 68: /* PNPEnv_TUNER_TAPE_H001F_MK3 */
+ case 61: /* PNPEnv_TUNER_TAPE_M001D_MK3 */
+ case 78: /* PNPEnv_TUNER_TDA8275C1_8290_FM */
+ case 89: /* PNPEnv_TUNER_TCL_MFPE05_2 */
+ case 92: /* PNPEnv_TUNER_PHILIPS_FQ1236A_MK4 */
+ case 105:
+ return 1;
+ }
+ return 0;
+}
+
+void tveeprom_hauppauge_analog(struct tveeprom *tvee,
+ unsigned char *eeprom_data)
+{
+ /* ----------------------------------------------
+ ** The hauppauge eeprom format is tagged
+ **
+ ** if packet[0] == 0x84, then packet[0..1] == length
+ ** else length = packet[0] & 3f;
+ ** if packet[0] & f8 == f8, then EOD and packet[1] == checksum
+ **
+ ** In our (ivtv) case we're interested in the following:
+ ** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuner)
+ ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into
+ ** hauppauge_tuner_fmt)
+ ** radio: tag [00].{last} or [0e].00 (bitmask. bit2=FM)
+ ** audio proc: tag [02].01 or [05].00 (mask with 0x7f)
+ ** decoder proc: tag [09].01)
+
+ ** Fun info:
+ ** model: tag [00].07-08 or [06].00-01
+ ** revision: tag [00].09-0b or [06].04-06
+ ** serial#: tag [01].05-07 or [04].04-06
+
+ ** # of inputs/outputs ???
+ */
+
+ int i, j, len, done, beenhere, tag, start;
+
+ int tuner1 = 0, t_format1 = 0, audioic = -1;
+ const char *t_name1 = NULL;
+ const char *t_fmt_name1[8] = { " none", "", "", "", "", "", "", "" };
+
+ int tuner2 = 0, t_format2 = 0;
+ const char *t_name2 = NULL;
+ const char *t_fmt_name2[8] = { " none", "", "", "", "", "", "", "" };
+
+ memset(tvee, 0, sizeof(*tvee));
+ tvee->tuner_type = TUNER_ABSENT;
+ tvee->tuner2_type = TUNER_ABSENT;
+
+ done = len = beenhere = 0;
+
+ /* Different eeprom start offsets for em28xx, cx2388x and cx23418 */
+ if (eeprom_data[0] == 0x1a &&
+ eeprom_data[1] == 0xeb &&
+ eeprom_data[2] == 0x67 &&
+ eeprom_data[3] == 0x95)
+ start = 0xa0; /* Generic em28xx offset */
+ else if ((eeprom_data[0] & 0xe1) == 0x01 &&
+ eeprom_data[1] == 0x00 &&
+ eeprom_data[2] == 0x00 &&
+ eeprom_data[8] == 0x84)
+ start = 8; /* Generic cx2388x offset */
+ else if (eeprom_data[1] == 0x70 &&
+ eeprom_data[2] == 0x00 &&
+ eeprom_data[4] == 0x74 &&
+ eeprom_data[8] == 0x84)
+ start = 8; /* Generic cx23418 offset (models 74xxx) */
+ else
+ start = 0;
+
+ for (i = start; !done && i < 256; i += len) {
+ if (eeprom_data[i] == 0x84) {
+ len = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8);
+ i += 3;
+ } else if ((eeprom_data[i] & 0xf0) == 0x70) {
+ if (eeprom_data[i] & 0x08) {
+ /* verify checksum! */
+ done = 1;
+ break;
+ }
+ len = eeprom_data[i] & 0x07;
+ ++i;
+ } else {
+ pr_warn("Encountered bad packet header [%02x]. Corrupt or not a Hauppauge eeprom.\n",
+ eeprom_data[i]);
+ return;
+ }
+
+ pr_debug("Tag [%02x] + %d bytes: %*ph\n",
+ eeprom_data[i], len - 1, len, &eeprom_data[i]);
+
+ /* process by tag */
+ tag = eeprom_data[i];
+ switch (tag) {
+ case 0x00:
+ /* tag: 'Comprehensive' */
+ tuner1 = eeprom_data[i+6];
+ t_format1 = eeprom_data[i+5];
+ tvee->has_radio = eeprom_data[i+len-1];
+ /* old style tag, don't know how to detect
+ IR presence, mark as unknown. */
+ tvee->has_ir = 0;
+ tvee->model =
+ eeprom_data[i+8] +
+ (eeprom_data[i+9] << 8);
+ tvee->revision = eeprom_data[i+10] +
+ (eeprom_data[i+11] << 8) +
+ (eeprom_data[i+12] << 16);
+ break;
+
+ case 0x01:
+ /* tag: 'SerialID' */
+ tvee->serial_number =
+ eeprom_data[i+6] +
+ (eeprom_data[i+7] << 8) +
+ (eeprom_data[i+8] << 16);
+ break;
+
+ case 0x02:
+ /* tag 'AudioInfo'
+ Note mask with 0x7F, high bit used on some older models
+ to indicate 4052 mux was removed in favor of using MSP
+ inputs directly. */
+ audioic = eeprom_data[i+2] & 0x7f;
+ if (audioic < ARRAY_SIZE(audio_ic))
+ tvee->audio_processor = audio_ic[audioic].id;
+ else
+ tvee->audio_processor = TVEEPROM_AUDPROC_OTHER;
+ break;
+
+ /* case 0x03: tag 'EEInfo' */
+
+ case 0x04:
+ /* tag 'SerialID2' */
+ tvee->serial_number =
+ eeprom_data[i+5] +
+ (eeprom_data[i+6] << 8) +
+ (eeprom_data[i+7] << 16)+
+ (eeprom_data[i+8] << 24);
+
+ if (eeprom_data[i + 8] == 0xf0) {
+ tvee->MAC_address[0] = 0x00;
+ tvee->MAC_address[1] = 0x0D;
+ tvee->MAC_address[2] = 0xFE;
+ tvee->MAC_address[3] = eeprom_data[i + 7];
+ tvee->MAC_address[4] = eeprom_data[i + 6];
+ tvee->MAC_address[5] = eeprom_data[i + 5];
+ tvee->has_MAC_address = 1;
+ }
+ break;
+
+ case 0x05:
+ /* tag 'Audio2'
+ Note mask with 0x7F, high bit used on some older models
+ to indicate 4052 mux was removed in favor of using MSP
+ inputs directly. */
+ audioic = eeprom_data[i+1] & 0x7f;
+ if (audioic < ARRAY_SIZE(audio_ic))
+ tvee->audio_processor = audio_ic[audioic].id;
+ else
+ tvee->audio_processor = TVEEPROM_AUDPROC_OTHER;
+
+ break;
+
+ case 0x06:
+ /* tag 'ModelRev' */
+ tvee->model =
+ eeprom_data[i + 1] +
+ (eeprom_data[i + 2] << 8) +
+ (eeprom_data[i + 3] << 16) +
+ (eeprom_data[i + 4] << 24);
+ tvee->revision =
+ eeprom_data[i + 5] +
+ (eeprom_data[i + 6] << 8) +
+ (eeprom_data[i + 7] << 16);
+ break;
+
+ case 0x07:
+ /* tag 'Details': according to Hauppauge not interesting
+ on any PCI-era or later boards. */
+ break;
+
+ /* there is no tag 0x08 defined */
+
+ case 0x09:
+ /* tag 'Video' */
+ tvee->decoder_processor = eeprom_data[i + 1];
+ break;
+
+ case 0x0a:
+ /* tag 'Tuner' */
+ if (beenhere == 0) {
+ tuner1 = eeprom_data[i + 2];
+ t_format1 = eeprom_data[i + 1];
+ beenhere = 1;
+ } else {
+ /* a second (radio) tuner may be present */
+ tuner2 = eeprom_data[i + 2];
+ t_format2 = eeprom_data[i + 1];
+ /* not a TV tuner? */
+ if (t_format2 == 0)
+ tvee->has_radio = 1; /* must be radio */
+ }
+ break;
+
+ case 0x0b:
+ /* tag 'Inputs': according to Hauppauge this is specific
+ to each driver family, so no good assumptions can be
+ made. */
+ break;
+
+ /* case 0x0c: tag 'Balun' */
+ /* case 0x0d: tag 'Teletext' */
+
+ case 0x0e:
+ /* tag: 'Radio' */
+ tvee->has_radio = eeprom_data[i+1];
+ break;
+
+ case 0x0f:
+ /* tag 'IRInfo' */
+ tvee->has_ir = 1 | (eeprom_data[i+1] << 1);
+ break;
+
+ /* case 0x10: tag 'VBIInfo' */
+ /* case 0x11: tag 'QCInfo' */
+ /* case 0x12: tag 'InfoBits' */
+
+ default:
+ pr_debug("Not sure what to do with tag [%02x]\n",
+ tag);
+ /* dump the rest of the packet? */
+ }
+ }
+
+ if (!done) {
+ pr_warn("Ran out of data!\n");
+ return;
+ }
+
+ if (tvee->revision != 0) {
+ tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f);
+ tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f);
+ tvee->rev_str[2] = 32 + ((tvee->revision >> 6) & 0x3f);
+ tvee->rev_str[3] = 32 + (tvee->revision & 0x3f);
+ tvee->rev_str[4] = 0;
+ }
+
+ if (hasRadioTuner(tuner1) && !tvee->has_radio) {
+ pr_info("The eeprom says no radio is present, but the tuner type\n");
+ pr_info("indicates otherwise. I will assume that radio is present.\n");
+ tvee->has_radio = 1;
+ }
+
+ if (tuner1 < ARRAY_SIZE(hauppauge_tuner)) {
+ tvee->tuner_type = hauppauge_tuner[tuner1].id;
+ t_name1 = hauppauge_tuner[tuner1].name;
+ } else {
+ t_name1 = "unknown";
+ }
+
+ if (tuner2 < ARRAY_SIZE(hauppauge_tuner)) {
+ tvee->tuner2_type = hauppauge_tuner[tuner2].id;
+ t_name2 = hauppauge_tuner[tuner2].name;
+ } else {
+ t_name2 = "unknown";
+ }
+
+ tvee->tuner_hauppauge_model = tuner1;
+ tvee->tuner2_hauppauge_model = tuner2;
+ tvee->tuner_formats = 0;
+ tvee->tuner2_formats = 0;
+ for (i = j = 0; i < 8; i++) {
+ if (t_format1 & (1 << i)) {
+ tvee->tuner_formats |= hauppauge_tuner_fmt[i].id;
+ t_fmt_name1[j++] = hauppauge_tuner_fmt[i].name;
+ }
+ }
+ for (i = j = 0; i < 8; i++) {
+ if (t_format2 & (1 << i)) {
+ tvee->tuner2_formats |= hauppauge_tuner_fmt[i].id;
+ t_fmt_name2[j++] = hauppauge_tuner_fmt[i].name;
+ }
+ }
+
+ pr_info("Hauppauge model %d, rev %s, serial# %u\n",
+ tvee->model, tvee->rev_str, tvee->serial_number);
+ if (tvee->has_MAC_address == 1)
+ pr_info("MAC address is %pM\n", tvee->MAC_address);
+ pr_info("tuner model is %s (idx %d, type %d)\n",
+ t_name1, tuner1, tvee->tuner_type);
+ pr_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n",
+ t_fmt_name1[0], t_fmt_name1[1], t_fmt_name1[2],
+ t_fmt_name1[3], t_fmt_name1[4], t_fmt_name1[5],
+ t_fmt_name1[6], t_fmt_name1[7], t_format1);
+ if (tuner2)
+ pr_info("second tuner model is %s (idx %d, type %d)\n",
+ t_name2, tuner2, tvee->tuner2_type);
+ if (t_format2)
+ pr_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n",
+ t_fmt_name2[0], t_fmt_name2[1], t_fmt_name2[2],
+ t_fmt_name2[3], t_fmt_name2[4], t_fmt_name2[5],
+ t_fmt_name2[6], t_fmt_name2[7], t_format2);
+ if (audioic < 0) {
+ pr_info("audio processor is unknown (no idx)\n");
+ tvee->audio_processor = TVEEPROM_AUDPROC_OTHER;
+ } else {
+ if (audioic < ARRAY_SIZE(audio_ic))
+ pr_info("audio processor is %s (idx %d)\n",
+ audio_ic[audioic].name, audioic);
+ else
+ pr_info("audio processor is unknown (idx %d)\n",
+ audioic);
+ }
+ if (tvee->decoder_processor)
+ pr_info("decoder processor is %s (idx %d)\n",
+ STRM(decoderIC, tvee->decoder_processor),
+ tvee->decoder_processor);
+ if (tvee->has_ir)
+ pr_info("has %sradio, has %sIR receiver, has %sIR transmitter\n",
+ tvee->has_radio ? "" : "no ",
+ (tvee->has_ir & 2) ? "" : "no ",
+ (tvee->has_ir & 4) ? "" : "no ");
+ else
+ pr_info("has %sradio\n",
+ tvee->has_radio ? "" : "no ");
+}
+EXPORT_SYMBOL(tveeprom_hauppauge_analog);
+
+/* ----------------------------------------------------------------------- */
+/* generic helper functions */
+
+int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len)
+{
+ unsigned char buf;
+ int err;
+
+ buf = 0;
+ err = i2c_master_send(c, &buf, 1);
+ if (err != 1) {
+ pr_info("Huh, no eeprom present (err=%d)?\n", err);
+ return -1;
+ }
+ err = i2c_master_recv(c, eedata, len);
+ if (err != len) {
+ pr_warn("i2c eeprom read error (err=%d)\n", err);
+ return -1;
+ }
+
+ print_hex_dump_debug("full 256-byte eeprom dump:", DUMP_PREFIX_NONE,
+ 16, 1, eedata, len, true);
+ return 0;
+}
+EXPORT_SYMBOL(tveeprom_read);
diff --git a/drivers/media/common/uvc.c b/drivers/media/common/uvc.c
new file mode 100644
index 000000000000..1ad4604474ac
--- /dev/null
+++ b/drivers/media/common/uvc.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/usb/uvc.h>
+#include <linux/videodev2.h>
+
+/* ------------------------------------------------------------------------
+ * Video formats
+ */
+
+static const struct uvc_format_desc uvc_fmts[] = {
+ {
+ .guid = UVC_GUID_FORMAT_YUY2,
+ .fcc = V4L2_PIX_FMT_YUYV,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_YUY2_ISIGHT,
+ .fcc = V4L2_PIX_FMT_YUYV,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_NV12,
+ .fcc = V4L2_PIX_FMT_NV12,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_MJPEG,
+ .fcc = V4L2_PIX_FMT_MJPEG,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_YV12,
+ .fcc = V4L2_PIX_FMT_YVU420,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_I420,
+ .fcc = V4L2_PIX_FMT_YUV420,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_M420,
+ .fcc = V4L2_PIX_FMT_M420,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_UYVY,
+ .fcc = V4L2_PIX_FMT_UYVY,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y800,
+ .fcc = V4L2_PIX_FMT_GREY,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y8,
+ .fcc = V4L2_PIX_FMT_GREY,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_D3DFMT_L8,
+ .fcc = V4L2_PIX_FMT_GREY,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_KSMEDIA_L8_IR,
+ .fcc = V4L2_PIX_FMT_GREY,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y10,
+ .fcc = V4L2_PIX_FMT_Y10,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y12,
+ .fcc = V4L2_PIX_FMT_Y12,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y16,
+ .fcc = V4L2_PIX_FMT_Y16,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_BY8,
+ .fcc = V4L2_PIX_FMT_SBGGR8,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_BA81,
+ .fcc = V4L2_PIX_FMT_SBGGR8,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_GBRG,
+ .fcc = V4L2_PIX_FMT_SGBRG8,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_GRBG,
+ .fcc = V4L2_PIX_FMT_SGRBG8,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_RGGB,
+ .fcc = V4L2_PIX_FMT_SRGGB8,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_RGBP,
+ .fcc = V4L2_PIX_FMT_RGB565,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_D3DFMT_R5G6B5,
+ .fcc = V4L2_PIX_FMT_RGB565,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_BGR3,
+ .fcc = V4L2_PIX_FMT_BGR24,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_BGR4,
+ .fcc = V4L2_PIX_FMT_XBGR32,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_H264,
+ .fcc = V4L2_PIX_FMT_H264,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_H265,
+ .fcc = V4L2_PIX_FMT_HEVC,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y8I,
+ .fcc = V4L2_PIX_FMT_Y8I,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y12I,
+ .fcc = V4L2_PIX_FMT_Y12I,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Y16I,
+ .fcc = V4L2_PIX_FMT_Y16I,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_Z16,
+ .fcc = V4L2_PIX_FMT_Z16,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_RW10,
+ .fcc = V4L2_PIX_FMT_SRGGB10P,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_BG16,
+ .fcc = V4L2_PIX_FMT_SBGGR16,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_GB16,
+ .fcc = V4L2_PIX_FMT_SGBRG16,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_RG16,
+ .fcc = V4L2_PIX_FMT_SRGGB16,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_GR16,
+ .fcc = V4L2_PIX_FMT_SGRBG16,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_INVZ,
+ .fcc = V4L2_PIX_FMT_Z16,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_INVI,
+ .fcc = V4L2_PIX_FMT_Y10,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_INZI,
+ .fcc = V4L2_PIX_FMT_INZI,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_CNF4,
+ .fcc = V4L2_PIX_FMT_CNF4,
+ },
+ {
+ .guid = UVC_GUID_FORMAT_HEVC,
+ .fcc = V4L2_PIX_FMT_HEVC,
+ },
+};
+
+const struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16])
+{
+ unsigned int len = ARRAY_SIZE(uvc_fmts);
+ unsigned int i;
+
+ for (i = 0; i < len; ++i) {
+ if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
+ return &uvc_fmts[i];
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(uvc_format_by_guid);
+
+MODULE_DESCRIPTION("USB Video Class common code");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/v4l2-tpg/Kconfig b/drivers/media/common/v4l2-tpg/Kconfig
new file mode 100644
index 000000000000..7ec4efd12e86
--- /dev/null
+++ b/drivers/media/common/v4l2-tpg/Kconfig
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_V4L2_TPG
+ tristate
diff --git a/drivers/media/common/v4l2-tpg/Makefile b/drivers/media/common/v4l2-tpg/Makefile
new file mode 100644
index 000000000000..f6278ca66147
--- /dev/null
+++ b/drivers/media/common/v4l2-tpg/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+v4l2-tpg-objs := v4l2-tpg-core.o v4l2-tpg-colors.o
+
+obj-$(CONFIG_VIDEO_V4L2_TPG) += v4l2-tpg.o
diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c
new file mode 100644
index 000000000000..a4341205c197
--- /dev/null
+++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c
@@ -0,0 +1,1409 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * v4l2-tpg-colors.c - A table that converts colors to various colorspaces
+ *
+ * The test pattern generator uses the tpg_colors for its test patterns.
+ * For testing colorspaces the first 8 colors of that table need to be
+ * converted to their equivalent in the target colorspace.
+ *
+ * The tpg_csc_colors[] table is the result of that conversion and since
+ * it is precalculated the colorspace conversion is just a simple table
+ * lookup.
+ *
+ * This source also contains the code used to generate the tpg_csc_colors
+ * table. Run the following command to compile it:
+ *
+ * gcc v4l2-tpg-colors.c -DCOMPILE_APP -o gen-colors -lm
+ *
+ * and run the utility.
+ *
+ * Note that the converted colors are in the range 0x000-0xff0 (so times 16)
+ * in order to preserve precision.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <linux/videodev2.h>
+#include <media/tpg/v4l2-tpg.h>
+
+/* sRGB colors with range [0-255] */
+const struct tpg_rbg_color8 tpg_colors[TPG_COLOR_MAX] = {
+ /*
+ * Colors to test colorspace conversion: converting these colors
+ * to other colorspaces will never lead to out-of-gamut colors.
+ */
+ { 191, 191, 191 }, /* TPG_COLOR_CSC_WHITE */
+ { 191, 191, 50 }, /* TPG_COLOR_CSC_YELLOW */
+ { 50, 191, 191 }, /* TPG_COLOR_CSC_CYAN */
+ { 50, 191, 50 }, /* TPG_COLOR_CSC_GREEN */
+ { 191, 50, 191 }, /* TPG_COLOR_CSC_MAGENTA */
+ { 191, 50, 50 }, /* TPG_COLOR_CSC_RED */
+ { 50, 50, 191 }, /* TPG_COLOR_CSC_BLUE */
+ { 50, 50, 50 }, /* TPG_COLOR_CSC_BLACK */
+
+ /* 75% colors */
+ { 191, 191, 0 }, /* TPG_COLOR_75_YELLOW */
+ { 0, 191, 191 }, /* TPG_COLOR_75_CYAN */
+ { 0, 191, 0 }, /* TPG_COLOR_75_GREEN */
+ { 191, 0, 191 }, /* TPG_COLOR_75_MAGENTA */
+ { 191, 0, 0 }, /* TPG_COLOR_75_RED */
+ { 0, 0, 191 }, /* TPG_COLOR_75_BLUE */
+
+ /* 100% colors */
+ { 255, 255, 255 }, /* TPG_COLOR_100_WHITE */
+ { 255, 255, 0 }, /* TPG_COLOR_100_YELLOW */
+ { 0, 255, 255 }, /* TPG_COLOR_100_CYAN */
+ { 0, 255, 0 }, /* TPG_COLOR_100_GREEN */
+ { 255, 0, 255 }, /* TPG_COLOR_100_MAGENTA */
+ { 255, 0, 0 }, /* TPG_COLOR_100_RED */
+ { 0, 0, 255 }, /* TPG_COLOR_100_BLUE */
+ { 0, 0, 0 }, /* TPG_COLOR_100_BLACK */
+
+ { 0, 0, 0 }, /* TPG_COLOR_RANDOM placeholder */
+};
+
+#ifndef COMPILE_APP
+
+/* Generated table */
+const unsigned short tpg_rec709_to_linear[255 * 16 + 1] = {
+ 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7,
+ 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10,
+ 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14,
+ 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18,
+ 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 20, 21, 21, 21,
+ 21, 22, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 25,
+ 25, 25, 25, 26, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28,
+ 28, 29, 29, 29, 29, 30, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32,
+ 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35,
+ 36, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 38, 39, 39,
+ 39, 39, 40, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 42,
+ 43, 43, 43, 43, 44, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46,
+ 46, 46, 47, 47, 47, 47, 48, 48, 48, 48, 48, 49, 49, 49, 49, 50,
+ 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 52, 53, 53, 53,
+ 53, 54, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 56, 56, 56, 57,
+ 57, 57, 57, 58, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60,
+ 60, 61, 61, 61, 61, 62, 62, 62, 62, 62, 63, 63, 63, 63, 64, 64,
+ 64, 64, 64, 65, 65, 65, 65, 66, 66, 66, 66, 66, 67, 67, 67, 67,
+ 68, 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 70, 71, 71,
+ 71, 71, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, 74, 74, 74, 74,
+ 74, 75, 75, 75, 75, 76, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78,
+ 78, 78, 79, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82,
+ 82, 82, 82, 82, 83, 83, 83, 83, 84, 84, 84, 84, 85, 85, 85, 85,
+ 86, 86, 86, 86, 87, 87, 87, 87, 88, 88, 88, 88, 89, 89, 89, 89,
+ 90, 90, 90, 90, 91, 91, 91, 91, 92, 92, 92, 92, 93, 93, 93, 93,
+ 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96, 96, 97, 97, 97, 97,
+ 98, 98, 98, 98, 99, 99, 99, 99, 100, 100, 100, 101, 101, 101, 101, 102,
+ 102, 102, 102, 103, 103, 103, 103, 104, 104, 104, 105, 105, 105, 105, 106, 106,
+ 106, 106, 107, 107, 107, 107, 108, 108, 108, 109, 109, 109, 109, 110, 110, 110,
+ 111, 111, 111, 111, 112, 112, 112, 112, 113, 113, 113, 114, 114, 114, 114, 115,
+ 115, 115, 116, 116, 116, 116, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119,
+ 120, 120, 120, 120, 121, 121, 121, 122, 122, 122, 123, 123, 123, 123, 124, 124,
+ 124, 125, 125, 125, 125, 126, 126, 126, 127, 127, 127, 128, 128, 128, 128, 129,
+ 129, 129, 130, 130, 130, 131, 131, 131, 132, 132, 132, 132, 133, 133, 133, 134,
+ 134, 134, 135, 135, 135, 136, 136, 136, 136, 137, 137, 137, 138, 138, 138, 139,
+ 139, 139, 140, 140, 140, 141, 141, 141, 142, 142, 142, 142, 143, 143, 143, 144,
+ 144, 144, 145, 145, 145, 146, 146, 146, 147, 147, 147, 148, 148, 148, 149, 149,
+ 149, 150, 150, 150, 151, 151, 151, 152, 152, 152, 153, 153, 153, 154, 154, 154,
+ 155, 155, 155, 156, 156, 156, 157, 157, 157, 158, 158, 158, 159, 159, 159, 160,
+ 160, 160, 161, 161, 161, 162, 162, 162, 163, 163, 163, 164, 164, 164, 165, 165,
+ 165, 166, 166, 167, 167, 167, 168, 168, 168, 169, 169, 169, 170, 170, 170, 171,
+ 171, 171, 172, 172, 172, 173, 173, 174, 174, 174, 175, 175, 175, 176, 176, 176,
+ 177, 177, 177, 178, 178, 179, 179, 179, 180, 180, 180, 181, 181, 181, 182, 182,
+ 183, 183, 183, 184, 184, 184, 185, 185, 186, 186, 186, 187, 187, 187, 188, 188,
+ 188, 189, 189, 190, 190, 190, 191, 191, 191, 192, 192, 193, 193, 193, 194, 194,
+ 194, 195, 195, 196, 196, 196, 197, 197, 198, 198, 198, 199, 199, 199, 200, 200,
+ 201, 201, 201, 202, 202, 203, 203, 203, 204, 204, 204, 205, 205, 206, 206, 206,
+ 207, 207, 208, 208, 208, 209, 209, 210, 210, 210, 211, 211, 212, 212, 212, 213,
+ 213, 214, 214, 214, 215, 215, 216, 216, 216, 217, 217, 218, 218, 218, 219, 219,
+ 220, 220, 220, 221, 221, 222, 222, 222, 223, 223, 224, 224, 224, 225, 225, 226,
+ 226, 227, 227, 227, 228, 228, 229, 229, 229, 230, 230, 231, 231, 232, 232, 232,
+ 233, 233, 234, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239,
+ 240, 240, 240, 241, 241, 242, 242, 243, 243, 243, 244, 244, 245, 245, 246, 246,
+ 246, 247, 247, 248, 248, 249, 249, 249, 250, 250, 251, 251, 252, 252, 252, 253,
+ 253, 254, 254, 255, 255, 256, 256, 256, 257, 257, 258, 258, 259, 259, 260, 260,
+ 260, 261, 261, 262, 262, 263, 263, 264, 264, 264, 265, 265, 266, 266, 267, 267,
+ 268, 268, 269, 269, 269, 270, 270, 271, 271, 272, 272, 273, 273, 274, 274, 274,
+ 275, 275, 276, 276, 277, 277, 278, 278, 279, 279, 279, 280, 280, 281, 281, 282,
+ 282, 283, 283, 284, 284, 285, 285, 286, 286, 286, 287, 287, 288, 288, 289, 289,
+ 290, 290, 291, 291, 292, 292, 293, 293, 294, 294, 295, 295, 295, 296, 296, 297,
+ 297, 298, 298, 299, 299, 300, 300, 301, 301, 302, 302, 303, 303, 304, 304, 305,
+ 305, 306, 306, 307, 307, 308, 308, 309, 309, 309, 310, 310, 311, 311, 312, 312,
+ 313, 313, 314, 314, 315, 315, 316, 316, 317, 317, 318, 318, 319, 319, 320, 320,
+ 321, 321, 322, 322, 323, 323, 324, 324, 325, 325, 326, 326, 327, 327, 328, 328,
+ 329, 329, 330, 330, 331, 331, 332, 332, 333, 333, 334, 335, 335, 336, 336, 337,
+ 337, 338, 338, 339, 339, 340, 340, 341, 341, 342, 342, 343, 343, 344, 344, 345,
+ 345, 346, 346, 347, 347, 348, 348, 349, 349, 350, 351, 351, 352, 352, 353, 353,
+ 354, 354, 355, 355, 356, 356, 357, 357, 358, 358, 359, 360, 360, 361, 361, 362,
+ 362, 363, 363, 364, 364, 365, 365, 366, 366, 367, 368, 368, 369, 369, 370, 370,
+ 371, 371, 372, 372, 373, 373, 374, 375, 375, 376, 376, 377, 377, 378, 378, 379,
+ 379, 380, 381, 381, 382, 382, 383, 383, 384, 384, 385, 386, 386, 387, 387, 388,
+ 388, 389, 389, 390, 391, 391, 392, 392, 393, 393, 394, 394, 395, 396, 396, 397,
+ 397, 398, 398, 399, 399, 400, 401, 401, 402, 402, 403, 403, 404, 405, 405, 406,
+ 406, 407, 407, 408, 409, 409, 410, 410, 411, 411, 412, 413, 413, 414, 414, 415,
+ 415, 416, 417, 417, 418, 418, 419, 419, 420, 421, 421, 422, 422, 423, 424, 424,
+ 425, 425, 426, 426, 427, 428, 428, 429, 429, 430, 431, 431, 432, 432, 433, 433,
+ 434, 435, 435, 436, 436, 437, 438, 438, 439, 439, 440, 441, 441, 442, 442, 443,
+ 444, 444, 445, 445, 446, 447, 447, 448, 448, 449, 450, 450, 451, 451, 452, 453,
+ 453, 454, 454, 455, 456, 456, 457, 457, 458, 459, 459, 460, 460, 461, 462, 462,
+ 463, 463, 464, 465, 465, 466, 467, 467, 468, 468, 469, 470, 470, 471, 471, 472,
+ 473, 473, 474, 475, 475, 476, 476, 477, 478, 478, 479, 480, 480, 481, 481, 482,
+ 483, 483, 484, 485, 485, 486, 486, 487, 488, 488, 489, 490, 490, 491, 491, 492,
+ 493, 493, 494, 495, 495, 496, 497, 497, 498, 498, 499, 500, 500, 501, 502, 502,
+ 503, 504, 504, 505, 505, 506, 507, 507, 508, 509, 509, 510, 511, 511, 512, 513,
+ 513, 514, 514, 515, 516, 516, 517, 518, 518, 519, 520, 520, 521, 522, 522, 523,
+ 524, 524, 525, 526, 526, 527, 528, 528, 529, 529, 530, 531, 531, 532, 533, 533,
+ 534, 535, 535, 536, 537, 537, 538, 539, 539, 540, 541, 541, 542, 543, 543, 544,
+ 545, 545, 546, 547, 547, 548, 549, 549, 550, 551, 551, 552, 553, 553, 554, 555,
+ 555, 556, 557, 557, 558, 559, 560, 560, 561, 562, 562, 563, 564, 564, 565, 566,
+ 566, 567, 568, 568, 569, 570, 570, 571, 572, 572, 573, 574, 575, 575, 576, 577,
+ 577, 578, 579, 579, 580, 581, 581, 582, 583, 584, 584, 585, 586, 586, 587, 588,
+ 588, 589, 590, 590, 591, 592, 593, 593, 594, 595, 595, 596, 597, 598, 598, 599,
+ 600, 600, 601, 602, 602, 603, 604, 605, 605, 606, 607, 607, 608, 609, 610, 610,
+ 611, 612, 612, 613, 614, 615, 615, 616, 617, 617, 618, 619, 620, 620, 621, 622,
+ 622, 623, 624, 625, 625, 626, 627, 627, 628, 629, 630, 630, 631, 632, 632, 633,
+ 634, 635, 635, 636, 637, 638, 638, 639, 640, 640, 641, 642, 643, 643, 644, 645,
+ 646, 646, 647, 648, 649, 649, 650, 651, 652, 652, 653, 654, 654, 655, 656, 657,
+ 657, 658, 659, 660, 660, 661, 662, 663, 663, 664, 665, 666, 666, 667, 668, 669,
+ 669, 670, 671, 672, 672, 673, 674, 675, 675, 676, 677, 678, 678, 679, 680, 681,
+ 681, 682, 683, 684, 684, 685, 686, 687, 687, 688, 689, 690, 690, 691, 692, 693,
+ 694, 694, 695, 696, 697, 697, 698, 699, 700, 700, 701, 702, 703, 703, 704, 705,
+ 706, 707, 707, 708, 709, 710, 710, 711, 712, 713, 714, 714, 715, 716, 717, 717,
+ 718, 719, 720, 720, 721, 722, 723, 724, 724, 725, 726, 727, 728, 728, 729, 730,
+ 731, 731, 732, 733, 734, 735, 735, 736, 737, 738, 739, 739, 740, 741, 742, 742,
+ 743, 744, 745, 746, 746, 747, 748, 749, 750, 750, 751, 752, 753, 754, 754, 755,
+ 756, 757, 758, 758, 759, 760, 761, 762, 762, 763, 764, 765, 766, 766, 767, 768,
+ 769, 770, 771, 771, 772, 773, 774, 775, 775, 776, 777, 778, 779, 779, 780, 781,
+ 782, 783, 783, 784, 785, 786, 787, 788, 788, 789, 790, 791, 792, 793, 793, 794,
+ 795, 796, 797, 797, 798, 799, 800, 801, 802, 802, 803, 804, 805, 806, 807, 807,
+ 808, 809, 810, 811, 812, 812, 813, 814, 815, 816, 817, 817, 818, 819, 820, 821,
+ 822, 822, 823, 824, 825, 826, 827, 827, 828, 829, 830, 831, 832, 832, 833, 834,
+ 835, 836, 837, 838, 838, 839, 840, 841, 842, 843, 843, 844, 845, 846, 847, 848,
+ 849, 849, 850, 851, 852, 853, 854, 855, 855, 856, 857, 858, 859, 860, 861, 861,
+ 862, 863, 864, 865, 866, 867, 867, 868, 869, 870, 871, 872, 873, 873, 874, 875,
+ 876, 877, 878, 879, 880, 880, 881, 882, 883, 884, 885, 886, 887, 887, 888, 889,
+ 890, 891, 892, 893, 894, 894, 895, 896, 897, 898, 899, 900, 901, 901, 902, 903,
+ 904, 905, 906, 907, 908, 909, 909, 910, 911, 912, 913, 914, 915, 916, 916, 917,
+ 918, 919, 920, 921, 922, 923, 924, 925, 925, 926, 927, 928, 929, 930, 931, 932,
+ 933, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 942, 943, 944, 945, 946,
+ 947, 948, 949, 950, 951, 952, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961,
+ 962, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 973, 974, 975,
+ 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 985, 986, 987, 988, 989, 990,
+ 991, 992, 993, 994, 995, 996, 997, 998, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005,
+ 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020,
+ 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1030, 1031, 1032, 1033, 1034, 1035,
+ 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1050,
+ 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066,
+ 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1078, 1079, 1080, 1081,
+ 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097,
+ 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113,
+ 1114, 1115, 1116, 1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129,
+ 1130, 1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1143, 1144, 1145,
+ 1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 1154, 1155, 1156, 1157, 1158, 1159, 1160, 1161,
+ 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1170, 1171, 1172, 1173, 1174, 1175, 1176, 1177,
+ 1178, 1179, 1180, 1181, 1182, 1183, 1184, 1185, 1186, 1187, 1188, 1189, 1190, 1191, 1193, 1194,
+ 1195, 1196, 1197, 1198, 1199, 1200, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210,
+ 1211, 1212, 1213, 1214, 1215, 1216, 1217, 1218, 1219, 1220, 1221, 1223, 1224, 1225, 1226, 1227,
+ 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, 1236, 1237, 1238, 1239, 1240, 1241, 1242, 1243,
+ 1245, 1246, 1247, 1248, 1249, 1250, 1251, 1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259, 1260,
+ 1261, 1262, 1264, 1265, 1266, 1267, 1268, 1269, 1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277,
+ 1278, 1280, 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1295,
+ 1296, 1297, 1298, 1299, 1300, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1309, 1310, 1311, 1312,
+ 1313, 1314, 1315, 1316, 1317, 1318, 1319, 1320, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329,
+ 1330, 1331, 1332, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1341, 1342, 1343, 1345, 1346, 1347,
+ 1348, 1349, 1350, 1351, 1352, 1353, 1354, 1356, 1357, 1358, 1359, 1360, 1361, 1362, 1363, 1364,
+ 1365, 1367, 1368, 1369, 1370, 1371, 1372, 1373, 1374, 1375, 1377, 1378, 1379, 1380, 1381, 1382,
+ 1383, 1384, 1385, 1387, 1388, 1389, 1390, 1391, 1392, 1393, 1394, 1396, 1397, 1398, 1399, 1400,
+ 1401, 1402, 1403, 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412, 1414, 1415, 1416, 1417, 1418,
+ 1419, 1420, 1421, 1423, 1424, 1425, 1426, 1427, 1428, 1429, 1431, 1432, 1433, 1434, 1435, 1436,
+ 1437, 1439, 1440, 1441, 1442, 1443, 1444, 1445, 1446, 1448, 1449, 1450, 1451, 1452, 1453, 1455,
+ 1456, 1457, 1458, 1459, 1460, 1461, 1463, 1464, 1465, 1466, 1467, 1468, 1469, 1471, 1472, 1473,
+ 1474, 1475, 1476, 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1486, 1487, 1488, 1489, 1490, 1491,
+ 1493, 1494, 1495, 1496, 1497, 1498, 1500, 1501, 1502, 1503, 1504, 1505, 1507, 1508, 1509, 1510,
+ 1511, 1512, 1514, 1515, 1516, 1517, 1518, 1519, 1521, 1522, 1523, 1524, 1525, 1527, 1528, 1529,
+ 1530, 1531, 1532, 1534, 1535, 1536, 1537, 1538, 1540, 1541, 1542, 1543, 1544, 1545, 1547, 1548,
+ 1549, 1550, 1551, 1553, 1554, 1555, 1556, 1557, 1559, 1560, 1561, 1562, 1563, 1564, 1566, 1567,
+ 1568, 1569, 1570, 1572, 1573, 1574, 1575, 1576, 1578, 1579, 1580, 1581, 1582, 1584, 1585, 1586,
+ 1587, 1588, 1590, 1591, 1592, 1593, 1594, 1596, 1597, 1598, 1599, 1601, 1602, 1603, 1604, 1605,
+ 1607, 1608, 1609, 1610, 1611, 1613, 1614, 1615, 1616, 1617, 1619, 1620, 1621, 1622, 1624, 1625,
+ 1626, 1627, 1628, 1630, 1631, 1632, 1633, 1635, 1636, 1637, 1638, 1639, 1641, 1642, 1643, 1644,
+ 1646, 1647, 1648, 1649, 1650, 1652, 1653, 1654, 1655, 1657, 1658, 1659, 1660, 1662, 1663, 1664,
+ 1665, 1667, 1668, 1669, 1670, 1671, 1673, 1674, 1675, 1676, 1678, 1679, 1680, 1681, 1683, 1684,
+ 1685, 1686, 1688, 1689, 1690, 1691, 1693, 1694, 1695, 1696, 1698, 1699, 1700, 1701, 1703, 1704,
+ 1705, 1706, 1708, 1709, 1710, 1711, 1713, 1714, 1715, 1716, 1718, 1719, 1720, 1721, 1723, 1724,
+ 1725, 1726, 1728, 1729, 1730, 1731, 1733, 1734, 1735, 1737, 1738, 1739, 1740, 1742, 1743, 1744,
+ 1745, 1747, 1748, 1749, 1750, 1752, 1753, 1754, 1756, 1757, 1758, 1759, 1761, 1762, 1763, 1764,
+ 1766, 1767, 1768, 1770, 1771, 1772, 1773, 1775, 1776, 1777, 1778, 1780, 1781, 1782, 1784, 1785,
+ 1786, 1787, 1789, 1790, 1791, 1793, 1794, 1795, 1796, 1798, 1799, 1800, 1802, 1803, 1804, 1806,
+ 1807, 1808, 1809, 1811, 1812, 1813, 1815, 1816, 1817, 1818, 1820, 1821, 1822, 1824, 1825, 1826,
+ 1828, 1829, 1830, 1831, 1833, 1834, 1835, 1837, 1838, 1839, 1841, 1842, 1843, 1844, 1846, 1847,
+ 1848, 1850, 1851, 1852, 1854, 1855, 1856, 1858, 1859, 1860, 1862, 1863, 1864, 1865, 1867, 1868,
+ 1869, 1871, 1872, 1873, 1875, 1876, 1877, 1879, 1880, 1881, 1883, 1884, 1885, 1887, 1888, 1889,
+ 1891, 1892, 1893, 1894, 1896, 1897, 1898, 1900, 1901, 1902, 1904, 1905, 1906, 1908, 1909, 1910,
+ 1912, 1913, 1914, 1916, 1917, 1918, 1920, 1921, 1922, 1924, 1925, 1926, 1928, 1929, 1930, 1932,
+ 1933, 1935, 1936, 1937, 1939, 1940, 1941, 1943, 1944, 1945, 1947, 1948, 1949, 1951, 1952, 1953,
+ 1955, 1956, 1957, 1959, 1960, 1961, 1963, 1964, 1965, 1967, 1968, 1970, 1971, 1972, 1974, 1975,
+ 1976, 1978, 1979, 1980, 1982, 1983, 1984, 1986, 1987, 1989, 1990, 1991, 1993, 1994, 1995, 1997,
+ 1998, 1999, 2001, 2002, 2004, 2005, 2006, 2008, 2009, 2010, 2012, 2013, 2015, 2016, 2017, 2019,
+ 2020, 2021, 2023, 2024, 2026, 2027, 2028, 2030, 2031, 2032, 2034, 2035, 2037, 2038, 2039, 2041,
+ 2042, 2043, 2045, 2046, 2048, 2049, 2050, 2052, 2053, 2055, 2056, 2057, 2059, 2060, 2061, 2063,
+ 2064, 2066, 2067, 2068, 2070, 2071, 2073, 2074, 2075, 2077, 2078, 2080, 2081, 2082, 2084, 2085,
+ 2087, 2088, 2089, 2091, 2092, 2094, 2095, 2096, 2098, 2099, 2101, 2102, 2103, 2105, 2106, 2108,
+ 2109, 2110, 2112, 2113, 2115, 2116, 2117, 2119, 2120, 2122, 2123, 2124, 2126, 2127, 2129, 2130,
+ 2132, 2133, 2134, 2136, 2137, 2139, 2140, 2141, 2143, 2144, 2146, 2147, 2149, 2150, 2151, 2153,
+ 2154, 2156, 2157, 2159, 2160, 2161, 2163, 2164, 2166, 2167, 2169, 2170, 2171, 2173, 2174, 2176,
+ 2177, 2179, 2180, 2181, 2183, 2184, 2186, 2187, 2189, 2190, 2191, 2193, 2194, 2196, 2197, 2199,
+ 2200, 2202, 2203, 2204, 2206, 2207, 2209, 2210, 2212, 2213, 2214, 2216, 2217, 2219, 2220, 2222,
+ 2223, 2225, 2226, 2228, 2229, 2230, 2232, 2233, 2235, 2236, 2238, 2239, 2241, 2242, 2243, 2245,
+ 2246, 2248, 2249, 2251, 2252, 2254, 2255, 2257, 2258, 2260, 2261, 2262, 2264, 2265, 2267, 2268,
+ 2270, 2271, 2273, 2274, 2276, 2277, 2279, 2280, 2282, 2283, 2284, 2286, 2287, 2289, 2290, 2292,
+ 2293, 2295, 2296, 2298, 2299, 2301, 2302, 2304, 2305, 2307, 2308, 2310, 2311, 2312, 2314, 2315,
+ 2317, 2318, 2320, 2321, 2323, 2324, 2326, 2327, 2329, 2330, 2332, 2333, 2335, 2336, 2338, 2339,
+ 2341, 2342, 2344, 2345, 2347, 2348, 2350, 2351, 2353, 2354, 2356, 2357, 2359, 2360, 2362, 2363,
+ 2365, 2366, 2368, 2369, 2371, 2372, 2374, 2375, 2377, 2378, 2380, 2381, 2383, 2384, 2386, 2387,
+ 2389, 2390, 2392, 2393, 2395, 2396, 2398, 2399, 2401, 2402, 2404, 2405, 2407, 2408, 2410, 2411,
+ 2413, 2414, 2416, 2417, 2419, 2420, 2422, 2423, 2425, 2426, 2428, 2429, 2431, 2433, 2434, 2436,
+ 2437, 2439, 2440, 2442, 2443, 2445, 2446, 2448, 2449, 2451, 2452, 2454, 2455, 2457, 2458, 2460,
+ 2462, 2463, 2465, 2466, 2468, 2469, 2471, 2472, 2474, 2475, 2477, 2478, 2480, 2481, 2483, 2485,
+ 2486, 2488, 2489, 2491, 2492, 2494, 2495, 2497, 2498, 2500, 2502, 2503, 2505, 2506, 2508, 2509,
+ 2511, 2512, 2514, 2515, 2517, 2519, 2520, 2522, 2523, 2525, 2526, 2528, 2529, 2531, 2533, 2534,
+ 2536, 2537, 2539, 2540, 2542, 2543, 2545, 2547, 2548, 2550, 2551, 2553, 2554, 2556, 2557, 2559,
+ 2561, 2562, 2564, 2565, 2567, 2568, 2570, 2572, 2573, 2575, 2576, 2578, 2579, 2581, 2583, 2584,
+ 2586, 2587, 2589, 2590, 2592, 2594, 2595, 2597, 2598, 2600, 2601, 2603, 2605, 2606, 2608, 2609,
+ 2611, 2613, 2614, 2616, 2617, 2619, 2620, 2622, 2624, 2625, 2627, 2628, 2630, 2632, 2633, 2635,
+ 2636, 2638, 2640, 2641, 2643, 2644, 2646, 2647, 2649, 2651, 2652, 2654, 2655, 2657, 2659, 2660,
+ 2662, 2663, 2665, 2667, 2668, 2670, 2671, 2673, 2675, 2676, 2678, 2679, 2681, 2683, 2684, 2686,
+ 2687, 2689, 2691, 2692, 2694, 2696, 2697, 2699, 2700, 2702, 2704, 2705, 2707, 2708, 2710, 2712,
+ 2713, 2715, 2716, 2718, 2720, 2721, 2723, 2725, 2726, 2728, 2729, 2731, 2733, 2734, 2736, 2738,
+ 2739, 2741, 2742, 2744, 2746, 2747, 2749, 2751, 2752, 2754, 2755, 2757, 2759, 2760, 2762, 2764,
+ 2765, 2767, 2769, 2770, 2772, 2773, 2775, 2777, 2778, 2780, 2782, 2783, 2785, 2787, 2788, 2790,
+ 2791, 2793, 2795, 2796, 2798, 2800, 2801, 2803, 2805, 2806, 2808, 2810, 2811, 2813, 2814, 2816,
+ 2818, 2819, 2821, 2823, 2824, 2826, 2828, 2829, 2831, 2833, 2834, 2836, 2838, 2839, 2841, 2843,
+ 2844, 2846, 2848, 2849, 2851, 2853, 2854, 2856, 2857, 2859, 2861, 2862, 2864, 2866, 2867, 2869,
+ 2871, 2872, 2874, 2876, 2877, 2879, 2881, 2882, 2884, 2886, 2888, 2889, 2891, 2893, 2894, 2896,
+ 2898, 2899, 2901, 2903, 2904, 2906, 2908, 2909, 2911, 2913, 2914, 2916, 2918, 2919, 2921, 2923,
+ 2924, 2926, 2928, 2929, 2931, 2933, 2935, 2936, 2938, 2940, 2941, 2943, 2945, 2946, 2948, 2950,
+ 2951, 2953, 2955, 2956, 2958, 2960, 2962, 2963, 2965, 2967, 2968, 2970, 2972, 2973, 2975, 2977,
+ 2979, 2980, 2982, 2984, 2985, 2987, 2989, 2990, 2992, 2994, 2996, 2997, 2999, 3001, 3002, 3004,
+ 3006, 3008, 3009, 3011, 3013, 3014, 3016, 3018, 3020, 3021, 3023, 3025, 3026, 3028, 3030, 3032,
+ 3033, 3035, 3037, 3038, 3040, 3042, 3044, 3045, 3047, 3049, 3050, 3052, 3054, 3056, 3057, 3059,
+ 3061, 3063, 3064, 3066, 3068, 3069, 3071, 3073, 3075, 3076, 3078, 3080, 3082, 3083, 3085, 3087,
+ 3089, 3090, 3092, 3094, 3095, 3097, 3099, 3101, 3102, 3104, 3106, 3108, 3109, 3111, 3113, 3115,
+ 3116, 3118, 3120, 3122, 3123, 3125, 3127, 3129, 3130, 3132, 3134, 3136, 3137, 3139, 3141, 3143,
+ 3144, 3146, 3148, 3150, 3151, 3153, 3155, 3157, 3158, 3160, 3162, 3164, 3165, 3167, 3169, 3171,
+ 3172, 3174, 3176, 3178, 3179, 3181, 3183, 3185, 3187, 3188, 3190, 3192, 3194, 3195, 3197, 3199,
+ 3201, 3202, 3204, 3206, 3208, 3209, 3211, 3213, 3215, 3217, 3218, 3220, 3222, 3224, 3225, 3227,
+ 3229, 3231, 3233, 3234, 3236, 3238, 3240, 3241, 3243, 3245, 3247, 3249, 3250, 3252, 3254, 3256,
+ 3258, 3259, 3261, 3263, 3265, 3266, 3268, 3270, 3272, 3274, 3275, 3277, 3279, 3281, 3283, 3284,
+ 3286, 3288, 3290, 3292, 3293, 3295, 3297, 3299, 3301, 3302, 3304, 3306, 3308, 3310, 3311, 3313,
+ 3315, 3317, 3319, 3320, 3322, 3324, 3326, 3328, 3329, 3331, 3333, 3335, 3337, 3338, 3340, 3342,
+ 3344, 3346, 3348, 3349, 3351, 3353, 3355, 3357, 3358, 3360, 3362, 3364, 3366, 3368, 3369, 3371,
+ 3373, 3375, 3377, 3378, 3380, 3382, 3384, 3386, 3388, 3389, 3391, 3393, 3395, 3397, 3399, 3400,
+ 3402, 3404, 3406, 3408, 3410, 3411, 3413, 3415, 3417, 3419, 3421, 3422, 3424, 3426, 3428, 3430,
+ 3432, 3433, 3435, 3437, 3439, 3441, 3443, 3444, 3446, 3448, 3450, 3452, 3454, 3455, 3457, 3459,
+ 3461, 3463, 3465, 3467, 3468, 3470, 3472, 3474, 3476, 3478, 3480, 3481, 3483, 3485, 3487, 3489,
+ 3491, 3492, 3494, 3496, 3498, 3500, 3502, 3504, 3506, 3507, 3509, 3511, 3513, 3515, 3517, 3519,
+ 3520, 3522, 3524, 3526, 3528, 3530, 3532, 3533, 3535, 3537, 3539, 3541, 3543, 3545, 3547, 3548,
+ 3550, 3552, 3554, 3556, 3558, 3560, 3562, 3563, 3565, 3567, 3569, 3571, 3573, 3575, 3577, 3578,
+ 3580, 3582, 3584, 3586, 3588, 3590, 3592, 3594, 3595, 3597, 3599, 3601, 3603, 3605, 3607, 3609,
+ 3611, 3612, 3614, 3616, 3618, 3620, 3622, 3624, 3626, 3628, 3629, 3631, 3633, 3635, 3637, 3639,
+ 3641, 3643, 3645, 3647, 3648, 3650, 3652, 3654, 3656, 3658, 3660, 3662, 3664, 3666, 3667, 3669,
+ 3671, 3673, 3675, 3677, 3679, 3681, 3683, 3685, 3687, 3688, 3690, 3692, 3694, 3696, 3698, 3700,
+ 3702, 3704, 3706, 3708, 3710, 3711, 3713, 3715, 3717, 3719, 3721, 3723, 3725, 3727, 3729, 3731,
+ 3733, 3735, 3736, 3738, 3740, 3742, 3744, 3746, 3748, 3750, 3752, 3754, 3756, 3758, 3760, 3762,
+ 3764, 3765, 3767, 3769, 3771, 3773, 3775, 3777, 3779, 3781, 3783, 3785, 3787, 3789, 3791, 3793,
+ 3795, 3796, 3798, 3800, 3802, 3804, 3806, 3808, 3810, 3812, 3814, 3816, 3818, 3820, 3822, 3824,
+ 3826, 3828, 3830, 3832, 3833, 3835, 3837, 3839, 3841, 3843, 3845, 3847, 3849, 3851, 3853, 3855,
+ 3857, 3859, 3861, 3863, 3865, 3867, 3869, 3871, 3873, 3875, 3877, 3879, 3881, 3883, 3884, 3886,
+ 3888, 3890, 3892, 3894, 3896, 3898, 3900, 3902, 3904, 3906, 3908, 3910, 3912, 3914, 3916, 3918,
+ 3920, 3922, 3924, 3926, 3928, 3930, 3932, 3934, 3936, 3938, 3940, 3942, 3944, 3946, 3948, 3950,
+ 3952, 3954, 3956, 3958, 3960, 3962, 3964, 3966, 3968, 3970, 3972, 3974, 3976, 3978, 3980, 3982,
+ 3984, 3986, 3988, 3990, 3992, 3994, 3996, 3998, 4000, 4002, 4004, 4006, 4008, 4010, 4012, 4014,
+ 4016, 4018, 4020, 4022, 4024, 4026, 4028, 4030, 4032, 4034, 4036, 4038, 4040, 4042, 4044, 4046,
+ 4048, 4050, 4052, 4054, 4056, 4058, 4060, 4062, 4064, 4066, 4068, 4070, 4072, 4074, 4076, 4078,
+ 4080,
+};
+
+/* Generated table */
+const unsigned short tpg_linear_to_rec709[255 * 16 + 1] = {
+ 0, 5, 9, 14, 18, 22, 27, 32, 36, 41, 45, 50, 54, 59, 63, 68,
+ 72, 77, 81, 86, 90, 95, 99, 104, 108, 113, 117, 122, 126, 131, 135, 139,
+ 144, 149, 153, 158, 162, 167, 171, 176, 180, 185, 189, 194, 198, 203, 207, 212,
+ 216, 221, 225, 230, 234, 239, 243, 248, 252, 257, 261, 266, 270, 275, 279, 284,
+ 288, 293, 297, 302, 306, 311, 315, 320, 324, 328, 334, 338, 343, 347, 352, 356,
+ 360, 365, 369, 373, 377, 381, 386, 390, 394, 398, 402, 406, 410, 414, 418, 422,
+ 426, 430, 433, 437, 441, 445, 449, 452, 456, 460, 464, 467, 471, 475, 478, 482,
+ 485, 489, 492, 496, 499, 503, 506, 510, 513, 517, 520, 524, 527, 530, 534, 537,
+ 540, 544, 547, 550, 554, 557, 560, 563, 566, 570, 573, 576, 579, 582, 586, 589,
+ 592, 595, 598, 601, 604, 607, 610, 613, 616, 619, 622, 625, 628, 631, 634, 637,
+ 640, 643, 646, 649, 652, 655, 658, 660, 663, 666, 669, 672, 675, 677, 680, 683,
+ 686, 689, 691, 694, 697, 700, 702, 705, 708, 711, 713, 716, 719, 721, 724, 727,
+ 729, 732, 735, 737, 740, 743, 745, 748, 750, 753, 756, 758, 761, 763, 766, 768,
+ 771, 773, 776, 779, 781, 784, 786, 789, 791, 794, 796, 799, 801, 803, 806, 808,
+ 811, 813, 816, 818, 821, 823, 825, 828, 830, 833, 835, 837, 840, 842, 844, 847,
+ 849, 851, 854, 856, 858, 861, 863, 865, 868, 870, 872, 875, 877, 879, 881, 884,
+ 886, 888, 891, 893, 895, 897, 900, 902, 904, 906, 908, 911, 913, 915, 917, 919,
+ 922, 924, 926, 928, 930, 933, 935, 937, 939, 941, 943, 946, 948, 950, 952, 954,
+ 956, 958, 960, 963, 965, 967, 969, 971, 973, 975, 977, 979, 981, 984, 986, 988,
+ 990, 992, 994, 996, 998, 1000, 1002, 1004, 1006, 1008, 1010, 1012, 1014, 1016, 1018, 1020,
+ 1022, 1024, 1026, 1028, 1030, 1032, 1034, 1036, 1038, 1040, 1042, 1044, 1046, 1048, 1050, 1052,
+ 1054, 1056, 1058, 1060, 1062, 1064, 1066, 1068, 1069, 1071, 1073, 1075, 1077, 1079, 1081, 1083,
+ 1085, 1087, 1089, 1090, 1092, 1094, 1096, 1098, 1100, 1102, 1104, 1106, 1107, 1109, 1111, 1113,
+ 1115, 1117, 1119, 1120, 1122, 1124, 1126, 1128, 1130, 1131, 1133, 1135, 1137, 1139, 1141, 1142,
+ 1144, 1146, 1148, 1150, 1151, 1153, 1155, 1157, 1159, 1160, 1162, 1164, 1166, 1168, 1169, 1171,
+ 1173, 1175, 1176, 1178, 1180, 1182, 1184, 1185, 1187, 1189, 1191, 1192, 1194, 1196, 1198, 1199,
+ 1201, 1203, 1204, 1206, 1208, 1210, 1211, 1213, 1215, 1217, 1218, 1220, 1222, 1223, 1225, 1227,
+ 1228, 1230, 1232, 1234, 1235, 1237, 1239, 1240, 1242, 1244, 1245, 1247, 1249, 1250, 1252, 1254,
+ 1255, 1257, 1259, 1260, 1262, 1264, 1265, 1267, 1269, 1270, 1272, 1274, 1275, 1277, 1279, 1280,
+ 1282, 1283, 1285, 1287, 1288, 1290, 1292, 1293, 1295, 1296, 1298, 1300, 1301, 1303, 1305, 1306,
+ 1308, 1309, 1311, 1313, 1314, 1316, 1317, 1319, 1321, 1322, 1324, 1325, 1327, 1328, 1330, 1332,
+ 1333, 1335, 1336, 1338, 1339, 1341, 1343, 1344, 1346, 1347, 1349, 1350, 1352, 1354, 1355, 1357,
+ 1358, 1360, 1361, 1363, 1364, 1366, 1367, 1369, 1371, 1372, 1374, 1375, 1377, 1378, 1380, 1381,
+ 1383, 1384, 1386, 1387, 1389, 1390, 1392, 1393, 1395, 1396, 1398, 1399, 1401, 1402, 1404, 1405,
+ 1407, 1408, 1410, 1411, 1413, 1414, 1416, 1417, 1419, 1420, 1422, 1423, 1425, 1426, 1428, 1429,
+ 1431, 1432, 1434, 1435, 1437, 1438, 1440, 1441, 1442, 1444, 1445, 1447, 1448, 1450, 1451, 1453,
+ 1454, 1456, 1457, 1458, 1460, 1461, 1463, 1464, 1466, 1467, 1469, 1470, 1471, 1473, 1474, 1476,
+ 1477, 1479, 1480, 1481, 1483, 1484, 1486, 1487, 1489, 1490, 1491, 1493, 1494, 1496, 1497, 1498,
+ 1500, 1501, 1503, 1504, 1505, 1507, 1508, 1510, 1511, 1512, 1514, 1515, 1517, 1518, 1519, 1521,
+ 1522, 1524, 1525, 1526, 1528, 1529, 1531, 1532, 1533, 1535, 1536, 1537, 1539, 1540, 1542, 1543,
+ 1544, 1546, 1547, 1548, 1550, 1551, 1553, 1554, 1555, 1557, 1558, 1559, 1561, 1562, 1563, 1565,
+ 1566, 1567, 1569, 1570, 1571, 1573, 1574, 1576, 1577, 1578, 1580, 1581, 1582, 1584, 1585, 1586,
+ 1588, 1589, 1590, 1592, 1593, 1594, 1596, 1597, 1598, 1600, 1601, 1602, 1603, 1605, 1606, 1607,
+ 1609, 1610, 1611, 1613, 1614, 1615, 1617, 1618, 1619, 1621, 1622, 1623, 1624, 1626, 1627, 1628,
+ 1630, 1631, 1632, 1634, 1635, 1636, 1637, 1639, 1640, 1641, 1643, 1644, 1645, 1647, 1648, 1649,
+ 1650, 1652, 1653, 1654, 1655, 1657, 1658, 1659, 1661, 1662, 1663, 1664, 1666, 1667, 1668, 1670,
+ 1671, 1672, 1673, 1675, 1676, 1677, 1678, 1680, 1681, 1682, 1683, 1685, 1686, 1687, 1688, 1690,
+ 1691, 1692, 1693, 1695, 1696, 1697, 1698, 1700, 1701, 1702, 1703, 1705, 1706, 1707, 1708, 1710,
+ 1711, 1712, 1713, 1715, 1716, 1717, 1718, 1720, 1721, 1722, 1723, 1724, 1726, 1727, 1728, 1729,
+ 1731, 1732, 1733, 1734, 1736, 1737, 1738, 1739, 1740, 1742, 1743, 1744, 1745, 1746, 1748, 1749,
+ 1750, 1751, 1753, 1754, 1755, 1756, 1757, 1759, 1760, 1761, 1762, 1763, 1765, 1766, 1767, 1768,
+ 1769, 1771, 1772, 1773, 1774, 1775, 1777, 1778, 1779, 1780, 1781, 1783, 1784, 1785, 1786, 1787,
+ 1788, 1790, 1791, 1792, 1793, 1794, 1796, 1797, 1798, 1799, 1800, 1801, 1803, 1804, 1805, 1806,
+ 1807, 1809, 1810, 1811, 1812, 1813, 1814, 1816, 1817, 1818, 1819, 1820, 1821, 1823, 1824, 1825,
+ 1826, 1827, 1828, 1829, 1831, 1832, 1833, 1834, 1835, 1836, 1838, 1839, 1840, 1841, 1842, 1843,
+ 1845, 1846, 1847, 1848, 1849, 1850, 1851, 1853, 1854, 1855, 1856, 1857, 1858, 1859, 1861, 1862,
+ 1863, 1864, 1865, 1866, 1867, 1868, 1870, 1871, 1872, 1873, 1874, 1875, 1876, 1878, 1879, 1880,
+ 1881, 1882, 1883, 1884, 1885, 1887, 1888, 1889, 1890, 1891, 1892, 1893, 1894, 1896, 1897, 1898,
+ 1899, 1900, 1901, 1902, 1903, 1904, 1906, 1907, 1908, 1909, 1910, 1911, 1912, 1913, 1914, 1916,
+ 1917, 1918, 1919, 1920, 1921, 1922, 1923, 1924, 1925, 1927, 1928, 1929, 1930, 1931, 1932, 1933,
+ 1934, 1935, 1936, 1938, 1939, 1940, 1941, 1942, 1943, 1944, 1945, 1946, 1947, 1948, 1950, 1951,
+ 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960, 1961, 1963, 1964, 1965, 1966, 1967, 1968,
+ 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985,
+ 1986, 1987, 1988, 1989, 1990, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004, 2005, 2006, 2007, 2008, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
+ 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029, 2031, 2032, 2033, 2034, 2035, 2036,
+ 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047, 2048, 2049, 2050, 2051, 2052,
+ 2053, 2054, 2055, 2056, 2057, 2058, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068, 2069,
+ 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085,
+ 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099, 2100, 2101,
+ 2102, 2103, 2104, 2105, 2106, 2107, 2108, 2109, 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117,
+ 2118, 2119, 2120, 2121, 2122, 2123, 2124, 2125, 2126, 2127, 2128, 2129, 2130, 2131, 2132, 2133,
+ 2134, 2135, 2136, 2137, 2138, 2139, 2140, 2141, 2142, 2143, 2144, 2145, 2146, 2147, 2148, 2149,
+ 2150, 2151, 2152, 2153, 2154, 2155, 2156, 2157, 2158, 2159, 2160, 2161, 2162, 2163, 2164, 2165,
+ 2166, 2167, 2168, 2169, 2170, 2171, 2172, 2173, 2173, 2174, 2175, 2176, 2177, 2178, 2179, 2180,
+ 2181, 2182, 2183, 2184, 2185, 2186, 2187, 2188, 2189, 2190, 2191, 2192, 2193, 2194, 2195, 2196,
+ 2197, 2198, 2199, 2200, 2201, 2202, 2202, 2203, 2204, 2205, 2206, 2207, 2208, 2209, 2210, 2211,
+ 2212, 2213, 2214, 2215, 2216, 2217, 2218, 2219, 2220, 2221, 2222, 2223, 2224, 2224, 2225, 2226,
+ 2227, 2228, 2229, 2230, 2231, 2232, 2233, 2234, 2235, 2236, 2237, 2238, 2239, 2240, 2241, 2241,
+ 2242, 2243, 2244, 2245, 2246, 2247, 2248, 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257,
+ 2257, 2258, 2259, 2260, 2261, 2262, 2263, 2264, 2265, 2266, 2267, 2268, 2269, 2270, 2271, 2271,
+ 2272, 2273, 2274, 2275, 2276, 2277, 2278, 2279, 2280, 2281, 2282, 2283, 2283, 2284, 2285, 2286,
+ 2287, 2288, 2289, 2290, 2291, 2292, 2293, 2294, 2295, 2295, 2296, 2297, 2298, 2299, 2300, 2301,
+ 2302, 2303, 2304, 2305, 2306, 2306, 2307, 2308, 2309, 2310, 2311, 2312, 2313, 2314, 2315, 2316,
+ 2317, 2317, 2318, 2319, 2320, 2321, 2322, 2323, 2324, 2325, 2326, 2327, 2327, 2328, 2329, 2330,
+ 2331, 2332, 2333, 2334, 2335, 2336, 2336, 2337, 2338, 2339, 2340, 2341, 2342, 2343, 2344, 2345,
+ 2345, 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2354, 2355, 2356, 2357, 2358, 2359,
+ 2360, 2361, 2362, 2363, 2363, 2364, 2365, 2366, 2367, 2368, 2369, 2370, 2371, 2371, 2372, 2373,
+ 2374, 2375, 2376, 2377, 2378, 2379, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2386, 2387,
+ 2388, 2389, 2390, 2391, 2392, 2393, 2394, 2394, 2395, 2396, 2397, 2398, 2399, 2400, 2401, 2401,
+ 2402, 2403, 2404, 2405, 2406, 2407, 2408, 2408, 2409, 2410, 2411, 2412, 2413, 2414, 2415, 2415,
+ 2416, 2417, 2418, 2419, 2420, 2421, 2422, 2422, 2423, 2424, 2425, 2426, 2427, 2428, 2428, 2429,
+ 2430, 2431, 2432, 2433, 2434, 2435, 2435, 2436, 2437, 2438, 2439, 2440, 2441, 2441, 2442, 2443,
+ 2444, 2445, 2446, 2447, 2447, 2448, 2449, 2450, 2451, 2452, 2453, 2453, 2454, 2455, 2456, 2457,
+ 2458, 2459, 2459, 2460, 2461, 2462, 2463, 2464, 2465, 2465, 2466, 2467, 2468, 2469, 2470, 2471,
+ 2471, 2472, 2473, 2474, 2475, 2476, 2477, 2477, 2478, 2479, 2480, 2481, 2482, 2482, 2483, 2484,
+ 2485, 2486, 2487, 2488, 2488, 2489, 2490, 2491, 2492, 2493, 2493, 2494, 2495, 2496, 2497, 2498,
+ 2499, 2499, 2500, 2501, 2502, 2503, 2504, 2504, 2505, 2506, 2507, 2508, 2509, 2509, 2510, 2511,
+ 2512, 2513, 2514, 2514, 2515, 2516, 2517, 2518, 2519, 2519, 2520, 2521, 2522, 2523, 2524, 2524,
+ 2525, 2526, 2527, 2528, 2529, 2529, 2530, 2531, 2532, 2533, 2534, 2534, 2535, 2536, 2537, 2538,
+ 2539, 2539, 2540, 2541, 2542, 2543, 2544, 2544, 2545, 2546, 2547, 2548, 2548, 2549, 2550, 2551,
+ 2552, 2553, 2553, 2554, 2555, 2556, 2557, 2558, 2558, 2559, 2560, 2561, 2562, 2562, 2563, 2564,
+ 2565, 2566, 2567, 2567, 2568, 2569, 2570, 2571, 2571, 2572, 2573, 2574, 2575, 2576, 2576, 2577,
+ 2578, 2579, 2580, 2580, 2581, 2582, 2583, 2584, 2584, 2585, 2586, 2587, 2588, 2589, 2589, 2590,
+ 2591, 2592, 2593, 2593, 2594, 2595, 2596, 2597, 2597, 2598, 2599, 2600, 2601, 2601, 2602, 2603,
+ 2604, 2605, 2605, 2606, 2607, 2608, 2609, 2610, 2610, 2611, 2612, 2613, 2614, 2614, 2615, 2616,
+ 2617, 2618, 2618, 2619, 2620, 2621, 2622, 2622, 2623, 2624, 2625, 2626, 2626, 2627, 2628, 2629,
+ 2630, 2630, 2631, 2632, 2633, 2634, 2634, 2635, 2636, 2637, 2637, 2638, 2639, 2640, 2641, 2641,
+ 2642, 2643, 2644, 2645, 2645, 2646, 2647, 2648, 2649, 2649, 2650, 2651, 2652, 2653, 2653, 2654,
+ 2655, 2656, 2656, 2657, 2658, 2659, 2660, 2660, 2661, 2662, 2663, 2664, 2664, 2665, 2666, 2667,
+ 2668, 2668, 2669, 2670, 2671, 2671, 2672, 2673, 2674, 2675, 2675, 2676, 2677, 2678, 2678, 2679,
+ 2680, 2681, 2682, 2682, 2683, 2684, 2685, 2686, 2686, 2687, 2688, 2689, 2689, 2690, 2691, 2692,
+ 2693, 2693, 2694, 2695, 2696, 2696, 2697, 2698, 2699, 2700, 2700, 2701, 2702, 2703, 2703, 2704,
+ 2705, 2706, 2706, 2707, 2708, 2709, 2710, 2710, 2711, 2712, 2713, 2713, 2714, 2715, 2716, 2717,
+ 2717, 2718, 2719, 2720, 2720, 2721, 2722, 2723, 2723, 2724, 2725, 2726, 2727, 2727, 2728, 2729,
+ 2730, 2730, 2731, 2732, 2733, 2733, 2734, 2735, 2736, 2736, 2737, 2738, 2739, 2740, 2740, 2741,
+ 2742, 2743, 2743, 2744, 2745, 2746, 2746, 2747, 2748, 2749, 2749, 2750, 2751, 2752, 2752, 2753,
+ 2754, 2755, 2755, 2756, 2757, 2758, 2759, 2759, 2760, 2761, 2762, 2762, 2763, 2764, 2765, 2765,
+ 2766, 2767, 2768, 2768, 2769, 2770, 2771, 2771, 2772, 2773, 2774, 2774, 2775, 2776, 2777, 2777,
+ 2778, 2779, 2780, 2780, 2781, 2782, 2783, 2783, 2784, 2785, 2786, 2786, 2787, 2788, 2789, 2789,
+ 2790, 2791, 2792, 2792, 2793, 2794, 2795, 2795, 2796, 2797, 2798, 2798, 2799, 2800, 2801, 2801,
+ 2802, 2803, 2804, 2804, 2805, 2806, 2807, 2807, 2808, 2809, 2810, 2810, 2811, 2812, 2813, 2813,
+ 2814, 2815, 2815, 2816, 2817, 2818, 2818, 2819, 2820, 2821, 2821, 2822, 2823, 2824, 2824, 2825,
+ 2826, 2827, 2827, 2828, 2829, 2830, 2830, 2831, 2832, 2832, 2833, 2834, 2835, 2835, 2836, 2837,
+ 2838, 2838, 2839, 2840, 2841, 2841, 2842, 2843, 2844, 2844, 2845, 2846, 2846, 2847, 2848, 2849,
+ 2849, 2850, 2851, 2852, 2852, 2853, 2854, 2855, 2855, 2856, 2857, 2857, 2858, 2859, 2860, 2860,
+ 2861, 2862, 2863, 2863, 2864, 2865, 2865, 2866, 2867, 2868, 2868, 2869, 2870, 2871, 2871, 2872,
+ 2873, 2873, 2874, 2875, 2876, 2876, 2877, 2878, 2879, 2879, 2880, 2881, 2881, 2882, 2883, 2884,
+ 2884, 2885, 2886, 2886, 2887, 2888, 2889, 2889, 2890, 2891, 2892, 2892, 2893, 2894, 2894, 2895,
+ 2896, 2897, 2897, 2898, 2899, 2899, 2900, 2901, 2902, 2902, 2903, 2904, 2904, 2905, 2906, 2907,
+ 2907, 2908, 2909, 2909, 2910, 2911, 2912, 2912, 2913, 2914, 2914, 2915, 2916, 2917, 2917, 2918,
+ 2919, 2919, 2920, 2921, 2922, 2922, 2923, 2924, 2924, 2925, 2926, 2927, 2927, 2928, 2929, 2929,
+ 2930, 2931, 2932, 2932, 2933, 2934, 2934, 2935, 2936, 2937, 2937, 2938, 2939, 2939, 2940, 2941,
+ 2941, 2942, 2943, 2944, 2944, 2945, 2946, 2946, 2947, 2948, 2949, 2949, 2950, 2951, 2951, 2952,
+ 2953, 2953, 2954, 2955, 2956, 2956, 2957, 2958, 2958, 2959, 2960, 2961, 2961, 2962, 2963, 2963,
+ 2964, 2965, 2965, 2966, 2967, 2968, 2968, 2969, 2970, 2970, 2971, 2972, 2972, 2973, 2974, 2975,
+ 2975, 2976, 2977, 2977, 2978, 2979, 2979, 2980, 2981, 2982, 2982, 2983, 2984, 2984, 2985, 2986,
+ 2986, 2987, 2988, 2988, 2989, 2990, 2991, 2991, 2992, 2993, 2993, 2994, 2995, 2995, 2996, 2997,
+ 2998, 2998, 2999, 3000, 3000, 3001, 3002, 3002, 3003, 3004, 3004, 3005, 3006, 3006, 3007, 3008,
+ 3009, 3009, 3010, 3011, 3011, 3012, 3013, 3013, 3014, 3015, 3015, 3016, 3017, 3018, 3018, 3019,
+ 3020, 3020, 3021, 3022, 3022, 3023, 3024, 3024, 3025, 3026, 3026, 3027, 3028, 3029, 3029, 3030,
+ 3031, 3031, 3032, 3033, 3033, 3034, 3035, 3035, 3036, 3037, 3037, 3038, 3039, 3039, 3040, 3041,
+ 3042, 3042, 3043, 3044, 3044, 3045, 3046, 3046, 3047, 3048, 3048, 3049, 3050, 3050, 3051, 3052,
+ 3052, 3053, 3054, 3054, 3055, 3056, 3056, 3057, 3058, 3059, 3059, 3060, 3061, 3061, 3062, 3063,
+ 3063, 3064, 3065, 3065, 3066, 3067, 3067, 3068, 3069, 3069, 3070, 3071, 3071, 3072, 3073, 3073,
+ 3074, 3075, 3075, 3076, 3077, 3077, 3078, 3079, 3079, 3080, 3081, 3081, 3082, 3083, 3084, 3084,
+ 3085, 3086, 3086, 3087, 3088, 3088, 3089, 3090, 3090, 3091, 3092, 3092, 3093, 3094, 3094, 3095,
+ 3096, 3096, 3097, 3098, 3098, 3099, 3100, 3100, 3101, 3102, 3102, 3103, 3104, 3104, 3105, 3106,
+ 3106, 3107, 3108, 3108, 3109, 3110, 3110, 3111, 3112, 3112, 3113, 3114, 3114, 3115, 3116, 3116,
+ 3117, 3118, 3118, 3119, 3120, 3120, 3121, 3122, 3122, 3123, 3124, 3124, 3125, 3126, 3126, 3127,
+ 3128, 3128, 3129, 3130, 3130, 3131, 3132, 3132, 3133, 3134, 3134, 3135, 3135, 3136, 3137, 3137,
+ 3138, 3139, 3139, 3140, 3141, 3141, 3142, 3143, 3143, 3144, 3145, 3145, 3146, 3147, 3147, 3148,
+ 3149, 3149, 3150, 3151, 3151, 3152, 3153, 3153, 3154, 3155, 3155, 3156, 3157, 3157, 3158, 3159,
+ 3159, 3160, 3160, 3161, 3162, 3162, 3163, 3164, 3164, 3165, 3166, 3166, 3167, 3168, 3168, 3169,
+ 3170, 3170, 3171, 3172, 3172, 3173, 3174, 3174, 3175, 3175, 3176, 3177, 3177, 3178, 3179, 3179,
+ 3180, 3181, 3181, 3182, 3183, 3183, 3184, 3185, 3185, 3186, 3187, 3187, 3188, 3188, 3189, 3190,
+ 3190, 3191, 3192, 3192, 3193, 3194, 3194, 3195, 3196, 3196, 3197, 3198, 3198, 3199, 3199, 3200,
+ 3201, 3201, 3202, 3203, 3203, 3204, 3205, 3205, 3206, 3207, 3207, 3208, 3209, 3209, 3210, 3210,
+ 3211, 3212, 3212, 3213, 3214, 3214, 3215, 3216, 3216, 3217, 3218, 3218, 3219, 3219, 3220, 3221,
+ 3221, 3222, 3223, 3223, 3224, 3225, 3225, 3226, 3227, 3227, 3228, 3228, 3229, 3230, 3230, 3231,
+ 3232, 3232, 3233, 3234, 3234, 3235, 3235, 3236, 3237, 3237, 3238, 3239, 3239, 3240, 3241, 3241,
+ 3242, 3242, 3243, 3244, 3244, 3245, 3246, 3246, 3247, 3248, 3248, 3249, 3249, 3250, 3251, 3251,
+ 3252, 3253, 3253, 3254, 3255, 3255, 3256, 3256, 3257, 3258, 3258, 3259, 3260, 3260, 3261, 3262,
+ 3262, 3263, 3263, 3264, 3265, 3265, 3266, 3267, 3267, 3268, 3268, 3269, 3270, 3270, 3271, 3272,
+ 3272, 3273, 3274, 3274, 3275, 3275, 3276, 3277, 3277, 3278, 3279, 3279, 3280, 3280, 3281, 3282,
+ 3282, 3283, 3284, 3284, 3285, 3285, 3286, 3287, 3287, 3288, 3289, 3289, 3290, 3290, 3291, 3292,
+ 3292, 3293, 3294, 3294, 3295, 3295, 3296, 3297, 3297, 3298, 3299, 3299, 3300, 3300, 3301, 3302,
+ 3302, 3303, 3304, 3304, 3305, 3305, 3306, 3307, 3307, 3308, 3309, 3309, 3310, 3310, 3311, 3312,
+ 3312, 3313, 3314, 3314, 3315, 3315, 3316, 3317, 3317, 3318, 3319, 3319, 3320, 3320, 3321, 3322,
+ 3322, 3323, 3323, 3324, 3325, 3325, 3326, 3327, 3327, 3328, 3328, 3329, 3330, 3330, 3331, 3332,
+ 3332, 3333, 3333, 3334, 3335, 3335, 3336, 3336, 3337, 3338, 3338, 3339, 3340, 3340, 3341, 3341,
+ 3342, 3343, 3343, 3344, 3345, 3345, 3346, 3346, 3347, 3348, 3348, 3349, 3349, 3350, 3351, 3351,
+ 3352, 3352, 3353, 3354, 3354, 3355, 3356, 3356, 3357, 3357, 3358, 3359, 3359, 3360, 3360, 3361,
+ 3362, 3362, 3363, 3364, 3364, 3365, 3365, 3366, 3367, 3367, 3368, 3368, 3369, 3370, 3370, 3371,
+ 3371, 3372, 3373, 3373, 3374, 3375, 3375, 3376, 3376, 3377, 3378, 3378, 3379, 3379, 3380, 3381,
+ 3381, 3382, 3382, 3383, 3384, 3384, 3385, 3385, 3386, 3387, 3387, 3388, 3389, 3389, 3390, 3390,
+ 3391, 3392, 3392, 3393, 3393, 3394, 3395, 3395, 3396, 3396, 3397, 3398, 3398, 3399, 3399, 3400,
+ 3401, 3401, 3402, 3402, 3403, 3404, 3404, 3405, 3405, 3406, 3407, 3407, 3408, 3408, 3409, 3410,
+ 3410, 3411, 3411, 3412, 3413, 3413, 3414, 3414, 3415, 3416, 3416, 3417, 3418, 3418, 3419, 3419,
+ 3420, 3421, 3421, 3422, 3422, 3423, 3424, 3424, 3425, 3425, 3426, 3427, 3427, 3428, 3428, 3429,
+ 3430, 3430, 3431, 3431, 3432, 3433, 3433, 3434, 3434, 3435, 3435, 3436, 3437, 3437, 3438, 3438,
+ 3439, 3440, 3440, 3441, 3441, 3442, 3443, 3443, 3444, 3444, 3445, 3446, 3446, 3447, 3447, 3448,
+ 3449, 3449, 3450, 3450, 3451, 3452, 3452, 3453, 3453, 3454, 3455, 3455, 3456, 3456, 3457, 3458,
+ 3458, 3459, 3459, 3460, 3461, 3461, 3462, 3462, 3463, 3463, 3464, 3465, 3465, 3466, 3466, 3467,
+ 3468, 3468, 3469, 3469, 3470, 3471, 3471, 3472, 3472, 3473, 3474, 3474, 3475, 3475, 3476, 3476,
+ 3477, 3478, 3478, 3479, 3479, 3480, 3481, 3481, 3482, 3482, 3483, 3484, 3484, 3485, 3485, 3486,
+ 3486, 3487, 3488, 3488, 3489, 3489, 3490, 3491, 3491, 3492, 3492, 3493, 3494, 3494, 3495, 3495,
+ 3496, 3496, 3497, 3498, 3498, 3499, 3499, 3500, 3501, 3501, 3502, 3502, 3503, 3504, 3504, 3505,
+ 3505, 3506, 3506, 3507, 3508, 3508, 3509, 3509, 3510, 3511, 3511, 3512, 3512, 3513, 3513, 3514,
+ 3515, 3515, 3516, 3516, 3517, 3518, 3518, 3519, 3519, 3520, 3520, 3521, 3522, 3522, 3523, 3523,
+ 3524, 3525, 3525, 3526, 3526, 3527, 3527, 3528, 3529, 3529, 3530, 3530, 3531, 3531, 3532, 3533,
+ 3533, 3534, 3534, 3535, 3536, 3536, 3537, 3537, 3538, 3538, 3539, 3540, 3540, 3541, 3541, 3542,
+ 3542, 3543, 3544, 3544, 3545, 3545, 3546, 3547, 3547, 3548, 3548, 3549, 3549, 3550, 3551, 3551,
+ 3552, 3552, 3553, 3553, 3554, 3555, 3555, 3556, 3556, 3557, 3557, 3558, 3559, 3559, 3560, 3560,
+ 3561, 3561, 3562, 3563, 3563, 3564, 3564, 3565, 3566, 3566, 3567, 3567, 3568, 3568, 3569, 3570,
+ 3570, 3571, 3571, 3572, 3572, 3573, 3574, 3574, 3575, 3575, 3576, 3576, 3577, 3578, 3578, 3579,
+ 3579, 3580, 3580, 3581, 3582, 3582, 3583, 3583, 3584, 3584, 3585, 3586, 3586, 3587, 3587, 3588,
+ 3588, 3589, 3590, 3590, 3591, 3591, 3592, 3592, 3593, 3594, 3594, 3595, 3595, 3596, 3596, 3597,
+ 3597, 3598, 3599, 3599, 3600, 3600, 3601, 3601, 3602, 3603, 3603, 3604, 3604, 3605, 3605, 3606,
+ 3607, 3607, 3608, 3608, 3609, 3609, 3610, 3611, 3611, 3612, 3612, 3613, 3613, 3614, 3615, 3615,
+ 3616, 3616, 3617, 3617, 3618, 3618, 3619, 3620, 3620, 3621, 3621, 3622, 3622, 3623, 3624, 3624,
+ 3625, 3625, 3626, 3626, 3627, 3627, 3628, 3629, 3629, 3630, 3630, 3631, 3631, 3632, 3633, 3633,
+ 3634, 3634, 3635, 3635, 3636, 3636, 3637, 3638, 3638, 3639, 3639, 3640, 3640, 3641, 3642, 3642,
+ 3643, 3643, 3644, 3644, 3645, 3645, 3646, 3647, 3647, 3648, 3648, 3649, 3649, 3650, 3650, 3651,
+ 3652, 3652, 3653, 3653, 3654, 3654, 3655, 3656, 3656, 3657, 3657, 3658, 3658, 3659, 3659, 3660,
+ 3661, 3661, 3662, 3662, 3663, 3663, 3664, 3664, 3665, 3666, 3666, 3667, 3667, 3668, 3668, 3669,
+ 3669, 3670, 3671, 3671, 3672, 3672, 3673, 3673, 3674, 3674, 3675, 3676, 3676, 3677, 3677, 3678,
+ 3678, 3679, 3679, 3680, 3681, 3681, 3682, 3682, 3683, 3683, 3684, 3684, 3685, 3686, 3686, 3687,
+ 3687, 3688, 3688, 3689, 3689, 3690, 3691, 3691, 3692, 3692, 3693, 3693, 3694, 3694, 3695, 3695,
+ 3696, 3697, 3697, 3698, 3698, 3699, 3699, 3700, 3700, 3701, 3702, 3702, 3703, 3703, 3704, 3704,
+ 3705, 3705, 3706, 3707, 3707, 3708, 3708, 3709, 3709, 3710, 3710, 3711, 3711, 3712, 3713, 3713,
+ 3714, 3714, 3715, 3715, 3716, 3716, 3717, 3717, 3718, 3719, 3719, 3720, 3720, 3721, 3721, 3722,
+ 3722, 3723, 3724, 3724, 3725, 3725, 3726, 3726, 3727, 3727, 3728, 3728, 3729, 3730, 3730, 3731,
+ 3731, 3732, 3732, 3733, 3733, 3734, 3734, 3735, 3736, 3736, 3737, 3737, 3738, 3738, 3739, 3739,
+ 3740, 3740, 3741, 3742, 3742, 3743, 3743, 3744, 3744, 3745, 3745, 3746, 3746, 3747, 3748, 3748,
+ 3749, 3749, 3750, 3750, 3751, 3751, 3752, 3752, 3753, 3753, 3754, 3755, 3755, 3756, 3756, 3757,
+ 3757, 3758, 3758, 3759, 3759, 3760, 3761, 3761, 3762, 3762, 3763, 3763, 3764, 3764, 3765, 3765,
+ 3766, 3766, 3767, 3768, 3768, 3769, 3769, 3770, 3770, 3771, 3771, 3772, 3772, 3773, 3773, 3774,
+ 3775, 3775, 3776, 3776, 3777, 3777, 3778, 3778, 3779, 3779, 3780, 3781, 3781, 3782, 3782, 3783,
+ 3783, 3784, 3784, 3785, 3785, 3786, 3786, 3787, 3787, 3788, 3789, 3789, 3790, 3790, 3791, 3791,
+ 3792, 3792, 3793, 3793, 3794, 3794, 3795, 3796, 3796, 3797, 3797, 3798, 3798, 3799, 3799, 3800,
+ 3800, 3801, 3801, 3802, 3802, 3803, 3804, 3804, 3805, 3805, 3806, 3806, 3807, 3807, 3808, 3808,
+ 3809, 3809, 3810, 3811, 3811, 3812, 3812, 3813, 3813, 3814, 3814, 3815, 3815, 3816, 3816, 3817,
+ 3817, 3818, 3819, 3819, 3820, 3820, 3821, 3821, 3822, 3822, 3823, 3823, 3824, 3824, 3825, 3825,
+ 3826, 3826, 3827, 3828, 3828, 3829, 3829, 3830, 3830, 3831, 3831, 3832, 3832, 3833, 3833, 3834,
+ 3834, 3835, 3835, 3836, 3837, 3837, 3838, 3838, 3839, 3839, 3840, 3840, 3841, 3841, 3842, 3842,
+ 3843, 3843, 3844, 3844, 3845, 3846, 3846, 3847, 3847, 3848, 3848, 3849, 3849, 3850, 3850, 3851,
+ 3851, 3852, 3852, 3853, 3853, 3854, 3855, 3855, 3856, 3856, 3857, 3857, 3858, 3858, 3859, 3859,
+ 3860, 3860, 3861, 3861, 3862, 3862, 3863, 3863, 3864, 3864, 3865, 3866, 3866, 3867, 3867, 3868,
+ 3868, 3869, 3869, 3870, 3870, 3871, 3871, 3872, 3872, 3873, 3873, 3874, 3874, 3875, 3876, 3876,
+ 3877, 3877, 3878, 3878, 3879, 3879, 3880, 3880, 3881, 3881, 3882, 3882, 3883, 3883, 3884, 3884,
+ 3885, 3885, 3886, 3886, 3887, 3888, 3888, 3889, 3889, 3890, 3890, 3891, 3891, 3892, 3892, 3893,
+ 3893, 3894, 3894, 3895, 3895, 3896, 3896, 3897, 3897, 3898, 3898, 3899, 3900, 3900, 3901, 3901,
+ 3902, 3902, 3903, 3903, 3904, 3904, 3905, 3905, 3906, 3906, 3907, 3907, 3908, 3908, 3909, 3909,
+ 3910, 3910, 3911, 3911, 3912, 3912, 3913, 3914, 3914, 3915, 3915, 3916, 3916, 3917, 3917, 3918,
+ 3918, 3919, 3919, 3920, 3920, 3921, 3921, 3922, 3922, 3923, 3923, 3924, 3924, 3925, 3925, 3926,
+ 3926, 3927, 3927, 3928, 3929, 3929, 3930, 3930, 3931, 3931, 3932, 3932, 3933, 3933, 3934, 3934,
+ 3935, 3935, 3936, 3936, 3937, 3937, 3938, 3938, 3939, 3939, 3940, 3940, 3941, 3941, 3942, 3942,
+ 3943, 3943, 3944, 3944, 3945, 3945, 3946, 3947, 3947, 3948, 3948, 3949, 3949, 3950, 3950, 3951,
+ 3951, 3952, 3952, 3953, 3953, 3954, 3954, 3955, 3955, 3956, 3956, 3957, 3957, 3958, 3958, 3959,
+ 3959, 3960, 3960, 3961, 3961, 3962, 3962, 3963, 3963, 3964, 3964, 3965, 3965, 3966, 3966, 3967,
+ 3967, 3968, 3969, 3969, 3970, 3970, 3971, 3971, 3972, 3972, 3973, 3973, 3974, 3974, 3975, 3975,
+ 3976, 3976, 3977, 3977, 3978, 3978, 3979, 3979, 3980, 3980, 3981, 3981, 3982, 3982, 3983, 3983,
+ 3984, 3984, 3985, 3985, 3986, 3986, 3987, 3987, 3988, 3988, 3989, 3989, 3990, 3990, 3991, 3991,
+ 3992, 3992, 3993, 3993, 3994, 3994, 3995, 3995, 3996, 3996, 3997, 3997, 3998, 3998, 3999, 3999,
+ 4000, 4001, 4001, 4002, 4002, 4003, 4003, 4004, 4004, 4005, 4005, 4006, 4006, 4007, 4007, 4008,
+ 4008, 4009, 4009, 4010, 4010, 4011, 4011, 4012, 4012, 4013, 4013, 4014, 4014, 4015, 4015, 4016,
+ 4016, 4017, 4017, 4018, 4018, 4019, 4019, 4020, 4020, 4021, 4021, 4022, 4022, 4023, 4023, 4024,
+ 4024, 4025, 4025, 4026, 4026, 4027, 4027, 4028, 4028, 4029, 4029, 4030, 4030, 4031, 4031, 4032,
+ 4032, 4033, 4033, 4034, 4034, 4035, 4035, 4036, 4036, 4037, 4037, 4038, 4038, 4039, 4039, 4040,
+ 4040, 4041, 4041, 4042, 4042, 4043, 4043, 4044, 4044, 4045, 4045, 4046, 4046, 4047, 4047, 4048,
+ 4048, 4049, 4049, 4050, 4050, 4051, 4051, 4052, 4052, 4053, 4053, 4054, 4054, 4055, 4055, 4056,
+ 4056, 4057, 4057, 4058, 4058, 4059, 4059, 4060, 4060, 4061, 4061, 4062, 4062, 4063, 4063, 4064,
+ 4064, 4065, 4065, 4066, 4066, 4067, 4067, 4068, 4068, 4069, 4069, 4070, 4070, 4071, 4071, 4072,
+ 4072, 4073, 4073, 4074, 4074, 4075, 4075, 4076, 4076, 4077, 4077, 4078, 4078, 4079, 4079, 4080,
+ 4080,
+};
+
+/* Generated table */
+const struct tpg_rbg_color16 tpg_csc_colors[V4L2_COLORSPACE_DCI_P3 + 1][V4L2_XFER_FUNC_SMPTE2084 + 1][TPG_COLOR_CSC_BLACK + 1] = {
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][1] = { 2953, 2963, 586 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][2] = { 0, 2967, 2937 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][3] = { 88, 2990, 575 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][4] = { 3016, 259, 2933 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][5] = { 3030, 405, 558 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][6] = { 478, 428, 2931 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3056 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][1] = { 3068, 3077, 838 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][2] = { 0, 3081, 3053 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][3] = { 241, 3102, 828 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][4] = { 3126, 504, 3050 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][5] = { 3138, 657, 810 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][6] = { 731, 680, 3048 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SRGB][7] = { 800, 799, 800 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_OPRGB][0] = { 3033, 3033, 3033 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_OPRGB][1] = { 3046, 3054, 886 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_OPRGB][2] = { 0, 3058, 3031 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_OPRGB][3] = { 360, 3079, 877 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_OPRGB][4] = { 3103, 587, 3027 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_OPRGB][5] = { 3116, 723, 861 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_OPRGB][6] = { 789, 744, 3025 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_OPRGB][7] = { 851, 851, 851 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][1] = { 2941, 2950, 546 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][2] = { 0, 2954, 2924 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][3] = { 78, 2978, 536 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][4] = { 3004, 230, 2920 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][5] = { 3018, 363, 518 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][6] = { 437, 387, 2918 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][1] = { 2145, 2159, 142 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][2] = { 0, 2164, 2122 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][3] = { 19, 2198, 138 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][4] = { 2236, 57, 2116 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][5] = { 2256, 90, 133 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][6] = { 110, 96, 2113 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][1] = { 3186, 3194, 1121 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][2] = { 0, 3197, 3173 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][3] = { 523, 3216, 1112 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][4] = { 3237, 792, 3169 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][5] = { 3248, 944, 1094 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][6] = { 1017, 967, 3168 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][0] = { 1812, 1812, 1812 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][1] = { 1815, 1818, 910 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][2] = { 0, 1819, 1811 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][3] = { 472, 1825, 904 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][4] = { 1832, 686, 1810 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][5] = { 1835, 794, 893 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][6] = { 843, 809, 1810 },
+ [V4L2_COLORSPACE_SMPTE170M][V4L2_XFER_FUNC_SMPTE2084][7] = { 886, 886, 886 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][1] = { 2953, 2963, 586 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][2] = { 0, 2967, 2937 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][3] = { 88, 2990, 575 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][4] = { 3016, 259, 2933 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][5] = { 3030, 405, 558 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][6] = { 478, 428, 2931 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3056 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][1] = { 3068, 3077, 838 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][2] = { 0, 3081, 3053 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][3] = { 241, 3102, 828 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][4] = { 3126, 504, 3050 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][5] = { 3138, 657, 810 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][6] = { 731, 680, 3048 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SRGB][7] = { 800, 799, 800 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_OPRGB][0] = { 3033, 3033, 3033 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_OPRGB][1] = { 3046, 3054, 886 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_OPRGB][2] = { 0, 3058, 3031 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_OPRGB][3] = { 360, 3079, 877 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_OPRGB][4] = { 3103, 587, 3027 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_OPRGB][5] = { 3116, 723, 861 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_OPRGB][6] = { 789, 744, 3025 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_OPRGB][7] = { 851, 851, 851 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][1] = { 2941, 2950, 546 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][2] = { 0, 2954, 2924 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][3] = { 78, 2978, 536 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][4] = { 3004, 230, 2920 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][5] = { 3018, 363, 518 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][6] = { 437, 387, 2918 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][1] = { 2145, 2159, 142 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][2] = { 0, 2164, 2122 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][3] = { 19, 2198, 138 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][4] = { 2236, 57, 2116 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][5] = { 2256, 90, 133 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][6] = { 110, 96, 2113 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][1] = { 3186, 3194, 1121 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][2] = { 0, 3197, 3173 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][3] = { 523, 3216, 1112 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][4] = { 3237, 792, 3169 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][5] = { 3248, 944, 1094 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][6] = { 1017, 967, 3168 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][0] = { 1812, 1812, 1812 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][1] = { 1815, 1818, 910 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][2] = { 0, 1819, 1811 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][3] = { 472, 1825, 904 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][4] = { 1832, 686, 1810 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][5] = { 1835, 794, 893 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][6] = { 843, 809, 1810 },
+ [V4L2_COLORSPACE_SMPTE240M][V4L2_XFER_FUNC_SMPTE2084][7] = { 886, 886, 886 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][1] = { 2939, 2939, 547 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][2] = { 547, 2939, 2939 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][3] = { 547, 2939, 547 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][4] = { 2939, 547, 2939 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][5] = { 2939, 547, 547 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][6] = { 547, 547, 2939 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3056 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][1] = { 3056, 3056, 800 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][2] = { 800, 3056, 3056 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][3] = { 800, 3056, 800 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][4] = { 3056, 800, 3056 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][5] = { 3056, 800, 800 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][6] = { 800, 800, 3056 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SRGB][7] = { 800, 800, 800 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_OPRGB][0] = { 3033, 3033, 3033 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_OPRGB][1] = { 3033, 3033, 851 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_OPRGB][2] = { 851, 3033, 3033 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_OPRGB][3] = { 851, 3033, 851 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_OPRGB][4] = { 3033, 851, 3033 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_OPRGB][5] = { 3033, 851, 851 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_OPRGB][6] = { 851, 851, 3033 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_OPRGB][7] = { 851, 851, 851 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][1] = { 2926, 2926, 507 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][2] = { 507, 2926, 2926 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][3] = { 507, 2926, 507 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][4] = { 2926, 507, 2926 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][5] = { 2926, 507, 507 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][6] = { 507, 507, 2926 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][1] = { 2125, 2125, 130 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][2] = { 130, 2125, 2125 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][3] = { 130, 2125, 130 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][4] = { 2125, 130, 2125 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][5] = { 2125, 130, 130 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][6] = { 130, 130, 2125 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][1] = { 3175, 3175, 1084 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][2] = { 1084, 3175, 3175 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][3] = { 1084, 3175, 1084 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][4] = { 3175, 1084, 3175 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][5] = { 3175, 1084, 1084 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][6] = { 1084, 1084, 3175 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][0] = { 1812, 1812, 1812 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][1] = { 1812, 1812, 886 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][2] = { 886, 1812, 1812 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][3] = { 886, 1812, 886 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][4] = { 1812, 886, 1812 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][5] = { 1812, 886, 886 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][6] = { 886, 886, 1812 },
+ [V4L2_COLORSPACE_REC709][V4L2_XFER_FUNC_SMPTE2084][7] = { 886, 886, 886 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][1] = { 2892, 3034, 910 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][2] = { 1715, 2916, 2914 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][3] = { 1631, 3012, 828 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][4] = { 2497, 119, 2867 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][5] = { 2440, 649, 657 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][6] = { 740, 0, 2841 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3055, 3056 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][1] = { 3013, 3142, 1157 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][2] = { 1926, 3034, 3032 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][3] = { 1847, 3121, 1076 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][4] = { 2651, 304, 2990 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][5] = { 2599, 901, 909 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][6] = { 991, 0, 2966 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SRGB][7] = { 800, 799, 800 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_OPRGB][0] = { 3033, 3033, 3033 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_OPRGB][1] = { 2989, 3120, 1180 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_OPRGB][2] = { 1913, 3011, 3009 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_OPRGB][3] = { 1836, 3099, 1105 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_OPRGB][4] = { 2627, 413, 2966 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_OPRGB][5] = { 2576, 943, 951 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_OPRGB][6] = { 1026, 0, 2942 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_OPRGB][7] = { 851, 851, 851 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][1] = { 2879, 3022, 874 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][2] = { 1688, 2903, 2901 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][3] = { 1603, 2999, 791 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][4] = { 2479, 106, 2853 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][5] = { 2422, 610, 618 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][6] = { 702, 0, 2827 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][1] = { 2059, 2262, 266 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][2] = { 771, 2092, 2089 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][3] = { 705, 2229, 231 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][4] = { 1550, 26, 2024 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][5] = { 1484, 163, 165 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][6] = { 196, 0, 1988 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][1] = { 3136, 3251, 1429 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][2] = { 2150, 3156, 3154 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][3] = { 2077, 3233, 1352 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][4] = { 2812, 589, 3116 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][5] = { 2765, 1182, 1190 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][6] = { 1270, 0, 3094 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][0] = { 1812, 1812, 1812 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][1] = { 1800, 1836, 1090 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][2] = { 1436, 1806, 1805 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][3] = { 1405, 1830, 1047 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][4] = { 1691, 527, 1793 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][5] = { 1674, 947, 952 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][6] = { 1000, 0, 1786 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][V4L2_XFER_FUNC_SMPTE2084][7] = { 886, 886, 886 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][1] = { 2939, 2939, 464 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][2] = { 786, 2939, 2939 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][3] = { 786, 2939, 464 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][4] = { 2879, 547, 2956 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][5] = { 2879, 547, 547 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][6] = { 547, 547, 2956 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3056 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][1] = { 3056, 3056, 717 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][2] = { 1036, 3056, 3056 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][3] = { 1036, 3056, 717 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][4] = { 3001, 800, 3071 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][5] = { 3001, 800, 799 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][6] = { 800, 800, 3071 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SRGB][7] = { 800, 800, 799 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_OPRGB][0] = { 3033, 3033, 3033 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_OPRGB][1] = { 3033, 3033, 776 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_OPRGB][2] = { 1068, 3033, 3033 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_OPRGB][3] = { 1068, 3033, 776 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_OPRGB][4] = { 2977, 851, 3048 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_OPRGB][5] = { 2977, 851, 851 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_OPRGB][6] = { 851, 851, 3048 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_OPRGB][7] = { 851, 851, 851 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][1] = { 2926, 2926, 423 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][2] = { 749, 2926, 2926 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][3] = { 749, 2926, 423 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][4] = { 2865, 507, 2943 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][5] = { 2865, 507, 507 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][6] = { 507, 507, 2943 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][1] = { 2125, 2125, 106 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][2] = { 214, 2125, 2125 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][3] = { 214, 2125, 106 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][4] = { 2041, 130, 2149 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][5] = { 2041, 130, 130 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][6] = { 130, 130, 2149 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][1] = { 3175, 3175, 1003 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][2] = { 1313, 3175, 3175 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][3] = { 1313, 3175, 1003 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][4] = { 3126, 1084, 3188 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][5] = { 3126, 1084, 1084 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][6] = { 1084, 1084, 3188 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][0] = { 1812, 1812, 1812 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][1] = { 1812, 1812, 833 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][2] = { 1025, 1812, 1812 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][3] = { 1025, 1812, 833 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][4] = { 1796, 886, 1816 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][5] = { 1796, 886, 886 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][6] = { 886, 886, 1816 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][V4L2_XFER_FUNC_SMPTE2084][7] = { 886, 886, 886 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][1] = { 2939, 2939, 547 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][2] = { 547, 2939, 2939 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][3] = { 547, 2939, 547 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][4] = { 2939, 547, 2939 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][5] = { 2939, 547, 547 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][6] = { 547, 547, 2939 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3056 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][1] = { 3056, 3056, 800 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][2] = { 800, 3056, 3056 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][3] = { 800, 3056, 800 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][4] = { 3056, 800, 3056 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][5] = { 3056, 800, 800 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][6] = { 800, 800, 3056 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SRGB][7] = { 800, 800, 800 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_OPRGB][0] = { 3033, 3033, 3033 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_OPRGB][1] = { 3033, 3033, 851 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_OPRGB][2] = { 851, 3033, 3033 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_OPRGB][3] = { 851, 3033, 851 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_OPRGB][4] = { 3033, 851, 3033 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_OPRGB][5] = { 3033, 851, 851 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_OPRGB][6] = { 851, 851, 3033 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_OPRGB][7] = { 851, 851, 851 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][1] = { 2926, 2926, 507 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][2] = { 507, 2926, 2926 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][3] = { 507, 2926, 507 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][4] = { 2926, 507, 2926 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][5] = { 2926, 507, 507 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][6] = { 507, 507, 2926 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][1] = { 2125, 2125, 130 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][2] = { 130, 2125, 2125 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][3] = { 130, 2125, 130 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][4] = { 2125, 130, 2125 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][5] = { 2125, 130, 130 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][6] = { 130, 130, 2125 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][1] = { 3175, 3175, 1084 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][2] = { 1084, 3175, 3175 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][3] = { 1084, 3175, 1084 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][4] = { 3175, 1084, 3175 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][5] = { 3175, 1084, 1084 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][6] = { 1084, 1084, 3175 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][0] = { 1812, 1812, 1812 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][1] = { 1812, 1812, 886 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][2] = { 886, 1812, 1812 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][3] = { 886, 1812, 886 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][4] = { 1812, 886, 1812 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][5] = { 1812, 886, 886 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][6] = { 886, 886, 1812 },
+ [V4L2_COLORSPACE_SRGB][V4L2_XFER_FUNC_SMPTE2084][7] = { 886, 886, 886 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_709][1] = { 2939, 2939, 781 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_709][2] = { 1622, 2939, 2939 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_709][3] = { 1622, 2939, 781 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_709][4] = { 2502, 547, 2881 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_709][5] = { 2502, 547, 547 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_709][6] = { 547, 547, 2881 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3056 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SRGB][1] = { 3056, 3056, 1031 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SRGB][2] = { 1838, 3056, 3056 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SRGB][3] = { 1838, 3056, 1031 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SRGB][4] = { 2657, 800, 3002 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SRGB][5] = { 2657, 800, 800 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SRGB][6] = { 800, 800, 3002 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SRGB][7] = { 800, 800, 800 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_OPRGB][0] = { 3033, 3033, 3033 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_OPRGB][1] = { 3033, 3033, 1063 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_OPRGB][2] = { 1828, 3033, 3033 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_OPRGB][3] = { 1828, 3033, 1063 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_OPRGB][4] = { 2633, 851, 2979 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_OPRGB][5] = { 2633, 851, 851 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_OPRGB][6] = { 851, 851, 2979 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_OPRGB][7] = { 851, 851, 851 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE240M][1] = { 2926, 2926, 744 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE240M][2] = { 1594, 2926, 2926 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE240M][3] = { 1594, 2926, 744 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE240M][4] = { 2484, 507, 2867 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE240M][5] = { 2484, 507, 507 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE240M][6] = { 507, 507, 2867 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_NONE][1] = { 2125, 2125, 212 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_NONE][2] = { 698, 2125, 2125 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_NONE][3] = { 698, 2125, 212 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_NONE][4] = { 1557, 130, 2043 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_NONE][5] = { 1557, 130, 130 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_NONE][6] = { 130, 130, 2043 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_DCI_P3][1] = { 3175, 3175, 1308 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_DCI_P3][2] = { 2069, 3175, 3175 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_DCI_P3][3] = { 2069, 3175, 1308 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_DCI_P3][4] = { 2816, 1084, 3127 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_DCI_P3][5] = { 2816, 1084, 1084 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_DCI_P3][6] = { 1084, 1084, 3127 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE2084][0] = { 1812, 1812, 1812 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE2084][1] = { 1812, 1812, 1022 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE2084][2] = { 1402, 1812, 1812 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE2084][3] = { 1402, 1812, 1022 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE2084][4] = { 1692, 886, 1797 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE2084][5] = { 1692, 886, 886 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE2084][6] = { 886, 886, 1797 },
+ [V4L2_COLORSPACE_OPRGB][V4L2_XFER_FUNC_SMPTE2084][7] = { 886, 886, 886 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][1] = { 2877, 2923, 1058 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][2] = { 1837, 2840, 2916 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][3] = { 1734, 2823, 993 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][4] = { 2427, 961, 2812 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][5] = { 2351, 912, 648 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][6] = { 792, 618, 2788 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3056 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][1] = { 2999, 3041, 1301 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][2] = { 2040, 2965, 3034 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][3] = { 1944, 2950, 1238 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][4] = { 2587, 1207, 2940 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][5] = { 2517, 1159, 900 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][6] = { 1042, 870, 2917 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SRGB][7] = { 800, 800, 800 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_OPRGB][0] = { 3033, 3033, 3033 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_OPRGB][1] = { 2976, 3018, 1315 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_OPRGB][2] = { 2024, 2942, 3011 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_OPRGB][3] = { 1930, 2926, 1256 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_OPRGB][4] = { 2563, 1227, 2916 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_OPRGB][5] = { 2494, 1183, 943 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_OPRGB][6] = { 1073, 916, 2894 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_OPRGB][7] = { 851, 851, 851 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][1] = { 2864, 2910, 1024 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][2] = { 1811, 2826, 2903 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][3] = { 1707, 2809, 958 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][4] = { 2408, 926, 2798 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][5] = { 2331, 876, 609 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][6] = { 755, 579, 2773 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][1] = { 2039, 2102, 338 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][2] = { 873, 1987, 2092 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][3] = { 787, 1965, 305 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][4] = { 1468, 290, 1949 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][5] = { 1382, 268, 162 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][6] = { 216, 152, 1917 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][1] = { 3124, 3161, 1566 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][2] = { 2255, 3094, 3156 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][3] = { 2166, 3080, 1506 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][4] = { 2754, 1477, 3071 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][5] = { 2690, 1431, 1182 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][6] = { 1318, 1153, 3051 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][0] = { 1812, 1812, 1812 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][1] = { 1796, 1808, 1163 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][2] = { 1480, 1786, 1806 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][3] = { 1443, 1781, 1131 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][4] = { 1670, 1116, 1778 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][5] = { 1648, 1091, 947 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][6] = { 1028, 929, 1772 },
+ [V4L2_COLORSPACE_BT2020][V4L2_XFER_FUNC_SMPTE2084][7] = { 886, 886, 886 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][0] = { 2939, 2939, 2939 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][1] = { 2936, 2934, 992 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][2] = { 1159, 2890, 2916 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][3] = { 1150, 2885, 921 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][4] = { 2751, 766, 2837 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][5] = { 2747, 747, 650 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][6] = { 563, 570, 2812 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_709][7] = { 547, 547, 547 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][0] = { 3056, 3056, 3055 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][1] = { 3052, 3051, 1237 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][2] = { 1397, 3011, 3034 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][3] = { 1389, 3006, 1168 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][4] = { 2884, 1016, 2962 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][5] = { 2880, 998, 902 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][6] = { 816, 823, 2940 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SRGB][7] = { 800, 800, 799 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_OPRGB][0] = { 3033, 3033, 3033 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_OPRGB][1] = { 3029, 3028, 1255 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_OPRGB][2] = { 1406, 2988, 3011 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_OPRGB][3] = { 1398, 2983, 1190 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_OPRGB][4] = { 2860, 1050, 2939 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_OPRGB][5] = { 2857, 1033, 945 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_OPRGB][6] = { 866, 873, 2916 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_OPRGB][7] = { 851, 851, 851 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][0] = { 2926, 2926, 2926 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][1] = { 2923, 2921, 957 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][2] = { 1125, 2877, 2902 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][3] = { 1116, 2871, 885 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][4] = { 2736, 729, 2823 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][5] = { 2732, 710, 611 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][6] = { 523, 531, 2798 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE240M][7] = { 507, 507, 507 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][0] = { 2125, 2125, 2125 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][1] = { 2120, 2118, 305 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][2] = { 392, 2056, 2092 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][3] = { 387, 2049, 271 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][4] = { 1868, 206, 1983 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][5] = { 1863, 199, 163 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][6] = { 135, 137, 1950 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_NONE][7] = { 130, 130, 130 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][0] = { 3175, 3175, 3175 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][1] = { 3172, 3170, 1505 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][2] = { 1657, 3135, 3155 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][3] = { 1649, 3130, 1439 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][4] = { 3021, 1294, 3091 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][5] = { 3018, 1276, 1184 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][6] = { 1100, 1107, 3071 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_DCI_P3][7] = { 1084, 1084, 1084 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][0] = { 1812, 1812, 1812 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][1] = { 1811, 1810, 1131 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][2] = { 1210, 1799, 1806 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][3] = { 1206, 1798, 1096 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][4] = { 1762, 1014, 1785 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][5] = { 1761, 1004, 948 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][6] = { 896, 901, 1778 },
+ [V4L2_COLORSPACE_DCI_P3][V4L2_XFER_FUNC_SMPTE2084][7] = { 886, 886, 886 },
+};
+
+#else
+
+/* This code generates the table above */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static const double rec709_to_ntsc1953[3][3] = {
+ /*
+ * This transform uses the Bradford method to compensate for
+ * the different whitepoints.
+ */
+ { 0.6785011, 0.2883441, 0.0331548 },
+ { 0.0165284, 1.0518725, -0.0684009 },
+ { 0.0179230, 0.0506096, 0.9314674 }
+};
+
+static const double rec709_to_ebu[3][3] = {
+ { 0.9578221, 0.0421779, -0.0000000 },
+ { -0.0000000, 1.0000000, 0.0000000 },
+ { -0.0000000, -0.0119367, 1.0119367 }
+};
+
+static const double rec709_to_170m[3][3] = {
+ { 1.0653640, -0.0553900, -0.0099740 },
+ { -0.0196361, 1.0363630, -0.0167269 },
+ { 0.0016327, 0.0044133, 0.9939540 },
+};
+
+static const double rec709_to_240m[3][3] = {
+ { 1.0653640, -0.0553900, -0.0099740 },
+ { -0.0196361, 1.0363630, -0.0167269 },
+ { 0.0016327, 0.0044133, 0.9939540 },
+};
+
+static const double rec709_to_oprgb[3][3] = {
+ { 0.7151627, 0.2848373, -0.0000000 },
+ { 0.0000000, 1.0000000, 0.0000000 },
+ { -0.0000000, 0.0411705, 0.9588295 },
+};
+
+static const double rec709_to_bt2020[3][3] = {
+ { 0.6274524, 0.3292485, 0.0432991 },
+ { 0.0691092, 0.9195311, 0.0113597 },
+ { 0.0163976, 0.0880301, 0.8955723 },
+};
+
+static const double rec709_to_dcip3[3][3] = {
+ /*
+ * This transform uses the Bradford method to compensate for
+ * the different whitepoints.
+ */
+ { 0.8686648, 0.1288456, 0.0024896 },
+ { 0.0345479, 0.9618084, 0.0036437 },
+ { 0.0167785, 0.0710559, 0.9121655 }
+};
+
+static void mult_matrix(double *r, double *g, double *b, const double m[3][3])
+{
+ double ir, ig, ib;
+
+ ir = m[0][0] * (*r) + m[0][1] * (*g) + m[0][2] * (*b);
+ ig = m[1][0] * (*r) + m[1][1] * (*g) + m[1][2] * (*b);
+ ib = m[2][0] * (*r) + m[2][1] * (*g) + m[2][2] * (*b);
+ *r = ir;
+ *g = ig;
+ *b = ib;
+}
+
+static double transfer_srgb_to_rgb(double v)
+{
+ if (v < -0.04045)
+ return pow((-v + 0.055) / 1.055, 2.4);
+ return (v <= 0.04045) ? v / 12.92 : pow((v + 0.055) / 1.055, 2.4);
+}
+
+static double transfer_rgb_to_srgb(double v)
+{
+ if (v <= -0.0031308)
+ return -1.055 * pow(-v, 1.0 / 2.4) + 0.055;
+ if (v <= 0.0031308)
+ return v * 12.92;
+ return 1.055 * pow(v, 1.0 / 2.4) - 0.055;
+}
+
+static double transfer_rgb_to_smpte240m(double v)
+{
+ return (v <= 0.0228) ? v * 4.0 : 1.1115 * pow(v, 0.45) - 0.1115;
+}
+
+static double transfer_rgb_to_rec709(double v)
+{
+ if (v <= -0.018)
+ return -1.099 * pow(-v, 0.45) + 0.099;
+ return (v < 0.018) ? v * 4.5 : 1.099 * pow(v, 0.45) - 0.099;
+}
+
+static double transfer_rec709_to_rgb(double v)
+{
+ return (v < 0.081) ? v / 4.5 : pow((v + 0.099) / 1.099, 1.0 / 0.45);
+}
+
+static double transfer_rgb_to_oprgb(double v)
+{
+ return pow(v, 1.0 / 2.19921875);
+}
+
+static double transfer_rgb_to_dcip3(double v)
+{
+ return pow(v, 1.0 / 2.6);
+}
+
+static double transfer_rgb_to_smpte2084(double v)
+{
+ const double m1 = (2610.0 / 4096.0) / 4.0;
+ const double m2 = 128.0 * 2523.0 / 4096.0;
+ const double c1 = 3424.0 / 4096.0;
+ const double c2 = 32.0 * 2413.0 / 4096.0;
+ const double c3 = 32.0 * 2392.0 / 4096.0;
+
+ /*
+ * The RGB input maps to the luminance range 0-100 cd/m^2, while
+ * SMPTE-2084 maps values to the luminance range of 0-10000 cd/m^2.
+ * Hence the factor 100.
+ */
+ v /= 100.0;
+ v = pow(v, m1);
+ return pow((c1 + c2 * v) / (1 + c3 * v), m2);
+}
+
+static double transfer_srgb_to_rec709(double v)
+{
+ return transfer_rgb_to_rec709(transfer_srgb_to_rgb(v));
+}
+
+static void csc(enum v4l2_colorspace colorspace, enum v4l2_xfer_func xfer_func,
+ double *r, double *g, double *b)
+{
+ int clamp = 1;
+
+ *r = transfer_srgb_to_rgb(*r);
+ *g = transfer_srgb_to_rgb(*g);
+ *b = transfer_srgb_to_rgb(*b);
+
+ /* Convert the primaries of Rec. 709 Linear RGB */
+ switch (colorspace) {
+ case V4L2_COLORSPACE_SMPTE240M:
+ mult_matrix(r, g, b, rec709_to_240m);
+ break;
+ case V4L2_COLORSPACE_SMPTE170M:
+ mult_matrix(r, g, b, rec709_to_170m);
+ break;
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ mult_matrix(r, g, b, rec709_to_ebu);
+ break;
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ mult_matrix(r, g, b, rec709_to_ntsc1953);
+ break;
+ case V4L2_COLORSPACE_OPRGB:
+ mult_matrix(r, g, b, rec709_to_oprgb);
+ break;
+ case V4L2_COLORSPACE_BT2020:
+ mult_matrix(r, g, b, rec709_to_bt2020);
+ break;
+ case V4L2_COLORSPACE_DCI_P3:
+ mult_matrix(r, g, b, rec709_to_dcip3);
+ break;
+ case V4L2_COLORSPACE_SRGB:
+ case V4L2_COLORSPACE_REC709:
+ break;
+ default:
+ break;
+ }
+
+ if (clamp) {
+ *r = ((*r) < 0) ? 0 : (((*r) > 1) ? 1 : (*r));
+ *g = ((*g) < 0) ? 0 : (((*g) > 1) ? 1 : (*g));
+ *b = ((*b) < 0) ? 0 : (((*b) > 1) ? 1 : (*b));
+ }
+
+ switch (xfer_func) {
+ case V4L2_XFER_FUNC_709:
+ *r = transfer_rgb_to_rec709(*r);
+ *g = transfer_rgb_to_rec709(*g);
+ *b = transfer_rgb_to_rec709(*b);
+ break;
+ case V4L2_XFER_FUNC_SRGB:
+ *r = transfer_rgb_to_srgb(*r);
+ *g = transfer_rgb_to_srgb(*g);
+ *b = transfer_rgb_to_srgb(*b);
+ break;
+ case V4L2_XFER_FUNC_OPRGB:
+ *r = transfer_rgb_to_oprgb(*r);
+ *g = transfer_rgb_to_oprgb(*g);
+ *b = transfer_rgb_to_oprgb(*b);
+ break;
+ case V4L2_XFER_FUNC_DCI_P3:
+ *r = transfer_rgb_to_dcip3(*r);
+ *g = transfer_rgb_to_dcip3(*g);
+ *b = transfer_rgb_to_dcip3(*b);
+ break;
+ case V4L2_XFER_FUNC_SMPTE2084:
+ *r = transfer_rgb_to_smpte2084(*r);
+ *g = transfer_rgb_to_smpte2084(*g);
+ *b = transfer_rgb_to_smpte2084(*b);
+ break;
+ case V4L2_XFER_FUNC_SMPTE240M:
+ *r = transfer_rgb_to_smpte240m(*r);
+ *g = transfer_rgb_to_smpte240m(*g);
+ *b = transfer_rgb_to_smpte240m(*b);
+ break;
+ case V4L2_XFER_FUNC_NONE:
+ break;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ static const unsigned colorspaces[] = {
+ 0,
+ V4L2_COLORSPACE_SMPTE170M,
+ V4L2_COLORSPACE_SMPTE240M,
+ V4L2_COLORSPACE_REC709,
+ 0,
+ V4L2_COLORSPACE_470_SYSTEM_M,
+ V4L2_COLORSPACE_470_SYSTEM_BG,
+ 0,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_COLORSPACE_OPRGB,
+ V4L2_COLORSPACE_BT2020,
+ 0,
+ V4L2_COLORSPACE_DCI_P3,
+ };
+ static const char * const colorspace_names[] = {
+ "",
+ "V4L2_COLORSPACE_SMPTE170M",
+ "V4L2_COLORSPACE_SMPTE240M",
+ "V4L2_COLORSPACE_REC709",
+ "",
+ "V4L2_COLORSPACE_470_SYSTEM_M",
+ "V4L2_COLORSPACE_470_SYSTEM_BG",
+ "",
+ "V4L2_COLORSPACE_SRGB",
+ "V4L2_COLORSPACE_OPRGB",
+ "V4L2_COLORSPACE_BT2020",
+ "",
+ "V4L2_COLORSPACE_DCI_P3",
+ };
+ static const char * const xfer_func_names[] = {
+ "",
+ "V4L2_XFER_FUNC_709",
+ "V4L2_XFER_FUNC_SRGB",
+ "V4L2_XFER_FUNC_OPRGB",
+ "V4L2_XFER_FUNC_SMPTE240M",
+ "V4L2_XFER_FUNC_NONE",
+ "V4L2_XFER_FUNC_DCI_P3",
+ "V4L2_XFER_FUNC_SMPTE2084",
+ };
+ int i;
+ int x;
+ int c;
+
+ printf("/* Generated table */\n");
+ printf("const unsigned short tpg_rec709_to_linear[255 * 16 + 1] = {");
+ for (i = 0; i <= 255 * 16; i++) {
+ if (i % 16 == 0)
+ printf("\n\t");
+ printf("%4d,%s",
+ (int)(0.5 + 16.0 * 255.0 *
+ transfer_rec709_to_rgb(i / (16.0 * 255.0))),
+ i % 16 == 15 || i == 255 * 16 ? "" : " ");
+ }
+ printf("\n};\n\n");
+
+ printf("/* Generated table */\n");
+ printf("const unsigned short tpg_linear_to_rec709[255 * 16 + 1] = {");
+ for (i = 0; i <= 255 * 16; i++) {
+ if (i % 16 == 0)
+ printf("\n\t");
+ printf("%4d,%s",
+ (int)(0.5 + 16.0 * 255.0 *
+ transfer_rgb_to_rec709(i / (16.0 * 255.0))),
+ i % 16 == 15 || i == 255 * 16 ? "" : " ");
+ }
+ printf("\n};\n\n");
+
+ printf("/* Generated table */\n");
+ printf("const struct tpg_rbg_color16 tpg_csc_colors[V4L2_COLORSPACE_DCI_P3 + 1][V4L2_XFER_FUNC_SMPTE2084 + 1][TPG_COLOR_CSC_BLACK + 1] = {\n");
+ for (c = 0; c <= V4L2_COLORSPACE_DCI_P3; c++) {
+ for (x = 1; x <= V4L2_XFER_FUNC_SMPTE2084; x++) {
+ for (i = 0; i <= TPG_COLOR_CSC_BLACK; i++) {
+ double r, g, b;
+
+ if (colorspaces[c] == 0)
+ continue;
+
+ r = tpg_colors[i].r / 255.0;
+ g = tpg_colors[i].g / 255.0;
+ b = tpg_colors[i].b / 255.0;
+
+ csc(c, x, &r, &g, &b);
+
+ printf("\t[%s][%s][%d] = { %d, %d, %d },\n",
+ colorspace_names[c],
+ xfer_func_names[x], i,
+ (int)(r * 4080), (int)(g * 4080), (int)(b * 4080));
+ }
+ }
+ }
+ printf("};\n\n");
+ return 0;
+}
+
+#endif
diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c
new file mode 100644
index 000000000000..931e5dc453b9
--- /dev/null
+++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c
@@ -0,0 +1,2732 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * v4l2-tpg-core.c - Test Pattern Generator
+ *
+ * Note: gen_twopix and tpg_gen_text are based on code from vivi.c. See the
+ * vivi.c source for the copyright information of those functions.
+ *
+ * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <media/tpg/v4l2-tpg.h>
+
+/* Must remain in sync with enum tpg_pattern */
+const char * const tpg_pattern_strings[] = {
+ "75% Colorbar",
+ "100% Colorbar",
+ "CSC Colorbar",
+ "Horizontal 100% Colorbar",
+ "100% Color Squares",
+ "100% Black",
+ "100% White",
+ "100% Red",
+ "100% Green",
+ "100% Blue",
+ "16x16 Checkers",
+ "2x2 Checkers",
+ "1x1 Checkers",
+ "2x2 Red/Green Checkers",
+ "1x1 Red/Green Checkers",
+ "Alternating Hor Lines",
+ "Alternating Vert Lines",
+ "One Pixel Wide Cross",
+ "Two Pixels Wide Cross",
+ "Ten Pixels Wide Cross",
+ "Gray Ramp",
+ "Noise",
+ NULL
+};
+EXPORT_SYMBOL_GPL(tpg_pattern_strings);
+
+/* Must remain in sync with enum tpg_aspect */
+const char * const tpg_aspect_strings[] = {
+ "Source Width x Height",
+ "4x3",
+ "14x9",
+ "16x9",
+ "16x9 Anamorphic",
+ NULL
+};
+EXPORT_SYMBOL_GPL(tpg_aspect_strings);
+
+/*
+ * Sine table: sin[0] = 127 * sin(-180 degrees)
+ * sin[128] = 127 * sin(0 degrees)
+ * sin[256] = 127 * sin(180 degrees)
+ */
+static const s8 sin[257] = {
+ 0, -4, -7, -11, -13, -18, -20, -22, -26, -29, -33, -35, -37, -41, -43, -48,
+ -50, -52, -56, -58, -62, -63, -65, -69, -71, -75, -76, -78, -82, -83, -87, -88,
+ -90, -93, -94, -97, -99, -101, -103, -104, -107, -108, -110, -111, -112, -114, -115, -117,
+ -118, -119, -120, -121, -122, -123, -123, -124, -125, -125, -126, -126, -127, -127, -127, -127,
+ -127, -127, -127, -127, -126, -126, -125, -125, -124, -124, -123, -122, -121, -120, -119, -118,
+ -117, -116, -114, -113, -111, -110, -109, -107, -105, -103, -101, -100, -97, -96, -93, -91,
+ -90, -87, -85, -82, -80, -76, -75, -73, -69, -67, -63, -62, -60, -56, -54, -50,
+ -48, -46, -41, -39, -35, -33, -31, -26, -24, -20, -18, -15, -11, -9, -4, -2,
+ 0, 2, 4, 9, 11, 15, 18, 20, 24, 26, 31, 33, 35, 39, 41, 46,
+ 48, 50, 54, 56, 60, 62, 64, 67, 69, 73, 75, 76, 80, 82, 85, 87,
+ 90, 91, 93, 96, 97, 100, 101, 103, 105, 107, 109, 110, 111, 113, 114, 116,
+ 117, 118, 119, 120, 121, 122, 123, 124, 124, 125, 125, 126, 126, 127, 127, 127,
+ 127, 127, 127, 127, 127, 126, 126, 125, 125, 124, 123, 123, 122, 121, 120, 119,
+ 118, 117, 115, 114, 112, 111, 110, 108, 107, 104, 103, 101, 99, 97, 94, 93,
+ 90, 88, 87, 83, 82, 78, 76, 75, 71, 69, 65, 64, 62, 58, 56, 52,
+ 50, 48, 43, 41, 37, 35, 33, 29, 26, 22, 20, 18, 13, 11, 7, 4,
+ 0,
+};
+
+#define cos(idx) sin[((idx) + 64) % sizeof(sin)]
+
+/* Global font descriptor */
+static const u8 *font8x16;
+
+void tpg_set_font(const u8 *f)
+{
+ font8x16 = f;
+}
+EXPORT_SYMBOL_GPL(tpg_set_font);
+
+void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h)
+{
+ memset(tpg, 0, sizeof(*tpg));
+ tpg->scaled_width = tpg->src_width = w;
+ tpg->src_height = tpg->buf_height = h;
+ tpg->crop.width = tpg->compose.width = w;
+ tpg->crop.height = tpg->compose.height = h;
+ tpg->recalc_colors = true;
+ tpg->recalc_square_border = true;
+ tpg->brightness = 128;
+ tpg->contrast = 128;
+ tpg->saturation = 128;
+ tpg->hue = 0;
+ tpg->mv_hor_mode = TPG_MOVE_NONE;
+ tpg->mv_vert_mode = TPG_MOVE_NONE;
+ tpg->field = V4L2_FIELD_NONE;
+ tpg_s_fourcc(tpg, V4L2_PIX_FMT_RGB24);
+ tpg->colorspace = V4L2_COLORSPACE_SRGB;
+ tpg->perc_fill = 100;
+ tpg->hsv_enc = V4L2_HSV_ENC_180;
+}
+EXPORT_SYMBOL_GPL(tpg_init);
+
+int tpg_alloc(struct tpg_data *tpg, unsigned max_w)
+{
+ unsigned pat;
+ unsigned plane;
+ int ret = 0;
+
+ tpg->max_line_width = max_w;
+ for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++) {
+ for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
+ unsigned pixelsz = plane ? 2 : 4;
+
+ tpg->lines[pat][plane] =
+ vzalloc(array3_size(max_w, 2, pixelsz));
+ if (!tpg->lines[pat][plane]) {
+ ret = -ENOMEM;
+ goto free_lines;
+ }
+ if (plane == 0)
+ continue;
+ tpg->downsampled_lines[pat][plane] =
+ vzalloc(array3_size(max_w, 2, pixelsz));
+ if (!tpg->downsampled_lines[pat][plane]) {
+ ret = -ENOMEM;
+ goto free_lines;
+ }
+ }
+ }
+ for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
+ unsigned pixelsz = plane ? 2 : 4;
+
+ tpg->contrast_line[plane] =
+ vzalloc(array_size(pixelsz, max_w));
+ if (!tpg->contrast_line[plane]) {
+ ret = -ENOMEM;
+ goto free_contrast_line;
+ }
+ tpg->black_line[plane] =
+ vzalloc(array_size(pixelsz, max_w));
+ if (!tpg->black_line[plane]) {
+ ret = -ENOMEM;
+ goto free_contrast_line;
+ }
+ tpg->random_line[plane] =
+ vzalloc(array3_size(max_w, 2, pixelsz));
+ if (!tpg->random_line[plane]) {
+ ret = -ENOMEM;
+ goto free_contrast_line;
+ }
+ }
+ return 0;
+
+free_contrast_line:
+ for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
+ vfree(tpg->contrast_line[plane]);
+ vfree(tpg->black_line[plane]);
+ vfree(tpg->random_line[plane]);
+ tpg->contrast_line[plane] = NULL;
+ tpg->black_line[plane] = NULL;
+ tpg->random_line[plane] = NULL;
+ }
+free_lines:
+ for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++)
+ for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
+ vfree(tpg->lines[pat][plane]);
+ tpg->lines[pat][plane] = NULL;
+ if (plane == 0)
+ continue;
+ vfree(tpg->downsampled_lines[pat][plane]);
+ tpg->downsampled_lines[pat][plane] = NULL;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tpg_alloc);
+
+void tpg_free(struct tpg_data *tpg)
+{
+ unsigned pat;
+ unsigned plane;
+
+ for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++)
+ for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
+ vfree(tpg->lines[pat][plane]);
+ tpg->lines[pat][plane] = NULL;
+ if (plane == 0)
+ continue;
+ vfree(tpg->downsampled_lines[pat][plane]);
+ tpg->downsampled_lines[pat][plane] = NULL;
+ }
+ for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
+ vfree(tpg->contrast_line[plane]);
+ vfree(tpg->black_line[plane]);
+ vfree(tpg->random_line[plane]);
+ tpg->contrast_line[plane] = NULL;
+ tpg->black_line[plane] = NULL;
+ tpg->random_line[plane] = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(tpg_free);
+
+bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
+{
+ tpg->fourcc = fourcc;
+ tpg->planes = 1;
+ tpg->buffers = 1;
+ tpg->recalc_colors = true;
+ tpg->interleaved = false;
+ tpg->vdownsampling[0] = 1;
+ tpg->hdownsampling[0] = 1;
+ tpg->hmask[0] = ~0;
+ tpg->hmask[1] = ~0;
+ tpg->hmask[2] = ~0;
+
+ switch (fourcc) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ case V4L2_PIX_FMT_SBGGR16:
+ case V4L2_PIX_FMT_SGBRG16:
+ case V4L2_PIX_FMT_SGRBG16:
+ case V4L2_PIX_FMT_SRGGB16:
+ tpg->interleaved = true;
+ tpg->vdownsampling[1] = 1;
+ tpg->hdownsampling[1] = 1;
+ tpg->planes = 2;
+ fallthrough;
+ case V4L2_PIX_FMT_RGB332:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ case V4L2_PIX_FMT_RGB444:
+ case V4L2_PIX_FMT_XRGB444:
+ case V4L2_PIX_FMT_ARGB444:
+ case V4L2_PIX_FMT_RGBX444:
+ case V4L2_PIX_FMT_RGBA444:
+ case V4L2_PIX_FMT_XBGR444:
+ case V4L2_PIX_FMT_ABGR444:
+ case V4L2_PIX_FMT_BGRX444:
+ case V4L2_PIX_FMT_BGRA444:
+ case V4L2_PIX_FMT_RGB555:
+ case V4L2_PIX_FMT_XRGB555:
+ case V4L2_PIX_FMT_ARGB555:
+ case V4L2_PIX_FMT_RGBX555:
+ case V4L2_PIX_FMT_RGBA555:
+ case V4L2_PIX_FMT_XBGR555:
+ case V4L2_PIX_FMT_ABGR555:
+ case V4L2_PIX_FMT_BGRX555:
+ case V4L2_PIX_FMT_BGRA555:
+ case V4L2_PIX_FMT_RGB555X:
+ case V4L2_PIX_FMT_XRGB555X:
+ case V4L2_PIX_FMT_ARGB555X:
+ case V4L2_PIX_FMT_BGR666:
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_XRGB32:
+ case V4L2_PIX_FMT_XBGR32:
+ case V4L2_PIX_FMT_ARGB32:
+ case V4L2_PIX_FMT_ABGR32:
+ case V4L2_PIX_FMT_RGBX32:
+ case V4L2_PIX_FMT_BGRX32:
+ case V4L2_PIX_FMT_RGBA32:
+ case V4L2_PIX_FMT_BGRA32:
+ tpg->color_enc = TGP_COLOR_ENC_RGB;
+ break;
+ case V4L2_PIX_FMT_GREY:
+ case V4L2_PIX_FMT_Y10:
+ case V4L2_PIX_FMT_Y12:
+ case V4L2_PIX_FMT_Y16:
+ case V4L2_PIX_FMT_Y16_BE:
+ case V4L2_PIX_FMT_Z16:
+ tpg->color_enc = TGP_COLOR_ENC_LUMA;
+ break;
+ case V4L2_PIX_FMT_YUV444:
+ case V4L2_PIX_FMT_YUV555:
+ case V4L2_PIX_FMT_YUV565:
+ case V4L2_PIX_FMT_YUV32:
+ case V4L2_PIX_FMT_AYUV32:
+ case V4L2_PIX_FMT_XYUV32:
+ case V4L2_PIX_FMT_VUYA32:
+ case V4L2_PIX_FMT_VUYX32:
+ case V4L2_PIX_FMT_YUVA32:
+ case V4L2_PIX_FMT_YUVX32:
+ tpg->color_enc = TGP_COLOR_ENC_YCBCR;
+ break;
+ case V4L2_PIX_FMT_YUV420M:
+ case V4L2_PIX_FMT_YVU420M:
+ tpg->buffers = 3;
+ fallthrough;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ tpg->vdownsampling[1] = 2;
+ tpg->vdownsampling[2] = 2;
+ tpg->hdownsampling[1] = 2;
+ tpg->hdownsampling[2] = 2;
+ tpg->planes = 3;
+ tpg->color_enc = TGP_COLOR_ENC_YCBCR;
+ break;
+ case V4L2_PIX_FMT_YUV422M:
+ case V4L2_PIX_FMT_YVU422M:
+ tpg->buffers = 3;
+ fallthrough;
+ case V4L2_PIX_FMT_YUV422P:
+ tpg->vdownsampling[1] = 1;
+ tpg->vdownsampling[2] = 1;
+ tpg->hdownsampling[1] = 2;
+ tpg->hdownsampling[2] = 2;
+ tpg->planes = 3;
+ tpg->color_enc = TGP_COLOR_ENC_YCBCR;
+ break;
+ case V4L2_PIX_FMT_NV16M:
+ case V4L2_PIX_FMT_NV61M:
+ tpg->buffers = 2;
+ fallthrough;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ tpg->vdownsampling[1] = 1;
+ tpg->hdownsampling[1] = 1;
+ tpg->hmask[1] = ~1;
+ tpg->planes = 2;
+ tpg->color_enc = TGP_COLOR_ENC_YCBCR;
+ break;
+ case V4L2_PIX_FMT_NV12M:
+ case V4L2_PIX_FMT_NV21M:
+ tpg->buffers = 2;
+ fallthrough;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ tpg->vdownsampling[1] = 2;
+ tpg->hdownsampling[1] = 1;
+ tpg->hmask[1] = ~1;
+ tpg->planes = 2;
+ tpg->color_enc = TGP_COLOR_ENC_YCBCR;
+ break;
+ case V4L2_PIX_FMT_YUV444M:
+ case V4L2_PIX_FMT_YVU444M:
+ tpg->buffers = 3;
+ tpg->planes = 3;
+ tpg->vdownsampling[1] = 1;
+ tpg->vdownsampling[2] = 1;
+ tpg->hdownsampling[1] = 1;
+ tpg->hdownsampling[2] = 1;
+ tpg->color_enc = TGP_COLOR_ENC_YCBCR;
+ break;
+ case V4L2_PIX_FMT_NV24:
+ case V4L2_PIX_FMT_NV42:
+ tpg->vdownsampling[1] = 1;
+ tpg->hdownsampling[1] = 1;
+ tpg->planes = 2;
+ tpg->color_enc = TGP_COLOR_ENC_YCBCR;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_VYUY:
+ tpg->hmask[0] = ~1;
+ tpg->color_enc = TGP_COLOR_ENC_YCBCR;
+ break;
+ case V4L2_PIX_FMT_HSV24:
+ case V4L2_PIX_FMT_HSV32:
+ tpg->color_enc = TGP_COLOR_ENC_HSV;
+ break;
+ default:
+ return false;
+ }
+
+ switch (fourcc) {
+ case V4L2_PIX_FMT_GREY:
+ case V4L2_PIX_FMT_RGB332:
+ tpg->twopixelsize[0] = 2;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ case V4L2_PIX_FMT_RGB444:
+ case V4L2_PIX_FMT_XRGB444:
+ case V4L2_PIX_FMT_ARGB444:
+ case V4L2_PIX_FMT_RGBX444:
+ case V4L2_PIX_FMT_RGBA444:
+ case V4L2_PIX_FMT_XBGR444:
+ case V4L2_PIX_FMT_ABGR444:
+ case V4L2_PIX_FMT_BGRX444:
+ case V4L2_PIX_FMT_BGRA444:
+ case V4L2_PIX_FMT_RGB555:
+ case V4L2_PIX_FMT_XRGB555:
+ case V4L2_PIX_FMT_ARGB555:
+ case V4L2_PIX_FMT_RGBX555:
+ case V4L2_PIX_FMT_RGBA555:
+ case V4L2_PIX_FMT_XBGR555:
+ case V4L2_PIX_FMT_ABGR555:
+ case V4L2_PIX_FMT_BGRX555:
+ case V4L2_PIX_FMT_BGRA555:
+ case V4L2_PIX_FMT_RGB555X:
+ case V4L2_PIX_FMT_XRGB555X:
+ case V4L2_PIX_FMT_ARGB555X:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YUV444:
+ case V4L2_PIX_FMT_YUV555:
+ case V4L2_PIX_FMT_YUV565:
+ case V4L2_PIX_FMT_Y10:
+ case V4L2_PIX_FMT_Y12:
+ case V4L2_PIX_FMT_Y16:
+ case V4L2_PIX_FMT_Y16_BE:
+ case V4L2_PIX_FMT_Z16:
+ tpg->twopixelsize[0] = 2 * 2;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_HSV24:
+ tpg->twopixelsize[0] = 2 * 3;
+ break;
+ case V4L2_PIX_FMT_BGR666:
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_XRGB32:
+ case V4L2_PIX_FMT_XBGR32:
+ case V4L2_PIX_FMT_ARGB32:
+ case V4L2_PIX_FMT_ABGR32:
+ case V4L2_PIX_FMT_RGBX32:
+ case V4L2_PIX_FMT_BGRX32:
+ case V4L2_PIX_FMT_RGBA32:
+ case V4L2_PIX_FMT_BGRA32:
+ case V4L2_PIX_FMT_YUV32:
+ case V4L2_PIX_FMT_AYUV32:
+ case V4L2_PIX_FMT_XYUV32:
+ case V4L2_PIX_FMT_VUYA32:
+ case V4L2_PIX_FMT_VUYX32:
+ case V4L2_PIX_FMT_YUVA32:
+ case V4L2_PIX_FMT_YUVX32:
+ case V4L2_PIX_FMT_HSV32:
+ tpg->twopixelsize[0] = 2 * 4;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV12M:
+ case V4L2_PIX_FMT_NV21M:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_NV16M:
+ case V4L2_PIX_FMT_NV61M:
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ tpg->twopixelsize[0] = 2;
+ tpg->twopixelsize[1] = 2;
+ break;
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SRGGB12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SRGGB16:
+ case V4L2_PIX_FMT_SGRBG16:
+ case V4L2_PIX_FMT_SGBRG16:
+ case V4L2_PIX_FMT_SBGGR16:
+ tpg->twopixelsize[0] = 4;
+ tpg->twopixelsize[1] = 4;
+ break;
+ case V4L2_PIX_FMT_YUV444M:
+ case V4L2_PIX_FMT_YVU444M:
+ case V4L2_PIX_FMT_YUV422M:
+ case V4L2_PIX_FMT_YVU422M:
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_YUV420M:
+ case V4L2_PIX_FMT_YVU420M:
+ tpg->twopixelsize[0] = 2;
+ tpg->twopixelsize[1] = 2;
+ tpg->twopixelsize[2] = 2;
+ break;
+ case V4L2_PIX_FMT_NV24:
+ case V4L2_PIX_FMT_NV42:
+ tpg->twopixelsize[0] = 2;
+ tpg->twopixelsize[1] = 4;
+ break;
+ }
+ return true;
+}
+EXPORT_SYMBOL_GPL(tpg_s_fourcc);
+
+void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop,
+ const struct v4l2_rect *compose)
+{
+ tpg->crop = *crop;
+ tpg->compose = *compose;
+ tpg->scaled_width = (tpg->src_width * tpg->compose.width +
+ tpg->crop.width - 1) / tpg->crop.width;
+ tpg->scaled_width &= ~1;
+ if (tpg->scaled_width > tpg->max_line_width)
+ tpg->scaled_width = tpg->max_line_width;
+ if (tpg->scaled_width < 2)
+ tpg->scaled_width = 2;
+ tpg->recalc_lines = true;
+}
+EXPORT_SYMBOL_GPL(tpg_s_crop_compose);
+
+void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height,
+ u32 field)
+{
+ unsigned p;
+
+ tpg->src_width = width;
+ tpg->src_height = height;
+ tpg->field = field;
+ tpg->buf_height = height;
+ if (V4L2_FIELD_HAS_T_OR_B(field))
+ tpg->buf_height /= 2;
+ tpg->scaled_width = width;
+ tpg->crop.top = tpg->crop.left = 0;
+ tpg->crop.width = width;
+ tpg->crop.height = height;
+ tpg->compose.top = tpg->compose.left = 0;
+ tpg->compose.width = width;
+ tpg->compose.height = tpg->buf_height;
+ for (p = 0; p < tpg->planes; p++)
+ tpg->bytesperline[p] = (width * tpg->twopixelsize[p]) /
+ (2 * tpg->hdownsampling[p]);
+ tpg->recalc_square_border = true;
+}
+EXPORT_SYMBOL_GPL(tpg_reset_source);
+
+static enum tpg_color tpg_get_textbg_color(struct tpg_data *tpg)
+{
+ switch (tpg->pattern) {
+ case TPG_PAT_BLACK:
+ return TPG_COLOR_100_WHITE;
+ case TPG_PAT_CSC_COLORBAR:
+ return TPG_COLOR_CSC_BLACK;
+ default:
+ return TPG_COLOR_100_BLACK;
+ }
+}
+
+static enum tpg_color tpg_get_textfg_color(struct tpg_data *tpg)
+{
+ switch (tpg->pattern) {
+ case TPG_PAT_75_COLORBAR:
+ case TPG_PAT_CSC_COLORBAR:
+ return TPG_COLOR_CSC_WHITE;
+ case TPG_PAT_BLACK:
+ return TPG_COLOR_100_BLACK;
+ default:
+ return TPG_COLOR_100_WHITE;
+ }
+}
+
+static inline int rec709_to_linear(int v)
+{
+ v = clamp(v, 0, 0xff0);
+ return tpg_rec709_to_linear[v];
+}
+
+static inline int linear_to_rec709(int v)
+{
+ v = clamp(v, 0, 0xff0);
+ return tpg_linear_to_rec709[v];
+}
+
+static void color_to_hsv(struct tpg_data *tpg, int r, int g, int b,
+ int *h, int *s, int *v)
+{
+ int max_rgb, min_rgb, diff_rgb;
+ int aux;
+ int third;
+ int third_size;
+
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+
+ /* Value */
+ max_rgb = max3(r, g, b);
+ *v = max_rgb;
+ if (!max_rgb) {
+ *h = 0;
+ *s = 0;
+ return;
+ }
+
+ /* Saturation */
+ min_rgb = min3(r, g, b);
+ diff_rgb = max_rgb - min_rgb;
+ aux = 255 * diff_rgb;
+ aux += max_rgb / 2;
+ aux /= max_rgb;
+ *s = aux;
+ if (!aux) {
+ *h = 0;
+ return;
+ }
+
+ third_size = (tpg->real_hsv_enc == V4L2_HSV_ENC_180) ? 60 : 85;
+
+ /* Hue */
+ if (max_rgb == r) {
+ aux = g - b;
+ third = 0;
+ } else if (max_rgb == g) {
+ aux = b - r;
+ third = third_size;
+ } else {
+ aux = r - g;
+ third = third_size * 2;
+ }
+
+ aux *= third_size / 2;
+ aux += diff_rgb / 2;
+ aux /= diff_rgb;
+ aux += third;
+
+ /* Clamp Hue */
+ if (tpg->real_hsv_enc == V4L2_HSV_ENC_180) {
+ if (aux < 0)
+ aux += 180;
+ else if (aux > 180)
+ aux -= 180;
+ } else {
+ aux = aux & 0xff;
+ }
+
+ *h = aux;
+}
+
+static void rgb2ycbcr(const int m[3][3], int r, int g, int b,
+ int y_offset, int *y, int *cb, int *cr)
+{
+ *y = ((m[0][0] * r + m[0][1] * g + m[0][2] * b) >> 16) + (y_offset << 4);
+ *cb = ((m[1][0] * r + m[1][1] * g + m[1][2] * b) >> 16) + (128 << 4);
+ *cr = ((m[2][0] * r + m[2][1] * g + m[2][2] * b) >> 16) + (128 << 4);
+}
+
+static void color_to_ycbcr(struct tpg_data *tpg, int r, int g, int b,
+ int *y, int *cb, int *cr)
+{
+#define COEFF(v, r) ((int)(0.5 + (v) * (r) * 256.0))
+
+ static const int bt601[3][3] = {
+ { COEFF(0.299, 219), COEFF(0.587, 219), COEFF(0.114, 219) },
+ { COEFF(-0.1687, 224), COEFF(-0.3313, 224), COEFF(0.5, 224) },
+ { COEFF(0.5, 224), COEFF(-0.4187, 224), COEFF(-0.0813, 224) },
+ };
+ static const int bt601_full[3][3] = {
+ { COEFF(0.299, 255), COEFF(0.587, 255), COEFF(0.114, 255) },
+ { COEFF(-0.1687, 255), COEFF(-0.3313, 255), COEFF(0.5, 255) },
+ { COEFF(0.5, 255), COEFF(-0.4187, 255), COEFF(-0.0813, 255) },
+ };
+ static const int rec709[3][3] = {
+ { COEFF(0.2126, 219), COEFF(0.7152, 219), COEFF(0.0722, 219) },
+ { COEFF(-0.1146, 224), COEFF(-0.3854, 224), COEFF(0.5, 224) },
+ { COEFF(0.5, 224), COEFF(-0.4542, 224), COEFF(-0.0458, 224) },
+ };
+ static const int rec709_full[3][3] = {
+ { COEFF(0.2126, 255), COEFF(0.7152, 255), COEFF(0.0722, 255) },
+ { COEFF(-0.1146, 255), COEFF(-0.3854, 255), COEFF(0.5, 255) },
+ { COEFF(0.5, 255), COEFF(-0.4542, 255), COEFF(-0.0458, 255) },
+ };
+ static const int smpte240m[3][3] = {
+ { COEFF(0.212, 219), COEFF(0.701, 219), COEFF(0.087, 219) },
+ { COEFF(-0.116, 224), COEFF(-0.384, 224), COEFF(0.5, 224) },
+ { COEFF(0.5, 224), COEFF(-0.445, 224), COEFF(-0.055, 224) },
+ };
+ static const int smpte240m_full[3][3] = {
+ { COEFF(0.212, 255), COEFF(0.701, 255), COEFF(0.087, 255) },
+ { COEFF(-0.116, 255), COEFF(-0.384, 255), COEFF(0.5, 255) },
+ { COEFF(0.5, 255), COEFF(-0.445, 255), COEFF(-0.055, 255) },
+ };
+ static const int bt2020[3][3] = {
+ { COEFF(0.2627, 219), COEFF(0.6780, 219), COEFF(0.0593, 219) },
+ { COEFF(-0.1396, 224), COEFF(-0.3604, 224), COEFF(0.5, 224) },
+ { COEFF(0.5, 224), COEFF(-0.4598, 224), COEFF(-0.0402, 224) },
+ };
+ static const int bt2020_full[3][3] = {
+ { COEFF(0.2627, 255), COEFF(0.6780, 255), COEFF(0.0593, 255) },
+ { COEFF(-0.1396, 255), COEFF(-0.3604, 255), COEFF(0.5, 255) },
+ { COEFF(0.5, 255), COEFF(-0.4598, 255), COEFF(-0.0402, 255) },
+ };
+ static const int bt2020c[4] = {
+ COEFF(1.0 / 1.9404, 224), COEFF(1.0 / 1.5816, 224),
+ COEFF(1.0 / 1.7184, 224), COEFF(1.0 / 0.9936, 224),
+ };
+ static const int bt2020c_full[4] = {
+ COEFF(1.0 / 1.9404, 255), COEFF(1.0 / 1.5816, 255),
+ COEFF(1.0 / 1.7184, 255), COEFF(1.0 / 0.9936, 255),
+ };
+
+ bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE;
+ unsigned y_offset = full ? 0 : 16;
+ int lin_y, yc;
+
+ switch (tpg->real_ycbcr_enc) {
+ case V4L2_YCBCR_ENC_601:
+ rgb2ycbcr(full ? bt601_full : bt601, r, g, b, y_offset, y, cb, cr);
+ break;
+ case V4L2_YCBCR_ENC_XV601:
+ /* Ignore quantization range, there is only one possible
+ * Y'CbCr encoding. */
+ rgb2ycbcr(bt601, r, g, b, 16, y, cb, cr);
+ break;
+ case V4L2_YCBCR_ENC_XV709:
+ /* Ignore quantization range, there is only one possible
+ * Y'CbCr encoding. */
+ rgb2ycbcr(rec709, r, g, b, 16, y, cb, cr);
+ break;
+ case V4L2_YCBCR_ENC_BT2020:
+ rgb2ycbcr(full ? bt2020_full : bt2020, r, g, b, y_offset, y, cb, cr);
+ break;
+ case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
+ lin_y = (COEFF(0.2627, 255) * rec709_to_linear(r) +
+ COEFF(0.6780, 255) * rec709_to_linear(g) +
+ COEFF(0.0593, 255) * rec709_to_linear(b)) >> 16;
+ yc = linear_to_rec709(lin_y);
+ *y = full ? yc : (yc * 219) / 255 + (16 << 4);
+ if (b <= yc)
+ *cb = (((b - yc) * (full ? bt2020c_full[0] : bt2020c[0])) >> 16) + (128 << 4);
+ else
+ *cb = (((b - yc) * (full ? bt2020c_full[1] : bt2020c[1])) >> 16) + (128 << 4);
+ if (r <= yc)
+ *cr = (((r - yc) * (full ? bt2020c_full[2] : bt2020c[2])) >> 16) + (128 << 4);
+ else
+ *cr = (((r - yc) * (full ? bt2020c_full[3] : bt2020c[3])) >> 16) + (128 << 4);
+ break;
+ case V4L2_YCBCR_ENC_SMPTE240M:
+ rgb2ycbcr(full ? smpte240m_full : smpte240m, r, g, b, y_offset, y, cb, cr);
+ break;
+ case V4L2_YCBCR_ENC_709:
+ default:
+ rgb2ycbcr(full ? rec709_full : rec709, r, g, b, y_offset, y, cb, cr);
+ break;
+ }
+}
+
+static void ycbcr2rgb(const int m[3][3], int y, int cb, int cr,
+ int y_offset, int *r, int *g, int *b)
+{
+ y -= y_offset << 4;
+ cb -= 128 << 4;
+ cr -= 128 << 4;
+ *r = m[0][0] * y + m[0][1] * cb + m[0][2] * cr;
+ *g = m[1][0] * y + m[1][1] * cb + m[1][2] * cr;
+ *b = m[2][0] * y + m[2][1] * cb + m[2][2] * cr;
+ *r = clamp(*r >> 12, 0, 0xff0);
+ *g = clamp(*g >> 12, 0, 0xff0);
+ *b = clamp(*b >> 12, 0, 0xff0);
+}
+
+static void ycbcr_to_color(struct tpg_data *tpg, int y, int cb, int cr,
+ int *r, int *g, int *b)
+{
+#undef COEFF
+#define COEFF(v, r) ((int)(0.5 + (v) * ((255.0 * 255.0 * 16.0) / (r))))
+ static const int bt601[3][3] = {
+ { COEFF(1, 219), COEFF(0, 224), COEFF(1.4020, 224) },
+ { COEFF(1, 219), COEFF(-0.3441, 224), COEFF(-0.7141, 224) },
+ { COEFF(1, 219), COEFF(1.7720, 224), COEFF(0, 224) },
+ };
+ static const int bt601_full[3][3] = {
+ { COEFF(1, 255), COEFF(0, 255), COEFF(1.4020, 255) },
+ { COEFF(1, 255), COEFF(-0.3441, 255), COEFF(-0.7141, 255) },
+ { COEFF(1, 255), COEFF(1.7720, 255), COEFF(0, 255) },
+ };
+ static const int rec709[3][3] = {
+ { COEFF(1, 219), COEFF(0, 224), COEFF(1.5748, 224) },
+ { COEFF(1, 219), COEFF(-0.1873, 224), COEFF(-0.4681, 224) },
+ { COEFF(1, 219), COEFF(1.8556, 224), COEFF(0, 224) },
+ };
+ static const int rec709_full[3][3] = {
+ { COEFF(1, 255), COEFF(0, 255), COEFF(1.5748, 255) },
+ { COEFF(1, 255), COEFF(-0.1873, 255), COEFF(-0.4681, 255) },
+ { COEFF(1, 255), COEFF(1.8556, 255), COEFF(0, 255) },
+ };
+ static const int smpte240m[3][3] = {
+ { COEFF(1, 219), COEFF(0, 224), COEFF(1.5756, 224) },
+ { COEFF(1, 219), COEFF(-0.2253, 224), COEFF(-0.4767, 224) },
+ { COEFF(1, 219), COEFF(1.8270, 224), COEFF(0, 224) },
+ };
+ static const int smpte240m_full[3][3] = {
+ { COEFF(1, 255), COEFF(0, 255), COEFF(1.5756, 255) },
+ { COEFF(1, 255), COEFF(-0.2253, 255), COEFF(-0.4767, 255) },
+ { COEFF(1, 255), COEFF(1.8270, 255), COEFF(0, 255) },
+ };
+ static const int bt2020[3][3] = {
+ { COEFF(1, 219), COEFF(0, 224), COEFF(1.4746, 224) },
+ { COEFF(1, 219), COEFF(-0.1646, 224), COEFF(-0.5714, 224) },
+ { COEFF(1, 219), COEFF(1.8814, 224), COEFF(0, 224) },
+ };
+ static const int bt2020_full[3][3] = {
+ { COEFF(1, 255), COEFF(0, 255), COEFF(1.4746, 255) },
+ { COEFF(1, 255), COEFF(-0.1646, 255), COEFF(-0.5714, 255) },
+ { COEFF(1, 255), COEFF(1.8814, 255), COEFF(0, 255) },
+ };
+ static const int bt2020c[4] = {
+ COEFF(1.9404, 224), COEFF(1.5816, 224),
+ COEFF(1.7184, 224), COEFF(0.9936, 224),
+ };
+ static const int bt2020c_full[4] = {
+ COEFF(1.9404, 255), COEFF(1.5816, 255),
+ COEFF(1.7184, 255), COEFF(0.9936, 255),
+ };
+
+ bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE;
+ unsigned y_offset = full ? 0 : 16;
+ int y_fac = full ? COEFF(1.0, 255) : COEFF(1.0, 219);
+ int lin_r, lin_g, lin_b, lin_y;
+
+ switch (tpg->real_ycbcr_enc) {
+ case V4L2_YCBCR_ENC_601:
+ ycbcr2rgb(full ? bt601_full : bt601, y, cb, cr, y_offset, r, g, b);
+ break;
+ case V4L2_YCBCR_ENC_XV601:
+ /* Ignore quantization range, there is only one possible
+ * Y'CbCr encoding. */
+ ycbcr2rgb(bt601, y, cb, cr, 16, r, g, b);
+ break;
+ case V4L2_YCBCR_ENC_XV709:
+ /* Ignore quantization range, there is only one possible
+ * Y'CbCr encoding. */
+ ycbcr2rgb(rec709, y, cb, cr, 16, r, g, b);
+ break;
+ case V4L2_YCBCR_ENC_BT2020:
+ ycbcr2rgb(full ? bt2020_full : bt2020, y, cb, cr, y_offset, r, g, b);
+ break;
+ case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
+ y -= full ? 0 : 16 << 4;
+ cb -= 128 << 4;
+ cr -= 128 << 4;
+
+ if (cb <= 0)
+ *b = y_fac * y + (full ? bt2020c_full[0] : bt2020c[0]) * cb;
+ else
+ *b = y_fac * y + (full ? bt2020c_full[1] : bt2020c[1]) * cb;
+ *b = *b >> 12;
+ if (cr <= 0)
+ *r = y_fac * y + (full ? bt2020c_full[2] : bt2020c[2]) * cr;
+ else
+ *r = y_fac * y + (full ? bt2020c_full[3] : bt2020c[3]) * cr;
+ *r = *r >> 12;
+ lin_r = rec709_to_linear(*r);
+ lin_b = rec709_to_linear(*b);
+ lin_y = rec709_to_linear((y * 255) / (full ? 255 : 219));
+
+ lin_g = COEFF(1.0 / 0.6780, 255) * lin_y -
+ COEFF(0.2627 / 0.6780, 255) * lin_r -
+ COEFF(0.0593 / 0.6780, 255) * lin_b;
+ *g = linear_to_rec709(lin_g >> 12);
+ break;
+ case V4L2_YCBCR_ENC_SMPTE240M:
+ ycbcr2rgb(full ? smpte240m_full : smpte240m, y, cb, cr, y_offset, r, g, b);
+ break;
+ case V4L2_YCBCR_ENC_709:
+ default:
+ ycbcr2rgb(full ? rec709_full : rec709, y, cb, cr, y_offset, r, g, b);
+ break;
+ }
+}
+
+/* precalculate color bar values to speed up rendering */
+static void precalculate_color(struct tpg_data *tpg, int k)
+{
+ int col = k;
+ int r = tpg_colors[col].r;
+ int g = tpg_colors[col].g;
+ int b = tpg_colors[col].b;
+ int y, cb, cr;
+ bool ycbcr_valid = false;
+
+ if (k == TPG_COLOR_TEXTBG) {
+ col = tpg_get_textbg_color(tpg);
+
+ r = tpg_colors[col].r;
+ g = tpg_colors[col].g;
+ b = tpg_colors[col].b;
+ } else if (k == TPG_COLOR_TEXTFG) {
+ col = tpg_get_textfg_color(tpg);
+
+ r = tpg_colors[col].r;
+ g = tpg_colors[col].g;
+ b = tpg_colors[col].b;
+ } else if (tpg->pattern == TPG_PAT_NOISE) {
+ r = g = b = get_random_u8();
+ } else if (k == TPG_COLOR_RANDOM) {
+ r = g = b = tpg->qual_offset + get_random_u32_below(196);
+ } else if (k >= TPG_COLOR_RAMP) {
+ r = g = b = k - TPG_COLOR_RAMP;
+ }
+
+ if (tpg->pattern == TPG_PAT_CSC_COLORBAR && col <= TPG_COLOR_CSC_BLACK) {
+ r = tpg_csc_colors[tpg->colorspace][tpg->real_xfer_func][col].r;
+ g = tpg_csc_colors[tpg->colorspace][tpg->real_xfer_func][col].g;
+ b = tpg_csc_colors[tpg->colorspace][tpg->real_xfer_func][col].b;
+ } else {
+ r <<= 4;
+ g <<= 4;
+ b <<= 4;
+ }
+
+ if (tpg->qual == TPG_QUAL_GRAY ||
+ tpg->color_enc == TGP_COLOR_ENC_LUMA) {
+ /* Rec. 709 Luma function */
+ /* (0.2126, 0.7152, 0.0722) * (255 * 256) */
+ r = g = b = (13879 * r + 46688 * g + 4713 * b) >> 16;
+ }
+
+ /*
+ * The assumption is that the RGB output is always full range,
+ * so only if the rgb_range overrides the 'real' rgb range do
+ * we need to convert the RGB values.
+ *
+ * Remember that r, g and b are still in the 0 - 0xff0 range.
+ */
+ if (tpg->real_rgb_range == V4L2_DV_RGB_RANGE_LIMITED &&
+ tpg->rgb_range == V4L2_DV_RGB_RANGE_FULL &&
+ tpg->color_enc == TGP_COLOR_ENC_RGB) {
+ /*
+ * Convert from full range (which is what r, g and b are)
+ * to limited range (which is the 'real' RGB range), which
+ * is then interpreted as full range.
+ */
+ r = (r * 219) / 255 + (16 << 4);
+ g = (g * 219) / 255 + (16 << 4);
+ b = (b * 219) / 255 + (16 << 4);
+ } else if (tpg->real_rgb_range != V4L2_DV_RGB_RANGE_LIMITED &&
+ tpg->rgb_range == V4L2_DV_RGB_RANGE_LIMITED &&
+ tpg->color_enc == TGP_COLOR_ENC_RGB) {
+
+ /*
+ * Clamp r, g and b to the limited range and convert to full
+ * range since that's what we deliver.
+ */
+ r = clamp(r, 16 << 4, 235 << 4);
+ g = clamp(g, 16 << 4, 235 << 4);
+ b = clamp(b, 16 << 4, 235 << 4);
+ r = (r - (16 << 4)) * 255 / 219;
+ g = (g - (16 << 4)) * 255 / 219;
+ b = (b - (16 << 4)) * 255 / 219;
+ }
+
+ if ((tpg->brightness != 128 || tpg->contrast != 128 ||
+ tpg->saturation != 128 || tpg->hue) &&
+ tpg->color_enc != TGP_COLOR_ENC_LUMA) {
+ /* Implement these operations */
+ int tmp_cb, tmp_cr;
+
+ /* First convert to YCbCr */
+
+ color_to_ycbcr(tpg, r, g, b, &y, &cb, &cr);
+
+ y = (16 << 4) + ((y - (16 << 4)) * tpg->contrast) / 128;
+ y += (tpg->brightness << 4) - (128 << 4);
+
+ cb -= 128 << 4;
+ cr -= 128 << 4;
+ tmp_cb = (cb * cos(128 + tpg->hue)) / 127 + (cr * sin[128 + tpg->hue]) / 127;
+ tmp_cr = (cr * cos(128 + tpg->hue)) / 127 - (cb * sin[128 + tpg->hue]) / 127;
+
+ cb = (128 << 4) + (tmp_cb * tpg->contrast * tpg->saturation) / (128 * 128);
+ cr = (128 << 4) + (tmp_cr * tpg->contrast * tpg->saturation) / (128 * 128);
+ if (tpg->color_enc == TGP_COLOR_ENC_YCBCR)
+ ycbcr_valid = true;
+ else
+ ycbcr_to_color(tpg, y, cb, cr, &r, &g, &b);
+ } else if ((tpg->brightness != 128 || tpg->contrast != 128) &&
+ tpg->color_enc == TGP_COLOR_ENC_LUMA) {
+ r = (16 << 4) + ((r - (16 << 4)) * tpg->contrast) / 128;
+ r += (tpg->brightness << 4) - (128 << 4);
+ }
+
+ switch (tpg->color_enc) {
+ case TGP_COLOR_ENC_HSV:
+ {
+ int h, s, v;
+
+ color_to_hsv(tpg, r, g, b, &h, &s, &v);
+ tpg->colors[k][0] = h;
+ tpg->colors[k][1] = s;
+ tpg->colors[k][2] = v;
+ break;
+ }
+ case TGP_COLOR_ENC_YCBCR:
+ {
+ /* Convert to YCbCr */
+ if (!ycbcr_valid)
+ color_to_ycbcr(tpg, r, g, b, &y, &cb, &cr);
+
+ y >>= 4;
+ cb >>= 4;
+ cr >>= 4;
+ /*
+ * XV601/709 use the header/footer margins to encode R', G'
+ * and B' values outside the range [0-1]. So do not clamp
+ * XV601/709 values.
+ */
+ if (tpg->real_quantization == V4L2_QUANTIZATION_LIM_RANGE &&
+ tpg->real_ycbcr_enc != V4L2_YCBCR_ENC_XV601 &&
+ tpg->real_ycbcr_enc != V4L2_YCBCR_ENC_XV709) {
+ y = clamp(y, 16, 235);
+ cb = clamp(cb, 16, 240);
+ cr = clamp(cr, 16, 240);
+ } else {
+ y = clamp(y, 1, 254);
+ cb = clamp(cb, 1, 254);
+ cr = clamp(cr, 1, 254);
+ }
+ switch (tpg->fourcc) {
+ case V4L2_PIX_FMT_YUV444:
+ y >>= 4;
+ cb >>= 4;
+ cr >>= 4;
+ break;
+ case V4L2_PIX_FMT_YUV555:
+ y >>= 3;
+ cb >>= 3;
+ cr >>= 3;
+ break;
+ case V4L2_PIX_FMT_YUV565:
+ y >>= 3;
+ cb >>= 2;
+ cr >>= 3;
+ break;
+ }
+ tpg->colors[k][0] = y;
+ tpg->colors[k][1] = cb;
+ tpg->colors[k][2] = cr;
+ break;
+ }
+ case TGP_COLOR_ENC_LUMA:
+ {
+ tpg->colors[k][0] = r >> 4;
+ break;
+ }
+ case TGP_COLOR_ENC_RGB:
+ {
+ if (tpg->real_quantization == V4L2_QUANTIZATION_LIM_RANGE) {
+ r = (r * 219) / 255 + (16 << 4);
+ g = (g * 219) / 255 + (16 << 4);
+ b = (b * 219) / 255 + (16 << 4);
+ }
+ switch (tpg->fourcc) {
+ case V4L2_PIX_FMT_RGB332:
+ r >>= 9;
+ g >>= 9;
+ b >>= 10;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ r >>= 7;
+ g >>= 6;
+ b >>= 7;
+ break;
+ case V4L2_PIX_FMT_RGB444:
+ case V4L2_PIX_FMT_XRGB444:
+ case V4L2_PIX_FMT_ARGB444:
+ case V4L2_PIX_FMT_RGBX444:
+ case V4L2_PIX_FMT_RGBA444:
+ case V4L2_PIX_FMT_XBGR444:
+ case V4L2_PIX_FMT_ABGR444:
+ case V4L2_PIX_FMT_BGRX444:
+ case V4L2_PIX_FMT_BGRA444:
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ case V4L2_PIX_FMT_XRGB555:
+ case V4L2_PIX_FMT_ARGB555:
+ case V4L2_PIX_FMT_RGBX555:
+ case V4L2_PIX_FMT_RGBA555:
+ case V4L2_PIX_FMT_XBGR555:
+ case V4L2_PIX_FMT_ABGR555:
+ case V4L2_PIX_FMT_BGRX555:
+ case V4L2_PIX_FMT_BGRA555:
+ case V4L2_PIX_FMT_RGB555X:
+ case V4L2_PIX_FMT_XRGB555X:
+ case V4L2_PIX_FMT_ARGB555X:
+ r >>= 7;
+ g >>= 7;
+ b >>= 7;
+ break;
+ case V4L2_PIX_FMT_BGR666:
+ r >>= 6;
+ g >>= 6;
+ b >>= 6;
+ break;
+ default:
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ break;
+ }
+
+ tpg->colors[k][0] = r;
+ tpg->colors[k][1] = g;
+ tpg->colors[k][2] = b;
+ break;
+ }
+ }
+}
+
+static void tpg_precalculate_colors(struct tpg_data *tpg)
+{
+ int k;
+
+ for (k = 0; k < TPG_COLOR_MAX; k++)
+ precalculate_color(tpg, k);
+}
+
+/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
+static void gen_twopix(struct tpg_data *tpg,
+ u8 buf[TPG_MAX_PLANES][8], int color, bool odd)
+{
+ unsigned offset = odd * tpg->twopixelsize[0] / 2;
+ u8 alpha = tpg->alpha_component;
+ u8 r_y_h, g_u_s, b_v;
+
+ if (tpg->alpha_red_only && color != TPG_COLOR_CSC_RED &&
+ color != TPG_COLOR_100_RED &&
+ color != TPG_COLOR_75_RED)
+ alpha = 0;
+ if (color == TPG_COLOR_RANDOM)
+ precalculate_color(tpg, color);
+ r_y_h = tpg->colors[color][0]; /* R or precalculated Y, H */
+ g_u_s = tpg->colors[color][1]; /* G or precalculated U, V */
+ b_v = tpg->colors[color][2]; /* B or precalculated V */
+
+ switch (tpg->fourcc) {
+ case V4L2_PIX_FMT_GREY:
+ buf[0][offset] = r_y_h;
+ break;
+ case V4L2_PIX_FMT_Y10:
+ buf[0][offset] = (r_y_h << 2) & 0xff;
+ buf[0][offset+1] = r_y_h >> 6;
+ break;
+ case V4L2_PIX_FMT_Y12:
+ buf[0][offset] = (r_y_h << 4) & 0xff;
+ buf[0][offset+1] = r_y_h >> 4;
+ break;
+ case V4L2_PIX_FMT_Y16:
+ case V4L2_PIX_FMT_Z16:
+ /*
+ * Ideally both bytes should be set to r_y_h, but then you won't
+ * be able to detect endian problems. So keep it 0 except for
+ * the corner case where r_y_h is 0xff so white really will be
+ * white (0xffff).
+ */
+ buf[0][offset] = r_y_h == 0xff ? r_y_h : 0;
+ buf[0][offset+1] = r_y_h;
+ break;
+ case V4L2_PIX_FMT_Y16_BE:
+ /* See comment for V4L2_PIX_FMT_Y16 above */
+ buf[0][offset] = r_y_h;
+ buf[0][offset+1] = r_y_h == 0xff ? r_y_h : 0;
+ break;
+ case V4L2_PIX_FMT_YUV422M:
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YUV420M:
+ buf[0][offset] = r_y_h;
+ if (odd) {
+ buf[1][0] = (buf[1][0] + g_u_s) / 2;
+ buf[2][0] = (buf[2][0] + b_v) / 2;
+ buf[1][1] = buf[1][0];
+ buf[2][1] = buf[2][0];
+ break;
+ }
+ buf[1][0] = g_u_s;
+ buf[2][0] = b_v;
+ break;
+ case V4L2_PIX_FMT_YVU422M:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_YVU420M:
+ buf[0][offset] = r_y_h;
+ if (odd) {
+ buf[1][0] = (buf[1][0] + b_v) / 2;
+ buf[2][0] = (buf[2][0] + g_u_s) / 2;
+ buf[1][1] = buf[1][0];
+ buf[2][1] = buf[2][0];
+ break;
+ }
+ buf[1][0] = b_v;
+ buf[2][0] = g_u_s;
+ break;
+
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV12M:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV16M:
+ buf[0][offset] = r_y_h;
+ if (odd) {
+ buf[1][0] = (buf[1][0] + g_u_s) / 2;
+ buf[1][1] = (buf[1][1] + b_v) / 2;
+ break;
+ }
+ buf[1][0] = g_u_s;
+ buf[1][1] = b_v;
+ break;
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV21M:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_NV61M:
+ buf[0][offset] = r_y_h;
+ if (odd) {
+ buf[1][0] = (buf[1][0] + b_v) / 2;
+ buf[1][1] = (buf[1][1] + g_u_s) / 2;
+ break;
+ }
+ buf[1][0] = b_v;
+ buf[1][1] = g_u_s;
+ break;
+
+ case V4L2_PIX_FMT_YUV444M:
+ buf[0][offset] = r_y_h;
+ buf[1][offset] = g_u_s;
+ buf[2][offset] = b_v;
+ break;
+
+ case V4L2_PIX_FMT_YVU444M:
+ buf[0][offset] = r_y_h;
+ buf[1][offset] = b_v;
+ buf[2][offset] = g_u_s;
+ break;
+
+ case V4L2_PIX_FMT_NV24:
+ buf[0][offset] = r_y_h;
+ buf[1][2 * offset] = g_u_s;
+ buf[1][(2 * offset + 1) % 8] = b_v;
+ break;
+
+ case V4L2_PIX_FMT_NV42:
+ buf[0][offset] = r_y_h;
+ buf[1][2 * offset] = b_v;
+ buf[1][(2 * offset + 1) % 8] = g_u_s;
+ break;
+
+ case V4L2_PIX_FMT_YUYV:
+ buf[0][offset] = r_y_h;
+ if (odd) {
+ buf[0][1] = (buf[0][1] + g_u_s) / 2;
+ buf[0][3] = (buf[0][3] + b_v) / 2;
+ break;
+ }
+ buf[0][1] = g_u_s;
+ buf[0][3] = b_v;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ buf[0][offset + 1] = r_y_h;
+ if (odd) {
+ buf[0][0] = (buf[0][0] + g_u_s) / 2;
+ buf[0][2] = (buf[0][2] + b_v) / 2;
+ break;
+ }
+ buf[0][0] = g_u_s;
+ buf[0][2] = b_v;
+ break;
+ case V4L2_PIX_FMT_YVYU:
+ buf[0][offset] = r_y_h;
+ if (odd) {
+ buf[0][1] = (buf[0][1] + b_v) / 2;
+ buf[0][3] = (buf[0][3] + g_u_s) / 2;
+ break;
+ }
+ buf[0][1] = b_v;
+ buf[0][3] = g_u_s;
+ break;
+ case V4L2_PIX_FMT_VYUY:
+ buf[0][offset + 1] = r_y_h;
+ if (odd) {
+ buf[0][0] = (buf[0][0] + b_v) / 2;
+ buf[0][2] = (buf[0][2] + g_u_s) / 2;
+ break;
+ }
+ buf[0][0] = b_v;
+ buf[0][2] = g_u_s;
+ break;
+ case V4L2_PIX_FMT_RGB332:
+ buf[0][offset] = (r_y_h << 5) | (g_u_s << 2) | b_v;
+ break;
+ case V4L2_PIX_FMT_YUV565:
+ case V4L2_PIX_FMT_RGB565:
+ buf[0][offset] = (g_u_s << 5) | b_v;
+ buf[0][offset + 1] = (r_y_h << 3) | (g_u_s >> 3);
+ break;
+ case V4L2_PIX_FMT_RGB565X:
+ buf[0][offset] = (r_y_h << 3) | (g_u_s >> 3);
+ buf[0][offset + 1] = (g_u_s << 5) | b_v;
+ break;
+ case V4L2_PIX_FMT_RGB444:
+ case V4L2_PIX_FMT_XRGB444:
+ alpha = 0;
+ fallthrough;
+ case V4L2_PIX_FMT_YUV444:
+ case V4L2_PIX_FMT_ARGB444:
+ buf[0][offset] = (g_u_s << 4) | b_v;
+ buf[0][offset + 1] = (alpha & 0xf0) | r_y_h;
+ break;
+ case V4L2_PIX_FMT_RGBX444:
+ alpha = 0;
+ fallthrough;
+ case V4L2_PIX_FMT_RGBA444:
+ buf[0][offset] = (b_v << 4) | (alpha >> 4);
+ buf[0][offset + 1] = (r_y_h << 4) | g_u_s;
+ break;
+ case V4L2_PIX_FMT_XBGR444:
+ alpha = 0;
+ fallthrough;
+ case V4L2_PIX_FMT_ABGR444:
+ buf[0][offset] = (g_u_s << 4) | r_y_h;
+ buf[0][offset + 1] = (alpha & 0xf0) | b_v;
+ break;
+ case V4L2_PIX_FMT_BGRX444:
+ alpha = 0;
+ fallthrough;
+ case V4L2_PIX_FMT_BGRA444:
+ buf[0][offset] = (r_y_h << 4) | (alpha >> 4);
+ buf[0][offset + 1] = (b_v << 4) | g_u_s;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ case V4L2_PIX_FMT_XRGB555:
+ alpha = 0;
+ fallthrough;
+ case V4L2_PIX_FMT_YUV555:
+ case V4L2_PIX_FMT_ARGB555:
+ buf[0][offset] = (g_u_s << 5) | b_v;
+ buf[0][offset + 1] = (alpha & 0x80) | (r_y_h << 2)
+ | (g_u_s >> 3);
+ break;
+ case V4L2_PIX_FMT_RGBX555:
+ alpha = 0;
+ fallthrough;
+ case V4L2_PIX_FMT_RGBA555:
+ buf[0][offset] = (g_u_s << 6) | (b_v << 1) |
+ ((alpha & 0x80) >> 7);
+ buf[0][offset + 1] = (r_y_h << 3) | (g_u_s >> 2);
+ break;
+ case V4L2_PIX_FMT_XBGR555:
+ alpha = 0;
+ fallthrough;
+ case V4L2_PIX_FMT_ABGR555:
+ buf[0][offset] = (g_u_s << 5) | r_y_h;
+ buf[0][offset + 1] = (alpha & 0x80) | (b_v << 2)
+ | (g_u_s >> 3);
+ break;
+ case V4L2_PIX_FMT_BGRX555:
+ alpha = 0;
+ fallthrough;
+ case V4L2_PIX_FMT_BGRA555:
+ buf[0][offset] = (g_u_s << 6) | (r_y_h << 1) |
+ ((alpha & 0x80) >> 7);
+ buf[0][offset + 1] = (b_v << 3) | (g_u_s >> 2);
+ break;
+ case V4L2_PIX_FMT_RGB555X:
+ case V4L2_PIX_FMT_XRGB555X:
+ alpha = 0;
+ fallthrough;
+ case V4L2_PIX_FMT_ARGB555X:
+ buf[0][offset] = (alpha & 0x80) | (r_y_h << 2) | (g_u_s >> 3);
+ buf[0][offset + 1] = (g_u_s << 5) | b_v;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_HSV24:
+ buf[0][offset] = r_y_h;
+ buf[0][offset + 1] = g_u_s;
+ buf[0][offset + 2] = b_v;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ buf[0][offset] = b_v;
+ buf[0][offset + 1] = g_u_s;
+ buf[0][offset + 2] = r_y_h;
+ break;
+ case V4L2_PIX_FMT_BGR666:
+ buf[0][offset] = (b_v << 2) | (g_u_s >> 4);
+ buf[0][offset + 1] = (g_u_s << 4) | (r_y_h >> 2);
+ buf[0][offset + 2] = r_y_h << 6;
+ buf[0][offset + 3] = 0;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_XRGB32:
+ case V4L2_PIX_FMT_HSV32:
+ case V4L2_PIX_FMT_XYUV32:
+ alpha = 0;
+ fallthrough;
+ case V4L2_PIX_FMT_YUV32:
+ case V4L2_PIX_FMT_ARGB32:
+ case V4L2_PIX_FMT_AYUV32:
+ buf[0][offset] = alpha;
+ buf[0][offset + 1] = r_y_h;
+ buf[0][offset + 2] = g_u_s;
+ buf[0][offset + 3] = b_v;
+ break;
+ case V4L2_PIX_FMT_RGBX32:
+ case V4L2_PIX_FMT_YUVX32:
+ alpha = 0;
+ fallthrough;
+ case V4L2_PIX_FMT_RGBA32:
+ case V4L2_PIX_FMT_YUVA32:
+ buf[0][offset] = r_y_h;
+ buf[0][offset + 1] = g_u_s;
+ buf[0][offset + 2] = b_v;
+ buf[0][offset + 3] = alpha;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_XBGR32:
+ case V4L2_PIX_FMT_VUYX32:
+ alpha = 0;
+ fallthrough;
+ case V4L2_PIX_FMT_ABGR32:
+ case V4L2_PIX_FMT_VUYA32:
+ buf[0][offset] = b_v;
+ buf[0][offset + 1] = g_u_s;
+ buf[0][offset + 2] = r_y_h;
+ buf[0][offset + 3] = alpha;
+ break;
+ case V4L2_PIX_FMT_BGRX32:
+ alpha = 0;
+ fallthrough;
+ case V4L2_PIX_FMT_BGRA32:
+ buf[0][offset] = alpha;
+ buf[0][offset + 1] = b_v;
+ buf[0][offset + 2] = g_u_s;
+ buf[0][offset + 3] = r_y_h;
+ break;
+ case V4L2_PIX_FMT_SBGGR8:
+ buf[0][offset] = odd ? g_u_s : b_v;
+ buf[1][offset] = odd ? r_y_h : g_u_s;
+ break;
+ case V4L2_PIX_FMT_SGBRG8:
+ buf[0][offset] = odd ? b_v : g_u_s;
+ buf[1][offset] = odd ? g_u_s : r_y_h;
+ break;
+ case V4L2_PIX_FMT_SGRBG8:
+ buf[0][offset] = odd ? r_y_h : g_u_s;
+ buf[1][offset] = odd ? g_u_s : b_v;
+ break;
+ case V4L2_PIX_FMT_SRGGB8:
+ buf[0][offset] = odd ? g_u_s : r_y_h;
+ buf[1][offset] = odd ? b_v : g_u_s;
+ break;
+ case V4L2_PIX_FMT_SBGGR10:
+ buf[0][offset] = odd ? g_u_s << 2 : b_v << 2;
+ buf[0][offset + 1] = odd ? g_u_s >> 6 : b_v >> 6;
+ buf[1][offset] = odd ? r_y_h << 2 : g_u_s << 2;
+ buf[1][offset + 1] = odd ? r_y_h >> 6 : g_u_s >> 6;
+ buf[0][offset] |= (buf[0][offset] >> 2) & 3;
+ buf[1][offset] |= (buf[1][offset] >> 2) & 3;
+ break;
+ case V4L2_PIX_FMT_SGBRG10:
+ buf[0][offset] = odd ? b_v << 2 : g_u_s << 2;
+ buf[0][offset + 1] = odd ? b_v >> 6 : g_u_s >> 6;
+ buf[1][offset] = odd ? g_u_s << 2 : r_y_h << 2;
+ buf[1][offset + 1] = odd ? g_u_s >> 6 : r_y_h >> 6;
+ buf[0][offset] |= (buf[0][offset] >> 2) & 3;
+ buf[1][offset] |= (buf[1][offset] >> 2) & 3;
+ break;
+ case V4L2_PIX_FMT_SGRBG10:
+ buf[0][offset] = odd ? r_y_h << 2 : g_u_s << 2;
+ buf[0][offset + 1] = odd ? r_y_h >> 6 : g_u_s >> 6;
+ buf[1][offset] = odd ? g_u_s << 2 : b_v << 2;
+ buf[1][offset + 1] = odd ? g_u_s >> 6 : b_v >> 6;
+ buf[0][offset] |= (buf[0][offset] >> 2) & 3;
+ buf[1][offset] |= (buf[1][offset] >> 2) & 3;
+ break;
+ case V4L2_PIX_FMT_SRGGB10:
+ buf[0][offset] = odd ? g_u_s << 2 : r_y_h << 2;
+ buf[0][offset + 1] = odd ? g_u_s >> 6 : r_y_h >> 6;
+ buf[1][offset] = odd ? b_v << 2 : g_u_s << 2;
+ buf[1][offset + 1] = odd ? b_v >> 6 : g_u_s >> 6;
+ buf[0][offset] |= (buf[0][offset] >> 2) & 3;
+ buf[1][offset] |= (buf[1][offset] >> 2) & 3;
+ break;
+ case V4L2_PIX_FMT_SBGGR12:
+ buf[0][offset] = odd ? g_u_s << 4 : b_v << 4;
+ buf[0][offset + 1] = odd ? g_u_s >> 4 : b_v >> 4;
+ buf[1][offset] = odd ? r_y_h << 4 : g_u_s << 4;
+ buf[1][offset + 1] = odd ? r_y_h >> 4 : g_u_s >> 4;
+ buf[0][offset] |= (buf[0][offset] >> 4) & 0xf;
+ buf[1][offset] |= (buf[1][offset] >> 4) & 0xf;
+ break;
+ case V4L2_PIX_FMT_SGBRG12:
+ buf[0][offset] = odd ? b_v << 4 : g_u_s << 4;
+ buf[0][offset + 1] = odd ? b_v >> 4 : g_u_s >> 4;
+ buf[1][offset] = odd ? g_u_s << 4 : r_y_h << 4;
+ buf[1][offset + 1] = odd ? g_u_s >> 4 : r_y_h >> 4;
+ buf[0][offset] |= (buf[0][offset] >> 4) & 0xf;
+ buf[1][offset] |= (buf[1][offset] >> 4) & 0xf;
+ break;
+ case V4L2_PIX_FMT_SGRBG12:
+ buf[0][offset] = odd ? r_y_h << 4 : g_u_s << 4;
+ buf[0][offset + 1] = odd ? r_y_h >> 4 : g_u_s >> 4;
+ buf[1][offset] = odd ? g_u_s << 4 : b_v << 4;
+ buf[1][offset + 1] = odd ? g_u_s >> 4 : b_v >> 4;
+ buf[0][offset] |= (buf[0][offset] >> 4) & 0xf;
+ buf[1][offset] |= (buf[1][offset] >> 4) & 0xf;
+ break;
+ case V4L2_PIX_FMT_SRGGB12:
+ buf[0][offset] = odd ? g_u_s << 4 : r_y_h << 4;
+ buf[0][offset + 1] = odd ? g_u_s >> 4 : r_y_h >> 4;
+ buf[1][offset] = odd ? b_v << 4 : g_u_s << 4;
+ buf[1][offset + 1] = odd ? b_v >> 4 : g_u_s >> 4;
+ buf[0][offset] |= (buf[0][offset] >> 4) & 0xf;
+ buf[1][offset] |= (buf[1][offset] >> 4) & 0xf;
+ break;
+ case V4L2_PIX_FMT_SBGGR16:
+ buf[0][offset] = buf[0][offset + 1] = odd ? g_u_s : b_v;
+ buf[1][offset] = buf[1][offset + 1] = odd ? r_y_h : g_u_s;
+ break;
+ case V4L2_PIX_FMT_SGBRG16:
+ buf[0][offset] = buf[0][offset + 1] = odd ? b_v : g_u_s;
+ buf[1][offset] = buf[1][offset + 1] = odd ? g_u_s : r_y_h;
+ break;
+ case V4L2_PIX_FMT_SGRBG16:
+ buf[0][offset] = buf[0][offset + 1] = odd ? r_y_h : g_u_s;
+ buf[1][offset] = buf[1][offset + 1] = odd ? g_u_s : b_v;
+ break;
+ case V4L2_PIX_FMT_SRGGB16:
+ buf[0][offset] = buf[0][offset + 1] = odd ? g_u_s : r_y_h;
+ buf[1][offset] = buf[1][offset + 1] = odd ? b_v : g_u_s;
+ break;
+ }
+}
+
+unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line)
+{
+ switch (tpg->fourcc) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ case V4L2_PIX_FMT_SBGGR16:
+ case V4L2_PIX_FMT_SGBRG16:
+ case V4L2_PIX_FMT_SGRBG16:
+ case V4L2_PIX_FMT_SRGGB16:
+ return buf_line & 1;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(tpg_g_interleaved_plane);
+
+/* Return how many pattern lines are used by the current pattern. */
+static unsigned tpg_get_pat_lines(const struct tpg_data *tpg)
+{
+ switch (tpg->pattern) {
+ case TPG_PAT_CHECKERS_16X16:
+ case TPG_PAT_CHECKERS_2X2:
+ case TPG_PAT_CHECKERS_1X1:
+ case TPG_PAT_COLOR_CHECKERS_2X2:
+ case TPG_PAT_COLOR_CHECKERS_1X1:
+ case TPG_PAT_ALTERNATING_HLINES:
+ case TPG_PAT_CROSS_1_PIXEL:
+ case TPG_PAT_CROSS_2_PIXELS:
+ case TPG_PAT_CROSS_10_PIXELS:
+ return 2;
+ case TPG_PAT_100_COLORSQUARES:
+ case TPG_PAT_100_HCOLORBAR:
+ return 8;
+ default:
+ return 1;
+ }
+}
+
+/* Which pattern line should be used for the given frame line. */
+static unsigned tpg_get_pat_line(const struct tpg_data *tpg, unsigned line)
+{
+ switch (tpg->pattern) {
+ case TPG_PAT_CHECKERS_16X16:
+ return (line >> 4) & 1;
+ case TPG_PAT_CHECKERS_1X1:
+ case TPG_PAT_COLOR_CHECKERS_1X1:
+ case TPG_PAT_ALTERNATING_HLINES:
+ return line & 1;
+ case TPG_PAT_CHECKERS_2X2:
+ case TPG_PAT_COLOR_CHECKERS_2X2:
+ return (line & 2) >> 1;
+ case TPG_PAT_100_COLORSQUARES:
+ case TPG_PAT_100_HCOLORBAR:
+ return (line * 8) / tpg->src_height;
+ case TPG_PAT_CROSS_1_PIXEL:
+ return line == tpg->src_height / 2;
+ case TPG_PAT_CROSS_2_PIXELS:
+ return (line + 1) / 2 == tpg->src_height / 4;
+ case TPG_PAT_CROSS_10_PIXELS:
+ return (line + 10) / 20 == tpg->src_height / 40;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Which color should be used for the given pattern line and X coordinate.
+ * Note: x is in the range 0 to 2 * tpg->src_width.
+ */
+static enum tpg_color tpg_get_color(const struct tpg_data *tpg,
+ unsigned pat_line, unsigned x)
+{
+ /* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code
+ should be modified */
+ static const enum tpg_color bars[3][8] = {
+ /* Standard ITU-R 75% color bar sequence */
+ { TPG_COLOR_CSC_WHITE, TPG_COLOR_75_YELLOW,
+ TPG_COLOR_75_CYAN, TPG_COLOR_75_GREEN,
+ TPG_COLOR_75_MAGENTA, TPG_COLOR_75_RED,
+ TPG_COLOR_75_BLUE, TPG_COLOR_100_BLACK, },
+ /* Standard ITU-R 100% color bar sequence */
+ { TPG_COLOR_100_WHITE, TPG_COLOR_100_YELLOW,
+ TPG_COLOR_100_CYAN, TPG_COLOR_100_GREEN,
+ TPG_COLOR_100_MAGENTA, TPG_COLOR_100_RED,
+ TPG_COLOR_100_BLUE, TPG_COLOR_100_BLACK, },
+ /* Color bar sequence suitable to test CSC */
+ { TPG_COLOR_CSC_WHITE, TPG_COLOR_CSC_YELLOW,
+ TPG_COLOR_CSC_CYAN, TPG_COLOR_CSC_GREEN,
+ TPG_COLOR_CSC_MAGENTA, TPG_COLOR_CSC_RED,
+ TPG_COLOR_CSC_BLUE, TPG_COLOR_CSC_BLACK, },
+ };
+
+ switch (tpg->pattern) {
+ case TPG_PAT_75_COLORBAR:
+ case TPG_PAT_100_COLORBAR:
+ case TPG_PAT_CSC_COLORBAR:
+ return bars[tpg->pattern][((x * 8) / tpg->src_width) % 8];
+ case TPG_PAT_100_COLORSQUARES:
+ return bars[1][(pat_line + (x * 8) / tpg->src_width) % 8];
+ case TPG_PAT_100_HCOLORBAR:
+ return bars[1][pat_line];
+ case TPG_PAT_BLACK:
+ return TPG_COLOR_100_BLACK;
+ case TPG_PAT_WHITE:
+ return TPG_COLOR_100_WHITE;
+ case TPG_PAT_RED:
+ return TPG_COLOR_100_RED;
+ case TPG_PAT_GREEN:
+ return TPG_COLOR_100_GREEN;
+ case TPG_PAT_BLUE:
+ return TPG_COLOR_100_BLUE;
+ case TPG_PAT_CHECKERS_16X16:
+ return (((x >> 4) & 1) ^ (pat_line & 1)) ?
+ TPG_COLOR_100_BLACK : TPG_COLOR_100_WHITE;
+ case TPG_PAT_CHECKERS_1X1:
+ return ((x & 1) ^ (pat_line & 1)) ?
+ TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
+ case TPG_PAT_COLOR_CHECKERS_1X1:
+ return ((x & 1) ^ (pat_line & 1)) ?
+ TPG_COLOR_100_RED : TPG_COLOR_100_BLUE;
+ case TPG_PAT_CHECKERS_2X2:
+ return (((x >> 1) & 1) ^ (pat_line & 1)) ?
+ TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
+ case TPG_PAT_COLOR_CHECKERS_2X2:
+ return (((x >> 1) & 1) ^ (pat_line & 1)) ?
+ TPG_COLOR_100_RED : TPG_COLOR_100_BLUE;
+ case TPG_PAT_ALTERNATING_HLINES:
+ return pat_line ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
+ case TPG_PAT_ALTERNATING_VLINES:
+ return (x & 1) ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
+ case TPG_PAT_CROSS_1_PIXEL:
+ if (pat_line || (x % tpg->src_width) == tpg->src_width / 2)
+ return TPG_COLOR_100_BLACK;
+ return TPG_COLOR_100_WHITE;
+ case TPG_PAT_CROSS_2_PIXELS:
+ if (pat_line || ((x % tpg->src_width) + 1) / 2 == tpg->src_width / 4)
+ return TPG_COLOR_100_BLACK;
+ return TPG_COLOR_100_WHITE;
+ case TPG_PAT_CROSS_10_PIXELS:
+ if (pat_line || ((x % tpg->src_width) + 10) / 20 == tpg->src_width / 40)
+ return TPG_COLOR_100_BLACK;
+ return TPG_COLOR_100_WHITE;
+ case TPG_PAT_GRAY_RAMP:
+ return TPG_COLOR_RAMP + ((x % tpg->src_width) * 256) / tpg->src_width;
+ default:
+ return TPG_COLOR_100_RED;
+ }
+}
+
+/*
+ * Given the pixel aspect ratio and video aspect ratio calculate the
+ * coordinates of a centered square and the coordinates of the border of
+ * the active video area. The coordinates are relative to the source
+ * frame rectangle.
+ */
+static void tpg_calculate_square_border(struct tpg_data *tpg)
+{
+ unsigned w = tpg->src_width;
+ unsigned h = tpg->src_height;
+ unsigned sq_w, sq_h;
+
+ sq_w = (w * 2 / 5) & ~1;
+ if (((w - sq_w) / 2) & 1)
+ sq_w += 2;
+ sq_h = sq_w;
+ tpg->square.width = sq_w;
+ if (tpg->vid_aspect == TPG_VIDEO_ASPECT_16X9_ANAMORPHIC) {
+ unsigned ana_sq_w = (sq_w / 4) * 3;
+
+ if (((w - ana_sq_w) / 2) & 1)
+ ana_sq_w += 2;
+ tpg->square.width = ana_sq_w;
+ }
+ tpg->square.left = (w - tpg->square.width) / 2;
+ if (tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC)
+ sq_h = sq_w * 10 / 11;
+ else if (tpg->pix_aspect == TPG_PIXEL_ASPECT_PAL)
+ sq_h = sq_w * 59 / 54;
+ tpg->square.height = sq_h;
+ tpg->square.top = (h - sq_h) / 2;
+ tpg->border.left = 0;
+ tpg->border.width = w;
+ tpg->border.top = 0;
+ tpg->border.height = h;
+ switch (tpg->vid_aspect) {
+ case TPG_VIDEO_ASPECT_4X3:
+ if (tpg->pix_aspect)
+ return;
+ if (3 * w >= 4 * h) {
+ tpg->border.width = ((4 * h) / 3) & ~1;
+ if (((w - tpg->border.width) / 2) & ~1)
+ tpg->border.width -= 2;
+ tpg->border.left = (w - tpg->border.width) / 2;
+ break;
+ }
+ tpg->border.height = ((3 * w) / 4) & ~1;
+ tpg->border.top = (h - tpg->border.height) / 2;
+ break;
+ case TPG_VIDEO_ASPECT_14X9_CENTRE:
+ if (tpg->pix_aspect) {
+ tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 420 : 506;
+ tpg->border.top = (h - tpg->border.height) / 2;
+ break;
+ }
+ if (9 * w >= 14 * h) {
+ tpg->border.width = ((14 * h) / 9) & ~1;
+ if (((w - tpg->border.width) / 2) & ~1)
+ tpg->border.width -= 2;
+ tpg->border.left = (w - tpg->border.width) / 2;
+ break;
+ }
+ tpg->border.height = ((9 * w) / 14) & ~1;
+ tpg->border.top = (h - tpg->border.height) / 2;
+ break;
+ case TPG_VIDEO_ASPECT_16X9_CENTRE:
+ if (tpg->pix_aspect) {
+ tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 368 : 442;
+ tpg->border.top = (h - tpg->border.height) / 2;
+ break;
+ }
+ if (9 * w >= 16 * h) {
+ tpg->border.width = ((16 * h) / 9) & ~1;
+ if (((w - tpg->border.width) / 2) & ~1)
+ tpg->border.width -= 2;
+ tpg->border.left = (w - tpg->border.width) / 2;
+ break;
+ }
+ tpg->border.height = ((9 * w) / 16) & ~1;
+ tpg->border.top = (h - tpg->border.height) / 2;
+ break;
+ default:
+ break;
+ }
+}
+
+static void tpg_precalculate_line(struct tpg_data *tpg)
+{
+ enum tpg_color contrast;
+ u8 pix[TPG_MAX_PLANES][8];
+ unsigned pat;
+ unsigned p;
+ unsigned x;
+
+ if (WARN_ON_ONCE(!tpg->src_width || !tpg->scaled_width))
+ return;
+
+ switch (tpg->pattern) {
+ case TPG_PAT_GREEN:
+ contrast = TPG_COLOR_100_RED;
+ break;
+ case TPG_PAT_CSC_COLORBAR:
+ contrast = TPG_COLOR_CSC_GREEN;
+ break;
+ default:
+ contrast = TPG_COLOR_100_GREEN;
+ break;
+ }
+
+ for (pat = 0; pat < tpg_get_pat_lines(tpg); pat++) {
+ /* Coarse scaling with Bresenham */
+ unsigned int_part = tpg->src_width / tpg->scaled_width;
+ unsigned fract_part = tpg->src_width % tpg->scaled_width;
+ unsigned src_x = 0;
+ unsigned error = 0;
+
+ for (x = 0; x < tpg->scaled_width * 2; x += 2) {
+ unsigned real_x = src_x;
+ enum tpg_color color1, color2;
+
+ real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x;
+ color1 = tpg_get_color(tpg, pat, real_x);
+
+ src_x += int_part;
+ error += fract_part;
+ if (error >= tpg->scaled_width) {
+ error -= tpg->scaled_width;
+ src_x++;
+ }
+
+ real_x = src_x;
+ real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x;
+ color2 = tpg_get_color(tpg, pat, real_x);
+
+ src_x += int_part;
+ error += fract_part;
+ if (error >= tpg->scaled_width) {
+ error -= tpg->scaled_width;
+ src_x++;
+ }
+
+ gen_twopix(tpg, pix, tpg->hflip ? color2 : color1, 0);
+ gen_twopix(tpg, pix, tpg->hflip ? color1 : color2, 1);
+ for (p = 0; p < tpg->planes; p++) {
+ unsigned twopixsize = tpg->twopixelsize[p];
+ unsigned hdiv = tpg->hdownsampling[p];
+ u8 *pos = tpg->lines[pat][p] + tpg_hdiv(tpg, p, x);
+
+ memcpy(pos, pix[p], twopixsize / hdiv);
+ }
+ }
+ }
+
+ if (tpg->vdownsampling[tpg->planes - 1] > 1) {
+ unsigned pat_lines = tpg_get_pat_lines(tpg);
+
+ for (pat = 0; pat < pat_lines; pat++) {
+ unsigned next_pat = (pat + 1) % pat_lines;
+
+ for (p = 1; p < tpg->planes; p++) {
+ unsigned w = tpg_hdiv(tpg, p, tpg->scaled_width * 2);
+ u8 *pos1 = tpg->lines[pat][p];
+ u8 *pos2 = tpg->lines[next_pat][p];
+ u8 *dest = tpg->downsampled_lines[pat][p];
+
+ for (x = 0; x < w; x++, pos1++, pos2++, dest++)
+ *dest = ((u16)*pos1 + (u16)*pos2) / 2;
+ }
+ }
+ }
+
+ gen_twopix(tpg, pix, contrast, 0);
+ gen_twopix(tpg, pix, contrast, 1);
+ for (p = 0; p < tpg->planes; p++) {
+ unsigned twopixsize = tpg->twopixelsize[p];
+ u8 *pos = tpg->contrast_line[p];
+
+ for (x = 0; x < tpg->scaled_width; x += 2, pos += twopixsize)
+ memcpy(pos, pix[p], twopixsize);
+ }
+
+ gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 0);
+ gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 1);
+ for (p = 0; p < tpg->planes; p++) {
+ unsigned twopixsize = tpg->twopixelsize[p];
+ u8 *pos = tpg->black_line[p];
+
+ for (x = 0; x < tpg->scaled_width; x += 2, pos += twopixsize)
+ memcpy(pos, pix[p], twopixsize);
+ }
+
+ for (x = 0; x < tpg->scaled_width * 2; x += 2) {
+ gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 0);
+ gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 1);
+ for (p = 0; p < tpg->planes; p++) {
+ unsigned twopixsize = tpg->twopixelsize[p];
+ u8 *pos = tpg->random_line[p] + x * twopixsize / 2;
+
+ memcpy(pos, pix[p], twopixsize);
+ }
+ }
+
+ gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 0);
+ gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 1);
+ gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 0);
+ gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 1);
+}
+
+/* need this to do rgb24 rendering */
+typedef struct { u16 __; u8 _; } __packed x24;
+
+#define PRINTSTR(PIXTYPE) do { \
+ unsigned vdiv = tpg->vdownsampling[p]; \
+ unsigned hdiv = tpg->hdownsampling[p]; \
+ int line; \
+ PIXTYPE fg; \
+ PIXTYPE bg; \
+ memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE)); \
+ memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE)); \
+ \
+ for (line = first; line < 16; line += vdiv * step) { \
+ int l = tpg->vflip ? 15 - line : line; \
+ PIXTYPE *pos = (PIXTYPE *)(basep[p][(line / vdiv) & 1] + \
+ ((y * step + l) / (vdiv * div)) * tpg->bytesperline[p] + \
+ (x / hdiv) * sizeof(PIXTYPE)); \
+ unsigned s; \
+ \
+ for (s = 0; s < len; s++) { \
+ u8 chr = font8x16[(u8)text[s] * 16 + line]; \
+ \
+ if (hdiv == 2 && tpg->hflip) { \
+ pos[3] = (chr & (0x01 << 6) ? fg : bg); \
+ pos[2] = (chr & (0x01 << 4) ? fg : bg); \
+ pos[1] = (chr & (0x01 << 2) ? fg : bg); \
+ pos[0] = (chr & (0x01 << 0) ? fg : bg); \
+ } else if (hdiv == 2) { \
+ pos[0] = (chr & (0x01 << 7) ? fg : bg); \
+ pos[1] = (chr & (0x01 << 5) ? fg : bg); \
+ pos[2] = (chr & (0x01 << 3) ? fg : bg); \
+ pos[3] = (chr & (0x01 << 1) ? fg : bg); \
+ } else if (tpg->hflip) { \
+ pos[7] = (chr & (0x01 << 7) ? fg : bg); \
+ pos[6] = (chr & (0x01 << 6) ? fg : bg); \
+ pos[5] = (chr & (0x01 << 5) ? fg : bg); \
+ pos[4] = (chr & (0x01 << 4) ? fg : bg); \
+ pos[3] = (chr & (0x01 << 3) ? fg : bg); \
+ pos[2] = (chr & (0x01 << 2) ? fg : bg); \
+ pos[1] = (chr & (0x01 << 1) ? fg : bg); \
+ pos[0] = (chr & (0x01 << 0) ? fg : bg); \
+ } else { \
+ pos[0] = (chr & (0x01 << 7) ? fg : bg); \
+ pos[1] = (chr & (0x01 << 6) ? fg : bg); \
+ pos[2] = (chr & (0x01 << 5) ? fg : bg); \
+ pos[3] = (chr & (0x01 << 4) ? fg : bg); \
+ pos[4] = (chr & (0x01 << 3) ? fg : bg); \
+ pos[5] = (chr & (0x01 << 2) ? fg : bg); \
+ pos[6] = (chr & (0x01 << 1) ? fg : bg); \
+ pos[7] = (chr & (0x01 << 0) ? fg : bg); \
+ } \
+ \
+ pos += (tpg->hflip ? -8 : 8) / (int)hdiv; \
+ } \
+ } \
+} while (0)
+
+static noinline void tpg_print_str_2(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
+ unsigned p, unsigned first, unsigned div, unsigned step,
+ int y, int x, const char *text, unsigned len)
+{
+ PRINTSTR(u8);
+}
+
+static noinline void tpg_print_str_4(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
+ unsigned p, unsigned first, unsigned div, unsigned step,
+ int y, int x, const char *text, unsigned len)
+{
+ PRINTSTR(u16);
+}
+
+static noinline void tpg_print_str_6(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
+ unsigned p, unsigned first, unsigned div, unsigned step,
+ int y, int x, const char *text, unsigned len)
+{
+ PRINTSTR(x24);
+}
+
+static noinline void tpg_print_str_8(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
+ unsigned p, unsigned first, unsigned div, unsigned step,
+ int y, int x, const char *text, unsigned len)
+{
+ PRINTSTR(u32);
+}
+
+void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
+ int y, int x, const char *text)
+{
+ unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
+ unsigned div = step;
+ unsigned first = 0;
+ unsigned len;
+ unsigned p;
+
+ if (font8x16 == NULL || basep == NULL || text == NULL)
+ return;
+
+ len = strlen(text);
+
+ /* Checks if it is possible to show string */
+ if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width)
+ return;
+
+ if (len > (tpg->compose.width - x) / 8)
+ len = (tpg->compose.width - x) / 8;
+ if (tpg->vflip)
+ y = tpg->compose.height - y - 16;
+ if (tpg->hflip)
+ x = tpg->compose.width - x - 8;
+ y += tpg->compose.top;
+ x += tpg->compose.left;
+ if (tpg->field == V4L2_FIELD_BOTTOM)
+ first = 1;
+ else if (tpg->field == V4L2_FIELD_SEQ_TB || tpg->field == V4L2_FIELD_SEQ_BT)
+ div = 2;
+
+ for (p = 0; p < tpg->planes; p++) {
+ /* Print text */
+ switch (tpg->twopixelsize[p]) {
+ case 2:
+ tpg_print_str_2(tpg, basep, p, first, div, step, y, x,
+ text, len);
+ break;
+ case 4:
+ tpg_print_str_4(tpg, basep, p, first, div, step, y, x,
+ text, len);
+ break;
+ case 6:
+ tpg_print_str_6(tpg, basep, p, first, div, step, y, x,
+ text, len);
+ break;
+ case 8:
+ tpg_print_str_8(tpg, basep, p, first, div, step, y, x,
+ text, len);
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(tpg_gen_text);
+
+const char *tpg_g_color_order(const struct tpg_data *tpg)
+{
+ switch (tpg->pattern) {
+ case TPG_PAT_75_COLORBAR:
+ case TPG_PAT_100_COLORBAR:
+ case TPG_PAT_CSC_COLORBAR:
+ case TPG_PAT_100_HCOLORBAR:
+ return "White, yellow, cyan, green, magenta, red, blue, black";
+ case TPG_PAT_BLACK:
+ return "Black";
+ case TPG_PAT_WHITE:
+ return "White";
+ case TPG_PAT_RED:
+ return "Red";
+ case TPG_PAT_GREEN:
+ return "Green";
+ case TPG_PAT_BLUE:
+ return "Blue";
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(tpg_g_color_order);
+
+void tpg_update_mv_step(struct tpg_data *tpg)
+{
+ int factor = tpg->mv_hor_mode > TPG_MOVE_NONE ? -1 : 1;
+
+ if (tpg->hflip)
+ factor = -factor;
+ switch (tpg->mv_hor_mode) {
+ case TPG_MOVE_NEG_FAST:
+ case TPG_MOVE_POS_FAST:
+ tpg->mv_hor_step = ((tpg->src_width + 319) / 320) * 4;
+ break;
+ case TPG_MOVE_NEG:
+ case TPG_MOVE_POS:
+ tpg->mv_hor_step = ((tpg->src_width + 639) / 640) * 4;
+ break;
+ case TPG_MOVE_NEG_SLOW:
+ case TPG_MOVE_POS_SLOW:
+ tpg->mv_hor_step = 2;
+ break;
+ case TPG_MOVE_NONE:
+ tpg->mv_hor_step = 0;
+ break;
+ }
+ if (factor < 0)
+ tpg->mv_hor_step = tpg->src_width - tpg->mv_hor_step;
+
+ factor = tpg->mv_vert_mode > TPG_MOVE_NONE ? -1 : 1;
+ switch (tpg->mv_vert_mode) {
+ case TPG_MOVE_NEG_FAST:
+ case TPG_MOVE_POS_FAST:
+ tpg->mv_vert_step = ((tpg->src_width + 319) / 320) * 4;
+ break;
+ case TPG_MOVE_NEG:
+ case TPG_MOVE_POS:
+ tpg->mv_vert_step = ((tpg->src_width + 639) / 640) * 4;
+ break;
+ case TPG_MOVE_NEG_SLOW:
+ case TPG_MOVE_POS_SLOW:
+ tpg->mv_vert_step = 1;
+ break;
+ case TPG_MOVE_NONE:
+ tpg->mv_vert_step = 0;
+ break;
+ }
+ if (factor < 0)
+ tpg->mv_vert_step = tpg->src_height - tpg->mv_vert_step;
+}
+EXPORT_SYMBOL_GPL(tpg_update_mv_step);
+
+/* Map the line number relative to the crop rectangle to a frame line number */
+static unsigned tpg_calc_frameline(const struct tpg_data *tpg, unsigned src_y,
+ unsigned field)
+{
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ return tpg->crop.top + src_y * 2;
+ case V4L2_FIELD_BOTTOM:
+ return tpg->crop.top + src_y * 2 + 1;
+ default:
+ return src_y + tpg->crop.top;
+ }
+}
+
+/*
+ * Map the line number relative to the compose rectangle to a destination
+ * buffer line number.
+ */
+static unsigned tpg_calc_buffer_line(const struct tpg_data *tpg, unsigned y,
+ unsigned field)
+{
+ y += tpg->compose.top;
+ switch (field) {
+ case V4L2_FIELD_SEQ_TB:
+ if (y & 1)
+ return tpg->buf_height / 2 + y / 2;
+ return y / 2;
+ case V4L2_FIELD_SEQ_BT:
+ if (y & 1)
+ return y / 2;
+ return tpg->buf_height / 2 + y / 2;
+ default:
+ return y;
+ }
+}
+
+static void tpg_recalc(struct tpg_data *tpg)
+{
+ if (tpg->recalc_colors) {
+ tpg->recalc_colors = false;
+ tpg->recalc_lines = true;
+ tpg->real_xfer_func = tpg->xfer_func;
+ tpg->real_ycbcr_enc = tpg->ycbcr_enc;
+ tpg->real_hsv_enc = tpg->hsv_enc;
+ tpg->real_quantization = tpg->quantization;
+
+ if (tpg->xfer_func == V4L2_XFER_FUNC_DEFAULT)
+ tpg->real_xfer_func =
+ V4L2_MAP_XFER_FUNC_DEFAULT(tpg->colorspace);
+
+ if (tpg->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT)
+ tpg->real_ycbcr_enc =
+ V4L2_MAP_YCBCR_ENC_DEFAULT(tpg->colorspace);
+
+ if (tpg->quantization == V4L2_QUANTIZATION_DEFAULT)
+ tpg->real_quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(
+ tpg->color_enc != TGP_COLOR_ENC_YCBCR,
+ tpg->colorspace, tpg->real_ycbcr_enc);
+
+ tpg_precalculate_colors(tpg);
+ }
+ if (tpg->recalc_square_border) {
+ tpg->recalc_square_border = false;
+ tpg_calculate_square_border(tpg);
+ }
+ if (tpg->recalc_lines) {
+ tpg->recalc_lines = false;
+ tpg_precalculate_line(tpg);
+ }
+}
+
+void tpg_calc_text_basep(struct tpg_data *tpg,
+ u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf)
+{
+ unsigned stride = tpg->bytesperline[p];
+ unsigned h = tpg->buf_height;
+
+ tpg_recalc(tpg);
+
+ basep[p][0] = vbuf;
+ basep[p][1] = vbuf;
+ h /= tpg->vdownsampling[p];
+ if (tpg->field == V4L2_FIELD_SEQ_TB)
+ basep[p][1] += h * stride / 2;
+ else if (tpg->field == V4L2_FIELD_SEQ_BT)
+ basep[p][0] += h * stride / 2;
+ if (p == 0 && tpg->interleaved)
+ tpg_calc_text_basep(tpg, basep, 1, vbuf);
+}
+EXPORT_SYMBOL_GPL(tpg_calc_text_basep);
+
+static int tpg_pattern_avg(const struct tpg_data *tpg,
+ unsigned pat1, unsigned pat2)
+{
+ unsigned pat_lines = tpg_get_pat_lines(tpg);
+
+ if (pat1 == (pat2 + 1) % pat_lines)
+ return pat2;
+ if (pat2 == (pat1 + 1) % pat_lines)
+ return pat1;
+ return -1;
+}
+
+static const char *tpg_color_enc_str(enum tgp_color_enc
+ color_enc)
+{
+ switch (color_enc) {
+ case TGP_COLOR_ENC_HSV:
+ return "HSV";
+ case TGP_COLOR_ENC_YCBCR:
+ return "Y'CbCr";
+ case TGP_COLOR_ENC_LUMA:
+ return "Luma";
+ case TGP_COLOR_ENC_RGB:
+ default:
+ return "R'G'B";
+
+ }
+}
+
+void tpg_log_status(struct tpg_data *tpg)
+{
+ pr_info("tpg source WxH: %ux%u (%s)\n",
+ tpg->src_width, tpg->src_height,
+ tpg_color_enc_str(tpg->color_enc));
+ pr_info("tpg field: %u\n", tpg->field);
+ pr_info("tpg crop: (%d,%d)/%ux%u\n", tpg->crop.left, tpg->crop.top,
+ tpg->crop.width, tpg->crop.height);
+ pr_info("tpg compose: (%d,%d)/%ux%u\n", tpg->compose.left, tpg->compose.top,
+ tpg->compose.width, tpg->compose.height);
+ pr_info("tpg colorspace: %d\n", tpg->colorspace);
+ pr_info("tpg transfer function: %d/%d\n", tpg->xfer_func, tpg->real_xfer_func);
+ if (tpg->color_enc == TGP_COLOR_ENC_HSV)
+ pr_info("tpg HSV encoding: %d/%d\n",
+ tpg->hsv_enc, tpg->real_hsv_enc);
+ else if (tpg->color_enc == TGP_COLOR_ENC_YCBCR)
+ pr_info("tpg Y'CbCr encoding: %d/%d\n",
+ tpg->ycbcr_enc, tpg->real_ycbcr_enc);
+ pr_info("tpg quantization: %d/%d\n", tpg->quantization, tpg->real_quantization);
+ pr_info("tpg RGB range: %d/%d\n", tpg->rgb_range, tpg->real_rgb_range);
+}
+EXPORT_SYMBOL_GPL(tpg_log_status);
+
+/*
+ * This struct contains common parameters used by both the drawing of the
+ * test pattern and the drawing of the extras (borders, square, etc.)
+ */
+struct tpg_draw_params {
+ /* common data */
+ bool is_tv;
+ bool is_60hz;
+ unsigned twopixsize;
+ unsigned img_width;
+ unsigned stride;
+ unsigned hmax;
+ unsigned frame_line;
+ unsigned frame_line_next;
+
+ /* test pattern */
+ unsigned mv_hor_old;
+ unsigned mv_hor_new;
+ unsigned mv_vert_old;
+ unsigned mv_vert_new;
+
+ /* extras */
+ unsigned wss_width;
+ unsigned wss_random_offset;
+ unsigned sav_eav_f;
+ unsigned left_pillar_width;
+ unsigned right_pillar_start;
+};
+
+static void tpg_fill_params_pattern(const struct tpg_data *tpg, unsigned p,
+ struct tpg_draw_params *params)
+{
+ params->mv_hor_old =
+ tpg_hscale_div(tpg, p, tpg->mv_hor_count % tpg->src_width);
+ params->mv_hor_new =
+ tpg_hscale_div(tpg, p, (tpg->mv_hor_count + tpg->mv_hor_step) %
+ tpg->src_width);
+ params->mv_vert_old = tpg->mv_vert_count % tpg->src_height;
+ params->mv_vert_new =
+ (tpg->mv_vert_count + tpg->mv_vert_step) % tpg->src_height;
+}
+
+static void tpg_fill_params_extras(const struct tpg_data *tpg,
+ unsigned p,
+ struct tpg_draw_params *params)
+{
+ unsigned left_pillar_width = 0;
+ unsigned right_pillar_start = params->img_width;
+
+ params->wss_width = tpg->crop.left < tpg->src_width / 2 ?
+ tpg->src_width / 2 - tpg->crop.left : 0;
+ if (params->wss_width > tpg->crop.width)
+ params->wss_width = tpg->crop.width;
+ params->wss_width = tpg_hscale_div(tpg, p, params->wss_width);
+ params->wss_random_offset =
+ params->twopixsize * get_random_u32_below(tpg->src_width / 2);
+
+ if (tpg->crop.left < tpg->border.left) {
+ left_pillar_width = tpg->border.left - tpg->crop.left;
+ if (left_pillar_width > tpg->crop.width)
+ left_pillar_width = tpg->crop.width;
+ left_pillar_width = tpg_hscale_div(tpg, p, left_pillar_width);
+ }
+ params->left_pillar_width = left_pillar_width;
+
+ if (tpg->crop.left + tpg->crop.width >
+ tpg->border.left + tpg->border.width) {
+ right_pillar_start =
+ tpg->border.left + tpg->border.width - tpg->crop.left;
+ right_pillar_start =
+ tpg_hscale_div(tpg, p, right_pillar_start);
+ if (right_pillar_start > params->img_width)
+ right_pillar_start = params->img_width;
+ }
+ params->right_pillar_start = right_pillar_start;
+
+ params->sav_eav_f = tpg->field ==
+ (params->is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+}
+
+static void tpg_fill_plane_extras(const struct tpg_data *tpg,
+ const struct tpg_draw_params *params,
+ unsigned p, unsigned h, u8 *vbuf)
+{
+ unsigned twopixsize = params->twopixsize;
+ unsigned img_width = params->img_width;
+ unsigned frame_line = params->frame_line;
+ const struct v4l2_rect *sq = &tpg->square;
+ const struct v4l2_rect *b = &tpg->border;
+ const struct v4l2_rect *c = &tpg->crop;
+
+ if (params->is_tv && !params->is_60hz &&
+ frame_line == 0 && params->wss_width) {
+ /*
+ * Replace the first half of the top line of a 50 Hz frame
+ * with random data to simulate a WSS signal.
+ */
+ u8 *wss = tpg->random_line[p] + params->wss_random_offset;
+
+ memcpy(vbuf, wss, params->wss_width);
+ }
+
+ if (tpg->show_border && frame_line >= b->top &&
+ frame_line < b->top + b->height) {
+ unsigned bottom = b->top + b->height - 1;
+ unsigned left = params->left_pillar_width;
+ unsigned right = params->right_pillar_start;
+
+ if (frame_line == b->top || frame_line == b->top + 1 ||
+ frame_line == bottom || frame_line == bottom - 1) {
+ memcpy(vbuf + left, tpg->contrast_line[p],
+ right - left);
+ } else {
+ if (b->left >= c->left &&
+ b->left < c->left + c->width)
+ memcpy(vbuf + left,
+ tpg->contrast_line[p], twopixsize);
+ if (b->left + b->width > c->left &&
+ b->left + b->width <= c->left + c->width)
+ memcpy(vbuf + right - twopixsize,
+ tpg->contrast_line[p], twopixsize);
+ }
+ }
+ if (tpg->qual != TPG_QUAL_NOISE && frame_line >= b->top &&
+ frame_line < b->top + b->height) {
+ memcpy(vbuf, tpg->black_line[p], params->left_pillar_width);
+ memcpy(vbuf + params->right_pillar_start, tpg->black_line[p],
+ img_width - params->right_pillar_start);
+ }
+ if (tpg->show_square && frame_line >= sq->top &&
+ frame_line < sq->top + sq->height &&
+ sq->left < c->left + c->width &&
+ sq->left + sq->width >= c->left) {
+ unsigned left = sq->left;
+ unsigned width = sq->width;
+
+ if (c->left > left) {
+ width -= c->left - left;
+ left = c->left;
+ }
+ if (c->left + c->width < left + width)
+ width -= left + width - c->left - c->width;
+ left -= c->left;
+ left = tpg_hscale_div(tpg, p, left);
+ width = tpg_hscale_div(tpg, p, width);
+ memcpy(vbuf + left, tpg->contrast_line[p], width);
+ }
+ if (tpg->insert_sav) {
+ unsigned offset = tpg_hdiv(tpg, p, tpg->compose.width / 3);
+ u8 *p = vbuf + offset;
+ unsigned vact = 0, hact = 0;
+
+ p[0] = 0xff;
+ p[1] = 0;
+ p[2] = 0;
+ p[3] = 0x80 | (params->sav_eav_f << 6) |
+ (vact << 5) | (hact << 4) |
+ ((hact ^ vact) << 3) |
+ ((hact ^ params->sav_eav_f) << 2) |
+ ((params->sav_eav_f ^ vact) << 1) |
+ (hact ^ vact ^ params->sav_eav_f);
+ }
+ if (tpg->insert_eav) {
+ unsigned offset = tpg_hdiv(tpg, p, tpg->compose.width * 2 / 3);
+ u8 *p = vbuf + offset;
+ unsigned vact = 0, hact = 1;
+
+ p[0] = 0xff;
+ p[1] = 0;
+ p[2] = 0;
+ p[3] = 0x80 | (params->sav_eav_f << 6) |
+ (vact << 5) | (hact << 4) |
+ ((hact ^ vact) << 3) |
+ ((hact ^ params->sav_eav_f) << 2) |
+ ((params->sav_eav_f ^ vact) << 1) |
+ (hact ^ vact ^ params->sav_eav_f);
+ }
+ if (tpg->insert_hdmi_video_guard_band) {
+ unsigned int i;
+
+ switch (tpg->fourcc) {
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_RGB24:
+ for (i = 0; i < 3 * 4; i += 3) {
+ vbuf[i] = 0xab;
+ vbuf[i + 1] = 0x55;
+ vbuf[i + 2] = 0xab;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_ARGB32:
+ case V4L2_PIX_FMT_XRGB32:
+ case V4L2_PIX_FMT_BGRX32:
+ case V4L2_PIX_FMT_BGRA32:
+ for (i = 0; i < 4 * 4; i += 4) {
+ vbuf[i] = 0x00;
+ vbuf[i + 1] = 0xab;
+ vbuf[i + 2] = 0x55;
+ vbuf[i + 3] = 0xab;
+ }
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_XBGR32:
+ case V4L2_PIX_FMT_ABGR32:
+ case V4L2_PIX_FMT_RGBX32:
+ case V4L2_PIX_FMT_RGBA32:
+ for (i = 0; i < 4 * 4; i += 4) {
+ vbuf[i] = 0xab;
+ vbuf[i + 1] = 0x55;
+ vbuf[i + 2] = 0xab;
+ vbuf[i + 3] = 0x00;
+ }
+ break;
+ }
+ }
+}
+
+static void tpg_fill_plane_pattern(const struct tpg_data *tpg,
+ const struct tpg_draw_params *params,
+ unsigned p, unsigned h, u8 *vbuf)
+{
+ unsigned twopixsize = params->twopixsize;
+ unsigned img_width = params->img_width;
+ unsigned mv_hor_old = params->mv_hor_old;
+ unsigned mv_hor_new = params->mv_hor_new;
+ unsigned mv_vert_old = params->mv_vert_old;
+ unsigned mv_vert_new = params->mv_vert_new;
+ unsigned frame_line = params->frame_line;
+ unsigned frame_line_next = params->frame_line_next;
+ unsigned line_offset = tpg_hscale_div(tpg, p, tpg->crop.left);
+ bool even;
+ bool fill_blank = false;
+ unsigned pat_line_old;
+ unsigned pat_line_new;
+ u8 *linestart_older;
+ u8 *linestart_newer;
+ u8 *linestart_top;
+ u8 *linestart_bottom;
+
+ even = !(frame_line & 1);
+
+ if (h >= params->hmax) {
+ if (params->hmax == tpg->compose.height)
+ return;
+ if (!tpg->perc_fill_blank)
+ return;
+ fill_blank = true;
+ }
+
+ if (tpg->vflip) {
+ frame_line = tpg->src_height - frame_line - 1;
+ frame_line_next = tpg->src_height - frame_line_next - 1;
+ }
+
+ if (fill_blank) {
+ linestart_older = tpg->contrast_line[p];
+ linestart_newer = tpg->contrast_line[p];
+ } else if (tpg->qual != TPG_QUAL_NOISE &&
+ (frame_line < tpg->border.top ||
+ frame_line >= tpg->border.top + tpg->border.height)) {
+ linestart_older = tpg->black_line[p];
+ linestart_newer = tpg->black_line[p];
+ } else if (tpg->pattern == TPG_PAT_NOISE || tpg->qual == TPG_QUAL_NOISE) {
+ linestart_older = tpg->random_line[p] +
+ twopixsize * get_random_u32_below(tpg->src_width / 2);
+ linestart_newer = tpg->random_line[p] +
+ twopixsize * get_random_u32_below(tpg->src_width / 2);
+ } else {
+ unsigned frame_line_old =
+ (frame_line + mv_vert_old) % tpg->src_height;
+ unsigned frame_line_new =
+ (frame_line + mv_vert_new) % tpg->src_height;
+ unsigned pat_line_next_old;
+ unsigned pat_line_next_new;
+
+ pat_line_old = tpg_get_pat_line(tpg, frame_line_old);
+ pat_line_new = tpg_get_pat_line(tpg, frame_line_new);
+ linestart_older = tpg->lines[pat_line_old][p] + mv_hor_old;
+ linestart_newer = tpg->lines[pat_line_new][p] + mv_hor_new;
+
+ if (tpg->vdownsampling[p] > 1 && frame_line != frame_line_next) {
+ int avg_pat;
+
+ /*
+ * Now decide whether we need to use downsampled_lines[].
+ * That's necessary if the two lines use different patterns.
+ */
+ pat_line_next_old = tpg_get_pat_line(tpg,
+ (frame_line_next + mv_vert_old) % tpg->src_height);
+ pat_line_next_new = tpg_get_pat_line(tpg,
+ (frame_line_next + mv_vert_new) % tpg->src_height);
+
+ switch (tpg->field) {
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_INTERLACED_BT:
+ case V4L2_FIELD_INTERLACED_TB:
+ avg_pat = tpg_pattern_avg(tpg, pat_line_old, pat_line_new);
+ if (avg_pat < 0)
+ break;
+ linestart_older = tpg->downsampled_lines[avg_pat][p] + mv_hor_old;
+ linestart_newer = linestart_older;
+ break;
+ case V4L2_FIELD_NONE:
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_SEQ_BT:
+ case V4L2_FIELD_SEQ_TB:
+ avg_pat = tpg_pattern_avg(tpg, pat_line_old, pat_line_next_old);
+ if (avg_pat >= 0)
+ linestart_older = tpg->downsampled_lines[avg_pat][p] +
+ mv_hor_old;
+ avg_pat = tpg_pattern_avg(tpg, pat_line_new, pat_line_next_new);
+ if (avg_pat >= 0)
+ linestart_newer = tpg->downsampled_lines[avg_pat][p] +
+ mv_hor_new;
+ break;
+ }
+ }
+ linestart_older += line_offset;
+ linestart_newer += line_offset;
+ }
+ if (tpg->field_alternate) {
+ linestart_top = linestart_bottom = linestart_older;
+ } else if (params->is_60hz) {
+ linestart_top = linestart_newer;
+ linestart_bottom = linestart_older;
+ } else {
+ linestart_top = linestart_older;
+ linestart_bottom = linestart_newer;
+ }
+
+ switch (tpg->field) {
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_SEQ_TB:
+ case V4L2_FIELD_SEQ_BT:
+ if (even)
+ memcpy(vbuf, linestart_top, img_width);
+ else
+ memcpy(vbuf, linestart_bottom, img_width);
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ if (even)
+ memcpy(vbuf, linestart_bottom, img_width);
+ else
+ memcpy(vbuf, linestart_top, img_width);
+ break;
+ case V4L2_FIELD_TOP:
+ memcpy(vbuf, linestart_top, img_width);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ memcpy(vbuf, linestart_bottom, img_width);
+ break;
+ case V4L2_FIELD_NONE:
+ default:
+ memcpy(vbuf, linestart_older, img_width);
+ break;
+ }
+}
+
+void tpg_fill_plane_buffer(struct tpg_data *tpg, v4l2_std_id std,
+ unsigned p, u8 *vbuf)
+{
+ struct tpg_draw_params params;
+ unsigned factor = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
+
+ /* Coarse scaling with Bresenham */
+ unsigned int_part = (tpg->crop.height / factor) / tpg->compose.height;
+ unsigned fract_part = (tpg->crop.height / factor) % tpg->compose.height;
+ unsigned src_y = 0;
+ unsigned error = 0;
+ unsigned h;
+
+ tpg_recalc(tpg);
+
+ params.is_tv = std;
+ params.is_60hz = std & V4L2_STD_525_60;
+ params.twopixsize = tpg->twopixelsize[p];
+ params.img_width = tpg_hdiv(tpg, p, tpg->compose.width);
+ params.stride = tpg->bytesperline[p];
+ params.hmax = (tpg->compose.height * tpg->perc_fill) / 100;
+
+ tpg_fill_params_pattern(tpg, p, &params);
+ tpg_fill_params_extras(tpg, p, &params);
+
+ vbuf += tpg_hdiv(tpg, p, tpg->compose.left);
+
+ for (h = 0; h < tpg->compose.height; h++) {
+ unsigned buf_line;
+
+ params.frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
+ params.frame_line_next = params.frame_line;
+ buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
+ src_y += int_part;
+ error += fract_part;
+ if (error >= tpg->compose.height) {
+ error -= tpg->compose.height;
+ src_y++;
+ }
+
+ /*
+ * For line-interleaved formats determine the 'plane'
+ * based on the buffer line.
+ */
+ if (tpg_g_interleaved(tpg))
+ p = tpg_g_interleaved_plane(tpg, buf_line);
+
+ if (tpg->vdownsampling[p] > 1) {
+ /*
+ * When doing vertical downsampling the field setting
+ * matters: for SEQ_BT/TB we downsample each field
+ * separately (i.e. lines 0+2 are combined, as are
+ * lines 1+3), for the other field settings we combine
+ * odd and even lines. Doing that for SEQ_BT/TB would
+ * be really weird.
+ */
+ if (tpg->field == V4L2_FIELD_SEQ_BT ||
+ tpg->field == V4L2_FIELD_SEQ_TB) {
+ unsigned next_src_y = src_y;
+
+ if ((h & 3) >= 2)
+ continue;
+ next_src_y += int_part;
+ if (error + fract_part >= tpg->compose.height)
+ next_src_y++;
+ params.frame_line_next =
+ tpg_calc_frameline(tpg, next_src_y, tpg->field);
+ } else {
+ if (h & 1)
+ continue;
+ params.frame_line_next =
+ tpg_calc_frameline(tpg, src_y, tpg->field);
+ }
+
+ buf_line /= tpg->vdownsampling[p];
+ }
+ tpg_fill_plane_pattern(tpg, &params, p, h,
+ vbuf + buf_line * params.stride);
+ tpg_fill_plane_extras(tpg, &params, p, h,
+ vbuf + buf_line * params.stride);
+ }
+}
+EXPORT_SYMBOL_GPL(tpg_fill_plane_buffer);
+
+void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
+{
+ unsigned offset = 0;
+ unsigned i;
+
+ if (tpg->buffers > 1) {
+ tpg_fill_plane_buffer(tpg, std, p, vbuf);
+ return;
+ }
+
+ for (i = 0; i < tpg_g_planes(tpg); i++) {
+ tpg_fill_plane_buffer(tpg, std, i, vbuf + offset);
+ offset += tpg_calc_plane_size(tpg, i);
+ }
+}
+EXPORT_SYMBOL_GPL(tpg_fillbuffer);
+
+MODULE_DESCRIPTION("V4L2 Test Pattern Generator");
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/videobuf2/Kconfig b/drivers/media/common/videobuf2/Kconfig
new file mode 100644
index 000000000000..d2223a12c95f
--- /dev/null
+++ b/drivers/media/common/videobuf2/Kconfig
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Used by drivers that need Videobuf2 modules
+config VIDEOBUF2_CORE
+ select DMA_SHARED_BUFFER
+ tristate
+
+config VIDEOBUF2_V4L2
+ tristate
+
+config VIDEOBUF2_MEMOPS
+ tristate
+
+config VIDEOBUF2_DMA_CONTIG
+ tristate
+ select VIDEOBUF2_CORE
+ select VIDEOBUF2_MEMOPS
+ select DMA_SHARED_BUFFER
+
+config VIDEOBUF2_VMALLOC
+ tristate
+ select VIDEOBUF2_CORE
+ select VIDEOBUF2_MEMOPS
+ select DMA_SHARED_BUFFER
+
+config VIDEOBUF2_DMA_SG
+ tristate
+ select VIDEOBUF2_CORE
+ select VIDEOBUF2_MEMOPS
+
+config VIDEOBUF2_DVB
+ tristate
+ select VIDEOBUF2_CORE
diff --git a/drivers/media/common/videobuf2/Makefile b/drivers/media/common/videobuf2/Makefile
new file mode 100644
index 000000000000..a6fe3f304685
--- /dev/null
+++ b/drivers/media/common/videobuf2/Makefile
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
+videobuf2-common-objs := videobuf2-core.o
+videobuf2-common-objs += frame_vector.o
+
+ifeq ($(CONFIG_TRACEPOINTS),y)
+ videobuf2-common-objs += vb2-trace.o
+endif
+
+# Please keep it alphabetically sorted by Kconfig name
+# (e. g. LC_ALL=C sort Makefile)
+obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-common.o
+obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o
+obj-$(CONFIG_VIDEOBUF2_DMA_SG) += videobuf2-dma-sg.o
+obj-$(CONFIG_VIDEOBUF2_DVB) += videobuf2-dvb.o
+obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o
+obj-$(CONFIG_VIDEOBUF2_V4L2) += videobuf2-v4l2.o
+obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o
diff --git a/drivers/media/common/videobuf2/frame_vector.c b/drivers/media/common/videobuf2/frame_vector.c
new file mode 100644
index 000000000000..41f289c75cbb
--- /dev/null
+++ b/drivers/media/common/videobuf2/frame_vector.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+#include <linux/sched.h>
+
+#include <media/frame_vector.h>
+
+/**
+ * get_vaddr_frames() - map virtual addresses to pfns
+ * @start: starting user address
+ * @nr_frames: number of pages / pfns from start to map
+ * @write: the mapped address has write permission
+ * @vec: structure which receives pages / pfns of the addresses mapped.
+ * It should have space for at least nr_frames entries.
+ *
+ * This function maps virtual addresses from @start and fills @vec structure
+ * with page frame numbers or page pointers to corresponding pages (choice
+ * depends on the type of the vma underlying the virtual address). If @start
+ * belongs to a normal vma, the function grabs reference to each of the pages
+ * to pin them in memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't
+ * touch page structures and the caller must make sure pfns aren't reused for
+ * anything else while he is using them.
+ *
+ * The function returns number of pages mapped which may be less than
+ * @nr_frames. In particular we stop mapping if there are more vmas of
+ * different type underlying the specified range of virtual addresses.
+ * When the function isn't able to map a single page, it returns error.
+ *
+ * Note that get_vaddr_frames() cannot follow VM_IO mappings. It used
+ * to be able to do that, but that could (racily) return non-refcounted
+ * pfns.
+ *
+ * This function takes care of grabbing mmap_lock as necessary.
+ */
+int get_vaddr_frames(unsigned long start, unsigned int nr_frames, bool write,
+ struct frame_vector *vec)
+{
+ int ret;
+ unsigned int gup_flags = FOLL_LONGTERM;
+
+ if (nr_frames == 0)
+ return 0;
+
+ if (WARN_ON_ONCE(nr_frames > vec->nr_allocated))
+ nr_frames = vec->nr_allocated;
+
+ start = untagged_addr(start);
+
+ if (write)
+ gup_flags |= FOLL_WRITE;
+
+ ret = pin_user_pages_fast(start, nr_frames, gup_flags,
+ (struct page **)(vec->ptrs));
+ vec->got_ref = true;
+ vec->is_pfns = false;
+ vec->nr_frames = ret;
+
+ if (likely(ret > 0))
+ return ret;
+
+ vec->nr_frames = 0;
+ return ret ? ret : -EFAULT;
+}
+EXPORT_SYMBOL(get_vaddr_frames);
+
+/**
+ * put_vaddr_frames() - drop references to pages if get_vaddr_frames() acquired
+ * them
+ * @vec: frame vector to put
+ *
+ * Drop references to pages if get_vaddr_frames() acquired them. We also
+ * invalidate the frame vector so that it is prepared for the next call into
+ * get_vaddr_frames().
+ */
+void put_vaddr_frames(struct frame_vector *vec)
+{
+ struct page **pages;
+
+ if (!vec->got_ref)
+ goto out;
+ pages = frame_vector_pages(vec);
+ /*
+ * frame_vector_pages() might needed to do a conversion when
+ * get_vaddr_frames() got pages but vec was later converted to pfns.
+ * But it shouldn't really fail to convert pfns back...
+ */
+ if (WARN_ON(IS_ERR(pages)))
+ goto out;
+
+ unpin_user_pages(pages, vec->nr_frames);
+ vec->got_ref = false;
+out:
+ vec->nr_frames = 0;
+}
+EXPORT_SYMBOL(put_vaddr_frames);
+
+/**
+ * frame_vector_to_pages - convert frame vector to contain page pointers
+ * @vec: frame vector to convert
+ *
+ * Convert @vec to contain array of page pointers. If the conversion is
+ * successful, return 0. Otherwise return an error. Note that we do not grab
+ * page references for the page structures.
+ */
+int frame_vector_to_pages(struct frame_vector *vec)
+{
+ int i;
+ unsigned long *nums;
+ struct page **pages;
+
+ if (!vec->is_pfns)
+ return 0;
+ nums = frame_vector_pfns(vec);
+ for (i = 0; i < vec->nr_frames; i++)
+ if (!pfn_valid(nums[i]))
+ return -EINVAL;
+ pages = (struct page **)nums;
+ for (i = 0; i < vec->nr_frames; i++)
+ pages[i] = pfn_to_page(nums[i]);
+ vec->is_pfns = false;
+ return 0;
+}
+EXPORT_SYMBOL(frame_vector_to_pages);
+
+/**
+ * frame_vector_to_pfns - convert frame vector to contain pfns
+ * @vec: frame vector to convert
+ *
+ * Convert @vec to contain array of pfns.
+ */
+void frame_vector_to_pfns(struct frame_vector *vec)
+{
+ int i;
+ unsigned long *nums;
+ struct page **pages;
+
+ if (vec->is_pfns)
+ return;
+ pages = (struct page **)(vec->ptrs);
+ nums = (unsigned long *)pages;
+ for (i = 0; i < vec->nr_frames; i++)
+ nums[i] = page_to_pfn(pages[i]);
+ vec->is_pfns = true;
+}
+EXPORT_SYMBOL(frame_vector_to_pfns);
+
+/**
+ * frame_vector_create() - allocate & initialize structure for pinned pfns
+ * @nr_frames: number of pfns slots we should reserve
+ *
+ * Allocate and initialize struct pinned_pfns to be able to hold @nr_pfns
+ * pfns.
+ */
+struct frame_vector *frame_vector_create(unsigned int nr_frames)
+{
+ struct frame_vector *vec;
+ int size = struct_size(vec, ptrs, nr_frames);
+
+ if (WARN_ON_ONCE(nr_frames == 0))
+ return NULL;
+ /*
+ * This is absurdly high. It's here just to avoid strange effects when
+ * arithmetics overflows.
+ */
+ if (WARN_ON_ONCE(nr_frames > INT_MAX / sizeof(void *) / 2))
+ return NULL;
+ /*
+ * Avoid higher order allocations, use vmalloc instead. It should
+ * be rare anyway.
+ */
+ vec = kvmalloc(size, GFP_KERNEL);
+ if (!vec)
+ return NULL;
+ vec->nr_allocated = nr_frames;
+ vec->nr_frames = 0;
+ return vec;
+}
+EXPORT_SYMBOL(frame_vector_create);
+
+/**
+ * frame_vector_destroy() - free memory allocated to carry frame vector
+ * @vec: Frame vector to free
+ *
+ * Free structure allocated by frame_vector_create() to carry frames.
+ */
+void frame_vector_destroy(struct frame_vector *vec)
+{
+ /* Make sure put_vaddr_frames() got called properly... */
+ VM_BUG_ON(vec->nr_frames > 0);
+ kvfree(vec);
+}
+EXPORT_SYMBOL(frame_vector_destroy);
diff --git a/drivers/media/common/videobuf2/vb2-trace.c b/drivers/media/common/videobuf2/vb2-trace.c
new file mode 100644
index 000000000000..4c0f39d271f0
--- /dev/null
+++ b/drivers/media/common/videobuf2/vb2-trace.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <media/videobuf2-core.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/vb2.h>
+
+EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_buf_done);
+EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_buf_queue);
+EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_dqbuf);
+EXPORT_TRACEPOINT_SYMBOL_GPL(vb2_qbuf);
diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
new file mode 100644
index 000000000000..2df566f409b6
--- /dev/null
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -0,0 +1,3337 @@
+/*
+ * videobuf2-core.c - video buffer 2 core framework
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.com>
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * The vb2_thread implementation was based on code from videobuf-dvb.c:
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+
+#include <media/videobuf2-core.h>
+#include <media/v4l2-mc.h>
+
+#include <trace/events/vb2.h>
+
+#define PLANE_INDEX_BITS 3
+#define PLANE_INDEX_SHIFT (PAGE_SHIFT + PLANE_INDEX_BITS)
+#define PLANE_INDEX_MASK (BIT_MASK(PLANE_INDEX_BITS) - 1)
+#define MAX_BUFFER_INDEX BIT_MASK(30 - PLANE_INDEX_SHIFT)
+#define BUFFER_INDEX_MASK (MAX_BUFFER_INDEX - 1)
+
+#if BIT(PLANE_INDEX_BITS) != VIDEO_MAX_PLANES
+#error PLANE_INDEX_BITS order must be equal to VIDEO_MAX_PLANES
+#endif
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define dprintk(q, level, fmt, arg...) \
+ do { \
+ if (debug >= level) \
+ pr_info("[%s] %s: " fmt, (q)->name, __func__, \
+ ## arg); \
+ } while (0)
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+
+/*
+ * If advanced debugging is on, then count how often each op is called
+ * successfully, which can either be per-buffer or per-queue.
+ *
+ * This makes it easy to check that the 'init' and 'cleanup'
+ * (and variations thereof) stay balanced.
+ */
+
+#define log_memop(vb, op) \
+ dprintk((vb)->vb2_queue, 2, "call_memop(%d, %s)%s\n", \
+ (vb)->index, #op, \
+ (vb)->vb2_queue->mem_ops->op ? "" : " (nop)")
+
+#define call_memop(vb, op, args...) \
+({ \
+ struct vb2_queue *_q = (vb)->vb2_queue; \
+ int err; \
+ \
+ log_memop(vb, op); \
+ err = _q->mem_ops->op ? _q->mem_ops->op(args) : 0; \
+ if (!err) \
+ (vb)->cnt_mem_ ## op++; \
+ err; \
+})
+
+#define call_ptr_memop(op, vb, args...) \
+({ \
+ struct vb2_queue *_q = (vb)->vb2_queue; \
+ void *ptr; \
+ \
+ log_memop(vb, op); \
+ ptr = _q->mem_ops->op ? _q->mem_ops->op(vb, args) : NULL; \
+ if (!IS_ERR_OR_NULL(ptr)) \
+ (vb)->cnt_mem_ ## op++; \
+ ptr; \
+})
+
+#define call_void_memop(vb, op, args...) \
+({ \
+ struct vb2_queue *_q = (vb)->vb2_queue; \
+ \
+ log_memop(vb, op); \
+ if (_q->mem_ops->op) \
+ _q->mem_ops->op(args); \
+ (vb)->cnt_mem_ ## op++; \
+})
+
+#define log_qop(q, op) \
+ dprintk(q, 2, "call_qop(%s)%s\n", #op, \
+ (q)->ops->op ? "" : " (nop)")
+
+#define call_qop(q, op, args...) \
+({ \
+ int err; \
+ \
+ log_qop(q, op); \
+ err = (q)->ops->op ? (q)->ops->op(args) : 0; \
+ if (!err) \
+ (q)->cnt_ ## op++; \
+ err; \
+})
+
+#define call_void_qop(q, op, args...) \
+({ \
+ log_qop(q, op); \
+ if ((q)->ops->op) \
+ (q)->ops->op(args); \
+ (q)->cnt_ ## op++; \
+})
+
+#define log_vb_qop(vb, op, args...) \
+ dprintk((vb)->vb2_queue, 2, "call_vb_qop(%d, %s)%s\n", \
+ (vb)->index, #op, \
+ (vb)->vb2_queue->ops->op ? "" : " (nop)")
+
+#define call_vb_qop(vb, op, args...) \
+({ \
+ int err; \
+ \
+ log_vb_qop(vb, op); \
+ err = (vb)->vb2_queue->ops->op ? \
+ (vb)->vb2_queue->ops->op(args) : 0; \
+ if (!err) \
+ (vb)->cnt_ ## op++; \
+ err; \
+})
+
+#define call_void_vb_qop(vb, op, args...) \
+({ \
+ log_vb_qop(vb, op); \
+ if ((vb)->vb2_queue->ops->op) \
+ (vb)->vb2_queue->ops->op(args); \
+ (vb)->cnt_ ## op++; \
+})
+
+#else
+
+#define call_memop(vb, op, args...) \
+ ((vb)->vb2_queue->mem_ops->op ? \
+ (vb)->vb2_queue->mem_ops->op(args) : 0)
+
+#define call_ptr_memop(op, vb, args...) \
+ ((vb)->vb2_queue->mem_ops->op ? \
+ (vb)->vb2_queue->mem_ops->op(vb, args) : NULL)
+
+#define call_void_memop(vb, op, args...) \
+ do { \
+ if ((vb)->vb2_queue->mem_ops->op) \
+ (vb)->vb2_queue->mem_ops->op(args); \
+ } while (0)
+
+#define call_qop(q, op, args...) \
+ ((q)->ops->op ? (q)->ops->op(args) : 0)
+
+#define call_void_qop(q, op, args...) \
+ do { \
+ if ((q)->ops->op) \
+ (q)->ops->op(args); \
+ } while (0)
+
+#define call_vb_qop(vb, op, args...) \
+ ((vb)->vb2_queue->ops->op ? (vb)->vb2_queue->ops->op(args) : 0)
+
+#define call_void_vb_qop(vb, op, args...) \
+ do { \
+ if ((vb)->vb2_queue->ops->op) \
+ (vb)->vb2_queue->ops->op(args); \
+ } while (0)
+
+#endif
+
+#define call_bufop(q, op, args...) \
+({ \
+ int ret = 0; \
+ if (q && q->buf_ops && q->buf_ops->op) \
+ ret = q->buf_ops->op(args); \
+ ret; \
+})
+
+#define call_void_bufop(q, op, args...) \
+({ \
+ if (q && q->buf_ops && q->buf_ops->op) \
+ q->buf_ops->op(args); \
+})
+
+static void __vb2_queue_cancel(struct vb2_queue *q);
+
+static const char *vb2_state_name(enum vb2_buffer_state s)
+{
+ static const char * const state_names[] = {
+ [VB2_BUF_STATE_DEQUEUED] = "dequeued",
+ [VB2_BUF_STATE_IN_REQUEST] = "in request",
+ [VB2_BUF_STATE_PREPARING] = "preparing",
+ [VB2_BUF_STATE_QUEUED] = "queued",
+ [VB2_BUF_STATE_ACTIVE] = "active",
+ [VB2_BUF_STATE_DONE] = "done",
+ [VB2_BUF_STATE_ERROR] = "error",
+ };
+
+ if ((unsigned int)(s) < ARRAY_SIZE(state_names))
+ return state_names[s];
+ return "unknown";
+}
+
+/*
+ * __vb2_buf_mem_alloc() - allocate video memory for the given buffer
+ */
+static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ void *mem_priv;
+ int plane;
+ int ret = -ENOMEM;
+
+ /*
+ * Allocate memory for all planes in this buffer
+ * NOTE: mmapped areas should be page aligned
+ */
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ /* Memops alloc requires size to be page aligned. */
+ unsigned long size = PAGE_ALIGN(vb->planes[plane].length);
+
+ /* Did it wrap around? */
+ if (size < vb->planes[plane].length)
+ goto free;
+
+ mem_priv = call_ptr_memop(alloc,
+ vb,
+ q->alloc_devs[plane] ? : q->dev,
+ size);
+ if (IS_ERR_OR_NULL(mem_priv)) {
+ if (mem_priv)
+ ret = PTR_ERR(mem_priv);
+ goto free;
+ }
+
+ /* Associate allocator private data with this plane */
+ vb->planes[plane].mem_priv = mem_priv;
+ }
+
+ return 0;
+free:
+ /* Free already allocated memory if one of the allocations failed */
+ for (; plane > 0; --plane) {
+ call_void_memop(vb, put, vb->planes[plane - 1].mem_priv);
+ vb->planes[plane - 1].mem_priv = NULL;
+ }
+
+ return ret;
+}
+
+/*
+ * __vb2_buf_mem_free() - free memory of the given buffer
+ */
+static void __vb2_buf_mem_free(struct vb2_buffer *vb)
+{
+ unsigned int plane;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ call_void_memop(vb, put, vb->planes[plane].mem_priv);
+ vb->planes[plane].mem_priv = NULL;
+ dprintk(vb->vb2_queue, 3, "freed plane %d of buffer %d\n",
+ plane, vb->index);
+ }
+}
+
+/*
+ * __vb2_buf_userptr_put() - release userspace memory associated with
+ * a USERPTR buffer
+ */
+static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
+{
+ unsigned int plane;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ if (vb->planes[plane].mem_priv)
+ call_void_memop(vb, put_userptr, vb->planes[plane].mem_priv);
+ vb->planes[plane].mem_priv = NULL;
+ }
+}
+
+/*
+ * __vb2_plane_dmabuf_put() - release memory associated with
+ * a DMABUF shared plane
+ */
+static void __vb2_plane_dmabuf_put(struct vb2_buffer *vb, struct vb2_plane *p)
+{
+ if (!p->mem_priv)
+ return;
+
+ if (!p->dbuf_duplicated) {
+ if (p->dbuf_mapped)
+ call_void_memop(vb, unmap_dmabuf, p->mem_priv);
+
+ call_void_memop(vb, detach_dmabuf, p->mem_priv);
+ }
+
+ dma_buf_put(p->dbuf);
+ p->mem_priv = NULL;
+ p->dbuf = NULL;
+ p->dbuf_mapped = 0;
+ p->bytesused = 0;
+ p->length = 0;
+ p->m.fd = 0;
+ p->data_offset = 0;
+ p->dbuf_duplicated = false;
+}
+
+/*
+ * __vb2_buf_dmabuf_put() - release memory associated with
+ * a DMABUF shared buffer
+ */
+static void __vb2_buf_dmabuf_put(struct vb2_buffer *vb)
+{
+ int plane;
+
+ /*
+ * When multiple planes share the same DMA buffer attachment, the plane
+ * with the lowest index owns the mem_priv.
+ * Put planes in the reversed order so that we don't leave invalid
+ * mem_priv behind.
+ */
+ for (plane = vb->num_planes - 1; plane >= 0; --plane)
+ __vb2_plane_dmabuf_put(vb, &vb->planes[plane]);
+}
+
+/*
+ * __vb2_buf_mem_prepare() - call ->prepare() on buffer's private memory
+ * to sync caches
+ */
+static void __vb2_buf_mem_prepare(struct vb2_buffer *vb)
+{
+ unsigned int plane;
+
+ if (vb->synced)
+ return;
+
+ vb->synced = 1;
+ for (plane = 0; plane < vb->num_planes; ++plane)
+ call_void_memop(vb, prepare, vb->planes[plane].mem_priv);
+}
+
+/*
+ * __vb2_buf_mem_finish() - call ->finish on buffer's private memory
+ * to sync caches
+ */
+static void __vb2_buf_mem_finish(struct vb2_buffer *vb)
+{
+ unsigned int plane;
+
+ if (!vb->synced)
+ return;
+
+ vb->synced = 0;
+ for (plane = 0; plane < vb->num_planes; ++plane)
+ call_void_memop(vb, finish, vb->planes[plane].mem_priv);
+}
+
+/*
+ * __setup_offsets() - setup unique offsets ("cookies") for every plane in
+ * the buffer.
+ */
+static void __setup_offsets(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ unsigned int plane;
+ unsigned long offset = 0;
+
+ /*
+ * The offset "cookie" value has the following constraints:
+ * - a buffer can have up to 8 planes.
+ * - v4l2 mem2mem uses bit 30 to distinguish between
+ * OUTPUT (aka "source", bit 30 is 0) and
+ * CAPTURE (aka "destination", bit 30 is 1) buffers.
+ * - must be page aligned
+ * That led to this bit mapping when PAGE_SHIFT = 12:
+ * |30 |29 15|14 12|11 0|
+ * |DST_QUEUE_OFF_BASE|buffer index|plane index| 0 |
+ * where there are 15 bits to store the buffer index.
+ * Depending on PAGE_SHIFT value we can have fewer bits
+ * to store the buffer index.
+ */
+ offset = vb->index << PLANE_INDEX_SHIFT;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ vb->planes[plane].m.offset = offset + (plane << PAGE_SHIFT);
+
+ dprintk(q, 3, "buffer %d, plane %d offset 0x%08lx\n",
+ vb->index, plane, offset);
+ }
+}
+
+static void init_buffer_cache_hints(struct vb2_queue *q, struct vb2_buffer *vb)
+{
+ /*
+ * DMA exporter should take care of cache syncs, so we can avoid
+ * explicit ->prepare()/->finish() syncs. For other ->memory types
+ * we always need ->prepare() or/and ->finish() cache sync.
+ */
+ if (q->memory == VB2_MEMORY_DMABUF) {
+ vb->skip_cache_sync_on_finish = 1;
+ vb->skip_cache_sync_on_prepare = 1;
+ return;
+ }
+
+ /*
+ * ->finish() cache sync can be avoided when queue direction is
+ * TO_DEVICE.
+ */
+ if (q->dma_dir == DMA_TO_DEVICE)
+ vb->skip_cache_sync_on_finish = 1;
+}
+
+/**
+ * vb2_queue_add_buffer() - add a buffer to a queue
+ * @q: pointer to &struct vb2_queue with videobuf2 queue.
+ * @vb: pointer to &struct vb2_buffer to be added to the queue.
+ * @index: index where add vb2_buffer in the queue
+ */
+static void vb2_queue_add_buffer(struct vb2_queue *q, struct vb2_buffer *vb, unsigned int index)
+{
+ WARN_ON(index >= q->max_num_buffers || test_bit(index, q->bufs_bitmap) || vb->vb2_queue);
+
+ q->bufs[index] = vb;
+ vb->index = index;
+ vb->vb2_queue = q;
+ set_bit(index, q->bufs_bitmap);
+}
+
+/**
+ * vb2_queue_remove_buffer() - remove a buffer from a queue
+ * @vb: pointer to &struct vb2_buffer to be removed from the queue.
+ */
+static void vb2_queue_remove_buffer(struct vb2_buffer *vb)
+{
+ clear_bit(vb->index, vb->vb2_queue->bufs_bitmap);
+ vb->vb2_queue->bufs[vb->index] = NULL;
+ vb->vb2_queue = NULL;
+}
+
+/*
+ * __vb2_queue_alloc() - allocate vb2 buffer structures and (for MMAP type)
+ * video buffer memory for all buffers/planes on the queue and initializes the
+ * queue
+ * @first_index: index of the first created buffer, all newly allocated buffers
+ * have indices in the range [first_index..first_index+count-1]
+ *
+ * Returns the number of buffers successfully allocated.
+ */
+static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
+ unsigned int num_buffers, unsigned int num_planes,
+ const unsigned int plane_sizes[VB2_MAX_PLANES],
+ unsigned int *first_index)
+{
+ unsigned int buffer, plane;
+ struct vb2_buffer *vb;
+ unsigned long index = q->max_num_buffers;
+ int ret;
+
+ /*
+ * Ensure that the number of already queue + the number of buffers already
+ * in the queue is below q->max_num_buffers
+ */
+ num_buffers = min_t(unsigned int, num_buffers,
+ q->max_num_buffers - vb2_get_num_buffers(q));
+
+ while (num_buffers) {
+ index = bitmap_find_next_zero_area(q->bufs_bitmap, q->max_num_buffers,
+ 0, num_buffers, 0);
+
+ if (index < q->max_num_buffers)
+ break;
+ /* Try to find free space for less buffers */
+ num_buffers--;
+ }
+
+ /* If there is no space left to allocate buffers return 0 to indicate the error */
+ if (!num_buffers) {
+ *first_index = 0;
+ return 0;
+ }
+
+ *first_index = index;
+
+ for (buffer = 0; buffer < num_buffers; ++buffer) {
+ /* Allocate vb2 buffer structures */
+ vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
+ if (!vb) {
+ dprintk(q, 1, "memory alloc for buffer struct failed\n");
+ break;
+ }
+
+ vb->state = VB2_BUF_STATE_DEQUEUED;
+ vb->num_planes = num_planes;
+ vb->type = q->type;
+ vb->memory = memory;
+ init_buffer_cache_hints(q, vb);
+ for (plane = 0; plane < num_planes; ++plane) {
+ vb->planes[plane].length = plane_sizes[plane];
+ vb->planes[plane].min_length = plane_sizes[plane];
+ }
+
+ vb2_queue_add_buffer(q, vb, index++);
+ call_void_bufop(q, init_buffer, vb);
+
+ /* Allocate video buffer memory for the MMAP type */
+ if (memory == VB2_MEMORY_MMAP) {
+ ret = __vb2_buf_mem_alloc(vb);
+ if (ret) {
+ dprintk(q, 1, "failed allocating memory for buffer %d\n",
+ buffer);
+ vb2_queue_remove_buffer(vb);
+ kfree(vb);
+ break;
+ }
+ __setup_offsets(vb);
+ /*
+ * Call the driver-provided buffer initialization
+ * callback, if given. An error in initialization
+ * results in queue setup failure.
+ */
+ ret = call_vb_qop(vb, buf_init, vb);
+ if (ret) {
+ dprintk(q, 1, "buffer %d %p initialization failed\n",
+ buffer, vb);
+ __vb2_buf_mem_free(vb);
+ vb2_queue_remove_buffer(vb);
+ kfree(vb);
+ break;
+ }
+ }
+ }
+
+ dprintk(q, 3, "allocated %d buffers, %d plane(s) each\n",
+ buffer, num_planes);
+
+ return buffer;
+}
+
+/*
+ * __vb2_free_mem() - release video buffer memory for a given range of
+ * buffers in a given queue
+ */
+static void __vb2_free_mem(struct vb2_queue *q, unsigned int start, unsigned int count)
+{
+ unsigned int i;
+ struct vb2_buffer *vb;
+
+ for (i = start; i < start + count; i++) {
+ vb = vb2_get_buffer(q, i);
+ if (!vb)
+ continue;
+
+ /* Free MMAP buffers or release USERPTR buffers */
+ if (q->memory == VB2_MEMORY_MMAP)
+ __vb2_buf_mem_free(vb);
+ else if (q->memory == VB2_MEMORY_DMABUF)
+ __vb2_buf_dmabuf_put(vb);
+ else
+ __vb2_buf_userptr_put(vb);
+ }
+}
+
+/*
+ * __vb2_queue_free() - free @count buffers from @start index of the queue - video memory and
+ * related information, if no buffers are left return the queue to an
+ * uninitialized state. Might be called even if the queue has already been freed.
+ */
+static void __vb2_queue_free(struct vb2_queue *q, unsigned int start, unsigned int count)
+{
+ unsigned int i;
+
+ lockdep_assert_held(&q->mmap_lock);
+
+ /* Call driver-provided cleanup function for each buffer, if provided */
+ for (i = start; i < start + count; i++) {
+ struct vb2_buffer *vb = vb2_get_buffer(q, i);
+
+ if (vb && vb->planes[0].mem_priv)
+ call_void_vb_qop(vb, buf_cleanup, vb);
+ }
+
+ /* Release video buffer memory */
+ __vb2_free_mem(q, start, count);
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ /*
+ * Check that all the calls were balanced during the life-time of this
+ * queue. If not then dump the counters to the kernel log.
+ */
+ if (vb2_get_num_buffers(q)) {
+ bool unbalanced = q->cnt_start_streaming != q->cnt_stop_streaming ||
+ q->cnt_prepare_streaming != q->cnt_unprepare_streaming ||
+ q->cnt_wait_prepare != q->cnt_wait_finish;
+
+ if (unbalanced) {
+ pr_info("unbalanced counters for queue %p:\n", q);
+ if (q->cnt_start_streaming != q->cnt_stop_streaming)
+ pr_info(" setup: %u start_streaming: %u stop_streaming: %u\n",
+ q->cnt_queue_setup, q->cnt_start_streaming,
+ q->cnt_stop_streaming);
+ if (q->cnt_prepare_streaming != q->cnt_unprepare_streaming)
+ pr_info(" prepare_streaming: %u unprepare_streaming: %u\n",
+ q->cnt_prepare_streaming, q->cnt_unprepare_streaming);
+ if (q->cnt_wait_prepare != q->cnt_wait_finish)
+ pr_info(" wait_prepare: %u wait_finish: %u\n",
+ q->cnt_wait_prepare, q->cnt_wait_finish);
+ }
+ q->cnt_queue_setup = 0;
+ q->cnt_wait_prepare = 0;
+ q->cnt_wait_finish = 0;
+ q->cnt_prepare_streaming = 0;
+ q->cnt_start_streaming = 0;
+ q->cnt_stop_streaming = 0;
+ q->cnt_unprepare_streaming = 0;
+ }
+ for (i = start; i < start + count; i++) {
+ struct vb2_buffer *vb = vb2_get_buffer(q, i);
+ bool unbalanced;
+
+ if (!vb)
+ continue;
+
+ unbalanced = vb->cnt_mem_alloc != vb->cnt_mem_put ||
+ vb->cnt_mem_prepare != vb->cnt_mem_finish ||
+ vb->cnt_mem_get_userptr != vb->cnt_mem_put_userptr ||
+ vb->cnt_mem_attach_dmabuf != vb->cnt_mem_detach_dmabuf ||
+ vb->cnt_mem_map_dmabuf != vb->cnt_mem_unmap_dmabuf ||
+ vb->cnt_buf_queue != vb->cnt_buf_done ||
+ vb->cnt_buf_prepare != vb->cnt_buf_finish ||
+ vb->cnt_buf_init != vb->cnt_buf_cleanup;
+
+ if (unbalanced) {
+ pr_info("unbalanced counters for queue %p, buffer %d:\n",
+ q, i);
+ if (vb->cnt_buf_init != vb->cnt_buf_cleanup)
+ pr_info(" buf_init: %u buf_cleanup: %u\n",
+ vb->cnt_buf_init, vb->cnt_buf_cleanup);
+ if (vb->cnt_buf_prepare != vb->cnt_buf_finish)
+ pr_info(" buf_prepare: %u buf_finish: %u\n",
+ vb->cnt_buf_prepare, vb->cnt_buf_finish);
+ if (vb->cnt_buf_queue != vb->cnt_buf_done)
+ pr_info(" buf_out_validate: %u buf_queue: %u buf_done: %u buf_request_complete: %u\n",
+ vb->cnt_buf_out_validate, vb->cnt_buf_queue,
+ vb->cnt_buf_done, vb->cnt_buf_request_complete);
+ if (vb->cnt_mem_alloc != vb->cnt_mem_put)
+ pr_info(" alloc: %u put: %u\n",
+ vb->cnt_mem_alloc, vb->cnt_mem_put);
+ if (vb->cnt_mem_prepare != vb->cnt_mem_finish)
+ pr_info(" prepare: %u finish: %u\n",
+ vb->cnt_mem_prepare, vb->cnt_mem_finish);
+ if (vb->cnt_mem_get_userptr != vb->cnt_mem_put_userptr)
+ pr_info(" get_userptr: %u put_userptr: %u\n",
+ vb->cnt_mem_get_userptr, vb->cnt_mem_put_userptr);
+ if (vb->cnt_mem_attach_dmabuf != vb->cnt_mem_detach_dmabuf)
+ pr_info(" attach_dmabuf: %u detach_dmabuf: %u\n",
+ vb->cnt_mem_attach_dmabuf, vb->cnt_mem_detach_dmabuf);
+ if (vb->cnt_mem_map_dmabuf != vb->cnt_mem_unmap_dmabuf)
+ pr_info(" map_dmabuf: %u unmap_dmabuf: %u\n",
+ vb->cnt_mem_map_dmabuf, vb->cnt_mem_unmap_dmabuf);
+ pr_info(" get_dmabuf: %u num_users: %u\n",
+ vb->cnt_mem_get_dmabuf,
+ vb->cnt_mem_num_users);
+ }
+ }
+#endif
+
+ /* Free vb2 buffers */
+ for (i = start; i < start + count; i++) {
+ struct vb2_buffer *vb = vb2_get_buffer(q, i);
+
+ if (!vb)
+ continue;
+
+ vb2_queue_remove_buffer(vb);
+ kfree(vb);
+ }
+
+ if (!vb2_get_num_buffers(q)) {
+ q->memory = VB2_MEMORY_UNKNOWN;
+ INIT_LIST_HEAD(&q->queued_list);
+ }
+}
+
+bool vb2_buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
+{
+ unsigned int plane;
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ void *mem_priv = vb->planes[plane].mem_priv;
+ /*
+ * If num_users() has not been provided, call_memop
+ * will return 0, apparently nobody cares about this
+ * case anyway. If num_users() returns more than 1,
+ * we are not the only user of the plane's memory.
+ */
+ if (mem_priv && call_memop(vb, num_users, mem_priv) > 1)
+ return true;
+ }
+ return false;
+}
+EXPORT_SYMBOL(vb2_buffer_in_use);
+
+/*
+ * __buffers_in_use() - return true if any buffers on the queue are in use and
+ * the queue cannot be freed (by the means of REQBUFS(0)) call
+ */
+static bool __buffers_in_use(struct vb2_queue *q)
+{
+ unsigned int buffer;
+ for (buffer = 0; buffer < q->max_num_buffers; ++buffer) {
+ struct vb2_buffer *vb = vb2_get_buffer(q, buffer);
+
+ if (!vb)
+ continue;
+
+ if (vb2_buffer_in_use(q, vb))
+ return true;
+ }
+ return false;
+}
+
+void vb2_core_querybuf(struct vb2_queue *q, struct vb2_buffer *vb, void *pb)
+{
+ call_void_bufop(q, fill_user_buffer, vb, pb);
+}
+EXPORT_SYMBOL_GPL(vb2_core_querybuf);
+
+/*
+ * __verify_userptr_ops() - verify that all memory operations required for
+ * USERPTR queue type have been provided
+ */
+static int __verify_userptr_ops(struct vb2_queue *q)
+{
+ if (!(q->io_modes & VB2_USERPTR) || !q->mem_ops->get_userptr ||
+ !q->mem_ops->put_userptr)
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * __verify_mmap_ops() - verify that all memory operations required for
+ * MMAP queue type have been provided
+ */
+static int __verify_mmap_ops(struct vb2_queue *q)
+{
+ if (!(q->io_modes & VB2_MMAP) || !q->mem_ops->alloc ||
+ !q->mem_ops->put || !q->mem_ops->mmap)
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * __verify_dmabuf_ops() - verify that all memory operations required for
+ * DMABUF queue type have been provided
+ */
+static int __verify_dmabuf_ops(struct vb2_queue *q)
+{
+ if (!(q->io_modes & VB2_DMABUF) || !q->mem_ops->attach_dmabuf ||
+ !q->mem_ops->detach_dmabuf || !q->mem_ops->map_dmabuf ||
+ !q->mem_ops->unmap_dmabuf)
+ return -EINVAL;
+
+ return 0;
+}
+
+int vb2_verify_memory_type(struct vb2_queue *q,
+ enum vb2_memory memory, unsigned int type)
+{
+ if (memory != VB2_MEMORY_MMAP && memory != VB2_MEMORY_USERPTR &&
+ memory != VB2_MEMORY_DMABUF) {
+ dprintk(q, 1, "unsupported memory type\n");
+ return -EINVAL;
+ }
+
+ if (type != q->type) {
+ dprintk(q, 1, "requested type is incorrect\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure all the required memory ops for given memory type
+ * are available.
+ */
+ if (memory == VB2_MEMORY_MMAP && __verify_mmap_ops(q)) {
+ dprintk(q, 1, "MMAP for current setup unsupported\n");
+ return -EINVAL;
+ }
+
+ if (memory == VB2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
+ dprintk(q, 1, "USERPTR for current setup unsupported\n");
+ return -EINVAL;
+ }
+
+ if (memory == VB2_MEMORY_DMABUF && __verify_dmabuf_ops(q)) {
+ dprintk(q, 1, "DMABUF for current setup unsupported\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Place the busy tests at the end: -EBUSY can be ignored when
+ * create_bufs is called with count == 0, but count == 0 should still
+ * do the memory and type validation.
+ */
+ if (vb2_fileio_is_active(q)) {
+ dprintk(q, 1, "file io in progress\n");
+ return -EBUSY;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(vb2_verify_memory_type);
+
+static void set_queue_coherency(struct vb2_queue *q, bool non_coherent_mem)
+{
+ q->non_coherent_mem = 0;
+
+ if (!vb2_queue_allows_cache_hints(q))
+ return;
+ q->non_coherent_mem = non_coherent_mem;
+}
+
+static bool verify_coherency_flags(struct vb2_queue *q, bool non_coherent_mem)
+{
+ if (non_coherent_mem != q->non_coherent_mem) {
+ dprintk(q, 1, "memory coherency model mismatch\n");
+ return false;
+ }
+ return true;
+}
+
+static int vb2_core_allocated_buffers_storage(struct vb2_queue *q)
+{
+ if (!q->bufs)
+ q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL);
+ if (!q->bufs)
+ return -ENOMEM;
+
+ if (!q->bufs_bitmap)
+ q->bufs_bitmap = bitmap_zalloc(q->max_num_buffers, GFP_KERNEL);
+ if (!q->bufs_bitmap) {
+ kfree(q->bufs);
+ q->bufs = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void vb2_core_free_buffers_storage(struct vb2_queue *q)
+{
+ kfree(q->bufs);
+ q->bufs = NULL;
+ bitmap_free(q->bufs_bitmap);
+ q->bufs_bitmap = NULL;
+}
+
+int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
+ unsigned int flags, unsigned int *count)
+{
+ unsigned int num_buffers, allocated_buffers, num_planes = 0;
+ unsigned int q_num_bufs = vb2_get_num_buffers(q);
+ unsigned plane_sizes[VB2_MAX_PLANES] = { };
+ bool non_coherent_mem = flags & V4L2_MEMORY_FLAG_NON_COHERENT;
+ unsigned int i, first_index;
+ int ret = 0;
+
+ if (q->streaming) {
+ dprintk(q, 1, "streaming active\n");
+ return -EBUSY;
+ }
+
+ if (q->waiting_in_dqbuf && *count) {
+ dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n");
+ return -EBUSY;
+ }
+
+ if (*count == 0 || q_num_bufs != 0 ||
+ (q->memory != VB2_MEMORY_UNKNOWN && q->memory != memory) ||
+ !verify_coherency_flags(q, non_coherent_mem)) {
+ /*
+ * We already have buffers allocated, so first check if they
+ * are not in use and can be freed.
+ */
+ mutex_lock(&q->mmap_lock);
+ if (debug && q->memory == VB2_MEMORY_MMAP &&
+ __buffers_in_use(q))
+ dprintk(q, 1, "memory in use, orphaning buffers\n");
+
+ /*
+ * Call queue_cancel to clean up any buffers in the
+ * QUEUED state which is possible if buffers were prepared or
+ * queued without ever calling STREAMON.
+ */
+ __vb2_queue_cancel(q);
+ __vb2_queue_free(q, 0, q->max_num_buffers);
+ mutex_unlock(&q->mmap_lock);
+
+ q->is_busy = 0;
+ /*
+ * In case of REQBUFS(0) return immediately without calling
+ * driver's queue_setup() callback and allocating resources.
+ */
+ if (*count == 0)
+ return 0;
+ }
+
+ /*
+ * Make sure the requested values and current defaults are sane.
+ */
+ num_buffers = max_t(unsigned int, *count, q->min_reqbufs_allocation);
+ num_buffers = min_t(unsigned int, num_buffers, q->max_num_buffers);
+ memset(q->alloc_devs, 0, sizeof(q->alloc_devs));
+ /*
+ * Set this now to ensure that drivers see the correct q->memory value
+ * in the queue_setup op.
+ */
+ mutex_lock(&q->mmap_lock);
+ ret = vb2_core_allocated_buffers_storage(q);
+ q->memory = memory;
+ mutex_unlock(&q->mmap_lock);
+ if (ret)
+ return ret;
+ set_queue_coherency(q, non_coherent_mem);
+
+ /*
+ * Ask the driver how many buffers and planes per buffer it requires.
+ * Driver also sets the size and allocator context for each plane.
+ */
+ ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
+ plane_sizes, q->alloc_devs);
+ if (ret)
+ goto error;
+
+ /* Check that driver has set sane values */
+ if (WARN_ON(!num_planes)) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ for (i = 0; i < num_planes; i++)
+ if (WARN_ON(!plane_sizes[i])) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Finally, allocate buffers and video memory */
+ allocated_buffers =
+ __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes, &first_index);
+ if (allocated_buffers == 0) {
+ /* There shouldn't be any buffers allocated, so first_index == 0 */
+ WARN_ON(first_index);
+ dprintk(q, 1, "memory allocation failed\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /*
+ * There is no point in continuing if we can't allocate the minimum
+ * number of buffers needed by this vb2_queue.
+ */
+ if (allocated_buffers < q->min_reqbufs_allocation)
+ ret = -ENOMEM;
+
+ /*
+ * Check if driver can handle the allocated number of buffers.
+ */
+ if (!ret && allocated_buffers < num_buffers) {
+ num_buffers = allocated_buffers;
+ /*
+ * num_planes is set by the previous queue_setup(), but since it
+ * signals to queue_setup() whether it is called from create_bufs()
+ * vs reqbufs() we zero it here to signal that queue_setup() is
+ * called for the reqbufs() case.
+ */
+ num_planes = 0;
+
+ ret = call_qop(q, queue_setup, q, &num_buffers,
+ &num_planes, plane_sizes, q->alloc_devs);
+
+ if (!ret && allocated_buffers < num_buffers)
+ ret = -ENOMEM;
+
+ /*
+ * Either the driver has accepted a smaller number of buffers,
+ * or .queue_setup() returned an error
+ */
+ }
+
+ mutex_lock(&q->mmap_lock);
+
+ if (ret < 0) {
+ /*
+ * Note: __vb2_queue_free() will subtract 'allocated_buffers'
+ * from already queued buffers and it will reset q->memory to
+ * VB2_MEMORY_UNKNOWN.
+ */
+ __vb2_queue_free(q, first_index, allocated_buffers);
+ mutex_unlock(&q->mmap_lock);
+ return ret;
+ }
+ mutex_unlock(&q->mmap_lock);
+
+ /*
+ * Return the number of successfully allocated buffers
+ * to the userspace.
+ */
+ *count = allocated_buffers;
+ q->waiting_for_buffers = !q->is_output;
+ q->is_busy = 1;
+
+ return 0;
+
+error:
+ mutex_lock(&q->mmap_lock);
+ q->memory = VB2_MEMORY_UNKNOWN;
+ mutex_unlock(&q->mmap_lock);
+ vb2_core_free_buffers_storage(q);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_core_reqbufs);
+
+int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
+ unsigned int flags, unsigned int *count,
+ unsigned int requested_planes,
+ const unsigned int requested_sizes[],
+ unsigned int *first_index)
+{
+ unsigned int num_planes = 0, num_buffers, allocated_buffers;
+ unsigned plane_sizes[VB2_MAX_PLANES] = { };
+ bool non_coherent_mem = flags & V4L2_MEMORY_FLAG_NON_COHERENT;
+ unsigned int q_num_bufs = vb2_get_num_buffers(q);
+ bool no_previous_buffers = !q_num_bufs;
+ int ret = 0;
+
+ if (q_num_bufs == q->max_num_buffers) {
+ dprintk(q, 1, "maximum number of buffers already allocated\n");
+ return -ENOBUFS;
+ }
+
+ if (no_previous_buffers) {
+ if (q->waiting_in_dqbuf && *count) {
+ dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n");
+ return -EBUSY;
+ }
+ memset(q->alloc_devs, 0, sizeof(q->alloc_devs));
+ /*
+ * Set this now to ensure that drivers see the correct q->memory
+ * value in the queue_setup op.
+ */
+ mutex_lock(&q->mmap_lock);
+ ret = vb2_core_allocated_buffers_storage(q);
+ q->memory = memory;
+ mutex_unlock(&q->mmap_lock);
+ if (ret)
+ return ret;
+ q->waiting_for_buffers = !q->is_output;
+ set_queue_coherency(q, non_coherent_mem);
+ } else {
+ if (q->memory != memory) {
+ dprintk(q, 1, "memory model mismatch\n");
+ return -EINVAL;
+ }
+ if (!verify_coherency_flags(q, non_coherent_mem))
+ return -EINVAL;
+ }
+
+ num_buffers = min(*count, q->max_num_buffers - q_num_bufs);
+
+ if (requested_planes && requested_sizes) {
+ num_planes = requested_planes;
+ memcpy(plane_sizes, requested_sizes, sizeof(plane_sizes));
+ }
+
+ /*
+ * Ask the driver, whether the requested number of buffers, planes per
+ * buffer and their sizes are acceptable
+ */
+ ret = call_qop(q, queue_setup, q, &num_buffers,
+ &num_planes, plane_sizes, q->alloc_devs);
+ if (ret)
+ goto error;
+
+ /* Finally, allocate buffers and video memory */
+ allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers,
+ num_planes, plane_sizes, first_index);
+ if (allocated_buffers == 0) {
+ dprintk(q, 1, "memory allocation failed\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /*
+ * Check if driver can handle the so far allocated number of buffers.
+ */
+ if (allocated_buffers < num_buffers) {
+ num_buffers = allocated_buffers;
+
+ /*
+ * num_buffers contains the total number of buffers, that the
+ * queue driver has set up
+ */
+ ret = call_qop(q, queue_setup, q, &num_buffers,
+ &num_planes, plane_sizes, q->alloc_devs);
+
+ if (!ret && allocated_buffers < num_buffers)
+ ret = -ENOMEM;
+
+ /*
+ * Either the driver has accepted a smaller number of buffers,
+ * or .queue_setup() returned an error
+ */
+ }
+
+ mutex_lock(&q->mmap_lock);
+
+ if (ret < 0) {
+ /*
+ * Note: __vb2_queue_free() will subtract 'allocated_buffers'
+ * from already queued buffers and it will reset q->memory to
+ * VB2_MEMORY_UNKNOWN.
+ */
+ __vb2_queue_free(q, *first_index, allocated_buffers);
+ mutex_unlock(&q->mmap_lock);
+ return -ENOMEM;
+ }
+ mutex_unlock(&q->mmap_lock);
+
+ /*
+ * Return the number of successfully allocated buffers
+ * to the userspace.
+ */
+ *count = allocated_buffers;
+ q->is_busy = 1;
+
+ return 0;
+
+error:
+ if (no_previous_buffers) {
+ mutex_lock(&q->mmap_lock);
+ q->memory = VB2_MEMORY_UNKNOWN;
+ mutex_unlock(&q->mmap_lock);
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_core_create_bufs);
+
+void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no)
+{
+ if (plane_no >= vb->num_planes || !vb->planes[plane_no].mem_priv)
+ return NULL;
+
+ return call_ptr_memop(vaddr, vb, vb->planes[plane_no].mem_priv);
+
+}
+EXPORT_SYMBOL_GPL(vb2_plane_vaddr);
+
+void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no)
+{
+ if (plane_no >= vb->num_planes || !vb->planes[plane_no].mem_priv)
+ return NULL;
+
+ return call_ptr_memop(cookie, vb, vb->planes[plane_no].mem_priv);
+}
+EXPORT_SYMBOL_GPL(vb2_plane_cookie);
+
+void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ unsigned long flags;
+
+ if (WARN_ON(vb->state != VB2_BUF_STATE_ACTIVE))
+ return;
+
+ if (WARN_ON(state != VB2_BUF_STATE_DONE &&
+ state != VB2_BUF_STATE_ERROR &&
+ state != VB2_BUF_STATE_QUEUED))
+ state = VB2_BUF_STATE_ERROR;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ /*
+ * Although this is not a callback, it still does have to balance
+ * with the buf_queue op. So update this counter manually.
+ */
+ vb->cnt_buf_done++;
+#endif
+ dprintk(q, 4, "done processing on buffer %d, state: %s\n",
+ vb->index, vb2_state_name(state));
+
+ if (state != VB2_BUF_STATE_QUEUED)
+ __vb2_buf_mem_finish(vb);
+
+ spin_lock_irqsave(&q->done_lock, flags);
+ if (state == VB2_BUF_STATE_QUEUED) {
+ vb->state = VB2_BUF_STATE_QUEUED;
+ } else {
+ /* Add the buffer to the done buffers list */
+ list_add_tail(&vb->done_entry, &q->done_list);
+ vb->state = state;
+ }
+ atomic_dec(&q->owned_by_drv_count);
+
+ if (state != VB2_BUF_STATE_QUEUED && vb->req_obj.req) {
+ media_request_object_unbind(&vb->req_obj);
+ media_request_object_put(&vb->req_obj);
+ }
+
+ spin_unlock_irqrestore(&q->done_lock, flags);
+
+ trace_vb2_buf_done(q, vb);
+
+ switch (state) {
+ case VB2_BUF_STATE_QUEUED:
+ return;
+ default:
+ /* Inform any processes that may be waiting for buffers */
+ wake_up(&q->done_wq);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(vb2_buffer_done);
+
+void vb2_discard_done(struct vb2_queue *q)
+{
+ struct vb2_buffer *vb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&q->done_lock, flags);
+ list_for_each_entry(vb, &q->done_list, done_entry)
+ vb->state = VB2_BUF_STATE_ERROR;
+ spin_unlock_irqrestore(&q->done_lock, flags);
+}
+EXPORT_SYMBOL_GPL(vb2_discard_done);
+
+/*
+ * __prepare_mmap() - prepare an MMAP buffer
+ */
+static int __prepare_mmap(struct vb2_buffer *vb)
+{
+ int ret = 0;
+
+ ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
+ vb, vb->planes);
+ return ret ? ret : call_vb_qop(vb, buf_prepare, vb);
+}
+
+/*
+ * __prepare_userptr() - prepare a USERPTR buffer
+ */
+static int __prepare_userptr(struct vb2_buffer *vb)
+{
+ struct vb2_plane planes[VB2_MAX_PLANES];
+ struct vb2_queue *q = vb->vb2_queue;
+ void *mem_priv;
+ unsigned int plane;
+ int ret = 0;
+ bool reacquired = vb->planes[0].mem_priv == NULL;
+
+ memset(planes, 0, sizeof(planes[0]) * vb->num_planes);
+ /* Copy relevant information provided by the userspace */
+ ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
+ vb, planes);
+ if (ret)
+ return ret;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ /* Skip the plane if already verified */
+ if (vb->planes[plane].m.userptr &&
+ vb->planes[plane].m.userptr == planes[plane].m.userptr
+ && vb->planes[plane].length == planes[plane].length)
+ continue;
+
+ dprintk(q, 3, "userspace address for plane %d changed, reacquiring memory\n",
+ plane);
+
+ /* Check if the provided plane buffer is large enough */
+ if (planes[plane].length < vb->planes[plane].min_length) {
+ dprintk(q, 1, "provided buffer size %u is less than setup size %u for plane %d\n",
+ planes[plane].length,
+ vb->planes[plane].min_length,
+ plane);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Release previously acquired memory if present */
+ if (vb->planes[plane].mem_priv) {
+ if (!reacquired) {
+ reacquired = true;
+ vb->copied_timestamp = 0;
+ call_void_vb_qop(vb, buf_cleanup, vb);
+ }
+ call_void_memop(vb, put_userptr, vb->planes[plane].mem_priv);
+ }
+
+ vb->planes[plane].mem_priv = NULL;
+ vb->planes[plane].bytesused = 0;
+ vb->planes[plane].length = 0;
+ vb->planes[plane].m.userptr = 0;
+ vb->planes[plane].data_offset = 0;
+
+ /* Acquire each plane's memory */
+ mem_priv = call_ptr_memop(get_userptr,
+ vb,
+ q->alloc_devs[plane] ? : q->dev,
+ planes[plane].m.userptr,
+ planes[plane].length);
+ if (IS_ERR(mem_priv)) {
+ dprintk(q, 1, "failed acquiring userspace memory for plane %d\n",
+ plane);
+ ret = PTR_ERR(mem_priv);
+ goto err;
+ }
+ vb->planes[plane].mem_priv = mem_priv;
+ }
+
+ /*
+ * Now that everything is in order, copy relevant information
+ * provided by userspace.
+ */
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ vb->planes[plane].bytesused = planes[plane].bytesused;
+ vb->planes[plane].length = planes[plane].length;
+ vb->planes[plane].m.userptr = planes[plane].m.userptr;
+ vb->planes[plane].data_offset = planes[plane].data_offset;
+ }
+
+ if (reacquired) {
+ /*
+ * One or more planes changed, so we must call buf_init to do
+ * the driver-specific initialization on the newly acquired
+ * buffer, if provided.
+ */
+ ret = call_vb_qop(vb, buf_init, vb);
+ if (ret) {
+ dprintk(q, 1, "buffer initialization failed\n");
+ goto err;
+ }
+ }
+
+ ret = call_vb_qop(vb, buf_prepare, vb);
+ if (ret) {
+ dprintk(q, 1, "buffer preparation failed\n");
+ call_void_vb_qop(vb, buf_cleanup, vb);
+ goto err;
+ }
+
+ return 0;
+err:
+ /* In case of errors, release planes that were already acquired */
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ if (vb->planes[plane].mem_priv)
+ call_void_memop(vb, put_userptr,
+ vb->planes[plane].mem_priv);
+ vb->planes[plane].mem_priv = NULL;
+ vb->planes[plane].m.userptr = 0;
+ vb->planes[plane].length = 0;
+ }
+
+ return ret;
+}
+
+/*
+ * __prepare_dmabuf() - prepare a DMABUF buffer
+ */
+static int __prepare_dmabuf(struct vb2_buffer *vb)
+{
+ struct vb2_plane planes[VB2_MAX_PLANES];
+ struct vb2_queue *q = vb->vb2_queue;
+ void *mem_priv;
+ unsigned int plane, i;
+ int ret = 0;
+ bool reacquired = vb->planes[0].mem_priv == NULL;
+
+ memset(planes, 0, sizeof(planes[0]) * vb->num_planes);
+ /* Copy relevant information provided by the userspace */
+ ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
+ vb, planes);
+ if (ret)
+ return ret;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd);
+
+ planes[plane].dbuf = dbuf;
+
+ if (IS_ERR_OR_NULL(dbuf)) {
+ dprintk(q, 1, "invalid dmabuf fd for plane %d\n",
+ plane);
+ ret = -EINVAL;
+ goto err_put_planes;
+ }
+
+ /* use DMABUF size if length is not provided */
+ if (planes[plane].length == 0)
+ planes[plane].length = dbuf->size;
+
+ if (planes[plane].length < vb->planes[plane].min_length) {
+ dprintk(q, 1, "invalid dmabuf length %u for plane %d, minimum length %u\n",
+ planes[plane].length, plane,
+ vb->planes[plane].min_length);
+ ret = -EINVAL;
+ goto err_put_planes;
+ }
+
+ /* Skip the plane if already verified */
+ if (dbuf == vb->planes[plane].dbuf &&
+ vb->planes[plane].length == planes[plane].length)
+ continue;
+
+ dprintk(q, 3, "buffer for plane %d changed\n", plane);
+
+ reacquired = true;
+ }
+
+ if (reacquired) {
+ if (vb->planes[0].mem_priv) {
+ vb->copied_timestamp = 0;
+ call_void_vb_qop(vb, buf_cleanup, vb);
+ __vb2_buf_dmabuf_put(vb);
+ }
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ /*
+ * This is an optimization to reduce dma_buf attachment/mapping.
+ * When the same dma_buf is used for multiple planes, there is no need
+ * to create duplicated attachments.
+ */
+ for (i = 0; i < plane; ++i) {
+ if (planes[plane].dbuf == vb->planes[i].dbuf &&
+ q->alloc_devs[plane] == q->alloc_devs[i]) {
+ vb->planes[plane].dbuf_duplicated = true;
+ vb->planes[plane].dbuf = vb->planes[i].dbuf;
+ vb->planes[plane].mem_priv = vb->planes[i].mem_priv;
+ break;
+ }
+ }
+
+ if (vb->planes[plane].dbuf_duplicated)
+ continue;
+
+ /* Acquire each plane's memory */
+ mem_priv = call_ptr_memop(attach_dmabuf,
+ vb,
+ q->alloc_devs[plane] ? : q->dev,
+ planes[plane].dbuf,
+ planes[plane].length);
+ if (IS_ERR(mem_priv)) {
+ dprintk(q, 1, "failed to attach dmabuf\n");
+ ret = PTR_ERR(mem_priv);
+ goto err_put_vb2_buf;
+ }
+
+ vb->planes[plane].dbuf = planes[plane].dbuf;
+ vb->planes[plane].mem_priv = mem_priv;
+
+ /*
+ * This pins the buffer(s) with dma_buf_map_attachment()). It's done
+ * here instead just before the DMA, while queueing the buffer(s) so
+ * userspace knows sooner rather than later if the dma-buf map fails.
+ */
+ ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv);
+ if (ret) {
+ dprintk(q, 1, "failed to map dmabuf for plane %d\n",
+ plane);
+ goto err_put_vb2_buf;
+ }
+ vb->planes[plane].dbuf_mapped = 1;
+ }
+ } else {
+ for (plane = 0; plane < vb->num_planes; ++plane)
+ dma_buf_put(planes[plane].dbuf);
+ }
+
+ /*
+ * Now that everything is in order, copy relevant information
+ * provided by userspace.
+ */
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ vb->planes[plane].bytesused = planes[plane].bytesused;
+ vb->planes[plane].length = planes[plane].length;
+ vb->planes[plane].m.fd = planes[plane].m.fd;
+ vb->planes[plane].data_offset = planes[plane].data_offset;
+ }
+
+ if (reacquired) {
+ /*
+ * Call driver-specific initialization on the newly acquired buffer,
+ * if provided.
+ */
+ ret = call_vb_qop(vb, buf_init, vb);
+ if (ret) {
+ dprintk(q, 1, "buffer initialization failed\n");
+ goto err_put_vb2_buf;
+ }
+ }
+
+ ret = call_vb_qop(vb, buf_prepare, vb);
+ if (ret) {
+ dprintk(q, 1, "buffer preparation failed\n");
+ call_void_vb_qop(vb, buf_cleanup, vb);
+ goto err_put_vb2_buf;
+ }
+
+ return 0;
+
+err_put_planes:
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ if (!IS_ERR_OR_NULL(planes[plane].dbuf))
+ dma_buf_put(planes[plane].dbuf);
+ }
+err_put_vb2_buf:
+ /* In case of errors, release planes that were already acquired */
+ __vb2_buf_dmabuf_put(vb);
+
+ return ret;
+}
+
+/*
+ * __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing
+ */
+static void __enqueue_in_driver(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+
+ vb->state = VB2_BUF_STATE_ACTIVE;
+ atomic_inc(&q->owned_by_drv_count);
+
+ trace_vb2_buf_queue(q, vb);
+
+ call_void_vb_qop(vb, buf_queue, vb);
+}
+
+static int __buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ enum vb2_buffer_state orig_state = vb->state;
+ int ret;
+
+ if (q->error) {
+ dprintk(q, 1, "fatal error occurred on queue\n");
+ return -EIO;
+ }
+
+ if (vb->prepared)
+ return 0;
+ WARN_ON(vb->synced);
+
+ if (q->is_output) {
+ ret = call_vb_qop(vb, buf_out_validate, vb);
+ if (ret) {
+ dprintk(q, 1, "buffer validation failed\n");
+ return ret;
+ }
+ }
+
+ vb->state = VB2_BUF_STATE_PREPARING;
+
+ switch (q->memory) {
+ case VB2_MEMORY_MMAP:
+ ret = __prepare_mmap(vb);
+ break;
+ case VB2_MEMORY_USERPTR:
+ ret = __prepare_userptr(vb);
+ break;
+ case VB2_MEMORY_DMABUF:
+ ret = __prepare_dmabuf(vb);
+ break;
+ default:
+ WARN(1, "Invalid queue type\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret) {
+ dprintk(q, 1, "buffer preparation failed: %d\n", ret);
+ vb->state = orig_state;
+ return ret;
+ }
+
+ __vb2_buf_mem_prepare(vb);
+ vb->prepared = 1;
+ vb->state = orig_state;
+
+ return 0;
+}
+
+static int vb2_req_prepare(struct media_request_object *obj)
+{
+ struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
+ int ret;
+
+ if (WARN_ON(vb->state != VB2_BUF_STATE_IN_REQUEST))
+ return -EINVAL;
+
+ mutex_lock(vb->vb2_queue->lock);
+ ret = __buf_prepare(vb);
+ mutex_unlock(vb->vb2_queue->lock);
+ return ret;
+}
+
+static void __vb2_dqbuf(struct vb2_buffer *vb);
+
+static void vb2_req_unprepare(struct media_request_object *obj)
+{
+ struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
+
+ mutex_lock(vb->vb2_queue->lock);
+ __vb2_dqbuf(vb);
+ vb->state = VB2_BUF_STATE_IN_REQUEST;
+ mutex_unlock(vb->vb2_queue->lock);
+ WARN_ON(!vb->req_obj.req);
+}
+
+static void vb2_req_queue(struct media_request_object *obj)
+{
+ struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
+ int err;
+
+ mutex_lock(vb->vb2_queue->lock);
+ /*
+ * There is no method to propagate an error from vb2_core_qbuf(),
+ * so if this returns a non-0 value, then WARN.
+ *
+ * The only exception is -EIO which is returned if q->error is
+ * set. We just ignore that, and expect this will be caught the
+ * next time vb2_req_prepare() is called.
+ */
+ err = vb2_core_qbuf(vb->vb2_queue, vb, NULL, NULL);
+ WARN_ON_ONCE(err && err != -EIO);
+ mutex_unlock(vb->vb2_queue->lock);
+}
+
+static void vb2_req_unbind(struct media_request_object *obj)
+{
+ struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
+
+ if (vb->state == VB2_BUF_STATE_IN_REQUEST)
+ call_void_bufop(vb->vb2_queue, init_buffer, vb);
+}
+
+static void vb2_req_release(struct media_request_object *obj)
+{
+ struct vb2_buffer *vb = container_of(obj, struct vb2_buffer, req_obj);
+
+ if (vb->state == VB2_BUF_STATE_IN_REQUEST) {
+ vb->state = VB2_BUF_STATE_DEQUEUED;
+ if (vb->request)
+ media_request_put(vb->request);
+ vb->request = NULL;
+ }
+}
+
+static const struct media_request_object_ops vb2_core_req_ops = {
+ .prepare = vb2_req_prepare,
+ .unprepare = vb2_req_unprepare,
+ .queue = vb2_req_queue,
+ .unbind = vb2_req_unbind,
+ .release = vb2_req_release,
+};
+
+bool vb2_request_object_is_buffer(struct media_request_object *obj)
+{
+ return obj->ops == &vb2_core_req_ops;
+}
+EXPORT_SYMBOL_GPL(vb2_request_object_is_buffer);
+
+unsigned int vb2_request_buffer_cnt(struct media_request *req)
+{
+ struct media_request_object *obj;
+ unsigned long flags;
+ unsigned int buffer_cnt = 0;
+
+ spin_lock_irqsave(&req->lock, flags);
+ list_for_each_entry(obj, &req->objects, list)
+ if (vb2_request_object_is_buffer(obj))
+ buffer_cnt++;
+ spin_unlock_irqrestore(&req->lock, flags);
+
+ return buffer_cnt;
+}
+EXPORT_SYMBOL_GPL(vb2_request_buffer_cnt);
+
+int vb2_core_prepare_buf(struct vb2_queue *q, struct vb2_buffer *vb, void *pb)
+{
+ int ret;
+
+ if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+ dprintk(q, 1, "invalid buffer state %s\n",
+ vb2_state_name(vb->state));
+ return -EINVAL;
+ }
+ if (vb->prepared) {
+ dprintk(q, 1, "buffer already prepared\n");
+ return -EINVAL;
+ }
+
+ ret = __buf_prepare(vb);
+ if (ret)
+ return ret;
+
+ /* Fill buffer information for the userspace */
+ call_void_bufop(q, fill_user_buffer, vb, pb);
+
+ dprintk(q, 2, "prepare of buffer %d succeeded\n", vb->index);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_core_prepare_buf);
+
+int vb2_core_remove_bufs(struct vb2_queue *q, unsigned int start, unsigned int count)
+{
+ unsigned int i, ret = 0;
+ unsigned int q_num_bufs = vb2_get_num_buffers(q);
+
+ if (count == 0)
+ return 0;
+
+ if (count > q_num_bufs)
+ return -EINVAL;
+
+ if (start > q->max_num_buffers - count)
+ return -EINVAL;
+
+ mutex_lock(&q->mmap_lock);
+
+ /* Check that all buffers in the range exist */
+ for (i = start; i < start + count; i++) {
+ struct vb2_buffer *vb = vb2_get_buffer(q, i);
+
+ if (!vb) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+ }
+ __vb2_queue_free(q, start, count);
+ dprintk(q, 2, "%u buffers removed\n", count);
+
+unlock:
+ mutex_unlock(&q->mmap_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_core_remove_bufs);
+
+/*
+ * vb2_start_streaming() - Attempt to start streaming.
+ * @q: videobuf2 queue
+ *
+ * Attempt to start streaming. When this function is called there must be
+ * at least q->min_queued_buffers queued up (i.e. the minimum
+ * number of buffers required for the DMA engine to function). If the
+ * @start_streaming op fails it is supposed to return all the driver-owned
+ * buffers back to vb2 in state QUEUED. Check if that happened and if
+ * not warn and reclaim them forcefully.
+ */
+static int vb2_start_streaming(struct vb2_queue *q)
+{
+ struct vb2_buffer *vb;
+ int ret;
+
+ /*
+ * If any buffers were queued before streamon,
+ * we can now pass them to driver for processing.
+ */
+ list_for_each_entry(vb, &q->queued_list, queued_entry)
+ __enqueue_in_driver(vb);
+
+ /* Tell the driver to start streaming */
+ q->start_streaming_called = 1;
+ ret = call_qop(q, start_streaming, q,
+ atomic_read(&q->owned_by_drv_count));
+ if (!ret)
+ return 0;
+
+ q->start_streaming_called = 0;
+
+ dprintk(q, 1, "driver refused to start streaming\n");
+ /*
+ * If you see this warning, then the driver isn't cleaning up properly
+ * after a failed start_streaming(). See the start_streaming()
+ * documentation in videobuf2-core.h for more information how buffers
+ * should be returned to vb2 in start_streaming().
+ */
+ if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
+ unsigned i;
+
+ /*
+ * Forcefully reclaim buffers if the driver did not
+ * correctly return them to vb2.
+ */
+ for (i = 0; i < q->max_num_buffers; ++i) {
+ vb = vb2_get_buffer(q, i);
+
+ if (!vb)
+ continue;
+
+ if (vb->state == VB2_BUF_STATE_ACTIVE)
+ vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);
+ }
+ /* Must be zero now */
+ WARN_ON(atomic_read(&q->owned_by_drv_count));
+ }
+ /*
+ * If done_list is not empty, then start_streaming() didn't call
+ * vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED) but STATE_ERROR or
+ * STATE_DONE.
+ */
+ WARN_ON(!list_empty(&q->done_list));
+ return ret;
+}
+
+int vb2_core_qbuf(struct vb2_queue *q, struct vb2_buffer *vb, void *pb,
+ struct media_request *req)
+{
+ enum vb2_buffer_state orig_state;
+ int ret;
+
+ if (q->error) {
+ dprintk(q, 1, "fatal error occurred on queue\n");
+ return -EIO;
+ }
+
+ if (!req && vb->state != VB2_BUF_STATE_IN_REQUEST &&
+ q->requires_requests) {
+ dprintk(q, 1, "qbuf requires a request\n");
+ return -EBADR;
+ }
+
+ if ((req && q->uses_qbuf) ||
+ (!req && vb->state != VB2_BUF_STATE_IN_REQUEST &&
+ q->uses_requests)) {
+ dprintk(q, 1, "queue in wrong mode (qbuf vs requests)\n");
+ return -EBUSY;
+ }
+
+ if (req) {
+ int ret;
+
+ q->uses_requests = 1;
+ if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+ dprintk(q, 1, "buffer %d not in dequeued state\n",
+ vb->index);
+ return -EINVAL;
+ }
+
+ if (q->is_output && !vb->prepared) {
+ ret = call_vb_qop(vb, buf_out_validate, vb);
+ if (ret) {
+ dprintk(q, 1, "buffer validation failed\n");
+ return ret;
+ }
+ }
+
+ media_request_object_init(&vb->req_obj);
+
+ /* Make sure the request is in a safe state for updating. */
+ ret = media_request_lock_for_update(req);
+ if (ret)
+ return ret;
+ ret = media_request_object_bind(req, &vb2_core_req_ops,
+ q, true, &vb->req_obj);
+ media_request_unlock_for_update(req);
+ if (ret)
+ return ret;
+
+ vb->state = VB2_BUF_STATE_IN_REQUEST;
+
+ /*
+ * Increment the refcount and store the request.
+ * The request refcount is decremented again when the
+ * buffer is dequeued. This is to prevent vb2_buffer_done()
+ * from freeing the request from interrupt context, which can
+ * happen if the application closed the request fd after
+ * queueing the request.
+ */
+ media_request_get(req);
+ vb->request = req;
+
+ /* Fill buffer information for the userspace */
+ if (pb) {
+ call_void_bufop(q, copy_timestamp, vb, pb);
+ call_void_bufop(q, fill_user_buffer, vb, pb);
+ }
+
+ dprintk(q, 2, "qbuf of buffer %d succeeded\n", vb->index);
+ return 0;
+ }
+
+ if (vb->state != VB2_BUF_STATE_IN_REQUEST)
+ q->uses_qbuf = 1;
+
+ switch (vb->state) {
+ case VB2_BUF_STATE_DEQUEUED:
+ case VB2_BUF_STATE_IN_REQUEST:
+ if (!vb->prepared) {
+ ret = __buf_prepare(vb);
+ if (ret)
+ return ret;
+ }
+ break;
+ case VB2_BUF_STATE_PREPARING:
+ dprintk(q, 1, "buffer still being prepared\n");
+ return -EINVAL;
+ default:
+ dprintk(q, 1, "invalid buffer state %s\n",
+ vb2_state_name(vb->state));
+ return -EINVAL;
+ }
+
+ /*
+ * Add to the queued buffers list, a buffer will stay on it until
+ * dequeued in dqbuf.
+ */
+ orig_state = vb->state;
+ list_add_tail(&vb->queued_entry, &q->queued_list);
+ q->queued_count++;
+ q->waiting_for_buffers = false;
+ vb->state = VB2_BUF_STATE_QUEUED;
+
+ if (pb)
+ call_void_bufop(q, copy_timestamp, vb, pb);
+
+ trace_vb2_qbuf(q, vb);
+
+ /*
+ * If already streaming, give the buffer to driver for processing.
+ * If not, the buffer will be given to driver on next streamon.
+ */
+ if (q->start_streaming_called)
+ __enqueue_in_driver(vb);
+
+ /* Fill buffer information for the userspace */
+ if (pb)
+ call_void_bufop(q, fill_user_buffer, vb, pb);
+
+ /*
+ * If streamon has been called, and we haven't yet called
+ * start_streaming() since not enough buffers were queued, and
+ * we now have reached the minimum number of queued buffers,
+ * then we can finally call start_streaming().
+ */
+ if (q->streaming && !q->start_streaming_called &&
+ q->queued_count >= q->min_queued_buffers) {
+ ret = vb2_start_streaming(q);
+ if (ret) {
+ /*
+ * Since vb2_core_qbuf will return with an error,
+ * we should return it to state DEQUEUED since
+ * the error indicates that the buffer wasn't queued.
+ */
+ list_del(&vb->queued_entry);
+ q->queued_count--;
+ vb->state = orig_state;
+ return ret;
+ }
+ }
+
+ dprintk(q, 2, "qbuf of buffer %d succeeded\n", vb->index);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_core_qbuf);
+
+/*
+ * __vb2_wait_for_done_vb() - wait for a buffer to become available
+ * for dequeuing
+ *
+ * Will sleep if required for nonblocking == false.
+ */
+static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
+{
+ /*
+ * All operations on vb_done_list are performed under done_lock
+ * spinlock protection. However, buffers may be removed from
+ * it and returned to userspace only while holding both driver's
+ * lock and the done_lock spinlock. Thus we can be sure that as
+ * long as we hold the driver's lock, the list will remain not
+ * empty if list_empty() check succeeds.
+ */
+
+ for (;;) {
+ int ret;
+
+ if (q->waiting_in_dqbuf) {
+ dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n");
+ return -EBUSY;
+ }
+
+ if (!q->streaming) {
+ dprintk(q, 1, "streaming off, will not wait for buffers\n");
+ return -EINVAL;
+ }
+
+ if (q->error) {
+ dprintk(q, 1, "Queue in error state, will not wait for buffers\n");
+ return -EIO;
+ }
+
+ if (q->last_buffer_dequeued) {
+ dprintk(q, 3, "last buffer dequeued already, will not wait for buffers\n");
+ return -EPIPE;
+ }
+
+ if (!list_empty(&q->done_list)) {
+ /*
+ * Found a buffer that we were waiting for.
+ */
+ break;
+ }
+
+ if (nonblocking) {
+ dprintk(q, 3, "nonblocking and no buffers to dequeue, will not wait\n");
+ return -EAGAIN;
+ }
+
+ q->waiting_in_dqbuf = 1;
+ /*
+ * We are streaming and blocking, wait for another buffer to
+ * become ready or for streamoff. Driver's lock is released to
+ * allow streamoff or qbuf to be called while waiting.
+ */
+ if (q->ops->wait_prepare)
+ call_void_qop(q, wait_prepare, q);
+ else if (q->lock)
+ mutex_unlock(q->lock);
+
+ /*
+ * All locks have been released, it is safe to sleep now.
+ */
+ dprintk(q, 3, "will sleep waiting for buffers\n");
+ ret = wait_event_interruptible(q->done_wq,
+ !list_empty(&q->done_list) || !q->streaming ||
+ q->error);
+
+ if (q->ops->wait_finish)
+ call_void_qop(q, wait_finish, q);
+ else if (q->lock)
+ mutex_lock(q->lock);
+
+ q->waiting_in_dqbuf = 0;
+ /*
+ * We need to reevaluate both conditions again after reacquiring
+ * the locks or return an error if one occurred.
+ */
+ if (ret) {
+ dprintk(q, 1, "sleep was interrupted\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/*
+ * __vb2_get_done_vb() - get a buffer ready for dequeuing
+ *
+ * Will sleep if required for nonblocking == false.
+ */
+static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
+ void *pb, int nonblocking)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ /*
+ * Wait for at least one buffer to become available on the done_list.
+ */
+ ret = __vb2_wait_for_done_vb(q, nonblocking);
+ if (ret)
+ return ret;
+
+ /*
+ * Driver's lock has been held since we last verified that done_list
+ * is not empty, so no need for another list_empty(done_list) check.
+ */
+ spin_lock_irqsave(&q->done_lock, flags);
+ *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
+ /*
+ * Only remove the buffer from done_list if all planes can be
+ * handled. Some cases such as V4L2 file I/O and DVB have pb
+ * == NULL; skip the check then as there's nothing to verify.
+ */
+ if (pb)
+ ret = call_bufop(q, verify_planes_array, *vb, pb);
+ if (!ret)
+ list_del(&(*vb)->done_entry);
+ spin_unlock_irqrestore(&q->done_lock, flags);
+
+ return ret;
+}
+
+int vb2_wait_for_all_buffers(struct vb2_queue *q)
+{
+ if (!q->streaming) {
+ dprintk(q, 1, "streaming off, will not wait for buffers\n");
+ return -EINVAL;
+ }
+
+ if (q->start_streaming_called)
+ wait_event(q->done_wq, !atomic_read(&q->owned_by_drv_count));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers);
+
+/*
+ * __vb2_dqbuf() - bring back the buffer to the DEQUEUED state
+ */
+static void __vb2_dqbuf(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+
+ /* nothing to do if the buffer is already dequeued */
+ if (vb->state == VB2_BUF_STATE_DEQUEUED)
+ return;
+
+ vb->state = VB2_BUF_STATE_DEQUEUED;
+
+ call_void_bufop(q, init_buffer, vb);
+}
+
+int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
+ bool nonblocking)
+{
+ struct vb2_buffer *vb = NULL;
+ int ret;
+
+ ret = __vb2_get_done_vb(q, &vb, pb, nonblocking);
+ if (ret < 0)
+ return ret;
+
+ switch (vb->state) {
+ case VB2_BUF_STATE_DONE:
+ dprintk(q, 3, "returning done buffer\n");
+ break;
+ case VB2_BUF_STATE_ERROR:
+ dprintk(q, 3, "returning done buffer with errors\n");
+ break;
+ default:
+ dprintk(q, 1, "invalid buffer state %s\n",
+ vb2_state_name(vb->state));
+ return -EINVAL;
+ }
+
+ call_void_vb_qop(vb, buf_finish, vb);
+ vb->prepared = 0;
+
+ if (pindex)
+ *pindex = vb->index;
+
+ /* Fill buffer information for the userspace */
+ if (pb)
+ call_void_bufop(q, fill_user_buffer, vb, pb);
+
+ /* Remove from vb2 queue */
+ list_del(&vb->queued_entry);
+ q->queued_count--;
+
+ trace_vb2_dqbuf(q, vb);
+
+ /* go back to dequeued state */
+ __vb2_dqbuf(vb);
+
+ if (WARN_ON(vb->req_obj.req)) {
+ media_request_object_unbind(&vb->req_obj);
+ media_request_object_put(&vb->req_obj);
+ }
+ if (vb->request)
+ media_request_put(vb->request);
+ vb->request = NULL;
+
+ dprintk(q, 2, "dqbuf of buffer %d, state: %s\n",
+ vb->index, vb2_state_name(vb->state));
+
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(vb2_core_dqbuf);
+
+/*
+ * __vb2_queue_cancel() - cancel and stop (pause) streaming
+ *
+ * Removes all queued buffers from driver's queue and all buffers queued by
+ * userspace from vb2's queue. Returns to state after reqbufs.
+ */
+static void __vb2_queue_cancel(struct vb2_queue *q)
+{
+ unsigned int i;
+
+ /*
+ * Tell driver to stop all transactions and release all queued
+ * buffers.
+ */
+ if (q->start_streaming_called)
+ call_void_qop(q, stop_streaming, q);
+
+ if (q->streaming)
+ call_void_qop(q, unprepare_streaming, q);
+
+ /*
+ * If you see this warning, then the driver isn't cleaning up properly
+ * in stop_streaming(). See the stop_streaming() documentation in
+ * videobuf2-core.h for more information how buffers should be returned
+ * to vb2 in stop_streaming().
+ */
+ if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
+ for (i = 0; i < q->max_num_buffers; i++) {
+ struct vb2_buffer *vb = vb2_get_buffer(q, i);
+
+ if (!vb)
+ continue;
+
+ if (vb->state == VB2_BUF_STATE_ACTIVE) {
+ pr_warn("driver bug: stop_streaming operation is leaving buffer %u in active state\n",
+ vb->index);
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ }
+ }
+ /* Must be zero now */
+ WARN_ON(atomic_read(&q->owned_by_drv_count));
+ }
+
+ q->streaming = 0;
+ q->start_streaming_called = 0;
+ q->queued_count = 0;
+ q->error = 0;
+ q->uses_requests = 0;
+ q->uses_qbuf = 0;
+
+ /*
+ * Remove all buffers from vb2's list...
+ */
+ INIT_LIST_HEAD(&q->queued_list);
+ /*
+ * ...and done list; userspace will not receive any buffers it
+ * has not already dequeued before initiating cancel.
+ */
+ INIT_LIST_HEAD(&q->done_list);
+ atomic_set(&q->owned_by_drv_count, 0);
+ wake_up_all(&q->done_wq);
+
+ /*
+ * Reinitialize all buffers for next use.
+ * Make sure to call buf_finish for any queued buffers. Normally
+ * that's done in dqbuf, but that's not going to happen when we
+ * cancel the whole queue. Note: this code belongs here, not in
+ * __vb2_dqbuf() since in vb2_core_dqbuf() there is a critical
+ * call to __fill_user_buffer() after buf_finish(). That order can't
+ * be changed, so we can't move the buf_finish() to __vb2_dqbuf().
+ */
+ for (i = 0; i < q->max_num_buffers; i++) {
+ struct vb2_buffer *vb;
+ struct media_request *req;
+
+ vb = vb2_get_buffer(q, i);
+ if (!vb)
+ continue;
+
+ req = vb->req_obj.req;
+ /*
+ * If a request is associated with this buffer, then
+ * call buf_request_cancel() to give the driver to complete()
+ * related request objects. Otherwise those objects would
+ * never complete.
+ */
+ if (req) {
+ enum media_request_state state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&req->lock, flags);
+ state = req->state;
+ spin_unlock_irqrestore(&req->lock, flags);
+
+ if (state == MEDIA_REQUEST_STATE_QUEUED)
+ call_void_vb_qop(vb, buf_request_complete, vb);
+ }
+
+ __vb2_buf_mem_finish(vb);
+
+ if (vb->prepared) {
+ call_void_vb_qop(vb, buf_finish, vb);
+ vb->prepared = 0;
+ }
+ __vb2_dqbuf(vb);
+
+ if (vb->req_obj.req) {
+ media_request_object_unbind(&vb->req_obj);
+ media_request_object_put(&vb->req_obj);
+ }
+ if (vb->request)
+ media_request_put(vb->request);
+ vb->request = NULL;
+ vb->copied_timestamp = 0;
+ }
+}
+
+int vb2_core_streamon(struct vb2_queue *q, unsigned int type)
+{
+ unsigned int q_num_bufs = vb2_get_num_buffers(q);
+ int ret;
+
+ if (type != q->type) {
+ dprintk(q, 1, "invalid stream type\n");
+ return -EINVAL;
+ }
+
+ if (q->streaming) {
+ dprintk(q, 3, "already streaming\n");
+ return 0;
+ }
+
+ if (!q_num_bufs) {
+ dprintk(q, 1, "no buffers have been allocated\n");
+ return -EINVAL;
+ }
+
+ if (q_num_bufs < q->min_queued_buffers) {
+ dprintk(q, 1, "need at least %u allocated buffers\n",
+ q->min_queued_buffers);
+ return -EINVAL;
+ }
+
+ ret = call_qop(q, prepare_streaming, q);
+ if (ret)
+ return ret;
+
+ /*
+ * Tell driver to start streaming provided sufficient buffers
+ * are available.
+ */
+ if (q->queued_count >= q->min_queued_buffers) {
+ ret = vb2_start_streaming(q);
+ if (ret)
+ goto unprepare;
+ }
+
+ q->streaming = 1;
+
+ dprintk(q, 3, "successful\n");
+ return 0;
+
+unprepare:
+ call_void_qop(q, unprepare_streaming, q);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_core_streamon);
+
+void vb2_queue_error(struct vb2_queue *q)
+{
+ q->error = 1;
+
+ wake_up_all(&q->done_wq);
+}
+EXPORT_SYMBOL_GPL(vb2_queue_error);
+
+int vb2_core_streamoff(struct vb2_queue *q, unsigned int type)
+{
+ if (type != q->type) {
+ dprintk(q, 1, "invalid stream type\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Cancel will pause streaming and remove all buffers from the driver
+ * and vb2, effectively returning control over them to userspace.
+ *
+ * Note that we do this even if q->streaming == 0: if you prepare or
+ * queue buffers, and then call streamoff without ever having called
+ * streamon, you would still expect those buffers to be returned to
+ * their normal dequeued state.
+ */
+ __vb2_queue_cancel(q);
+ q->waiting_for_buffers = !q->is_output;
+ q->last_buffer_dequeued = false;
+
+ dprintk(q, 3, "successful\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_core_streamoff);
+
+/*
+ * __find_plane_by_offset() - find plane associated with the given offset
+ */
+static int __find_plane_by_offset(struct vb2_queue *q, unsigned long offset,
+ struct vb2_buffer **vb, unsigned int *plane)
+{
+ unsigned int buffer;
+
+ /*
+ * Sanity checks to ensure the lock is held, MEMORY_MMAP is
+ * used and fileio isn't active.
+ */
+ lockdep_assert_held(&q->mmap_lock);
+
+ if (q->memory != VB2_MEMORY_MMAP) {
+ dprintk(q, 1, "queue is not currently set up for mmap\n");
+ return -EINVAL;
+ }
+
+ if (vb2_fileio_is_active(q)) {
+ dprintk(q, 1, "file io in progress\n");
+ return -EBUSY;
+ }
+
+ /* Get buffer and plane from the offset */
+ buffer = (offset >> PLANE_INDEX_SHIFT) & BUFFER_INDEX_MASK;
+ *plane = (offset >> PAGE_SHIFT) & PLANE_INDEX_MASK;
+
+ *vb = vb2_get_buffer(q, buffer);
+ if (!*vb)
+ return -EINVAL;
+ if (*plane >= (*vb)->num_planes)
+ return -EINVAL;
+
+ return 0;
+}
+
+int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type,
+ struct vb2_buffer *vb, unsigned int plane, unsigned int flags)
+{
+ struct vb2_plane *vb_plane;
+ int ret;
+ struct dma_buf *dbuf;
+
+ if (q->memory != VB2_MEMORY_MMAP) {
+ dprintk(q, 1, "queue is not currently set up for mmap\n");
+ return -EINVAL;
+ }
+
+ if (!q->mem_ops->get_dmabuf) {
+ dprintk(q, 1, "queue does not support DMA buffer exporting\n");
+ return -EINVAL;
+ }
+
+ if (flags & ~(O_CLOEXEC | O_ACCMODE)) {
+ dprintk(q, 1, "queue does support only O_CLOEXEC and access mode flags\n");
+ return -EINVAL;
+ }
+
+ if (type != q->type) {
+ dprintk(q, 1, "invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ if (plane >= vb->num_planes) {
+ dprintk(q, 1, "buffer plane out of range\n");
+ return -EINVAL;
+ }
+
+ if (vb2_fileio_is_active(q)) {
+ dprintk(q, 1, "expbuf: file io in progress\n");
+ return -EBUSY;
+ }
+
+ vb_plane = &vb->planes[plane];
+
+ dbuf = call_ptr_memop(get_dmabuf,
+ vb,
+ vb_plane->mem_priv,
+ flags & O_ACCMODE);
+ if (IS_ERR_OR_NULL(dbuf)) {
+ dprintk(q, 1, "failed to export buffer %d, plane %d\n",
+ vb->index, plane);
+ return -EINVAL;
+ }
+
+ ret = dma_buf_fd(dbuf, flags & ~O_ACCMODE);
+ if (ret < 0) {
+ dprintk(q, 3, "buffer %d, plane %d failed to export (%d)\n",
+ vb->index, plane, ret);
+ dma_buf_put(dbuf);
+ return ret;
+ }
+
+ dprintk(q, 3, "buffer %d, plane %d exported as %d descriptor\n",
+ vb->index, plane, ret);
+ *fd = ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_core_expbuf);
+
+int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
+{
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ struct vb2_buffer *vb;
+ unsigned int plane = 0;
+ int ret;
+ unsigned long length;
+
+ /*
+ * Check memory area access mode.
+ */
+ if (!(vma->vm_flags & VM_SHARED)) {
+ dprintk(q, 1, "invalid vma flags, VM_SHARED needed\n");
+ return -EINVAL;
+ }
+ if (q->is_output) {
+ if (!(vma->vm_flags & VM_WRITE)) {
+ dprintk(q, 1, "invalid vma flags, VM_WRITE needed\n");
+ return -EINVAL;
+ }
+ } else {
+ if (!(vma->vm_flags & VM_READ)) {
+ dprintk(q, 1, "invalid vma flags, VM_READ needed\n");
+ return -EINVAL;
+ }
+ }
+
+ mutex_lock(&q->mmap_lock);
+
+ /*
+ * Find the plane corresponding to the offset passed by userspace. This
+ * will return an error if not MEMORY_MMAP or file I/O is in progress.
+ */
+ ret = __find_plane_by_offset(q, offset, &vb, &plane);
+ if (ret)
+ goto unlock;
+
+ /*
+ * MMAP requires page_aligned buffers.
+ * The buffer length was page_aligned at __vb2_buf_mem_alloc(),
+ * so, we need to do the same here.
+ */
+ length = PAGE_ALIGN(vb->planes[plane].length);
+ if (length < (vma->vm_end - vma->vm_start)) {
+ dprintk(q, 1,
+ "MMAP invalid, as it would overflow buffer length\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ /*
+ * vm_pgoff is treated in V4L2 API as a 'cookie' to select a buffer,
+ * not as a in-buffer offset. We always want to mmap a whole buffer
+ * from its beginning.
+ */
+ vma->vm_pgoff = 0;
+
+ ret = call_memop(vb, mmap, vb->planes[plane].mem_priv, vma);
+
+unlock:
+ mutex_unlock(&q->mmap_lock);
+ if (ret)
+ return ret;
+
+ dprintk(q, 3, "buffer %u, plane %d successfully mapped\n", vb->index, plane);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_mmap);
+
+#ifndef CONFIG_MMU
+unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags)
+{
+ unsigned long offset = pgoff << PAGE_SHIFT;
+ struct vb2_buffer *vb;
+ unsigned int plane;
+ void *vaddr;
+ int ret;
+
+ mutex_lock(&q->mmap_lock);
+
+ /*
+ * Find the plane corresponding to the offset passed by userspace. This
+ * will return an error if not MEMORY_MMAP or file I/O is in progress.
+ */
+ ret = __find_plane_by_offset(q, offset, &vb, &plane);
+ if (ret)
+ goto unlock;
+
+ vaddr = vb2_plane_vaddr(vb, plane);
+ mutex_unlock(&q->mmap_lock);
+ return vaddr ? (unsigned long)vaddr : -EINVAL;
+
+unlock:
+ mutex_unlock(&q->mmap_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_get_unmapped_area);
+#endif
+
+int vb2_core_queue_init(struct vb2_queue *q)
+{
+ /*
+ * Sanity check
+ */
+ /*
+ * For drivers who don't support max_num_buffers ensure
+ * a backward compatibility.
+ */
+ if (!q->max_num_buffers)
+ q->max_num_buffers = VB2_MAX_FRAME;
+
+ /* The maximum is limited by offset cookie encoding pattern */
+ q->max_num_buffers = min_t(unsigned int, q->max_num_buffers, MAX_BUFFER_INDEX);
+
+ if (WARN_ON(!q) ||
+ WARN_ON(!q->ops) ||
+ WARN_ON(!q->mem_ops) ||
+ WARN_ON(!q->type) ||
+ WARN_ON(!q->io_modes) ||
+ WARN_ON(!q->ops->queue_setup) ||
+ WARN_ON(!q->ops->buf_queue))
+ return -EINVAL;
+
+ if (WARN_ON(q->max_num_buffers < VB2_MAX_FRAME) ||
+ WARN_ON(q->min_queued_buffers > q->max_num_buffers))
+ return -EINVAL;
+
+ if (WARN_ON(q->requires_requests && !q->supports_requests))
+ return -EINVAL;
+
+ /*
+ * This combination is not allowed since a non-zero value of
+ * q->min_queued_buffers can cause vb2_core_qbuf() to fail if
+ * it has to call start_streaming(), and the Request API expects
+ * that queueing a request (and thus queueing a buffer contained
+ * in that request) will always succeed. There is no method of
+ * propagating an error back to userspace.
+ */
+ if (WARN_ON(q->supports_requests && q->min_queued_buffers))
+ return -EINVAL;
+
+ /*
+ * If the driver needs 'min_queued_buffers' in the queue before
+ * calling start_streaming() then the minimum requirement is
+ * 'min_queued_buffers + 1' to keep at least one buffer available
+ * for userspace.
+ */
+ if (q->min_reqbufs_allocation < q->min_queued_buffers + 1)
+ q->min_reqbufs_allocation = q->min_queued_buffers + 1;
+
+ if (WARN_ON(q->min_reqbufs_allocation > q->max_num_buffers))
+ return -EINVAL;
+
+ /* Either both or none are set */
+ if (WARN_ON(!q->ops->wait_prepare ^ !q->ops->wait_finish))
+ return -EINVAL;
+
+ /* Warn if q->lock is NULL and no custom wait_prepare is provided */
+ if (WARN_ON(!q->lock && !q->ops->wait_prepare))
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&q->queued_list);
+ INIT_LIST_HEAD(&q->done_list);
+ spin_lock_init(&q->done_lock);
+ mutex_init(&q->mmap_lock);
+ init_waitqueue_head(&q->done_wq);
+
+ q->memory = VB2_MEMORY_UNKNOWN;
+
+ if (q->buf_struct_size == 0)
+ q->buf_struct_size = sizeof(struct vb2_buffer);
+
+ if (q->bidirectional)
+ q->dma_dir = DMA_BIDIRECTIONAL;
+ else
+ q->dma_dir = q->is_output ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
+ if (q->name[0] == '\0')
+ snprintf(q->name, sizeof(q->name), "%s-%p",
+ q->is_output ? "out" : "cap", q);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_core_queue_init);
+
+static int __vb2_init_fileio(struct vb2_queue *q, int read);
+static int __vb2_cleanup_fileio(struct vb2_queue *q);
+void vb2_core_queue_release(struct vb2_queue *q)
+{
+ __vb2_cleanup_fileio(q);
+ __vb2_queue_cancel(q);
+ mutex_lock(&q->mmap_lock);
+ __vb2_queue_free(q, 0, q->max_num_buffers);
+ vb2_core_free_buffers_storage(q);
+ q->is_busy = 0;
+ mutex_unlock(&q->mmap_lock);
+}
+EXPORT_SYMBOL_GPL(vb2_core_queue_release);
+
+__poll_t vb2_core_poll(struct vb2_queue *q, struct file *file,
+ poll_table *wait)
+{
+ __poll_t req_events = poll_requested_events(wait);
+ struct vb2_buffer *vb = NULL;
+ unsigned long flags;
+
+ /*
+ * poll_wait() MUST be called on the first invocation on all the
+ * potential queues of interest, even if we are not interested in their
+ * events during this first call. Failure to do so will result in
+ * queue's events to be ignored because the poll_table won't be capable
+ * of adding new wait queues thereafter.
+ */
+ poll_wait(file, &q->done_wq, wait);
+
+ if (!q->is_output && !(req_events & (EPOLLIN | EPOLLRDNORM)))
+ return 0;
+ if (q->is_output && !(req_events & (EPOLLOUT | EPOLLWRNORM)))
+ return 0;
+
+ /*
+ * Start file I/O emulator only if streaming API has not been used yet.
+ */
+ if (vb2_get_num_buffers(q) == 0 && !vb2_fileio_is_active(q)) {
+ if (!q->is_output && (q->io_modes & VB2_READ) &&
+ (req_events & (EPOLLIN | EPOLLRDNORM))) {
+ if (__vb2_init_fileio(q, 1))
+ return EPOLLERR;
+ }
+ if (q->is_output && (q->io_modes & VB2_WRITE) &&
+ (req_events & (EPOLLOUT | EPOLLWRNORM))) {
+ if (__vb2_init_fileio(q, 0))
+ return EPOLLERR;
+ /*
+ * Write to OUTPUT queue can be done immediately.
+ */
+ return EPOLLOUT | EPOLLWRNORM;
+ }
+ }
+
+ /*
+ * There is nothing to wait for if the queue isn't streaming, or if the
+ * error flag is set.
+ */
+ if (!vb2_is_streaming(q) || q->error)
+ return EPOLLERR;
+
+ /*
+ * If this quirk is set and QBUF hasn't been called yet then
+ * return EPOLLERR as well. This only affects capture queues, output
+ * queues will always initialize waiting_for_buffers to false.
+ * This quirk is set by V4L2 for backwards compatibility reasons.
+ */
+ if (q->quirk_poll_must_check_waiting_for_buffers &&
+ q->waiting_for_buffers && (req_events & (EPOLLIN | EPOLLRDNORM)))
+ return EPOLLERR;
+
+ /*
+ * For output streams you can call write() as long as there are fewer
+ * buffers queued than there are buffers available.
+ */
+ if (q->is_output && q->fileio && q->queued_count < vb2_get_num_buffers(q))
+ return EPOLLOUT | EPOLLWRNORM;
+
+ if (list_empty(&q->done_list)) {
+ /*
+ * If the last buffer was dequeued from a capture queue,
+ * return immediately. DQBUF will return -EPIPE.
+ */
+ if (q->last_buffer_dequeued)
+ return EPOLLIN | EPOLLRDNORM;
+ }
+
+ /*
+ * Take first buffer available for dequeuing.
+ */
+ spin_lock_irqsave(&q->done_lock, flags);
+ if (!list_empty(&q->done_list))
+ vb = list_first_entry(&q->done_list, struct vb2_buffer,
+ done_entry);
+ spin_unlock_irqrestore(&q->done_lock, flags);
+
+ if (vb && (vb->state == VB2_BUF_STATE_DONE
+ || vb->state == VB2_BUF_STATE_ERROR)) {
+ return (q->is_output) ?
+ EPOLLOUT | EPOLLWRNORM :
+ EPOLLIN | EPOLLRDNORM;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_core_poll);
+
+/*
+ * struct vb2_fileio_buf - buffer context used by file io emulator
+ *
+ * vb2 provides a compatibility layer and emulator of file io (read and
+ * write) calls on top of streaming API. This structure is used for
+ * tracking context related to the buffers.
+ */
+struct vb2_fileio_buf {
+ void *vaddr;
+ unsigned int size;
+ unsigned int pos;
+ unsigned int queued:1;
+};
+
+/*
+ * struct vb2_fileio_data - queue context used by file io emulator
+ *
+ * @cur_index: the index of the buffer currently being read from or
+ * written to. If equal to number of buffers in the vb2_queue
+ * then a new buffer must be dequeued.
+ * @initial_index: in the read() case all buffers are queued up immediately
+ * in __vb2_init_fileio() and __vb2_perform_fileio() just cycles
+ * buffers. However, in the write() case no buffers are initially
+ * queued, instead whenever a buffer is full it is queued up by
+ * __vb2_perform_fileio(). Only once all available buffers have
+ * been queued up will __vb2_perform_fileio() start to dequeue
+ * buffers. This means that initially __vb2_perform_fileio()
+ * needs to know what buffer index to use when it is queuing up
+ * the buffers for the first time. That initial index is stored
+ * in this field. Once it is equal to number of buffers in the
+ * vb2_queue all available buffers have been queued and
+ * __vb2_perform_fileio() should start the normal dequeue/queue cycle.
+ *
+ * vb2 provides a compatibility layer and emulator of file io (read and
+ * write) calls on top of streaming API. For proper operation it required
+ * this structure to save the driver state between each call of the read
+ * or write function.
+ */
+struct vb2_fileio_data {
+ unsigned int count;
+ unsigned int type;
+ unsigned int memory;
+ struct vb2_fileio_buf bufs[VB2_MAX_FRAME];
+ unsigned int cur_index;
+ unsigned int initial_index;
+ unsigned int q_count;
+ unsigned int dq_count;
+ unsigned read_once:1;
+ unsigned write_immediately:1;
+};
+
+/*
+ * __vb2_init_fileio() - initialize file io emulator
+ * @q: videobuf2 queue
+ * @read: mode selector (1 means read, 0 means write)
+ */
+static int __vb2_init_fileio(struct vb2_queue *q, int read)
+{
+ struct vb2_fileio_data *fileio;
+ struct vb2_buffer *vb;
+ int i, ret;
+
+ /*
+ * Sanity check
+ */
+ if (WARN_ON((read && !(q->io_modes & VB2_READ)) ||
+ (!read && !(q->io_modes & VB2_WRITE))))
+ return -EINVAL;
+
+ /*
+ * Check if device supports mapping buffers to kernel virtual space.
+ */
+ if (!q->mem_ops->vaddr)
+ return -EBUSY;
+
+ /*
+ * Check if streaming api has not been already activated.
+ */
+ if (q->streaming || vb2_get_num_buffers(q) > 0)
+ return -EBUSY;
+
+ dprintk(q, 3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n",
+ (read) ? "read" : "write", q->min_reqbufs_allocation, q->fileio_read_once,
+ q->fileio_write_immediately);
+
+ fileio = kzalloc(sizeof(*fileio), GFP_KERNEL);
+ if (fileio == NULL)
+ return -ENOMEM;
+
+ fileio->read_once = q->fileio_read_once;
+ fileio->write_immediately = q->fileio_write_immediately;
+
+ /*
+ * Request buffers and use MMAP type to force driver
+ * to allocate buffers by itself.
+ */
+ fileio->count = q->min_reqbufs_allocation;
+ fileio->memory = VB2_MEMORY_MMAP;
+ fileio->type = q->type;
+ q->fileio = fileio;
+ ret = vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count);
+ if (ret)
+ goto err_kfree;
+ /* vb2_fileio_data supports max VB2_MAX_FRAME buffers */
+ if (fileio->count > VB2_MAX_FRAME) {
+ dprintk(q, 1, "fileio: more than VB2_MAX_FRAME buffers requested\n");
+ ret = -ENOSPC;
+ goto err_reqbufs;
+ }
+
+ /*
+ * Userspace can never add or delete buffers later, so there
+ * will never be holes. It is safe to assume that vb2_get_buffer(q, 0)
+ * will always return a valid vb pointer
+ */
+ vb = vb2_get_buffer(q, 0);
+
+ /*
+ * Check if plane_count is correct
+ * (multiplane buffers are not supported).
+ */
+ if (vb->num_planes != 1) {
+ ret = -EBUSY;
+ goto err_reqbufs;
+ }
+
+ /*
+ * Get kernel address of each buffer.
+ */
+ for (i = 0; i < vb2_get_num_buffers(q); i++) {
+ /* vb can never be NULL when using fileio. */
+ vb = vb2_get_buffer(q, i);
+
+ fileio->bufs[i].vaddr = vb2_plane_vaddr(vb, 0);
+ if (fileio->bufs[i].vaddr == NULL) {
+ ret = -EINVAL;
+ goto err_reqbufs;
+ }
+ fileio->bufs[i].size = vb2_plane_size(vb, 0);
+ }
+
+ /*
+ * Read mode requires pre queuing of all buffers.
+ */
+ if (read) {
+ /*
+ * Queue all buffers.
+ */
+ for (i = 0; i < vb2_get_num_buffers(q); i++) {
+ struct vb2_buffer *vb2 = vb2_get_buffer(q, i);
+
+ if (!vb2)
+ continue;
+
+ ret = vb2_core_qbuf(q, vb2, NULL, NULL);
+ if (ret)
+ goto err_reqbufs;
+ fileio->bufs[i].queued = 1;
+ }
+ /*
+ * All buffers have been queued, so mark that by setting
+ * initial_index to the number of buffers in the vb2_queue
+ */
+ fileio->initial_index = vb2_get_num_buffers(q);
+ fileio->cur_index = fileio->initial_index;
+ }
+
+ /*
+ * Start streaming.
+ */
+ ret = vb2_core_streamon(q, q->type);
+ if (ret)
+ goto err_reqbufs;
+
+ return ret;
+
+err_reqbufs:
+ fileio->count = 0;
+ vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count);
+
+err_kfree:
+ q->fileio = NULL;
+ kfree(fileio);
+ return ret;
+}
+
+/*
+ * __vb2_cleanup_fileio() - free resourced used by file io emulator
+ * @q: videobuf2 queue
+ */
+static int __vb2_cleanup_fileio(struct vb2_queue *q)
+{
+ struct vb2_fileio_data *fileio = q->fileio;
+
+ if (fileio) {
+ vb2_core_streamoff(q, q->type);
+ q->fileio = NULL;
+ fileio->count = 0;
+ vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count);
+ kfree(fileio);
+ dprintk(q, 3, "file io emulator closed\n");
+ }
+ return 0;
+}
+
+/*
+ * __vb2_perform_fileio() - perform a single file io (read or write) operation
+ * @q: videobuf2 queue
+ * @data: pointed to target userspace buffer
+ * @count: number of bytes to read or write
+ * @ppos: file handle position tracking pointer
+ * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking)
+ * @read: access mode selector (1 means read, 0 means write)
+ */
+static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblock, int read)
+{
+ struct vb2_fileio_data *fileio;
+ struct vb2_fileio_buf *buf;
+ bool is_multiplanar = q->is_multiplanar;
+ /*
+ * When using write() to write data to an output video node the vb2 core
+ * should copy timestamps if V4L2_BUF_FLAG_TIMESTAMP_COPY is set. Nobody
+ * else is able to provide this information with the write() operation.
+ */
+ bool copy_timestamp = !read && q->copy_timestamp;
+ unsigned index;
+ int ret;
+
+ dprintk(q, 3, "mode %s, offset %ld, count %zd, %sblocking\n",
+ read ? "read" : "write", (long)*ppos, count,
+ nonblock ? "non" : "");
+
+ if (!data)
+ return -EINVAL;
+
+ if (q->waiting_in_dqbuf) {
+ dprintk(q, 3, "another dup()ped fd is %s\n",
+ read ? "reading" : "writing");
+ return -EBUSY;
+ }
+
+ /*
+ * Initialize emulator on first call.
+ */
+ if (!vb2_fileio_is_active(q)) {
+ ret = __vb2_init_fileio(q, read);
+ dprintk(q, 3, "vb2_init_fileio result: %d\n", ret);
+ if (ret)
+ return ret;
+ }
+ fileio = q->fileio;
+
+ /*
+ * Check if we need to dequeue the buffer.
+ */
+ index = fileio->cur_index;
+ if (index >= vb2_get_num_buffers(q)) {
+ struct vb2_buffer *b;
+
+ /*
+ * Call vb2_dqbuf to get buffer back.
+ */
+ ret = vb2_core_dqbuf(q, &index, NULL, nonblock);
+ dprintk(q, 5, "vb2_dqbuf result: %d\n", ret);
+ if (ret)
+ return ret;
+ fileio->dq_count += 1;
+
+ fileio->cur_index = index;
+ buf = &fileio->bufs[index];
+
+ /* b can never be NULL when using fileio. */
+ b = vb2_get_buffer(q, index);
+
+ /*
+ * Get number of bytes filled by the driver
+ */
+ buf->pos = 0;
+ buf->queued = 0;
+ buf->size = read ? vb2_get_plane_payload(b, 0)
+ : vb2_plane_size(b, 0);
+ /* Compensate for data_offset on read in the multiplanar case. */
+ if (is_multiplanar && read &&
+ b->planes[0].data_offset < buf->size) {
+ buf->pos = b->planes[0].data_offset;
+ buf->size -= buf->pos;
+ }
+ } else {
+ buf = &fileio->bufs[index];
+ }
+
+ /*
+ * Limit count on last few bytes of the buffer.
+ */
+ if (buf->pos + count > buf->size) {
+ count = buf->size - buf->pos;
+ dprintk(q, 5, "reducing read count: %zd\n", count);
+ }
+
+ /*
+ * Transfer data to userspace.
+ */
+ dprintk(q, 3, "copying %zd bytes - buffer %d, offset %u\n",
+ count, index, buf->pos);
+ if (read)
+ ret = copy_to_user(data, buf->vaddr + buf->pos, count);
+ else
+ ret = copy_from_user(buf->vaddr + buf->pos, data, count);
+ if (ret) {
+ dprintk(q, 3, "error copying data\n");
+ return -EFAULT;
+ }
+
+ /*
+ * Update counters.
+ */
+ buf->pos += count;
+ *ppos += count;
+
+ /*
+ * Queue next buffer if required.
+ */
+ if (buf->pos == buf->size || (!read && fileio->write_immediately)) {
+ /* b can never be NULL when using fileio. */
+ struct vb2_buffer *b = vb2_get_buffer(q, index);
+
+ /*
+ * Check if this is the last buffer to read.
+ */
+ if (read && fileio->read_once && fileio->dq_count == 1) {
+ dprintk(q, 3, "read limit reached\n");
+ return __vb2_cleanup_fileio(q);
+ }
+
+ /*
+ * Call vb2_qbuf and give buffer to the driver.
+ */
+ b->planes[0].bytesused = buf->pos;
+
+ if (copy_timestamp)
+ b->timestamp = ktime_get_ns();
+ ret = vb2_core_qbuf(q, b, NULL, NULL);
+ dprintk(q, 5, "vb2_qbuf result: %d\n", ret);
+ if (ret)
+ return ret;
+
+ /*
+ * Buffer has been queued, update the status
+ */
+ buf->pos = 0;
+ buf->queued = 1;
+ buf->size = vb2_plane_size(b, 0);
+ fileio->q_count += 1;
+ /*
+ * If we are queuing up buffers for the first time, then
+ * increase initial_index by one.
+ */
+ if (fileio->initial_index < vb2_get_num_buffers(q))
+ fileio->initial_index++;
+ /*
+ * The next buffer to use is either a buffer that's going to be
+ * queued for the first time (initial_index < number of buffers in the vb2_queue)
+ * or it is equal to the number of buffers in the vb2_queue,
+ * meaning that the next time we need to dequeue a buffer since
+ * we've now queued up all the 'first time' buffers.
+ */
+ fileio->cur_index = fileio->initial_index;
+ }
+
+ /*
+ * Return proper number of bytes processed.
+ */
+ if (ret == 0)
+ ret = count;
+ return ret;
+}
+
+size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblocking)
+{
+ return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
+}
+EXPORT_SYMBOL_GPL(vb2_read);
+
+size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
+ loff_t *ppos, int nonblocking)
+{
+ return __vb2_perform_fileio(q, (char __user *) data, count,
+ ppos, nonblocking, 0);
+}
+EXPORT_SYMBOL_GPL(vb2_write);
+
+struct vb2_threadio_data {
+ struct task_struct *thread;
+ vb2_thread_fnc fnc;
+ void *priv;
+ bool stop;
+};
+
+static int vb2_thread(void *data)
+{
+ struct vb2_queue *q = data;
+ struct vb2_threadio_data *threadio = q->threadio;
+ bool copy_timestamp = false;
+ unsigned prequeue = 0;
+ unsigned index = 0;
+ int ret = 0;
+
+ if (q->is_output) {
+ prequeue = vb2_get_num_buffers(q);
+ copy_timestamp = q->copy_timestamp;
+ }
+
+ set_freezable();
+
+ for (;;) {
+ struct vb2_buffer *vb;
+
+ /*
+ * Call vb2_dqbuf to get buffer back.
+ */
+ if (prequeue) {
+ vb = vb2_get_buffer(q, index++);
+ if (!vb)
+ continue;
+ prequeue--;
+ } else {
+ if (!threadio->stop) {
+ if (q->ops->wait_finish)
+ call_void_qop(q, wait_finish, q);
+ else if (q->lock)
+ mutex_lock(q->lock);
+ ret = vb2_core_dqbuf(q, &index, NULL, 0);
+ if (q->ops->wait_prepare)
+ call_void_qop(q, wait_prepare, q);
+ else if (q->lock)
+ mutex_unlock(q->lock);
+ }
+ dprintk(q, 5, "file io: vb2_dqbuf result: %d\n", ret);
+ if (!ret)
+ vb = vb2_get_buffer(q, index);
+ }
+ if (ret || threadio->stop)
+ break;
+ try_to_freeze();
+
+ if (vb->state != VB2_BUF_STATE_ERROR)
+ if (threadio->fnc(vb, threadio->priv))
+ break;
+ if (copy_timestamp)
+ vb->timestamp = ktime_get_ns();
+ if (!threadio->stop) {
+ if (q->ops->wait_finish)
+ call_void_qop(q, wait_finish, q);
+ else if (q->lock)
+ mutex_lock(q->lock);
+ ret = vb2_core_qbuf(q, vb, NULL, NULL);
+ if (q->ops->wait_prepare)
+ call_void_qop(q, wait_prepare, q);
+ else if (q->lock)
+ mutex_unlock(q->lock);
+ }
+ if (ret || threadio->stop)
+ break;
+ }
+
+ /* Hmm, linux becomes *very* unhappy without this ... */
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ }
+ return 0;
+}
+
+/*
+ * This function should not be used for anything else but the videobuf2-dvb
+ * support. If you think you have another good use-case for this, then please
+ * contact the linux-media mailinglist first.
+ */
+int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv,
+ const char *thread_name)
+{
+ struct vb2_threadio_data *threadio;
+ int ret = 0;
+
+ if (q->threadio)
+ return -EBUSY;
+ if (vb2_is_busy(q))
+ return -EBUSY;
+ if (WARN_ON(q->fileio))
+ return -EBUSY;
+
+ threadio = kzalloc(sizeof(*threadio), GFP_KERNEL);
+ if (threadio == NULL)
+ return -ENOMEM;
+ threadio->fnc = fnc;
+ threadio->priv = priv;
+
+ ret = __vb2_init_fileio(q, !q->is_output);
+ dprintk(q, 3, "file io: vb2_init_fileio result: %d\n", ret);
+ if (ret)
+ goto nomem;
+ q->threadio = threadio;
+ threadio->thread = kthread_run(vb2_thread, q, "vb2-%s", thread_name);
+ if (IS_ERR(threadio->thread)) {
+ ret = PTR_ERR(threadio->thread);
+ threadio->thread = NULL;
+ goto nothread;
+ }
+ return 0;
+
+nothread:
+ __vb2_cleanup_fileio(q);
+nomem:
+ kfree(threadio);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_thread_start);
+
+int vb2_thread_stop(struct vb2_queue *q)
+{
+ struct vb2_threadio_data *threadio = q->threadio;
+ int err;
+
+ if (threadio == NULL)
+ return 0;
+ threadio->stop = true;
+ /* Wake up all pending sleeps in the thread */
+ vb2_queue_error(q);
+ err = kthread_stop(threadio->thread);
+ __vb2_cleanup_fileio(q);
+ threadio->thread = NULL;
+ kfree(threadio);
+ q->threadio = NULL;
+ return err;
+}
+EXPORT_SYMBOL_GPL(vb2_thread_stop);
+
+MODULE_DESCRIPTION("Media buffer core framework");
+MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("DMA_BUF");
diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c
new file mode 100644
index 000000000000..7123c5fae92c
--- /dev/null
+++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c
@@ -0,0 +1,866 @@
+/*
+ * videobuf2-dma-contig.c - DMA contig memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/module.h>
+#include <linux/refcount.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/highmem.h>
+
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-memops.h>
+
+struct vb2_dc_buf {
+ struct device *dev;
+ void *vaddr;
+ unsigned long size;
+ void *cookie;
+ dma_addr_t dma_addr;
+ unsigned long attrs;
+ enum dma_data_direction dma_dir;
+ struct sg_table *dma_sgt;
+ struct frame_vector *vec;
+
+ /* MMAP related */
+ struct vb2_vmarea_handler handler;
+ refcount_t refcount;
+ struct sg_table *sgt_base;
+
+ /* DMABUF related */
+ struct dma_buf_attachment *db_attach;
+
+ struct vb2_buffer *vb;
+ bool non_coherent_mem;
+};
+
+/*********************************************/
+/* scatterlist table functions */
+/*********************************************/
+
+static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt)
+{
+ struct scatterlist *s;
+ dma_addr_t expected = sg_dma_address(sgt->sgl);
+ unsigned int i;
+ unsigned long size = 0;
+
+ for_each_sgtable_dma_sg(sgt, s, i) {
+ if (sg_dma_address(s) != expected)
+ break;
+ expected += sg_dma_len(s);
+ size += sg_dma_len(s);
+ }
+ return size;
+}
+
+/*********************************************/
+/* callbacks for all buffers */
+/*********************************************/
+
+static void *vb2_dc_cookie(struct vb2_buffer *vb, void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ return &buf->dma_addr;
+}
+
+/*
+ * This function may fail if:
+ *
+ * - dma_buf_vmap() fails
+ * E.g. due to lack of virtual mapping address space, or due to
+ * dmabuf->ops misconfiguration.
+ *
+ * - dma_vmap_noncontiguous() fails
+ * For instance, when requested buffer size is larger than totalram_pages().
+ * Relevant for buffers that use non-coherent memory.
+ *
+ * - Queue DMA attrs have DMA_ATTR_NO_KERNEL_MAPPING set
+ * Relevant for buffers that use coherent memory.
+ */
+static void *vb2_dc_vaddr(struct vb2_buffer *vb, void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ if (buf->vaddr)
+ return buf->vaddr;
+
+ if (buf->db_attach) {
+ struct iosys_map map;
+
+ if (!dma_buf_vmap_unlocked(buf->db_attach->dmabuf, &map))
+ buf->vaddr = map.vaddr;
+
+ return buf->vaddr;
+ }
+
+ if (buf->non_coherent_mem)
+ buf->vaddr = dma_vmap_noncontiguous(buf->dev, buf->size,
+ buf->dma_sgt);
+ return buf->vaddr;
+}
+
+static unsigned int vb2_dc_num_users(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ return refcount_read(&buf->refcount);
+}
+
+static void vb2_dc_prepare(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+ struct sg_table *sgt = buf->dma_sgt;
+
+ /* This takes care of DMABUF and user-enforced cache sync hint */
+ if (buf->vb->skip_cache_sync_on_prepare)
+ return;
+
+ if (!buf->non_coherent_mem)
+ return;
+
+ /* Non-coherent MMAP only */
+ if (buf->vaddr)
+ flush_kernel_vmap_range(buf->vaddr, buf->size);
+
+ /* For both USERPTR and non-coherent MMAP */
+ dma_sync_sgtable_for_device(buf->dev, sgt, buf->dma_dir);
+}
+
+static void vb2_dc_finish(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+ struct sg_table *sgt = buf->dma_sgt;
+
+ /* This takes care of DMABUF and user-enforced cache sync hint */
+ if (buf->vb->skip_cache_sync_on_finish)
+ return;
+
+ if (!buf->non_coherent_mem)
+ return;
+
+ /* Non-coherent MMAP only */
+ if (buf->vaddr)
+ invalidate_kernel_vmap_range(buf->vaddr, buf->size);
+
+ /* For both USERPTR and non-coherent MMAP */
+ dma_sync_sgtable_for_cpu(buf->dev, sgt, buf->dma_dir);
+}
+
+/*********************************************/
+/* callbacks for MMAP buffers */
+/*********************************************/
+
+static void vb2_dc_put(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ if (!refcount_dec_and_test(&buf->refcount))
+ return;
+
+ if (buf->non_coherent_mem) {
+ if (buf->vaddr)
+ dma_vunmap_noncontiguous(buf->dev, buf->vaddr);
+ dma_free_noncontiguous(buf->dev, buf->size,
+ buf->dma_sgt, buf->dma_dir);
+ } else {
+ if (buf->sgt_base) {
+ sg_free_table(buf->sgt_base);
+ kfree(buf->sgt_base);
+ }
+ dma_free_attrs(buf->dev, buf->size, buf->cookie,
+ buf->dma_addr, buf->attrs);
+ }
+ put_device(buf->dev);
+ kfree(buf);
+}
+
+static int vb2_dc_alloc_coherent(struct vb2_dc_buf *buf)
+{
+ struct vb2_queue *q = buf->vb->vb2_queue;
+
+ buf->cookie = dma_alloc_attrs(buf->dev,
+ buf->size,
+ &buf->dma_addr,
+ GFP_KERNEL | q->gfp_flags,
+ buf->attrs);
+ if (!buf->cookie)
+ return -ENOMEM;
+
+ if (q->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING)
+ return 0;
+
+ buf->vaddr = buf->cookie;
+ return 0;
+}
+
+static int vb2_dc_alloc_non_coherent(struct vb2_dc_buf *buf)
+{
+ struct vb2_queue *q = buf->vb->vb2_queue;
+
+ buf->dma_sgt = dma_alloc_noncontiguous(buf->dev,
+ buf->size,
+ buf->dma_dir,
+ GFP_KERNEL | q->gfp_flags,
+ buf->attrs);
+ if (!buf->dma_sgt)
+ return -ENOMEM;
+
+ buf->dma_addr = sg_dma_address(buf->dma_sgt->sgl);
+
+ /*
+ * For non-coherent buffers the kernel mapping is created on demand
+ * in vb2_dc_vaddr().
+ */
+ return 0;
+}
+
+static void *vb2_dc_alloc(struct vb2_buffer *vb,
+ struct device *dev,
+ unsigned long size)
+{
+ struct vb2_dc_buf *buf;
+ int ret;
+
+ if (WARN_ON(!dev))
+ return ERR_PTR(-EINVAL);
+
+ buf = kzalloc(sizeof *buf, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->attrs = vb->vb2_queue->dma_attrs;
+ buf->dma_dir = vb->vb2_queue->dma_dir;
+ buf->vb = vb;
+ buf->non_coherent_mem = vb->vb2_queue->non_coherent_mem;
+
+ buf->size = size;
+ /* Prevent the device from being released while the buffer is used */
+ buf->dev = get_device(dev);
+
+ if (buf->non_coherent_mem)
+ ret = vb2_dc_alloc_non_coherent(buf);
+ else
+ ret = vb2_dc_alloc_coherent(buf);
+
+ if (ret) {
+ dev_err(dev, "dma alloc of size %lu failed\n", size);
+ put_device(buf->dev);
+ kfree(buf);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ buf->handler.refcount = &buf->refcount;
+ buf->handler.put = vb2_dc_put;
+ buf->handler.arg = buf;
+
+ refcount_set(&buf->refcount, 1);
+
+ return buf;
+}
+
+static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+ int ret;
+
+ if (!buf) {
+ printk(KERN_ERR "No buffer to map\n");
+ return -EINVAL;
+ }
+
+ if (buf->non_coherent_mem)
+ ret = dma_mmap_noncontiguous(buf->dev, vma, buf->size,
+ buf->dma_sgt);
+ else
+ ret = dma_mmap_attrs(buf->dev, vma, buf->cookie, buf->dma_addr,
+ buf->size, buf->attrs);
+ if (ret) {
+ pr_err("Remapping memory failed, error: %d\n", ret);
+ return ret;
+ }
+
+ vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
+ vma->vm_private_data = &buf->handler;
+ vma->vm_ops = &vb2_common_vm_ops;
+
+ vma->vm_ops->open(vma);
+
+ pr_debug("%s: mapped dma addr 0x%08lx at 0x%08lx, size %lu\n",
+ __func__, (unsigned long)buf->dma_addr, vma->vm_start,
+ buf->size);
+
+ return 0;
+}
+
+/*********************************************/
+/* DMABUF ops for exporters */
+/*********************************************/
+
+struct vb2_dc_attachment {
+ struct sg_table sgt;
+ enum dma_data_direction dma_dir;
+};
+
+static int vb2_dc_dmabuf_ops_attach(struct dma_buf *dbuf,
+ struct dma_buf_attachment *dbuf_attach)
+{
+ struct vb2_dc_attachment *attach;
+ unsigned int i;
+ struct scatterlist *rd, *wr;
+ struct sg_table *sgt;
+ struct vb2_dc_buf *buf = dbuf->priv;
+ int ret;
+
+ attach = kzalloc(sizeof(*attach), GFP_KERNEL);
+ if (!attach)
+ return -ENOMEM;
+
+ sgt = &attach->sgt;
+ /* Copy the buf->base_sgt scatter list to the attachment, as we can't
+ * map the same scatter list to multiple attachments at the same time.
+ */
+ ret = sg_alloc_table(sgt, buf->sgt_base->orig_nents, GFP_KERNEL);
+ if (ret) {
+ kfree(attach);
+ return -ENOMEM;
+ }
+
+ rd = buf->sgt_base->sgl;
+ wr = sgt->sgl;
+ for (i = 0; i < sgt->orig_nents; ++i) {
+ sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
+ rd = sg_next(rd);
+ wr = sg_next(wr);
+ }
+
+ attach->dma_dir = DMA_NONE;
+ dbuf_attach->priv = attach;
+
+ return 0;
+}
+
+static void vb2_dc_dmabuf_ops_detach(struct dma_buf *dbuf,
+ struct dma_buf_attachment *db_attach)
+{
+ struct vb2_dc_attachment *attach = db_attach->priv;
+ struct sg_table *sgt;
+
+ if (!attach)
+ return;
+
+ sgt = &attach->sgt;
+
+ /* release the scatterlist cache */
+ if (attach->dma_dir != DMA_NONE)
+ /*
+ * Cache sync can be skipped here, as the vb2_dc memory is
+ * allocated from device coherent memory, which means the
+ * memory locations do not require any explicit cache
+ * maintenance prior or after being used by the device.
+ */
+ dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir,
+ DMA_ATTR_SKIP_CPU_SYNC);
+ sg_free_table(sgt);
+ kfree(attach);
+ db_attach->priv = NULL;
+}
+
+static struct sg_table *vb2_dc_dmabuf_ops_map(
+ struct dma_buf_attachment *db_attach, enum dma_data_direction dma_dir)
+{
+ struct vb2_dc_attachment *attach = db_attach->priv;
+ struct sg_table *sgt;
+
+ sgt = &attach->sgt;
+ /* return previously mapped sg table */
+ if (attach->dma_dir == dma_dir)
+ return sgt;
+
+ /* release any previous cache */
+ if (attach->dma_dir != DMA_NONE) {
+ dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir,
+ DMA_ATTR_SKIP_CPU_SYNC);
+ attach->dma_dir = DMA_NONE;
+ }
+
+ /*
+ * mapping to the client with new direction, no cache sync
+ * required see comment in vb2_dc_dmabuf_ops_detach()
+ */
+ if (dma_map_sgtable(db_attach->dev, sgt, dma_dir,
+ DMA_ATTR_SKIP_CPU_SYNC)) {
+ pr_err("failed to map scatterlist\n");
+ return ERR_PTR(-EIO);
+ }
+
+ attach->dma_dir = dma_dir;
+
+ return sgt;
+}
+
+static void vb2_dc_dmabuf_ops_unmap(struct dma_buf_attachment *db_attach,
+ struct sg_table *sgt, enum dma_data_direction dma_dir)
+{
+ /* nothing to be done here */
+}
+
+static void vb2_dc_dmabuf_ops_release(struct dma_buf *dbuf)
+{
+ /* drop reference obtained in vb2_dc_get_dmabuf */
+ vb2_dc_put(dbuf->priv);
+}
+
+static int
+vb2_dc_dmabuf_ops_begin_cpu_access(struct dma_buf *dbuf,
+ enum dma_data_direction direction)
+{
+ return 0;
+}
+
+static int
+vb2_dc_dmabuf_ops_end_cpu_access(struct dma_buf *dbuf,
+ enum dma_data_direction direction)
+{
+ return 0;
+}
+
+static int vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf, struct iosys_map *map)
+{
+ struct vb2_dc_buf *buf;
+ void *vaddr;
+
+ buf = dbuf->priv;
+ vaddr = vb2_dc_vaddr(buf->vb, buf);
+ if (!vaddr)
+ return -EINVAL;
+
+ iosys_map_set_vaddr(map, vaddr);
+
+ return 0;
+}
+
+static int vb2_dc_dmabuf_ops_mmap(struct dma_buf *dbuf,
+ struct vm_area_struct *vma)
+{
+ return vb2_dc_mmap(dbuf->priv, vma);
+}
+
+static const struct dma_buf_ops vb2_dc_dmabuf_ops = {
+ .attach = vb2_dc_dmabuf_ops_attach,
+ .detach = vb2_dc_dmabuf_ops_detach,
+ .map_dma_buf = vb2_dc_dmabuf_ops_map,
+ .unmap_dma_buf = vb2_dc_dmabuf_ops_unmap,
+ .begin_cpu_access = vb2_dc_dmabuf_ops_begin_cpu_access,
+ .end_cpu_access = vb2_dc_dmabuf_ops_end_cpu_access,
+ .vmap = vb2_dc_dmabuf_ops_vmap,
+ .mmap = vb2_dc_dmabuf_ops_mmap,
+ .release = vb2_dc_dmabuf_ops_release,
+};
+
+static struct sg_table *vb2_dc_get_base_sgt(struct vb2_dc_buf *buf)
+{
+ int ret;
+ struct sg_table *sgt;
+
+ if (buf->non_coherent_mem)
+ return buf->dma_sgt;
+
+ sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt) {
+ dev_err(buf->dev, "failed to alloc sg table\n");
+ return NULL;
+ }
+
+ ret = dma_get_sgtable_attrs(buf->dev, sgt, buf->cookie, buf->dma_addr,
+ buf->size, buf->attrs);
+ if (ret < 0) {
+ dev_err(buf->dev, "failed to get scatterlist from DMA API\n");
+ kfree(sgt);
+ return NULL;
+ }
+
+ return sgt;
+}
+
+static struct dma_buf *vb2_dc_get_dmabuf(struct vb2_buffer *vb,
+ void *buf_priv,
+ unsigned long flags)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+ struct dma_buf *dbuf;
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+ exp_info.ops = &vb2_dc_dmabuf_ops;
+ exp_info.size = buf->size;
+ exp_info.flags = flags;
+ exp_info.priv = buf;
+
+ if (!buf->sgt_base)
+ buf->sgt_base = vb2_dc_get_base_sgt(buf);
+
+ if (WARN_ON(!buf->sgt_base))
+ return NULL;
+
+ dbuf = dma_buf_export(&exp_info);
+ if (IS_ERR(dbuf))
+ return NULL;
+
+ /* dmabuf keeps reference to vb2 buffer */
+ refcount_inc(&buf->refcount);
+
+ return dbuf;
+}
+
+/*********************************************/
+/* callbacks for USERPTR buffers */
+/*********************************************/
+
+static void vb2_dc_put_userptr(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+ struct sg_table *sgt = buf->dma_sgt;
+ int i;
+ struct page **pages;
+
+ if (sgt) {
+ /*
+ * No need to sync to CPU, it's already synced to the CPU
+ * since the finish() memop will have been called before this.
+ */
+ dma_unmap_sgtable(buf->dev, sgt, buf->dma_dir,
+ DMA_ATTR_SKIP_CPU_SYNC);
+ if (buf->dma_dir == DMA_FROM_DEVICE ||
+ buf->dma_dir == DMA_BIDIRECTIONAL) {
+ pages = frame_vector_pages(buf->vec);
+ /* sgt should exist only if vector contains pages... */
+ if (!WARN_ON_ONCE(IS_ERR(pages)))
+ for (i = 0; i < frame_vector_count(buf->vec); i++)
+ set_page_dirty_lock(pages[i]);
+ }
+ sg_free_table(sgt);
+ kfree(sgt);
+ } else {
+ dma_unmap_resource(buf->dev, buf->dma_addr, buf->size,
+ buf->dma_dir, 0);
+ }
+ vb2_destroy_framevec(buf->vec);
+ kfree(buf);
+}
+
+static void *vb2_dc_get_userptr(struct vb2_buffer *vb, struct device *dev,
+ unsigned long vaddr, unsigned long size)
+{
+ struct vb2_dc_buf *buf;
+ struct frame_vector *vec;
+ unsigned int offset;
+ int n_pages, i;
+ int ret = 0;
+ struct sg_table *sgt;
+ unsigned long contig_size;
+ unsigned long dma_align = dma_get_cache_alignment();
+
+ /* Only cache aligned DMA transfers are reliable */
+ if (!IS_ALIGNED(vaddr | size, dma_align)) {
+ pr_debug("user data must be aligned to %lu bytes\n", dma_align);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!size) {
+ pr_debug("size is zero\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (WARN_ON(!dev))
+ return ERR_PTR(-EINVAL);
+
+ buf = kzalloc(sizeof *buf, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->dev = dev;
+ buf->dma_dir = vb->vb2_queue->dma_dir;
+ buf->vb = vb;
+
+ offset = lower_32_bits(offset_in_page(vaddr));
+ vec = vb2_create_framevec(vaddr, size, buf->dma_dir == DMA_FROM_DEVICE ||
+ buf->dma_dir == DMA_BIDIRECTIONAL);
+ if (IS_ERR(vec)) {
+ ret = PTR_ERR(vec);
+ goto fail_buf;
+ }
+ buf->vec = vec;
+ n_pages = frame_vector_count(vec);
+ ret = frame_vector_to_pages(vec);
+ if (ret < 0) {
+ unsigned long *nums = frame_vector_pfns(vec);
+
+ /*
+ * Failed to convert to pages... Check the memory is physically
+ * contiguous and use direct mapping
+ */
+ for (i = 1; i < n_pages; i++)
+ if (nums[i-1] + 1 != nums[i])
+ goto fail_pfnvec;
+ buf->dma_addr = dma_map_resource(buf->dev,
+ __pfn_to_phys(nums[0]), size, buf->dma_dir, 0);
+ if (dma_mapping_error(buf->dev, buf->dma_addr)) {
+ ret = -ENOMEM;
+ goto fail_pfnvec;
+ }
+ goto out;
+ }
+
+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt) {
+ pr_err("failed to allocate sg table\n");
+ ret = -ENOMEM;
+ goto fail_pfnvec;
+ }
+
+ ret = sg_alloc_table_from_pages(sgt, frame_vector_pages(vec), n_pages,
+ offset, size, GFP_KERNEL);
+ if (ret) {
+ pr_err("failed to initialize sg table\n");
+ goto fail_sgt;
+ }
+
+ /*
+ * No need to sync to the device, this will happen later when the
+ * prepare() memop is called.
+ */
+ if (dma_map_sgtable(buf->dev, sgt, buf->dma_dir,
+ DMA_ATTR_SKIP_CPU_SYNC)) {
+ pr_err("failed to map scatterlist\n");
+ ret = -EIO;
+ goto fail_sgt_init;
+ }
+
+ contig_size = vb2_dc_get_contiguous_size(sgt);
+ if (contig_size < size) {
+ pr_err("contiguous mapping is too small %lu/%lu\n",
+ contig_size, size);
+ ret = -EFAULT;
+ goto fail_map_sg;
+ }
+
+ buf->dma_addr = sg_dma_address(sgt->sgl);
+ buf->dma_sgt = sgt;
+ buf->non_coherent_mem = 1;
+
+out:
+ buf->size = size;
+
+ return buf;
+
+fail_map_sg:
+ dma_unmap_sgtable(buf->dev, sgt, buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC);
+
+fail_sgt_init:
+ sg_free_table(sgt);
+
+fail_sgt:
+ kfree(sgt);
+
+fail_pfnvec:
+ vb2_destroy_framevec(vec);
+
+fail_buf:
+ kfree(buf);
+
+ return ERR_PTR(ret);
+}
+
+/*********************************************/
+/* callbacks for DMABUF buffers */
+/*********************************************/
+
+static int vb2_dc_map_dmabuf(void *mem_priv)
+{
+ struct vb2_dc_buf *buf = mem_priv;
+ struct sg_table *sgt;
+ unsigned long contig_size;
+
+ if (WARN_ON(!buf->db_attach)) {
+ pr_err("trying to pin a non attached buffer\n");
+ return -EINVAL;
+ }
+
+ if (WARN_ON(buf->dma_sgt)) {
+ pr_err("dmabuf buffer is already pinned\n");
+ return 0;
+ }
+
+ /* get the associated scatterlist for this buffer */
+ sgt = dma_buf_map_attachment_unlocked(buf->db_attach, buf->dma_dir);
+ if (IS_ERR(sgt)) {
+ pr_err("Error getting dmabuf scatterlist\n");
+ return -EINVAL;
+ }
+
+ /* checking if dmabuf is big enough to store contiguous chunk */
+ contig_size = vb2_dc_get_contiguous_size(sgt);
+ if (contig_size < buf->size) {
+ pr_err("contiguous chunk is too small %lu/%lu\n",
+ contig_size, buf->size);
+ dma_buf_unmap_attachment_unlocked(buf->db_attach, sgt,
+ buf->dma_dir);
+ return -EFAULT;
+ }
+
+ buf->dma_addr = sg_dma_address(sgt->sgl);
+ buf->dma_sgt = sgt;
+ buf->vaddr = NULL;
+
+ return 0;
+}
+
+static void vb2_dc_unmap_dmabuf(void *mem_priv)
+{
+ struct vb2_dc_buf *buf = mem_priv;
+ struct sg_table *sgt = buf->dma_sgt;
+ struct iosys_map map = IOSYS_MAP_INIT_VADDR(buf->vaddr);
+
+ if (WARN_ON(!buf->db_attach)) {
+ pr_err("trying to unpin a not attached buffer\n");
+ return;
+ }
+
+ if (WARN_ON(!sgt)) {
+ pr_err("dmabuf buffer is already unpinned\n");
+ return;
+ }
+
+ if (buf->vaddr) {
+ dma_buf_vunmap_unlocked(buf->db_attach->dmabuf, &map);
+ buf->vaddr = NULL;
+ }
+ dma_buf_unmap_attachment_unlocked(buf->db_attach, sgt, buf->dma_dir);
+
+ buf->dma_addr = 0;
+ buf->dma_sgt = NULL;
+}
+
+static void vb2_dc_detach_dmabuf(void *mem_priv)
+{
+ struct vb2_dc_buf *buf = mem_priv;
+
+ /* if vb2 works correctly you should never detach mapped buffer */
+ if (WARN_ON(buf->dma_addr))
+ vb2_dc_unmap_dmabuf(buf);
+
+ /* detach this attachment */
+ dma_buf_detach(buf->db_attach->dmabuf, buf->db_attach);
+ kfree(buf);
+}
+
+static void *vb2_dc_attach_dmabuf(struct vb2_buffer *vb, struct device *dev,
+ struct dma_buf *dbuf, unsigned long size)
+{
+ struct vb2_dc_buf *buf;
+ struct dma_buf_attachment *dba;
+
+ if (dbuf->size < size)
+ return ERR_PTR(-EFAULT);
+
+ if (WARN_ON(!dev))
+ return ERR_PTR(-EINVAL);
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->dev = dev;
+ buf->vb = vb;
+
+ /* create attachment for the dmabuf with the user device */
+ dba = dma_buf_attach(dbuf, buf->dev);
+ if (IS_ERR(dba)) {
+ pr_err("failed to attach dmabuf\n");
+ kfree(buf);
+ return dba;
+ }
+
+ buf->dma_dir = vb->vb2_queue->dma_dir;
+ buf->size = size;
+ buf->db_attach = dba;
+
+ return buf;
+}
+
+/*********************************************/
+/* DMA CONTIG exported functions */
+/*********************************************/
+
+const struct vb2_mem_ops vb2_dma_contig_memops = {
+ .alloc = vb2_dc_alloc,
+ .put = vb2_dc_put,
+ .get_dmabuf = vb2_dc_get_dmabuf,
+ .cookie = vb2_dc_cookie,
+ .vaddr = vb2_dc_vaddr,
+ .mmap = vb2_dc_mmap,
+ .get_userptr = vb2_dc_get_userptr,
+ .put_userptr = vb2_dc_put_userptr,
+ .prepare = vb2_dc_prepare,
+ .finish = vb2_dc_finish,
+ .map_dmabuf = vb2_dc_map_dmabuf,
+ .unmap_dmabuf = vb2_dc_unmap_dmabuf,
+ .attach_dmabuf = vb2_dc_attach_dmabuf,
+ .detach_dmabuf = vb2_dc_detach_dmabuf,
+ .num_users = vb2_dc_num_users,
+};
+EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
+
+/**
+ * vb2_dma_contig_set_max_seg_size() - configure DMA max segment size
+ * @dev: device for configuring DMA parameters
+ * @size: size of DMA max segment size to set
+ *
+ * To allow mapping the scatter-list into a single chunk in the DMA
+ * address space, the device is required to have the DMA max segment
+ * size parameter set to a value larger than the buffer size. Otherwise,
+ * the DMA-mapping subsystem will split the mapping into max segment
+ * size chunks. This function sets the DMA max segment size
+ * parameter to let DMA-mapping map a buffer as a single chunk in DMA
+ * address space.
+ * This code assumes that the DMA-mapping subsystem will merge all
+ * scatterlist segments if this is really possible (for example when
+ * an IOMMU is available and enabled).
+ * Ideally, this parameter should be set by the generic bus code, but it
+ * is left with the default 64KiB value due to historical litmiations in
+ * other subsystems (like limited USB host drivers) and there no good
+ * place to set it to the proper value.
+ * This function should be called from the drivers, which are known to
+ * operate on platforms with IOMMU and provide access to shared buffers
+ * (either USERPTR or DMABUF). This should be done before initializing
+ * videobuf2 queue.
+ */
+int vb2_dma_contig_set_max_seg_size(struct device *dev, unsigned int size)
+{
+ if (!dev->dma_parms) {
+ dev_err(dev, "Failed to set max_seg_size: dma_parms is NULL\n");
+ return -ENODEV;
+ }
+ if (dma_get_max_seg_size(dev) < size)
+ dma_set_max_seg_size(dev, size);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_dma_contig_set_max_seg_size);
+
+MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("DMA_BUF");
diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c
new file mode 100644
index 000000000000..b3bf2173c14e
--- /dev/null
+++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c
@@ -0,0 +1,679 @@
+/*
+ * videobuf2-dma-sg.c - dma scatter/gather memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/refcount.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-memops.h>
+#include <media/videobuf2-dma-sg.h>
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define dprintk(level, fmt, arg...) \
+ do { \
+ if (debug >= level) \
+ printk(KERN_DEBUG "vb2-dma-sg: " fmt, ## arg); \
+ } while (0)
+
+struct vb2_dma_sg_buf {
+ struct device *dev;
+ void *vaddr;
+ struct page **pages;
+ struct frame_vector *vec;
+ int offset;
+ enum dma_data_direction dma_dir;
+ struct sg_table sg_table;
+ /*
+ * This will point to sg_table when used with the MMAP or USERPTR
+ * memory model, and to the dma_buf sglist when used with the
+ * DMABUF memory model.
+ */
+ struct sg_table *dma_sgt;
+ size_t size;
+ unsigned int num_pages;
+ refcount_t refcount;
+ struct vb2_vmarea_handler handler;
+
+ struct dma_buf_attachment *db_attach;
+
+ struct vb2_buffer *vb;
+};
+
+static void vb2_dma_sg_put(void *buf_priv);
+
+static int vb2_dma_sg_alloc_compacted(struct vb2_dma_sg_buf *buf,
+ gfp_t gfp_flags)
+{
+ unsigned int last_page = 0;
+ unsigned long size = buf->size;
+
+ while (size > 0) {
+ struct page *pages;
+ int order;
+ int i;
+
+ order = get_order(size);
+ /* Don't over allocate*/
+ if ((PAGE_SIZE << order) > size)
+ order--;
+
+ pages = NULL;
+ while (!pages) {
+ pages = alloc_pages(GFP_KERNEL | __GFP_ZERO |
+ __GFP_NOWARN | gfp_flags, order);
+ if (pages)
+ break;
+
+ if (order == 0) {
+ while (last_page--)
+ __free_page(buf->pages[last_page]);
+ return -ENOMEM;
+ }
+ order--;
+ }
+
+ split_page(pages, order);
+ for (i = 0; i < (1 << order); i++)
+ buf->pages[last_page++] = &pages[i];
+
+ size -= PAGE_SIZE << order;
+ }
+
+ return 0;
+}
+
+static void *vb2_dma_sg_alloc(struct vb2_buffer *vb, struct device *dev,
+ unsigned long size)
+{
+ struct vb2_dma_sg_buf *buf;
+ struct sg_table *sgt;
+ int ret;
+ int num_pages;
+
+ if (WARN_ON(!dev) || WARN_ON(!size))
+ return ERR_PTR(-EINVAL);
+
+ buf = kzalloc(sizeof *buf, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->vaddr = NULL;
+ buf->dma_dir = vb->vb2_queue->dma_dir;
+ buf->offset = 0;
+ buf->size = size;
+ /* size is already page aligned */
+ buf->num_pages = size >> PAGE_SHIFT;
+ buf->dma_sgt = &buf->sg_table;
+
+ /*
+ * NOTE: dma-sg allocates memory using the page allocator directly, so
+ * there is no memory consistency guarantee, hence dma-sg ignores DMA
+ * attributes passed from the upper layer.
+ */
+ buf->pages = kvcalloc(buf->num_pages, sizeof(struct page *), GFP_KERNEL);
+ if (!buf->pages)
+ goto fail_pages_array_alloc;
+
+ ret = vb2_dma_sg_alloc_compacted(buf, vb->vb2_queue->gfp_flags);
+ if (ret)
+ goto fail_pages_alloc;
+
+ ret = sg_alloc_table_from_pages(buf->dma_sgt, buf->pages,
+ buf->num_pages, 0, size, GFP_KERNEL);
+ if (ret)
+ goto fail_table_alloc;
+
+ /* Prevent the device from being released while the buffer is used */
+ buf->dev = get_device(dev);
+
+ sgt = &buf->sg_table;
+ /*
+ * No need to sync to the device, this will happen later when the
+ * prepare() memop is called.
+ */
+ if (dma_map_sgtable(buf->dev, sgt, buf->dma_dir,
+ DMA_ATTR_SKIP_CPU_SYNC))
+ goto fail_map;
+
+ buf->handler.refcount = &buf->refcount;
+ buf->handler.put = vb2_dma_sg_put;
+ buf->handler.arg = buf;
+ buf->vb = vb;
+
+ refcount_set(&buf->refcount, 1);
+
+ dprintk(1, "%s: Allocated buffer of %d pages\n",
+ __func__, buf->num_pages);
+ return buf;
+
+fail_map:
+ put_device(buf->dev);
+ sg_free_table(buf->dma_sgt);
+fail_table_alloc:
+ num_pages = buf->num_pages;
+ while (num_pages--)
+ __free_page(buf->pages[num_pages]);
+fail_pages_alloc:
+ kvfree(buf->pages);
+fail_pages_array_alloc:
+ kfree(buf);
+ return ERR_PTR(-ENOMEM);
+}
+
+static void vb2_dma_sg_put(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ struct sg_table *sgt = &buf->sg_table;
+ int i = buf->num_pages;
+
+ if (refcount_dec_and_test(&buf->refcount)) {
+ dprintk(1, "%s: Freeing buffer of %d pages\n", __func__,
+ buf->num_pages);
+ dma_unmap_sgtable(buf->dev, sgt, buf->dma_dir,
+ DMA_ATTR_SKIP_CPU_SYNC);
+ if (buf->vaddr)
+ vm_unmap_ram(buf->vaddr, buf->num_pages);
+ sg_free_table(buf->dma_sgt);
+ while (--i >= 0)
+ __free_page(buf->pages[i]);
+ kvfree(buf->pages);
+ put_device(buf->dev);
+ kfree(buf);
+ }
+}
+
+static void vb2_dma_sg_prepare(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ struct sg_table *sgt = buf->dma_sgt;
+
+ if (buf->vb->skip_cache_sync_on_prepare)
+ return;
+
+ dma_sync_sgtable_for_device(buf->dev, sgt, buf->dma_dir);
+}
+
+static void vb2_dma_sg_finish(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ struct sg_table *sgt = buf->dma_sgt;
+
+ if (buf->vb->skip_cache_sync_on_finish)
+ return;
+
+ dma_sync_sgtable_for_cpu(buf->dev, sgt, buf->dma_dir);
+}
+
+static void *vb2_dma_sg_get_userptr(struct vb2_buffer *vb, struct device *dev,
+ unsigned long vaddr, unsigned long size)
+{
+ struct vb2_dma_sg_buf *buf;
+ struct sg_table *sgt;
+ struct frame_vector *vec;
+
+ if (WARN_ON(!dev))
+ return ERR_PTR(-EINVAL);
+
+ buf = kzalloc(sizeof *buf, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->vaddr = NULL;
+ buf->dev = dev;
+ buf->dma_dir = vb->vb2_queue->dma_dir;
+ buf->offset = vaddr & ~PAGE_MASK;
+ buf->size = size;
+ buf->dma_sgt = &buf->sg_table;
+ buf->vb = vb;
+ vec = vb2_create_framevec(vaddr, size,
+ buf->dma_dir == DMA_FROM_DEVICE ||
+ buf->dma_dir == DMA_BIDIRECTIONAL);
+ if (IS_ERR(vec))
+ goto userptr_fail_pfnvec;
+ buf->vec = vec;
+
+ buf->pages = frame_vector_pages(vec);
+ if (IS_ERR(buf->pages))
+ goto userptr_fail_sgtable;
+ buf->num_pages = frame_vector_count(vec);
+
+ if (sg_alloc_table_from_pages(buf->dma_sgt, buf->pages,
+ buf->num_pages, buf->offset, size, 0))
+ goto userptr_fail_sgtable;
+
+ sgt = &buf->sg_table;
+ /*
+ * No need to sync to the device, this will happen later when the
+ * prepare() memop is called.
+ */
+ if (dma_map_sgtable(buf->dev, sgt, buf->dma_dir,
+ DMA_ATTR_SKIP_CPU_SYNC))
+ goto userptr_fail_map;
+
+ return buf;
+
+userptr_fail_map:
+ sg_free_table(&buf->sg_table);
+userptr_fail_sgtable:
+ vb2_destroy_framevec(vec);
+userptr_fail_pfnvec:
+ kfree(buf);
+ return ERR_PTR(-ENOMEM);
+}
+
+/*
+ * @put_userptr: inform the allocator that a USERPTR buffer will no longer
+ * be used
+ */
+static void vb2_dma_sg_put_userptr(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ struct sg_table *sgt = &buf->sg_table;
+ int i = buf->num_pages;
+
+ dprintk(1, "%s: Releasing userspace buffer of %d pages\n",
+ __func__, buf->num_pages);
+ dma_unmap_sgtable(buf->dev, sgt, buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC);
+ if (buf->vaddr)
+ vm_unmap_ram(buf->vaddr, buf->num_pages);
+ sg_free_table(buf->dma_sgt);
+ if (buf->dma_dir == DMA_FROM_DEVICE ||
+ buf->dma_dir == DMA_BIDIRECTIONAL)
+ while (--i >= 0)
+ set_page_dirty_lock(buf->pages[i]);
+ vb2_destroy_framevec(buf->vec);
+ kfree(buf);
+}
+
+static void *vb2_dma_sg_vaddr(struct vb2_buffer *vb, void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ struct iosys_map map;
+ int ret;
+
+ BUG_ON(!buf);
+
+ if (!buf->vaddr) {
+ if (buf->db_attach) {
+ ret = dma_buf_vmap_unlocked(buf->db_attach->dmabuf, &map);
+ buf->vaddr = ret ? NULL : map.vaddr;
+ } else {
+ buf->vaddr = vm_map_ram(buf->pages, buf->num_pages, -1);
+ }
+ }
+
+ /* add offset in case userptr is not page-aligned */
+ return buf->vaddr ? buf->vaddr + buf->offset : NULL;
+}
+
+static unsigned int vb2_dma_sg_num_users(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+
+ return refcount_read(&buf->refcount);
+}
+
+static int vb2_dma_sg_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ int err;
+
+ if (!buf) {
+ printk(KERN_ERR "No memory to map\n");
+ return -EINVAL;
+ }
+
+ err = vm_map_pages(vma, buf->pages, buf->num_pages);
+ if (err) {
+ printk(KERN_ERR "Remapping memory, error: %d\n", err);
+ return err;
+ }
+
+ /*
+ * Use common vm_area operations to track buffer refcount.
+ */
+ vma->vm_private_data = &buf->handler;
+ vma->vm_ops = &vb2_common_vm_ops;
+
+ vma->vm_ops->open(vma);
+
+ return 0;
+}
+
+/*********************************************/
+/* DMABUF ops for exporters */
+/*********************************************/
+
+struct vb2_dma_sg_attachment {
+ struct sg_table sgt;
+ enum dma_data_direction dma_dir;
+};
+
+static int vb2_dma_sg_dmabuf_ops_attach(struct dma_buf *dbuf,
+ struct dma_buf_attachment *dbuf_attach)
+{
+ struct vb2_dma_sg_attachment *attach;
+ unsigned int i;
+ struct scatterlist *rd, *wr;
+ struct sg_table *sgt;
+ struct vb2_dma_sg_buf *buf = dbuf->priv;
+ int ret;
+
+ attach = kzalloc(sizeof(*attach), GFP_KERNEL);
+ if (!attach)
+ return -ENOMEM;
+
+ sgt = &attach->sgt;
+ /* Copy the buf->base_sgt scatter list to the attachment, as we can't
+ * map the same scatter list to multiple attachments at the same time.
+ */
+ ret = sg_alloc_table(sgt, buf->dma_sgt->orig_nents, GFP_KERNEL);
+ if (ret) {
+ kfree(attach);
+ return -ENOMEM;
+ }
+
+ rd = buf->dma_sgt->sgl;
+ wr = sgt->sgl;
+ for (i = 0; i < sgt->orig_nents; ++i) {
+ sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
+ rd = sg_next(rd);
+ wr = sg_next(wr);
+ }
+
+ attach->dma_dir = DMA_NONE;
+ dbuf_attach->priv = attach;
+
+ return 0;
+}
+
+static void vb2_dma_sg_dmabuf_ops_detach(struct dma_buf *dbuf,
+ struct dma_buf_attachment *db_attach)
+{
+ struct vb2_dma_sg_attachment *attach = db_attach->priv;
+ struct sg_table *sgt;
+
+ if (!attach)
+ return;
+
+ sgt = &attach->sgt;
+
+ /* release the scatterlist cache */
+ if (attach->dma_dir != DMA_NONE)
+ dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir, 0);
+ sg_free_table(sgt);
+ kfree(attach);
+ db_attach->priv = NULL;
+}
+
+static struct sg_table *vb2_dma_sg_dmabuf_ops_map(
+ struct dma_buf_attachment *db_attach, enum dma_data_direction dma_dir)
+{
+ struct vb2_dma_sg_attachment *attach = db_attach->priv;
+ struct sg_table *sgt;
+
+ sgt = &attach->sgt;
+ /* return previously mapped sg table */
+ if (attach->dma_dir == dma_dir)
+ return sgt;
+
+ /* release any previous cache */
+ if (attach->dma_dir != DMA_NONE) {
+ dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir, 0);
+ attach->dma_dir = DMA_NONE;
+ }
+
+ /* mapping to the client with new direction */
+ if (dma_map_sgtable(db_attach->dev, sgt, dma_dir, 0)) {
+ pr_err("failed to map scatterlist\n");
+ return ERR_PTR(-EIO);
+ }
+
+ attach->dma_dir = dma_dir;
+
+ return sgt;
+}
+
+static void vb2_dma_sg_dmabuf_ops_unmap(struct dma_buf_attachment *db_attach,
+ struct sg_table *sgt, enum dma_data_direction dma_dir)
+{
+ /* nothing to be done here */
+}
+
+static void vb2_dma_sg_dmabuf_ops_release(struct dma_buf *dbuf)
+{
+ /* drop reference obtained in vb2_dma_sg_get_dmabuf */
+ vb2_dma_sg_put(dbuf->priv);
+}
+
+static int
+vb2_dma_sg_dmabuf_ops_begin_cpu_access(struct dma_buf *dbuf,
+ enum dma_data_direction direction)
+{
+ struct vb2_dma_sg_buf *buf = dbuf->priv;
+ struct sg_table *sgt = buf->dma_sgt;
+
+ dma_sync_sgtable_for_cpu(buf->dev, sgt, buf->dma_dir);
+ return 0;
+}
+
+static int
+vb2_dma_sg_dmabuf_ops_end_cpu_access(struct dma_buf *dbuf,
+ enum dma_data_direction direction)
+{
+ struct vb2_dma_sg_buf *buf = dbuf->priv;
+ struct sg_table *sgt = buf->dma_sgt;
+
+ dma_sync_sgtable_for_device(buf->dev, sgt, buf->dma_dir);
+ return 0;
+}
+
+static int vb2_dma_sg_dmabuf_ops_vmap(struct dma_buf *dbuf,
+ struct iosys_map *map)
+{
+ struct vb2_dma_sg_buf *buf;
+ void *vaddr;
+
+ buf = dbuf->priv;
+ vaddr = vb2_dma_sg_vaddr(buf->vb, buf);
+ if (!vaddr)
+ return -EINVAL;
+
+ iosys_map_set_vaddr(map, vaddr);
+
+ return 0;
+}
+
+static int vb2_dma_sg_dmabuf_ops_mmap(struct dma_buf *dbuf,
+ struct vm_area_struct *vma)
+{
+ return vb2_dma_sg_mmap(dbuf->priv, vma);
+}
+
+static const struct dma_buf_ops vb2_dma_sg_dmabuf_ops = {
+ .attach = vb2_dma_sg_dmabuf_ops_attach,
+ .detach = vb2_dma_sg_dmabuf_ops_detach,
+ .map_dma_buf = vb2_dma_sg_dmabuf_ops_map,
+ .unmap_dma_buf = vb2_dma_sg_dmabuf_ops_unmap,
+ .begin_cpu_access = vb2_dma_sg_dmabuf_ops_begin_cpu_access,
+ .end_cpu_access = vb2_dma_sg_dmabuf_ops_end_cpu_access,
+ .vmap = vb2_dma_sg_dmabuf_ops_vmap,
+ .mmap = vb2_dma_sg_dmabuf_ops_mmap,
+ .release = vb2_dma_sg_dmabuf_ops_release,
+};
+
+static struct dma_buf *vb2_dma_sg_get_dmabuf(struct vb2_buffer *vb,
+ void *buf_priv,
+ unsigned long flags)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ struct dma_buf *dbuf;
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+ exp_info.ops = &vb2_dma_sg_dmabuf_ops;
+ exp_info.size = buf->size;
+ exp_info.flags = flags;
+ exp_info.priv = buf;
+
+ if (WARN_ON(!buf->dma_sgt))
+ return NULL;
+
+ dbuf = dma_buf_export(&exp_info);
+ if (IS_ERR(dbuf))
+ return NULL;
+
+ /* dmabuf keeps reference to vb2 buffer */
+ refcount_inc(&buf->refcount);
+
+ return dbuf;
+}
+
+/*********************************************/
+/* callbacks for DMABUF buffers */
+/*********************************************/
+
+static int vb2_dma_sg_map_dmabuf(void *mem_priv)
+{
+ struct vb2_dma_sg_buf *buf = mem_priv;
+ struct sg_table *sgt;
+
+ if (WARN_ON(!buf->db_attach)) {
+ pr_err("trying to pin a non attached buffer\n");
+ return -EINVAL;
+ }
+
+ if (WARN_ON(buf->dma_sgt)) {
+ pr_err("dmabuf buffer is already pinned\n");
+ return 0;
+ }
+
+ /* get the associated scatterlist for this buffer */
+ sgt = dma_buf_map_attachment_unlocked(buf->db_attach, buf->dma_dir);
+ if (IS_ERR(sgt)) {
+ pr_err("Error getting dmabuf scatterlist\n");
+ return -EINVAL;
+ }
+
+ buf->dma_sgt = sgt;
+ buf->vaddr = NULL;
+
+ return 0;
+}
+
+static void vb2_dma_sg_unmap_dmabuf(void *mem_priv)
+{
+ struct vb2_dma_sg_buf *buf = mem_priv;
+ struct sg_table *sgt = buf->dma_sgt;
+ struct iosys_map map = IOSYS_MAP_INIT_VADDR(buf->vaddr);
+
+ if (WARN_ON(!buf->db_attach)) {
+ pr_err("trying to unpin a not attached buffer\n");
+ return;
+ }
+
+ if (WARN_ON(!sgt)) {
+ pr_err("dmabuf buffer is already unpinned\n");
+ return;
+ }
+
+ if (buf->vaddr) {
+ dma_buf_vunmap_unlocked(buf->db_attach->dmabuf, &map);
+ buf->vaddr = NULL;
+ }
+ dma_buf_unmap_attachment_unlocked(buf->db_attach, sgt, buf->dma_dir);
+
+ buf->dma_sgt = NULL;
+}
+
+static void vb2_dma_sg_detach_dmabuf(void *mem_priv)
+{
+ struct vb2_dma_sg_buf *buf = mem_priv;
+
+ /* if vb2 works correctly you should never detach mapped buffer */
+ if (WARN_ON(buf->dma_sgt))
+ vb2_dma_sg_unmap_dmabuf(buf);
+
+ /* detach this attachment */
+ dma_buf_detach(buf->db_attach->dmabuf, buf->db_attach);
+ kfree(buf);
+}
+
+static void *vb2_dma_sg_attach_dmabuf(struct vb2_buffer *vb, struct device *dev,
+ struct dma_buf *dbuf, unsigned long size)
+{
+ struct vb2_dma_sg_buf *buf;
+ struct dma_buf_attachment *dba;
+
+ if (WARN_ON(!dev))
+ return ERR_PTR(-EINVAL);
+
+ if (dbuf->size < size)
+ return ERR_PTR(-EFAULT);
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->dev = dev;
+ /* create attachment for the dmabuf with the user device */
+ dba = dma_buf_attach(dbuf, buf->dev);
+ if (IS_ERR(dba)) {
+ pr_err("failed to attach dmabuf\n");
+ kfree(buf);
+ return dba;
+ }
+
+ buf->dma_dir = vb->vb2_queue->dma_dir;
+ buf->size = size;
+ buf->db_attach = dba;
+ buf->vb = vb;
+
+ return buf;
+}
+
+static void *vb2_dma_sg_cookie(struct vb2_buffer *vb, void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+
+ return buf->dma_sgt;
+}
+
+const struct vb2_mem_ops vb2_dma_sg_memops = {
+ .alloc = vb2_dma_sg_alloc,
+ .put = vb2_dma_sg_put,
+ .get_userptr = vb2_dma_sg_get_userptr,
+ .put_userptr = vb2_dma_sg_put_userptr,
+ .prepare = vb2_dma_sg_prepare,
+ .finish = vb2_dma_sg_finish,
+ .vaddr = vb2_dma_sg_vaddr,
+ .mmap = vb2_dma_sg_mmap,
+ .num_users = vb2_dma_sg_num_users,
+ .get_dmabuf = vb2_dma_sg_get_dmabuf,
+ .map_dmabuf = vb2_dma_sg_map_dmabuf,
+ .unmap_dmabuf = vb2_dma_sg_unmap_dmabuf,
+ .attach_dmabuf = vb2_dma_sg_attach_dmabuf,
+ .detach_dmabuf = vb2_dma_sg_detach_dmabuf,
+ .cookie = vb2_dma_sg_cookie,
+};
+EXPORT_SYMBOL_GPL(vb2_dma_sg_memops);
+
+MODULE_DESCRIPTION("dma scatter/gather memory handling routines for videobuf2");
+MODULE_AUTHOR("Andrzej Pietrasiewicz");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("DMA_BUF");
diff --git a/drivers/media/common/videobuf2/videobuf2-dvb.c b/drivers/media/common/videobuf2/videobuf2-dvb.c
new file mode 100644
index 000000000000..3746bf6d1c15
--- /dev/null
+++ b/drivers/media/common/videobuf2/videobuf2-dvb.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * some helper function for simple DVB cards which simply DMA the
+ * complete transport stream and let the computer sort everything else
+ * (i.e. we are using the software demux, ...). Also uses vb2
+ * to manage DMA buffers.
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include <media/videobuf2-dvb.h>
+
+/* ------------------------------------------------------------------ */
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_DESCRIPTION("Videobuf2 helper library for simple DVB cards");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static int dvb_fnc(struct vb2_buffer *vb, void *priv)
+{
+ struct vb2_dvb *dvb = priv;
+
+ dvb_dmx_swfilter(&dvb->demux, vb2_plane_vaddr(vb, 0),
+ vb2_get_plane_payload(vb, 0));
+ return 0;
+}
+
+static int vb2_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct vb2_dvb *dvb = demux->priv;
+ int rc = 0;
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ mutex_lock(&dvb->lock);
+ dvb->nfeeds++;
+
+ if (!dvb->dvbq.threadio) {
+ rc = vb2_thread_start(&dvb->dvbq, dvb_fnc, dvb, dvb->name);
+ if (rc)
+ dvb->nfeeds--;
+ }
+ if (!rc)
+ rc = dvb->nfeeds;
+ mutex_unlock(&dvb->lock);
+ return rc;
+}
+
+static int vb2_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct vb2_dvb *dvb = demux->priv;
+ int err = 0;
+
+ mutex_lock(&dvb->lock);
+ dvb->nfeeds--;
+ if (0 == dvb->nfeeds)
+ err = vb2_thread_stop(&dvb->dvbq);
+ mutex_unlock(&dvb->lock);
+ return err;
+}
+
+static int vb2_dvb_register_adapter(struct vb2_dvb_frontends *fe,
+ struct module *module,
+ void *adapter_priv,
+ struct device *device,
+ struct media_device *mdev,
+ char *adapter_name,
+ short *adapter_nr,
+ int mfe_shared)
+{
+ int result;
+
+ mutex_init(&fe->lock);
+
+ /* register adapter */
+ result = dvb_register_adapter(&fe->adapter, adapter_name, module,
+ device, adapter_nr);
+ if (result < 0) {
+ pr_warn("%s: dvb_register_adapter failed (errno = %d)\n",
+ adapter_name, result);
+ }
+ fe->adapter.priv = adapter_priv;
+ fe->adapter.mfe_shared = mfe_shared;
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ if (mdev)
+ fe->adapter.mdev = mdev;
+#endif
+ return result;
+}
+
+static int vb2_dvb_register_frontend(struct dvb_adapter *adapter,
+ struct vb2_dvb *dvb)
+{
+ int result;
+
+ /* register frontend */
+ result = dvb_register_frontend(adapter, dvb->frontend);
+ if (result < 0) {
+ pr_warn("%s: dvb_register_frontend failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_frontend;
+ }
+
+ /* register demux stuff */
+ dvb->demux.dmx.capabilities =
+ DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+ dvb->demux.priv = dvb;
+ dvb->demux.filternum = 256;
+ dvb->demux.feednum = 256;
+ dvb->demux.start_feed = vb2_dvb_start_feed;
+ dvb->demux.stop_feed = vb2_dvb_stop_feed;
+ result = dvb_dmx_init(&dvb->demux);
+ if (result < 0) {
+ pr_warn("%s: dvb_dmx_init failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_dmx;
+ }
+
+ dvb->dmxdev.filternum = 256;
+ dvb->dmxdev.demux = &dvb->demux.dmx;
+ dvb->dmxdev.capabilities = 0;
+ result = dvb_dmxdev_init(&dvb->dmxdev, adapter);
+
+ if (result < 0) {
+ pr_warn("%s: dvb_dmxdev_init failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_dmxdev;
+ }
+
+ dvb->fe_hw.source = DMX_FRONTEND_0;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ pr_warn("%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
+ dvb->name, result);
+ goto fail_fe_hw;
+ }
+
+ dvb->fe_mem.source = DMX_MEMORY_FE;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ if (result < 0) {
+ pr_warn("%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
+ dvb->name, result);
+ goto fail_fe_mem;
+ }
+
+ result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ pr_warn("%s: connect_frontend failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_fe_conn;
+ }
+
+ /* register network adapter */
+ result = dvb_net_init(adapter, &dvb->net, &dvb->demux.dmx);
+ if (result < 0) {
+ pr_warn("%s: dvb_net_init failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_fe_conn;
+ }
+ return 0;
+
+fail_fe_conn:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+fail_fe_mem:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+fail_fe_hw:
+ dvb_dmxdev_release(&dvb->dmxdev);
+fail_dmxdev:
+ dvb_dmx_release(&dvb->demux);
+fail_dmx:
+ dvb_unregister_frontend(dvb->frontend);
+fail_frontend:
+ dvb_frontend_detach(dvb->frontend);
+ dvb->frontend = NULL;
+
+ return result;
+}
+
+/* ------------------------------------------------------------------ */
+/* Register a single adapter and one or more frontends */
+int vb2_dvb_register_bus(struct vb2_dvb_frontends *f,
+ struct module *module,
+ void *adapter_priv,
+ struct device *device,
+ struct media_device *mdev,
+ short *adapter_nr,
+ int mfe_shared)
+{
+ struct list_head *list, *q;
+ struct vb2_dvb_frontend *fe;
+ int res;
+
+ fe = vb2_dvb_get_frontend(f, 1);
+ if (!fe) {
+ pr_warn("Unable to register the adapter which has no frontends\n");
+ return -EINVAL;
+ }
+
+ /* Bring up the adapter */
+ res = vb2_dvb_register_adapter(f, module, adapter_priv, device, mdev,
+ fe->dvb.name, adapter_nr, mfe_shared);
+ if (res < 0) {
+ pr_warn("vb2_dvb_register_adapter failed (errno = %d)\n", res);
+ return res;
+ }
+
+ /* Attach all of the frontends to the adapter */
+ mutex_lock(&f->lock);
+ list_for_each_safe(list, q, &f->felist) {
+ fe = list_entry(list, struct vb2_dvb_frontend, felist);
+ res = vb2_dvb_register_frontend(&f->adapter, &fe->dvb);
+ if (res < 0) {
+ pr_warn("%s: vb2_dvb_register_frontend failed (errno = %d)\n",
+ fe->dvb.name, res);
+ goto err;
+ }
+ res = dvb_create_media_graph(&f->adapter, false);
+ if (res < 0)
+ goto err;
+ }
+
+ mutex_unlock(&f->lock);
+ return 0;
+
+err:
+ mutex_unlock(&f->lock);
+ vb2_dvb_unregister_bus(f);
+ return res;
+}
+EXPORT_SYMBOL(vb2_dvb_register_bus);
+
+void vb2_dvb_unregister_bus(struct vb2_dvb_frontends *f)
+{
+ vb2_dvb_dealloc_frontends(f);
+
+ dvb_unregister_adapter(&f->adapter);
+}
+EXPORT_SYMBOL(vb2_dvb_unregister_bus);
+
+struct vb2_dvb_frontend *vb2_dvb_get_frontend(
+ struct vb2_dvb_frontends *f, int id)
+{
+ struct list_head *list, *q;
+ struct vb2_dvb_frontend *fe, *ret = NULL;
+
+ mutex_lock(&f->lock);
+
+ list_for_each_safe(list, q, &f->felist) {
+ fe = list_entry(list, struct vb2_dvb_frontend, felist);
+ if (fe->id == id) {
+ ret = fe;
+ break;
+ }
+ }
+
+ mutex_unlock(&f->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(vb2_dvb_get_frontend);
+
+int vb2_dvb_find_frontend(struct vb2_dvb_frontends *f,
+ struct dvb_frontend *p)
+{
+ struct list_head *list, *q;
+ struct vb2_dvb_frontend *fe = NULL;
+ int ret = 0;
+
+ mutex_lock(&f->lock);
+
+ list_for_each_safe(list, q, &f->felist) {
+ fe = list_entry(list, struct vb2_dvb_frontend, felist);
+ if (fe->dvb.frontend == p) {
+ ret = fe->id;
+ break;
+ }
+ }
+
+ mutex_unlock(&f->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(vb2_dvb_find_frontend);
+
+struct vb2_dvb_frontend *vb2_dvb_alloc_frontend(
+ struct vb2_dvb_frontends *f, int id)
+{
+ struct vb2_dvb_frontend *fe;
+
+ fe = kzalloc(sizeof(struct vb2_dvb_frontend), GFP_KERNEL);
+ if (fe == NULL)
+ return NULL;
+
+ fe->id = id;
+ mutex_init(&fe->dvb.lock);
+
+ mutex_lock(&f->lock);
+ list_add_tail(&fe->felist, &f->felist);
+ mutex_unlock(&f->lock);
+ return fe;
+}
+EXPORT_SYMBOL(vb2_dvb_alloc_frontend);
+
+void vb2_dvb_dealloc_frontends(struct vb2_dvb_frontends *f)
+{
+ struct list_head *list, *q;
+ struct vb2_dvb_frontend *fe;
+
+ mutex_lock(&f->lock);
+ list_for_each_safe(list, q, &f->felist) {
+ fe = list_entry(list, struct vb2_dvb_frontend, felist);
+ if (fe->dvb.net.dvbdev) {
+ dvb_net_release(&fe->dvb.net);
+ fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
+ &fe->dvb.fe_mem);
+ fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
+ &fe->dvb.fe_hw);
+ dvb_dmxdev_release(&fe->dvb.dmxdev);
+ dvb_dmx_release(&fe->dvb.demux);
+ dvb_unregister_frontend(fe->dvb.frontend);
+ }
+ if (fe->dvb.frontend)
+ /* always allocated, may have been reset */
+ dvb_frontend_detach(fe->dvb.frontend);
+ list_del(list); /* remove list entry */
+ kfree(fe); /* free frontend allocation */
+ }
+ mutex_unlock(&f->lock);
+}
+EXPORT_SYMBOL(vb2_dvb_dealloc_frontends);
diff --git a/drivers/media/common/videobuf2/videobuf2-memops.c b/drivers/media/common/videobuf2/videobuf2-memops.c
new file mode 100644
index 000000000000..f9a4ec44422e
--- /dev/null
+++ b/drivers/media/common/videobuf2/videobuf2-memops.c
@@ -0,0 +1,131 @@
+/*
+ * videobuf2-memops.c - generic memory handling routines for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.com>
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-memops.h>
+
+/**
+ * vb2_create_framevec() - map virtual addresses to pfns
+ * @start: Virtual user address where we start mapping
+ * @length: Length of a range to map
+ * @write: Should we map for writing into the area
+ *
+ * This function allocates and fills in a vector with pfns corresponding to
+ * virtual address range passed in arguments. If pfns have corresponding pages,
+ * page references are also grabbed to pin pages in memory. The function
+ * returns pointer to the vector on success and error pointer in case of
+ * failure. Returned vector needs to be freed via vb2_destroy_pfnvec().
+ */
+struct frame_vector *vb2_create_framevec(unsigned long start,
+ unsigned long length,
+ bool write)
+{
+ int ret;
+ unsigned long first, last;
+ unsigned long nr;
+ struct frame_vector *vec;
+
+ first = start >> PAGE_SHIFT;
+ last = (start + length - 1) >> PAGE_SHIFT;
+ nr = last - first + 1;
+ vec = frame_vector_create(nr);
+ if (!vec)
+ return ERR_PTR(-ENOMEM);
+ ret = get_vaddr_frames(start & PAGE_MASK, nr, write, vec);
+ if (ret < 0)
+ goto out_destroy;
+ /* We accept only complete set of PFNs */
+ if (ret != nr) {
+ ret = -EFAULT;
+ goto out_release;
+ }
+ return vec;
+out_release:
+ put_vaddr_frames(vec);
+out_destroy:
+ frame_vector_destroy(vec);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(vb2_create_framevec);
+
+/**
+ * vb2_destroy_framevec() - release vector of mapped pfns
+ * @vec: vector of pfns / pages to release
+ *
+ * This releases references to all pages in the vector @vec (if corresponding
+ * pfns are backed by pages) and frees the passed vector.
+ */
+void vb2_destroy_framevec(struct frame_vector *vec)
+{
+ put_vaddr_frames(vec);
+ frame_vector_destroy(vec);
+}
+EXPORT_SYMBOL(vb2_destroy_framevec);
+
+/**
+ * vb2_common_vm_open() - increase refcount of the vma
+ * @vma: virtual memory region for the mapping
+ *
+ * This function adds another user to the provided vma. It expects
+ * struct vb2_vmarea_handler pointer in vma->vm_private_data.
+ */
+static void vb2_common_vm_open(struct vm_area_struct *vma)
+{
+ struct vb2_vmarea_handler *h = vma->vm_private_data;
+
+ pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
+ __func__, h, refcount_read(h->refcount), vma->vm_start,
+ vma->vm_end);
+
+ refcount_inc(h->refcount);
+}
+
+/**
+ * vb2_common_vm_close() - decrease refcount of the vma
+ * @vma: virtual memory region for the mapping
+ *
+ * This function releases the user from the provided vma. It expects
+ * struct vb2_vmarea_handler pointer in vma->vm_private_data.
+ */
+static void vb2_common_vm_close(struct vm_area_struct *vma)
+{
+ struct vb2_vmarea_handler *h = vma->vm_private_data;
+
+ pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
+ __func__, h, refcount_read(h->refcount), vma->vm_start,
+ vma->vm_end);
+
+ h->put(h->arg);
+}
+
+/*
+ * vb2_common_vm_ops - common vm_ops used for tracking refcount of mmapped
+ * video buffers
+ */
+const struct vm_operations_struct vb2_common_vm_ops = {
+ .open = vb2_common_vm_open,
+ .close = vb2_common_vm_close,
+};
+EXPORT_SYMBOL_GPL(vb2_common_vm_ops);
+
+MODULE_DESCRIPTION("common memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
new file mode 100644
index 000000000000..83862d57b126
--- /dev/null
+++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
@@ -0,0 +1,1372 @@
+/*
+ * videobuf2-v4l2.c - V4L2 driver helper framework
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.com>
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * The vb2_thread implementation was based on code from videobuf-dvb.c:
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/freezer.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+
+#include <media/videobuf2-v4l2.h>
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define dprintk(q, level, fmt, arg...) \
+ do { \
+ if (debug >= level) \
+ pr_info("vb2-v4l2: [%p] %s: " fmt, \
+ (q)->name, __func__, ## arg); \
+ } while (0)
+
+/* Flags that are set by us */
+#define V4L2_BUFFER_MASK_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
+ V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \
+ V4L2_BUF_FLAG_PREPARED | \
+ V4L2_BUF_FLAG_IN_REQUEST | \
+ V4L2_BUF_FLAG_REQUEST_FD | \
+ V4L2_BUF_FLAG_TIMESTAMP_MASK)
+/* Output buffer flags that should be passed on to the driver */
+#define V4L2_BUFFER_OUT_FLAGS (V4L2_BUF_FLAG_PFRAME | \
+ V4L2_BUF_FLAG_BFRAME | \
+ V4L2_BUF_FLAG_KEYFRAME | \
+ V4L2_BUF_FLAG_TIMECODE | \
+ V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF)
+
+/*
+ * __verify_planes_array() - verify that the planes array passed in struct
+ * v4l2_buffer from userspace can be safely used
+ */
+static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+ if (!V4L2_TYPE_IS_MULTIPLANAR(b->type))
+ return 0;
+
+ /* Is memory for copying plane information present? */
+ if (b->m.planes == NULL) {
+ dprintk(vb->vb2_queue, 1,
+ "multi-planar buffer passed but planes array not provided\n");
+ return -EINVAL;
+ }
+
+ if (b->length < vb->num_planes || b->length > VB2_MAX_PLANES) {
+ dprintk(vb->vb2_queue, 1,
+ "incorrect planes array length, expected %d, got %d\n",
+ vb->num_planes, b->length);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __verify_planes_array_core(struct vb2_buffer *vb, const void *pb)
+{
+ return __verify_planes_array(vb, pb);
+}
+
+/*
+ * __verify_length() - Verify that the bytesused value for each plane fits in
+ * the plane length and that the data offset doesn't exceed the bytesused value.
+ */
+static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+ unsigned int length;
+ unsigned int bytesused;
+ unsigned int plane;
+
+ if (V4L2_TYPE_IS_CAPTURE(b->type))
+ return 0;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ length = (b->memory == VB2_MEMORY_USERPTR ||
+ b->memory == VB2_MEMORY_DMABUF)
+ ? b->m.planes[plane].length
+ : vb->planes[plane].length;
+ bytesused = b->m.planes[plane].bytesused
+ ? b->m.planes[plane].bytesused : length;
+
+ if (b->m.planes[plane].bytesused > length)
+ return -EINVAL;
+
+ if (b->m.planes[plane].data_offset > 0 &&
+ b->m.planes[plane].data_offset >= bytesused)
+ return -EINVAL;
+ }
+ } else {
+ length = (b->memory == VB2_MEMORY_USERPTR)
+ ? b->length : vb->planes[0].length;
+
+ if (b->bytesused > length)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * __init_vb2_v4l2_buffer() - initialize the vb2_v4l2_buffer struct
+ */
+static void __init_vb2_v4l2_buffer(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ vbuf->request_fd = -1;
+}
+
+static void __copy_timestamp(struct vb2_buffer *vb, const void *pb)
+{
+ const struct v4l2_buffer *b = pb;
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vb2_queue *q = vb->vb2_queue;
+
+ if (q->is_output) {
+ /*
+ * For output buffers copy the timestamp if needed,
+ * and the timecode field and flag if needed.
+ */
+ if (q->copy_timestamp)
+ vb->timestamp = v4l2_buffer_get_timestamp(b);
+ vbuf->flags |= b->flags & V4L2_BUF_FLAG_TIMECODE;
+ if (b->flags & V4L2_BUF_FLAG_TIMECODE)
+ vbuf->timecode = b->timecode;
+ }
+};
+
+static void vb2_warn_zero_bytesused(struct vb2_buffer *vb)
+{
+ static bool check_once;
+
+ if (check_once)
+ return;
+
+ check_once = true;
+
+ pr_warn("use of bytesused == 0 is deprecated and will be removed in the future,\n");
+ if (vb->vb2_queue->allow_zero_bytesused)
+ pr_warn("use VIDIOC_DECODER_CMD(V4L2_DEC_CMD_STOP) instead.\n");
+ else
+ pr_warn("use the actual size instead.\n");
+}
+
+static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vb2_plane *planes = vbuf->planes;
+ unsigned int plane;
+ int ret;
+
+ ret = __verify_length(vb, b);
+ if (ret < 0) {
+ dprintk(q, 1, "plane parameters verification failed: %d\n", ret);
+ return ret;
+ }
+ if (b->field == V4L2_FIELD_ALTERNATE && q->is_output) {
+ /*
+ * If the format's field is ALTERNATE, then the buffer's field
+ * should be either TOP or BOTTOM, not ALTERNATE since that
+ * makes no sense. The driver has to know whether the
+ * buffer represents a top or a bottom field in order to
+ * program any DMA correctly. Using ALTERNATE is wrong, since
+ * that just says that it is either a top or a bottom field,
+ * but not which of the two it is.
+ */
+ dprintk(q, 1, "the field is incorrectly set to ALTERNATE for an output buffer\n");
+ return -EINVAL;
+ }
+ vbuf->sequence = 0;
+ vbuf->request_fd = -1;
+ vbuf->is_held = false;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
+ switch (b->memory) {
+ case VB2_MEMORY_USERPTR:
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ planes[plane].m.userptr =
+ b->m.planes[plane].m.userptr;
+ planes[plane].length =
+ b->m.planes[plane].length;
+ }
+ break;
+ case VB2_MEMORY_DMABUF:
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ planes[plane].m.fd =
+ b->m.planes[plane].m.fd;
+ planes[plane].length =
+ b->m.planes[plane].length;
+ }
+ break;
+ default:
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ planes[plane].m.offset =
+ vb->planes[plane].m.offset;
+ planes[plane].length =
+ vb->planes[plane].length;
+ }
+ break;
+ }
+
+ /* Fill in user-provided information for OUTPUT types */
+ if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+ /*
+ * Will have to go up to b->length when API starts
+ * accepting variable number of planes.
+ *
+ * If bytesused == 0 for the output buffer, then fall
+ * back to the full buffer size. In that case
+ * userspace clearly never bothered to set it and
+ * it's a safe assumption that they really meant to
+ * use the full plane sizes.
+ *
+ * Some drivers, e.g. old codec drivers, use bytesused == 0
+ * as a way to indicate that streaming is finished.
+ * In that case, the driver should use the
+ * allow_zero_bytesused flag to keep old userspace
+ * applications working.
+ */
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ struct vb2_plane *pdst = &planes[plane];
+ struct v4l2_plane *psrc = &b->m.planes[plane];
+
+ if (psrc->bytesused == 0)
+ vb2_warn_zero_bytesused(vb);
+
+ if (vb->vb2_queue->allow_zero_bytesused)
+ pdst->bytesused = psrc->bytesused;
+ else
+ pdst->bytesused = psrc->bytesused ?
+ psrc->bytesused : pdst->length;
+ pdst->data_offset = psrc->data_offset;
+ }
+ }
+ } else {
+ /*
+ * Single-planar buffers do not use planes array,
+ * so fill in relevant v4l2_buffer struct fields instead.
+ * In vb2 we use our internal V4l2_planes struct for
+ * single-planar buffers as well, for simplicity.
+ *
+ * If bytesused == 0 for the output buffer, then fall back
+ * to the full buffer size as that's a sensible default.
+ *
+ * Some drivers, e.g. old codec drivers, use bytesused == 0 as
+ * a way to indicate that streaming is finished. In that case,
+ * the driver should use the allow_zero_bytesused flag to keep
+ * old userspace applications working.
+ */
+ switch (b->memory) {
+ case VB2_MEMORY_USERPTR:
+ planes[0].m.userptr = b->m.userptr;
+ planes[0].length = b->length;
+ break;
+ case VB2_MEMORY_DMABUF:
+ planes[0].m.fd = b->m.fd;
+ planes[0].length = b->length;
+ break;
+ default:
+ planes[0].m.offset = vb->planes[0].m.offset;
+ planes[0].length = vb->planes[0].length;
+ break;
+ }
+
+ planes[0].data_offset = 0;
+ if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+ if (b->bytesused == 0)
+ vb2_warn_zero_bytesused(vb);
+
+ if (vb->vb2_queue->allow_zero_bytesused)
+ planes[0].bytesused = b->bytesused;
+ else
+ planes[0].bytesused = b->bytesused ?
+ b->bytesused : planes[0].length;
+ } else
+ planes[0].bytesused = 0;
+
+ }
+
+ /* Zero flags that we handle */
+ vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS;
+ if (!vb->vb2_queue->copy_timestamp || V4L2_TYPE_IS_CAPTURE(b->type)) {
+ /*
+ * Non-COPY timestamps and non-OUTPUT queues will get
+ * their timestamp and timestamp source flags from the
+ * queue.
+ */
+ vbuf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ }
+
+ if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+ /*
+ * For output buffers mask out the timecode flag:
+ * this will be handled later in vb2_qbuf().
+ * The 'field' is valid metadata for this output buffer
+ * and so that needs to be copied here.
+ */
+ vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE;
+ vbuf->field = b->field;
+ if (!(q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF))
+ vbuf->flags &= ~V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
+ } else {
+ /* Zero any output buffer flags as this is a capture buffer */
+ vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS;
+ /* Zero last flag, this is a signal from driver to userspace */
+ vbuf->flags &= ~V4L2_BUF_FLAG_LAST;
+ }
+
+ return 0;
+}
+
+static void set_buffer_cache_hints(struct vb2_queue *q,
+ struct vb2_buffer *vb,
+ struct v4l2_buffer *b)
+{
+ if (!vb2_queue_allows_cache_hints(q)) {
+ /*
+ * Clear buffer cache flags if queue does not support user
+ * space hints. That's to indicate to userspace that these
+ * flags won't work.
+ */
+ b->flags &= ~V4L2_BUF_FLAG_NO_CACHE_INVALIDATE;
+ b->flags &= ~V4L2_BUF_FLAG_NO_CACHE_CLEAN;
+ return;
+ }
+
+ if (b->flags & V4L2_BUF_FLAG_NO_CACHE_INVALIDATE)
+ vb->skip_cache_sync_on_finish = 1;
+
+ if (b->flags & V4L2_BUF_FLAG_NO_CACHE_CLEAN)
+ vb->skip_cache_sync_on_prepare = 1;
+}
+
+static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *mdev,
+ struct vb2_buffer *vb, struct v4l2_buffer *b,
+ bool is_prepare, struct media_request **p_req)
+{
+ const char *opname = is_prepare ? "prepare_buf" : "qbuf";
+ struct media_request *req;
+ struct vb2_v4l2_buffer *vbuf;
+ int ret;
+
+ if (b->type != q->type) {
+ dprintk(q, 1, "%s: invalid buffer type\n", opname);
+ return -EINVAL;
+ }
+
+ if (b->memory != q->memory) {
+ dprintk(q, 1, "%s: invalid memory type\n", opname);
+ return -EINVAL;
+ }
+
+ vbuf = to_vb2_v4l2_buffer(vb);
+ ret = __verify_planes_array(vb, b);
+ if (ret)
+ return ret;
+
+ if (!is_prepare && (b->flags & V4L2_BUF_FLAG_REQUEST_FD) &&
+ vb->state != VB2_BUF_STATE_DEQUEUED) {
+ dprintk(q, 1, "%s: buffer is not in dequeued state\n", opname);
+ return -EINVAL;
+ }
+
+ if (!vb->prepared) {
+ set_buffer_cache_hints(q, vb, b);
+ /* Copy relevant information provided by the userspace */
+ memset(vbuf->planes, 0,
+ sizeof(vbuf->planes[0]) * vb->num_planes);
+ ret = vb2_fill_vb2_v4l2_buffer(vb, b);
+ if (ret)
+ return ret;
+ }
+
+ if (is_prepare)
+ return 0;
+
+ if (!(b->flags & V4L2_BUF_FLAG_REQUEST_FD)) {
+ if (q->requires_requests) {
+ dprintk(q, 1, "%s: queue requires requests\n", opname);
+ return -EBADR;
+ }
+ if (q->uses_requests) {
+ dprintk(q, 1, "%s: queue uses requests\n", opname);
+ return -EBUSY;
+ }
+ return 0;
+ } else if (!q->supports_requests) {
+ dprintk(q, 1, "%s: queue does not support requests\n", opname);
+ return -EBADR;
+ } else if (q->uses_qbuf) {
+ dprintk(q, 1, "%s: queue does not use requests\n", opname);
+ return -EBUSY;
+ }
+
+ /*
+ * For proper locking when queueing a request you need to be able
+ * to lock access to the vb2 queue, so check that there is a lock
+ * that we can use. In addition p_req must be non-NULL.
+ */
+ if (WARN_ON(!q->lock || !p_req))
+ return -EINVAL;
+
+ /*
+ * Make sure this op is implemented by the driver. It's easy to forget
+ * this callback, but is it important when canceling a buffer in a
+ * queued request.
+ */
+ if (WARN_ON(!q->ops->buf_request_complete))
+ return -EINVAL;
+ /*
+ * Make sure this op is implemented by the driver for the output queue.
+ * It's easy to forget this callback, but is it important to correctly
+ * validate the 'field' value at QBUF time.
+ */
+ if (WARN_ON((q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) &&
+ !q->ops->buf_out_validate))
+ return -EINVAL;
+
+ req = media_request_get_by_fd(mdev, b->request_fd);
+ if (IS_ERR(req)) {
+ dprintk(q, 1, "%s: invalid request_fd\n", opname);
+ return PTR_ERR(req);
+ }
+
+ /*
+ * Early sanity check. This is checked again when the buffer
+ * is bound to the request in vb2_core_qbuf().
+ */
+ if (req->state != MEDIA_REQUEST_STATE_IDLE &&
+ req->state != MEDIA_REQUEST_STATE_UPDATING) {
+ dprintk(q, 1, "%s: request is not idle\n", opname);
+ media_request_put(req);
+ return -EBUSY;
+ }
+
+ *p_req = req;
+ vbuf->request_fd = b->request_fd;
+
+ return 0;
+}
+
+/*
+ * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be
+ * returned to userspace
+ */
+static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
+{
+ struct v4l2_buffer *b = pb;
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vb2_queue *q = vb->vb2_queue;
+ unsigned int plane;
+
+ /* Copy back data such as timestamp, flags, etc. */
+ b->index = vb->index;
+ b->type = vb->type;
+ b->memory = vb->memory;
+ b->bytesused = 0;
+
+ b->flags = vbuf->flags;
+ b->field = vbuf->field;
+ v4l2_buffer_set_timestamp(b, vb->timestamp);
+ b->timecode = vbuf->timecode;
+ b->sequence = vbuf->sequence;
+ b->reserved2 = 0;
+ b->request_fd = 0;
+
+ if (q->is_multiplanar) {
+ /*
+ * Fill in plane-related data if userspace provided an array
+ * for it. The caller has already verified memory and size.
+ */
+ b->length = vb->num_planes;
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ struct v4l2_plane *pdst = &b->m.planes[plane];
+ struct vb2_plane *psrc = &vb->planes[plane];
+
+ pdst->bytesused = psrc->bytesused;
+ pdst->length = psrc->length;
+ if (q->memory == VB2_MEMORY_MMAP)
+ pdst->m.mem_offset = psrc->m.offset;
+ else if (q->memory == VB2_MEMORY_USERPTR)
+ pdst->m.userptr = psrc->m.userptr;
+ else if (q->memory == VB2_MEMORY_DMABUF)
+ pdst->m.fd = psrc->m.fd;
+ pdst->data_offset = psrc->data_offset;
+ memset(pdst->reserved, 0, sizeof(pdst->reserved));
+ }
+ } else {
+ /*
+ * We use length and offset in v4l2_planes array even for
+ * single-planar buffers, but userspace does not.
+ */
+ b->length = vb->planes[0].length;
+ b->bytesused = vb->planes[0].bytesused;
+ if (q->memory == VB2_MEMORY_MMAP)
+ b->m.offset = vb->planes[0].m.offset;
+ else if (q->memory == VB2_MEMORY_USERPTR)
+ b->m.userptr = vb->planes[0].m.userptr;
+ else if (q->memory == VB2_MEMORY_DMABUF)
+ b->m.fd = vb->planes[0].m.fd;
+ }
+
+ /*
+ * Clear any buffer state related flags.
+ */
+ b->flags &= ~V4L2_BUFFER_MASK_FLAGS;
+ b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK;
+ if (!q->copy_timestamp) {
+ /*
+ * For non-COPY timestamps, drop timestamp source bits
+ * and obtain the timestamp source from the queue.
+ */
+ b->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ }
+
+ switch (vb->state) {
+ case VB2_BUF_STATE_QUEUED:
+ case VB2_BUF_STATE_ACTIVE:
+ b->flags |= V4L2_BUF_FLAG_QUEUED;
+ break;
+ case VB2_BUF_STATE_IN_REQUEST:
+ b->flags |= V4L2_BUF_FLAG_IN_REQUEST;
+ break;
+ case VB2_BUF_STATE_ERROR:
+ b->flags |= V4L2_BUF_FLAG_ERROR;
+ fallthrough;
+ case VB2_BUF_STATE_DONE:
+ b->flags |= V4L2_BUF_FLAG_DONE;
+ break;
+ case VB2_BUF_STATE_PREPARING:
+ case VB2_BUF_STATE_DEQUEUED:
+ /* nothing */
+ break;
+ }
+
+ if ((vb->state == VB2_BUF_STATE_DEQUEUED ||
+ vb->state == VB2_BUF_STATE_IN_REQUEST) &&
+ vb->synced && vb->prepared)
+ b->flags |= V4L2_BUF_FLAG_PREPARED;
+
+ if (vb2_buffer_in_use(q, vb))
+ b->flags |= V4L2_BUF_FLAG_MAPPED;
+ if (vbuf->request_fd >= 0) {
+ b->flags |= V4L2_BUF_FLAG_REQUEST_FD;
+ b->request_fd = vbuf->request_fd;
+ }
+}
+
+/*
+ * __fill_vb2_buffer() - fill a vb2_buffer with information provided in a
+ * v4l2_buffer by the userspace. It also verifies that struct
+ * v4l2_buffer has a valid number of planes.
+ */
+static int __fill_vb2_buffer(struct vb2_buffer *vb, struct vb2_plane *planes)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ unsigned int plane;
+
+ if (!vb->vb2_queue->copy_timestamp)
+ vb->timestamp = 0;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ if (vb->vb2_queue->memory != VB2_MEMORY_MMAP) {
+ planes[plane].m = vbuf->planes[plane].m;
+ planes[plane].length = vbuf->planes[plane].length;
+ }
+ planes[plane].bytesused = vbuf->planes[plane].bytesused;
+ planes[plane].data_offset = vbuf->planes[plane].data_offset;
+ }
+ return 0;
+}
+
+static const struct vb2_buf_ops v4l2_buf_ops = {
+ .verify_planes_array = __verify_planes_array_core,
+ .init_buffer = __init_vb2_v4l2_buffer,
+ .fill_user_buffer = __fill_v4l2_buffer,
+ .fill_vb2_buffer = __fill_vb2_buffer,
+ .copy_timestamp = __copy_timestamp,
+};
+
+struct vb2_buffer *vb2_find_buffer(struct vb2_queue *q, u64 timestamp)
+{
+ unsigned int i;
+ struct vb2_buffer *vb2;
+
+ /*
+ * This loop doesn't scale if there is a really large number of buffers.
+ * Maybe something more efficient will be needed in this case.
+ */
+ for (i = 0; i < q->max_num_buffers; i++) {
+ vb2 = vb2_get_buffer(q, i);
+
+ if (!vb2)
+ continue;
+
+ if (vb2->copied_timestamp &&
+ vb2->timestamp == timestamp)
+ return vb2;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(vb2_find_buffer);
+
+/*
+ * vb2_querybuf() - query video buffer information
+ * @q: vb2 queue
+ * @b: buffer struct passed from userspace to vidioc_querybuf handler
+ * in driver
+ *
+ * Should be called from vidioc_querybuf ioctl handler in driver.
+ * This function will verify the passed v4l2_buffer structure and fill the
+ * relevant information for the userspace.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_querybuf handler in driver.
+ */
+int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+ struct vb2_buffer *vb;
+ int ret;
+
+ if (b->type != q->type) {
+ dprintk(q, 1, "wrong buffer type\n");
+ return -EINVAL;
+ }
+
+ vb = vb2_get_buffer(q, b->index);
+ if (!vb) {
+ dprintk(q, 1, "can't find the requested buffer %u\n", b->index);
+ return -EINVAL;
+ }
+
+ ret = __verify_planes_array(vb, b);
+ if (!ret)
+ vb2_core_querybuf(q, vb, b);
+ return ret;
+}
+EXPORT_SYMBOL(vb2_querybuf);
+
+static void vb2_set_flags_and_caps(struct vb2_queue *q, u32 memory,
+ u32 *flags, u32 *caps, u32 *max_num_bufs)
+{
+ if (!q->allow_cache_hints || memory != V4L2_MEMORY_MMAP) {
+ /*
+ * This needs to clear V4L2_MEMORY_FLAG_NON_COHERENT only,
+ * but in order to avoid bugs we zero out all bits.
+ */
+ *flags = 0;
+ } else {
+ /* Clear all unknown flags. */
+ *flags &= V4L2_MEMORY_FLAG_NON_COHERENT;
+ }
+
+ *caps |= V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS;
+ if (q->io_modes & VB2_MMAP)
+ *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP;
+ if (q->io_modes & VB2_USERPTR)
+ *caps |= V4L2_BUF_CAP_SUPPORTS_USERPTR;
+ if (q->io_modes & VB2_DMABUF)
+ *caps |= V4L2_BUF_CAP_SUPPORTS_DMABUF;
+ if (q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF)
+ *caps |= V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF;
+ if (q->allow_cache_hints && q->io_modes & VB2_MMAP)
+ *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS;
+ if (q->supports_requests)
+ *caps |= V4L2_BUF_CAP_SUPPORTS_REQUESTS;
+ if (max_num_bufs) {
+ *max_num_bufs = q->max_num_buffers;
+ *caps |= V4L2_BUF_CAP_SUPPORTS_MAX_NUM_BUFFERS;
+ }
+}
+
+int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
+{
+ int ret = vb2_verify_memory_type(q, req->memory, req->type);
+ u32 flags = req->flags;
+
+ vb2_set_flags_and_caps(q, req->memory, &flags,
+ &req->capabilities, NULL);
+ req->flags = flags;
+ return ret ? ret : vb2_core_reqbufs(q, req->memory,
+ req->flags, &req->count);
+}
+EXPORT_SYMBOL_GPL(vb2_reqbufs);
+
+int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev,
+ struct v4l2_buffer *b)
+{
+ struct vb2_buffer *vb;
+ int ret;
+
+ if (vb2_fileio_is_active(q)) {
+ dprintk(q, 1, "file io in progress\n");
+ return -EBUSY;
+ }
+
+ if (b->flags & V4L2_BUF_FLAG_REQUEST_FD)
+ return -EINVAL;
+
+ vb = vb2_get_buffer(q, b->index);
+ if (!vb) {
+ dprintk(q, 1, "can't find the requested buffer %u\n", b->index);
+ return -EINVAL;
+ }
+
+ ret = vb2_queue_or_prepare_buf(q, mdev, vb, b, true, NULL);
+
+ return ret ? ret : vb2_core_prepare_buf(q, vb, b);
+}
+EXPORT_SYMBOL_GPL(vb2_prepare_buf);
+
+int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
+{
+ unsigned requested_planes = 1;
+ unsigned requested_sizes[VIDEO_MAX_PLANES];
+ struct v4l2_format *f = &create->format;
+ int ret = vb2_verify_memory_type(q, create->memory, f->type);
+ unsigned i;
+
+ create->index = vb2_get_num_buffers(q);
+ vb2_set_flags_and_caps(q, create->memory, &create->flags,
+ &create->capabilities, &create->max_num_buffers);
+ if (create->count == 0)
+ return ret != -EBUSY ? ret : 0;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ requested_planes = f->fmt.pix_mp.num_planes;
+ if (requested_planes == 0 ||
+ requested_planes > VIDEO_MAX_PLANES)
+ return -EINVAL;
+ for (i = 0; i < requested_planes; i++)
+ requested_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ requested_sizes[0] = f->fmt.pix.sizeimage;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ requested_sizes[0] = f->fmt.vbi.samples_per_line *
+ (f->fmt.vbi.count[0] + f->fmt.vbi.count[1]);
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ requested_sizes[0] = f->fmt.sliced.io_size;
+ break;
+ case V4L2_BUF_TYPE_SDR_CAPTURE:
+ case V4L2_BUF_TYPE_SDR_OUTPUT:
+ requested_sizes[0] = f->fmt.sdr.buffersize;
+ break;
+ case V4L2_BUF_TYPE_META_CAPTURE:
+ case V4L2_BUF_TYPE_META_OUTPUT:
+ requested_sizes[0] = f->fmt.meta.buffersize;
+ break;
+ default:
+ return -EINVAL;
+ }
+ for (i = 0; i < requested_planes; i++)
+ if (requested_sizes[i] == 0)
+ return -EINVAL;
+ if (ret)
+ return ret;
+
+ return vb2_core_create_bufs(q, create->memory,
+ create->flags,
+ &create->count,
+ requested_planes,
+ requested_sizes,
+ &create->index);
+}
+EXPORT_SYMBOL_GPL(vb2_create_bufs);
+
+int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev,
+ struct v4l2_buffer *b)
+{
+ struct media_request *req = NULL;
+ struct vb2_buffer *vb;
+ int ret;
+
+ if (vb2_fileio_is_active(q)) {
+ dprintk(q, 1, "file io in progress\n");
+ return -EBUSY;
+ }
+
+ vb = vb2_get_buffer(q, b->index);
+ if (!vb) {
+ dprintk(q, 1, "can't find the requested buffer %u\n", b->index);
+ return -EINVAL;
+ }
+
+ ret = vb2_queue_or_prepare_buf(q, mdev, vb, b, false, &req);
+ if (ret)
+ return ret;
+ ret = vb2_core_qbuf(q, vb, b, req);
+ if (req)
+ media_request_put(req);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_qbuf);
+
+int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
+{
+ int ret;
+
+ if (vb2_fileio_is_active(q)) {
+ dprintk(q, 1, "file io in progress\n");
+ return -EBUSY;
+ }
+
+ if (b->type != q->type) {
+ dprintk(q, 1, "invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ ret = vb2_core_dqbuf(q, NULL, b, nonblocking);
+
+ if (!q->is_output &&
+ b->flags & V4L2_BUF_FLAG_DONE &&
+ b->flags & V4L2_BUF_FLAG_LAST)
+ q->last_buffer_dequeued = true;
+
+ /*
+ * After calling the VIDIOC_DQBUF V4L2_BUF_FLAG_DONE must be
+ * cleared.
+ */
+ b->flags &= ~V4L2_BUF_FLAG_DONE;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_dqbuf);
+
+int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
+{
+ if (vb2_fileio_is_active(q)) {
+ dprintk(q, 1, "file io in progress\n");
+ return -EBUSY;
+ }
+ return vb2_core_streamon(q, type);
+}
+EXPORT_SYMBOL_GPL(vb2_streamon);
+
+int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
+{
+ if (vb2_fileio_is_active(q)) {
+ dprintk(q, 1, "file io in progress\n");
+ return -EBUSY;
+ }
+ return vb2_core_streamoff(q, type);
+}
+EXPORT_SYMBOL_GPL(vb2_streamoff);
+
+int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb)
+{
+ struct vb2_buffer *vb;
+
+ vb = vb2_get_buffer(q, eb->index);
+ if (!vb) {
+ dprintk(q, 1, "can't find the requested buffer %u\n", eb->index);
+ return -EINVAL;
+ }
+
+ return vb2_core_expbuf(q, &eb->fd, eb->type, vb,
+ eb->plane, eb->flags);
+}
+EXPORT_SYMBOL_GPL(vb2_expbuf);
+
+int vb2_queue_init_name(struct vb2_queue *q, const char *name)
+{
+ /* vb2_memory should match with v4l2_memory */
+ BUILD_BUG_ON(VB2_MEMORY_MMAP != (int)V4L2_MEMORY_MMAP);
+ BUILD_BUG_ON(VB2_MEMORY_USERPTR != (int)V4L2_MEMORY_USERPTR);
+ BUILD_BUG_ON(VB2_MEMORY_DMABUF != (int)V4L2_MEMORY_DMABUF);
+
+ /*
+ * Sanity check
+ */
+ if (WARN_ON(!q) ||
+ WARN_ON(q->timestamp_flags &
+ ~(V4L2_BUF_FLAG_TIMESTAMP_MASK |
+ V4L2_BUF_FLAG_TSTAMP_SRC_MASK)))
+ return -EINVAL;
+
+ /* Warn that the driver should choose an appropriate timestamp type */
+ WARN_ON((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) ==
+ V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN);
+
+ if (q->buf_struct_size == 0)
+ q->buf_struct_size = sizeof(struct vb2_v4l2_buffer);
+
+ q->buf_ops = &v4l2_buf_ops;
+ q->is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(q->type);
+ q->is_output = V4L2_TYPE_IS_OUTPUT(q->type);
+ q->copy_timestamp = (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK)
+ == V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ /*
+ * For compatibility with vb1: if QBUF hasn't been called yet, then
+ * return EPOLLERR as well. This only affects capture queues, output
+ * queues will always initialize waiting_for_buffers to false.
+ */
+ q->quirk_poll_must_check_waiting_for_buffers = true;
+
+ if (name)
+ strscpy(q->name, name, sizeof(q->name));
+ else
+ q->name[0] = '\0';
+
+ return vb2_core_queue_init(q);
+}
+EXPORT_SYMBOL_GPL(vb2_queue_init_name);
+
+int vb2_queue_init(struct vb2_queue *q)
+{
+ return vb2_queue_init_name(q, NULL);
+}
+EXPORT_SYMBOL_GPL(vb2_queue_init);
+
+void vb2_queue_release(struct vb2_queue *q)
+{
+ vb2_core_queue_release(q);
+}
+EXPORT_SYMBOL_GPL(vb2_queue_release);
+
+int vb2_queue_change_type(struct vb2_queue *q, unsigned int type)
+{
+ if (type == q->type)
+ return 0;
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ q->type = type;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_queue_change_type);
+
+__poll_t vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
+{
+ struct v4l2_fh *fh = file_to_v4l2_fh(file);
+ __poll_t res;
+
+ res = vb2_core_poll(q, file, wait);
+
+ poll_wait(file, &fh->wait, wait);
+ if (v4l2_event_pending(fh))
+ res |= EPOLLPRI;
+
+ return res;
+}
+EXPORT_SYMBOL_GPL(vb2_poll);
+
+/*
+ * The following functions are not part of the vb2 core API, but are helper
+ * functions that plug into struct v4l2_ioctl_ops, struct v4l2_file_operations
+ * and struct vb2_ops.
+ * They contain boilerplate code that most if not all drivers have to do
+ * and so they simplify the driver code.
+ */
+
+/* vb2 ioctl helpers */
+
+int vb2_ioctl_remove_bufs(struct file *file, void *priv,
+ struct v4l2_remove_buffers *d)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->queue->type != d->type)
+ return -EINVAL;
+
+ if (d->count == 0)
+ return 0;
+
+ if (vb2_queue_is_busy(vdev->queue, file))
+ return -EBUSY;
+
+ if (vb2_fileio_is_active(vdev->queue)) {
+ dprintk(vdev->queue, 1, "file io in progress\n");
+ return -EBUSY;
+ }
+
+ return vb2_core_remove_bufs(vdev->queue, d->index, d->count);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_remove_bufs);
+
+int vb2_ioctl_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct video_device *vdev = video_devdata(file);
+ int res = vb2_verify_memory_type(vdev->queue, p->memory, p->type);
+ u32 flags = p->flags;
+
+ vb2_set_flags_and_caps(vdev->queue, p->memory, &flags,
+ &p->capabilities, NULL);
+ p->flags = flags;
+ if (res)
+ return res;
+ if (vb2_queue_is_busy(vdev->queue, file))
+ return -EBUSY;
+ res = vb2_core_reqbufs(vdev->queue, p->memory, p->flags, &p->count);
+ /* If count == 0, then the owner has released all buffers and he
+ is no longer owner of the queue. Otherwise we have a new owner. */
+ if (res == 0)
+ vdev->queue->owner = p->count ? file->private_data : NULL;
+ return res;
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs);
+
+int vb2_ioctl_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *p)
+{
+ struct video_device *vdev = video_devdata(file);
+ int res = vb2_verify_memory_type(vdev->queue, p->memory, p->format.type);
+
+ p->index = vb2_get_num_buffers(vdev->queue);
+ vb2_set_flags_and_caps(vdev->queue, p->memory, &p->flags,
+ &p->capabilities, &p->max_num_buffers);
+ /*
+ * If count == 0, then just check if memory and type are valid.
+ * Any -EBUSY result from vb2_verify_memory_type can be mapped to 0.
+ */
+ if (p->count == 0)
+ return res != -EBUSY ? res : 0;
+ if (res)
+ return res;
+ if (vb2_queue_is_busy(vdev->queue, file))
+ return -EBUSY;
+
+ res = vb2_create_bufs(vdev->queue, p);
+ if (res == 0)
+ vdev->queue->owner = file->private_data;
+ return res;
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_create_bufs);
+
+int vb2_ioctl_prepare_buf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev->queue, file))
+ return -EBUSY;
+ return vb2_prepare_buf(vdev->queue, vdev->v4l2_dev->mdev, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf);
+
+int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ /* No need to call vb2_queue_is_busy(), anyone can query buffers. */
+ return vb2_querybuf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_querybuf);
+
+int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev->queue, file))
+ return -EBUSY;
+ return vb2_qbuf(vdev->queue, vdev->v4l2_dev->mdev, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf);
+
+int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev->queue, file))
+ return -EBUSY;
+ return vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_dqbuf);
+
+int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev->queue, file))
+ return -EBUSY;
+ return vb2_streamon(vdev->queue, i);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_streamon);
+
+int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev->queue, file))
+ return -EBUSY;
+ return vb2_streamoff(vdev->queue, i);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff);
+
+int vb2_ioctl_expbuf(struct file *file, void *priv, struct v4l2_exportbuffer *p)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev->queue, file))
+ return -EBUSY;
+ return vb2_expbuf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_expbuf);
+
+/* v4l2_file_operations helpers */
+
+int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ return vb2_mmap(vdev->queue, vma);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_mmap);
+
+int _vb2_fop_release(struct file *file, struct mutex *lock)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (lock)
+ mutex_lock(lock);
+ if (!vdev->queue->owner || file->private_data == vdev->queue->owner) {
+ vb2_queue_release(vdev->queue);
+ vdev->queue->owner = NULL;
+ }
+ if (lock)
+ mutex_unlock(lock);
+ return v4l2_fh_release(file);
+}
+EXPORT_SYMBOL_GPL(_vb2_fop_release);
+
+int vb2_fop_release(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
+
+ return _vb2_fop_release(file, lock);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_release);
+
+ssize_t vb2_fop_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
+ int err = -EBUSY;
+
+ if (!(vdev->queue->io_modes & VB2_WRITE))
+ return -EINVAL;
+ if (lock && mutex_lock_interruptible(lock))
+ return -ERESTARTSYS;
+ if (vb2_queue_is_busy(vdev->queue, file))
+ goto exit;
+ err = vb2_write(vdev->queue, buf, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ if (vdev->queue->fileio)
+ vdev->queue->owner = file->private_data;
+exit:
+ if (lock)
+ mutex_unlock(lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_write);
+
+ssize_t vb2_fop_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
+ int err = -EBUSY;
+
+ if (!(vdev->queue->io_modes & VB2_READ))
+ return -EINVAL;
+ if (lock && mutex_lock_interruptible(lock))
+ return -ERESTARTSYS;
+ if (vb2_queue_is_busy(vdev->queue, file))
+ goto exit;
+ vdev->queue->owner = file->private_data;
+ err = vb2_read(vdev->queue, buf, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ if (!vdev->queue->fileio)
+ vdev->queue->owner = NULL;
+exit:
+ if (lock)
+ mutex_unlock(lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_read);
+
+__poll_t vb2_fop_poll(struct file *file, poll_table *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct vb2_queue *q = vdev->queue;
+ struct mutex *lock = q->lock ? q->lock : vdev->lock;
+ __poll_t res;
+ void *fileio;
+
+ /*
+ * If this helper doesn't know how to lock, then you shouldn't be using
+ * it but you should write your own.
+ */
+ WARN_ON(!lock);
+
+ if (lock && mutex_lock_interruptible(lock))
+ return EPOLLERR;
+
+ fileio = q->fileio;
+
+ res = vb2_poll(vdev->queue, file, wait);
+
+ /* If fileio was started, then we have a new queue owner. */
+ if (!fileio && q->fileio)
+ q->owner = file->private_data;
+ if (lock)
+ mutex_unlock(lock);
+ return res;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_poll);
+
+#ifndef CONFIG_MMU
+unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area);
+#endif
+
+void vb2_video_unregister_device(struct video_device *vdev)
+{
+ /* Check if vdev was ever registered at all */
+ if (!vdev || !video_is_registered(vdev))
+ return;
+
+ /*
+ * Calling this function only makes sense if vdev->queue is set.
+ * If it is NULL, then just call video_unregister_device() instead.
+ */
+ WARN_ON(!vdev->queue);
+
+ /*
+ * Take a reference to the device since video_unregister_device()
+ * calls device_unregister(), but we don't want that to release
+ * the device since we want to clean up the queue first.
+ */
+ get_device(&vdev->dev);
+ video_unregister_device(vdev);
+ if (vdev->queue) {
+ struct mutex *lock = vdev->queue->lock ?
+ vdev->queue->lock : vdev->lock;
+
+ if (lock)
+ mutex_lock(lock);
+ vb2_queue_release(vdev->queue);
+ vdev->queue->owner = NULL;
+ if (lock)
+ mutex_unlock(lock);
+ }
+ /*
+ * Now we put the device, and in most cases this will release
+ * everything.
+ */
+ put_device(&vdev->dev);
+}
+EXPORT_SYMBOL_GPL(vb2_video_unregister_device);
+
+/* vb2_ops helpers. Only use if vq->lock is non-NULL. */
+
+void vb2_ops_wait_prepare(struct vb2_queue *vq)
+{
+ mutex_unlock(vq->lock);
+}
+EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare);
+
+void vb2_ops_wait_finish(struct vb2_queue *vq)
+{
+ mutex_lock(vq->lock);
+}
+EXPORT_SYMBOL_GPL(vb2_ops_wait_finish);
+
+/*
+ * Note that this function is called during validation time and
+ * thus the req_queue_mutex is held to ensure no request objects
+ * can be added or deleted while validating. So there is no need
+ * to protect the objects list.
+ */
+int vb2_request_validate(struct media_request *req)
+{
+ struct media_request_object *obj;
+ int ret = 0;
+
+ if (!vb2_request_buffer_cnt(req))
+ return -ENOENT;
+
+ list_for_each_entry(obj, &req->objects, list) {
+ if (!obj->ops->prepare)
+ continue;
+
+ ret = obj->ops->prepare(obj);
+ if (ret)
+ break;
+ }
+
+ if (ret) {
+ list_for_each_entry_continue_reverse(obj, &req->objects, list)
+ if (obj->ops->unprepare)
+ obj->ops->unprepare(obj);
+ return ret;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_request_validate);
+
+void vb2_request_queue(struct media_request *req)
+{
+ struct media_request_object *obj, *obj_safe;
+
+ /*
+ * Queue all objects. Note that buffer objects are at the end of the
+ * objects list, after all other object types. Once buffer objects
+ * are queued, the driver might delete them immediately (if the driver
+ * processes the buffer at once), so we have to use
+ * list_for_each_entry_safe() to handle the case where the object we
+ * queue is deleted.
+ */
+ list_for_each_entry_safe(obj, obj_safe, &req->objects, list)
+ if (obj->ops->queue)
+ obj->ops->queue(obj);
+}
+EXPORT_SYMBOL_GPL(vb2_request_queue);
+
+MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
+MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/videobuf2/videobuf2-vmalloc.c b/drivers/media/common/videobuf2/videobuf2-vmalloc.c
new file mode 100644
index 000000000000..3f777068cd34
--- /dev/null
+++ b/drivers/media/common/videobuf2/videobuf2-vmalloc.c
@@ -0,0 +1,445 @@
+/*
+ * videobuf2-vmalloc.c - vmalloc memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/refcount.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/videobuf2-memops.h>
+
+struct vb2_vmalloc_buf {
+ void *vaddr;
+ struct frame_vector *vec;
+ enum dma_data_direction dma_dir;
+ unsigned long size;
+ refcount_t refcount;
+ struct vb2_vmarea_handler handler;
+ struct dma_buf *dbuf;
+};
+
+static void vb2_vmalloc_put(void *buf_priv);
+
+static void *vb2_vmalloc_alloc(struct vb2_buffer *vb, struct device *dev,
+ unsigned long size)
+{
+ struct vb2_vmalloc_buf *buf;
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL | vb->vb2_queue->gfp_flags);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->size = size;
+ buf->vaddr = vmalloc_user(buf->size);
+ if (!buf->vaddr) {
+ pr_debug("vmalloc of size %ld failed\n", buf->size);
+ kfree(buf);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ buf->dma_dir = vb->vb2_queue->dma_dir;
+ buf->handler.refcount = &buf->refcount;
+ buf->handler.put = vb2_vmalloc_put;
+ buf->handler.arg = buf;
+
+ refcount_set(&buf->refcount, 1);
+ return buf;
+}
+
+static void vb2_vmalloc_put(void *buf_priv)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+
+ if (refcount_dec_and_test(&buf->refcount)) {
+ vfree(buf->vaddr);
+ kfree(buf);
+ }
+}
+
+static void *vb2_vmalloc_get_userptr(struct vb2_buffer *vb, struct device *dev,
+ unsigned long vaddr, unsigned long size)
+{
+ struct vb2_vmalloc_buf *buf;
+ struct frame_vector *vec;
+ int n_pages, offset, i;
+ int ret = -ENOMEM;
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->dma_dir = vb->vb2_queue->dma_dir;
+ offset = vaddr & ~PAGE_MASK;
+ buf->size = size;
+ vec = vb2_create_framevec(vaddr, size,
+ buf->dma_dir == DMA_FROM_DEVICE ||
+ buf->dma_dir == DMA_BIDIRECTIONAL);
+ if (IS_ERR(vec)) {
+ ret = PTR_ERR(vec);
+ goto fail_pfnvec_create;
+ }
+ buf->vec = vec;
+ n_pages = frame_vector_count(vec);
+ if (frame_vector_to_pages(vec) < 0) {
+ unsigned long *nums = frame_vector_pfns(vec);
+
+ /*
+ * We cannot get page pointers for these pfns. Check memory is
+ * physically contiguous and use direct mapping.
+ */
+ for (i = 1; i < n_pages; i++)
+ if (nums[i-1] + 1 != nums[i])
+ goto fail_map;
+ buf->vaddr = (__force void *)
+ ioremap(__pfn_to_phys(nums[0]), size + offset);
+ } else {
+ buf->vaddr = vm_map_ram(frame_vector_pages(vec), n_pages, -1);
+ }
+
+ if (!buf->vaddr)
+ goto fail_map;
+ buf->vaddr += offset;
+ return buf;
+
+fail_map:
+ vb2_destroy_framevec(vec);
+fail_pfnvec_create:
+ kfree(buf);
+
+ return ERR_PTR(ret);
+}
+
+static void vb2_vmalloc_put_userptr(void *buf_priv)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+ unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
+ unsigned int i;
+ struct page **pages;
+ unsigned int n_pages;
+
+ if (!buf->vec->is_pfns) {
+ n_pages = frame_vector_count(buf->vec);
+ if (vaddr)
+ vm_unmap_ram((void *)vaddr, n_pages);
+ if (buf->dma_dir == DMA_FROM_DEVICE ||
+ buf->dma_dir == DMA_BIDIRECTIONAL) {
+ pages = frame_vector_pages(buf->vec);
+ if (!WARN_ON_ONCE(IS_ERR(pages)))
+ for (i = 0; i < n_pages; i++)
+ set_page_dirty_lock(pages[i]);
+ }
+ } else {
+ iounmap((__force void __iomem *)buf->vaddr);
+ }
+ vb2_destroy_framevec(buf->vec);
+ kfree(buf);
+}
+
+static void *vb2_vmalloc_vaddr(struct vb2_buffer *vb, void *buf_priv)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+
+ if (!buf->vaddr) {
+ pr_err("Address of an unallocated plane requested or cannot map user pointer\n");
+ return NULL;
+ }
+
+ return buf->vaddr;
+}
+
+static unsigned int vb2_vmalloc_num_users(void *buf_priv)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+ return refcount_read(&buf->refcount);
+}
+
+static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+ int ret;
+
+ if (!buf) {
+ pr_err("No memory to map\n");
+ return -EINVAL;
+ }
+
+ ret = remap_vmalloc_range(vma, buf->vaddr, 0);
+ if (ret) {
+ pr_err("Remapping vmalloc memory, error: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Make sure that vm_areas for 2 buffers won't be merged together
+ */
+ vm_flags_set(vma, VM_DONTEXPAND);
+
+ /*
+ * Use common vm_area operations to track buffer refcount.
+ */
+ vma->vm_private_data = &buf->handler;
+ vma->vm_ops = &vb2_common_vm_ops;
+
+ vma->vm_ops->open(vma);
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_DMA
+/*********************************************/
+/* DMABUF ops for exporters */
+/*********************************************/
+
+struct vb2_vmalloc_attachment {
+ struct sg_table sgt;
+ enum dma_data_direction dma_dir;
+};
+
+static int vb2_vmalloc_dmabuf_ops_attach(struct dma_buf *dbuf,
+ struct dma_buf_attachment *dbuf_attach)
+{
+ struct vb2_vmalloc_attachment *attach;
+ struct vb2_vmalloc_buf *buf = dbuf->priv;
+ int num_pages = PAGE_ALIGN(buf->size) / PAGE_SIZE;
+ struct sg_table *sgt;
+ struct scatterlist *sg;
+ void *vaddr = buf->vaddr;
+ int ret;
+ int i;
+
+ attach = kzalloc(sizeof(*attach), GFP_KERNEL);
+ if (!attach)
+ return -ENOMEM;
+
+ sgt = &attach->sgt;
+ ret = sg_alloc_table(sgt, num_pages, GFP_KERNEL);
+ if (ret) {
+ kfree(attach);
+ return ret;
+ }
+ for_each_sgtable_sg(sgt, sg, i) {
+ struct page *page = vmalloc_to_page(vaddr);
+
+ if (!page) {
+ sg_free_table(sgt);
+ kfree(attach);
+ return -ENOMEM;
+ }
+ sg_set_page(sg, page, PAGE_SIZE, 0);
+ vaddr += PAGE_SIZE;
+ }
+
+ attach->dma_dir = DMA_NONE;
+ dbuf_attach->priv = attach;
+ return 0;
+}
+
+static void vb2_vmalloc_dmabuf_ops_detach(struct dma_buf *dbuf,
+ struct dma_buf_attachment *db_attach)
+{
+ struct vb2_vmalloc_attachment *attach = db_attach->priv;
+ struct sg_table *sgt;
+
+ if (!attach)
+ return;
+
+ sgt = &attach->sgt;
+
+ /* release the scatterlist cache */
+ if (attach->dma_dir != DMA_NONE)
+ dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir, 0);
+ sg_free_table(sgt);
+ kfree(attach);
+ db_attach->priv = NULL;
+}
+
+static struct sg_table *vb2_vmalloc_dmabuf_ops_map(
+ struct dma_buf_attachment *db_attach, enum dma_data_direction dma_dir)
+{
+ struct vb2_vmalloc_attachment *attach = db_attach->priv;
+ struct sg_table *sgt;
+
+ sgt = &attach->sgt;
+ /* return previously mapped sg table */
+ if (attach->dma_dir == dma_dir)
+ return sgt;
+
+ /* release any previous cache */
+ if (attach->dma_dir != DMA_NONE) {
+ dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir, 0);
+ attach->dma_dir = DMA_NONE;
+ }
+
+ /* mapping to the client with new direction */
+ if (dma_map_sgtable(db_attach->dev, sgt, dma_dir, 0)) {
+ pr_err("failed to map scatterlist\n");
+ return ERR_PTR(-EIO);
+ }
+
+ attach->dma_dir = dma_dir;
+
+ return sgt;
+}
+
+static void vb2_vmalloc_dmabuf_ops_unmap(struct dma_buf_attachment *db_attach,
+ struct sg_table *sgt, enum dma_data_direction dma_dir)
+{
+ /* nothing to be done here */
+}
+
+static void vb2_vmalloc_dmabuf_ops_release(struct dma_buf *dbuf)
+{
+ /* drop reference obtained in vb2_vmalloc_get_dmabuf */
+ vb2_vmalloc_put(dbuf->priv);
+}
+
+static int vb2_vmalloc_dmabuf_ops_vmap(struct dma_buf *dbuf,
+ struct iosys_map *map)
+{
+ struct vb2_vmalloc_buf *buf = dbuf->priv;
+
+ iosys_map_set_vaddr(map, buf->vaddr);
+
+ return 0;
+}
+
+static int vb2_vmalloc_dmabuf_ops_mmap(struct dma_buf *dbuf,
+ struct vm_area_struct *vma)
+{
+ return vb2_vmalloc_mmap(dbuf->priv, vma);
+}
+
+static const struct dma_buf_ops vb2_vmalloc_dmabuf_ops = {
+ .attach = vb2_vmalloc_dmabuf_ops_attach,
+ .detach = vb2_vmalloc_dmabuf_ops_detach,
+ .map_dma_buf = vb2_vmalloc_dmabuf_ops_map,
+ .unmap_dma_buf = vb2_vmalloc_dmabuf_ops_unmap,
+ .vmap = vb2_vmalloc_dmabuf_ops_vmap,
+ .mmap = vb2_vmalloc_dmabuf_ops_mmap,
+ .release = vb2_vmalloc_dmabuf_ops_release,
+};
+
+static struct dma_buf *vb2_vmalloc_get_dmabuf(struct vb2_buffer *vb,
+ void *buf_priv,
+ unsigned long flags)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+ struct dma_buf *dbuf;
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+ exp_info.ops = &vb2_vmalloc_dmabuf_ops;
+ exp_info.size = buf->size;
+ exp_info.flags = flags;
+ exp_info.priv = buf;
+
+ if (WARN_ON(!buf->vaddr))
+ return NULL;
+
+ dbuf = dma_buf_export(&exp_info);
+ if (IS_ERR(dbuf))
+ return NULL;
+
+ /* dmabuf keeps reference to vb2 buffer */
+ refcount_inc(&buf->refcount);
+
+ return dbuf;
+}
+#endif /* CONFIG_HAS_DMA */
+
+
+/*********************************************/
+/* callbacks for DMABUF buffers */
+/*********************************************/
+
+static int vb2_vmalloc_map_dmabuf(void *mem_priv)
+{
+ struct vb2_vmalloc_buf *buf = mem_priv;
+ struct iosys_map map;
+ int ret;
+
+ ret = dma_buf_vmap_unlocked(buf->dbuf, &map);
+ if (ret)
+ return -EFAULT;
+ buf->vaddr = map.vaddr;
+
+ return 0;
+}
+
+static void vb2_vmalloc_unmap_dmabuf(void *mem_priv)
+{
+ struct vb2_vmalloc_buf *buf = mem_priv;
+ struct iosys_map map = IOSYS_MAP_INIT_VADDR(buf->vaddr);
+
+ dma_buf_vunmap_unlocked(buf->dbuf, &map);
+ buf->vaddr = NULL;
+}
+
+static void vb2_vmalloc_detach_dmabuf(void *mem_priv)
+{
+ struct vb2_vmalloc_buf *buf = mem_priv;
+ struct iosys_map map = IOSYS_MAP_INIT_VADDR(buf->vaddr);
+
+ if (buf->vaddr)
+ dma_buf_vunmap_unlocked(buf->dbuf, &map);
+
+ kfree(buf);
+}
+
+static void *vb2_vmalloc_attach_dmabuf(struct vb2_buffer *vb,
+ struct device *dev,
+ struct dma_buf *dbuf,
+ unsigned long size)
+{
+ struct vb2_vmalloc_buf *buf;
+
+ if (dbuf->size < size)
+ return ERR_PTR(-EFAULT);
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->dbuf = dbuf;
+ buf->dma_dir = vb->vb2_queue->dma_dir;
+ buf->size = size;
+
+ return buf;
+}
+
+
+const struct vb2_mem_ops vb2_vmalloc_memops = {
+ .alloc = vb2_vmalloc_alloc,
+ .put = vb2_vmalloc_put,
+ .get_userptr = vb2_vmalloc_get_userptr,
+ .put_userptr = vb2_vmalloc_put_userptr,
+#ifdef CONFIG_HAS_DMA
+ .get_dmabuf = vb2_vmalloc_get_dmabuf,
+#endif
+ .map_dmabuf = vb2_vmalloc_map_dmabuf,
+ .unmap_dmabuf = vb2_vmalloc_unmap_dmabuf,
+ .attach_dmabuf = vb2_vmalloc_attach_dmabuf,
+ .detach_dmabuf = vb2_vmalloc_detach_dmabuf,
+ .vaddr = vb2_vmalloc_vaddr,
+ .mmap = vb2_vmalloc_mmap,
+ .num_users = vb2_vmalloc_num_users,
+};
+EXPORT_SYMBOL_GPL(vb2_vmalloc_memops);
+
+MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("DMA_BUF");
diff --git a/drivers/media/dvb-core/Kconfig b/drivers/media/dvb-core/Kconfig
new file mode 100644
index 000000000000..8b3f2d53cd62
--- /dev/null
+++ b/drivers/media/dvb-core/Kconfig
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# DVB device configuration
+#
+
+config DVB_MMAP
+ bool "Enable DVB memory-mapped API (EXPERIMENTAL)"
+ depends on DVB_CORE
+ depends on VIDEO_DEV=y || VIDEO_DEV=DVB_CORE
+ select VIDEOBUF2_VMALLOC
+ help
+ This option enables DVB experimental memory-mapped API, which
+ reduces the number of context switches to read DVB buffers, as
+ the buffers can use mmap() syscalls.
+
+ Support for it is experimental. Use with care. If unsure,
+ say N.
+
+config DVB_NET
+ bool "DVB Network Support"
+ default (NET && INET)
+ depends on NET && INET && DVB_CORE
+ help
+ This option enables DVB Network Support which is a part of the DVB
+ standard. It is used, for example, by automatic firmware updates used
+ on Set-Top-Boxes. It can also be used to access the Internet via the
+ DVB card, if the network provider supports it.
+
+ You may want to disable the network support on embedded devices. If
+ unsure say Y.
+
+config DVB_MAX_ADAPTERS
+ int "maximum number of DVB/ATSC adapters"
+ depends on DVB_CORE
+ default 16
+ range 1 255
+ help
+ Maximum number of DVB/ATSC adapters. Increasing this number
+ increases the memory consumption of the DVB subsystem even
+ if a much lower number of DVB/ATSC adapters is present.
+ Only values in the range 4-32 are tested.
+
+ If you are unsure about this, use the default value 16
+
+config DVB_DYNAMIC_MINORS
+ bool "Dynamic DVB minor allocation"
+ depends on DVB_CORE
+ default y
+ help
+ If you say Y here, the DVB subsystem will use dynamic minor
+ allocation for any device that uses the DVB major number.
+ This means that you can have more than 4 of a single type
+ of device (like demuxes and frontends) per adapter, but udev
+ will be required to manage the device nodes.
+
+ If you are unsure about this, say N here.
+
+config DVB_DEMUX_SECTION_LOSS_LOG
+ bool "Enable DVB demux section packet loss log"
+ depends on DVB_CORE
+ help
+ Enable extra log messages meant to detect packet loss
+ inside the Kernel.
+
+ Should not be enabled on normal cases, as logs can
+ be very verbose.
+
+ If you are unsure about this, say N here.
+
+config DVB_ULE_DEBUG
+ bool "Enable DVB net ULE packet debug messages"
+ depends on DVB_CORE
+ help
+ Enable extra log messages meant to detect problems while
+ handling DVB network ULE packet loss inside the Kernel.
+
+ Should not be enabled on normal cases, as logs can
+ be very verbose.
+
+ If you are unsure about this, say N here.
diff --git a/drivers/media/dvb-core/Makefile b/drivers/media/dvb-core/Makefile
new file mode 100644
index 000000000000..1cb3ca67bed9
--- /dev/null
+++ b/drivers/media/dvb-core/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the kernel DVB device drivers.
+#
+
+dvb-net-$(CONFIG_DVB_NET) := dvb_net.o
+dvb-vb2-$(CONFIG_DVB_MMAP) := dvb_vb2.o
+
+dvb-core-objs := dvbdev.o dmxdev.o dvb_demux.o \
+ dvb_ca_en50221.o dvb_frontend.o \
+ $(dvb-net-y) dvb_ringbuffer.o $(dvb-vb2-y)
+
+obj-$(CONFIG_DVB_CORE) += dvb-core.o
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
new file mode 100644
index 000000000000..8c6f5aafda1d
--- /dev/null
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -0,0 +1,1478 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * dmxdev.c - DVB demultiplexer device
+ *
+ * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
+ * for convergence integrated media GmbH
+ */
+
+#define pr_fmt(fmt) "dmxdev: " fmt
+
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/ioctl.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <media/dmxdev.h>
+#include <media/dvb_vb2.h>
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
+
+static int dvb_dmxdev_buffer_write(struct dvb_ringbuffer *buf,
+ const u8 *src, size_t len)
+{
+ ssize_t free;
+
+ if (!len)
+ return 0;
+ if (!buf->data)
+ return 0;
+
+ free = dvb_ringbuffer_free(buf);
+ if (len > free) {
+ dprintk("buffer overflow\n");
+ return -EOVERFLOW;
+ }
+
+ return dvb_ringbuffer_write(buf, src, len);
+}
+
+static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,
+ int non_blocking, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ size_t todo;
+ ssize_t avail;
+ ssize_t ret = 0;
+
+ if (!src->data)
+ return 0;
+
+ if (src->error) {
+ ret = src->error;
+ dvb_ringbuffer_flush(src);
+ return ret;
+ }
+
+ for (todo = count; todo > 0; todo -= ret) {
+ if (non_blocking && dvb_ringbuffer_empty(src)) {
+ ret = -EWOULDBLOCK;
+ break;
+ }
+
+ ret = wait_event_interruptible(src->queue,
+ !dvb_ringbuffer_empty(src) ||
+ (src->error != 0));
+ if (ret < 0)
+ break;
+
+ if (src->error) {
+ ret = src->error;
+ dvb_ringbuffer_flush(src);
+ break;
+ }
+
+ avail = dvb_ringbuffer_avail(src);
+ if (avail > todo)
+ avail = todo;
+
+ ret = dvb_ringbuffer_read_user(src, buf, avail);
+ if (ret < 0)
+ break;
+
+ buf += ret;
+ }
+
+ return (count - todo) ? (count - todo) : ret;
+}
+
+static struct dmx_frontend *get_fe(struct dmx_demux *demux, int type)
+{
+ struct list_head *head, *pos;
+
+ head = demux->get_frontends(demux);
+ if (!head)
+ return NULL;
+ list_for_each(pos, head)
+ if (DMX_FE_ENTRY(pos)->source == type)
+ return DMX_FE_ENTRY(pos);
+
+ return NULL;
+}
+
+static int dvb_dvr_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ struct dmx_frontend *front;
+ bool need_ringbuffer = false;
+
+ dprintk("%s\n", __func__);
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if (dmxdev->exit) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENODEV;
+ }
+
+ dmxdev->may_do_mmap = 0;
+
+ /*
+ * The logic here is a little tricky due to the ifdef.
+ *
+ * The ringbuffer is used for both read and mmap.
+ *
+ * It is not needed, however, on two situations:
+ * - Write devices (access with O_WRONLY);
+ * - For duplex device nodes, opened with O_RDWR.
+ */
+
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+ need_ringbuffer = true;
+ else if ((file->f_flags & O_ACCMODE) == O_RDWR) {
+ if (!(dmxdev->capabilities & DMXDEV_CAP_DUPLEX)) {
+#ifdef CONFIG_DVB_MMAP
+ dmxdev->may_do_mmap = 1;
+ need_ringbuffer = true;
+#else
+ mutex_unlock(&dmxdev->mutex);
+ return -EOPNOTSUPP;
+#endif
+ }
+ }
+
+ if (need_ringbuffer) {
+ void *mem;
+
+ if (!dvbdev->readers) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EBUSY;
+ }
+ mem = vmalloc(DVR_BUFFER_SIZE);
+ if (!mem) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENOMEM;
+ }
+ dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
+ if (dmxdev->may_do_mmap)
+ dvb_vb2_init(&dmxdev->dvr_vb2_ctx, "dvr",
+ file->f_flags & O_NONBLOCK);
+ dvbdev->readers--;
+ }
+
+ if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+ dmxdev->dvr_orig_fe = dmxdev->demux->frontend;
+
+ if (!dmxdev->demux->write) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EOPNOTSUPP;
+ }
+
+ front = get_fe(dmxdev->demux, DMX_MEMORY_FE);
+
+ if (!front) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EINVAL;
+ }
+ dmxdev->demux->disconnect_frontend(dmxdev->demux);
+ dmxdev->demux->connect_frontend(dmxdev->demux, front);
+ }
+ dvbdev->users++;
+ mutex_unlock(&dmxdev->mutex);
+ return 0;
+}
+
+static int dvb_dvr_release(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+
+ mutex_lock(&dmxdev->mutex);
+
+ if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+ dmxdev->demux->disconnect_frontend(dmxdev->demux);
+ dmxdev->demux->connect_frontend(dmxdev->demux,
+ dmxdev->dvr_orig_fe);
+ }
+
+ if (((file->f_flags & O_ACCMODE) == O_RDONLY) ||
+ dmxdev->may_do_mmap) {
+ if (dmxdev->may_do_mmap) {
+ if (dvb_vb2_is_streaming(&dmxdev->dvr_vb2_ctx))
+ dvb_vb2_stream_off(&dmxdev->dvr_vb2_ctx);
+ dvb_vb2_release(&dmxdev->dvr_vb2_ctx);
+ }
+ dvbdev->readers++;
+ if (dmxdev->dvr_buffer.data) {
+ void *mem = dmxdev->dvr_buffer.data;
+ /*memory barrier*/
+ mb();
+ spin_lock_irq(&dmxdev->lock);
+ dmxdev->dvr_buffer.data = NULL;
+ spin_unlock_irq(&dmxdev->lock);
+ vfree(mem);
+ }
+ }
+ /* TODO */
+ dvbdev->users--;
+ if (dvbdev->users == 1 && dmxdev->exit == 1) {
+ mutex_unlock(&dmxdev->mutex);
+ wake_up(&dvbdev->wait_queue);
+ } else
+ mutex_unlock(&dmxdev->mutex);
+
+ return 0;
+}
+
+static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ int ret;
+
+ if (!dmxdev->demux->write)
+ return -EOPNOTSUPP;
+ if ((file->f_flags & O_ACCMODE) != O_WRONLY)
+ return -EINVAL;
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if (dmxdev->exit) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENODEV;
+ }
+ ret = dmxdev->demux->write(dmxdev->demux, buf, count);
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+}
+
+static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+
+ if (dmxdev->exit)
+ return -ENODEV;
+
+ return dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,
+ file->f_flags & O_NONBLOCK,
+ buf, count, ppos);
+}
+
+static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
+ unsigned long size)
+{
+ struct dvb_ringbuffer *buf = &dmxdev->dvr_buffer;
+ void *newmem;
+ void *oldmem;
+
+ dprintk("%s\n", __func__);
+
+ if (buf->size == size)
+ return 0;
+ if (!size)
+ return -EINVAL;
+
+ newmem = vmalloc(size);
+ if (!newmem)
+ return -ENOMEM;
+
+ oldmem = buf->data;
+
+ spin_lock_irq(&dmxdev->lock);
+ buf->data = newmem;
+ buf->size = size;
+
+ /* reset and not flush in case the buffer shrinks */
+ dvb_ringbuffer_reset(buf);
+ spin_unlock_irq(&dmxdev->lock);
+
+ vfree(oldmem);
+
+ return 0;
+}
+
+static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter
+ *dmxdevfilter, int state)
+{
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ dmxdevfilter->state = state;
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+}
+
+static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter,
+ unsigned long size)
+{
+ struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;
+ void *newmem;
+ void *oldmem;
+
+ if (buf->size == size)
+ return 0;
+ if (!size)
+ return -EINVAL;
+ if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+ return -EBUSY;
+
+ newmem = vmalloc(size);
+ if (!newmem)
+ return -ENOMEM;
+
+ oldmem = buf->data;
+
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ buf->data = newmem;
+ buf->size = size;
+
+ /* reset and not flush in case the buffer shrinks */
+ dvb_ringbuffer_reset(buf);
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+ vfree(oldmem);
+
+ return 0;
+}
+
+static void dvb_dmxdev_filter_timeout(struct timer_list *t)
+{
+ struct dmxdev_filter *dmxdevfilter = timer_container_of(dmxdevfilter,
+ t, timer);
+
+ dmxdevfilter->buffer.error = -ETIMEDOUT;
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ dmxdevfilter->state = DMXDEV_STATE_TIMEDOUT;
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+ wake_up(&dmxdevfilter->buffer.queue);
+}
+
+static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
+{
+ struct dmx_sct_filter_params *para = &dmxdevfilter->params.sec;
+
+ timer_delete(&dmxdevfilter->timer);
+ if (para->timeout) {
+ dmxdevfilter->timer.expires =
+ jiffies + 1 + (HZ / 2 + HZ * para->timeout) / 1000;
+ add_timer(&dmxdevfilter->timer);
+ }
+}
+
+static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
+ const u8 *buffer2, size_t buffer2_len,
+ struct dmx_section_filter *filter,
+ u32 *buffer_flags)
+{
+ struct dmxdev_filter *dmxdevfilter = filter->priv;
+ int ret;
+
+ if (!dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx) &&
+ dmxdevfilter->buffer.error) {
+ wake_up(&dmxdevfilter->buffer.queue);
+ return 0;
+ }
+ spin_lock(&dmxdevfilter->dev->lock);
+ if (dmxdevfilter->state != DMXDEV_STATE_GO) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return 0;
+ }
+ timer_delete(&dmxdevfilter->timer);
+ dprintk("section callback %*ph\n", 6, buffer1);
+ if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx)) {
+ ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx,
+ buffer1, buffer1_len,
+ buffer_flags);
+ if (ret == buffer1_len)
+ ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx,
+ buffer2, buffer2_len,
+ buffer_flags);
+ } else {
+ ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer,
+ buffer1, buffer1_len);
+ if (ret == buffer1_len) {
+ ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer,
+ buffer2, buffer2_len);
+ }
+ }
+ if (ret < 0)
+ dmxdevfilter->buffer.error = ret;
+ if (dmxdevfilter->params.sec.flags & DMX_ONESHOT)
+ dmxdevfilter->state = DMXDEV_STATE_DONE;
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up(&dmxdevfilter->buffer.queue);
+ return 0;
+}
+
+static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
+ const u8 *buffer2, size_t buffer2_len,
+ struct dmx_ts_feed *feed,
+ u32 *buffer_flags)
+{
+ struct dmxdev_filter *dmxdevfilter = feed->priv;
+ struct dvb_ringbuffer *buffer;
+#ifdef CONFIG_DVB_MMAP
+ struct dvb_vb2_ctx *ctx;
+#endif
+ int ret;
+
+ spin_lock(&dmxdevfilter->dev->lock);
+ if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return 0;
+ }
+
+ if (dmxdevfilter->params.pes.output == DMX_OUT_TAP ||
+ dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP) {
+ buffer = &dmxdevfilter->buffer;
+#ifdef CONFIG_DVB_MMAP
+ ctx = &dmxdevfilter->vb2_ctx;
+#endif
+ } else {
+ buffer = &dmxdevfilter->dev->dvr_buffer;
+#ifdef CONFIG_DVB_MMAP
+ ctx = &dmxdevfilter->dev->dvr_vb2_ctx;
+#endif
+ }
+
+ if (dvb_vb2_is_streaming(ctx)) {
+ ret = dvb_vb2_fill_buffer(ctx, buffer1, buffer1_len,
+ buffer_flags);
+ if (ret == buffer1_len)
+ ret = dvb_vb2_fill_buffer(ctx, buffer2, buffer2_len,
+ buffer_flags);
+ } else {
+ if (buffer->error) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up(&buffer->queue);
+ return 0;
+ }
+ ret = dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
+ if (ret == buffer1_len)
+ ret = dvb_dmxdev_buffer_write(buffer,
+ buffer2, buffer2_len);
+ }
+ if (ret < 0)
+ buffer->error = ret;
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up(&buffer->queue);
+ return 0;
+}
+
+/* stop feed but only mark the specified filter as stopped (state set) */
+static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
+{
+ struct dmxdev_feed *feed;
+
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+ switch (dmxdevfilter->type) {
+ case DMXDEV_TYPE_SEC:
+ timer_delete(&dmxdevfilter->timer);
+ dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);
+ break;
+ case DMXDEV_TYPE_PES:
+ list_for_each_entry(feed, &dmxdevfilter->feed.ts, next)
+ feed->ts->stop_filtering(feed->ts);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* start feed associated with the specified filter */
+static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter)
+{
+ struct dmxdev_feed *feed;
+ int ret;
+
+ dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
+
+ switch (filter->type) {
+ case DMXDEV_TYPE_SEC:
+ return filter->feed.sec->start_filtering(filter->feed.sec);
+ case DMXDEV_TYPE_PES:
+ list_for_each_entry(feed, &filter->feed.ts, next) {
+ ret = feed->ts->start_filtering(feed->ts);
+ if (ret < 0) {
+ dvb_dmxdev_feed_stop(filter);
+ return ret;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* restart section feed if it has filters left associated with it,
+ otherwise release the feed */
+static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter)
+{
+ int i;
+ struct dmxdev *dmxdev = filter->dev;
+ u16 pid = filter->params.sec.pid;
+
+ for (i = 0; i < dmxdev->filternum; i++)
+ if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
+ dmxdev->filter[i].type == DMXDEV_TYPE_SEC &&
+ dmxdev->filter[i].params.sec.pid == pid) {
+ dvb_dmxdev_feed_start(&dmxdev->filter[i]);
+ return 0;
+ }
+
+ filter->dev->demux->release_section_feed(dmxdev->demux,
+ filter->feed.sec);
+
+ return 0;
+}
+
+static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
+{
+ struct dmxdev_feed *feed;
+ struct dmx_demux *demux;
+
+ if (dmxdevfilter->state < DMXDEV_STATE_GO)
+ return 0;
+
+ switch (dmxdevfilter->type) {
+ case DMXDEV_TYPE_SEC:
+ if (!dmxdevfilter->feed.sec)
+ break;
+ dvb_dmxdev_feed_stop(dmxdevfilter);
+ if (dmxdevfilter->filter.sec)
+ dmxdevfilter->feed.sec->
+ release_filter(dmxdevfilter->feed.sec,
+ dmxdevfilter->filter.sec);
+ dvb_dmxdev_feed_restart(dmxdevfilter);
+ dmxdevfilter->feed.sec = NULL;
+ break;
+ case DMXDEV_TYPE_PES:
+ dvb_dmxdev_feed_stop(dmxdevfilter);
+ demux = dmxdevfilter->dev->demux;
+ list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) {
+ demux->release_ts_feed(demux, feed->ts);
+ feed->ts = NULL;
+ }
+ break;
+ default:
+ if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED)
+ return 0;
+ return -EINVAL;
+ }
+
+ dvb_ringbuffer_flush(&dmxdevfilter->buffer);
+ return 0;
+}
+
+static void dvb_dmxdev_delete_pids(struct dmxdev_filter *dmxdevfilter)
+{
+ struct dmxdev_feed *feed, *tmp;
+
+ /* delete all PIDs */
+ list_for_each_entry_safe(feed, tmp, &dmxdevfilter->feed.ts, next) {
+ list_del(&feed->next);
+ kfree(feed);
+ }
+
+ BUG_ON(!list_empty(&dmxdevfilter->feed.ts));
+}
+
+static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter)
+{
+ if (dmxdevfilter->state < DMXDEV_STATE_SET)
+ return 0;
+
+ if (dmxdevfilter->type == DMXDEV_TYPE_PES)
+ dvb_dmxdev_delete_pids(dmxdevfilter);
+
+ dmxdevfilter->type = DMXDEV_TYPE_NONE;
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+ return 0;
+}
+
+static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter,
+ struct dmxdev_feed *feed)
+{
+ ktime_t timeout = ktime_set(0, 0);
+ struct dmx_pes_filter_params *para = &filter->params.pes;
+ enum dmx_output otype;
+ int ret;
+ int ts_type;
+ enum dmx_ts_pes ts_pes;
+ struct dmx_ts_feed *tsfeed;
+
+ feed->ts = NULL;
+ otype = para->output;
+
+ ts_pes = para->pes_type;
+
+ if (ts_pes < DMX_PES_OTHER)
+ ts_type = TS_DECODER;
+ else
+ ts_type = 0;
+
+ if (otype == DMX_OUT_TS_TAP)
+ ts_type |= TS_PACKET;
+ else if (otype == DMX_OUT_TSDEMUX_TAP)
+ ts_type |= TS_PACKET | TS_DEMUX;
+ else if (otype == DMX_OUT_TAP)
+ ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY;
+
+ ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, &feed->ts,
+ dvb_dmxdev_ts_callback);
+ if (ret < 0)
+ return ret;
+
+ tsfeed = feed->ts;
+ tsfeed->priv = filter;
+
+ ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, timeout);
+ if (ret < 0) {
+ dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+ return ret;
+ }
+
+ ret = tsfeed->start_filtering(tsfeed);
+ if (ret < 0) {
+ dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
+{
+ struct dmxdev *dmxdev = filter->dev;
+ struct dmxdev_feed *feed;
+ void *mem;
+ int ret, i;
+
+ if (filter->state < DMXDEV_STATE_SET)
+ return -EINVAL;
+
+ if (filter->state >= DMXDEV_STATE_GO)
+ dvb_dmxdev_filter_stop(filter);
+
+ if (!filter->buffer.data) {
+ mem = vmalloc(filter->buffer.size);
+ if (!mem)
+ return -ENOMEM;
+ spin_lock_irq(&filter->dev->lock);
+ filter->buffer.data = mem;
+ spin_unlock_irq(&filter->dev->lock);
+ }
+
+ dvb_ringbuffer_flush(&filter->buffer);
+
+ switch (filter->type) {
+ case DMXDEV_TYPE_SEC:
+ {
+ struct dmx_sct_filter_params *para = &filter->params.sec;
+ struct dmx_section_filter **secfilter = &filter->filter.sec;
+ struct dmx_section_feed **secfeed = &filter->feed.sec;
+
+ *secfilter = NULL;
+ *secfeed = NULL;
+
+
+ /* find active filter/feed with same PID */
+ for (i = 0; i < dmxdev->filternum; i++) {
+ if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
+ dmxdev->filter[i].type == DMXDEV_TYPE_SEC &&
+ dmxdev->filter[i].params.sec.pid == para->pid) {
+ *secfeed = dmxdev->filter[i].feed.sec;
+ break;
+ }
+ }
+
+ /* if no feed found, try to allocate new one */
+ if (!*secfeed) {
+ ret = dmxdev->demux->allocate_section_feed(dmxdev->demux,
+ secfeed,
+ dvb_dmxdev_section_callback);
+ if (!*secfeed) {
+ pr_err("DVB (%s): could not alloc feed\n",
+ __func__);
+ return ret;
+ }
+
+ ret = (*secfeed)->set(*secfeed, para->pid,
+ (para->flags & DMX_CHECK_CRC) ? 1 : 0);
+ if (ret < 0) {
+ pr_err("DVB (%s): could not set feed\n",
+ __func__);
+ dvb_dmxdev_feed_restart(filter);
+ return ret;
+ }
+ } else {
+ dvb_dmxdev_feed_stop(filter);
+ }
+
+ ret = (*secfeed)->allocate_filter(*secfeed, secfilter);
+ if (ret < 0) {
+ dvb_dmxdev_feed_restart(filter);
+ *secfeed = NULL;
+ dprintk("could not get filter\n");
+ return ret;
+ }
+
+ (*secfilter)->priv = filter;
+
+ memcpy(&((*secfilter)->filter_value[3]),
+ &(para->filter.filter[1]), DMX_FILTER_SIZE - 1);
+ memcpy(&(*secfilter)->filter_mask[3],
+ &para->filter.mask[1], DMX_FILTER_SIZE - 1);
+ memcpy(&(*secfilter)->filter_mode[3],
+ &para->filter.mode[1], DMX_FILTER_SIZE - 1);
+
+ (*secfilter)->filter_value[0] = para->filter.filter[0];
+ (*secfilter)->filter_mask[0] = para->filter.mask[0];
+ (*secfilter)->filter_mode[0] = para->filter.mode[0];
+ (*secfilter)->filter_mask[1] = 0;
+ (*secfilter)->filter_mask[2] = 0;
+
+ filter->todo = 0;
+
+ ret = filter->feed.sec->start_filtering(filter->feed.sec);
+ if (ret < 0)
+ return ret;
+
+ dvb_dmxdev_filter_timer(filter);
+ break;
+ }
+ case DMXDEV_TYPE_PES:
+ list_for_each_entry(feed, &filter->feed.ts, next) {
+ ret = dvb_dmxdev_start_feed(dmxdev, filter, feed);
+ if (ret < 0) {
+ dvb_dmxdev_filter_stop(filter);
+ return ret;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
+ return 0;
+}
+
+static int dvb_demux_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ int i;
+ struct dmxdev_filter *dmxdevfilter;
+
+ if (!dmxdev->filter)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if (dmxdev->exit) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < dmxdev->filternum; i++)
+ if (dmxdev->filter[i].state == DMXDEV_STATE_FREE)
+ break;
+
+ if (i == dmxdev->filternum) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EMFILE;
+ }
+
+ dmxdevfilter = &dmxdev->filter[i];
+ mutex_init(&dmxdevfilter->mutex);
+ file->private_data = dmxdevfilter;
+
+#ifdef CONFIG_DVB_MMAP
+ dmxdev->may_do_mmap = 1;
+#else
+ dmxdev->may_do_mmap = 0;
+#endif
+
+ dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192);
+ dvb_vb2_init(&dmxdevfilter->vb2_ctx, "demux_filter",
+ file->f_flags & O_NONBLOCK);
+ dmxdevfilter->type = DMXDEV_TYPE_NONE;
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+ timer_setup(&dmxdevfilter->timer, dvb_dmxdev_filter_timeout, 0);
+
+ dvbdev->users++;
+
+ mutex_unlock(&dmxdev->mutex);
+ return 0;
+}
+
+static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev,
+ struct dmxdev_filter *dmxdevfilter)
+{
+ mutex_lock(&dmxdev->mutex);
+ mutex_lock(&dmxdevfilter->mutex);
+ if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx))
+ dvb_vb2_stream_off(&dmxdevfilter->vb2_ctx);
+ dvb_vb2_release(&dmxdevfilter->vb2_ctx);
+
+
+ dvb_dmxdev_filter_stop(dmxdevfilter);
+ dvb_dmxdev_filter_reset(dmxdevfilter);
+
+ if (dmxdevfilter->buffer.data) {
+ void *mem = dmxdevfilter->buffer.data;
+
+ spin_lock_irq(&dmxdev->lock);
+ dmxdevfilter->buffer.data = NULL;
+ spin_unlock_irq(&dmxdev->lock);
+ vfree(mem);
+ }
+
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE);
+ wake_up(&dmxdevfilter->buffer.queue);
+ mutex_unlock(&dmxdevfilter->mutex);
+ mutex_unlock(&dmxdev->mutex);
+ return 0;
+}
+
+static inline void invert_mode(struct dmx_filter *filter)
+{
+ int i;
+
+ for (i = 0; i < DMX_FILTER_SIZE; i++)
+ filter->mode[i] ^= 0xff;
+}
+
+static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter, u16 pid)
+{
+ struct dmxdev_feed *feed;
+
+ if ((filter->type != DMXDEV_TYPE_PES) ||
+ (filter->state < DMXDEV_STATE_SET))
+ return -EINVAL;
+
+ /* only TS packet filters may have multiple PIDs */
+ if ((filter->params.pes.output != DMX_OUT_TSDEMUX_TAP) &&
+ (!list_empty(&filter->feed.ts)))
+ return -EINVAL;
+
+ feed = kzalloc(sizeof(struct dmxdev_feed), GFP_KERNEL);
+ if (feed == NULL)
+ return -ENOMEM;
+
+ feed->pid = pid;
+ list_add(&feed->next, &filter->feed.ts);
+
+ if (filter->state >= DMXDEV_STATE_GO)
+ return dvb_dmxdev_start_feed(dmxdev, filter, feed);
+
+ return 0;
+}
+
+static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter, u16 pid)
+{
+ struct dmxdev_feed *feed, *tmp;
+
+ if ((filter->type != DMXDEV_TYPE_PES) ||
+ (filter->state < DMXDEV_STATE_SET))
+ return -EINVAL;
+
+ list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) {
+ if ((feed->pid == pid) && (feed->ts != NULL)) {
+ feed->ts->stop_filtering(feed->ts);
+ filter->dev->demux->release_ts_feed(filter->dev->demux,
+ feed->ts);
+ list_del(&feed->next);
+ kfree(feed);
+ }
+ }
+
+ return 0;
+}
+
+static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
+ struct dmxdev_filter *dmxdevfilter,
+ struct dmx_sct_filter_params *params)
+{
+ dprintk("%s: PID=0x%04x, flags=%02x, timeout=%d\n",
+ __func__, params->pid, params->flags, params->timeout);
+
+ dvb_dmxdev_filter_stop(dmxdevfilter);
+
+ dmxdevfilter->type = DMXDEV_TYPE_SEC;
+ memcpy(&dmxdevfilter->params.sec,
+ params, sizeof(struct dmx_sct_filter_params));
+ invert_mode(&dmxdevfilter->params.sec.filter);
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+ if (params->flags & DMX_IMMEDIATE_START)
+ return dvb_dmxdev_filter_start(dmxdevfilter);
+
+ return 0;
+}
+
+static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
+ struct dmxdev_filter *dmxdevfilter,
+ struct dmx_pes_filter_params *params)
+{
+ int ret;
+
+ dvb_dmxdev_filter_stop(dmxdevfilter);
+ dvb_dmxdev_filter_reset(dmxdevfilter);
+
+ if ((unsigned int)params->pes_type > DMX_PES_OTHER)
+ return -EINVAL;
+
+ dmxdevfilter->type = DMXDEV_TYPE_PES;
+ memcpy(&dmxdevfilter->params, params,
+ sizeof(struct dmx_pes_filter_params));
+ INIT_LIST_HEAD(&dmxdevfilter->feed.ts);
+
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+ ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter,
+ dmxdevfilter->params.pes.pid);
+ if (ret < 0)
+ return ret;
+
+ if (params->flags & DMX_IMMEDIATE_START)
+ return dvb_dmxdev_filter_start(dmxdevfilter);
+
+ return 0;
+}
+
+static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil,
+ struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int result, hcount;
+ int done = 0;
+
+ if (dfil->todo <= 0) {
+ hcount = 3 + dfil->todo;
+ if (hcount > count)
+ hcount = count;
+ result = dvb_dmxdev_buffer_read(&dfil->buffer,
+ file->f_flags & O_NONBLOCK,
+ buf, hcount, ppos);
+ if (result < 0) {
+ dfil->todo = 0;
+ return result;
+ }
+ if (copy_from_user(dfil->secheader - dfil->todo, buf, result))
+ return -EFAULT;
+ buf += result;
+ done = result;
+ count -= result;
+ dfil->todo -= result;
+ if (dfil->todo > -3)
+ return done;
+ dfil->todo = ((dfil->secheader[1] << 8) | dfil->secheader[2]) & 0xfff;
+ if (!count)
+ return done;
+ }
+ if (count > dfil->todo)
+ count = dfil->todo;
+ result = dvb_dmxdev_buffer_read(&dfil->buffer,
+ file->f_flags & O_NONBLOCK,
+ buf, count, ppos);
+ if (result < 0)
+ return result;
+ dfil->todo -= result;
+ return (result + done);
+}
+
+static ssize_t
+dvb_demux_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct dmxdev_filter *dmxdevfilter = file->private_data;
+ int ret;
+
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex))
+ return -ERESTARTSYS;
+
+ if (dmxdevfilter->type == DMXDEV_TYPE_SEC)
+ ret = dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos);
+ else
+ ret = dvb_dmxdev_buffer_read(&dmxdevfilter->buffer,
+ file->f_flags & O_NONBLOCK,
+ buf, count, ppos);
+
+ mutex_unlock(&dmxdevfilter->mutex);
+ return ret;
+}
+
+static int dvb_demux_do_ioctl(struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dmxdev_filter *dmxdevfilter = file->private_data;
+ struct dmxdev *dmxdev = dmxdevfilter->dev;
+ unsigned long arg = (unsigned long)parg;
+ int ret = 0;
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ switch (cmd) {
+ case DMX_START:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ if (dmxdevfilter->state < DMXDEV_STATE_SET)
+ ret = -EINVAL;
+ else
+ ret = dvb_dmxdev_filter_start(dmxdevfilter);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_STOP:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_filter_stop(dmxdevfilter);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_FILTER:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_filter_set(dmxdev, dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_PES_FILTER:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_pes_filter_set(dmxdev, dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_BUFFER_SIZE:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_set_buffer_size(dmxdevfilter, arg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_GET_PES_PIDS:
+ if (!dmxdev->demux->get_pes_pids) {
+ ret = -EINVAL;
+ break;
+ }
+ dmxdev->demux->get_pes_pids(dmxdev->demux, parg);
+ break;
+
+ case DMX_GET_STC:
+ if (!dmxdev->demux->get_stc) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = dmxdev->demux->get_stc(dmxdev->demux,
+ ((struct dmx_stc *)parg)->num,
+ &((struct dmx_stc *)parg)->stc,
+ &((struct dmx_stc *)parg)->base);
+ break;
+
+ case DMX_ADD_PID:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_REMOVE_PID:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ ret = dvb_dmxdev_remove_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+#ifdef CONFIG_DVB_MMAP
+ case DMX_REQBUFS:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_vb2_reqbufs(&dmxdevfilter->vb2_ctx, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_QUERYBUF:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_vb2_querybuf(&dmxdevfilter->vb2_ctx, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_EXPBUF:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_vb2_expbuf(&dmxdevfilter->vb2_ctx, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_QBUF:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_vb2_qbuf(&dmxdevfilter->vb2_ctx, parg);
+ if (ret == 0 && !dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx))
+ ret = dvb_vb2_stream_on(&dmxdevfilter->vb2_ctx);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_DQBUF:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_vb2_dqbuf(&dmxdevfilter->vb2_ctx, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+#endif
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+}
+
+static long dvb_demux_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return dvb_usercopy(file, cmd, arg, dvb_demux_do_ioctl);
+}
+
+static __poll_t dvb_demux_poll(struct file *file, poll_table *wait)
+{
+ struct dmxdev_filter *dmxdevfilter = file->private_data;
+ __poll_t mask = 0;
+
+ poll_wait(file, &dmxdevfilter->buffer.queue, wait);
+
+ if ((!dmxdevfilter) || dmxdevfilter->dev->exit)
+ return EPOLLERR;
+ if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx))
+ return dvb_vb2_poll(&dmxdevfilter->vb2_ctx, file, wait);
+
+ if (dmxdevfilter->state != DMXDEV_STATE_GO &&
+ dmxdevfilter->state != DMXDEV_STATE_DONE &&
+ dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT)
+ return 0;
+
+ if (dmxdevfilter->buffer.error)
+ mask |= (EPOLLIN | EPOLLRDNORM | EPOLLPRI | EPOLLERR);
+
+ if (!dvb_ringbuffer_empty(&dmxdevfilter->buffer))
+ mask |= (EPOLLIN | EPOLLRDNORM | EPOLLPRI);
+
+ return mask;
+}
+
+#ifdef CONFIG_DVB_MMAP
+static int dvb_demux_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct dmxdev_filter *dmxdevfilter = file->private_data;
+ struct dmxdev *dmxdev = dmxdevfilter->dev;
+ int ret;
+
+ if (!dmxdev->may_do_mmap)
+ return -ENOTTY;
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_vb2_mmap(&dmxdevfilter->vb2_ctx, vma);
+
+ mutex_unlock(&dmxdevfilter->mutex);
+ mutex_unlock(&dmxdev->mutex);
+
+ return ret;
+}
+#endif
+
+static int dvb_demux_release(struct inode *inode, struct file *file)
+{
+ struct dmxdev_filter *dmxdevfilter = file->private_data;
+ struct dmxdev *dmxdev = dmxdevfilter->dev;
+ int ret;
+
+ ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
+
+ mutex_lock(&dmxdev->mutex);
+ dmxdev->dvbdev->users--;
+ if (dmxdev->dvbdev->users == 1 && dmxdev->exit == 1) {
+ mutex_unlock(&dmxdev->mutex);
+ wake_up(&dmxdev->dvbdev->wait_queue);
+ } else
+ mutex_unlock(&dmxdev->mutex);
+
+ return ret;
+}
+
+static const struct file_operations dvb_demux_fops = {
+ .owner = THIS_MODULE,
+ .read = dvb_demux_read,
+ .unlocked_ioctl = dvb_demux_ioctl,
+ .compat_ioctl = dvb_demux_ioctl,
+ .open = dvb_demux_open,
+ .release = dvb_demux_release,
+ .poll = dvb_demux_poll,
+ .llseek = default_llseek,
+#ifdef CONFIG_DVB_MMAP
+ .mmap = dvb_demux_mmap,
+#endif
+};
+
+static const struct dvb_device dvbdev_demux = {
+ .priv = NULL,
+ .users = 1,
+ .writers = 1,
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+ .name = "dvb-demux",
+#endif
+ .fops = &dvb_demux_fops
+};
+
+static int dvb_dvr_do_ioctl(struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ unsigned long arg = (unsigned long)parg;
+ int ret;
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ switch (cmd) {
+ case DMX_SET_BUFFER_SIZE:
+ ret = dvb_dvr_set_buffer_size(dmxdev, arg);
+ break;
+
+#ifdef CONFIG_DVB_MMAP
+ case DMX_REQBUFS:
+ ret = dvb_vb2_reqbufs(&dmxdev->dvr_vb2_ctx, parg);
+ break;
+
+ case DMX_QUERYBUF:
+ ret = dvb_vb2_querybuf(&dmxdev->dvr_vb2_ctx, parg);
+ break;
+
+ case DMX_EXPBUF:
+ ret = dvb_vb2_expbuf(&dmxdev->dvr_vb2_ctx, parg);
+ break;
+
+ case DMX_QBUF:
+ ret = dvb_vb2_qbuf(&dmxdev->dvr_vb2_ctx, parg);
+ if (ret == 0 && !dvb_vb2_is_streaming(&dmxdev->dvr_vb2_ctx))
+ ret = dvb_vb2_stream_on(&dmxdev->dvr_vb2_ctx);
+ break;
+
+ case DMX_DQBUF:
+ ret = dvb_vb2_dqbuf(&dmxdev->dvr_vb2_ctx, parg);
+ break;
+#endif
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+}
+
+static long dvb_dvr_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return dvb_usercopy(file, cmd, arg, dvb_dvr_do_ioctl);
+}
+
+static __poll_t dvb_dvr_poll(struct file *file, poll_table *wait)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ __poll_t mask = 0;
+
+ dprintk("%s\n", __func__);
+
+ poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
+
+ if (dmxdev->exit)
+ return EPOLLERR;
+ if (dvb_vb2_is_streaming(&dmxdev->dvr_vb2_ctx))
+ return dvb_vb2_poll(&dmxdev->dvr_vb2_ctx, file, wait);
+
+ if (((file->f_flags & O_ACCMODE) == O_RDONLY) ||
+ dmxdev->may_do_mmap) {
+ if (dmxdev->dvr_buffer.error)
+ mask |= (EPOLLIN | EPOLLRDNORM | EPOLLPRI | EPOLLERR);
+
+ if (!dvb_ringbuffer_empty(&dmxdev->dvr_buffer))
+ mask |= (EPOLLIN | EPOLLRDNORM | EPOLLPRI);
+ } else
+ mask |= (EPOLLOUT | EPOLLWRNORM | EPOLLPRI);
+
+ return mask;
+}
+
+#ifdef CONFIG_DVB_MMAP
+static int dvb_dvr_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ int ret;
+
+ if (!dmxdev->may_do_mmap)
+ return -ENOTTY;
+
+ if (dmxdev->exit)
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ ret = dvb_vb2_mmap(&dmxdev->dvr_vb2_ctx, vma);
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+}
+#endif
+
+static const struct file_operations dvb_dvr_fops = {
+ .owner = THIS_MODULE,
+ .read = dvb_dvr_read,
+ .write = dvb_dvr_write,
+ .unlocked_ioctl = dvb_dvr_ioctl,
+ .open = dvb_dvr_open,
+ .release = dvb_dvr_release,
+ .poll = dvb_dvr_poll,
+ .llseek = default_llseek,
+#ifdef CONFIG_DVB_MMAP
+ .mmap = dvb_dvr_mmap,
+#endif
+};
+
+static const struct dvb_device dvbdev_dvr = {
+ .priv = NULL,
+ .readers = 1,
+ .users = 1,
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+ .name = "dvb-dvr",
+#endif
+ .fops = &dvb_dvr_fops
+};
+int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
+{
+ int i, ret;
+
+ if (dmxdev->demux->open(dmxdev->demux) < 0)
+ return -EUSERS;
+
+ dmxdev->filter = vmalloc_array(dmxdev->filternum,
+ sizeof(struct dmxdev_filter));
+ if (!dmxdev->filter)
+ return -ENOMEM;
+
+ mutex_init(&dmxdev->mutex);
+ spin_lock_init(&dmxdev->lock);
+ for (i = 0; i < dmxdev->filternum; i++) {
+ dmxdev->filter[i].dev = dmxdev;
+ dmxdev->filter[i].buffer.data = NULL;
+ dvb_dmxdev_filter_state_set(&dmxdev->filter[i],
+ DMXDEV_STATE_FREE);
+ }
+
+ ret = dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev,
+ DVB_DEVICE_DEMUX, dmxdev->filternum);
+ if (ret < 0)
+ goto err_register_dvbdev;
+
+ ret = dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr,
+ dmxdev, DVB_DEVICE_DVR, dmxdev->filternum);
+ if (ret < 0)
+ goto err_register_dvr_dvbdev;
+
+ dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192);
+
+ return 0;
+
+err_register_dvr_dvbdev:
+ dvb_unregister_device(dmxdev->dvbdev);
+err_register_dvbdev:
+ vfree(dmxdev->filter);
+ dmxdev->filter = NULL;
+ return ret;
+}
+
+EXPORT_SYMBOL(dvb_dmxdev_init);
+
+void dvb_dmxdev_release(struct dmxdev *dmxdev)
+{
+ mutex_lock(&dmxdev->mutex);
+ dmxdev->exit = 1;
+ mutex_unlock(&dmxdev->mutex);
+
+ if (dmxdev->dvbdev->users > 1) {
+ wait_event(dmxdev->dvbdev->wait_queue,
+ dmxdev->dvbdev->users == 1);
+ }
+ if (dmxdev->dvr_dvbdev->users > 1) {
+ wait_event(dmxdev->dvr_dvbdev->wait_queue,
+ dmxdev->dvr_dvbdev->users == 1);
+ }
+
+ dvb_unregister_device(dmxdev->dvbdev);
+ dvb_unregister_device(dmxdev->dvr_dvbdev);
+
+ vfree(dmxdev->filter);
+ dmxdev->filter = NULL;
+ dmxdev->demux->close(dmxdev->demux);
+}
+
+EXPORT_SYMBOL(dvb_dmxdev_release);
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
new file mode 100644
index 000000000000..7b591aa1179f
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
@@ -0,0 +1,1983 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * dvb_ca.c: generic DVB functions for EN50221 CAM interfaces
+ *
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (C) 2003 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * based on code:
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ */
+
+#define pr_fmt(fmt) "dvb_ca_en50221: " fmt
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/nospec.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/sched/signal.h>
+#include <linux/kthread.h>
+
+#include <media/dvb_ca_en50221.h>
+#include <media/dvb_ringbuffer.h>
+
+static int dvb_ca_en50221_debug;
+
+module_param_named(cam_debug, dvb_ca_en50221_debug, int, 0644);
+MODULE_PARM_DESC(cam_debug, "enable verbose debug messages");
+
+#define dprintk(fmt, arg...) do { \
+ if (dvb_ca_en50221_debug) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), __func__, ##arg);\
+} while (0)
+
+#define INIT_TIMEOUT_SECS 10
+
+#define HOST_LINK_BUF_SIZE 0x200
+
+#define RX_BUFFER_SIZE 65535
+
+#define MAX_RX_PACKETS_PER_ITERATION 10
+
+#define CTRLIF_DATA 0
+#define CTRLIF_COMMAND 1
+#define CTRLIF_STATUS 1
+#define CTRLIF_SIZE_LOW 2
+#define CTRLIF_SIZE_HIGH 3
+
+#define CMDREG_HC 1 /* Host control */
+#define CMDREG_SW 2 /* Size write */
+#define CMDREG_SR 4 /* Size read */
+#define CMDREG_RS 8 /* Reset interface */
+#define CMDREG_FRIE 0x40 /* Enable FR interrupt */
+#define CMDREG_DAIE 0x80 /* Enable DA interrupt */
+#define IRQEN (CMDREG_DAIE)
+
+#define STATUSREG_RE 1 /* read error */
+#define STATUSREG_WE 2 /* write error */
+#define STATUSREG_FR 0x40 /* module free */
+#define STATUSREG_DA 0x80 /* data available */
+
+#define DVB_CA_SLOTSTATE_NONE 0
+#define DVB_CA_SLOTSTATE_UNINITIALISED 1
+#define DVB_CA_SLOTSTATE_RUNNING 2
+#define DVB_CA_SLOTSTATE_INVALID 3
+#define DVB_CA_SLOTSTATE_WAITREADY 4
+#define DVB_CA_SLOTSTATE_VALIDATE 5
+#define DVB_CA_SLOTSTATE_WAITFR 6
+#define DVB_CA_SLOTSTATE_LINKINIT 7
+
+/* Information on a CA slot */
+struct dvb_ca_slot {
+ /* current state of the CAM */
+ int slot_state;
+
+ /* mutex used for serializing access to one CI slot */
+ struct mutex slot_lock;
+
+ /* Number of CAMCHANGES that have occurred since last processing */
+ atomic_t camchange_count;
+
+ /* Type of last CAMCHANGE */
+ int camchange_type;
+
+ /* base address of CAM config */
+ u32 config_base;
+
+ /* value to write into Config Control register */
+ u8 config_option;
+
+ /* if 1, the CAM supports DA IRQs */
+ u8 da_irq_supported:1;
+
+ /* size of the buffer to use when talking to the CAM */
+ int link_buf_size;
+
+ /* buffer for incoming packets */
+ struct dvb_ringbuffer rx_buffer;
+
+ /* timer used during various states of the slot */
+ unsigned long timeout;
+};
+
+/* Private CA-interface information */
+struct dvb_ca_private {
+ struct kref refcount;
+
+ /* pointer back to the public data structure */
+ struct dvb_ca_en50221 *pub;
+
+ /* the DVB device */
+ struct dvb_device *dvbdev;
+
+ /* Flags describing the interface (DVB_CA_FLAG_*) */
+ u32 flags;
+
+ /* number of slots supported by this CA interface */
+ unsigned int slot_count;
+
+ /* information on each slot */
+ struct dvb_ca_slot *slot_info;
+
+ /* wait queues for read() and write() operations */
+ wait_queue_head_t wait_queue;
+
+ /* PID of the monitoring thread */
+ struct task_struct *thread;
+
+ /* Flag indicating if the CA device is open */
+ unsigned int open:1;
+
+ /* Flag indicating the thread should wake up now */
+ unsigned int wakeup:1;
+
+ /* Delay the main thread should use */
+ unsigned long delay;
+
+ /*
+ * Slot to start looking for data to read from in the next user-space
+ * read operation
+ */
+ int next_read_slot;
+
+ /* mutex serializing ioctls */
+ struct mutex ioctl_mutex;
+
+ /* A mutex used when a device is disconnected */
+ struct mutex remove_mutex;
+
+ /* Whether the device is disconnected */
+ int exit;
+};
+
+static void dvb_ca_private_free(struct dvb_ca_private *ca)
+{
+ unsigned int i;
+
+ dvb_device_put(ca->dvbdev);
+ for (i = 0; i < ca->slot_count; i++)
+ vfree(ca->slot_info[i].rx_buffer.data);
+
+ kfree(ca->slot_info);
+ kfree(ca);
+}
+
+static void dvb_ca_private_release(struct kref *ref)
+{
+ struct dvb_ca_private *ca;
+
+ ca = container_of(ref, struct dvb_ca_private, refcount);
+ dvb_ca_private_free(ca);
+}
+
+static void dvb_ca_private_get(struct dvb_ca_private *ca)
+{
+ kref_get(&ca->refcount);
+}
+
+static void dvb_ca_private_put(struct dvb_ca_private *ca)
+{
+ kref_put(&ca->refcount, dvb_ca_private_release);
+}
+
+static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca);
+static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot,
+ u8 *ebuf, int ecount);
+static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot,
+ u8 *ebuf, int ecount, int size_write_flag);
+
+/**
+ * findstr - Safely find needle in haystack.
+ *
+ * @haystack: Buffer to look in.
+ * @hlen: Number of bytes in haystack.
+ * @needle: Buffer to find.
+ * @nlen: Number of bytes in needle.
+ * return: Pointer into haystack needle was found at, or NULL if not found.
+ */
+static char *findstr(char *haystack, int hlen, char *needle, int nlen)
+{
+ int i;
+
+ if (hlen < nlen)
+ return NULL;
+
+ for (i = 0; i <= hlen - nlen; i++) {
+ if (!strncmp(haystack + i, needle, nlen))
+ return haystack + i;
+ }
+
+ return NULL;
+}
+
+/* ************************************************************************** */
+/* EN50221 physical interface functions */
+
+/*
+ * dvb_ca_en50221_check_camstatus - Check CAM status.
+ */
+static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot)
+{
+ struct dvb_ca_slot *sl = &ca->slot_info[slot];
+ int slot_status;
+ int cam_present_now;
+ int cam_changed;
+
+ /* IRQ mode */
+ if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)
+ return (atomic_read(&sl->camchange_count) != 0);
+
+ /* poll mode */
+ slot_status = ca->pub->poll_slot_status(ca->pub, slot, ca->open);
+
+ cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1 : 0;
+ cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1 : 0;
+ if (!cam_changed) {
+ int cam_present_old = (sl->slot_state != DVB_CA_SLOTSTATE_NONE);
+
+ cam_changed = (cam_present_now != cam_present_old);
+ }
+
+ if (cam_changed) {
+ if (!cam_present_now)
+ sl->camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+ else
+ sl->camchange_type = DVB_CA_EN50221_CAMCHANGE_INSERTED;
+ atomic_set(&sl->camchange_count, 1);
+ } else {
+ if ((sl->slot_state == DVB_CA_SLOTSTATE_WAITREADY) &&
+ (slot_status & DVB_CA_EN50221_POLL_CAM_READY)) {
+ /* move to validate state if reset is completed */
+ sl->slot_state = DVB_CA_SLOTSTATE_VALIDATE;
+ }
+ }
+
+ return cam_changed;
+}
+
+/**
+ * dvb_ca_en50221_wait_if_status - Wait for flags to become set on the STATUS
+ * register on a CAM interface, checking for errors and timeout.
+ *
+ * @ca: CA instance.
+ * @slot: Slot on interface.
+ * @waitfor: Flags to wait for.
+ * @timeout_hz: Timeout in milliseconds.
+ *
+ * return: 0 on success, nonzero on error.
+ */
+static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot,
+ u8 waitfor, int timeout_hz)
+{
+ unsigned long timeout;
+ unsigned long start;
+
+ dprintk("%s\n", __func__);
+
+ /* loop until timeout elapsed */
+ start = jiffies;
+ timeout = jiffies + timeout_hz;
+ while (1) {
+ int res;
+
+ /* read the status and check for error */
+ res = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+ if (res < 0)
+ return -EIO;
+
+ /* if we got the flags, it was successful! */
+ if (res & waitfor) {
+ dprintk("%s succeeded timeout:%lu\n",
+ __func__, jiffies - start);
+ return 0;
+ }
+
+ /* check for timeout */
+ if (time_after(jiffies, timeout))
+ break;
+
+ /* wait for a bit */
+ usleep_range(1000, 1100);
+ }
+
+ dprintk("%s failed timeout:%lu\n", __func__, jiffies - start);
+
+ /* if we get here, we've timed out */
+ return -ETIMEDOUT;
+}
+
+/**
+ * dvb_ca_en50221_link_init - Initialise the link layer connection to a CAM.
+ *
+ * @ca: CA instance.
+ * @slot: Slot id.
+ *
+ * return: 0 on success, nonzero on failure.
+ */
+static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot)
+{
+ struct dvb_ca_slot *sl = &ca->slot_info[slot];
+ int ret;
+ int buf_size;
+ u8 buf[2];
+
+ dprintk("%s\n", __func__);
+
+ /* we'll be determining these during this function */
+ sl->da_irq_supported = 0;
+
+ /*
+ * set the host link buffer size temporarily. it will be overwritten
+ * with the real negotiated size later.
+ */
+ sl->link_buf_size = 2;
+
+ /* read the buffer size from the CAM */
+ ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
+ IRQEN | CMDREG_SR);
+ if (ret)
+ return ret;
+ ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ);
+ if (ret)
+ return ret;
+ ret = dvb_ca_en50221_read_data(ca, slot, buf, 2);
+ if (ret != 2)
+ return -EIO;
+ ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
+ if (ret)
+ return ret;
+
+ /*
+ * store it, and choose the minimum of our buffer and the CAM's buffer
+ * size
+ */
+ buf_size = (buf[0] << 8) | buf[1];
+ if (buf_size > HOST_LINK_BUF_SIZE)
+ buf_size = HOST_LINK_BUF_SIZE;
+ sl->link_buf_size = buf_size;
+ buf[0] = buf_size >> 8;
+ buf[1] = buf_size & 0xff;
+ dprintk("Chosen link buffer size of %i\n", buf_size);
+
+ /* write the buffer size to the CAM */
+ ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
+ IRQEN | CMDREG_SW);
+ if (ret)
+ return ret;
+ ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10);
+ if (ret)
+ return ret;
+ ret = dvb_ca_en50221_write_data(ca, slot, buf, 2, CMDREG_SW);
+ if (ret != 2)
+ return -EIO;
+ ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
+ if (ret)
+ return ret;
+
+ /* success */
+ return 0;
+}
+
+/**
+ * dvb_ca_en50221_read_tuple - Read a tuple from attribute memory.
+ *
+ * @ca: CA instance.
+ * @slot: Slot id.
+ * @address: Address to read from. Updated.
+ * @tuple_type: Tuple id byte. Updated.
+ * @tuple_length: Tuple length. Updated.
+ * @tuple: Dest buffer for tuple (must be 256 bytes). Updated.
+ *
+ * return: 0 on success, nonzero on error.
+ */
+static int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot,
+ int *address, int *tuple_type,
+ int *tuple_length, u8 *tuple)
+{
+ int i;
+ int _tuple_type;
+ int _tuple_length;
+ int _address = *address;
+
+ /* grab the next tuple length and type */
+ _tuple_type = ca->pub->read_attribute_mem(ca->pub, slot, _address);
+ if (_tuple_type < 0)
+ return _tuple_type;
+ if (_tuple_type == 0xff) {
+ dprintk("END OF CHAIN TUPLE type:0x%x\n", _tuple_type);
+ *address += 2;
+ *tuple_type = _tuple_type;
+ *tuple_length = 0;
+ return 0;
+ }
+ _tuple_length = ca->pub->read_attribute_mem(ca->pub, slot,
+ _address + 2);
+ if (_tuple_length < 0)
+ return _tuple_length;
+ _address += 4;
+
+ dprintk("TUPLE type:0x%x length:%i\n", _tuple_type, _tuple_length);
+
+ /* read in the whole tuple */
+ for (i = 0; i < _tuple_length; i++) {
+ tuple[i] = ca->pub->read_attribute_mem(ca->pub, slot,
+ _address + (i * 2));
+ dprintk(" 0x%02x: 0x%02x %c\n",
+ i, tuple[i] & 0xff,
+ ((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.');
+ }
+ _address += (_tuple_length * 2);
+
+ /* success */
+ *tuple_type = _tuple_type;
+ *tuple_length = _tuple_length;
+ *address = _address;
+ return 0;
+}
+
+/**
+ * dvb_ca_en50221_parse_attributes - Parse attribute memory of a CAM module,
+ * extracting Config register, and checking it is a DVB CAM module.
+ *
+ * @ca: CA instance.
+ * @slot: Slot id.
+ *
+ * return: 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot)
+{
+ struct dvb_ca_slot *sl;
+ int address = 0;
+ int tuple_length;
+ int tuple_type;
+ u8 tuple[257];
+ char *dvb_str;
+ int rasz;
+ int status;
+ int got_cftableentry = 0;
+ int end_chain = 0;
+ int i;
+ u16 manfid = 0;
+ u16 devid = 0;
+
+ /* CISTPL_DEVICE_0A */
+ status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
+ &tuple_length, tuple);
+ if (status < 0)
+ return status;
+ if (tuple_type != 0x1D)
+ return -EINVAL;
+
+ /* CISTPL_DEVICE_0C */
+ status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
+ &tuple_length, tuple);
+ if (status < 0)
+ return status;
+ if (tuple_type != 0x1C)
+ return -EINVAL;
+
+ /* CISTPL_VERS_1 */
+ status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
+ &tuple_length, tuple);
+ if (status < 0)
+ return status;
+ if (tuple_type != 0x15)
+ return -EINVAL;
+
+ /* CISTPL_MANFID */
+ status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
+ &tuple_length, tuple);
+ if (status < 0)
+ return status;
+ if (tuple_type != 0x20)
+ return -EINVAL;
+ if (tuple_length != 4)
+ return -EINVAL;
+ manfid = (tuple[1] << 8) | tuple[0];
+ devid = (tuple[3] << 8) | tuple[2];
+
+ /* CISTPL_CONFIG */
+ status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
+ &tuple_length, tuple);
+ if (status < 0)
+ return status;
+ if (tuple_type != 0x1A)
+ return -EINVAL;
+ if (tuple_length < 3)
+ return -EINVAL;
+
+ /* extract the configbase */
+ rasz = tuple[0] & 3;
+ if (tuple_length < (3 + rasz + 14))
+ return -EINVAL;
+ sl = &ca->slot_info[slot];
+ sl->config_base = 0;
+ for (i = 0; i < rasz + 1; i++)
+ sl->config_base |= (tuple[2 + i] << (8 * i));
+
+ /* check it contains the correct DVB string */
+ dvb_str = findstr((char *)tuple, tuple_length, "DVB_CI_V", 8);
+ if (!dvb_str)
+ return -EINVAL;
+ if (tuple_length < ((dvb_str - (char *)tuple) + 12))
+ return -EINVAL;
+
+ /* is it a version we support? */
+ if (strncmp(dvb_str + 8, "1.00", 4)) {
+ pr_err("dvb_ca adapter %d: Unsupported DVB CAM module version %c%c%c%c\n",
+ ca->dvbdev->adapter->num, dvb_str[8], dvb_str[9],
+ dvb_str[10], dvb_str[11]);
+ return -EINVAL;
+ }
+
+ /* process the CFTABLE_ENTRY tuples, and any after those */
+ while ((!end_chain) && (address < 0x1000)) {
+ status = dvb_ca_en50221_read_tuple(ca, slot, &address,
+ &tuple_type, &tuple_length,
+ tuple);
+ if (status < 0)
+ return status;
+ switch (tuple_type) {
+ case 0x1B: /* CISTPL_CFTABLE_ENTRY */
+ if (tuple_length < (2 + 11 + 17))
+ break;
+
+ /* if we've already parsed one, just use it */
+ if (got_cftableentry)
+ break;
+
+ /* get the config option */
+ sl->config_option = tuple[0] & 0x3f;
+
+ /* OK, check it contains the correct strings */
+ if (!findstr((char *)tuple, tuple_length,
+ "DVB_HOST", 8) ||
+ !findstr((char *)tuple, tuple_length,
+ "DVB_CI_MODULE", 13))
+ break;
+
+ got_cftableentry = 1;
+ break;
+
+ case 0x14: /* CISTPL_NO_LINK */
+ break;
+
+ case 0xFF: /* CISTPL_END */
+ end_chain = 1;
+ break;
+
+ default: /* Unknown tuple type - just skip this tuple */
+ dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n",
+ tuple_type, tuple_length);
+ break;
+ }
+ }
+
+ if ((address > 0x1000) || (!got_cftableentry))
+ return -EINVAL;
+
+ dprintk("Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x\n",
+ manfid, devid, sl->config_base, sl->config_option);
+
+ /* success! */
+ return 0;
+}
+
+/**
+ * dvb_ca_en50221_set_configoption - Set CAM's configoption correctly.
+ *
+ * @ca: CA instance.
+ * @slot: Slot containing the CAM.
+ */
+static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot)
+{
+ struct dvb_ca_slot *sl = &ca->slot_info[slot];
+ int configoption;
+
+ dprintk("%s\n", __func__);
+
+ /* set the config option */
+ ca->pub->write_attribute_mem(ca->pub, slot, sl->config_base,
+ sl->config_option);
+
+ /* check it */
+ configoption = ca->pub->read_attribute_mem(ca->pub, slot,
+ sl->config_base);
+ dprintk("Set configoption 0x%x, read configoption 0x%x\n",
+ sl->config_option, configoption & 0x3f);
+
+ /* fine! */
+ return 0;
+}
+
+/**
+ * dvb_ca_en50221_read_data - This function talks to an EN50221 CAM control
+ * interface. It reads a buffer of data from the CAM. The data can either
+ * be stored in a supplied buffer, or automatically be added to the slot's
+ * rx_buffer.
+ *
+ * @ca: CA instance.
+ * @slot: Slot to read from.
+ * @ebuf: If non-NULL, the data will be written to this buffer. If NULL,
+ * the data will be added into the buffering system as a normal
+ * fragment.
+ * @ecount: Size of ebuf. Ignored if ebuf is NULL.
+ *
+ * return: Number of bytes read, or < 0 on error
+ */
+static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot,
+ u8 *ebuf, int ecount)
+{
+ struct dvb_ca_slot *sl = &ca->slot_info[slot];
+ int bytes_read;
+ int status;
+ u8 buf[HOST_LINK_BUF_SIZE];
+ int i;
+
+ dprintk("%s\n", __func__);
+
+ /* check if we have space for a link buf in the rx_buffer */
+ if (!ebuf) {
+ int buf_free;
+
+ if (!sl->rx_buffer.data) {
+ status = -EIO;
+ goto exit;
+ }
+ buf_free = dvb_ringbuffer_free(&sl->rx_buffer);
+
+ if (buf_free < (sl->link_buf_size +
+ DVB_RINGBUFFER_PKTHDRSIZE)) {
+ status = -EAGAIN;
+ goto exit;
+ }
+ }
+
+ if (ca->pub->read_data &&
+ (sl->slot_state != DVB_CA_SLOTSTATE_LINKINIT)) {
+ if (!ebuf)
+ status = ca->pub->read_data(ca->pub, slot, buf,
+ sizeof(buf));
+ else
+ status = ca->pub->read_data(ca->pub, slot, buf, ecount);
+ if (status < 0)
+ return status;
+ bytes_read = status;
+ if (status == 0)
+ goto exit;
+ } else {
+ /* check if there is data available */
+ status = ca->pub->read_cam_control(ca->pub, slot,
+ CTRLIF_STATUS);
+ if (status < 0)
+ goto exit;
+ if (!(status & STATUSREG_DA)) {
+ /* no data */
+ status = 0;
+ goto exit;
+ }
+
+ /* read the amount of data */
+ status = ca->pub->read_cam_control(ca->pub, slot,
+ CTRLIF_SIZE_HIGH);
+ if (status < 0)
+ goto exit;
+ bytes_read = status << 8;
+ status = ca->pub->read_cam_control(ca->pub, slot,
+ CTRLIF_SIZE_LOW);
+ if (status < 0)
+ goto exit;
+ bytes_read |= status;
+
+ /* check it will fit */
+ if (!ebuf) {
+ if (bytes_read > sl->link_buf_size) {
+ pr_err("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n",
+ ca->dvbdev->adapter->num, bytes_read,
+ sl->link_buf_size);
+ sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+ if (bytes_read < 2) {
+ pr_err("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n",
+ ca->dvbdev->adapter->num);
+ sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+ } else {
+ if (bytes_read > ecount) {
+ pr_err("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n",
+ ca->dvbdev->adapter->num);
+ status = -EIO;
+ goto exit;
+ }
+ }
+
+ /* fill the buffer */
+ for (i = 0; i < bytes_read; i++) {
+ /* read byte and check */
+ status = ca->pub->read_cam_control(ca->pub, slot,
+ CTRLIF_DATA);
+ if (status < 0)
+ goto exit;
+
+ /* OK, store it in the buffer */
+ buf[i] = status;
+ }
+
+ /* check for read error (RE should now be 0) */
+ status = ca->pub->read_cam_control(ca->pub, slot,
+ CTRLIF_STATUS);
+ if (status < 0)
+ goto exit;
+ if (status & STATUSREG_RE) {
+ sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+ }
+
+ /*
+ * OK, add it to the receive buffer, or copy into external buffer if
+ * supplied
+ */
+ if (!ebuf) {
+ if (!sl->rx_buffer.data) {
+ status = -EIO;
+ goto exit;
+ }
+ dvb_ringbuffer_pkt_write(&sl->rx_buffer, buf, bytes_read);
+ } else {
+ memcpy(ebuf, buf, bytes_read);
+ }
+
+ dprintk("Received CA packet for slot %i connection id 0x%x last_frag:%i size:0x%x\n", slot,
+ buf[0], (buf[1] & 0x80) == 0, bytes_read);
+
+ /* wake up readers when a last_fragment is received */
+ if ((buf[1] & 0x80) == 0x00)
+ wake_up_interruptible(&ca->wait_queue);
+
+ status = bytes_read;
+
+exit:
+ return status;
+}
+
+/**
+ * dvb_ca_en50221_write_data - This function talks to an EN50221 CAM control
+ * interface. It writes a buffer of data to a CAM.
+ *
+ * @ca: CA instance.
+ * @slot: Slot to write to.
+ * @buf: The data in this buffer is treated as a complete link-level packet to
+ * be written.
+ * @bytes_write: Size of ebuf.
+ * @size_write_flag: A flag on Command Register which says whether the link size
+ * information will be written or not.
+ *
+ * return: Number of bytes written, or < 0 on error.
+ */
+static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot,
+ u8 *buf, int bytes_write, int size_write_flag)
+{
+ struct dvb_ca_slot *sl = &ca->slot_info[slot];
+ int status;
+ int i;
+
+ dprintk("%s\n", __func__);
+
+ /* sanity check */
+ if (bytes_write > sl->link_buf_size)
+ return -EINVAL;
+
+ if (ca->pub->write_data &&
+ (sl->slot_state != DVB_CA_SLOTSTATE_LINKINIT))
+ return ca->pub->write_data(ca->pub, slot, buf, bytes_write);
+
+ /*
+ * it is possible we are dealing with a single buffer implementation,
+ * thus if there is data available for read or if there is even a read
+ * already in progress, we do nothing but awake the kernel thread to
+ * process the data if necessary.
+ */
+ status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+ if (status < 0)
+ goto exitnowrite;
+ if (status & (STATUSREG_DA | STATUSREG_RE)) {
+ if (status & STATUSREG_DA)
+ dvb_ca_en50221_thread_wakeup(ca);
+
+ status = -EAGAIN;
+ goto exitnowrite;
+ }
+
+ /* OK, set HC bit */
+ status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
+ IRQEN | CMDREG_HC | size_write_flag);
+ if (status)
+ goto exit;
+
+ /* check if interface is still free */
+ status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+ if (status < 0)
+ goto exit;
+ if (!(status & STATUSREG_FR)) {
+ /* it wasn't free => try again later */
+ status = -EAGAIN;
+ goto exit;
+ }
+
+ /*
+ * It may need some time for the CAM to settle down, or there might
+ * be a race condition between the CAM, writing HC and our last
+ * check for DA. This happens, if the CAM asserts DA, just after
+ * checking DA before we are setting HC. In this case it might be
+ * a bug in the CAM to keep the FR bit, the lower layer/HW
+ * communication requires a longer timeout or the CAM needs more
+ * time internally. But this happens in reality!
+ * We need to read the status from the HW again and do the same
+ * we did for the previous check for DA
+ */
+ status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+ if (status < 0)
+ goto exit;
+
+ if (status & (STATUSREG_DA | STATUSREG_RE)) {
+ if (status & STATUSREG_DA)
+ dvb_ca_en50221_thread_wakeup(ca);
+
+ status = -EAGAIN;
+ goto exit;
+ }
+
+ /* send the amount of data */
+ status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH,
+ bytes_write >> 8);
+ if (status)
+ goto exit;
+ status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW,
+ bytes_write & 0xff);
+ if (status)
+ goto exit;
+
+ /* send the buffer */
+ for (i = 0; i < bytes_write; i++) {
+ status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_DATA,
+ buf[i]);
+ if (status)
+ goto exit;
+ }
+
+ /* check for write error (WE should now be 0) */
+ status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+ if (status < 0)
+ goto exit;
+ if (status & STATUSREG_WE) {
+ sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+ status = bytes_write;
+
+ dprintk("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot,
+ buf[0], (buf[1] & 0x80) == 0, bytes_write);
+
+exit:
+ ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
+
+exitnowrite:
+ return status;
+}
+
+/* ************************************************************************** */
+/* EN50221 higher level functions */
+
+/**
+ * dvb_ca_en50221_slot_shutdown - A CAM has been removed => shut it down.
+ *
+ * @ca: CA instance.
+ * @slot: Slot to shut down.
+ */
+static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot)
+{
+ dprintk("%s\n", __func__);
+
+ ca->pub->slot_shutdown(ca->pub, slot);
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
+
+ /*
+ * need to wake up all processes to check if they're now trying to
+ * write to a defunct CAM
+ */
+ wake_up_interruptible(&ca->wait_queue);
+
+ dprintk("Slot %i shutdown\n", slot);
+
+ /* success */
+ return 0;
+}
+
+/**
+ * dvb_ca_en50221_camchange_irq - A CAMCHANGE IRQ has occurred.
+ *
+ * @pubca: CA instance.
+ * @slot: Slot concerned.
+ * @change_type: One of the DVB_CA_CAMCHANGE_* values.
+ */
+void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 *pubca, int slot,
+ int change_type)
+{
+ struct dvb_ca_private *ca = pubca->private;
+ struct dvb_ca_slot *sl = &ca->slot_info[slot];
+
+ dprintk("CAMCHANGE IRQ slot:%i change_type:%i\n", slot, change_type);
+
+ switch (change_type) {
+ case DVB_CA_EN50221_CAMCHANGE_REMOVED:
+ case DVB_CA_EN50221_CAMCHANGE_INSERTED:
+ break;
+
+ default:
+ return;
+ }
+
+ sl->camchange_type = change_type;
+ atomic_inc(&sl->camchange_count);
+ dvb_ca_en50221_thread_wakeup(ca);
+}
+EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq);
+
+/**
+ * dvb_ca_en50221_camready_irq - A CAMREADY IRQ has occurred.
+ *
+ * @pubca: CA instance.
+ * @slot: Slot concerned.
+ */
+void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot)
+{
+ struct dvb_ca_private *ca = pubca->private;
+ struct dvb_ca_slot *sl = &ca->slot_info[slot];
+
+ dprintk("CAMREADY IRQ slot:%i\n", slot);
+
+ if (sl->slot_state == DVB_CA_SLOTSTATE_WAITREADY) {
+ sl->slot_state = DVB_CA_SLOTSTATE_VALIDATE;
+ dvb_ca_en50221_thread_wakeup(ca);
+ }
+}
+EXPORT_SYMBOL(dvb_ca_en50221_camready_irq);
+
+/**
+ * dvb_ca_en50221_frda_irq - An FR or DA IRQ has occurred.
+ *
+ * @pubca: CA instance.
+ * @slot: Slot concerned.
+ */
+void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot)
+{
+ struct dvb_ca_private *ca = pubca->private;
+ struct dvb_ca_slot *sl = &ca->slot_info[slot];
+ int flags;
+
+ dprintk("FR/DA IRQ slot:%i\n", slot);
+
+ switch (sl->slot_state) {
+ case DVB_CA_SLOTSTATE_LINKINIT:
+ flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS);
+ if (flags & STATUSREG_DA) {
+ dprintk("CAM supports DA IRQ\n");
+ sl->da_irq_supported = 1;
+ }
+ break;
+
+ case DVB_CA_SLOTSTATE_RUNNING:
+ if (ca->open)
+ dvb_ca_en50221_thread_wakeup(ca);
+ break;
+ }
+}
+EXPORT_SYMBOL(dvb_ca_en50221_frda_irq);
+
+/* ************************************************************************** */
+/* EN50221 thread functions */
+
+/**
+ * dvb_ca_en50221_thread_wakeup - Wake up the DVB CA thread
+ *
+ * @ca: CA instance.
+ */
+static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca)
+{
+ dprintk("%s\n", __func__);
+
+ ca->wakeup = 1;
+ mb();
+ wake_up_process(ca->thread);
+}
+
+/**
+ * dvb_ca_en50221_thread_update_delay - Update the delay used by the thread.
+ *
+ * @ca: CA instance.
+ */
+static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private *ca)
+{
+ int delay;
+ int curdelay = 100000000;
+ int slot;
+
+ /*
+ * Beware of too high polling frequency, because one polling
+ * call might take several hundred milliseconds until timeout!
+ */
+ for (slot = 0; slot < ca->slot_count; slot++) {
+ struct dvb_ca_slot *sl = &ca->slot_info[slot];
+
+ switch (sl->slot_state) {
+ default:
+ case DVB_CA_SLOTSTATE_NONE:
+ delay = HZ * 60; /* 60s */
+ if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
+ delay = HZ * 5; /* 5s */
+ break;
+ case DVB_CA_SLOTSTATE_INVALID:
+ delay = HZ * 60; /* 60s */
+ if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
+ delay = HZ / 10; /* 100ms */
+ break;
+
+ case DVB_CA_SLOTSTATE_UNINITIALISED:
+ case DVB_CA_SLOTSTATE_WAITREADY:
+ case DVB_CA_SLOTSTATE_VALIDATE:
+ case DVB_CA_SLOTSTATE_WAITFR:
+ case DVB_CA_SLOTSTATE_LINKINIT:
+ delay = HZ / 10; /* 100ms */
+ break;
+
+ case DVB_CA_SLOTSTATE_RUNNING:
+ delay = HZ * 60; /* 60s */
+ if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
+ delay = HZ / 10; /* 100ms */
+ if (ca->open) {
+ if ((!sl->da_irq_supported) ||
+ (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA)))
+ delay = HZ / 10; /* 100ms */
+ }
+ break;
+ }
+
+ if (delay < curdelay)
+ curdelay = delay;
+ }
+
+ ca->delay = curdelay;
+}
+
+/**
+ * dvb_ca_en50221_poll_cam_gone - Poll if the CAM is gone.
+ *
+ * @ca: CA instance.
+ * @slot: Slot to process.
+ * return:: 0 .. no change
+ * 1 .. CAM state changed
+ */
+
+static int dvb_ca_en50221_poll_cam_gone(struct dvb_ca_private *ca, int slot)
+{
+ int changed = 0;
+ int status;
+
+ /*
+ * we need this extra check for annoying interfaces like the
+ * budget-av
+ */
+ if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) &&
+ (ca->pub->poll_slot_status)) {
+ status = ca->pub->poll_slot_status(ca->pub, slot, 0);
+ if (!(status &
+ DVB_CA_EN50221_POLL_CAM_PRESENT)) {
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
+ dvb_ca_en50221_thread_update_delay(ca);
+ changed = 1;
+ }
+ }
+ return changed;
+}
+
+/**
+ * dvb_ca_en50221_thread_state_machine - Thread state machine for one CA slot
+ * to perform the data transfer.
+ *
+ * @ca: CA instance.
+ * @slot: Slot to process.
+ */
+static void dvb_ca_en50221_thread_state_machine(struct dvb_ca_private *ca,
+ int slot)
+{
+ struct dvb_ca_slot *sl = &ca->slot_info[slot];
+ int flags;
+ int pktcount;
+ void *rxbuf;
+
+ mutex_lock(&sl->slot_lock);
+
+ /* check the cam status + deal with CAMCHANGEs */
+ while (dvb_ca_en50221_check_camstatus(ca, slot)) {
+ /* clear down an old CI slot if necessary */
+ if (sl->slot_state != DVB_CA_SLOTSTATE_NONE)
+ dvb_ca_en50221_slot_shutdown(ca, slot);
+
+ /* if a CAM is NOW present, initialise it */
+ if (sl->camchange_type == DVB_CA_EN50221_CAMCHANGE_INSERTED)
+ sl->slot_state = DVB_CA_SLOTSTATE_UNINITIALISED;
+
+ /* we've handled one CAMCHANGE */
+ dvb_ca_en50221_thread_update_delay(ca);
+ atomic_dec(&sl->camchange_count);
+ }
+
+ /* CAM state machine */
+ switch (sl->slot_state) {
+ case DVB_CA_SLOTSTATE_NONE:
+ case DVB_CA_SLOTSTATE_INVALID:
+ /* no action needed */
+ break;
+
+ case DVB_CA_SLOTSTATE_UNINITIALISED:
+ sl->slot_state = DVB_CA_SLOTSTATE_WAITREADY;
+ ca->pub->slot_reset(ca->pub, slot);
+ sl->timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
+ break;
+
+ case DVB_CA_SLOTSTATE_WAITREADY:
+ if (time_after(jiffies, sl->timeout)) {
+ pr_err("dvb_ca adaptor %d: PC card did not respond :(\n",
+ ca->dvbdev->adapter->num);
+ sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+ /*
+ * no other action needed; will automatically change state when
+ * ready
+ */
+ break;
+
+ case DVB_CA_SLOTSTATE_VALIDATE:
+ if (dvb_ca_en50221_parse_attributes(ca, slot) != 0) {
+ if (dvb_ca_en50221_poll_cam_gone(ca, slot))
+ break;
+
+ pr_err("dvb_ca adapter %d: Invalid PC card inserted :(\n",
+ ca->dvbdev->adapter->num);
+ sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+ if (dvb_ca_en50221_set_configoption(ca, slot) != 0) {
+ pr_err("dvb_ca adapter %d: Unable to initialise CAM :(\n",
+ ca->dvbdev->adapter->num);
+ sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+ if (ca->pub->write_cam_control(ca->pub, slot,
+ CTRLIF_COMMAND,
+ CMDREG_RS) != 0) {
+ pr_err("dvb_ca adapter %d: Unable to reset CAM IF\n",
+ ca->dvbdev->adapter->num);
+ sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+ dprintk("DVB CAM validated successfully\n");
+
+ sl->timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
+ sl->slot_state = DVB_CA_SLOTSTATE_WAITFR;
+ ca->wakeup = 1;
+ break;
+
+ case DVB_CA_SLOTSTATE_WAITFR:
+ if (time_after(jiffies, sl->timeout)) {
+ pr_err("dvb_ca adapter %d: DVB CAM did not respond :(\n",
+ ca->dvbdev->adapter->num);
+ sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+
+ flags = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+ if (flags & STATUSREG_FR) {
+ sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ ca->wakeup = 1;
+ }
+ break;
+
+ case DVB_CA_SLOTSTATE_LINKINIT:
+ if (dvb_ca_en50221_link_init(ca, slot) != 0) {
+ if (dvb_ca_en50221_poll_cam_gone(ca, slot))
+ break;
+
+ pr_err("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n",
+ ca->dvbdev->adapter->num);
+ sl->slot_state = DVB_CA_SLOTSTATE_UNINITIALISED;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+
+ if (!sl->rx_buffer.data) {
+ rxbuf = vmalloc(RX_BUFFER_SIZE);
+ if (!rxbuf) {
+ pr_err("dvb_ca adapter %d: Unable to allocate CAM rx buffer :(\n",
+ ca->dvbdev->adapter->num);
+ sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+ dvb_ringbuffer_init(&sl->rx_buffer, rxbuf,
+ RX_BUFFER_SIZE);
+ }
+
+ ca->pub->slot_ts_enable(ca->pub, slot);
+ sl->slot_state = DVB_CA_SLOTSTATE_RUNNING;
+ dvb_ca_en50221_thread_update_delay(ca);
+ pr_info("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n",
+ ca->dvbdev->adapter->num);
+ break;
+
+ case DVB_CA_SLOTSTATE_RUNNING:
+ if (!ca->open)
+ break;
+
+ /* poll slots for data */
+ pktcount = 0;
+ while (dvb_ca_en50221_read_data(ca, slot, NULL, 0) > 0) {
+ if (!ca->open)
+ break;
+
+ /*
+ * if a CAMCHANGE occurred at some point, do not do any
+ * more processing of this slot
+ */
+ if (dvb_ca_en50221_check_camstatus(ca, slot)) {
+ /*
+ * we don't want to sleep on the next iteration
+ * so we can handle the cam change
+ */
+ ca->wakeup = 1;
+ break;
+ }
+
+ /* check if we've hit our limit this time */
+ if (++pktcount >= MAX_RX_PACKETS_PER_ITERATION) {
+ /*
+ * don't sleep; there is likely to be more data
+ * to read
+ */
+ ca->wakeup = 1;
+ break;
+ }
+ }
+ break;
+ }
+
+ mutex_unlock(&sl->slot_lock);
+}
+
+/*
+ * Kernel thread which monitors CA slots for CAM changes, and performs data
+ * transfers.
+ */
+static int dvb_ca_en50221_thread(void *data)
+{
+ struct dvb_ca_private *ca = data;
+ int slot;
+
+ dprintk("%s\n", __func__);
+
+ /* choose the correct initial delay */
+ dvb_ca_en50221_thread_update_delay(ca);
+
+ /* main loop */
+ while (!kthread_should_stop()) {
+ /* sleep for a bit */
+ if (!ca->wakeup) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(ca->delay);
+ if (kthread_should_stop())
+ return 0;
+ }
+ ca->wakeup = 0;
+
+ /* go through all the slots processing them */
+ for (slot = 0; slot < ca->slot_count; slot++)
+ dvb_ca_en50221_thread_state_machine(ca, slot);
+ }
+
+ return 0;
+}
+
+/* ************************************************************************** */
+/* EN50221 IO interface functions */
+
+/**
+ * dvb_ca_en50221_io_do_ioctl - Real ioctl implementation.
+ *
+ * @file: File concerned.
+ * @cmd: IOCTL command.
+ * @parg: Associated argument.
+ *
+ * NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them.
+ *
+ * return: 0 on success, <0 on error.
+ */
+static int dvb_ca_en50221_io_do_ioctl(struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_ca_private *ca = dvbdev->priv;
+ int err = 0;
+ int slot;
+
+ dprintk("%s\n", __func__);
+
+ if (mutex_lock_interruptible(&ca->ioctl_mutex))
+ return -ERESTARTSYS;
+
+ switch (cmd) {
+ case CA_RESET:
+ for (slot = 0; slot < ca->slot_count; slot++) {
+ struct dvb_ca_slot *sl = &ca->slot_info[slot];
+
+ mutex_lock(&sl->slot_lock);
+ if (sl->slot_state != DVB_CA_SLOTSTATE_NONE) {
+ dvb_ca_en50221_slot_shutdown(ca, slot);
+ if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)
+ dvb_ca_en50221_camchange_irq(ca->pub,
+ slot,
+ DVB_CA_EN50221_CAMCHANGE_INSERTED);
+ }
+ mutex_unlock(&sl->slot_lock);
+ }
+ ca->next_read_slot = 0;
+ dvb_ca_en50221_thread_wakeup(ca);
+ break;
+
+ case CA_GET_CAP: {
+ struct ca_caps *caps = parg;
+
+ caps->slot_num = ca->slot_count;
+ caps->slot_type = CA_CI_LINK;
+ caps->descr_num = 0;
+ caps->descr_type = 0;
+ break;
+ }
+
+ case CA_GET_SLOT_INFO: {
+ struct ca_slot_info *info = parg;
+ struct dvb_ca_slot *sl;
+
+ slot = info->num;
+ if ((slot >= ca->slot_count) || (slot < 0)) {
+ err = -EINVAL;
+ goto out_unlock;
+ }
+ slot = array_index_nospec(slot, ca->slot_count);
+
+ info->type = CA_CI_LINK;
+ info->flags = 0;
+ sl = &ca->slot_info[slot];
+ if ((sl->slot_state != DVB_CA_SLOTSTATE_NONE) &&
+ (sl->slot_state != DVB_CA_SLOTSTATE_INVALID)) {
+ info->flags = CA_CI_MODULE_PRESENT;
+ }
+ if (sl->slot_state == DVB_CA_SLOTSTATE_RUNNING)
+ info->flags |= CA_CI_MODULE_READY;
+ break;
+ }
+
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+out_unlock:
+ mutex_unlock(&ca->ioctl_mutex);
+ return err;
+}
+
+/**
+ * dvb_ca_en50221_io_ioctl - Wrapper for ioctl implementation.
+ *
+ * @file: File concerned.
+ * @cmd: IOCTL command.
+ * @arg: Associated argument.
+ *
+ * return: 0 on success, <0 on error.
+ */
+static long dvb_ca_en50221_io_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return dvb_usercopy(file, cmd, arg, dvb_ca_en50221_io_do_ioctl);
+}
+
+/**
+ * dvb_ca_en50221_io_write - Implementation of write() syscall.
+ *
+ * @file: File structure.
+ * @buf: Source buffer.
+ * @count: Size of source buffer.
+ * @ppos: Position in file (ignored).
+ *
+ * return: Number of bytes read, or <0 on error.
+ */
+static ssize_t dvb_ca_en50221_io_write(struct file *file,
+ const char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_ca_private *ca = dvbdev->priv;
+ struct dvb_ca_slot *sl;
+ u8 slot, connection_id;
+ int status;
+ u8 fragbuf[HOST_LINK_BUF_SIZE];
+ int fragpos = 0;
+ int fraglen;
+ unsigned long timeout;
+ int written;
+
+ dprintk("%s\n", __func__);
+
+ /*
+ * Incoming packet has a 2 byte header.
+ * hdr[0] = slot_id, hdr[1] = connection_id
+ */
+ if (count < 2)
+ return -EINVAL;
+
+ /* extract slot & connection id */
+ if (copy_from_user(&slot, buf, 1))
+ return -EFAULT;
+ if (copy_from_user(&connection_id, buf + 1, 1))
+ return -EFAULT;
+ buf += 2;
+ count -= 2;
+
+ if (slot >= ca->slot_count)
+ return -EINVAL;
+ slot = array_index_nospec(slot, ca->slot_count);
+ sl = &ca->slot_info[slot];
+
+ /* check if the slot is actually running */
+ if (sl->slot_state != DVB_CA_SLOTSTATE_RUNNING)
+ return -EINVAL;
+
+ /* fragment the packets & store in the buffer */
+ while (fragpos < count) {
+ fraglen = sl->link_buf_size - 2;
+ if (fraglen < 0)
+ break;
+ if (fraglen > HOST_LINK_BUF_SIZE - 2)
+ fraglen = HOST_LINK_BUF_SIZE - 2;
+ if ((count - fragpos) < fraglen)
+ fraglen = count - fragpos;
+
+ fragbuf[0] = connection_id;
+ fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00;
+ status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen);
+ if (status) {
+ status = -EFAULT;
+ goto exit;
+ }
+
+ timeout = jiffies + HZ / 2;
+ written = 0;
+ while (!time_after(jiffies, timeout)) {
+ /*
+ * check the CAM hasn't been removed/reset in the
+ * meantime
+ */
+ if (sl->slot_state != DVB_CA_SLOTSTATE_RUNNING) {
+ status = -EIO;
+ goto exit;
+ }
+
+ mutex_lock(&sl->slot_lock);
+ status = dvb_ca_en50221_write_data(ca, slot, fragbuf,
+ fraglen + 2, 0);
+ mutex_unlock(&sl->slot_lock);
+ if (status == (fraglen + 2)) {
+ written = 1;
+ break;
+ }
+ if (status != -EAGAIN)
+ goto exit;
+
+ usleep_range(1000, 1100);
+ }
+ if (!written) {
+ status = -EIO;
+ goto exit;
+ }
+
+ fragpos += fraglen;
+ }
+ status = count + 2;
+
+exit:
+ return status;
+}
+
+/*
+ * Condition for waking up in dvb_ca_en50221_io_read_condition
+ */
+static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private *ca,
+ int *result, int *_slot)
+{
+ int slot;
+ int slot_count = 0;
+ int idx;
+ size_t fraglen;
+ int connection_id = -1;
+ int found = 0;
+ u8 hdr[2];
+
+ slot = ca->next_read_slot;
+ while ((slot_count < ca->slot_count) && (!found)) {
+ struct dvb_ca_slot *sl = &ca->slot_info[slot];
+
+ if (sl->slot_state != DVB_CA_SLOTSTATE_RUNNING)
+ goto nextslot;
+
+ if (!sl->rx_buffer.data)
+ return 0;
+
+ idx = dvb_ringbuffer_pkt_next(&sl->rx_buffer, -1, &fraglen);
+ while (idx != -1) {
+ dvb_ringbuffer_pkt_read(&sl->rx_buffer, idx, 0, hdr, 2);
+ if (connection_id == -1)
+ connection_id = hdr[0];
+ if ((hdr[0] == connection_id) &&
+ ((hdr[1] & 0x80) == 0)) {
+ *_slot = slot;
+ found = 1;
+ break;
+ }
+
+ idx = dvb_ringbuffer_pkt_next(&sl->rx_buffer, idx,
+ &fraglen);
+ }
+
+nextslot:
+ slot = (slot + 1) % ca->slot_count;
+ slot_count++;
+ }
+
+ ca->next_read_slot = slot;
+ return found;
+}
+
+/**
+ * dvb_ca_en50221_io_read - Implementation of read() syscall.
+ *
+ * @file: File structure.
+ * @buf: Destination buffer.
+ * @count: Size of destination buffer.
+ * @ppos: Position in file (ignored).
+ *
+ * return: Number of bytes read, or <0 on error.
+ */
+static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_ca_private *ca = dvbdev->priv;
+ struct dvb_ca_slot *sl;
+ int status;
+ int result = 0;
+ u8 hdr[2];
+ int slot;
+ int connection_id = -1;
+ size_t idx, idx2;
+ int last_fragment = 0;
+ size_t fraglen;
+ int pktlen;
+ int dispose = 0;
+
+ dprintk("%s\n", __func__);
+
+ /*
+ * Outgoing packet has a 2 byte header.
+ * hdr[0] = slot_id, hdr[1] = connection_id
+ */
+ if (count < 2)
+ return -EINVAL;
+
+ /* wait for some data */
+ status = dvb_ca_en50221_io_read_condition(ca, &result, &slot);
+ if (status == 0) {
+ /* if we're in nonblocking mode, exit immediately */
+ if (file->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+
+ /* wait for some data */
+ status = wait_event_interruptible(ca->wait_queue,
+ dvb_ca_en50221_io_read_condition
+ (ca, &result, &slot));
+ }
+ if ((status < 0) || (result < 0)) {
+ if (result)
+ return result;
+ return status;
+ }
+
+ sl = &ca->slot_info[slot];
+ idx = dvb_ringbuffer_pkt_next(&sl->rx_buffer, -1, &fraglen);
+ pktlen = 2;
+ do {
+ if (idx == -1) {
+ pr_err("dvb_ca adapter %d: BUG: read packet ended before last_fragment encountered\n",
+ ca->dvbdev->adapter->num);
+ status = -EIO;
+ goto exit;
+ }
+
+ dvb_ringbuffer_pkt_read(&sl->rx_buffer, idx, 0, hdr, 2);
+ if (connection_id == -1)
+ connection_id = hdr[0];
+ if (hdr[0] == connection_id) {
+ if (pktlen < count) {
+ if ((pktlen + fraglen - 2) > count)
+ fraglen = count - pktlen;
+ else
+ fraglen -= 2;
+
+ status =
+ dvb_ringbuffer_pkt_read_user(&sl->rx_buffer,
+ idx, 2,
+ buf + pktlen,
+ fraglen);
+ if (status < 0)
+ goto exit;
+
+ pktlen += fraglen;
+ }
+
+ if ((hdr[1] & 0x80) == 0)
+ last_fragment = 1;
+ dispose = 1;
+ }
+
+ idx2 = dvb_ringbuffer_pkt_next(&sl->rx_buffer, idx, &fraglen);
+ if (dispose)
+ dvb_ringbuffer_pkt_dispose(&sl->rx_buffer, idx);
+ idx = idx2;
+ dispose = 0;
+ } while (!last_fragment);
+
+ hdr[0] = slot;
+ hdr[1] = connection_id;
+ status = copy_to_user(buf, hdr, 2);
+ if (status) {
+ status = -EFAULT;
+ goto exit;
+ }
+ status = pktlen;
+
+exit:
+ return status;
+}
+
+/**
+ * dvb_ca_en50221_io_open - Implementation of file open syscall.
+ *
+ * @inode: Inode concerned.
+ * @file: File concerned.
+ *
+ * return: 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_ca_private *ca = dvbdev->priv;
+ int err;
+ int i;
+
+ dprintk("%s\n", __func__);
+
+ mutex_lock(&ca->remove_mutex);
+
+ if (ca->exit) {
+ mutex_unlock(&ca->remove_mutex);
+ return -ENODEV;
+ }
+
+ if (!try_module_get(ca->pub->owner)) {
+ mutex_unlock(&ca->remove_mutex);
+ return -EIO;
+ }
+
+ err = dvb_generic_open(inode, file);
+ if (err < 0) {
+ module_put(ca->pub->owner);
+ mutex_unlock(&ca->remove_mutex);
+ return err;
+ }
+
+ for (i = 0; i < ca->slot_count; i++) {
+ struct dvb_ca_slot *sl = &ca->slot_info[i];
+
+ if (sl->slot_state == DVB_CA_SLOTSTATE_RUNNING) {
+ if (!sl->rx_buffer.data) {
+ /*
+ * it is safe to call this here without locks
+ * because ca->open == 0. Data is not read in
+ * this case
+ */
+ dvb_ringbuffer_flush(&sl->rx_buffer);
+ }
+ }
+ }
+
+ ca->open = 1;
+ dvb_ca_en50221_thread_update_delay(ca);
+ dvb_ca_en50221_thread_wakeup(ca);
+
+ dvb_ca_private_get(ca);
+
+ mutex_unlock(&ca->remove_mutex);
+ return 0;
+}
+
+/**
+ * dvb_ca_en50221_io_release - Implementation of file close syscall.
+ *
+ * @inode: Inode concerned.
+ * @file: File concerned.
+ *
+ * return: 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_ca_private *ca = dvbdev->priv;
+ int err;
+
+ dprintk("%s\n", __func__);
+
+ mutex_lock(&ca->remove_mutex);
+
+ /* mark the CA device as closed */
+ ca->open = 0;
+ dvb_ca_en50221_thread_update_delay(ca);
+
+ err = dvb_generic_release(inode, file);
+
+ module_put(ca->pub->owner);
+
+ dvb_ca_private_put(ca);
+
+ if (dvbdev->users == 1 && ca->exit == 1) {
+ mutex_unlock(&ca->remove_mutex);
+ wake_up(&dvbdev->wait_queue);
+ } else {
+ mutex_unlock(&ca->remove_mutex);
+ }
+
+ return err;
+}
+
+/**
+ * dvb_ca_en50221_io_poll - Implementation of poll() syscall.
+ *
+ * @file: File concerned.
+ * @wait: poll wait table.
+ *
+ * return: Standard poll mask.
+ */
+static __poll_t dvb_ca_en50221_io_poll(struct file *file, poll_table *wait)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_ca_private *ca = dvbdev->priv;
+ __poll_t mask = 0;
+ int slot;
+ int result = 0;
+
+ dprintk("%s\n", __func__);
+
+ poll_wait(file, &ca->wait_queue, wait);
+
+ if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1)
+ mask |= EPOLLIN;
+
+ /* if there is something, return now */
+ if (mask)
+ return mask;
+
+ if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1)
+ mask |= EPOLLIN;
+
+ return mask;
+}
+
+static const struct file_operations dvb_ca_fops = {
+ .owner = THIS_MODULE,
+ .read = dvb_ca_en50221_io_read,
+ .write = dvb_ca_en50221_io_write,
+ .unlocked_ioctl = dvb_ca_en50221_io_ioctl,
+ .open = dvb_ca_en50221_io_open,
+ .release = dvb_ca_en50221_io_release,
+ .poll = dvb_ca_en50221_io_poll,
+ .llseek = noop_llseek,
+};
+
+static const struct dvb_device dvbdev_ca = {
+ .priv = NULL,
+ .users = 1,
+ .readers = 1,
+ .writers = 1,
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+ .name = "dvb-ca-en50221",
+#endif
+ .fops = &dvb_ca_fops,
+};
+
+/* ************************************************************************** */
+/* Initialisation/shutdown functions */
+
+/**
+ * dvb_ca_en50221_init - Initialise a new DVB CA EN50221 interface device.
+ *
+ * @dvb_adapter: DVB adapter to attach the new CA device to.
+ * @pubca: The dvb_ca instance.
+ * @flags: Flags describing the CA device (DVB_CA_FLAG_*).
+ * @slot_count: Number of slots supported.
+ *
+ * return: 0 on success, nonzero on failure
+ */
+int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
+ struct dvb_ca_en50221 *pubca, int flags, int slot_count)
+{
+ int ret;
+ struct dvb_ca_private *ca = NULL;
+ int i;
+
+ dprintk("%s\n", __func__);
+
+ if (slot_count < 1)
+ return -EINVAL;
+
+ /* initialise the system data */
+ ca = kzalloc(sizeof(*ca), GFP_KERNEL);
+ if (!ca) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ kref_init(&ca->refcount);
+ ca->pub = pubca;
+ ca->flags = flags;
+ ca->slot_count = slot_count;
+ ca->slot_info = kcalloc(slot_count, sizeof(struct dvb_ca_slot),
+ GFP_KERNEL);
+ if (!ca->slot_info) {
+ ret = -ENOMEM;
+ goto free_ca;
+ }
+ init_waitqueue_head(&ca->wait_queue);
+ ca->open = 0;
+ ca->wakeup = 0;
+ ca->next_read_slot = 0;
+ pubca->private = ca;
+
+ /* register the DVB device */
+ ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca,
+ DVB_DEVICE_CA, 0);
+ if (ret)
+ goto free_slot_info;
+
+ /* now initialise each slot */
+ for (i = 0; i < slot_count; i++) {
+ struct dvb_ca_slot *sl = &ca->slot_info[i];
+
+ memset(sl, 0, sizeof(struct dvb_ca_slot));
+ sl->slot_state = DVB_CA_SLOTSTATE_NONE;
+ atomic_set(&sl->camchange_count, 0);
+ sl->camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+ mutex_init(&sl->slot_lock);
+ }
+
+ mutex_init(&ca->ioctl_mutex);
+ mutex_init(&ca->remove_mutex);
+
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ goto unregister_device;
+ }
+ mb();
+
+ /* create a kthread for monitoring this CA device */
+ ca->thread = kthread_run(dvb_ca_en50221_thread, ca, "kdvb-ca-%i:%i",
+ ca->dvbdev->adapter->num, ca->dvbdev->id);
+ if (IS_ERR(ca->thread)) {
+ ret = PTR_ERR(ca->thread);
+ pr_err("dvb_ca_init: failed to start kernel_thread (%d)\n",
+ ret);
+ goto unregister_device;
+ }
+ return 0;
+
+unregister_device:
+ dvb_unregister_device(ca->dvbdev);
+free_slot_info:
+ kfree(ca->slot_info);
+free_ca:
+ kfree(ca);
+exit:
+ pubca->private = NULL;
+ return ret;
+}
+EXPORT_SYMBOL(dvb_ca_en50221_init);
+
+/**
+ * dvb_ca_en50221_release - Release a DVB CA EN50221 interface device.
+ *
+ * @pubca: The associated dvb_ca instance.
+ */
+void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca)
+{
+ struct dvb_ca_private *ca = pubca->private;
+ int i;
+
+ dprintk("%s\n", __func__);
+
+ mutex_lock(&ca->remove_mutex);
+ ca->exit = 1;
+ mutex_unlock(&ca->remove_mutex);
+
+ if (ca->dvbdev->users < 1)
+ wait_event(ca->dvbdev->wait_queue,
+ ca->dvbdev->users == 1);
+
+ /* shutdown the thread if there was one */
+ kthread_stop(ca->thread);
+
+ for (i = 0; i < ca->slot_count; i++)
+ dvb_ca_en50221_slot_shutdown(ca, i);
+
+ dvb_remove_device(ca->dvbdev);
+ dvb_ca_private_put(ca);
+ pubca->private = NULL;
+}
+EXPORT_SYMBOL(dvb_ca_en50221_release);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c
index d82469f842e2..290fc7961647 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb-core/dvb_demux.c
@@ -1,27 +1,15 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* dvb_demux.c - DVB kernel demux API
*
* Copyright (C) 2000-2001 Ralph Metzler <ralph@convergence.de>
* & Marcus Metzler <marcus@convergence.de>
* for convergence integrated media GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
*/
-#include <linux/sched.h>
+#define pr_fmt(fmt) "dvb_demux: " fmt
+
+#include <linux/sched/signal.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
@@ -29,16 +17,10 @@
#include <linux/poll.h>
#include <linux/string.h>
#include <linux/crc32.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/div64.h>
-#include "dvb_demux.h"
-
-#define NOBUFS
-/*
-** #define DVB_DEMUX_SECTION_LOSS_LOG to monitor payload loss in the syslog
-*/
-// #define DVB_DEMUX_SECTION_LOSS_LOG
+#include <media/dvb_demux.h>
static int dvb_demux_tscheck;
module_param(dvb_demux_tscheck, int, 0644);
@@ -50,9 +32,28 @@ module_param(dvb_demux_speedcheck, int, 0644);
MODULE_PARM_DESC(dvb_demux_speedcheck,
"enable transport stream speed check");
-#define dprintk_tscheck(x...) do { \
- if (dvb_demux_tscheck && printk_ratelimit()) \
- printk(x); \
+static int dvb_demux_feed_err_pkts = 1;
+module_param(dvb_demux_feed_err_pkts, int, 0644);
+MODULE_PARM_DESC(dvb_demux_feed_err_pkts,
+ "when set to 0, drop packets with the TEI bit set (1 by default)");
+
+#define dprintk(fmt, arg...) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), __func__, ##arg)
+
+#define dprintk_tscheck(x...) do { \
+ if (dvb_demux_tscheck && printk_ratelimit()) \
+ dprintk(x); \
+} while (0)
+
+#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
+# define dprintk_sect_loss(x...) dprintk(x)
+#else
+# define dprintk_sect_loss(x...)
+#endif
+
+#define set_buf_flags(__feed, __flag) \
+ do { \
+ (__feed)->buffer_flags |= (__flag); \
} while (0)
/******************************************************************************
@@ -104,28 +105,30 @@ static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed,
{
int count = payload(buf);
int p;
- //int ccok;
- //u8 cc;
+ int ccok;
+ u8 cc;
if (count == 0)
return -1;
p = 188 - count;
- /*
cc = buf[3] & 0x0f;
ccok = ((feed->cc + 1) & 0x0f) == cc;
+ if (!ccok) {
+ set_buf_flags(feed, DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
+ dprintk_sect_loss("missed packet: %d instead of %d!\n",
+ cc, (feed->cc + 1) & 0x0f);
+ }
feed->cc = cc;
- if (!ccok)
- printk("missed packet!\n");
- */
if (buf[1] & 0x40) // PUSI ?
feed->peslen = 0xfffa;
feed->peslen += count;
- return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK);
+ return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts,
+ &feed->buffer_flags);
}
static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed,
@@ -147,7 +150,7 @@ static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed,
return 0;
return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen,
- NULL, 0, &f->filter, DMX_OK);
+ NULL, 0, &f->filter, &feed->buffer_flags);
}
static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)
@@ -166,8 +169,10 @@ static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)
if (sec->check_crc) {
section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0);
if (section_syntax_indicator &&
- demux->check_crc32(feed, sec->secbuf, sec->seclen))
+ demux->check_crc32(feed, sec->secbuf, sec->seclen)) {
+ set_buf_flags(feed, DMX_BUFFER_FLAG_HAD_CRC32_DISCARD);
return -1;
+ }
}
do {
@@ -184,9 +189,8 @@ static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed)
{
struct dmx_section_feed *sec = &feed->feed.sec;
-#ifdef DVB_DEMUX_SECTION_LOSS_LOG
if (sec->secbufp < sec->tsfeedp) {
- int i, n = sec->tsfeedp - sec->secbufp;
+ int n = sec->tsfeedp - sec->secbufp;
/*
* Section padding is done with 0xff bytes entirely.
@@ -194,15 +198,13 @@ static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed)
* but just first and last.
*/
if (sec->secbuf[0] != 0xff || sec->secbuf[n - 1] != 0xff) {
- printk("dvb_demux.c section ts padding loss: %d/%d\n",
- n, sec->tsfeedp);
- printk("dvb_demux.c pad data:");
- for (i = 0; i < n; i++)
- printk(" %02x", sec->secbuf[i]);
- printk("\n");
+ set_buf_flags(feed,
+ DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
+ dprintk_sect_loss("section ts padding loss: %d/%d\n",
+ n, sec->tsfeedp);
+ dprintk_sect_loss("pad data: %*ph\n", n, sec->secbuf);
}
}
-#endif
sec->tsfeedp = sec->secbufp = sec->seclen = 0;
sec->secbuf = sec->secbuf_base;
@@ -221,27 +223,26 @@ static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed)
* when the second packet arrives.
*
* Fix:
- * when demux is started, let feed->pusi_seen = 0 to
+ * when demux is started, let feed->pusi_seen = false to
* prevent initial feeding of garbage from the end of
* previous section. When you for the first time see PUSI=1
- * then set feed->pusi_seen = 1
+ * then set feed->pusi_seen = true
*/
static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed,
const u8 *buf, u8 len)
{
struct dvb_demux *demux = feed->demux;
struct dmx_section_feed *sec = &feed->feed.sec;
- u16 limit, seclen, n;
+ u16 limit, seclen;
if (sec->tsfeedp >= DMX_MAX_SECFEED_SIZE)
return 0;
if (sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE) {
-#ifdef DVB_DEMUX_SECTION_LOSS_LOG
- printk("dvb_demux.c section buffer full loss: %d/%d\n",
- sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE,
- DMX_MAX_SECFEED_SIZE);
-#endif
+ set_buf_flags(feed, DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
+ dprintk_sect_loss("section buffer full loss: %d/%d\n",
+ sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE,
+ DMX_MAX_SECFEED_SIZE);
len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp;
}
@@ -261,7 +262,7 @@ static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed,
/* to be sure always set secbuf */
sec->secbuf = sec->secbuf_base + sec->secbufp;
- for (n = 0; sec->secbufp + 2 < limit; n++) {
+ while (sec->secbufp + 2 < limit) {
seclen = section_length(sec->secbuf);
if (seclen <= 0 || seclen > DMX_MAX_SECTION_SIZE
|| seclen + sec->secbufp > limit)
@@ -269,12 +270,13 @@ static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed,
sec->seclen = seclen;
sec->crc_val = ~0;
/* dump [secbuf .. secbuf+seclen) */
- if (feed->pusi_seen)
+ if (feed->pusi_seen) {
dvb_dmx_swfilter_section_feed(feed);
-#ifdef DVB_DEMUX_SECTION_LOSS_LOG
- else
- printk("dvb_demux.c pusi not seen, discarding section data\n");
-#endif
+ } else {
+ set_buf_flags(feed,
+ DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
+ dprintk_sect_loss("pusi not seen, discarding section data\n");
+ }
sec->secbufp += seclen; /* secbufp and secbuf moving together is */
sec->secbuf += seclen; /* redundant but saves pointer arithmetic */
}
@@ -298,7 +300,6 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
cc = buf[3] & 0x0f;
ccok = ((feed->cc + 1) & 0x0f) == cc;
- feed->cc = cc;
if (buf[3] & 0x20) {
/* adaption field present, check for discontinuity_indicator */
@@ -307,21 +308,34 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
}
if (!ccok || dc_i) {
-#ifdef DVB_DEMUX_SECTION_LOSS_LOG
- printk("dvb_demux.c discontinuity detected %d bytes lost\n",
- count);
+ if (dc_i) {
+ set_buf_flags(feed,
+ DMX_BUFFER_FLAG_DISCONTINUITY_INDICATOR);
+ dprintk_sect_loss("%d frame with disconnect indicator\n",
+ cc);
+ } else {
+ set_buf_flags(feed,
+ DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
+ dprintk_sect_loss("discontinuity: %d instead of %d. %d bytes lost\n",
+ cc, (feed->cc + 1) & 0x0f, count + 4);
+ }
/*
- * those bytes under sume circumstances will again be reported
+ * those bytes under some circumstances will again be reported
* in the following dvb_dmx_swfilter_section_new
*/
-#endif
+
/*
- * Discontinuity detected. Reset pusi_seen = 0 to
+ * Discontinuity detected. Reset pusi_seen to
* stop feeding of suspicious data until next PUSI=1 arrives
+ *
+ * FIXME: does it make sense if the MPEG-TS is the one
+ * reporting discontinuity?
*/
- feed->pusi_seen = 0;
+
+ feed->pusi_seen = false;
dvb_dmx_swfilter_section_new(feed);
}
+ feed->cc = cc;
if (buf[1] & 0x40) {
/* PUSI=1 (is set), section boundary is here */
@@ -333,16 +347,16 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
dvb_dmx_swfilter_section_copy_dump(feed, before,
before_len);
- /* before start of new section, set pusi_seen = 1 */
- feed->pusi_seen = 1;
+ /* before start of new section, set pusi_seen */
+ feed->pusi_seen = true;
dvb_dmx_swfilter_section_new(feed);
dvb_dmx_swfilter_section_copy_dump(feed, after,
after_len);
+ } else if (count > 0) {
+ set_buf_flags(feed,
+ DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
+ dprintk_sect_loss("PUSI=1 but %d bytes lost\n", count);
}
-#ifdef DVB_DEMUX_SECTION_LOSS_LOG
- else if (count > 0)
- printk("dvb_demux.c PUSI=1 but %d bytes lost\n", count);
-#endif
} else {
/* PUSI=0 (is not set), no section boundary */
dvb_dmx_swfilter_section_copy_dump(feed, &buf[p], count);
@@ -363,8 +377,9 @@ static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
dvb_dmx_swfilter_payload(feed, buf);
else
feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts,
- DMX_OK);
+ &feed->buffer_flags);
}
+ /* Used only on full-featured devices */
if (feed->ts_type & TS_DECODER)
if (feed->demux->write_to_decoder)
feed->demux->write_to_decoder(feed, buf, 188);
@@ -394,57 +409,70 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
int dvr_done = 0;
if (dvb_demux_speedcheck) {
- struct timespec cur_time, delta_time;
+ ktime_t cur_time;
u64 speed_bytes, speed_timedelta;
demux->speed_pkts_cnt++;
/* show speed every SPEED_PKTS_INTERVAL packets */
if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) {
- cur_time = current_kernel_time();
+ cur_time = ktime_get();
- if (demux->speed_last_time.tv_sec != 0 &&
- demux->speed_last_time.tv_nsec != 0) {
- delta_time = timespec_sub(cur_time,
- demux->speed_last_time);
+ if (ktime_to_ns(demux->speed_last_time) != 0) {
speed_bytes = (u64)demux->speed_pkts_cnt
* 188 * 8;
/* convert to 1024 basis */
speed_bytes = 1000 * div64_u64(speed_bytes,
1024);
- speed_timedelta =
- (u64)timespec_to_ns(&delta_time);
- speed_timedelta = div64_u64(speed_timedelta,
- 1000000); /* nsec -> usec */
- printk(KERN_INFO "TS speed %llu Kbits/sec \n",
+ speed_timedelta = ktime_ms_delta(cur_time,
+ demux->speed_last_time);
+ if (speed_timedelta)
+ dprintk("TS speed %llu Kbits/sec \n",
div64_u64(speed_bytes,
- speed_timedelta));
- };
+ speed_timedelta));
+ }
demux->speed_last_time = cur_time;
demux->speed_pkts_cnt = 0;
- };
- };
-
- if (demux->cnt_storage && dvb_demux_tscheck) {
- /* check pkt counter */
- if (pid < MAX_PID) {
- if (buf[1] & 0x80)
- dprintk_tscheck("TEI detected. "
- "PID=0x%x data1=0x%x\n",
- pid, buf[1]);
-
- if ((buf[3] & 0xf) != demux->cnt_storage[pid])
- dprintk_tscheck("TS packet counter mismatch. "
- "PID=0x%x expected 0x%x "
- "got 0x%x\n",
- pid, demux->cnt_storage[pid],
- buf[3] & 0xf);
-
- demux->cnt_storage[pid] = ((buf[3] & 0xf) + 1)&0xf;
- };
- /* end check */
- };
+ }
+ }
+
+ if (buf[1] & 0x80) {
+ list_for_each_entry(feed, &demux->feed_list, list_head) {
+ if ((feed->pid != pid) && (feed->pid != 0x2000))
+ continue;
+ set_buf_flags(feed, DMX_BUFFER_FLAG_TEI);
+ }
+ dprintk_tscheck("TEI detected. PID=0x%x data1=0x%x\n",
+ pid, buf[1]);
+ /* data in this packet can't be trusted - drop it unless
+ * module option dvb_demux_feed_err_pkts is set */
+ if (!dvb_demux_feed_err_pkts)
+ return;
+ } else /* if TEI bit is set, pid may be wrong- skip pkt counter */
+ if (demux->cnt_storage && dvb_demux_tscheck) {
+ /* check pkt counter */
+ if (pid < MAX_PID) {
+ if (buf[3] & 0x10)
+ demux->cnt_storage[pid] =
+ (demux->cnt_storage[pid] + 1) & 0xf;
+
+ if ((buf[3] & 0xf) != demux->cnt_storage[pid]) {
+ list_for_each_entry(feed, &demux->feed_list, list_head) {
+ if ((feed->pid != pid) && (feed->pid != 0x2000))
+ continue;
+ set_buf_flags(feed,
+ DMX_BUFFER_PKT_COUNTER_MISMATCH);
+ }
+
+ dprintk_tscheck("TS packet counter mismatch. PID=0x%x expected 0x%x got 0x%x\n",
+ pid, demux->cnt_storage[pid],
+ buf[3] & 0xf);
+ demux->cnt_storage[pid] = buf[3] & 0xf;
+ }
+ }
+ /* end check */
+ }
list_for_each_entry(feed, &demux->feed_list, list_head) {
if ((feed->pid != pid) && (feed->pid != 0x2000))
@@ -458,14 +486,17 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
if (feed->pid == pid)
dvb_dmx_swfilter_packet_type(feed, buf);
else if (feed->pid == 0x2000)
- feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+ feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts,
+ &feed->buffer_flags);
}
}
void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
size_t count)
{
- spin_lock(&demux->lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&demux->lock, flags);
while (count--) {
if (buf[0] == 0x47)
@@ -473,7 +504,7 @@ void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
buf += 188;
}
- spin_unlock(&demux->lock);
+ spin_unlock_irqrestore(&demux->lock, flags);
}
EXPORT_SYMBOL(dvb_dmx_swfilter_packets);
@@ -508,8 +539,9 @@ static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,
{
int p = 0, i, j;
const u8 *q;
+ unsigned long flags;
- spin_lock(&demux->lock);
+ spin_lock_irqsave(&demux->lock, flags);
if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */
i = demux->tsbufp;
@@ -553,7 +585,7 @@ static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,
}
bailout:
- spin_unlock(&demux->lock);
+ spin_unlock_irqrestore(&demux->lock, flags);
}
void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count)
@@ -570,11 +602,14 @@ EXPORT_SYMBOL(dvb_dmx_swfilter_204);
void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count)
{
- spin_lock(&demux->lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&demux->lock, flags);
- demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts, DMX_OK);
+ demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts,
+ &demux->feed->buffer_flags);
- spin_unlock(&demux->lock);
+ spin_unlock_irqrestore(&demux->lock, flags);
}
EXPORT_SYMBOL(dvb_dmx_swfilter_raw);
@@ -625,7 +660,7 @@ static void dvb_demux_feed_add(struct dvb_demux_feed *feed)
{
spin_lock_irq(&feed->demux->lock);
if (dvb_demux_feed_find(feed)) {
- printk(KERN_ERR "%s: feed already in list (type=%x state=%x pid=%x)\n",
+ pr_err("%s: feed already in list (type=%x state=%x pid=%x)\n",
__func__, feed->type, feed->state, feed->pid);
goto out;
}
@@ -639,7 +674,7 @@ static void dvb_demux_feed_del(struct dvb_demux_feed *feed)
{
spin_lock_irq(&feed->demux->lock);
if (!(dvb_demux_feed_find(feed))) {
- printk(KERN_ERR "%s: feed not in list (type=%x state=%x pid=%x)\n",
+ pr_err("%s: feed not in list (type=%x state=%x pid=%x)\n",
__func__, feed->type, feed->state, feed->pid);
goto out;
}
@@ -650,8 +685,7 @@ out:
}
static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type,
- enum dmx_ts_pes pes_type,
- size_t circular_buffer_size, struct timespec timeout)
+ enum dmx_ts_pes pes_type, ktime_t timeout)
{
struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
struct dvb_demux *demux = feed->demux;
@@ -663,7 +697,7 @@ static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type,
return -ERESTARTSYS;
if (ts_type & TS_DECODER) {
- if (pes_type >= DMX_TS_PES_OTHER) {
+ if (pes_type >= DMX_PES_OTHER) {
mutex_unlock(&demux->mutex);
return -EINVAL;
}
@@ -681,23 +715,10 @@ static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type,
dvb_demux_feed_add(feed);
feed->pid = pid;
- feed->buffer_size = circular_buffer_size;
feed->timeout = timeout;
feed->ts_type = ts_type;
feed->pes_type = pes_type;
- if (feed->buffer_size) {
-#ifdef NOBUFS
- feed->buffer = NULL;
-#else
- feed->buffer = vmalloc(feed->buffer_size);
- if (!feed->buffer) {
- mutex_unlock(&demux->mutex);
- return -ENOMEM;
- }
-#endif
- }
-
feed->state = DMX_STATE_READY;
mutex_unlock(&demux->mutex);
@@ -723,7 +744,8 @@ static int dmx_ts_feed_start_filtering(struct dmx_ts_feed *ts_feed)
return -ENODEV;
}
- if ((ret = demux->start_feed(feed)) < 0) {
+ ret = demux->start_feed(feed);
+ if (ret < 0) {
mutex_unlock(&demux->mutex);
return ret;
}
@@ -776,7 +798,8 @@ static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
if (mutex_lock_interruptible(&demux->mutex))
return -ERESTARTSYS;
- if (!(feed = dvb_dmx_feed_alloc(demux))) {
+ feed = dvb_dmx_feed_alloc(demux);
+ if (!feed) {
mutex_unlock(&demux->mutex);
return -EBUSY;
}
@@ -786,7 +809,7 @@ static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
feed->demux = demux;
feed->pid = 0xffff;
feed->peslen = 0xfffa;
- feed->buffer = NULL;
+ feed->buffer_flags = 0;
(*ts_feed) = &feed->feed.ts;
(*ts_feed)->parent = dmx;
@@ -796,7 +819,8 @@ static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
(*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering;
(*ts_feed)->set = dmx_ts_feed_set;
- if (!(feed->filter = dvb_dmx_filter_alloc(demux))) {
+ feed->filter = dvb_dmx_filter_alloc(demux);
+ if (!feed->filter) {
feed->state = DMX_STATE_FREE;
mutex_unlock(&demux->mutex);
return -EBUSY;
@@ -823,10 +847,6 @@ static int dvbdmx_release_ts_feed(struct dmx_demux *dmx,
mutex_unlock(&demux->mutex);
return -EINVAL;
}
-#ifndef NOBUFS
- vfree(feed->buffer);
- feed->buffer = NULL;
-#endif
feed->state = DMX_STATE_FREE;
feed->filter->state = DMX_STATE_FREE;
@@ -835,7 +855,7 @@ static int dvbdmx_release_ts_feed(struct dmx_demux *dmx,
feed->pid = 0xffff;
- if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_TS_PES_OTHER)
+ if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_PES_OTHER)
demux->pesfilter[feed->pes_type] = NULL;
mutex_unlock(&demux->mutex);
@@ -878,8 +898,7 @@ static int dmx_section_feed_allocate_filter(struct dmx_section_feed *feed,
}
static int dmx_section_feed_set(struct dmx_section_feed *feed,
- u16 pid, size_t circular_buffer_size,
- int check_crc)
+ u16 pid, int check_crc)
{
struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
@@ -893,19 +912,8 @@ static int dmx_section_feed_set(struct dmx_section_feed *feed,
dvb_demux_feed_add(dvbdmxfeed);
dvbdmxfeed->pid = pid;
- dvbdmxfeed->buffer_size = circular_buffer_size;
dvbdmxfeed->feed.sec.check_crc = check_crc;
-#ifdef NOBUFS
- dvbdmxfeed->buffer = NULL;
-#else
- dvbdmxfeed->buffer = vmalloc(dvbdmxfeed->buffer_size);
- if (!dvbdmxfeed->buffer) {
- mutex_unlock(&dvbdmx->mutex);
- return -ENOMEM;
- }
-#endif
-
dvbdmxfeed->state = DMX_STATE_READY;
mutex_unlock(&dvbdmx->mutex);
return 0;
@@ -918,18 +926,19 @@ static void prepare_secfilters(struct dvb_demux_feed *dvbdmxfeed)
struct dmx_section_filter *sf;
u8 mask, mode, doneq;
- if (!(f = dvbdmxfeed->filter))
+ f = dvbdmxfeed->filter;
+ if (!f)
return;
do {
sf = &f->filter;
- doneq = 0;
+ doneq = false;
for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
mode = sf->filter_mode[i];
mask = sf->filter_mask[i];
f->maskandmode[i] = mask & mode;
doneq |= f->maskandnotmode[i] = mask & ~mode;
}
- f->doneq = doneq ? 1 : 0;
+ f->doneq = doneq ? true : false;
} while ((f = f->next));
}
@@ -956,6 +965,7 @@ static int dmx_section_feed_start_filtering(struct dmx_section_feed *feed)
dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;
dvbdmxfeed->feed.sec.secbufp = 0;
dvbdmxfeed->feed.sec.seclen = 0;
+ dvbdmxfeed->pusi_seen = false;
if (!dvbdmx->start_feed) {
mutex_unlock(&dvbdmx->mutex);
@@ -964,7 +974,8 @@ static int dmx_section_feed_start_filtering(struct dmx_section_feed *feed)
prepare_secfilters(dvbdmxfeed);
- if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) {
+ ret = dvbdmx->start_feed(dvbdmxfeed);
+ if (ret < 0) {
mutex_unlock(&dvbdmx->mutex);
return ret;
}
@@ -1016,8 +1027,13 @@ static int dmx_section_feed_release_filter(struct dmx_section_feed *feed,
return -EINVAL;
}
- if (feed->is_filtering)
+ if (feed->is_filtering) {
+ /* release dvbdmx->mutex as far as it is
+ acquired by stop_filtering() itself */
+ mutex_unlock(&dvbdmx->mutex);
feed->stop_filtering(feed);
+ mutex_lock(&dvbdmx->mutex);
+ }
spin_lock_irq(&dvbdmx->lock);
f = dvbdmxfeed->filter;
@@ -1046,7 +1062,8 @@ static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
if (mutex_lock_interruptible(&dvbdmx->mutex))
return -ERESTARTSYS;
- if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) {
+ dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx);
+ if (!dvbdmxfeed) {
mutex_unlock(&dvbdmx->mutex);
return -EBUSY;
}
@@ -1055,11 +1072,11 @@ static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
dvbdmxfeed->cb.sec = callback;
dvbdmxfeed->demux = dvbdmx;
dvbdmxfeed->pid = 0xffff;
+ dvbdmxfeed->buffer_flags = 0;
dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;
dvbdmxfeed->feed.sec.secbufp = dvbdmxfeed->feed.sec.seclen = 0;
dvbdmxfeed->feed.sec.tsfeedp = 0;
dvbdmxfeed->filter = NULL;
- dvbdmxfeed->buffer = NULL;
(*feed) = &dvbdmxfeed->feed.sec;
(*feed)->is_filtering = 0;
@@ -1088,10 +1105,6 @@ static int dvbdmx_release_section_feed(struct dmx_demux *demux,
mutex_unlock(&dvbdmx->mutex);
return -EINVAL;
}
-#ifndef NOBUFS
- vfree(dvbdmxfeed->buffer);
- dvbdmxfeed->buffer = NULL;
-#endif
dvbdmxfeed->state = DMX_STATE_FREE;
dvb_demux_feed_del(dvbdmxfeed);
@@ -1216,7 +1229,7 @@ static int dvbdmx_disconnect_frontend(struct dmx_demux *demux)
return 0;
}
-static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids)
+static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 *pids)
{
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
@@ -1231,12 +1244,14 @@ int dvb_dmx_init(struct dvb_demux *dvbdemux)
dvbdemux->cnt_storage = NULL;
dvbdemux->users = 0;
- dvbdemux->filter = vmalloc(dvbdemux->filternum * sizeof(struct dvb_demux_filter));
+ dvbdemux->filter = vmalloc_array(dvbdemux->filternum,
+ sizeof(struct dvb_demux_filter));
if (!dvbdemux->filter)
return -ENOMEM;
- dvbdemux->feed = vmalloc(dvbdemux->feednum * sizeof(struct dvb_demux_feed));
+ dvbdemux->feed = vmalloc_array(dvbdemux->feednum,
+ sizeof(struct dvb_demux_feed));
if (!dvbdemux->feed) {
vfree(dvbdemux->filter);
dvbdemux->filter = NULL;
@@ -1253,11 +1268,11 @@ int dvb_dmx_init(struct dvb_demux *dvbdemux)
dvbdemux->cnt_storage = vmalloc(MAX_PID + 1);
if (!dvbdemux->cnt_storage)
- printk(KERN_WARNING "Couldn't allocate memory for TS/TEI check. Disabling it\n");
+ pr_warn("Couldn't allocate memory for TS/TEI check. Disabling it\n");
INIT_LIST_HEAD(&dvbdemux->frontend_list);
- for (i = 0; i < DMX_TS_PES_OTHER; i++) {
+ for (i = 0; i < DMX_PES_OTHER; i++) {
dvbdemux->pesfilter[i] = NULL;
dvbdemux->pids[i] = 0xffff;
}
diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c
new file mode 100644
index 000000000000..a05aa271a1ba
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -0,0 +1,3107 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * dvb_frontend.c: DVB frontend tuning interface/thread
+ *
+ * Copyright (C) 1999-2001 Ralph Metzler
+ * Marcus Metzler
+ * Holger Waechtler
+ * for convergence integrated media GmbH
+ *
+ * Copyright (C) 2004 Andrew de Quincey (tuning thread cleanup)
+ */
+
+/* Enables DVBv3 compatibility bits at the headers */
+#define __DVB_CORE__
+
+#define pr_fmt(fmt) "dvb_frontend: " fmt
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched/signal.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/semaphore.h>
+#include <linux/module.h>
+#include <linux/nospec.h>
+#include <linux/list.h>
+#include <linux/freezer.h>
+#include <linux/jiffies.h>
+#include <linux/kthread.h>
+#include <linux/ktime.h>
+#include <linux/compat.h>
+#include <asm/processor.h>
+
+#include <media/dvb_frontend.h>
+#include <media/dvbdev.h>
+#include <linux/dvb/version.h>
+
+static int dvb_frontend_debug;
+static int dvb_shutdown_timeout;
+static int dvb_force_auto_inversion;
+static int dvb_override_tune_delay;
+static int dvb_powerdown_on_sleep = 1;
+static int dvb_mfe_wait_time = 5;
+
+module_param_named(frontend_debug, dvb_frontend_debug, int, 0644);
+MODULE_PARM_DESC(frontend_debug, "Turn on/off frontend core debugging (default:off).");
+module_param(dvb_shutdown_timeout, int, 0644);
+MODULE_PARM_DESC(dvb_shutdown_timeout, "wait <shutdown_timeout> seconds after close() before suspending hardware");
+module_param(dvb_force_auto_inversion, int, 0644);
+MODULE_PARM_DESC(dvb_force_auto_inversion, "0: normal (default), 1: INVERSION_AUTO forced always");
+module_param(dvb_override_tune_delay, int, 0644);
+MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in milliseconds to wait for lock after a tune attempt");
+module_param(dvb_powerdown_on_sleep, int, 0644);
+MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB voltage off on sleep (default)");
+module_param(dvb_mfe_wait_time, int, 0644);
+MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to <mfe_wait_time> seconds on open() for multi-frontend to become available (default:5 seconds)");
+
+#define dprintk(fmt, arg...) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), __func__, ##arg)
+
+#define FESTATE_IDLE 1
+#define FESTATE_RETUNE 2
+#define FESTATE_TUNING_FAST 4
+#define FESTATE_TUNING_SLOW 8
+#define FESTATE_TUNED 16
+#define FESTATE_ZIGZAG_FAST 32
+#define FESTATE_ZIGZAG_SLOW 64
+#define FESTATE_DISEQC 128
+#define FESTATE_ERROR 256
+#define FESTATE_WAITFORLOCK (FESTATE_TUNING_FAST | FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW | FESTATE_DISEQC)
+#define FESTATE_SEARCHING_FAST (FESTATE_TUNING_FAST | FESTATE_ZIGZAG_FAST)
+#define FESTATE_SEARCHING_SLOW (FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_SLOW)
+#define FESTATE_LOSTLOCK (FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW)
+
+/*
+ * FESTATE_IDLE. No tuning parameters have been supplied and the loop is idling.
+ * FESTATE_RETUNE. Parameters have been supplied, but we have not yet performed the first tune.
+ * FESTATE_TUNING_FAST. Tuning parameters have been supplied and fast zigzag scan is in progress.
+ * FESTATE_TUNING_SLOW. Tuning parameters have been supplied. Fast zigzag failed, so we're trying again, but slower.
+ * FESTATE_TUNED. The frontend has successfully locked on.
+ * FESTATE_ZIGZAG_FAST. The lock has been lost, and a fast zigzag has been initiated to try and regain it.
+ * FESTATE_ZIGZAG_SLOW. The lock has been lost. Fast zigzag has been failed, so we're trying again, but slower.
+ * FESTATE_DISEQC. A DISEQC command has just been issued.
+ * FESTATE_WAITFORLOCK. When we're waiting for a lock.
+ * FESTATE_SEARCHING_FAST. When we're searching for a signal using a fast zigzag scan.
+ * FESTATE_SEARCHING_SLOW. When we're searching for a signal using a slow zigzag scan.
+ * FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again.
+ */
+
+static DEFINE_MUTEX(frontend_mutex);
+
+struct dvb_frontend_private {
+ /* thread/frontend values */
+ struct dvb_device *dvbdev;
+ struct dvb_frontend_parameters parameters_out;
+ struct dvb_fe_events events;
+ struct semaphore sem;
+ struct list_head list_head;
+ wait_queue_head_t wait_queue;
+ struct task_struct *thread;
+ unsigned long release_jiffies;
+ unsigned int wakeup;
+ enum fe_status status;
+ unsigned long tune_mode_flags;
+ unsigned int delay;
+ unsigned int reinitialise;
+ int tone;
+ int voltage;
+
+ /* swzigzag values */
+ unsigned int state;
+ unsigned int bending;
+ int lnb_drift;
+ unsigned int inversion;
+ unsigned int auto_step;
+ unsigned int auto_sub_step;
+ unsigned int started_auto_step;
+ unsigned int min_delay;
+ unsigned int max_drift;
+ unsigned int step_size;
+ int quality;
+ unsigned int check_wrapped;
+ enum dvbfe_search algo_status;
+
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+ struct media_pipeline pipe;
+#endif
+};
+
+static void dvb_frontend_invoke_release(struct dvb_frontend *fe,
+ void (*release)(struct dvb_frontend *fe));
+
+static void __dvb_frontend_free(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ if (fepriv)
+ dvb_device_put(fepriv->dvbdev);
+
+ dvb_frontend_invoke_release(fe, fe->ops.release);
+
+ kfree(fepriv);
+}
+
+static void dvb_frontend_free(struct kref *ref)
+{
+ struct dvb_frontend *fe =
+ container_of(ref, struct dvb_frontend, refcount);
+
+ __dvb_frontend_free(fe);
+}
+
+static void dvb_frontend_put(struct dvb_frontend *fe)
+{
+ /* call detach before dropping the reference count */
+ if (fe->ops.detach)
+ fe->ops.detach(fe);
+ /*
+ * Check if the frontend was registered, as otherwise
+ * kref was not initialized yet.
+ */
+ if (fe->frontend_priv)
+ kref_put(&fe->refcount, dvb_frontend_free);
+ else
+ __dvb_frontend_free(fe);
+}
+
+static void dvb_frontend_get(struct dvb_frontend *fe)
+{
+ kref_get(&fe->refcount);
+}
+
+static void dvb_frontend_wakeup(struct dvb_frontend *fe);
+static int dtv_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c,
+ struct dvb_frontend_parameters *p_out);
+static int
+dtv_property_legacy_params_sync(struct dvb_frontend *fe,
+ const struct dtv_frontend_properties *c,
+ struct dvb_frontend_parameters *p);
+
+static bool has_get_frontend(struct dvb_frontend *fe)
+{
+ return fe->ops.get_frontend;
+}
+
+/*
+ * Due to DVBv3 API calls, a delivery system should be mapped into one of
+ * the 4 DVBv3 delivery systems (FE_QPSK, FE_QAM, FE_OFDM or FE_ATSC),
+ * otherwise, a DVBv3 call will fail.
+ */
+enum dvbv3_emulation_type {
+ DVBV3_UNKNOWN,
+ DVBV3_QPSK,
+ DVBV3_QAM,
+ DVBV3_OFDM,
+ DVBV3_ATSC,
+};
+
+static enum dvbv3_emulation_type dvbv3_type(u32 delivery_system)
+{
+ switch (delivery_system) {
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ return DVBV3_QAM;
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ case SYS_TURBO:
+ case SYS_ISDBS:
+ case SYS_DSS:
+ return DVBV3_QPSK;
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ case SYS_ISDBT:
+ case SYS_DTMB:
+ return DVBV3_OFDM;
+ case SYS_ATSC:
+ case SYS_ATSCMH:
+ case SYS_DVBC_ANNEX_B:
+ return DVBV3_ATSC;
+ case SYS_UNDEFINED:
+ case SYS_ISDBC:
+ case SYS_DVBH:
+ case SYS_DAB:
+ default:
+ /*
+ * Doesn't know how to emulate those types and/or
+ * there's no frontend driver from this type yet
+ * with some emulation code, so, we're not sure yet how
+ * to handle them, or they're not compatible with a DVBv3 call.
+ */
+ return DVBV3_UNKNOWN;
+ }
+}
+
+static void dvb_frontend_add_event(struct dvb_frontend *fe,
+ enum fe_status status)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_fe_events *events = &fepriv->events;
+ struct dvb_frontend_event *e;
+ int wp;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ if ((status & FE_HAS_LOCK) && has_get_frontend(fe))
+ dtv_get_frontend(fe, c, &fepriv->parameters_out);
+
+ mutex_lock(&events->mtx);
+
+ wp = (events->eventw + 1) % MAX_EVENT;
+ if (wp == events->eventr) {
+ events->overflow = 1;
+ events->eventr = (events->eventr + 1) % MAX_EVENT;
+ }
+
+ e = &events->events[events->eventw];
+ e->status = status;
+ e->parameters = fepriv->parameters_out;
+
+ events->eventw = wp;
+
+ mutex_unlock(&events->mtx);
+
+ wake_up_interruptible(&events->wait_queue);
+}
+
+static int dvb_frontend_test_event(struct dvb_frontend_private *fepriv,
+ struct dvb_fe_events *events)
+{
+ int ret;
+
+ up(&fepriv->sem);
+ ret = events->eventw != events->eventr;
+ down(&fepriv->sem);
+
+ return ret;
+}
+
+static int dvb_frontend_get_event(struct dvb_frontend *fe,
+ struct dvb_frontend_event *event, int flags)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_fe_events *events = &fepriv->events;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ if (events->overflow) {
+ events->overflow = 0;
+ return -EOVERFLOW;
+ }
+
+ if (events->eventw == events->eventr) {
+ struct wait_queue_entry wait;
+ int ret = 0;
+
+ if (flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&events->wait_queue, &wait);
+ while (!dvb_frontend_test_event(fepriv, events)) {
+ wait_woken(&wait, TASK_INTERRUPTIBLE, 0);
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ }
+ remove_wait_queue(&events->wait_queue, &wait);
+ if (ret < 0)
+ return ret;
+ }
+
+ mutex_lock(&events->mtx);
+ *event = events->events[events->eventr];
+ events->eventr = (events->eventr + 1) % MAX_EVENT;
+ mutex_unlock(&events->mtx);
+
+ return 0;
+}
+
+static void dvb_frontend_clear_events(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_fe_events *events = &fepriv->events;
+
+ mutex_lock(&events->mtx);
+ events->eventr = events->eventw;
+ mutex_unlock(&events->mtx);
+}
+
+static void dvb_frontend_init(struct dvb_frontend *fe)
+{
+ dev_dbg(fe->dvb->device,
+ "%s: initialising adapter %i frontend %i (%s)...\n",
+ __func__, fe->dvb->num, fe->id, fe->ops.info.name);
+
+ if (fe->ops.init)
+ fe->ops.init(fe);
+ if (fe->ops.tuner_ops.init) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ fe->ops.tuner_ops.init(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+}
+
+void dvb_frontend_reinitialise(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ fepriv->reinitialise = 1;
+ dvb_frontend_wakeup(fe);
+}
+EXPORT_SYMBOL(dvb_frontend_reinitialise);
+
+static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepriv, int locked)
+{
+ int q2;
+ struct dvb_frontend *fe = fepriv->dvbdev->priv;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ if (locked)
+ (fepriv->quality) = (fepriv->quality * 220 + 36 * 256) / 256;
+ else
+ (fepriv->quality) = (fepriv->quality * 220 + 0) / 256;
+
+ q2 = fepriv->quality - 128;
+ q2 *= q2;
+
+ fepriv->delay = fepriv->min_delay + q2 * HZ / (128 * 128);
+}
+
+/**
+ * dvb_frontend_swzigzag_autotune - Performs automatic twiddling of frontend
+ * parameters.
+ *
+ * @fe: The frontend concerned.
+ * @check_wrapped: Checks if an iteration has completed.
+ * DO NOT SET ON THE FIRST ATTEMPT.
+ *
+ * return: Number of complete iterations that have been performed.
+ */
+static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wrapped)
+{
+ int autoinversion;
+ int ready = 0;
+ int fe_set_err = 0;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp;
+ int original_inversion = c->inversion;
+ u32 original_frequency = c->frequency;
+
+ /* are we using autoinversion? */
+ autoinversion = ((!(fe->ops.info.caps & FE_CAN_INVERSION_AUTO)) &&
+ (c->inversion == INVERSION_AUTO));
+
+ /* setup parameters correctly */
+ while (!ready) {
+ /* calculate the lnb_drift */
+ fepriv->lnb_drift = fepriv->auto_step * fepriv->step_size;
+
+ /* wrap the auto_step if we've exceeded the maximum drift */
+ if (fepriv->lnb_drift > fepriv->max_drift) {
+ fepriv->auto_step = 0;
+ fepriv->auto_sub_step = 0;
+ fepriv->lnb_drift = 0;
+ }
+
+ /* perform inversion and +/- zigzag */
+ switch (fepriv->auto_sub_step) {
+ case 0:
+ /* try with the current inversion and current drift setting */
+ ready = 1;
+ break;
+
+ case 1:
+ if (!autoinversion) break;
+
+ fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF;
+ ready = 1;
+ break;
+
+ case 2:
+ if (fepriv->lnb_drift == 0) break;
+
+ fepriv->lnb_drift = -fepriv->lnb_drift;
+ ready = 1;
+ break;
+
+ case 3:
+ if (fepriv->lnb_drift == 0) break;
+ if (!autoinversion) break;
+
+ fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF;
+ fepriv->lnb_drift = -fepriv->lnb_drift;
+ ready = 1;
+ break;
+
+ default:
+ fepriv->auto_step++;
+ fepriv->auto_sub_step = 0;
+ continue;
+ }
+
+ if (!ready) fepriv->auto_sub_step++;
+ }
+
+ /* if this attempt would hit where we started, indicate a complete
+ * iteration has occurred */
+ if ((fepriv->auto_step == fepriv->started_auto_step) &&
+ (fepriv->auto_sub_step == 0) && check_wrapped) {
+ return 1;
+ }
+
+ dev_dbg(fe->dvb->device,
+ "%s: drift:%i inversion:%i auto_step:%i auto_sub_step:%i started_auto_step:%i\n",
+ __func__, fepriv->lnb_drift, fepriv->inversion,
+ fepriv->auto_step, fepriv->auto_sub_step,
+ fepriv->started_auto_step);
+
+ /* set the frontend itself */
+ c->frequency += fepriv->lnb_drift;
+ if (autoinversion)
+ c->inversion = fepriv->inversion;
+ tmp = *c;
+ if (fe->ops.set_frontend)
+ fe_set_err = fe->ops.set_frontend(fe);
+ *c = tmp;
+ if (fe_set_err < 0) {
+ fepriv->state = FESTATE_ERROR;
+ return fe_set_err;
+ }
+
+ c->frequency = original_frequency;
+ c->inversion = original_inversion;
+
+ fepriv->auto_sub_step++;
+ return 0;
+}
+
+static void dvb_frontend_swzigzag(struct dvb_frontend *fe)
+{
+ enum fe_status s = FE_NONE;
+ int retval = 0;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp;
+
+ if (fepriv->max_drift)
+ dev_warn_once(fe->dvb->device,
+ "Frontend requested software zigzag, but didn't set the frequency step size\n");
+
+ /* if we've got no parameters, just keep idling */
+ if (fepriv->state & FESTATE_IDLE) {
+ fepriv->delay = 3 * HZ;
+ fepriv->quality = 0;
+ return;
+ }
+
+ /* in SCAN mode, we just set the frontend when asked and leave it alone */
+ if (fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT) {
+ if (fepriv->state & FESTATE_RETUNE) {
+ tmp = *c;
+ if (fe->ops.set_frontend)
+ retval = fe->ops.set_frontend(fe);
+ *c = tmp;
+ if (retval < 0)
+ fepriv->state = FESTATE_ERROR;
+ else
+ fepriv->state = FESTATE_TUNED;
+ }
+ fepriv->delay = 3 * HZ;
+ fepriv->quality = 0;
+ return;
+ }
+
+ /* get the frontend status */
+ if (fepriv->state & FESTATE_RETUNE) {
+ s = 0;
+ } else {
+ if (fe->ops.read_status)
+ fe->ops.read_status(fe, &s);
+ if (s != fepriv->status) {
+ dvb_frontend_add_event(fe, s);
+ fepriv->status = s;
+ }
+ }
+
+ /* if we're not tuned, and we have a lock, move to the TUNED state */
+ if ((fepriv->state & FESTATE_WAITFORLOCK) && (s & FE_HAS_LOCK)) {
+ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
+ fepriv->state = FESTATE_TUNED;
+
+ /* if we're tuned, then we have determined the correct inversion */
+ if ((!(fe->ops.info.caps & FE_CAN_INVERSION_AUTO)) &&
+ (c->inversion == INVERSION_AUTO)) {
+ c->inversion = fepriv->inversion;
+ }
+ return;
+ }
+
+ /* if we are tuned already, check we're still locked */
+ if (fepriv->state & FESTATE_TUNED) {
+ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
+
+ /* we're tuned, and the lock is still good... */
+ if (s & FE_HAS_LOCK) {
+ return;
+ } else { /* if we _WERE_ tuned, but now don't have a lock */
+ fepriv->state = FESTATE_ZIGZAG_FAST;
+ fepriv->started_auto_step = fepriv->auto_step;
+ fepriv->check_wrapped = 0;
+ }
+ }
+
+ /* don't actually do anything if we're in the LOSTLOCK state,
+ * the frontend is set to FE_CAN_RECOVER, and the max_drift is 0 */
+ if ((fepriv->state & FESTATE_LOSTLOCK) &&
+ (fe->ops.info.caps & FE_CAN_RECOVER) && (fepriv->max_drift == 0)) {
+ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
+ return;
+ }
+
+ /* don't do anything if we're in the DISEQC state, since this
+ * might be someone with a motorized dish controlled by DISEQC.
+ * If its actually a re-tune, there will be a SET_FRONTEND soon enough. */
+ if (fepriv->state & FESTATE_DISEQC) {
+ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
+ return;
+ }
+
+ /* if we're in the RETUNE state, set everything up for a brand
+ * new scan, keeping the current inversion setting, as the next
+ * tune is _very_ likely to require the same */
+ if (fepriv->state & FESTATE_RETUNE) {
+ fepriv->lnb_drift = 0;
+ fepriv->auto_step = 0;
+ fepriv->auto_sub_step = 0;
+ fepriv->started_auto_step = 0;
+ fepriv->check_wrapped = 0;
+ }
+
+ /* fast zigzag. */
+ if ((fepriv->state & FESTATE_SEARCHING_FAST) || (fepriv->state & FESTATE_RETUNE)) {
+ fepriv->delay = fepriv->min_delay;
+
+ /* perform a tune */
+ retval = dvb_frontend_swzigzag_autotune(fe,
+ fepriv->check_wrapped);
+ if (retval < 0) {
+ return;
+ } else if (retval) {
+ /* OK, if we've run out of trials at the fast speed.
+ * Drop back to slow for the _next_ attempt */
+ fepriv->state = FESTATE_SEARCHING_SLOW;
+ fepriv->started_auto_step = fepriv->auto_step;
+ return;
+ }
+ fepriv->check_wrapped = 1;
+
+ /* if we've just re-tuned, enter the ZIGZAG_FAST state.
+ * This ensures we cannot return from an
+ * FE_SET_FRONTEND ioctl before the first frontend tune
+ * occurs */
+ if (fepriv->state & FESTATE_RETUNE) {
+ fepriv->state = FESTATE_TUNING_FAST;
+ }
+ }
+
+ /* slow zigzag */
+ if (fepriv->state & FESTATE_SEARCHING_SLOW) {
+ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
+
+ /* Note: don't bother checking for wrapping; we stay in this
+ * state until we get a lock */
+ dvb_frontend_swzigzag_autotune(fe, 0);
+ }
+}
+
+static int dvb_frontend_is_exiting(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ if (fe->exit != DVB_FE_NO_EXIT)
+ return 1;
+
+ if (fepriv->dvbdev->writers == 1)
+ if (time_after_eq(jiffies, fepriv->release_jiffies +
+ dvb_shutdown_timeout * HZ))
+ return 1;
+
+ return 0;
+}
+
+static int dvb_frontend_should_wakeup(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ if (fepriv->wakeup) {
+ fepriv->wakeup = 0;
+ return 1;
+ }
+ return dvb_frontend_is_exiting(fe);
+}
+
+static void dvb_frontend_wakeup(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ fepriv->wakeup = 1;
+ wake_up_interruptible(&fepriv->wait_queue);
+}
+
+static int dvb_frontend_thread(void *data)
+{
+ struct dvb_frontend *fe = data;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ enum fe_status s = FE_NONE;
+ enum dvbfe_algo algo;
+ bool re_tune = false;
+ bool semheld = false;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ fepriv->check_wrapped = 0;
+ fepriv->quality = 0;
+ fepriv->delay = 3 * HZ;
+ fepriv->status = 0;
+ fepriv->wakeup = 0;
+ fepriv->reinitialise = 0;
+
+ dvb_frontend_init(fe);
+
+ set_freezable();
+ while (1) {
+ up(&fepriv->sem); /* is locked when we enter the thread... */
+ wait_event_freezable_timeout(fepriv->wait_queue,
+ dvb_frontend_should_wakeup(fe) ||
+ kthread_should_stop(),
+ fepriv->delay);
+
+ if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) {
+ /* got signal or quitting */
+ if (!down_interruptible(&fepriv->sem))
+ semheld = true;
+ fe->exit = DVB_FE_NORMAL_EXIT;
+ break;
+ }
+
+ if (down_interruptible(&fepriv->sem))
+ break;
+
+ if (fepriv->reinitialise) {
+ dvb_frontend_init(fe);
+ if (fe->ops.set_tone && fepriv->tone != -1)
+ fe->ops.set_tone(fe, fepriv->tone);
+ if (fe->ops.set_voltage && fepriv->voltage != -1)
+ fe->ops.set_voltage(fe, fepriv->voltage);
+ fepriv->reinitialise = 0;
+ }
+
+ /* do an iteration of the tuning loop */
+ if (fe->ops.get_frontend_algo) {
+ algo = fe->ops.get_frontend_algo(fe);
+ switch (algo) {
+ case DVBFE_ALGO_HW:
+ dev_dbg(fe->dvb->device, "%s: Frontend ALGO = DVBFE_ALGO_HW\n", __func__);
+
+ if (fepriv->state & FESTATE_RETUNE) {
+ dev_dbg(fe->dvb->device, "%s: Retune requested, FESTATE_RETUNE\n", __func__);
+ re_tune = true;
+ fepriv->state = FESTATE_TUNED;
+ } else {
+ re_tune = false;
+ }
+
+ if (fe->ops.tune)
+ fe->ops.tune(fe, re_tune, fepriv->tune_mode_flags, &fepriv->delay, &s);
+
+ if (s != fepriv->status && !(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) {
+ dev_dbg(fe->dvb->device, "%s: state changed, adding current state\n", __func__);
+ dvb_frontend_add_event(fe, s);
+ fepriv->status = s;
+ }
+ break;
+ case DVBFE_ALGO_SW:
+ dev_dbg(fe->dvb->device, "%s: Frontend ALGO = DVBFE_ALGO_SW\n", __func__);
+ dvb_frontend_swzigzag(fe);
+ break;
+ case DVBFE_ALGO_CUSTOM:
+ dev_dbg(fe->dvb->device, "%s: Frontend ALGO = DVBFE_ALGO_CUSTOM, state=%d\n", __func__, fepriv->state);
+ if (fepriv->state & FESTATE_RETUNE) {
+ dev_dbg(fe->dvb->device, "%s: Retune requested, FESTAT_RETUNE\n", __func__);
+ fepriv->state = FESTATE_TUNED;
+ }
+ /* Case where we are going to search for a carrier
+ * User asked us to retune again for some reason, possibly
+ * requesting a search with a new set of parameters
+ */
+ if (fepriv->algo_status & DVBFE_ALGO_SEARCH_AGAIN) {
+ if (fe->ops.search) {
+ fepriv->algo_status = fe->ops.search(fe);
+ /* We did do a search as was requested, the flags are
+ * now unset as well and has the flags wrt to search.
+ */
+ } else {
+ fepriv->algo_status &= ~DVBFE_ALGO_SEARCH_AGAIN;
+ }
+ }
+ /* Track the carrier if the search was successful */
+ if (fepriv->algo_status != DVBFE_ALGO_SEARCH_SUCCESS) {
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+ fepriv->delay = HZ / 2;
+ }
+ dtv_property_legacy_params_sync(fe, c, &fepriv->parameters_out);
+ fe->ops.read_status(fe, &s);
+ if (s != fepriv->status) {
+ dvb_frontend_add_event(fe, s); /* update event list */
+ fepriv->status = s;
+ if (!(s & FE_HAS_LOCK)) {
+ fepriv->delay = HZ / 10;
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+ } else {
+ fepriv->delay = 60 * HZ;
+ }
+ }
+ break;
+ default:
+ dev_dbg(fe->dvb->device, "%s: UNDEFINED ALGO !\n", __func__);
+ break;
+ }
+ } else {
+ dvb_frontend_swzigzag(fe);
+ }
+ }
+
+ if (dvb_powerdown_on_sleep) {
+ if (fe->ops.set_voltage)
+ fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF);
+ if (fe->ops.tuner_ops.sleep) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ fe->ops.tuner_ops.sleep(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+ if (fe->ops.sleep)
+ fe->ops.sleep(fe);
+ }
+
+ fepriv->thread = NULL;
+ if (kthread_should_stop())
+ fe->exit = DVB_FE_DEVICE_REMOVED;
+ else
+ fe->exit = DVB_FE_NO_EXIT;
+ mb();
+
+ if (semheld)
+ up(&fepriv->sem);
+ dvb_frontend_wakeup(fe);
+ return 0;
+}
+
+static void dvb_frontend_stop(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ if (fe->exit != DVB_FE_DEVICE_REMOVED)
+ fe->exit = DVB_FE_NORMAL_EXIT;
+ mb();
+
+ if (!fepriv->thread)
+ return;
+
+ kthread_stop(fepriv->thread);
+
+ sema_init(&fepriv->sem, 1);
+ fepriv->state = FESTATE_IDLE;
+
+ /* paranoia check in case a signal arrived */
+ if (fepriv->thread)
+ dev_warn(fe->dvb->device,
+ "dvb_frontend_stop: warning: thread %p won't exit\n",
+ fepriv->thread);
+}
+
+/*
+ * Sleep for the amount of time given by add_usec parameter
+ *
+ * This needs to be as precise as possible, as it affects the detection of
+ * the dish tone command at the satellite subsystem. The precision is improved
+ * by using a scheduled msleep followed by udelay for the remainder.
+ */
+void dvb_frontend_sleep_until(ktime_t *waketime, u32 add_usec)
+{
+ s32 delta;
+
+ *waketime = ktime_add_us(*waketime, add_usec);
+ delta = ktime_us_delta(ktime_get_boottime(), *waketime);
+ if (delta > 2500) {
+ msleep((delta - 1500) / 1000);
+ delta = ktime_us_delta(ktime_get_boottime(), *waketime);
+ }
+ if (delta > 0)
+ udelay(delta);
+}
+EXPORT_SYMBOL(dvb_frontend_sleep_until);
+
+static int dvb_frontend_start(struct dvb_frontend *fe)
+{
+ int ret;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct task_struct *fe_thread;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ if (fepriv->thread) {
+ if (fe->exit == DVB_FE_NO_EXIT)
+ return 0;
+ else
+ dvb_frontend_stop(fe);
+ }
+
+ if (signal_pending(current))
+ return -EINTR;
+ if (down_interruptible(&fepriv->sem))
+ return -EINTR;
+
+ fepriv->state = FESTATE_IDLE;
+ fe->exit = DVB_FE_NO_EXIT;
+ fepriv->thread = NULL;
+ mb();
+
+ fe_thread = kthread_run(dvb_frontend_thread, fe,
+ "kdvb-ad-%i-fe-%i", fe->dvb->num, fe->id);
+ if (IS_ERR(fe_thread)) {
+ ret = PTR_ERR(fe_thread);
+ dev_warn(fe->dvb->device,
+ "dvb_frontend_start: failed to start kthread (%d)\n",
+ ret);
+ up(&fepriv->sem);
+ return ret;
+ }
+ fepriv->thread = fe_thread;
+ return 0;
+}
+
+static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe,
+ u32 *freq_min, u32 *freq_max,
+ u32 *tolerance)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 tuner_min = fe->ops.tuner_ops.info.frequency_min_hz;
+ u32 tuner_max = fe->ops.tuner_ops.info.frequency_max_hz;
+ u32 frontend_min = fe->ops.info.frequency_min_hz;
+ u32 frontend_max = fe->ops.info.frequency_max_hz;
+
+ *freq_min = max(frontend_min, tuner_min);
+
+ if (frontend_max == 0)
+ *freq_max = tuner_max;
+ else if (tuner_max == 0)
+ *freq_max = frontend_max;
+ else
+ *freq_max = min(frontend_max, tuner_max);
+
+ if (*freq_min == 0 || *freq_max == 0)
+ dev_warn(fe->dvb->device,
+ "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n",
+ fe->dvb->num, fe->id);
+
+ dev_dbg(fe->dvb->device, "frequency interval: tuner: %u...%u, frontend: %u...%u",
+ tuner_min, tuner_max, frontend_min, frontend_max);
+
+ /* If the standard is for satellite, convert frequencies to kHz */
+ switch (c->delivery_system) {
+ case SYS_DSS:
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ case SYS_TURBO:
+ case SYS_ISDBS:
+ *freq_min /= kHz;
+ *freq_max /= kHz;
+ if (tolerance)
+ *tolerance = fe->ops.info.frequency_tolerance_hz / kHz;
+
+ break;
+ default:
+ if (tolerance)
+ *tolerance = fe->ops.info.frequency_tolerance_hz;
+ break;
+ }
+}
+
+static u32 dvb_frontend_get_stepsize(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 fe_step = fe->ops.info.frequency_stepsize_hz;
+ u32 tuner_step = fe->ops.tuner_ops.info.frequency_step_hz;
+ u32 step = max(fe_step, tuner_step);
+
+ switch (c->delivery_system) {
+ case SYS_DSS:
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ case SYS_TURBO:
+ case SYS_ISDBS:
+ step /= kHz;
+ break;
+ default:
+ break;
+ }
+
+ return step;
+}
+
+static int dvb_frontend_check_parameters(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 freq_min;
+ u32 freq_max;
+
+ /* range check: frequency */
+ dvb_frontend_get_frequency_limits(fe, &freq_min, &freq_max, NULL);
+ if ((freq_min && c->frequency < freq_min) ||
+ (freq_max && c->frequency > freq_max)) {
+ dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n",
+ fe->dvb->num, fe->id, c->frequency,
+ freq_min, freq_max);
+ return -EINVAL;
+ }
+
+ /* range check: symbol rate */
+ switch (c->delivery_system) {
+ case SYS_DSS:
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ case SYS_TURBO:
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ if ((fe->ops.info.symbol_rate_min &&
+ c->symbol_rate < fe->ops.info.symbol_rate_min) ||
+ (fe->ops.info.symbol_rate_max &&
+ c->symbol_rate > fe->ops.info.symbol_rate_max)) {
+ dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n",
+ fe->dvb->num, fe->id, c->symbol_rate,
+ fe->ops.info.symbol_rate_min,
+ fe->ops.info.symbol_rate_max);
+ return -EINVAL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int i;
+ u32 delsys;
+
+ delsys = c->delivery_system;
+ memset(c, 0, offsetof(struct dtv_frontend_properties, strength));
+ c->delivery_system = delsys;
+
+ dev_dbg(fe->dvb->device, "%s: Clearing cache for delivery system %d\n",
+ __func__, c->delivery_system);
+
+ c->transmission_mode = TRANSMISSION_MODE_AUTO;
+ c->bandwidth_hz = 0; /* AUTO */
+ c->guard_interval = GUARD_INTERVAL_AUTO;
+ c->hierarchy = HIERARCHY_AUTO;
+ c->symbol_rate = 0;
+ c->code_rate_HP = FEC_AUTO;
+ c->code_rate_LP = FEC_AUTO;
+ c->fec_inner = FEC_AUTO;
+ c->rolloff = ROLLOFF_AUTO;
+ c->voltage = SEC_VOLTAGE_OFF;
+ c->sectone = SEC_TONE_OFF;
+ c->pilot = PILOT_AUTO;
+
+ c->isdbt_partial_reception = 0;
+ c->isdbt_sb_mode = 0;
+ c->isdbt_sb_subchannel = 0;
+ c->isdbt_sb_segment_idx = 0;
+ c->isdbt_sb_segment_count = 0;
+ c->isdbt_layer_enabled = 7; /* All layers (A,B,C) */
+ for (i = 0; i < 3; i++) {
+ c->layer[i].fec = FEC_AUTO;
+ c->layer[i].modulation = QAM_AUTO;
+ c->layer[i].interleaving = 0;
+ c->layer[i].segment_count = 0;
+ }
+
+ c->stream_id = NO_STREAM_ID_FILTER;
+ c->scrambling_sequence_index = 0;/* default sequence */
+
+ switch (c->delivery_system) {
+ case SYS_DSS:
+ c->modulation = QPSK;
+ c->rolloff = ROLLOFF_20;
+ break;
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ case SYS_TURBO:
+ c->modulation = QPSK; /* implied for DVB-S in legacy API */
+ c->rolloff = ROLLOFF_35;/* implied for DVB-S */
+ break;
+ case SYS_ATSC:
+ c->modulation = VSB_8;
+ break;
+ case SYS_ISDBS:
+ c->symbol_rate = 28860000;
+ c->rolloff = ROLLOFF_35;
+ c->bandwidth_hz = c->symbol_rate / 100 * 135;
+ break;
+ default:
+ c->modulation = QAM_AUTO;
+ break;
+ }
+
+ c->lna = LNA_AUTO;
+
+ return 0;
+}
+
+#define _DTV_CMD(n) \
+ [n] = #n
+
+static char *dtv_cmds[DTV_MAX_COMMAND + 1] = {
+ _DTV_CMD(DTV_TUNE),
+ _DTV_CMD(DTV_CLEAR),
+
+ /* Set */
+ _DTV_CMD(DTV_FREQUENCY),
+ _DTV_CMD(DTV_BANDWIDTH_HZ),
+ _DTV_CMD(DTV_MODULATION),
+ _DTV_CMD(DTV_INVERSION),
+ _DTV_CMD(DTV_DISEQC_MASTER),
+ _DTV_CMD(DTV_SYMBOL_RATE),
+ _DTV_CMD(DTV_INNER_FEC),
+ _DTV_CMD(DTV_VOLTAGE),
+ _DTV_CMD(DTV_TONE),
+ _DTV_CMD(DTV_PILOT),
+ _DTV_CMD(DTV_ROLLOFF),
+ _DTV_CMD(DTV_DELIVERY_SYSTEM),
+ _DTV_CMD(DTV_HIERARCHY),
+ _DTV_CMD(DTV_CODE_RATE_HP),
+ _DTV_CMD(DTV_CODE_RATE_LP),
+ _DTV_CMD(DTV_GUARD_INTERVAL),
+ _DTV_CMD(DTV_TRANSMISSION_MODE),
+ _DTV_CMD(DTV_INTERLEAVING),
+
+ _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION),
+ _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING),
+ _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID),
+ _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX),
+ _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT),
+ _DTV_CMD(DTV_ISDBT_LAYER_ENABLED),
+ _DTV_CMD(DTV_ISDBT_LAYERA_FEC),
+ _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION),
+ _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT),
+ _DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING),
+ _DTV_CMD(DTV_ISDBT_LAYERB_FEC),
+ _DTV_CMD(DTV_ISDBT_LAYERB_MODULATION),
+ _DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT),
+ _DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING),
+ _DTV_CMD(DTV_ISDBT_LAYERC_FEC),
+ _DTV_CMD(DTV_ISDBT_LAYERC_MODULATION),
+ _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT),
+ _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING),
+
+ _DTV_CMD(DTV_STREAM_ID),
+ _DTV_CMD(DTV_DVBT2_PLP_ID_LEGACY),
+ _DTV_CMD(DTV_SCRAMBLING_SEQUENCE_INDEX),
+ _DTV_CMD(DTV_LNA),
+
+ /* Get */
+ _DTV_CMD(DTV_DISEQC_SLAVE_REPLY),
+ _DTV_CMD(DTV_API_VERSION),
+
+ _DTV_CMD(DTV_ENUM_DELSYS),
+
+ _DTV_CMD(DTV_ATSCMH_PARADE_ID),
+ _DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE),
+
+ _DTV_CMD(DTV_ATSCMH_FIC_VER),
+ _DTV_CMD(DTV_ATSCMH_NOG),
+ _DTV_CMD(DTV_ATSCMH_TNOG),
+ _DTV_CMD(DTV_ATSCMH_SGN),
+ _DTV_CMD(DTV_ATSCMH_PRC),
+ _DTV_CMD(DTV_ATSCMH_RS_FRAME_MODE),
+ _DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_PRI),
+ _DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_SEC),
+ _DTV_CMD(DTV_ATSCMH_SCCC_BLOCK_MODE),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_A),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D),
+
+ /* Statistics API */
+ _DTV_CMD(DTV_STAT_SIGNAL_STRENGTH),
+ _DTV_CMD(DTV_STAT_CNR),
+ _DTV_CMD(DTV_STAT_PRE_ERROR_BIT_COUNT),
+ _DTV_CMD(DTV_STAT_PRE_TOTAL_BIT_COUNT),
+ _DTV_CMD(DTV_STAT_POST_ERROR_BIT_COUNT),
+ _DTV_CMD(DTV_STAT_POST_TOTAL_BIT_COUNT),
+ _DTV_CMD(DTV_STAT_ERROR_BLOCK_COUNT),
+ _DTV_CMD(DTV_STAT_TOTAL_BLOCK_COUNT),
+};
+
+static char *dtv_cmd_name(u32 cmd)
+{
+ cmd = array_index_nospec(cmd, DTV_MAX_COMMAND);
+ return dtv_cmds[cmd];
+}
+
+/* Synchronise the legacy tuning parameters into the cache, so that demodulator
+ * drivers can use a single set_frontend tuning function, regardless of whether
+ * it's being used for the legacy or new API, reducing code and complexity.
+ */
+static int dtv_property_cache_sync(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c,
+ const struct dvb_frontend_parameters *p)
+{
+ c->frequency = p->frequency;
+ c->inversion = p->inversion;
+
+ switch (dvbv3_type(c->delivery_system)) {
+ case DVBV3_QPSK:
+ dev_dbg(fe->dvb->device, "%s: Preparing QPSK req\n", __func__);
+ c->symbol_rate = p->u.qpsk.symbol_rate;
+ c->fec_inner = p->u.qpsk.fec_inner;
+ break;
+ case DVBV3_QAM:
+ dev_dbg(fe->dvb->device, "%s: Preparing QAM req\n", __func__);
+ c->symbol_rate = p->u.qam.symbol_rate;
+ c->fec_inner = p->u.qam.fec_inner;
+ c->modulation = p->u.qam.modulation;
+ break;
+ case DVBV3_OFDM:
+ dev_dbg(fe->dvb->device, "%s: Preparing OFDM req\n", __func__);
+
+ switch (p->u.ofdm.bandwidth) {
+ case BANDWIDTH_10_MHZ:
+ c->bandwidth_hz = 10000000;
+ break;
+ case BANDWIDTH_8_MHZ:
+ c->bandwidth_hz = 8000000;
+ break;
+ case BANDWIDTH_7_MHZ:
+ c->bandwidth_hz = 7000000;
+ break;
+ case BANDWIDTH_6_MHZ:
+ c->bandwidth_hz = 6000000;
+ break;
+ case BANDWIDTH_5_MHZ:
+ c->bandwidth_hz = 5000000;
+ break;
+ case BANDWIDTH_1_712_MHZ:
+ c->bandwidth_hz = 1712000;
+ break;
+ case BANDWIDTH_AUTO:
+ c->bandwidth_hz = 0;
+ }
+
+ c->code_rate_HP = p->u.ofdm.code_rate_HP;
+ c->code_rate_LP = p->u.ofdm.code_rate_LP;
+ c->modulation = p->u.ofdm.constellation;
+ c->transmission_mode = p->u.ofdm.transmission_mode;
+ c->guard_interval = p->u.ofdm.guard_interval;
+ c->hierarchy = p->u.ofdm.hierarchy_information;
+ break;
+ case DVBV3_ATSC:
+ dev_dbg(fe->dvb->device, "%s: Preparing ATSC req\n", __func__);
+ c->modulation = p->u.vsb.modulation;
+ if (c->delivery_system == SYS_ATSCMH)
+ break;
+ if ((c->modulation == VSB_8) || (c->modulation == VSB_16))
+ c->delivery_system = SYS_ATSC;
+ else
+ c->delivery_system = SYS_DVBC_ANNEX_B;
+ break;
+ case DVBV3_UNKNOWN:
+ dev_err(fe->dvb->device,
+ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
+ __func__, c->delivery_system);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Ensure the cached values are set correctly in the frontend
+ * legacy tuning structures, for the advanced tuning API.
+ */
+static int
+dtv_property_legacy_params_sync(struct dvb_frontend *fe,
+ const struct dtv_frontend_properties *c,
+ struct dvb_frontend_parameters *p)
+{
+ p->frequency = c->frequency;
+ p->inversion = c->inversion;
+
+ switch (dvbv3_type(c->delivery_system)) {
+ case DVBV3_UNKNOWN:
+ dev_err(fe->dvb->device,
+ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
+ __func__, c->delivery_system);
+ return -EINVAL;
+ case DVBV3_QPSK:
+ dev_dbg(fe->dvb->device, "%s: Preparing QPSK req\n", __func__);
+ p->u.qpsk.symbol_rate = c->symbol_rate;
+ p->u.qpsk.fec_inner = c->fec_inner;
+ break;
+ case DVBV3_QAM:
+ dev_dbg(fe->dvb->device, "%s: Preparing QAM req\n", __func__);
+ p->u.qam.symbol_rate = c->symbol_rate;
+ p->u.qam.fec_inner = c->fec_inner;
+ p->u.qam.modulation = c->modulation;
+ break;
+ case DVBV3_OFDM:
+ dev_dbg(fe->dvb->device, "%s: Preparing OFDM req\n", __func__);
+ switch (c->bandwidth_hz) {
+ case 10000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_10_MHZ;
+ break;
+ case 8000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+ break;
+ case 7000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+ break;
+ case 6000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+ break;
+ case 5000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_5_MHZ;
+ break;
+ case 1712000:
+ p->u.ofdm.bandwidth = BANDWIDTH_1_712_MHZ;
+ break;
+ case 0:
+ default:
+ p->u.ofdm.bandwidth = BANDWIDTH_AUTO;
+ }
+ p->u.ofdm.code_rate_HP = c->code_rate_HP;
+ p->u.ofdm.code_rate_LP = c->code_rate_LP;
+ p->u.ofdm.constellation = c->modulation;
+ p->u.ofdm.transmission_mode = c->transmission_mode;
+ p->u.ofdm.guard_interval = c->guard_interval;
+ p->u.ofdm.hierarchy_information = c->hierarchy;
+ break;
+ case DVBV3_ATSC:
+ dev_dbg(fe->dvb->device, "%s: Preparing VSB req\n", __func__);
+ p->u.vsb.modulation = c->modulation;
+ break;
+ }
+ return 0;
+}
+
+/**
+ * dtv_get_frontend - calls a callback for retrieving DTV parameters
+ * @fe: struct dvb_frontend pointer
+ * @c: struct dtv_frontend_properties pointer (DVBv5 cache)
+ * @p_out: struct dvb_frontend_parameters pointer (DVBv3 FE struct)
+ *
+ * This routine calls either the DVBv3 or DVBv5 get_frontend call.
+ * If c is not null, it will update the DVBv5 cache struct pointed by it.
+ * If p_out is not null, it will update the DVBv3 params pointed by it.
+ */
+static int dtv_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c,
+ struct dvb_frontend_parameters *p_out)
+{
+ int r;
+
+ if (fe->ops.get_frontend) {
+ r = fe->ops.get_frontend(fe, c);
+ if (unlikely(r < 0))
+ return r;
+ if (p_out)
+ dtv_property_legacy_params_sync(fe, c, p_out);
+ return 0;
+ }
+
+ /* As everything is in cache, get_frontend fops are always supported */
+ return 0;
+}
+
+static int dvb_frontend_handle_ioctl(struct file *file,
+ unsigned int cmd, void *parg);
+
+static int dtv_property_process_get(struct dvb_frontend *fe,
+ const struct dtv_frontend_properties *c,
+ struct dtv_property *tvp,
+ struct file *file)
+{
+ int ncaps;
+ unsigned int len = 1;
+
+ switch (tvp->cmd) {
+ case DTV_ENUM_DELSYS:
+ ncaps = 0;
+ while (ncaps < MAX_DELSYS && fe->ops.delsys[ncaps]) {
+ tvp->u.buffer.data[ncaps] = fe->ops.delsys[ncaps];
+ ncaps++;
+ }
+ tvp->u.buffer.len = ncaps;
+ len = ncaps;
+ break;
+ case DTV_FREQUENCY:
+ tvp->u.data = c->frequency;
+ break;
+ case DTV_MODULATION:
+ tvp->u.data = c->modulation;
+ break;
+ case DTV_BANDWIDTH_HZ:
+ tvp->u.data = c->bandwidth_hz;
+ break;
+ case DTV_INVERSION:
+ tvp->u.data = c->inversion;
+ break;
+ case DTV_SYMBOL_RATE:
+ tvp->u.data = c->symbol_rate;
+ break;
+ case DTV_INNER_FEC:
+ tvp->u.data = c->fec_inner;
+ break;
+ case DTV_PILOT:
+ tvp->u.data = c->pilot;
+ break;
+ case DTV_ROLLOFF:
+ tvp->u.data = c->rolloff;
+ break;
+ case DTV_DELIVERY_SYSTEM:
+ tvp->u.data = c->delivery_system;
+ break;
+ case DTV_VOLTAGE:
+ tvp->u.data = c->voltage;
+ break;
+ case DTV_TONE:
+ tvp->u.data = c->sectone;
+ break;
+ case DTV_API_VERSION:
+ tvp->u.data = (DVB_API_VERSION << 8) | DVB_API_VERSION_MINOR;
+ break;
+ case DTV_CODE_RATE_HP:
+ tvp->u.data = c->code_rate_HP;
+ break;
+ case DTV_CODE_RATE_LP:
+ tvp->u.data = c->code_rate_LP;
+ break;
+ case DTV_GUARD_INTERVAL:
+ tvp->u.data = c->guard_interval;
+ break;
+ case DTV_TRANSMISSION_MODE:
+ tvp->u.data = c->transmission_mode;
+ break;
+ case DTV_HIERARCHY:
+ tvp->u.data = c->hierarchy;
+ break;
+ case DTV_INTERLEAVING:
+ tvp->u.data = c->interleaving;
+ break;
+
+ /* ISDB-T Support here */
+ case DTV_ISDBT_PARTIAL_RECEPTION:
+ tvp->u.data = c->isdbt_partial_reception;
+ break;
+ case DTV_ISDBT_SOUND_BROADCASTING:
+ tvp->u.data = c->isdbt_sb_mode;
+ break;
+ case DTV_ISDBT_SB_SUBCHANNEL_ID:
+ tvp->u.data = c->isdbt_sb_subchannel;
+ break;
+ case DTV_ISDBT_SB_SEGMENT_IDX:
+ tvp->u.data = c->isdbt_sb_segment_idx;
+ break;
+ case DTV_ISDBT_SB_SEGMENT_COUNT:
+ tvp->u.data = c->isdbt_sb_segment_count;
+ break;
+ case DTV_ISDBT_LAYER_ENABLED:
+ tvp->u.data = c->isdbt_layer_enabled;
+ break;
+ case DTV_ISDBT_LAYERA_FEC:
+ tvp->u.data = c->layer[0].fec;
+ break;
+ case DTV_ISDBT_LAYERA_MODULATION:
+ tvp->u.data = c->layer[0].modulation;
+ break;
+ case DTV_ISDBT_LAYERA_SEGMENT_COUNT:
+ tvp->u.data = c->layer[0].segment_count;
+ break;
+ case DTV_ISDBT_LAYERA_TIME_INTERLEAVING:
+ tvp->u.data = c->layer[0].interleaving;
+ break;
+ case DTV_ISDBT_LAYERB_FEC:
+ tvp->u.data = c->layer[1].fec;
+ break;
+ case DTV_ISDBT_LAYERB_MODULATION:
+ tvp->u.data = c->layer[1].modulation;
+ break;
+ case DTV_ISDBT_LAYERB_SEGMENT_COUNT:
+ tvp->u.data = c->layer[1].segment_count;
+ break;
+ case DTV_ISDBT_LAYERB_TIME_INTERLEAVING:
+ tvp->u.data = c->layer[1].interleaving;
+ break;
+ case DTV_ISDBT_LAYERC_FEC:
+ tvp->u.data = c->layer[2].fec;
+ break;
+ case DTV_ISDBT_LAYERC_MODULATION:
+ tvp->u.data = c->layer[2].modulation;
+ break;
+ case DTV_ISDBT_LAYERC_SEGMENT_COUNT:
+ tvp->u.data = c->layer[2].segment_count;
+ break;
+ case DTV_ISDBT_LAYERC_TIME_INTERLEAVING:
+ tvp->u.data = c->layer[2].interleaving;
+ break;
+
+ /* Multistream support */
+ case DTV_STREAM_ID:
+ case DTV_DVBT2_PLP_ID_LEGACY:
+ tvp->u.data = c->stream_id;
+ break;
+
+ /* Physical layer scrambling support */
+ case DTV_SCRAMBLING_SEQUENCE_INDEX:
+ tvp->u.data = c->scrambling_sequence_index;
+ break;
+
+ /* ATSC-MH */
+ case DTV_ATSCMH_FIC_VER:
+ tvp->u.data = fe->dtv_property_cache.atscmh_fic_ver;
+ break;
+ case DTV_ATSCMH_PARADE_ID:
+ tvp->u.data = fe->dtv_property_cache.atscmh_parade_id;
+ break;
+ case DTV_ATSCMH_NOG:
+ tvp->u.data = fe->dtv_property_cache.atscmh_nog;
+ break;
+ case DTV_ATSCMH_TNOG:
+ tvp->u.data = fe->dtv_property_cache.atscmh_tnog;
+ break;
+ case DTV_ATSCMH_SGN:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sgn;
+ break;
+ case DTV_ATSCMH_PRC:
+ tvp->u.data = fe->dtv_property_cache.atscmh_prc;
+ break;
+ case DTV_ATSCMH_RS_FRAME_MODE:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_frame_mode;
+ break;
+ case DTV_ATSCMH_RS_FRAME_ENSEMBLE:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_frame_ensemble;
+ break;
+ case DTV_ATSCMH_RS_CODE_MODE_PRI:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_code_mode_pri;
+ break;
+ case DTV_ATSCMH_RS_CODE_MODE_SEC:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_code_mode_sec;
+ break;
+ case DTV_ATSCMH_SCCC_BLOCK_MODE:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_block_mode;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_A:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_a;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_B:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_b;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_C:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_c;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_D:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_d;
+ break;
+
+ case DTV_LNA:
+ tvp->u.data = c->lna;
+ break;
+
+ /* Fill quality measures */
+ case DTV_STAT_SIGNAL_STRENGTH:
+ tvp->u.st = c->strength;
+ if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+ tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+ len = tvp->u.buffer.len;
+ break;
+ case DTV_STAT_CNR:
+ tvp->u.st = c->cnr;
+ if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+ tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+ len = tvp->u.buffer.len;
+ break;
+ case DTV_STAT_PRE_ERROR_BIT_COUNT:
+ tvp->u.st = c->pre_bit_error;
+ if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+ tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+ len = tvp->u.buffer.len;
+ break;
+ case DTV_STAT_PRE_TOTAL_BIT_COUNT:
+ tvp->u.st = c->pre_bit_count;
+ if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+ tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+ len = tvp->u.buffer.len;
+ break;
+ case DTV_STAT_POST_ERROR_BIT_COUNT:
+ tvp->u.st = c->post_bit_error;
+ if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+ tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+ len = tvp->u.buffer.len;
+ break;
+ case DTV_STAT_POST_TOTAL_BIT_COUNT:
+ tvp->u.st = c->post_bit_count;
+ if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+ tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+ len = tvp->u.buffer.len;
+ break;
+ case DTV_STAT_ERROR_BLOCK_COUNT:
+ tvp->u.st = c->block_error;
+ if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+ tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+ len = tvp->u.buffer.len;
+ break;
+ case DTV_STAT_TOTAL_BLOCK_COUNT:
+ tvp->u.st = c->block_count;
+ if (tvp->u.buffer.len > MAX_DTV_STATS * sizeof(u32))
+ tvp->u.buffer.len = MAX_DTV_STATS * sizeof(u32);
+ len = tvp->u.buffer.len;
+ break;
+ default:
+ dev_dbg(fe->dvb->device,
+ "%s: FE property %d doesn't exist\n",
+ __func__, tvp->cmd);
+ return -EINVAL;
+ }
+
+ if (len < 1)
+ len = 1;
+
+ dev_dbg(fe->dvb->device,
+ "%s: GET cmd 0x%08x (%s) len %d: %*ph\n",
+ __func__, tvp->cmd, dtv_cmd_name(tvp->cmd),
+ tvp->u.buffer.len, tvp->u.buffer.len, tvp->u.buffer.data);
+
+ return 0;
+}
+
+static int dtv_set_frontend(struct dvb_frontend *fe);
+
+static bool is_dvbv3_delsys(u32 delsys)
+{
+ return (delsys == SYS_DVBT) || (delsys == SYS_DVBC_ANNEX_A) ||
+ (delsys == SYS_DVBS) || (delsys == SYS_ATSC);
+}
+
+/**
+ * emulate_delivery_system - emulate a DVBv5 delivery system with a DVBv3 type
+ * @fe: struct frontend;
+ * @delsys: DVBv5 type that will be used for emulation
+ *
+ * Provides emulation for delivery systems that are compatible with the old
+ * DVBv3 call. Among its usages, it provices support for ISDB-T, and allows
+ * using a DVB-S2 only frontend just like it were a DVB-S, if the frontend
+ * parameters are compatible with DVB-S spec.
+ */
+static int emulate_delivery_system(struct dvb_frontend *fe, u32 delsys)
+{
+ int i;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ c->delivery_system = delsys;
+
+ /*
+ * If the call is for ISDB-T, put it into full-seg, auto mode, TV
+ */
+ if (c->delivery_system == SYS_ISDBT) {
+ dev_dbg(fe->dvb->device,
+ "%s: Using defaults for SYS_ISDBT\n",
+ __func__);
+
+ if (!c->bandwidth_hz)
+ c->bandwidth_hz = 6000000;
+
+ c->isdbt_partial_reception = 0;
+ c->isdbt_sb_mode = 0;
+ c->isdbt_sb_subchannel = 0;
+ c->isdbt_sb_segment_idx = 0;
+ c->isdbt_sb_segment_count = 0;
+ c->isdbt_layer_enabled = 7;
+ for (i = 0; i < 3; i++) {
+ c->layer[i].fec = FEC_AUTO;
+ c->layer[i].modulation = QAM_AUTO;
+ c->layer[i].interleaving = 0;
+ c->layer[i].segment_count = 0;
+ }
+ }
+ dev_dbg(fe->dvb->device, "%s: change delivery system on cache to %d\n",
+ __func__, c->delivery_system);
+
+ return 0;
+}
+
+/**
+ * dvbv5_set_delivery_system - Sets the delivery system for a DVBv5 API call
+ * @fe: frontend struct
+ * @desired_system: delivery system requested by the user
+ *
+ * A DVBv5 call know what's the desired system it wants. So, set it.
+ *
+ * There are, however, a few known issues with early DVBv5 applications that
+ * are also handled by this logic:
+ *
+ * 1) Some early apps use SYS_UNDEFINED as the desired delivery system.
+ * This is an API violation, but, as we don't want to break userspace,
+ * convert it to the first supported delivery system.
+ * 2) Some apps might be using a DVBv5 call in a wrong way, passing, for
+ * example, SYS_DVBT instead of SYS_ISDBT. This is because early usage of
+ * ISDB-T provided backward compat with DVB-T.
+ */
+static int dvbv5_set_delivery_system(struct dvb_frontend *fe,
+ u32 desired_system)
+{
+ int ncaps;
+ u32 delsys = SYS_UNDEFINED;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ enum dvbv3_emulation_type type;
+
+ /*
+ * It was reported that some old DVBv5 applications were
+ * filling delivery_system with SYS_UNDEFINED. If this happens,
+ * assume that the application wants to use the first supported
+ * delivery system.
+ */
+ if (desired_system == SYS_UNDEFINED)
+ desired_system = fe->ops.delsys[0];
+
+ /*
+ * This is a DVBv5 call. So, it likely knows the supported
+ * delivery systems. So, check if the desired delivery system is
+ * supported
+ */
+ ncaps = 0;
+ while (ncaps < MAX_DELSYS && fe->ops.delsys[ncaps]) {
+ if (fe->ops.delsys[ncaps] == desired_system) {
+ c->delivery_system = desired_system;
+ dev_dbg(fe->dvb->device,
+ "%s: Changing delivery system to %d\n",
+ __func__, desired_system);
+ return 0;
+ }
+ ncaps++;
+ }
+
+ /*
+ * The requested delivery system isn't supported. Maybe userspace
+ * is requesting a DVBv3 compatible delivery system.
+ *
+ * The emulation only works if the desired system is one of the
+ * delivery systems supported by DVBv3 API
+ */
+ if (!is_dvbv3_delsys(desired_system)) {
+ dev_dbg(fe->dvb->device,
+ "%s: Delivery system %d not supported.\n",
+ __func__, desired_system);
+ return -EINVAL;
+ }
+
+ type = dvbv3_type(desired_system);
+
+ /*
+ * Get the last non-DVBv3 delivery system that has the same type
+ * of the desired system
+ */
+ ncaps = 0;
+ while (ncaps < MAX_DELSYS && fe->ops.delsys[ncaps]) {
+ if (dvbv3_type(fe->ops.delsys[ncaps]) == type)
+ delsys = fe->ops.delsys[ncaps];
+ ncaps++;
+ }
+
+ /* There's nothing compatible with the desired delivery system */
+ if (delsys == SYS_UNDEFINED) {
+ dev_dbg(fe->dvb->device,
+ "%s: Delivery system %d not supported on emulation mode.\n",
+ __func__, desired_system);
+ return -EINVAL;
+ }
+
+ dev_dbg(fe->dvb->device,
+ "%s: Using delivery system %d emulated as if it were %d\n",
+ __func__, delsys, desired_system);
+
+ return emulate_delivery_system(fe, desired_system);
+}
+
+/**
+ * dvbv3_set_delivery_system - Sets the delivery system for a DVBv3 API call
+ * @fe: frontend struct
+ *
+ * A DVBv3 call doesn't know what's the desired system it wants. It also
+ * doesn't allow to switch between different types. Due to that, userspace
+ * should use DVBv5 instead.
+ * However, in order to avoid breaking userspace API, limited backward
+ * compatibility support is provided.
+ *
+ * There are some delivery systems that are incompatible with DVBv3 calls.
+ *
+ * This routine should work fine for frontends that support just one delivery
+ * system.
+ *
+ * For frontends that support multiple frontends:
+ * 1) It defaults to use the first supported delivery system. There's an
+ * userspace application that allows changing it at runtime;
+ *
+ * 2) If the current delivery system is not compatible with DVBv3, it gets
+ * the first one that it is compatible.
+ *
+ * NOTE: in order for this to work with applications like Kaffeine that
+ * uses a DVBv5 call for DVB-S2 and a DVBv3 call to go back to
+ * DVB-S, drivers that support both DVB-S and DVB-S2 should have the
+ * SYS_DVBS entry before the SYS_DVBS2, otherwise it won't switch back
+ * to DVB-S.
+ */
+static int dvbv3_set_delivery_system(struct dvb_frontend *fe)
+{
+ int ncaps;
+ u32 delsys = SYS_UNDEFINED;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ /* If not set yet, defaults to the first supported delivery system */
+ if (c->delivery_system == SYS_UNDEFINED)
+ c->delivery_system = fe->ops.delsys[0];
+
+ /*
+ * Trivial case: just use the current one, if it already a DVBv3
+ * delivery system
+ */
+ if (is_dvbv3_delsys(c->delivery_system)) {
+ dev_dbg(fe->dvb->device,
+ "%s: Using delivery system to %d\n",
+ __func__, c->delivery_system);
+ return 0;
+ }
+
+ /*
+ * Seek for the first delivery system that it is compatible with a
+ * DVBv3 standard
+ */
+ ncaps = 0;
+ while (ncaps < MAX_DELSYS && fe->ops.delsys[ncaps]) {
+ if (dvbv3_type(fe->ops.delsys[ncaps]) != DVBV3_UNKNOWN) {
+ delsys = fe->ops.delsys[ncaps];
+ break;
+ }
+ ncaps++;
+ }
+ if (delsys == SYS_UNDEFINED) {
+ dev_dbg(fe->dvb->device,
+ "%s: Couldn't find a delivery system that works with FE_SET_FRONTEND\n",
+ __func__);
+ return -EINVAL;
+ }
+ return emulate_delivery_system(fe, delsys);
+}
+
+static void prepare_tuning_algo_parameters(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_frontend_tune_settings fetunesettings = { 0 };
+
+ /* get frontend-specific tuning settings */
+ if (fe->ops.get_tune_settings && (fe->ops.get_tune_settings(fe, &fetunesettings) == 0)) {
+ fepriv->min_delay = (fetunesettings.min_delay_ms * HZ) / 1000;
+ fepriv->max_drift = fetunesettings.max_drift;
+ fepriv->step_size = fetunesettings.step_size;
+ } else {
+ /* default values */
+ switch (c->delivery_system) {
+ case SYS_DSS:
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ case SYS_ISDBS:
+ case SYS_TURBO:
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ fepriv->min_delay = HZ / 20;
+ fepriv->step_size = c->symbol_rate / 16000;
+ fepriv->max_drift = c->symbol_rate / 2000;
+ break;
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ case SYS_ISDBT:
+ case SYS_DTMB:
+ fepriv->min_delay = HZ / 20;
+ fepriv->step_size = dvb_frontend_get_stepsize(fe) * 2;
+ fepriv->max_drift = fepriv->step_size + 1;
+ break;
+ default:
+ /*
+ * FIXME: This sounds wrong! if freqency_stepsize is
+ * defined by the frontend, why not use it???
+ */
+ fepriv->min_delay = HZ / 20;
+ fepriv->step_size = 0; /* no zigzag */
+ fepriv->max_drift = 0;
+ break;
+ }
+ }
+ if (dvb_override_tune_delay > 0)
+ fepriv->min_delay = (dvb_override_tune_delay * HZ) / 1000;
+}
+
+/**
+ * dtv_property_process_set - Sets a single DTV property
+ * @fe: Pointer to &struct dvb_frontend
+ * @file: Pointer to &struct file
+ * @cmd: Digital TV command
+ * @data: An unsigned 32-bits number
+ *
+ * This routine assigns the property
+ * value to the corresponding member of
+ * &struct dtv_frontend_properties
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+static int dtv_property_process_set(struct dvb_frontend *fe,
+ struct file *file,
+ u32 cmd, u32 data)
+{
+ int r = 0;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ /** Dump DTV command name and value*/
+ if (!cmd || cmd > DTV_MAX_COMMAND)
+ dev_warn(fe->dvb->device, "%s: SET cmd 0x%08x undefined\n",
+ __func__, cmd);
+ else
+ dev_dbg(fe->dvb->device,
+ "%s: SET cmd 0x%08x (%s) to 0x%08x\n",
+ __func__, cmd, dtv_cmd_name(cmd), data);
+ switch (cmd) {
+ case DTV_CLEAR:
+ /*
+ * Reset a cache of data specific to the frontend here. This does
+ * not effect hardware.
+ */
+ dvb_frontend_clear_cache(fe);
+ break;
+ case DTV_TUNE:
+ /*
+ * Use the cached Digital TV properties to tune the
+ * frontend
+ */
+ dev_dbg(fe->dvb->device,
+ "%s: Setting the frontend from property cache\n",
+ __func__);
+
+ r = dtv_set_frontend(fe);
+ break;
+ case DTV_FREQUENCY:
+ c->frequency = data;
+ break;
+ case DTV_MODULATION:
+ c->modulation = data;
+ break;
+ case DTV_BANDWIDTH_HZ:
+ c->bandwidth_hz = data;
+ break;
+ case DTV_INVERSION:
+ c->inversion = data;
+ break;
+ case DTV_SYMBOL_RATE:
+ c->symbol_rate = data;
+ break;
+ case DTV_INNER_FEC:
+ c->fec_inner = data;
+ break;
+ case DTV_PILOT:
+ c->pilot = data;
+ break;
+ case DTV_ROLLOFF:
+ c->rolloff = data;
+ break;
+ case DTV_DELIVERY_SYSTEM:
+ r = dvbv5_set_delivery_system(fe, data);
+ break;
+ case DTV_VOLTAGE:
+ c->voltage = data;
+ r = dvb_frontend_handle_ioctl(file, FE_SET_VOLTAGE,
+ (void *)c->voltage);
+ break;
+ case DTV_TONE:
+ c->sectone = data;
+ r = dvb_frontend_handle_ioctl(file, FE_SET_TONE,
+ (void *)c->sectone);
+ break;
+ case DTV_CODE_RATE_HP:
+ c->code_rate_HP = data;
+ break;
+ case DTV_CODE_RATE_LP:
+ c->code_rate_LP = data;
+ break;
+ case DTV_GUARD_INTERVAL:
+ c->guard_interval = data;
+ break;
+ case DTV_TRANSMISSION_MODE:
+ c->transmission_mode = data;
+ break;
+ case DTV_HIERARCHY:
+ c->hierarchy = data;
+ break;
+ case DTV_INTERLEAVING:
+ c->interleaving = data;
+ break;
+
+ /* ISDB-T Support here */
+ case DTV_ISDBT_PARTIAL_RECEPTION:
+ c->isdbt_partial_reception = data;
+ break;
+ case DTV_ISDBT_SOUND_BROADCASTING:
+ c->isdbt_sb_mode = data;
+ break;
+ case DTV_ISDBT_SB_SUBCHANNEL_ID:
+ c->isdbt_sb_subchannel = data;
+ break;
+ case DTV_ISDBT_SB_SEGMENT_IDX:
+ c->isdbt_sb_segment_idx = data;
+ break;
+ case DTV_ISDBT_SB_SEGMENT_COUNT:
+ c->isdbt_sb_segment_count = data;
+ break;
+ case DTV_ISDBT_LAYER_ENABLED:
+ c->isdbt_layer_enabled = data;
+ break;
+ case DTV_ISDBT_LAYERA_FEC:
+ c->layer[0].fec = data;
+ break;
+ case DTV_ISDBT_LAYERA_MODULATION:
+ c->layer[0].modulation = data;
+ break;
+ case DTV_ISDBT_LAYERA_SEGMENT_COUNT:
+ c->layer[0].segment_count = data;
+ break;
+ case DTV_ISDBT_LAYERA_TIME_INTERLEAVING:
+ c->layer[0].interleaving = data;
+ break;
+ case DTV_ISDBT_LAYERB_FEC:
+ c->layer[1].fec = data;
+ break;
+ case DTV_ISDBT_LAYERB_MODULATION:
+ c->layer[1].modulation = data;
+ break;
+ case DTV_ISDBT_LAYERB_SEGMENT_COUNT:
+ c->layer[1].segment_count = data;
+ break;
+ case DTV_ISDBT_LAYERB_TIME_INTERLEAVING:
+ c->layer[1].interleaving = data;
+ break;
+ case DTV_ISDBT_LAYERC_FEC:
+ c->layer[2].fec = data;
+ break;
+ case DTV_ISDBT_LAYERC_MODULATION:
+ c->layer[2].modulation = data;
+ break;
+ case DTV_ISDBT_LAYERC_SEGMENT_COUNT:
+ c->layer[2].segment_count = data;
+ break;
+ case DTV_ISDBT_LAYERC_TIME_INTERLEAVING:
+ c->layer[2].interleaving = data;
+ break;
+
+ /* Multistream support */
+ case DTV_STREAM_ID:
+ case DTV_DVBT2_PLP_ID_LEGACY:
+ c->stream_id = data;
+ break;
+
+ /* Physical layer scrambling support */
+ case DTV_SCRAMBLING_SEQUENCE_INDEX:
+ c->scrambling_sequence_index = data;
+ break;
+
+ /* ATSC-MH */
+ case DTV_ATSCMH_PARADE_ID:
+ fe->dtv_property_cache.atscmh_parade_id = data;
+ break;
+ case DTV_ATSCMH_RS_FRAME_ENSEMBLE:
+ fe->dtv_property_cache.atscmh_rs_frame_ensemble = data;
+ break;
+
+ case DTV_LNA:
+ c->lna = data;
+ if (fe->ops.set_lna)
+ r = fe->ops.set_lna(fe);
+ if (r < 0)
+ c->lna = LNA_AUTO;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return r;
+}
+
+static int dvb_frontend_do_ioctl(struct file *file, unsigned int cmd,
+ void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ int err;
+
+ dev_dbg(fe->dvb->device, "%s: (%d)\n", __func__, _IOC_NR(cmd));
+ if (down_interruptible(&fepriv->sem))
+ return -ERESTARTSYS;
+
+ if (fe->exit != DVB_FE_NO_EXIT) {
+ up(&fepriv->sem);
+ return -ENODEV;
+ }
+
+ /*
+ * If the frontend is opened in read-only mode, only the ioctls
+ * that don't interfere with the tune logic should be accepted.
+ * That allows an external application to monitor the DVB QoS and
+ * statistics parameters.
+ *
+ * That matches all _IOR() ioctls, except for two special cases:
+ * - FE_GET_EVENT is part of the tuning logic on a DVB application;
+ * - FE_DISEQC_RECV_SLAVE_REPLY is part of DiSEqC 2.0
+ * setup
+ * So, those two ioctls should also return -EPERM, as otherwise
+ * reading from them would interfere with a DVB tune application
+ */
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY
+ && (_IOC_DIR(cmd) != _IOC_READ
+ || cmd == FE_GET_EVENT
+ || cmd == FE_DISEQC_RECV_SLAVE_REPLY)) {
+ up(&fepriv->sem);
+ return -EPERM;
+ }
+
+ err = dvb_frontend_handle_ioctl(file, cmd, parg);
+
+ up(&fepriv->sem);
+ return err;
+}
+
+static long dvb_frontend_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+
+ if (!dvbdev)
+ return -ENODEV;
+
+ return dvb_usercopy(file, cmd, arg, dvb_frontend_do_ioctl);
+}
+
+#ifdef CONFIG_COMPAT
+struct compat_dtv_property {
+ __u32 cmd;
+ __u32 reserved[3];
+ union {
+ __u32 data;
+ struct dtv_fe_stats st;
+ struct {
+ __u8 data[32];
+ __u32 len;
+ __u32 reserved1[3];
+ compat_uptr_t reserved2;
+ } buffer;
+ } u;
+ int result;
+} __attribute__ ((packed));
+
+struct compat_dtv_properties {
+ __u32 num;
+ compat_uptr_t props;
+};
+
+#define COMPAT_FE_SET_PROPERTY _IOW('o', 82, struct compat_dtv_properties)
+#define COMPAT_FE_GET_PROPERTY _IOR('o', 83, struct compat_dtv_properties)
+
+static int dvb_frontend_handle_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ int i, err = 0;
+
+ if (cmd == COMPAT_FE_SET_PROPERTY) {
+ struct compat_dtv_properties prop, *tvps = NULL;
+ struct compat_dtv_property *tvp = NULL;
+
+ if (copy_from_user(&prop, compat_ptr(arg), sizeof(prop)))
+ return -EFAULT;
+
+ tvps = &prop;
+
+ /*
+ * Put an arbitrary limit on the number of messages that can
+ * be sent at once
+ */
+ if (!tvps->num || (tvps->num > DTV_IOCTL_MAX_MSGS))
+ return -EINVAL;
+
+ tvp = memdup_array_user(compat_ptr(tvps->props),
+ tvps->num, sizeof(*tvp));
+ if (IS_ERR(tvp))
+ return PTR_ERR(tvp);
+
+ for (i = 0; i < tvps->num; i++) {
+ err = dtv_property_process_set(fe, file,
+ (tvp + i)->cmd,
+ (tvp + i)->u.data);
+ if (err < 0) {
+ kfree(tvp);
+ return err;
+ }
+ }
+ kfree(tvp);
+ } else if (cmd == COMPAT_FE_GET_PROPERTY) {
+ struct compat_dtv_properties prop, *tvps = NULL;
+ struct compat_dtv_property *tvp = NULL;
+ struct dtv_frontend_properties getp = fe->dtv_property_cache;
+
+ if (copy_from_user(&prop, compat_ptr(arg), sizeof(prop)))
+ return -EFAULT;
+
+ tvps = &prop;
+
+ /*
+ * Put an arbitrary limit on the number of messages that can
+ * be sent at once
+ */
+ if (!tvps->num || (tvps->num > DTV_IOCTL_MAX_MSGS))
+ return -EINVAL;
+
+ tvp = memdup_array_user(compat_ptr(tvps->props),
+ tvps->num, sizeof(*tvp));
+ if (IS_ERR(tvp))
+ return PTR_ERR(tvp);
+
+ /*
+ * Let's use our own copy of property cache, in order to
+ * avoid mangling with DTV zigzag logic, as drivers might
+ * return crap, if they don't check if the data is available
+ * before updating the properties cache.
+ */
+ if (fepriv->state != FESTATE_IDLE) {
+ err = dtv_get_frontend(fe, &getp, NULL);
+ if (err < 0) {
+ kfree(tvp);
+ return err;
+ }
+ }
+ for (i = 0; i < tvps->num; i++) {
+ err = dtv_property_process_get(
+ fe, &getp, (struct dtv_property *)(tvp + i), file);
+ if (err < 0) {
+ kfree(tvp);
+ return err;
+ }
+ }
+
+ if (copy_to_user((void __user *)compat_ptr(tvps->props), tvp,
+ tvps->num * sizeof(struct compat_dtv_property))) {
+ kfree(tvp);
+ return -EFAULT;
+ }
+ kfree(tvp);
+ }
+
+ return err;
+}
+
+static long dvb_frontend_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ int err;
+
+ if (cmd == COMPAT_FE_SET_PROPERTY || cmd == COMPAT_FE_GET_PROPERTY) {
+ if (down_interruptible(&fepriv->sem))
+ return -ERESTARTSYS;
+
+ err = dvb_frontend_handle_compat_ioctl(file, cmd, arg);
+
+ up(&fepriv->sem);
+ return err;
+ }
+
+ return dvb_frontend_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static int dtv_set_frontend(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 rolloff = 0;
+
+ if (dvb_frontend_check_parameters(fe) < 0)
+ return -EINVAL;
+
+ /*
+ * Initialize output parameters to match the values given by
+ * the user. FE_SET_FRONTEND triggers an initial frontend event
+ * with status = 0, which copies output parameters to userspace.
+ */
+ dtv_property_legacy_params_sync(fe, c, &fepriv->parameters_out);
+
+ /*
+ * Be sure that the bandwidth will be filled for all
+ * non-satellite systems, as tuners need to know what
+ * low pass/Nyquist half filter should be applied, in
+ * order to avoid inter-channel noise.
+ *
+ * ISDB-T and DVB-T/T2 already sets bandwidth.
+ * ATSC and DVB-C don't set, so, the core should fill it.
+ *
+ * On DVB-C Annex A and C, the bandwidth is a function of
+ * the roll-off and symbol rate. Annex B defines different
+ * roll-off factors depending on the modulation. Fortunately,
+ * Annex B is only used with 6MHz, so there's no need to
+ * calculate it.
+ *
+ * While not officially supported, a side effect of handling it at
+ * the cache level is that a program could retrieve the bandwidth
+ * via DTV_BANDWIDTH_HZ, which may be useful for test programs.
+ */
+ switch (c->delivery_system) {
+ case SYS_ATSC:
+ case SYS_DVBC_ANNEX_B:
+ c->bandwidth_hz = 6000000;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ rolloff = 115;
+ break;
+ case SYS_DVBC_ANNEX_C:
+ rolloff = 113;
+ break;
+ case SYS_DSS:
+ rolloff = 120;
+ break;
+ case SYS_DVBS:
+ case SYS_TURBO:
+ case SYS_ISDBS:
+ rolloff = 135;
+ break;
+ case SYS_DVBS2:
+ switch (c->rolloff) {
+ case ROLLOFF_20:
+ rolloff = 120;
+ break;
+ case ROLLOFF_25:
+ rolloff = 125;
+ break;
+ default:
+ case ROLLOFF_35:
+ rolloff = 135;
+ }
+ break;
+ default:
+ break;
+ }
+ if (rolloff)
+ c->bandwidth_hz = mult_frac(c->symbol_rate, rolloff, 100);
+
+ /* force auto frequency inversion if requested */
+ if (dvb_force_auto_inversion)
+ c->inversion = INVERSION_AUTO;
+
+ /*
+ * without hierarchical coding code_rate_LP is irrelevant,
+ * so we tolerate the otherwise invalid FEC_NONE setting
+ */
+ if (c->hierarchy == HIERARCHY_NONE && c->code_rate_LP == FEC_NONE)
+ c->code_rate_LP = FEC_AUTO;
+
+ prepare_tuning_algo_parameters(fe);
+
+ fepriv->state = FESTATE_RETUNE;
+
+ /* Request the search algorithm to search */
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+
+ dvb_frontend_clear_events(fe);
+ dvb_frontend_add_event(fe, 0);
+ dvb_frontend_wakeup(fe);
+ fepriv->status = 0;
+
+ return 0;
+}
+
+static int dvb_get_property(struct dvb_frontend *fe, struct file *file,
+ struct dtv_properties *tvps)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_property *tvp = NULL;
+ struct dtv_frontend_properties getp;
+ int i, err;
+
+ memcpy(&getp, &fe->dtv_property_cache, sizeof(getp));
+
+ dev_dbg(fe->dvb->device, "%s: properties.num = %d\n",
+ __func__, tvps->num);
+ dev_dbg(fe->dvb->device, "%s: properties.props = %p\n",
+ __func__, tvps->props);
+
+ /*
+ * Put an arbitrary limit on the number of messages that can
+ * be sent at once
+ */
+ if (!tvps->num || tvps->num > DTV_IOCTL_MAX_MSGS)
+ return -EINVAL;
+
+ tvp = memdup_array_user((void __user *)tvps->props,
+ tvps->num, sizeof(*tvp));
+ if (IS_ERR(tvp))
+ return PTR_ERR(tvp);
+
+ /*
+ * Let's use our own copy of property cache, in order to
+ * avoid mangling with DTV zigzag logic, as drivers might
+ * return crap, if they don't check if the data is available
+ * before updating the properties cache.
+ */
+ if (fepriv->state != FESTATE_IDLE) {
+ err = dtv_get_frontend(fe, &getp, NULL);
+ if (err < 0)
+ goto out;
+ }
+ for (i = 0; i < tvps->num; i++) {
+ err = dtv_property_process_get(fe, &getp,
+ tvp + i, file);
+ if (err < 0)
+ goto out;
+ }
+
+ if (copy_to_user((void __user *)tvps->props, tvp,
+ tvps->num * sizeof(struct dtv_property))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ err = 0;
+out:
+ kfree(tvp);
+ return err;
+}
+
+static int dvb_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p_out)
+{
+ struct dtv_frontend_properties getp;
+
+ /*
+ * Let's use our own copy of property cache, in order to
+ * avoid mangling with DTV zigzag logic, as drivers might
+ * return crap, if they don't check if the data is available
+ * before updating the properties cache.
+ */
+ memcpy(&getp, &fe->dtv_property_cache, sizeof(getp));
+
+ return dtv_get_frontend(fe, &getp, p_out);
+}
+
+static int dvb_frontend_handle_ioctl(struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int i, err = -ENOTSUPP;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ switch (cmd) {
+ case FE_SET_PROPERTY: {
+ struct dtv_properties *tvps = parg;
+ struct dtv_property *tvp = NULL;
+
+ dev_dbg(fe->dvb->device, "%s: properties.num = %d\n",
+ __func__, tvps->num);
+ dev_dbg(fe->dvb->device, "%s: properties.props = %p\n",
+ __func__, tvps->props);
+
+ /*
+ * Put an arbitrary limit on the number of messages that can
+ * be sent at once
+ */
+ if (!tvps->num || (tvps->num > DTV_IOCTL_MAX_MSGS))
+ return -EINVAL;
+
+ tvp = memdup_array_user((void __user *)tvps->props,
+ tvps->num, sizeof(*tvp));
+ if (IS_ERR(tvp))
+ return PTR_ERR(tvp);
+
+ for (i = 0; i < tvps->num; i++) {
+ err = dtv_property_process_set(fe, file,
+ (tvp + i)->cmd,
+ (tvp + i)->u.data);
+ if (err < 0) {
+ kfree(tvp);
+ return err;
+ }
+ }
+ kfree(tvp);
+ err = 0;
+ break;
+ }
+ case FE_GET_PROPERTY:
+ err = dvb_get_property(fe, file, parg);
+ break;
+
+ case FE_GET_INFO: {
+ struct dvb_frontend_info *info = parg;
+ memset(info, 0, sizeof(*info));
+
+ strscpy(info->name, fe->ops.info.name, sizeof(info->name));
+ info->symbol_rate_min = fe->ops.info.symbol_rate_min;
+ info->symbol_rate_max = fe->ops.info.symbol_rate_max;
+ info->symbol_rate_tolerance = fe->ops.info.symbol_rate_tolerance;
+ info->caps = fe->ops.info.caps;
+ info->frequency_stepsize = dvb_frontend_get_stepsize(fe);
+ dvb_frontend_get_frequency_limits(fe, &info->frequency_min,
+ &info->frequency_max,
+ &info->frequency_tolerance);
+
+ /*
+ * Associate the 4 delivery systems supported by DVBv3
+ * API with their DVBv5 counterpart. For the other standards,
+ * use the closest type, assuming that it would hopefully
+ * work with a DVBv3 application.
+ * It should be noticed that, on multi-frontend devices with
+ * different types (terrestrial and cable, for example),
+ * a pure DVBv3 application won't be able to use all delivery
+ * systems. Yet, changing the DVBv5 cache to the other delivery
+ * system should be enough for making it work.
+ */
+ switch (dvbv3_type(c->delivery_system)) {
+ case DVBV3_QPSK:
+ info->type = FE_QPSK;
+ break;
+ case DVBV3_ATSC:
+ info->type = FE_ATSC;
+ break;
+ case DVBV3_QAM:
+ info->type = FE_QAM;
+ break;
+ case DVBV3_OFDM:
+ info->type = FE_OFDM;
+ break;
+ default:
+ dev_err(fe->dvb->device,
+ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
+ __func__, c->delivery_system);
+ info->type = FE_OFDM;
+ }
+ dev_dbg(fe->dvb->device, "%s: current delivery system on cache: %d, V3 type: %d\n",
+ __func__, c->delivery_system, info->type);
+
+ /* Set CAN_INVERSION_AUTO bit on in other than oneshot mode */
+ if (!(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT))
+ info->caps |= FE_CAN_INVERSION_AUTO;
+ err = 0;
+ break;
+ }
+
+ case FE_READ_STATUS: {
+ enum fe_status *status = parg;
+
+ /* if retune was requested but hasn't occurred yet, prevent
+ * that user get signal state from previous tuning */
+ if (fepriv->state == FESTATE_RETUNE ||
+ fepriv->state == FESTATE_ERROR) {
+ err = 0;
+ *status = 0;
+ break;
+ }
+
+ if (fe->ops.read_status)
+ err = fe->ops.read_status(fe, status);
+ break;
+ }
+
+ case FE_DISEQC_RESET_OVERLOAD:
+ if (fe->ops.diseqc_reset_overload) {
+ err = fe->ops.diseqc_reset_overload(fe);
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ }
+ break;
+
+ case FE_DISEQC_SEND_MASTER_CMD:
+ if (fe->ops.diseqc_send_master_cmd) {
+ struct dvb_diseqc_master_cmd *cmd = parg;
+
+ if (cmd->msg_len > sizeof(cmd->msg)) {
+ err = -EINVAL;
+ break;
+ }
+ err = fe->ops.diseqc_send_master_cmd(fe, cmd);
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ }
+ break;
+
+ case FE_DISEQC_SEND_BURST:
+ if (fe->ops.diseqc_send_burst) {
+ err = fe->ops.diseqc_send_burst(fe, (long)parg);
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ }
+ break;
+
+ case FE_SET_TONE:
+ if (fe->ops.set_tone) {
+ fepriv->tone = (long)parg;
+ err = fe->ops.set_tone(fe, fepriv->tone);
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ }
+ break;
+
+ case FE_SET_VOLTAGE:
+ if (fe->ops.set_voltage) {
+ fepriv->voltage = (long)parg;
+ err = fe->ops.set_voltage(fe, fepriv->voltage);
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ }
+ break;
+
+ case FE_DISEQC_RECV_SLAVE_REPLY:
+ if (fe->ops.diseqc_recv_slave_reply)
+ err = fe->ops.diseqc_recv_slave_reply(fe, parg);
+ break;
+
+ case FE_ENABLE_HIGH_LNB_VOLTAGE:
+ if (fe->ops.enable_high_lnb_voltage)
+ err = fe->ops.enable_high_lnb_voltage(fe, (long)parg);
+ break;
+
+ case FE_SET_FRONTEND_TUNE_MODE:
+ fepriv->tune_mode_flags = (unsigned long)parg;
+ err = 0;
+ break;
+ /* DEPRECATED dish control ioctls */
+
+ case FE_DISHNETWORK_SEND_LEGACY_CMD:
+ if (fe->ops.dishnetwork_send_legacy_command) {
+ err = fe->ops.dishnetwork_send_legacy_command(fe,
+ (unsigned long)parg);
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ } else if (fe->ops.set_voltage) {
+ /*
+ * NOTE: This is a fallback condition. Some frontends
+ * (stv0299 for instance) take longer than 8msec to
+ * respond to a set_voltage command. Those switches
+ * need custom routines to switch properly. For all
+ * other frontends, the following should work ok.
+ * Dish network legacy switches (as used by Dish500)
+ * are controlled by sending 9-bit command words
+ * spaced 8msec apart.
+ * the actual command word is switch/port dependent
+ * so it is up to the userspace application to send
+ * the right command.
+ * The command must always start with a '0' after
+ * initialization, so parg is 8 bits and does not
+ * include the initialization or start bit
+ */
+ unsigned long swcmd = ((unsigned long)parg) << 1;
+ ktime_t nexttime;
+ ktime_t tv[10];
+ int i;
+ u8 last = 1;
+
+ if (dvb_frontend_debug)
+ dprintk("switch command: 0x%04lx\n",
+ swcmd);
+ nexttime = ktime_get_boottime();
+ if (dvb_frontend_debug)
+ tv[0] = nexttime;
+ /* before sending a command, initialize by sending
+ * a 32ms 18V to the switch
+ */
+ fe->ops.set_voltage(fe, SEC_VOLTAGE_18);
+ dvb_frontend_sleep_until(&nexttime, 32000);
+
+ for (i = 0; i < 9; i++) {
+ if (dvb_frontend_debug)
+ tv[i + 1] = ktime_get_boottime();
+ if ((swcmd & 0x01) != last) {
+ /* set voltage to (last ? 13V : 18V) */
+ fe->ops.set_voltage(fe, (last) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18);
+ last = (last) ? 0 : 1;
+ }
+ swcmd = swcmd >> 1;
+ if (i != 8)
+ dvb_frontend_sleep_until(&nexttime, 8000);
+ }
+ if (dvb_frontend_debug) {
+ dprintk("(adapter %d): switch delay (should be 32k followed by all 8k)\n",
+ fe->dvb->num);
+ for (i = 1; i < 10; i++)
+ pr_info("%d: %d\n", i,
+ (int)ktime_us_delta(tv[i], tv[i - 1]));
+ }
+ err = 0;
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ }
+ break;
+
+ /* DEPRECATED statistics ioctls */
+
+ case FE_READ_BER:
+ if (fe->ops.read_ber) {
+ if (fepriv->thread)
+ err = fe->ops.read_ber(fe, parg);
+ else
+ err = -EAGAIN;
+ }
+ break;
+
+ case FE_READ_SIGNAL_STRENGTH:
+ if (fe->ops.read_signal_strength) {
+ if (fepriv->thread)
+ err = fe->ops.read_signal_strength(fe, parg);
+ else
+ err = -EAGAIN;
+ }
+ break;
+
+ case FE_READ_SNR:
+ if (fe->ops.read_snr) {
+ if (fepriv->thread)
+ err = fe->ops.read_snr(fe, parg);
+ else
+ err = -EAGAIN;
+ }
+ break;
+
+ case FE_READ_UNCORRECTED_BLOCKS:
+ if (fe->ops.read_ucblocks) {
+ if (fepriv->thread)
+ err = fe->ops.read_ucblocks(fe, parg);
+ else
+ err = -EAGAIN;
+ }
+ break;
+
+ /* DEPRECATED DVBv3 ioctls */
+
+ case FE_SET_FRONTEND:
+ err = dvbv3_set_delivery_system(fe);
+ if (err)
+ break;
+
+ err = dtv_property_cache_sync(fe, c, parg);
+ if (err)
+ break;
+ err = dtv_set_frontend(fe);
+ break;
+
+ case FE_GET_EVENT:
+ err = dvb_frontend_get_event(fe, parg, file->f_flags);
+ break;
+
+ case FE_GET_FRONTEND:
+ err = dvb_get_frontend(fe, parg);
+ break;
+
+ default:
+ return -ENOTSUPP;
+ } /* switch */
+
+ return err;
+}
+
+static __poll_t dvb_frontend_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ dev_dbg_ratelimited(fe->dvb->device, "%s:\n", __func__);
+
+ poll_wait(file, &fepriv->events.wait_queue, wait);
+
+ if (fepriv->events.eventw != fepriv->events.eventr)
+ return (EPOLLIN | EPOLLRDNORM | EPOLLPRI);
+
+ return 0;
+}
+
+static int dvb_frontend_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_adapter *adapter = fe->dvb;
+ int ret;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+ if (fe->exit == DVB_FE_DEVICE_REMOVED)
+ return -ENODEV;
+
+ if (adapter->mfe_shared == 2) {
+ mutex_lock(&adapter->mfe_lock);
+ if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+ if (adapter->mfe_dvbdev &&
+ !adapter->mfe_dvbdev->writers) {
+ mutex_unlock(&adapter->mfe_lock);
+ return -EBUSY;
+ }
+ adapter->mfe_dvbdev = dvbdev;
+ }
+ } else if (adapter->mfe_shared) {
+ mutex_lock(&adapter->mfe_lock);
+
+ if (!adapter->mfe_dvbdev)
+ adapter->mfe_dvbdev = dvbdev;
+
+ else if (adapter->mfe_dvbdev != dvbdev) {
+ struct dvb_device
+ *mfedev = adapter->mfe_dvbdev;
+ struct dvb_frontend
+ *mfe = mfedev->priv;
+ struct dvb_frontend_private
+ *mfepriv = mfe->frontend_priv;
+ int mferetry = (dvb_mfe_wait_time << 1);
+
+ mutex_unlock(&adapter->mfe_lock);
+ while (mferetry-- && (mfedev->users != -1 ||
+ mfepriv->thread)) {
+ if (msleep_interruptible(500)) {
+ if (signal_pending(current))
+ return -EINTR;
+ }
+ }
+
+ mutex_lock(&adapter->mfe_lock);
+ if (adapter->mfe_dvbdev != dvbdev) {
+ mfedev = adapter->mfe_dvbdev;
+ mfe = mfedev->priv;
+ mfepriv = mfe->frontend_priv;
+ if (mfedev->users != -1 ||
+ mfepriv->thread) {
+ mutex_unlock(&adapter->mfe_lock);
+ return -EBUSY;
+ }
+ adapter->mfe_dvbdev = dvbdev;
+ }
+ }
+ }
+
+ if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) {
+ if ((ret = fe->ops.ts_bus_ctrl(fe, 1)) < 0)
+ goto err0;
+
+ /* If we took control of the bus, we need to force
+ reinitialization. This is because many ts_bus_ctrl()
+ functions strobe the RESET pin on the demod, and if the
+ frontend thread already exists then the dvb_init() routine
+ won't get called (which is what usually does initial
+ register configuration). */
+ fepriv->reinitialise = 1;
+ }
+
+ if ((ret = dvb_generic_open(inode, file)) < 0)
+ goto err1;
+
+ if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+ /* normal tune mode when opened R/W */
+ fepriv->tune_mode_flags &= ~FE_TUNE_MODE_ONESHOT;
+ fepriv->tone = -1;
+ fepriv->voltage = -1;
+
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ mutex_lock(&fe->dvb->mdev_lock);
+ if (fe->dvb->mdev) {
+ mutex_lock(&fe->dvb->mdev->graph_mutex);
+ if (fe->dvb->mdev->enable_source)
+ ret = fe->dvb->mdev->enable_source(
+ dvbdev->entity,
+ &fepriv->pipe);
+ mutex_unlock(&fe->dvb->mdev->graph_mutex);
+ if (ret) {
+ mutex_unlock(&fe->dvb->mdev_lock);
+ dev_err(fe->dvb->device,
+ "Tuner is busy. Error %d\n", ret);
+ goto err2;
+ }
+ }
+ mutex_unlock(&fe->dvb->mdev_lock);
+#endif
+ ret = dvb_frontend_start(fe);
+ if (ret)
+ goto err3;
+
+ /* empty event queue */
+ fepriv->events.eventr = fepriv->events.eventw = 0;
+ }
+
+ dvb_frontend_get(fe);
+
+ if (adapter->mfe_shared)
+ mutex_unlock(&adapter->mfe_lock);
+ return ret;
+
+err3:
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ mutex_lock(&fe->dvb->mdev_lock);
+ if (fe->dvb->mdev) {
+ mutex_lock(&fe->dvb->mdev->graph_mutex);
+ if (fe->dvb->mdev->disable_source)
+ fe->dvb->mdev->disable_source(dvbdev->entity);
+ mutex_unlock(&fe->dvb->mdev->graph_mutex);
+ }
+ mutex_unlock(&fe->dvb->mdev_lock);
+err2:
+#endif
+ dvb_generic_release(inode, file);
+err1:
+ if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl)
+ fe->ops.ts_bus_ctrl(fe, 0);
+err0:
+ if (adapter->mfe_shared)
+ mutex_unlock(&adapter->mfe_lock);
+ return ret;
+}
+
+static int dvb_frontend_release(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ int ret;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+ fepriv->release_jiffies = jiffies;
+ mb();
+ }
+
+ ret = dvb_generic_release(inode, file);
+
+ if (dvbdev->users == -1) {
+ wake_up(&fepriv->wait_queue);
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ mutex_lock(&fe->dvb->mdev_lock);
+ if (fe->dvb->mdev) {
+ mutex_lock(&fe->dvb->mdev->graph_mutex);
+ if (fe->dvb->mdev->disable_source)
+ fe->dvb->mdev->disable_source(dvbdev->entity);
+ mutex_unlock(&fe->dvb->mdev->graph_mutex);
+ }
+ mutex_unlock(&fe->dvb->mdev_lock);
+#endif
+ if (fe->exit != DVB_FE_NO_EXIT)
+ wake_up(&dvbdev->wait_queue);
+ if (fe->ops.ts_bus_ctrl)
+ fe->ops.ts_bus_ctrl(fe, 0);
+ }
+
+ dvb_frontend_put(fe);
+
+ return ret;
+}
+
+static const struct file_operations dvb_frontend_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = dvb_frontend_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = dvb_frontend_compat_ioctl,
+#endif
+ .poll = dvb_frontend_poll,
+ .open = dvb_frontend_open,
+ .release = dvb_frontend_release,
+ .llseek = noop_llseek,
+};
+
+int dvb_frontend_suspend(struct dvb_frontend *fe)
+{
+ int ret = 0;
+
+ dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num,
+ fe->id);
+
+ if (fe->ops.tuner_ops.suspend)
+ ret = fe->ops.tuner_ops.suspend(fe);
+ else if (fe->ops.tuner_ops.sleep)
+ ret = fe->ops.tuner_ops.sleep(fe);
+
+ if (fe->ops.suspend)
+ ret = fe->ops.suspend(fe);
+ else if (fe->ops.sleep)
+ ret = fe->ops.sleep(fe);
+
+ return ret;
+}
+EXPORT_SYMBOL(dvb_frontend_suspend);
+
+int dvb_frontend_resume(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ int ret = 0;
+
+ dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num,
+ fe->id);
+
+ fe->exit = DVB_FE_DEVICE_RESUME;
+ if (fe->ops.resume)
+ ret = fe->ops.resume(fe);
+ else if (fe->ops.init)
+ ret = fe->ops.init(fe);
+
+ if (fe->ops.tuner_ops.resume)
+ ret = fe->ops.tuner_ops.resume(fe);
+ else if (fe->ops.tuner_ops.init)
+ ret = fe->ops.tuner_ops.init(fe);
+
+ if (fe->ops.set_tone && fepriv->tone != -1)
+ fe->ops.set_tone(fe, fepriv->tone);
+ if (fe->ops.set_voltage && fepriv->voltage != -1)
+ fe->ops.set_voltage(fe, fepriv->voltage);
+
+ fe->exit = DVB_FE_NO_EXIT;
+ fepriv->state = FESTATE_RETUNE;
+ dvb_frontend_wakeup(fe);
+
+ return ret;
+}
+EXPORT_SYMBOL(dvb_frontend_resume);
+
+int dvb_register_frontend(struct dvb_adapter *dvb,
+ struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv;
+ const struct dvb_device dvbdev_template = {
+ .users = ~0,
+ .writers = 1,
+ .readers = (~0) - 1,
+ .fops = &dvb_frontend_fops,
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+ .name = fe->ops.info.name,
+#endif
+ };
+ int ret;
+
+ dev_dbg(dvb->device, "%s:\n", __func__);
+
+ if (mutex_lock_interruptible(&frontend_mutex))
+ return -ERESTARTSYS;
+
+ fe->frontend_priv = kzalloc(sizeof(struct dvb_frontend_private), GFP_KERNEL);
+ if (!fe->frontend_priv) {
+ mutex_unlock(&frontend_mutex);
+ return -ENOMEM;
+ }
+ fepriv = fe->frontend_priv;
+
+ kref_init(&fe->refcount);
+
+ /*
+ * After initialization, there need to be two references: one
+ * for dvb_unregister_frontend(), and another one for
+ * dvb_frontend_detach().
+ */
+ dvb_frontend_get(fe);
+
+ sema_init(&fepriv->sem, 1);
+ init_waitqueue_head(&fepriv->wait_queue);
+ init_waitqueue_head(&fepriv->events.wait_queue);
+ mutex_init(&fepriv->events.mtx);
+ fe->dvb = dvb;
+ fepriv->inversion = INVERSION_OFF;
+
+ dev_info(fe->dvb->device,
+ "DVB: registering adapter %i frontend %i (%s)...\n",
+ fe->dvb->num, fe->id, fe->ops.info.name);
+
+ ret = dvb_register_device(fe->dvb, &fepriv->dvbdev, &dvbdev_template,
+ fe, DVB_DEVICE_FRONTEND, 0);
+ if (ret) {
+ dvb_frontend_put(fe);
+ mutex_unlock(&frontend_mutex);
+ return ret;
+ }
+
+ /*
+ * Initialize the cache to the proper values according with the
+ * first supported delivery system (ops->delsys[0])
+ */
+
+ fe->dtv_property_cache.delivery_system = fe->ops.delsys[0];
+ dvb_frontend_clear_cache(fe);
+
+ mutex_unlock(&frontend_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(dvb_register_frontend);
+
+int dvb_unregister_frontend(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ mutex_lock(&frontend_mutex);
+ dvb_frontend_stop(fe);
+ dvb_remove_device(fepriv->dvbdev);
+
+ /* fe is invalid now */
+ mutex_unlock(&frontend_mutex);
+ dvb_frontend_put(fe);
+ return 0;
+}
+EXPORT_SYMBOL(dvb_unregister_frontend);
+
+static void dvb_frontend_invoke_release(struct dvb_frontend *fe,
+ void (*release)(struct dvb_frontend *fe))
+{
+ if (release) {
+ release(fe);
+#ifdef CONFIG_MEDIA_ATTACH
+ dvb_detach(release);
+#endif
+ }
+}
+
+void dvb_frontend_detach(struct dvb_frontend *fe)
+{
+ dvb_frontend_invoke_release(fe, fe->ops.release_sec);
+ dvb_frontend_invoke_release(fe, fe->ops.tuner_ops.release);
+ dvb_frontend_invoke_release(fe, fe->ops.analog_ops.release);
+ dvb_frontend_put(fe);
+}
+EXPORT_SYMBOL(dvb_frontend_detach);
diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c
new file mode 100644
index 000000000000..8bb8dd34c223
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_net.c
@@ -0,0 +1,1664 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * dvb_net.c
+ *
+ * Copyright (C) 2001 Convergence integrated media GmbH
+ * Ralph Metzler <ralph@convergence.de>
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * ULE Decapsulation code:
+ * Copyright (C) 2003, 2004 gcs - Global Communication & Services GmbH.
+ * and Department of Scientific Computing
+ * Paris Lodron University of Salzburg.
+ * Hilmar Linder <hlinder@cosy.sbg.ac.at>
+ * and Wolfram Stering <wstering@cosy.sbg.ac.at>
+ *
+ * ULE Decaps according to RFC 4326.
+ */
+
+/*
+ * ULE ChangeLog:
+ * Feb 2004: hl/ws v1: Implementing draft-fair-ipdvb-ule-01.txt
+ *
+ * Dec 2004: hl/ws v2: Implementing draft-ietf-ipdvb-ule-03.txt:
+ * ULE Extension header handling.
+ * Bugreports by Moritz Vieth and Hanno Tersteegen,
+ * Fraunhofer Institute for Open Communication Systems
+ * Competence Center for Advanced Satellite Communications.
+ * Bugfixes and robustness improvements.
+ * Filtering on dest MAC addresses, if present (D-Bit = 0)
+ * DVB_ULE_DEBUG compile-time option.
+ * Apr 2006: cp v3: Bugfixes and compliency with RFC 4326 (ULE) by
+ * Christian Praehauser <cpraehaus@cosy.sbg.ac.at>,
+ * Paris Lodron University of Salzburg.
+ */
+
+/*
+ * FIXME / TODO (dvb_net.c):
+ *
+ * Unloading does not work for 2.6.9 kernels: a refcount doesn't go to zero.
+ *
+ */
+
+#define pr_fmt(fmt) "dvb_net: " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/nospec.h>
+#include <linux/etherdevice.h>
+#include <linux/dvb/net.h>
+#include <linux/uio.h>
+#include <linux/uaccess.h>
+#include <linux/crc32.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+
+#include <media/dvb_demux.h>
+#include <media/dvb_net.h>
+
+static inline __u32 iov_crc32( __u32 c, struct kvec *iov, unsigned int cnt )
+{
+ unsigned int j;
+ for (j = 0; j < cnt; j++)
+ c = crc32_be( c, iov[j].iov_base, iov[j].iov_len );
+ return c;
+}
+
+
+#define DVB_NET_MULTICAST_MAX 10
+
+#ifdef DVB_ULE_DEBUG
+/*
+ * The code inside DVB_ULE_DEBUG keeps a history of the
+ * last 100 TS cells processed.
+ */
+static unsigned char ule_hist[100*TS_SZ] = { 0 };
+static unsigned char *ule_where = ule_hist, ule_dump;
+
+static void hexdump(const unsigned char *buf, unsigned short len)
+{
+ print_hex_dump_debug("", DUMP_PREFIX_OFFSET, 16, 1, buf, len, true);
+}
+#endif
+
+struct dvb_net_priv {
+ int in_use;
+ u16 pid;
+ struct net_device *net;
+ struct dvb_net *host;
+ struct dmx_demux *demux;
+ struct dmx_section_feed *secfeed;
+ struct dmx_section_filter *secfilter;
+ struct dmx_ts_feed *tsfeed;
+ int multi_num;
+ struct dmx_section_filter *multi_secfilter[DVB_NET_MULTICAST_MAX];
+ unsigned char multi_macs[DVB_NET_MULTICAST_MAX][6];
+ int rx_mode;
+#define RX_MODE_UNI 0
+#define RX_MODE_MULTI 1
+#define RX_MODE_ALL_MULTI 2
+#define RX_MODE_PROMISC 3
+ struct work_struct set_multicast_list_wq;
+ struct work_struct restart_net_feed_wq;
+ unsigned char feedtype; /* Either FEED_TYPE_ or FEED_TYPE_ULE */
+ int need_pusi; /* Set to 1, if synchronization on PUSI required. */
+ unsigned char tscc; /* TS continuity counter after sync on PUSI. */
+ struct sk_buff *ule_skb; /* ULE SNDU decodes into this buffer. */
+ unsigned char *ule_next_hdr; /* Pointer into skb to next ULE extension header. */
+ unsigned short ule_sndu_len; /* ULE SNDU length in bytes, w/o D-Bit. */
+ unsigned short ule_sndu_type; /* ULE SNDU type field, complete. */
+ unsigned char ule_sndu_type_1; /* ULE SNDU type field, if split across 2 TS cells. */
+ unsigned char ule_dbit; /* Whether the DestMAC address present
+ * or not (bit is set). */
+ unsigned char ule_bridged; /* Whether the ULE_BRIDGED extension header was found. */
+ int ule_sndu_remain; /* Nr. of bytes still required for current ULE SNDU. */
+ unsigned long ts_count; /* Current ts cell counter. */
+ struct mutex mutex;
+};
+
+
+/*
+ * Determine the packet's protocol ID. The rule here is that we
+ * assume 802.3 if the type field is short enough to be a length.
+ * This is normal practice and works for any 'now in use' protocol.
+ *
+ * stolen from eth.c out of the linux kernel, hacked for dvb-device
+ * by Michael Holzt <kju@debian.org>
+ */
+static __be16 dvb_net_eth_type_trans(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ethhdr *eth;
+ unsigned char *rawp;
+
+ skb_reset_mac_header(skb);
+ skb_pull(skb,dev->hard_header_len);
+ eth = eth_hdr(skb);
+
+ if (*eth->h_dest & 1) {
+ if(ether_addr_equal(eth->h_dest,dev->broadcast))
+ skb->pkt_type=PACKET_BROADCAST;
+ else
+ skb->pkt_type=PACKET_MULTICAST;
+ }
+
+ if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN)
+ return eth->h_proto;
+
+ rawp = skb->data;
+
+ /*
+ * This is a magic hack to spot IPX packets. Older Novell breaks
+ * the protocol design and runs IPX over 802.3 without an 802.2 LLC
+ * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+ * won't work for fault tolerant netware but does for the rest.
+ */
+ if (*(unsigned short *)rawp == 0xFFFF)
+ return htons(ETH_P_802_3);
+
+ /*
+ * Real 802.2 LLC
+ */
+ return htons(ETH_P_802_2);
+}
+
+#define TS_SZ 188
+#define TS_SYNC 0x47
+#define TS_TEI 0x80
+#define TS_SC 0xC0
+#define TS_PUSI 0x40
+#define TS_AF_A 0x20
+#define TS_AF_D 0x10
+
+/* ULE Extension Header handlers. */
+
+#define ULE_TEST 0
+#define ULE_BRIDGED 1
+
+#define ULE_OPTEXTHDR_PADDING 0
+
+static int ule_test_sndu( struct dvb_net_priv *p )
+{
+ return -1;
+}
+
+static int ule_bridged_sndu( struct dvb_net_priv *p )
+{
+ struct ethhdr *hdr = (struct ethhdr*) p->ule_next_hdr;
+ if(ntohs(hdr->h_proto) < ETH_P_802_3_MIN) {
+ int framelen = p->ule_sndu_len - ((p->ule_next_hdr+sizeof(struct ethhdr)) - p->ule_skb->data);
+ /* A frame Type < ETH_P_802_3_MIN for a bridged frame, introduces a LLC Length field. */
+ if(framelen != ntohs(hdr->h_proto)) {
+ return -1;
+ }
+ }
+ /* Note:
+ * From RFC4326:
+ * "A bridged SNDU is a Mandatory Extension Header of Type 1.
+ * It must be the final (or only) extension header specified in the header chain of a SNDU."
+ * The 'ule_bridged' flag will cause the extension header processing loop to terminate.
+ */
+ p->ule_bridged = 1;
+ return 0;
+}
+
+static int ule_exthdr_padding(struct dvb_net_priv *p)
+{
+ return 0;
+}
+
+/*
+ * Handle ULE extension headers.
+ * Function is called after a successful CRC32 verification of an ULE SNDU to complete its decoding.
+ * Returns: >= 0: nr. of bytes consumed by next extension header
+ * -1: Mandatory extension header that is not recognized or TEST SNDU; discard.
+ */
+static int handle_one_ule_extension( struct dvb_net_priv *p )
+{
+ /* Table of mandatory extension header handlers. The header type is the index. */
+ static int (*ule_mandatory_ext_handlers[255])( struct dvb_net_priv *p ) =
+ { [0] = ule_test_sndu, [1] = ule_bridged_sndu, [2] = NULL, };
+
+ /* Table of optional extension header handlers. The header type is the index. */
+ static int (*ule_optional_ext_handlers[255])( struct dvb_net_priv *p ) =
+ { [0] = ule_exthdr_padding, [1] = NULL, };
+
+ int ext_len = 0;
+ unsigned char hlen = (p->ule_sndu_type & 0x0700) >> 8;
+ unsigned char htype = p->ule_sndu_type & 0x00FF;
+
+ /* Discriminate mandatory and optional extension headers. */
+ if (hlen == 0) {
+ /* Mandatory extension header */
+ if (ule_mandatory_ext_handlers[htype]) {
+ ext_len = ule_mandatory_ext_handlers[htype]( p );
+ if(ext_len >= 0) {
+ p->ule_next_hdr += ext_len;
+ if (!p->ule_bridged) {
+ p->ule_sndu_type = ntohs(*(__be16 *)p->ule_next_hdr);
+ p->ule_next_hdr += 2;
+ } else {
+ p->ule_sndu_type = ntohs(*(__be16 *)(p->ule_next_hdr + ((p->ule_dbit ? 2 : 3) * ETH_ALEN)));
+ /* This assures the extension handling loop will terminate. */
+ }
+ }
+ // else: extension handler failed or SNDU should be discarded
+ } else
+ ext_len = -1; /* SNDU has to be discarded. */
+ } else {
+ /* Optional extension header. Calculate the length. */
+ ext_len = hlen << 1;
+ /* Process the optional extension header according to its type. */
+ if (ule_optional_ext_handlers[htype])
+ (void)ule_optional_ext_handlers[htype]( p );
+ p->ule_next_hdr += ext_len;
+ p->ule_sndu_type = ntohs( *(__be16 *)(p->ule_next_hdr-2) );
+ /*
+ * note: the length of the next header type is included in the
+ * length of THIS optional extension header
+ */
+ }
+
+ return ext_len;
+}
+
+static int handle_ule_extensions( struct dvb_net_priv *p )
+{
+ int total_ext_len = 0, l;
+
+ p->ule_next_hdr = p->ule_skb->data;
+ do {
+ l = handle_one_ule_extension( p );
+ if (l < 0)
+ return l; /* Stop extension header processing and discard SNDU. */
+ total_ext_len += l;
+ pr_debug("ule_next_hdr=%p, ule_sndu_type=%i, l=%i, total_ext_len=%i\n",
+ p->ule_next_hdr, (int)p->ule_sndu_type,
+ l, total_ext_len);
+
+ } while (p->ule_sndu_type < ETH_P_802_3_MIN);
+
+ return total_ext_len;
+}
+
+
+/* Prepare for a new ULE SNDU: reset the decoder state. */
+static inline void reset_ule( struct dvb_net_priv *p )
+{
+ p->ule_skb = NULL;
+ p->ule_next_hdr = NULL;
+ p->ule_sndu_len = 0;
+ p->ule_sndu_type = 0;
+ p->ule_sndu_type_1 = 0;
+ p->ule_sndu_remain = 0;
+ p->ule_dbit = 0xFF;
+ p->ule_bridged = 0;
+}
+
+/*
+ * Decode ULE SNDUs according to draft-ietf-ipdvb-ule-03.txt from a sequence of
+ * TS cells of a single PID.
+ */
+
+struct dvb_net_ule_handle {
+ struct net_device *dev;
+ struct dvb_net_priv *priv;
+ struct ethhdr *ethh;
+ const u8 *buf;
+ size_t buf_len;
+ unsigned long skipped;
+ const u8 *ts, *ts_end, *from_where;
+ u8 ts_remain, how_much, new_ts;
+ bool error;
+};
+
+static int dvb_net_ule_new_ts_cell(struct dvb_net_ule_handle *h)
+{
+ /* We are about to process a new TS cell. */
+
+#ifdef DVB_ULE_DEBUG
+ if (ule_where >= &ule_hist[100*TS_SZ])
+ ule_where = ule_hist;
+ memcpy(ule_where, h->ts, TS_SZ);
+ if (ule_dump) {
+ hexdump(ule_where, TS_SZ);
+ ule_dump = 0;
+ }
+ ule_where += TS_SZ;
+#endif
+
+ /*
+ * Check TS h->error conditions: sync_byte, transport_error_indicator,
+ * scrambling_control .
+ */
+ if ((h->ts[0] != TS_SYNC) || (h->ts[1] & TS_TEI) ||
+ ((h->ts[3] & TS_SC) != 0)) {
+ pr_warn("%lu: Invalid TS cell: SYNC %#x, TEI %u, SC %#x.\n",
+ h->priv->ts_count, h->ts[0],
+ (h->ts[1] & TS_TEI) >> 7,
+ (h->ts[3] & TS_SC) >> 6);
+
+ /* Drop partly decoded SNDU, reset state, resync on PUSI. */
+ if (h->priv->ule_skb) {
+ dev_kfree_skb(h->priv->ule_skb);
+ /* Prepare for next SNDU. */
+ h->dev->stats.rx_errors++;
+ h->dev->stats.rx_frame_errors++;
+ }
+ reset_ule(h->priv);
+ h->priv->need_pusi = 1;
+
+ /* Continue with next TS cell. */
+ h->ts += TS_SZ;
+ h->priv->ts_count++;
+ return 1;
+ }
+
+ h->ts_remain = 184;
+ h->from_where = h->ts + 4;
+
+ return 0;
+}
+
+static int dvb_net_ule_ts_pusi(struct dvb_net_ule_handle *h)
+{
+ if (h->ts[1] & TS_PUSI) {
+ /* Find beginning of first ULE SNDU in current TS cell. */
+ /* Synchronize continuity counter. */
+ h->priv->tscc = h->ts[3] & 0x0F;
+ /* There is a pointer field here. */
+ if (h->ts[4] > h->ts_remain) {
+ pr_err("%lu: Invalid ULE packet (pointer field %d)\n",
+ h->priv->ts_count, h->ts[4]);
+ h->ts += TS_SZ;
+ h->priv->ts_count++;
+ return 1;
+ }
+ /* Skip to destination of pointer field. */
+ h->from_where = &h->ts[5] + h->ts[4];
+ h->ts_remain -= 1 + h->ts[4];
+ h->skipped = 0;
+ } else {
+ h->skipped++;
+ h->ts += TS_SZ;
+ h->priv->ts_count++;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int dvb_net_ule_new_ts(struct dvb_net_ule_handle *h)
+{
+ /* Check continuity counter. */
+ if ((h->ts[3] & 0x0F) == h->priv->tscc)
+ h->priv->tscc = (h->priv->tscc + 1) & 0x0F;
+ else {
+ /* TS discontinuity handling: */
+ pr_warn("%lu: TS discontinuity: got %#x, expected %#x.\n",
+ h->priv->ts_count, h->ts[3] & 0x0F,
+ h->priv->tscc);
+ /* Drop partly decoded SNDU, reset state, resync on PUSI. */
+ if (h->priv->ule_skb) {
+ dev_kfree_skb(h->priv->ule_skb);
+ /* Prepare for next SNDU. */
+ // reset_ule(h->priv); moved to below.
+ h->dev->stats.rx_errors++;
+ h->dev->stats.rx_frame_errors++;
+ }
+ reset_ule(h->priv);
+ /* skip to next PUSI. */
+ h->priv->need_pusi = 1;
+ return 1;
+ }
+ /*
+ * If we still have an incomplete payload, but PUSI is
+ * set; some TS cells are missing.
+ * This is only possible here, if we missed exactly 16 TS
+ * cells (continuity counter wrap).
+ */
+ if (h->ts[1] & TS_PUSI) {
+ if (!h->priv->need_pusi) {
+ if (!(*h->from_where < (h->ts_remain-1)) ||
+ *h->from_where != h->priv->ule_sndu_remain) {
+ /*
+ * Pointer field is invalid.
+ * Drop this TS cell and any started ULE SNDU.
+ */
+ pr_warn("%lu: Invalid pointer field: %u.\n",
+ h->priv->ts_count,
+ *h->from_where);
+
+ /*
+ * Drop partly decoded SNDU, reset state,
+ * resync on PUSI.
+ */
+ if (h->priv->ule_skb) {
+ h->error = true;
+ dev_kfree_skb(h->priv->ule_skb);
+ }
+
+ if (h->error || h->priv->ule_sndu_remain) {
+ h->dev->stats.rx_errors++;
+ h->dev->stats.rx_frame_errors++;
+ h->error = false;
+ }
+
+ reset_ule(h->priv);
+ h->priv->need_pusi = 1;
+ return 1;
+ }
+ /*
+ * Skip pointer field (we're processing a
+ * packed payload).
+ */
+ h->from_where += 1;
+ h->ts_remain -= 1;
+ } else
+ h->priv->need_pusi = 0;
+
+ if (h->priv->ule_sndu_remain > 183) {
+ /*
+ * Current SNDU lacks more data than there
+ * could be available in the current TS cell.
+ */
+ h->dev->stats.rx_errors++;
+ h->dev->stats.rx_length_errors++;
+ pr_warn("%lu: Expected %d more SNDU bytes, but got PUSI (pf %d, h->ts_remain %d). Flushing incomplete payload.\n",
+ h->priv->ts_count,
+ h->priv->ule_sndu_remain,
+ h->ts[4], h->ts_remain);
+ dev_kfree_skb(h->priv->ule_skb);
+ /* Prepare for next SNDU. */
+ reset_ule(h->priv);
+ /*
+ * Resync: go to where pointer field points to:
+ * start of next ULE SNDU.
+ */
+ h->from_where += h->ts[4];
+ h->ts_remain -= h->ts[4];
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Start a new payload with skb.
+ * Find ULE header. It is only guaranteed that the
+ * length field (2 bytes) is contained in the current
+ * TS.
+ * Check h.ts_remain has to be >= 2 here.
+ */
+static int dvb_net_ule_new_payload(struct dvb_net_ule_handle *h)
+{
+ if (h->ts_remain < 2) {
+ pr_warn("Invalid payload packing: only %d bytes left in TS. Resyncing.\n",
+ h->ts_remain);
+ h->priv->ule_sndu_len = 0;
+ h->priv->need_pusi = 1;
+ h->ts += TS_SZ;
+ return 1;
+ }
+
+ if (!h->priv->ule_sndu_len) {
+ /* Got at least two bytes, thus extrace the SNDU length. */
+ h->priv->ule_sndu_len = h->from_where[0] << 8 |
+ h->from_where[1];
+ if (h->priv->ule_sndu_len & 0x8000) {
+ /* D-Bit is set: no dest mac present. */
+ h->priv->ule_sndu_len &= 0x7FFF;
+ h->priv->ule_dbit = 1;
+ } else
+ h->priv->ule_dbit = 0;
+
+ if (h->priv->ule_sndu_len < 5) {
+ pr_warn("%lu: Invalid ULE SNDU length %u. Resyncing.\n",
+ h->priv->ts_count,
+ h->priv->ule_sndu_len);
+ h->dev->stats.rx_errors++;
+ h->dev->stats.rx_length_errors++;
+ h->priv->ule_sndu_len = 0;
+ h->priv->need_pusi = 1;
+ h->new_ts = 1;
+ h->ts += TS_SZ;
+ h->priv->ts_count++;
+ return 1;
+ }
+ h->ts_remain -= 2; /* consume the 2 bytes SNDU length. */
+ h->from_where += 2;
+ }
+
+ h->priv->ule_sndu_remain = h->priv->ule_sndu_len + 2;
+ /*
+ * State of current TS:
+ * h->ts_remain (remaining bytes in the current TS cell)
+ * 0 ule_type is not available now, we need the next TS cell
+ * 1 the first byte of the ule_type is present
+ * >=2 full ULE header present, maybe some payload data as well.
+ */
+ switch (h->ts_remain) {
+ case 1:
+ h->priv->ule_sndu_remain--;
+ h->priv->ule_sndu_type = h->from_where[0] << 8;
+
+ /* first byte of ule_type is set. */
+ h->priv->ule_sndu_type_1 = 1;
+ h->ts_remain -= 1;
+ h->from_where += 1;
+ fallthrough;
+ case 0:
+ h->new_ts = 1;
+ h->ts += TS_SZ;
+ h->priv->ts_count++;
+ return 1;
+
+ default: /* complete ULE header is present in current TS. */
+ /* Extract ULE type field. */
+ if (h->priv->ule_sndu_type_1) {
+ h->priv->ule_sndu_type_1 = 0;
+ h->priv->ule_sndu_type |= h->from_where[0];
+ h->from_where += 1; /* points to payload start. */
+ h->ts_remain -= 1;
+ } else {
+ /* Complete type is present in new TS. */
+ h->priv->ule_sndu_type = h->from_where[0] << 8 |
+ h->from_where[1];
+ h->from_where += 2; /* points to payload start. */
+ h->ts_remain -= 2;
+ }
+ break;
+ }
+
+ /*
+ * Allocate the skb (decoder target buffer) with the correct size,
+ * as follows:
+ *
+ * prepare for the largest case: bridged SNDU with MAC address
+ * (dbit = 0).
+ */
+ h->priv->ule_skb = dev_alloc_skb(h->priv->ule_sndu_len +
+ ETH_HLEN + ETH_ALEN);
+ if (!h->priv->ule_skb) {
+ pr_notice("%s: Memory squeeze, dropping packet.\n",
+ h->dev->name);
+ h->dev->stats.rx_dropped++;
+ return -1;
+ }
+
+ /* This includes the CRC32 _and_ dest mac, if !dbit. */
+ h->priv->ule_sndu_remain = h->priv->ule_sndu_len;
+ h->priv->ule_skb->dev = h->dev;
+ /*
+ * Leave space for Ethernet or bridged SNDU header
+ * (eth hdr plus one MAC addr).
+ */
+ skb_reserve(h->priv->ule_skb, ETH_HLEN + ETH_ALEN);
+
+ return 0;
+}
+
+
+static int dvb_net_ule_should_drop(struct dvb_net_ule_handle *h)
+{
+ static const u8 bc_addr[ETH_ALEN] = { [0 ... ETH_ALEN - 1] = 0xff };
+
+ /*
+ * The destination MAC address is the next data in the skb. It comes
+ * before any extension headers.
+ *
+ * Check if the payload of this SNDU should be passed up the stack.
+ */
+ if (h->priv->rx_mode == RX_MODE_PROMISC)
+ return 0;
+
+ if (h->priv->ule_skb->data[0] & 0x01) {
+ /* multicast or broadcast */
+ if (!ether_addr_equal(h->priv->ule_skb->data, bc_addr)) {
+ /* multicast */
+ if (h->priv->rx_mode == RX_MODE_MULTI) {
+ int i;
+
+ for (i = 0; i < h->priv->multi_num &&
+ !ether_addr_equal(h->priv->ule_skb->data,
+ h->priv->multi_macs[i]);
+ i++)
+ ;
+ if (i == h->priv->multi_num)
+ return 1;
+ } else if (h->priv->rx_mode != RX_MODE_ALL_MULTI)
+ return 1; /* no broadcast; */
+ /*
+ * else:
+ * all multicast mode: accept all multicast packets
+ */
+ }
+ /* else: broadcast */
+ } else if (!ether_addr_equal(h->priv->ule_skb->data, h->dev->dev_addr))
+ return 1;
+
+ return 0;
+}
+
+
+static void dvb_net_ule_check_crc(struct dvb_net_ule_handle *h,
+ struct kvec iov[3],
+ u32 ule_crc, u32 expected_crc)
+{
+ u8 dest_addr[ETH_ALEN];
+
+ if (ule_crc != expected_crc) {
+ pr_warn("%lu: CRC32 check FAILED: %08x / %08x, SNDU len %d type %#x, ts_remain %d, next 2: %x.\n",
+ h->priv->ts_count, ule_crc, expected_crc,
+ h->priv->ule_sndu_len, h->priv->ule_sndu_type,
+ h->ts_remain,
+ h->ts_remain > 2 ?
+ *(unsigned short *)h->from_where : 0);
+
+ #ifdef DVB_ULE_DEBUG
+ hexdump(iov[0].iov_base, iov[0].iov_len);
+ hexdump(iov[1].iov_base, iov[1].iov_len);
+ hexdump(iov[2].iov_base, iov[2].iov_len);
+
+ if (ule_where == ule_hist) {
+ hexdump(&ule_hist[98*TS_SZ], TS_SZ);
+ hexdump(&ule_hist[99*TS_SZ], TS_SZ);
+ } else if (ule_where == &ule_hist[TS_SZ]) {
+ hexdump(&ule_hist[99*TS_SZ], TS_SZ);
+ hexdump(ule_hist, TS_SZ);
+ } else {
+ hexdump(ule_where - TS_SZ - TS_SZ, TS_SZ);
+ hexdump(ule_where - TS_SZ, TS_SZ);
+ }
+ ule_dump = 1;
+ #endif
+
+ h->dev->stats.rx_errors++;
+ h->dev->stats.rx_crc_errors++;
+ dev_kfree_skb(h->priv->ule_skb);
+
+ return;
+ }
+
+ /* CRC32 verified OK. */
+
+ /* CRC32 was OK, so remove it from skb. */
+ h->priv->ule_skb->tail -= 4;
+ h->priv->ule_skb->len -= 4;
+
+ if (!h->priv->ule_dbit) {
+ if (dvb_net_ule_should_drop(h)) {
+ netdev_dbg(h->dev,
+ "Dropping SNDU: MAC destination address does not match: dest addr: %pM, h->dev addr: %pM\n",
+ h->priv->ule_skb->data, h->dev->dev_addr);
+ dev_kfree_skb(h->priv->ule_skb);
+ return;
+ }
+
+ skb_copy_from_linear_data(h->priv->ule_skb, dest_addr,
+ ETH_ALEN);
+ skb_pull(h->priv->ule_skb, ETH_ALEN);
+ } else {
+ /* dest_addr buffer is only valid if h->priv->ule_dbit == 0 */
+ eth_zero_addr(dest_addr);
+ }
+
+ /* Handle ULE Extension Headers. */
+ if (h->priv->ule_sndu_type < ETH_P_802_3_MIN) {
+ /* There is an extension header. Handle it accordingly. */
+ int l = handle_ule_extensions(h->priv);
+
+ if (l < 0) {
+ /*
+ * Mandatory extension header unknown or TEST SNDU.
+ * Drop it.
+ */
+
+ // pr_warn("Dropping SNDU, extension headers.\n" );
+ dev_kfree_skb(h->priv->ule_skb);
+ return;
+ }
+ skb_pull(h->priv->ule_skb, l);
+ }
+
+ /*
+ * Construct/assure correct ethernet header.
+ * Note: in bridged mode (h->priv->ule_bridged != 0)
+ * we already have the (original) ethernet
+ * header at the start of the payload (after
+ * optional dest. address and any extension
+ * headers).
+ */
+ if (!h->priv->ule_bridged) {
+ skb_push(h->priv->ule_skb, ETH_HLEN);
+ h->ethh = (struct ethhdr *)h->priv->ule_skb->data;
+ memcpy(h->ethh->h_dest, dest_addr, ETH_ALEN);
+ eth_zero_addr(h->ethh->h_source);
+ h->ethh->h_proto = htons(h->priv->ule_sndu_type);
+ }
+ /* else: skb is in correct state; nothing to do. */
+ h->priv->ule_bridged = 0;
+
+ /* Stuff into kernel's protocol stack. */
+ h->priv->ule_skb->protocol = dvb_net_eth_type_trans(h->priv->ule_skb,
+ h->dev);
+ /*
+ * If D-bit is set (i.e. destination MAC address not present),
+ * receive the packet anyhow.
+ */
+#if 0
+ if (h->priv->ule_dbit && skb->pkt_type == PACKET_OTHERHOST)
+ h->priv->ule_skb->pkt_type = PACKET_HOST;
+#endif
+ h->dev->stats.rx_packets++;
+ h->dev->stats.rx_bytes += h->priv->ule_skb->len;
+ netif_rx(h->priv->ule_skb);
+}
+
+static void dvb_net_ule(struct net_device *dev, const u8 *buf, size_t buf_len)
+{
+ int ret;
+ struct dvb_net_ule_handle h = {
+ .dev = dev,
+ .priv = netdev_priv(dev),
+ .ethh = NULL,
+ .buf = buf,
+ .buf_len = buf_len,
+ .skipped = 0L,
+ .ts = NULL,
+ .ts_end = NULL,
+ .from_where = NULL,
+ .ts_remain = 0,
+ .how_much = 0,
+ .new_ts = 1,
+ .error = false,
+ };
+
+ /*
+ * For all TS cells in current buffer.
+ * Appearently, we are called for every single TS cell.
+ */
+ for (h.ts = h.buf, h.ts_end = h.buf + h.buf_len;
+ h.ts < h.ts_end; /* no incr. */) {
+ if (h.new_ts) {
+ /* We are about to process a new TS cell. */
+ if (dvb_net_ule_new_ts_cell(&h))
+ continue;
+ }
+
+ /* Synchronize on PUSI, if required. */
+ if (h.priv->need_pusi) {
+ if (dvb_net_ule_ts_pusi(&h))
+ continue;
+ }
+
+ if (h.new_ts) {
+ if (dvb_net_ule_new_ts(&h))
+ continue;
+ }
+
+ /* Check if new payload needs to be started. */
+ if (h.priv->ule_skb == NULL) {
+ ret = dvb_net_ule_new_payload(&h);
+ if (ret < 0)
+ return;
+ if (ret)
+ continue;
+ }
+
+ /* Copy data into our current skb. */
+ h.how_much = min(h.priv->ule_sndu_remain, (int)h.ts_remain);
+ skb_put_data(h.priv->ule_skb, h.from_where, h.how_much);
+ h.priv->ule_sndu_remain -= h.how_much;
+ h.ts_remain -= h.how_much;
+ h.from_where += h.how_much;
+
+ /* Check for complete payload. */
+ if (h.priv->ule_sndu_remain <= 0) {
+ /* Check CRC32, we've got it in our skb already. */
+ __be16 ulen = htons(h.priv->ule_sndu_len);
+ __be16 utype = htons(h.priv->ule_sndu_type);
+ const u8 *tail;
+ struct kvec iov[3] = {
+ { &ulen, sizeof ulen },
+ { &utype, sizeof utype },
+ { h.priv->ule_skb->data,
+ h.priv->ule_skb->len - 4 }
+ };
+ u32 ule_crc = ~0L, expected_crc;
+ if (h.priv->ule_dbit) {
+ /* Set D-bit for CRC32 verification,
+ * if it was set originally. */
+ ulen |= htons(0x8000);
+ }
+
+ ule_crc = iov_crc32(ule_crc, iov, 3);
+ tail = skb_tail_pointer(h.priv->ule_skb);
+ expected_crc = *(tail - 4) << 24 |
+ *(tail - 3) << 16 |
+ *(tail - 2) << 8 |
+ *(tail - 1);
+
+ dvb_net_ule_check_crc(&h, iov, ule_crc, expected_crc);
+
+ /* Prepare for next SNDU. */
+ reset_ule(h.priv);
+ }
+
+ /* More data in current TS (look at the bytes following the CRC32)? */
+ if (h.ts_remain >= 2 && *((unsigned short *)h.from_where) != 0xFFFF) {
+ /* Next ULE SNDU starts right there. */
+ h.new_ts = 0;
+ h.priv->ule_skb = NULL;
+ h.priv->ule_sndu_type_1 = 0;
+ h.priv->ule_sndu_len = 0;
+ // pr_warn("More data in current TS: [%#x %#x %#x %#x]\n",
+ // *(h.from_where + 0), *(h.from_where + 1),
+ // *(h.from_where + 2), *(h.from_where + 3));
+ // pr_warn("h.ts @ %p, stopped @ %p:\n", h.ts, h.from_where + 0);
+ // hexdump(h.ts, 188);
+ } else {
+ h.new_ts = 1;
+ h.ts += TS_SZ;
+ h.priv->ts_count++;
+ if (h.priv->ule_skb == NULL) {
+ h.priv->need_pusi = 1;
+ h.priv->ule_sndu_type_1 = 0;
+ h.priv->ule_sndu_len = 0;
+ }
+ }
+ } /* for all available TS cells */
+}
+
+static int dvb_net_ts_callback(const u8 *buffer1, size_t buffer1_len,
+ const u8 *buffer2, size_t buffer2_len,
+ struct dmx_ts_feed *feed,
+ u32 *buffer_flags)
+{
+ struct net_device *dev = feed->priv;
+
+ if (buffer2)
+ pr_warn("buffer2 not NULL: %p.\n", buffer2);
+ if (buffer1_len > 32768)
+ pr_warn("length > 32k: %zu.\n", buffer1_len);
+ /* pr_info("TS callback: %u bytes, %u TS cells @ %p.\n",
+ buffer1_len, buffer1_len / TS_SZ, buffer1); */
+ dvb_net_ule(dev, buffer1, buffer1_len);
+ return 0;
+}
+
+
+static void dvb_net_sec(struct net_device *dev,
+ const u8 *pkt, int pkt_len)
+{
+ u8 *eth;
+ struct sk_buff *skb;
+ struct net_device_stats *stats = &dev->stats;
+ int snap = 0;
+
+ /* note: pkt_len includes a 32bit checksum */
+ if (pkt_len < 16) {
+ pr_warn("%s: IP/MPE packet length = %d too small.\n",
+ dev->name, pkt_len);
+ stats->rx_errors++;
+ stats->rx_length_errors++;
+ return;
+ }
+/* it seems some ISPs manage to screw up here, so we have to
+ * relax the error checks... */
+#if 0
+ if ((pkt[5] & 0xfd) != 0xc1) {
+ /* drop scrambled or broken packets */
+#else
+ if ((pkt[5] & 0x3c) != 0x00) {
+ /* drop scrambled */
+#endif
+ stats->rx_errors++;
+ stats->rx_crc_errors++;
+ return;
+ }
+ if (pkt[5] & 0x02) {
+ /* handle LLC/SNAP, see rfc-1042 */
+ if (pkt_len < 24 || memcmp(&pkt[12], "\xaa\xaa\x03\0\0\0", 6)) {
+ stats->rx_dropped++;
+ return;
+ }
+ snap = 8;
+ }
+ if (pkt[7]) {
+ /* FIXME: assemble datagram from multiple sections */
+ stats->rx_errors++;
+ stats->rx_frame_errors++;
+ return;
+ }
+
+ /* we have 14 byte ethernet header (ip header follows);
+ * 12 byte MPE header; 4 byte checksum; + 2 byte alignment, 8 byte LLC/SNAP
+ */
+ if (!(skb = dev_alloc_skb(pkt_len - 4 - 12 + 14 + 2 - snap))) {
+ //pr_notice("%s: Memory squeeze, dropping packet.\n", dev->name);
+ stats->rx_dropped++;
+ return;
+ }
+ skb_reserve(skb, 2); /* longword align L3 header */
+ skb->dev = dev;
+
+ /* copy L3 payload */
+ eth = skb_put(skb, pkt_len - 12 - 4 + 14 - snap);
+ memcpy(eth + 14, pkt + 12 + snap, pkt_len - 12 - 4 - snap);
+
+ /* create ethernet header: */
+ eth[0]=pkt[0x0b];
+ eth[1]=pkt[0x0a];
+ eth[2]=pkt[0x09];
+ eth[3]=pkt[0x08];
+ eth[4]=pkt[0x04];
+ eth[5]=pkt[0x03];
+
+ eth[6]=eth[7]=eth[8]=eth[9]=eth[10]=eth[11]=0;
+
+ if (snap) {
+ eth[12] = pkt[18];
+ eth[13] = pkt[19];
+ } else {
+ /* protocol numbers are from rfc-1700 or
+ * http://www.iana.org/assignments/ethernet-numbers
+ */
+ if (pkt[12] >> 4 == 6) { /* version field from IP header */
+ eth[12] = 0x86; /* IPv6 */
+ eth[13] = 0xdd;
+ } else {
+ eth[12] = 0x08; /* IPv4 */
+ eth[13] = 0x00;
+ }
+ }
+
+ skb->protocol = dvb_net_eth_type_trans(skb, dev);
+
+ stats->rx_packets++;
+ stats->rx_bytes+=skb->len;
+ netif_rx(skb);
+}
+
+static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len,
+ const u8 *buffer2, size_t buffer2_len,
+ struct dmx_section_filter *filter, u32 *buffer_flags)
+{
+ struct net_device *dev = filter->priv;
+
+ /*
+ * we rely on the DVB API definition where exactly one complete
+ * section is delivered in buffer1
+ */
+ dvb_net_sec (dev, buffer1, buffer1_len);
+ return 0;
+}
+
+static netdev_tx_t dvb_net_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+static u8 mask_normal[6]={0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+static u8 mask_allmulti[6]={0xff, 0xff, 0xff, 0x00, 0x00, 0x00};
+static u8 mac_allmulti[6]={0x01, 0x00, 0x5e, 0x00, 0x00, 0x00};
+static u8 mask_promisc[6]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+static int dvb_net_filter_sec_set(struct net_device *dev,
+ struct dmx_section_filter **secfilter,
+ const u8 *mac, u8 *mac_mask)
+{
+ struct dvb_net_priv *priv = netdev_priv(dev);
+ int ret;
+
+ *secfilter=NULL;
+ ret = priv->secfeed->allocate_filter(priv->secfeed, secfilter);
+ if (ret<0) {
+ pr_err("%s: could not get filter\n", dev->name);
+ return ret;
+ }
+
+ (*secfilter)->priv=(void *) dev;
+
+ memset((*secfilter)->filter_value, 0x00, DMX_MAX_FILTER_SIZE);
+ memset((*secfilter)->filter_mask, 0x00, DMX_MAX_FILTER_SIZE);
+ memset((*secfilter)->filter_mode, 0xff, DMX_MAX_FILTER_SIZE);
+
+ (*secfilter)->filter_value[0]=0x3e;
+ (*secfilter)->filter_value[3]=mac[5];
+ (*secfilter)->filter_value[4]=mac[4];
+ (*secfilter)->filter_value[8]=mac[3];
+ (*secfilter)->filter_value[9]=mac[2];
+ (*secfilter)->filter_value[10]=mac[1];
+ (*secfilter)->filter_value[11]=mac[0];
+
+ (*secfilter)->filter_mask[0] = 0xff;
+ (*secfilter)->filter_mask[3] = mac_mask[5];
+ (*secfilter)->filter_mask[4] = mac_mask[4];
+ (*secfilter)->filter_mask[8] = mac_mask[3];
+ (*secfilter)->filter_mask[9] = mac_mask[2];
+ (*secfilter)->filter_mask[10] = mac_mask[1];
+ (*secfilter)->filter_mask[11]=mac_mask[0];
+
+ netdev_dbg(dev, "filter mac=%pM mask=%pM\n", mac, mac_mask);
+
+ return 0;
+}
+
+static int dvb_net_feed_start(struct net_device *dev)
+{
+ int ret = 0, i;
+ struct dvb_net_priv *priv = netdev_priv(dev);
+ struct dmx_demux *demux = priv->demux;
+ const unsigned char *mac = (const unsigned char *) dev->dev_addr;
+
+ netdev_dbg(dev, "rx_mode %i\n", priv->rx_mode);
+ mutex_lock(&priv->mutex);
+ if (priv->tsfeed || priv->secfeed || priv->secfilter || priv->multi_secfilter[0])
+ pr_err("%s: BUG %d\n", __func__, __LINE__);
+
+ priv->secfeed=NULL;
+ priv->secfilter=NULL;
+ priv->tsfeed = NULL;
+
+ if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
+ netdev_dbg(dev, "alloc secfeed\n");
+ ret=demux->allocate_section_feed(demux, &priv->secfeed,
+ dvb_net_sec_callback);
+ if (ret<0) {
+ pr_err("%s: could not allocate section feed\n",
+ dev->name);
+ goto error;
+ }
+
+ ret = priv->secfeed->set(priv->secfeed, priv->pid, 1);
+
+ if (ret<0) {
+ pr_err("%s: could not set section feed\n", dev->name);
+ priv->demux->release_section_feed(priv->demux, priv->secfeed);
+ priv->secfeed=NULL;
+ goto error;
+ }
+
+ if (priv->rx_mode != RX_MODE_PROMISC) {
+ netdev_dbg(dev, "set secfilter\n");
+ dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_normal);
+ }
+
+ switch (priv->rx_mode) {
+ case RX_MODE_MULTI:
+ for (i = 0; i < priv->multi_num; i++) {
+ netdev_dbg(dev, "set multi_secfilter[%d]\n", i);
+ dvb_net_filter_sec_set(dev, &priv->multi_secfilter[i],
+ priv->multi_macs[i], mask_normal);
+ }
+ break;
+ case RX_MODE_ALL_MULTI:
+ priv->multi_num=1;
+ netdev_dbg(dev, "set multi_secfilter[0]\n");
+ dvb_net_filter_sec_set(dev, &priv->multi_secfilter[0],
+ mac_allmulti, mask_allmulti);
+ break;
+ case RX_MODE_PROMISC:
+ priv->multi_num=0;
+ netdev_dbg(dev, "set secfilter\n");
+ dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_promisc);
+ break;
+ }
+
+ netdev_dbg(dev, "start filtering\n");
+ priv->secfeed->start_filtering(priv->secfeed);
+ } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
+ ktime_t timeout = ns_to_ktime(10 * NSEC_PER_MSEC);
+
+ /* we have payloads encapsulated in TS */
+ netdev_dbg(dev, "alloc tsfeed\n");
+ ret = demux->allocate_ts_feed(demux, &priv->tsfeed, dvb_net_ts_callback);
+ if (ret < 0) {
+ pr_err("%s: could not allocate ts feed\n", dev->name);
+ goto error;
+ }
+
+ /* Set netdevice pointer for ts decaps callback. */
+ priv->tsfeed->priv = (void *)dev;
+ ret = priv->tsfeed->set(priv->tsfeed,
+ priv->pid, /* pid */
+ TS_PACKET, /* type */
+ DMX_PES_OTHER, /* pes type */
+ timeout /* timeout */
+ );
+
+ if (ret < 0) {
+ pr_err("%s: could not set ts feed\n", dev->name);
+ priv->demux->release_ts_feed(priv->demux, priv->tsfeed);
+ priv->tsfeed = NULL;
+ goto error;
+ }
+
+ netdev_dbg(dev, "start filtering\n");
+ priv->tsfeed->start_filtering(priv->tsfeed);
+ } else
+ ret = -EINVAL;
+
+error:
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+static int dvb_net_feed_stop(struct net_device *dev)
+{
+ struct dvb_net_priv *priv = netdev_priv(dev);
+ int i, ret = 0;
+
+ mutex_lock(&priv->mutex);
+ if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
+ if (priv->secfeed) {
+ if (priv->secfeed->is_filtering) {
+ netdev_dbg(dev, "stop secfeed\n");
+ priv->secfeed->stop_filtering(priv->secfeed);
+ }
+
+ if (priv->secfilter) {
+ netdev_dbg(dev, "release secfilter\n");
+ priv->secfeed->release_filter(priv->secfeed,
+ priv->secfilter);
+ priv->secfilter=NULL;
+ }
+
+ for (i=0; i<priv->multi_num; i++) {
+ if (priv->multi_secfilter[i]) {
+ netdev_dbg(dev, "release multi_filter[%d]\n",
+ i);
+ priv->secfeed->release_filter(priv->secfeed,
+ priv->multi_secfilter[i]);
+ priv->multi_secfilter[i] = NULL;
+ }
+ }
+
+ priv->demux->release_section_feed(priv->demux, priv->secfeed);
+ priv->secfeed = NULL;
+ } else
+ pr_err("%s: no feed to stop\n", dev->name);
+ } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
+ if (priv->tsfeed) {
+ if (priv->tsfeed->is_filtering) {
+ netdev_dbg(dev, "stop tsfeed\n");
+ priv->tsfeed->stop_filtering(priv->tsfeed);
+ }
+ priv->demux->release_ts_feed(priv->demux, priv->tsfeed);
+ priv->tsfeed = NULL;
+ }
+ else
+ pr_err("%s: no ts feed to stop\n", dev->name);
+ } else
+ ret = -EINVAL;
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+
+static int dvb_set_mc_filter(struct net_device *dev, unsigned char *addr)
+{
+ struct dvb_net_priv *priv = netdev_priv(dev);
+
+ if (priv->multi_num == DVB_NET_MULTICAST_MAX)
+ return -ENOMEM;
+
+ memcpy(priv->multi_macs[priv->multi_num], addr, ETH_ALEN);
+
+ priv->multi_num++;
+ return 0;
+}
+
+
+static void wq_set_multicast_list (struct work_struct *work)
+{
+ struct dvb_net_priv *priv =
+ container_of(work, struct dvb_net_priv, set_multicast_list_wq);
+ struct net_device *dev = priv->net;
+
+ dvb_net_feed_stop(dev);
+ priv->rx_mode = RX_MODE_UNI;
+ netif_addr_lock_bh(dev);
+
+ if (dev->flags & IFF_PROMISC) {
+ netdev_dbg(dev, "promiscuous mode\n");
+ priv->rx_mode = RX_MODE_PROMISC;
+ } else if ((dev->flags & IFF_ALLMULTI)) {
+ netdev_dbg(dev, "allmulti mode\n");
+ priv->rx_mode = RX_MODE_ALL_MULTI;
+ } else if (!netdev_mc_empty(dev)) {
+ struct netdev_hw_addr *ha;
+
+ netdev_dbg(dev, "set_mc_list, %d entries\n",
+ netdev_mc_count(dev));
+
+ priv->rx_mode = RX_MODE_MULTI;
+ priv->multi_num = 0;
+
+ netdev_for_each_mc_addr(ha, dev)
+ dvb_set_mc_filter(dev, ha->addr);
+ }
+
+ netif_addr_unlock_bh(dev);
+ dvb_net_feed_start(dev);
+}
+
+
+static void dvb_net_set_multicast_list (struct net_device *dev)
+{
+ struct dvb_net_priv *priv = netdev_priv(dev);
+ schedule_work(&priv->set_multicast_list_wq);
+}
+
+
+static void wq_restart_net_feed (struct work_struct *work)
+{
+ struct dvb_net_priv *priv =
+ container_of(work, struct dvb_net_priv, restart_net_feed_wq);
+ struct net_device *dev = priv->net;
+
+ if (netif_running(dev)) {
+ dvb_net_feed_stop(dev);
+ dvb_net_feed_start(dev);
+ }
+}
+
+
+static int dvb_net_set_mac (struct net_device *dev, void *p)
+{
+ struct dvb_net_priv *priv = netdev_priv(dev);
+ struct sockaddr *addr=p;
+
+ eth_hw_addr_set(dev, addr->sa_data);
+
+ if (netif_running(dev))
+ schedule_work(&priv->restart_net_feed_wq);
+
+ return 0;
+}
+
+
+static int dvb_net_open(struct net_device *dev)
+{
+ struct dvb_net_priv *priv = netdev_priv(dev);
+
+ priv->in_use++;
+ dvb_net_feed_start(dev);
+ return 0;
+}
+
+
+static int dvb_net_stop(struct net_device *dev)
+{
+ struct dvb_net_priv *priv = netdev_priv(dev);
+
+ priv->in_use--;
+ return dvb_net_feed_stop(dev);
+}
+
+static const struct header_ops dvb_header_ops = {
+ .create = eth_header,
+ .parse = eth_header_parse,
+};
+
+
+static const struct net_device_ops dvb_netdev_ops = {
+ .ndo_open = dvb_net_open,
+ .ndo_stop = dvb_net_stop,
+ .ndo_start_xmit = dvb_net_tx,
+ .ndo_set_rx_mode = dvb_net_set_multicast_list,
+ .ndo_set_mac_address = dvb_net_set_mac,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static void dvb_net_setup(struct net_device *dev)
+{
+ ether_setup(dev);
+
+ dev->header_ops = &dvb_header_ops;
+ dev->netdev_ops = &dvb_netdev_ops;
+ dev->mtu = 4096;
+ dev->max_mtu = 4096;
+
+ dev->flags |= IFF_NOARP;
+}
+
+static int get_if(struct dvb_net *dvbnet)
+{
+ int i;
+
+ for (i=0; i<DVB_NET_DEVICES_MAX; i++)
+ if (!dvbnet->state[i])
+ break;
+
+ if (i == DVB_NET_DEVICES_MAX)
+ return -1;
+
+ dvbnet->state[i]=1;
+ return i;
+}
+
+static int dvb_net_add_if(struct dvb_net *dvbnet, u16 pid, u8 feedtype)
+{
+ struct net_device *net;
+ struct dvb_net_priv *priv;
+ int result;
+ int if_num;
+
+ if (feedtype != DVB_NET_FEEDTYPE_MPE && feedtype != DVB_NET_FEEDTYPE_ULE)
+ return -EINVAL;
+ if ((if_num = get_if(dvbnet)) < 0)
+ return -EINVAL;
+
+ net = alloc_netdev(sizeof(struct dvb_net_priv), "dvb",
+ NET_NAME_UNKNOWN, dvb_net_setup);
+ if (!net)
+ return -ENOMEM;
+
+ if (dvbnet->dvbdev->id)
+ snprintf(net->name, IFNAMSIZ, "dvb%d%u%d",
+ dvbnet->dvbdev->adapter->num, dvbnet->dvbdev->id, if_num);
+ else
+ /* compatibility fix to keep dvb0_0 format */
+ snprintf(net->name, IFNAMSIZ, "dvb%d_%d",
+ dvbnet->dvbdev->adapter->num, if_num);
+
+ net->addr_len = 6;
+ eth_hw_addr_set(net, dvbnet->dvbdev->adapter->proposed_mac);
+
+ dvbnet->device[if_num] = net;
+
+ priv = netdev_priv(net);
+ priv->net = net;
+ priv->demux = dvbnet->demux;
+ priv->pid = pid;
+ priv->rx_mode = RX_MODE_UNI;
+ priv->need_pusi = 1;
+ priv->tscc = 0;
+ priv->feedtype = feedtype;
+ reset_ule(priv);
+
+ INIT_WORK(&priv->set_multicast_list_wq, wq_set_multicast_list);
+ INIT_WORK(&priv->restart_net_feed_wq, wq_restart_net_feed);
+ mutex_init(&priv->mutex);
+
+ net->base_addr = pid;
+
+ if ((result = register_netdev(net)) < 0) {
+ dvbnet->device[if_num] = NULL;
+ free_netdev(net);
+ return result;
+ }
+ pr_info("created network interface %s\n", net->name);
+
+ return if_num;
+}
+
+static int dvb_net_remove_if(struct dvb_net *dvbnet, unsigned long num)
+{
+ struct net_device *net = dvbnet->device[num];
+ struct dvb_net_priv *priv;
+
+ if (!dvbnet->state[num])
+ return -EINVAL;
+ priv = netdev_priv(net);
+ if (priv->in_use)
+ return -EBUSY;
+
+ dvb_net_stop(net);
+ flush_work(&priv->set_multicast_list_wq);
+ flush_work(&priv->restart_net_feed_wq);
+ pr_info("removed network interface %s\n", net->name);
+ unregister_netdev(net);
+ dvbnet->state[num]=0;
+ dvbnet->device[num] = NULL;
+ free_netdev(net);
+
+ return 0;
+}
+
+static int dvb_net_do_ioctl(struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_net *dvbnet = dvbdev->priv;
+ int ret = 0;
+
+ if (((file->f_flags&O_ACCMODE)==O_RDONLY))
+ return -EPERM;
+
+ if (mutex_lock_interruptible(&dvbnet->ioctl_mutex))
+ return -ERESTARTSYS;
+
+ switch (cmd) {
+ case NET_ADD_IF:
+ {
+ struct dvb_net_if *dvbnetif = parg;
+ int result;
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ ret = -EPERM;
+ goto ioctl_error;
+ }
+
+ if (!try_module_get(dvbdev->adapter->module)) {
+ ret = -EPERM;
+ goto ioctl_error;
+ }
+
+ result=dvb_net_add_if(dvbnet, dvbnetif->pid, dvbnetif->feedtype);
+ if (result<0) {
+ module_put(dvbdev->adapter->module);
+ ret = result;
+ goto ioctl_error;
+ }
+ dvbnetif->if_num=result;
+ break;
+ }
+ case NET_GET_IF:
+ {
+ struct net_device *netdev;
+ struct dvb_net_priv *priv_data;
+ struct dvb_net_if *dvbnetif = parg;
+ int if_num = dvbnetif->if_num;
+
+ if (if_num >= DVB_NET_DEVICES_MAX) {
+ ret = -EINVAL;
+ goto ioctl_error;
+ }
+ if_num = array_index_nospec(if_num, DVB_NET_DEVICES_MAX);
+
+ if (!dvbnet->state[if_num]) {
+ ret = -EINVAL;
+ goto ioctl_error;
+ }
+
+ netdev = dvbnet->device[if_num];
+
+ priv_data = netdev_priv(netdev);
+ dvbnetif->pid=priv_data->pid;
+ dvbnetif->feedtype=priv_data->feedtype;
+ break;
+ }
+ case NET_REMOVE_IF:
+ {
+ if (!capable(CAP_SYS_ADMIN)) {
+ ret = -EPERM;
+ goto ioctl_error;
+ }
+ if ((unsigned long) parg >= DVB_NET_DEVICES_MAX) {
+ ret = -EINVAL;
+ goto ioctl_error;
+ }
+ ret = dvb_net_remove_if(dvbnet, (unsigned long) parg);
+ if (!ret)
+ module_put(dvbdev->adapter->module);
+ break;
+ }
+
+ /* binary compatibility cruft */
+ case __NET_ADD_IF_OLD:
+ {
+ struct __dvb_net_if_old *dvbnetif = parg;
+ int result;
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ ret = -EPERM;
+ goto ioctl_error;
+ }
+
+ if (!try_module_get(dvbdev->adapter->module)) {
+ ret = -EPERM;
+ goto ioctl_error;
+ }
+
+ result=dvb_net_add_if(dvbnet, dvbnetif->pid, DVB_NET_FEEDTYPE_MPE);
+ if (result<0) {
+ module_put(dvbdev->adapter->module);
+ ret = result;
+ goto ioctl_error;
+ }
+ dvbnetif->if_num=result;
+ break;
+ }
+ case __NET_GET_IF_OLD:
+ {
+ struct net_device *netdev;
+ struct dvb_net_priv *priv_data;
+ struct __dvb_net_if_old *dvbnetif = parg;
+ int if_num = dvbnetif->if_num;
+
+ if (if_num >= DVB_NET_DEVICES_MAX) {
+ ret = -EINVAL;
+ goto ioctl_error;
+ }
+ if_num = array_index_nospec(if_num, DVB_NET_DEVICES_MAX);
+
+ if (!dvbnet->state[if_num]) {
+ ret = -EINVAL;
+ goto ioctl_error;
+ }
+
+ netdev = dvbnet->device[if_num];
+
+ priv_data = netdev_priv(netdev);
+ dvbnetif->pid=priv_data->pid;
+ break;
+ }
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ioctl_error:
+ mutex_unlock(&dvbnet->ioctl_mutex);
+ return ret;
+}
+
+static long dvb_net_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return dvb_usercopy(file, cmd, arg, dvb_net_do_ioctl);
+}
+
+static int locked_dvb_net_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_net *dvbnet = dvbdev->priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&dvbnet->remove_mutex))
+ return -ERESTARTSYS;
+
+ if (dvbnet->exit) {
+ mutex_unlock(&dvbnet->remove_mutex);
+ return -ENODEV;
+ }
+
+ ret = dvb_generic_open(inode, file);
+
+ mutex_unlock(&dvbnet->remove_mutex);
+
+ return ret;
+}
+
+static int dvb_net_close(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_net *dvbnet = dvbdev->priv;
+
+ mutex_lock(&dvbnet->remove_mutex);
+
+ dvb_generic_release(inode, file);
+
+ if (dvbdev->users == 1 && dvbnet->exit == 1) {
+ mutex_unlock(&dvbnet->remove_mutex);
+ wake_up(&dvbdev->wait_queue);
+ } else {
+ mutex_unlock(&dvbnet->remove_mutex);
+ }
+
+ return 0;
+}
+
+
+static const struct file_operations dvb_net_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = dvb_net_ioctl,
+ .open = locked_dvb_net_open,
+ .release = dvb_net_close,
+ .llseek = noop_llseek,
+};
+
+static const struct dvb_device dvbdev_net = {
+ .priv = NULL,
+ .users = 1,
+ .writers = 1,
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+ .name = "dvb-net",
+#endif
+ .fops = &dvb_net_fops,
+};
+
+void dvb_net_release (struct dvb_net *dvbnet)
+{
+ int i;
+
+ mutex_lock(&dvbnet->remove_mutex);
+ dvbnet->exit = 1;
+ mutex_unlock(&dvbnet->remove_mutex);
+
+ if (dvbnet->dvbdev->users < 1)
+ wait_event(dvbnet->dvbdev->wait_queue,
+ dvbnet->dvbdev->users == 1);
+
+ dvb_unregister_device(dvbnet->dvbdev);
+
+ for (i=0; i<DVB_NET_DEVICES_MAX; i++) {
+ if (!dvbnet->state[i])
+ continue;
+ dvb_net_remove_if(dvbnet, i);
+ }
+}
+EXPORT_SYMBOL(dvb_net_release);
+
+
+int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet,
+ struct dmx_demux *dmx)
+{
+ int i;
+
+ mutex_init(&dvbnet->ioctl_mutex);
+ mutex_init(&dvbnet->remove_mutex);
+ dvbnet->demux = dmx;
+
+ for (i=0; i<DVB_NET_DEVICES_MAX; i++)
+ dvbnet->state[i] = 0;
+
+ return dvb_register_device(adap, &dvbnet->dvbdev, &dvbdev_net,
+ dvbnet, DVB_DEVICE_NET, 0);
+}
+EXPORT_SYMBOL(dvb_net_init);
diff --git a/drivers/media/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb-core/dvb_ringbuffer.c
new file mode 100644
index 000000000000..de6226556826
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_ringbuffer.c
@@ -0,0 +1,377 @@
+/*
+ *
+ * dvb_ringbuffer.c: ring buffer implementation for the dvb driver
+ *
+ * Copyright (C) 2003 Oliver Endriss
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * based on code originally found in av7110.c & dvb_ci.c:
+ * Copyright (C) 1999-2003 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+
+#include <media/dvb_ringbuffer.h>
+
+#define PKT_READY 0
+#define PKT_DISPOSED 1
+
+
+void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
+{
+ rbuf->pread = 0;
+ rbuf->pwrite = 0;
+ rbuf->data = data;
+ rbuf->size = len;
+ rbuf->error = 0;
+
+ init_waitqueue_head(&rbuf->queue);
+
+ spin_lock_init(&(rbuf->lock));
+}
+
+
+
+int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf)
+{
+ /* smp_load_acquire() to load write pointer on reader side
+ * this pairs with smp_store_release() in dvb_ringbuffer_write(),
+ * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset()
+ *
+ * for memory barriers also see Documentation/core-api/circular-buffers.rst
+ */
+ return (rbuf->pread == smp_load_acquire(&rbuf->pwrite));
+}
+
+
+
+ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf)
+{
+ ssize_t free;
+
+ /* READ_ONCE() to load read pointer on writer side
+ * this pairs with smp_store_release() in dvb_ringbuffer_read(),
+ * dvb_ringbuffer_read_user(), dvb_ringbuffer_flush(),
+ * or dvb_ringbuffer_reset()
+ */
+ free = READ_ONCE(rbuf->pread) - rbuf->pwrite;
+ if (free <= 0)
+ free += rbuf->size;
+ return free-1;
+}
+
+
+
+ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf)
+{
+ ssize_t avail;
+
+ /* smp_load_acquire() to load write pointer on reader side
+ * this pairs with smp_store_release() in dvb_ringbuffer_write(),
+ * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset()
+ */
+ avail = smp_load_acquire(&rbuf->pwrite) - rbuf->pread;
+ if (avail < 0)
+ avail += rbuf->size;
+ return avail;
+}
+
+
+
+void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf)
+{
+ /* dvb_ringbuffer_flush() counts as read operation
+ * smp_load_acquire() to load write pointer
+ * smp_store_release() to update read pointer, this ensures that the
+ * correct pointer is visible for subsequent dvb_ringbuffer_free()
+ * calls on other cpu cores
+ */
+ smp_store_release(&rbuf->pread, smp_load_acquire(&rbuf->pwrite));
+ rbuf->error = 0;
+}
+EXPORT_SYMBOL(dvb_ringbuffer_flush);
+
+void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf)
+{
+ /* dvb_ringbuffer_reset() counts as read and write operation
+ * smp_store_release() to update read pointer
+ */
+ smp_store_release(&rbuf->pread, 0);
+ /* smp_store_release() to update write pointer */
+ smp_store_release(&rbuf->pwrite, 0);
+ rbuf->error = 0;
+}
+
+void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&rbuf->lock, flags);
+ dvb_ringbuffer_flush(rbuf);
+ spin_unlock_irqrestore(&rbuf->lock, flags);
+
+ wake_up(&rbuf->queue);
+}
+
+ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len)
+{
+ size_t todo = len;
+ size_t split;
+
+ split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
+ if (split > 0) {
+ if (copy_to_user(buf, rbuf->data+rbuf->pread, split))
+ return -EFAULT;
+ buf += split;
+ todo -= split;
+ /* smp_store_release() for read pointer update to ensure
+ * that buf is not overwritten until read is complete,
+ * this pairs with READ_ONCE() in dvb_ringbuffer_free()
+ */
+ smp_store_release(&rbuf->pread, 0);
+ }
+ if (copy_to_user(buf, rbuf->data+rbuf->pread, todo))
+ return -EFAULT;
+
+ /* smp_store_release() to update read pointer, see above */
+ smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size);
+
+ return len;
+}
+
+void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len)
+{
+ size_t todo = len;
+ size_t split;
+
+ split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
+ if (split > 0) {
+ memcpy(buf, rbuf->data+rbuf->pread, split);
+ buf += split;
+ todo -= split;
+ /* smp_store_release() for read pointer update to ensure
+ * that buf is not overwritten until read is complete,
+ * this pairs with READ_ONCE() in dvb_ringbuffer_free()
+ */
+ smp_store_release(&rbuf->pread, 0);
+ }
+ memcpy(buf, rbuf->data+rbuf->pread, todo);
+
+ /* smp_store_release() to update read pointer, see above */
+ smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size);
+}
+
+
+ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len)
+{
+ size_t todo = len;
+ size_t split;
+
+ split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0;
+
+ if (split > 0) {
+ memcpy(rbuf->data+rbuf->pwrite, buf, split);
+ buf += split;
+ todo -= split;
+ /* smp_store_release() for write pointer update to ensure that
+ * written data is visible on other cpu cores before the pointer
+ * update, this pairs with smp_load_acquire() in
+ * dvb_ringbuffer_empty() or dvb_ringbuffer_avail()
+ */
+ smp_store_release(&rbuf->pwrite, 0);
+ }
+ memcpy(rbuf->data+rbuf->pwrite, buf, todo);
+ /* smp_store_release() for write pointer update, see above */
+ smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size);
+
+ return len;
+}
+
+ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf,
+ const u8 __user *buf, size_t len)
+{
+ int status;
+ size_t todo = len;
+ size_t split;
+
+ split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0;
+
+ if (split > 0) {
+ status = copy_from_user(rbuf->data+rbuf->pwrite, buf, split);
+ if (status)
+ return len - todo;
+ buf += split;
+ todo -= split;
+ /* smp_store_release() for write pointer update to ensure that
+ * written data is visible on other cpu cores before the pointer
+ * update, this pairs with smp_load_acquire() in
+ * dvb_ringbuffer_empty() or dvb_ringbuffer_avail()
+ */
+ smp_store_release(&rbuf->pwrite, 0);
+ }
+ status = copy_from_user(rbuf->data+rbuf->pwrite, buf, todo);
+ if (status)
+ return len - todo;
+ /* smp_store_release() for write pointer update, see above */
+ smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size);
+
+ return len;
+}
+
+ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len)
+{
+ int status;
+ ssize_t oldpwrite = rbuf->pwrite;
+
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8);
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff);
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY);
+ status = dvb_ringbuffer_write(rbuf, buf, len);
+
+ if (status < 0)
+ rbuf->pwrite = oldpwrite;
+ return status;
+}
+
+ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx,
+ int offset, u8 __user *buf, size_t len)
+{
+ size_t todo;
+ size_t split;
+ size_t pktlen;
+
+ pktlen = rbuf->data[idx] << 8;
+ pktlen |= rbuf->data[(idx + 1) % rbuf->size];
+ if (offset > pktlen)
+ return -EINVAL;
+ if ((offset + len) > pktlen)
+ len = pktlen - offset;
+
+ idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
+ todo = len;
+ split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;
+ if (split > 0) {
+ if (copy_to_user(buf, rbuf->data+idx, split))
+ return -EFAULT;
+ buf += split;
+ todo -= split;
+ idx = 0;
+ }
+ if (copy_to_user(buf, rbuf->data+idx, todo))
+ return -EFAULT;
+
+ return len;
+}
+
+ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
+ int offset, u8 *buf, size_t len)
+{
+ size_t todo;
+ size_t split;
+ size_t pktlen;
+
+ pktlen = rbuf->data[idx] << 8;
+ pktlen |= rbuf->data[(idx + 1) % rbuf->size];
+ if (offset > pktlen)
+ return -EINVAL;
+ if ((offset + len) > pktlen)
+ len = pktlen - offset;
+
+ idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
+ todo = len;
+ split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;
+ if (split > 0) {
+ memcpy(buf, rbuf->data+idx, split);
+ buf += split;
+ todo -= split;
+ idx = 0;
+ }
+ memcpy(buf, rbuf->data+idx, todo);
+ return len;
+}
+
+void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
+{
+ size_t pktlen;
+
+ rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED;
+
+ // clean up disposed packets
+ while (dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) {
+ if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) {
+ pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8;
+ pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1);
+ DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE);
+ } else {
+ // first packet is not disposed, so we stop cleaning now
+ break;
+ }
+ }
+}
+
+ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t *pktlen)
+{
+ int consumed;
+ int curpktlen;
+ int curpktstatus;
+
+ if (idx == -1) {
+ idx = rbuf->pread;
+ } else {
+ curpktlen = rbuf->data[idx] << 8;
+ curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
+ idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
+ }
+
+ consumed = (idx - rbuf->pread);
+ if (consumed < 0)
+ consumed += rbuf->size;
+
+ while ((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) {
+
+ curpktlen = rbuf->data[idx] << 8;
+ curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
+ curpktstatus = rbuf->data[(idx + 2) % rbuf->size];
+
+ if (curpktstatus == PKT_READY) {
+ *pktlen = curpktlen;
+ return idx;
+ }
+
+ consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE;
+ idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
+ }
+
+ // no packets available
+ return -1;
+}
+
+
+
+EXPORT_SYMBOL(dvb_ringbuffer_init);
+EXPORT_SYMBOL(dvb_ringbuffer_empty);
+EXPORT_SYMBOL(dvb_ringbuffer_free);
+EXPORT_SYMBOL(dvb_ringbuffer_avail);
+EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup);
+EXPORT_SYMBOL(dvb_ringbuffer_read_user);
+EXPORT_SYMBOL(dvb_ringbuffer_read);
+EXPORT_SYMBOL(dvb_ringbuffer_write);
+EXPORT_SYMBOL(dvb_ringbuffer_write_user);
diff --git a/drivers/media/dvb-core/dvb_vb2.c b/drivers/media/dvb-core/dvb_vb2.c
new file mode 100644
index 000000000000..29edaaff7a5c
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_vb2.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dvb-vb2.c - dvb-vb2
+ *
+ * Copyright (C) 2015 Samsung Electronics
+ *
+ * Author: jh1009.sung@samsung.com
+ */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+
+#include <media/dvbdev.h>
+#include <media/dvb_vb2.h>
+
+#define DVB_V2_MAX_SIZE (4096 * 188)
+
+static int vb2_debug;
+module_param(vb2_debug, int, 0644);
+
+#define dprintk(level, fmt, arg...) \
+ do { \
+ if (vb2_debug >= level) \
+ pr_info("vb2: %s: " fmt, __func__, ## arg); \
+ } while (0)
+
+static int _queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vq);
+
+ ctx->buf_cnt = *nbuffers;
+ *nplanes = 1;
+ sizes[0] = ctx->buf_siz;
+
+ /*
+ * videobuf2-vmalloc allocator is context-less so no need to set
+ * alloc_ctxs array.
+ */
+
+ dprintk(3, "[%s] count=%d, size=%d\n", ctx->name,
+ *nbuffers, sizes[0]);
+
+ return 0;
+}
+
+static int _buffer_prepare(struct vb2_buffer *vb)
+{
+ struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long size = ctx->buf_siz;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dprintk(1, "[%s] data will not fit into plane (%lu < %lu)\n",
+ ctx->name, vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+ dprintk(3, "[%s]\n", ctx->name);
+
+ return 0;
+}
+
+static void _buffer_queue(struct vb2_buffer *vb)
+{
+ struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct dvb_buffer *buf = container_of(vb, struct dvb_buffer, vb);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&ctx->slock, flags);
+ list_add_tail(&buf->list, &ctx->dvb_q);
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ dprintk(3, "[%s]\n", ctx->name);
+}
+
+static int _start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vq);
+
+ dprintk(3, "[%s] count=%d\n", ctx->name, count);
+ return 0;
+}
+
+static void _stop_streaming(struct vb2_queue *vq)
+{
+ struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vq);
+ struct dvb_buffer *buf;
+ unsigned long flags = 0;
+
+ dprintk(3, "[%s]\n", ctx->name);
+
+ spin_lock_irqsave(&ctx->slock, flags);
+ while (!list_empty(&ctx->dvb_q)) {
+ buf = list_entry(ctx->dvb_q.next,
+ struct dvb_buffer, list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ list_del(&buf->list);
+ }
+ spin_unlock_irqrestore(&ctx->slock, flags);
+}
+
+static void _dmxdev_lock(struct vb2_queue *vq)
+{
+ struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vq);
+
+ mutex_lock(&ctx->mutex);
+ dprintk(3, "[%s]\n", ctx->name);
+}
+
+static void _dmxdev_unlock(struct vb2_queue *vq)
+{
+ struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vq);
+
+ if (mutex_is_locked(&ctx->mutex))
+ mutex_unlock(&ctx->mutex);
+ dprintk(3, "[%s]\n", ctx->name);
+}
+
+static const struct vb2_ops dvb_vb2_qops = {
+ .queue_setup = _queue_setup,
+ .buf_prepare = _buffer_prepare,
+ .buf_queue = _buffer_queue,
+ .start_streaming = _start_streaming,
+ .stop_streaming = _stop_streaming,
+ .wait_prepare = _dmxdev_unlock,
+ .wait_finish = _dmxdev_lock,
+};
+
+static void _fill_dmx_buffer(struct vb2_buffer *vb, void *pb)
+{
+ struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct dmx_buffer *b = pb;
+
+ b->index = vb->index;
+ b->length = vb->planes[0].length;
+ b->bytesused = vb->planes[0].bytesused;
+ b->offset = vb->planes[0].m.offset;
+ dprintk(3, "[%s]\n", ctx->name);
+}
+
+static int _fill_vb2_buffer(struct vb2_buffer *vb, struct vb2_plane *planes)
+{
+ struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ planes[0].bytesused = 0;
+ dprintk(3, "[%s]\n", ctx->name);
+
+ return 0;
+}
+
+static const struct vb2_buf_ops dvb_vb2_buf_ops = {
+ .fill_user_buffer = _fill_dmx_buffer,
+ .fill_vb2_buffer = _fill_vb2_buffer,
+};
+
+/*
+ * Videobuf operations
+ */
+int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name, int nonblocking)
+{
+ struct vb2_queue *q = &ctx->vb_q;
+ int ret;
+
+ memset(ctx, 0, sizeof(struct dvb_vb2_ctx));
+ q->type = DVB_BUF_TYPE_CAPTURE;
+ /**only mmap is supported currently*/
+ q->io_modes = VB2_MMAP;
+ q->drv_priv = ctx;
+ q->buf_struct_size = sizeof(struct dvb_buffer);
+ q->min_queued_buffers = 1;
+ q->ops = &dvb_vb2_qops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->buf_ops = &dvb_vb2_buf_ops;
+ ret = vb2_core_queue_init(q);
+ if (ret) {
+ ctx->state = DVB_VB2_STATE_NONE;
+ dprintk(1, "[%s] errno=%d\n", ctx->name, ret);
+ return ret;
+ }
+
+ mutex_init(&ctx->mutex);
+ spin_lock_init(&ctx->slock);
+ INIT_LIST_HEAD(&ctx->dvb_q);
+
+ strscpy(ctx->name, name, DVB_VB2_NAME_MAX);
+ ctx->nonblocking = nonblocking;
+ ctx->state = DVB_VB2_STATE_INIT;
+
+ dprintk(3, "[%s]\n", ctx->name);
+
+ return 0;
+}
+
+int dvb_vb2_release(struct dvb_vb2_ctx *ctx)
+{
+ struct vb2_queue *q = (struct vb2_queue *)&ctx->vb_q;
+
+ if (ctx->state & DVB_VB2_STATE_INIT)
+ vb2_core_queue_release(q);
+
+ ctx->state = DVB_VB2_STATE_NONE;
+ dprintk(3, "[%s]\n", ctx->name);
+
+ return 0;
+}
+
+int dvb_vb2_stream_on(struct dvb_vb2_ctx *ctx)
+{
+ struct vb2_queue *q = &ctx->vb_q;
+ int ret;
+
+ ret = vb2_core_streamon(q, q->type);
+ if (ret) {
+ ctx->state = DVB_VB2_STATE_NONE;
+ dprintk(1, "[%s] errno=%d\n", ctx->name, ret);
+ return ret;
+ }
+ ctx->state |= DVB_VB2_STATE_STREAMON;
+ dprintk(3, "[%s]\n", ctx->name);
+
+ return 0;
+}
+
+int dvb_vb2_stream_off(struct dvb_vb2_ctx *ctx)
+{
+ struct vb2_queue *q = (struct vb2_queue *)&ctx->vb_q;
+ int ret;
+
+ ctx->state &= ~DVB_VB2_STATE_STREAMON;
+ ret = vb2_core_streamoff(q, q->type);
+ if (ret) {
+ ctx->state = DVB_VB2_STATE_NONE;
+ dprintk(1, "[%s] errno=%d\n", ctx->name, ret);
+ return ret;
+ }
+ dprintk(3, "[%s]\n", ctx->name);
+
+ return 0;
+}
+
+int dvb_vb2_is_streaming(struct dvb_vb2_ctx *ctx)
+{
+ return (ctx->state & DVB_VB2_STATE_STREAMON);
+}
+
+int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx,
+ const unsigned char *src, int len,
+ enum dmx_buffer_flags *buffer_flags)
+{
+ unsigned long flags = 0;
+ void *vbuf = NULL;
+ int todo = len;
+ unsigned char *psrc = (unsigned char *)src;
+ int ll = 0;
+
+ /*
+ * normal case: This func is called twice from demux driver
+ * one with valid src pointer, second time with NULL pointer
+ */
+ if (!src || !len)
+ return 0;
+ spin_lock_irqsave(&ctx->slock, flags);
+ if (buffer_flags && *buffer_flags) {
+ ctx->flags |= *buffer_flags;
+ *buffer_flags = 0;
+ }
+ while (todo) {
+ if (!ctx->buf) {
+ if (list_empty(&ctx->dvb_q)) {
+ dprintk(3, "[%s] Buffer overflow!!!\n",
+ ctx->name);
+ break;
+ }
+
+ ctx->buf = list_entry(ctx->dvb_q.next,
+ struct dvb_buffer, list);
+ ctx->remain = vb2_plane_size(&ctx->buf->vb, 0);
+ ctx->offset = 0;
+ }
+
+ if (!dvb_vb2_is_streaming(ctx)) {
+ vb2_buffer_done(&ctx->buf->vb, VB2_BUF_STATE_ERROR);
+ list_del(&ctx->buf->list);
+ ctx->buf = NULL;
+ break;
+ }
+
+ /* Fill buffer */
+ ll = min(todo, ctx->remain);
+ vbuf = vb2_plane_vaddr(&ctx->buf->vb, 0);
+ memcpy(vbuf + ctx->offset, psrc, ll);
+ todo -= ll;
+ psrc += ll;
+
+ ctx->remain -= ll;
+ ctx->offset += ll;
+
+ if (ctx->remain == 0) {
+ vb2_buffer_done(&ctx->buf->vb, VB2_BUF_STATE_DONE);
+ list_del(&ctx->buf->list);
+ ctx->buf = NULL;
+ }
+ }
+
+ if (ctx->nonblocking && ctx->buf) {
+ vb2_set_plane_payload(&ctx->buf->vb, 0, ll);
+ vb2_buffer_done(&ctx->buf->vb, VB2_BUF_STATE_DONE);
+ list_del(&ctx->buf->list);
+ ctx->buf = NULL;
+ }
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ if (todo)
+ dprintk(1, "[%s] %d bytes are dropped.\n", ctx->name, todo);
+ else
+ dprintk(3, "[%s]\n", ctx->name);
+
+ dprintk(3, "[%s] %d bytes are copied\n", ctx->name, len - todo);
+ return (len - todo);
+}
+
+int dvb_vb2_reqbufs(struct dvb_vb2_ctx *ctx, struct dmx_requestbuffers *req)
+{
+ int ret;
+
+ /* Adjust size to a sane value */
+ if (req->size > DVB_V2_MAX_SIZE)
+ req->size = DVB_V2_MAX_SIZE;
+
+ /* FIXME: round req->size to a 188 or 204 multiple */
+
+ ctx->buf_siz = req->size;
+ ctx->buf_cnt = req->count;
+ ret = vb2_core_reqbufs(&ctx->vb_q, VB2_MEMORY_MMAP, 0, &req->count);
+ if (ret) {
+ ctx->state = DVB_VB2_STATE_NONE;
+ dprintk(1, "[%s] count=%d size=%d errno=%d\n", ctx->name,
+ ctx->buf_cnt, ctx->buf_siz, ret);
+ return ret;
+ }
+ ctx->state |= DVB_VB2_STATE_REQBUFS;
+ dprintk(3, "[%s] count=%d size=%d\n", ctx->name,
+ ctx->buf_cnt, ctx->buf_siz);
+
+ return 0;
+}
+
+int dvb_vb2_querybuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b)
+{
+ struct vb2_queue *q = &ctx->vb_q;
+ struct vb2_buffer *vb2 = vb2_get_buffer(q, b->index);
+
+ if (!vb2) {
+ dprintk(1, "[%s] invalid buffer index\n", ctx->name);
+ return -EINVAL;
+ }
+ vb2_core_querybuf(&ctx->vb_q, vb2, b);
+ dprintk(3, "[%s] index=%d\n", ctx->name, b->index);
+ return 0;
+}
+
+int dvb_vb2_expbuf(struct dvb_vb2_ctx *ctx, struct dmx_exportbuffer *exp)
+{
+ struct vb2_queue *q = &ctx->vb_q;
+ struct vb2_buffer *vb2 = vb2_get_buffer(q, exp->index);
+ int ret;
+
+ if (!vb2) {
+ dprintk(1, "[%s] invalid buffer index\n", ctx->name);
+ return -EINVAL;
+ }
+
+ ret = vb2_core_expbuf(&ctx->vb_q, &exp->fd, q->type, vb2,
+ 0, exp->flags);
+ if (ret) {
+ dprintk(1, "[%s] index=%d errno=%d\n", ctx->name,
+ exp->index, ret);
+ return ret;
+ }
+ dprintk(3, "[%s] index=%d fd=%d\n", ctx->name, exp->index, exp->fd);
+
+ return 0;
+}
+
+int dvb_vb2_qbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b)
+{
+ struct vb2_queue *q = &ctx->vb_q;
+ struct vb2_buffer *vb2 = vb2_get_buffer(q, b->index);
+ int ret;
+
+ if (!vb2) {
+ dprintk(1, "[%s] invalid buffer index\n", ctx->name);
+ return -EINVAL;
+ }
+ ret = vb2_core_qbuf(&ctx->vb_q, vb2, b, NULL);
+ if (ret) {
+ dprintk(1, "[%s] index=%d errno=%d\n", ctx->name,
+ b->index, ret);
+ return ret;
+ }
+ dprintk(5, "[%s] index=%d\n", ctx->name, b->index);
+
+ return 0;
+}
+
+int dvb_vb2_dqbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b)
+{
+ unsigned long flags;
+ int ret;
+
+ ret = vb2_core_dqbuf(&ctx->vb_q, &b->index, b, ctx->nonblocking);
+ if (ret) {
+ dprintk(1, "[%s] errno=%d\n", ctx->name, ret);
+ return ret;
+ }
+
+ spin_lock_irqsave(&ctx->slock, flags);
+ b->count = ctx->count++;
+ b->flags = ctx->flags;
+ ctx->flags = 0;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ dprintk(5, "[%s] index=%d, count=%d, flags=%d\n",
+ ctx->name, b->index, ctx->count, b->flags);
+
+
+ return 0;
+}
+
+int dvb_vb2_mmap(struct dvb_vb2_ctx *ctx, struct vm_area_struct *vma)
+{
+ int ret;
+
+ ret = vb2_mmap(&ctx->vb_q, vma);
+ if (ret) {
+ dprintk(1, "[%s] errno=%d\n", ctx->name, ret);
+ return ret;
+ }
+ dprintk(3, "[%s] ret=%d\n", ctx->name, ret);
+
+ return 0;
+}
+
+__poll_t dvb_vb2_poll(struct dvb_vb2_ctx *ctx, struct file *file,
+ poll_table *wait)
+{
+ dprintk(3, "[%s]\n", ctx->name);
+ return vb2_core_poll(&ctx->vb_q, file, wait);
+}
+
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
new file mode 100644
index 000000000000..8b980d371a45
--- /dev/null
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -0,0 +1,1140 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * dvbdev.c
+ *
+ * Copyright (C) 2000 Ralph Metzler <ralph@convergence.de>
+ * & Marcus Metzler <marcus@convergence.de>
+ * for convergence integrated media GmbH
+ */
+
+#define pr_fmt(fmt) "dvbdev: " fmt
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/mutex.h>
+#include <media/dvbdev.h>
+
+/* Due to enum tuner_pad_index */
+#include <media/tuner.h>
+
+static DEFINE_MUTEX(dvbdev_mutex);
+static LIST_HEAD(dvbdevfops_list);
+static int dvbdev_debug;
+
+module_param(dvbdev_debug, int, 0644);
+MODULE_PARM_DESC(dvbdev_debug, "Turn on/off device debugging (default:off).");
+
+#define dprintk(fmt, arg...) do { \
+ if (dvbdev_debug) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
+
+static LIST_HEAD(dvb_adapter_list);
+static DEFINE_MUTEX(dvbdev_register_lock);
+
+static const char * const dnames[] = {
+ [DVB_DEVICE_VIDEO] = "video",
+ [DVB_DEVICE_AUDIO] = "audio",
+ [DVB_DEVICE_SEC] = "sec",
+ [DVB_DEVICE_FRONTEND] = "frontend",
+ [DVB_DEVICE_DEMUX] = "demux",
+ [DVB_DEVICE_DVR] = "dvr",
+ [DVB_DEVICE_CA] = "ca",
+ [DVB_DEVICE_NET] = "net",
+ [DVB_DEVICE_OSD] = "osd"
+};
+
+#ifdef CONFIG_DVB_DYNAMIC_MINORS
+#define MAX_DVB_MINORS 256
+#define DVB_MAX_IDS MAX_DVB_MINORS
+#else
+#define DVB_MAX_IDS 4
+
+static const u8 minor_type[] = {
+ [DVB_DEVICE_VIDEO] = 0,
+ [DVB_DEVICE_AUDIO] = 1,
+ [DVB_DEVICE_SEC] = 2,
+ [DVB_DEVICE_FRONTEND] = 3,
+ [DVB_DEVICE_DEMUX] = 4,
+ [DVB_DEVICE_DVR] = 5,
+ [DVB_DEVICE_CA] = 6,
+ [DVB_DEVICE_NET] = 7,
+ [DVB_DEVICE_OSD] = 8,
+};
+
+#define nums2minor(num, type, id) \
+ (((num) << 6) | ((id) << 4) | minor_type[type])
+
+#define MAX_DVB_MINORS (DVB_MAX_ADAPTERS * 64)
+#endif
+
+static struct class *dvb_class;
+
+static struct dvb_device *dvb_minors[MAX_DVB_MINORS];
+static DECLARE_RWSEM(minor_rwsem);
+
+static int dvb_device_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev;
+ unsigned int minor = iminor(inode);
+
+ if (minor >= MAX_DVB_MINORS)
+ return -ENODEV;
+
+ mutex_lock(&dvbdev_mutex);
+ down_read(&minor_rwsem);
+
+ dvbdev = dvb_minors[minor];
+
+ if (dvbdev && dvbdev->fops) {
+ int err = 0;
+ const struct file_operations *new_fops;
+
+ new_fops = fops_get(dvbdev->fops);
+ if (!new_fops)
+ goto fail;
+ file->private_data = dvb_device_get(dvbdev);
+ replace_fops(file, new_fops);
+ if (file->f_op->open)
+ err = file->f_op->open(inode, file);
+ up_read(&minor_rwsem);
+ mutex_unlock(&dvbdev_mutex);
+ if (err)
+ dvb_device_put(dvbdev);
+ return err;
+ }
+fail:
+ up_read(&minor_rwsem);
+ mutex_unlock(&dvbdev_mutex);
+ return -ENODEV;
+}
+
+static const struct file_operations dvb_device_fops = {
+ .owner = THIS_MODULE,
+ .open = dvb_device_open,
+ .llseek = noop_llseek,
+};
+
+static struct cdev dvb_device_cdev;
+
+int dvb_generic_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+
+ if (!dvbdev)
+ return -ENODEV;
+
+ if (!dvbdev->users)
+ return -EBUSY;
+
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+ if (!dvbdev->readers)
+ return -EBUSY;
+ dvbdev->readers--;
+ } else {
+ if (!dvbdev->writers)
+ return -EBUSY;
+ dvbdev->writers--;
+ }
+
+ dvbdev->users--;
+ return 0;
+}
+EXPORT_SYMBOL(dvb_generic_open);
+
+int dvb_generic_release(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+
+ if (!dvbdev)
+ return -ENODEV;
+
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+ dvbdev->readers++;
+ else
+ dvbdev->writers++;
+
+ dvbdev->users++;
+
+ dvb_device_put(dvbdev);
+
+ return 0;
+}
+EXPORT_SYMBOL(dvb_generic_release);
+
+long dvb_generic_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+
+ if (!dvbdev)
+ return -ENODEV;
+
+ if (!dvbdev->kernel_ioctl)
+ return -EINVAL;
+
+ return dvb_usercopy(file, cmd, arg, dvbdev->kernel_ioctl);
+}
+EXPORT_SYMBOL(dvb_generic_ioctl);
+
+static int dvbdev_get_free_id(struct dvb_adapter *adap, int type)
+{
+ u32 id = 0;
+
+ while (id < DVB_MAX_IDS) {
+ struct dvb_device *dev;
+
+ list_for_each_entry(dev, &adap->device_list, list_head)
+ if (dev->type == type && dev->id == id)
+ goto skip;
+ return id;
+skip:
+ id++;
+ }
+ return -ENFILE;
+}
+
+static void dvb_media_device_free(struct dvb_device *dvbdev)
+{
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+ if (dvbdev->entity) {
+ media_device_unregister_entity(dvbdev->entity);
+ kfree(dvbdev->entity);
+ kfree(dvbdev->pads);
+ dvbdev->entity = NULL;
+ dvbdev->pads = NULL;
+ }
+
+ if (dvbdev->tsout_entity) {
+ int i;
+
+ for (i = 0; i < dvbdev->tsout_num_entities; i++) {
+ media_device_unregister_entity(&dvbdev->tsout_entity[i]);
+ kfree(dvbdev->tsout_entity[i].name);
+ }
+ kfree(dvbdev->tsout_entity);
+ kfree(dvbdev->tsout_pads);
+ dvbdev->tsout_entity = NULL;
+ dvbdev->tsout_pads = NULL;
+
+ dvbdev->tsout_num_entities = 0;
+ }
+
+ if (dvbdev->intf_devnode) {
+ media_devnode_remove(dvbdev->intf_devnode);
+ dvbdev->intf_devnode = NULL;
+ }
+
+ if (dvbdev->adapter->conn) {
+ media_device_unregister_entity(dvbdev->adapter->conn);
+ kfree(dvbdev->adapter->conn);
+ dvbdev->adapter->conn = NULL;
+ kfree(dvbdev->adapter->conn_pads);
+ dvbdev->adapter->conn_pads = NULL;
+ }
+#endif
+}
+
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+static int dvb_create_tsout_entity(struct dvb_device *dvbdev,
+ const char *name, int npads)
+{
+ int i;
+
+ dvbdev->tsout_pads = kcalloc(npads, sizeof(*dvbdev->tsout_pads),
+ GFP_KERNEL);
+ if (!dvbdev->tsout_pads)
+ return -ENOMEM;
+
+ dvbdev->tsout_entity = kcalloc(npads, sizeof(*dvbdev->tsout_entity),
+ GFP_KERNEL);
+ if (!dvbdev->tsout_entity)
+ return -ENOMEM;
+
+ dvbdev->tsout_num_entities = npads;
+
+ for (i = 0; i < npads; i++) {
+ struct media_pad *pads = &dvbdev->tsout_pads[i];
+ struct media_entity *entity = &dvbdev->tsout_entity[i];
+ int ret;
+
+ entity->name = kasprintf(GFP_KERNEL, "%s #%d", name, i);
+ if (!entity->name)
+ return -ENOMEM;
+
+ entity->function = MEDIA_ENT_F_IO_DTV;
+ pads->flags = MEDIA_PAD_FL_SINK;
+
+ ret = media_entity_pads_init(entity, 1, pads);
+ if (ret < 0)
+ return ret;
+
+ ret = media_device_register_entity(dvbdev->adapter->mdev,
+ entity);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+#define DEMUX_TSOUT "demux-tsout"
+#define DVR_TSOUT "dvr-tsout"
+
+static int dvb_create_media_entity(struct dvb_device *dvbdev,
+ int type, int demux_sink_pads)
+{
+ int i, ret, npads;
+
+ switch (type) {
+ case DVB_DEVICE_FRONTEND:
+ npads = 2;
+ break;
+ case DVB_DEVICE_DVR:
+ ret = dvb_create_tsout_entity(dvbdev, DVR_TSOUT,
+ demux_sink_pads);
+ return ret;
+ case DVB_DEVICE_DEMUX:
+ npads = 1 + demux_sink_pads;
+ ret = dvb_create_tsout_entity(dvbdev, DEMUX_TSOUT,
+ demux_sink_pads);
+ if (ret < 0)
+ return ret;
+ break;
+ case DVB_DEVICE_CA:
+ npads = 2;
+ break;
+ case DVB_DEVICE_NET:
+ /*
+ * We should be creating entities for the MPE/ULE
+ * decapsulation hardware (or software implementation).
+ *
+ * However, the number of for the MPE/ULE decaps may not be
+ * fixed. As we don't have yet dynamic support for PADs at
+ * the Media Controller, let's not create the decap
+ * entities yet.
+ */
+ return 0;
+ default:
+ return 0;
+ }
+
+ dvbdev->entity = kzalloc(sizeof(*dvbdev->entity), GFP_KERNEL);
+ if (!dvbdev->entity)
+ return -ENOMEM;
+
+ dvbdev->entity->name = dvbdev->name;
+
+ if (npads) {
+ dvbdev->pads = kcalloc(npads, sizeof(*dvbdev->pads),
+ GFP_KERNEL);
+ if (!dvbdev->pads) {
+ kfree(dvbdev->entity);
+ dvbdev->entity = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ switch (type) {
+ case DVB_DEVICE_FRONTEND:
+ dvbdev->entity->function = MEDIA_ENT_F_DTV_DEMOD;
+ dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
+ dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE;
+ break;
+ case DVB_DEVICE_DEMUX:
+ dvbdev->entity->function = MEDIA_ENT_F_TS_DEMUX;
+ dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
+ for (i = 1; i < npads; i++)
+ dvbdev->pads[i].flags = MEDIA_PAD_FL_SOURCE;
+ break;
+ case DVB_DEVICE_CA:
+ dvbdev->entity->function = MEDIA_ENT_F_DTV_CA;
+ dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
+ dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE;
+ break;
+ default:
+ /* Should never happen, as the first switch prevents it */
+ kfree(dvbdev->entity);
+ kfree(dvbdev->pads);
+ dvbdev->entity = NULL;
+ dvbdev->pads = NULL;
+ return 0;
+ }
+
+ if (npads) {
+ ret = media_entity_pads_init(dvbdev->entity, npads, dvbdev->pads);
+ if (ret)
+ return ret;
+ }
+ ret = media_device_register_entity(dvbdev->adapter->mdev,
+ dvbdev->entity);
+ if (ret)
+ return ret;
+
+ pr_info("%s: media entity '%s' registered.\n",
+ __func__, dvbdev->entity->name);
+
+ return 0;
+}
+#endif
+
+static int dvb_register_media_device(struct dvb_device *dvbdev,
+ int type, int minor,
+ unsigned int demux_sink_pads)
+{
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+ struct media_link *link;
+ u32 intf_type;
+ int ret;
+
+ if (!dvbdev->adapter->mdev)
+ return 0;
+
+ ret = dvb_create_media_entity(dvbdev, type, demux_sink_pads);
+ if (ret)
+ return ret;
+
+ switch (type) {
+ case DVB_DEVICE_FRONTEND:
+ intf_type = MEDIA_INTF_T_DVB_FE;
+ break;
+ case DVB_DEVICE_DEMUX:
+ intf_type = MEDIA_INTF_T_DVB_DEMUX;
+ break;
+ case DVB_DEVICE_DVR:
+ intf_type = MEDIA_INTF_T_DVB_DVR;
+ break;
+ case DVB_DEVICE_CA:
+ intf_type = MEDIA_INTF_T_DVB_CA;
+ break;
+ case DVB_DEVICE_NET:
+ intf_type = MEDIA_INTF_T_DVB_NET;
+ break;
+ default:
+ return 0;
+ }
+
+ dvbdev->intf_devnode = media_devnode_create(dvbdev->adapter->mdev,
+ intf_type, 0,
+ DVB_MAJOR, minor);
+
+ if (!dvbdev->intf_devnode)
+ return -ENOMEM;
+
+ /*
+ * Create the "obvious" link, e. g. the ones that represent
+ * a direct association between an interface and an entity.
+ * Other links should be created elsewhere, like:
+ * DVB FE intf -> tuner
+ * DVB demux intf -> dvr
+ */
+
+ if (!dvbdev->entity)
+ return 0;
+
+ link = media_create_intf_link(dvbdev->entity,
+ &dvbdev->intf_devnode->intf,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (!link)
+ return -ENOMEM;
+#endif
+ return 0;
+}
+
+int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
+ const struct dvb_device *template, void *priv,
+ enum dvb_device_type type, int demux_sink_pads)
+{
+ struct dvb_device *dvbdev;
+ struct file_operations *dvbdevfops = NULL;
+ struct dvbdevfops_node *node = NULL, *new_node = NULL;
+ struct device *clsdev;
+ int minor;
+ int id, ret;
+
+ mutex_lock(&dvbdev_register_lock);
+
+ id = dvbdev_get_free_id(adap, type);
+ if (id < 0) {
+ mutex_unlock(&dvbdev_register_lock);
+ *pdvbdev = NULL;
+ pr_err("%s: couldn't find free device id\n", __func__);
+ return -ENFILE;
+ }
+
+ *pdvbdev = dvbdev = kzalloc(sizeof(*dvbdev), GFP_KERNEL);
+ if (!dvbdev) {
+ mutex_unlock(&dvbdev_register_lock);
+ return -ENOMEM;
+ }
+
+ /*
+ * When a device of the same type is probe()d more than once,
+ * the first allocated fops are used. This prevents memory leaks
+ * that can occur when the same device is probe()d repeatedly.
+ */
+ list_for_each_entry(node, &dvbdevfops_list, list_head) {
+ if (node->fops->owner == adap->module &&
+ node->type == type && node->template == template) {
+ dvbdevfops = node->fops;
+ break;
+ }
+ }
+
+ if (!dvbdevfops) {
+ dvbdevfops = kmemdup(template->fops, sizeof(*dvbdevfops), GFP_KERNEL);
+ if (!dvbdevfops) {
+ kfree(dvbdev);
+ *pdvbdev = NULL;
+ mutex_unlock(&dvbdev_register_lock);
+ return -ENOMEM;
+ }
+
+ new_node = kzalloc(sizeof(*new_node), GFP_KERNEL);
+ if (!new_node) {
+ kfree(dvbdevfops);
+ kfree(dvbdev);
+ *pdvbdev = NULL;
+ mutex_unlock(&dvbdev_register_lock);
+ return -ENOMEM;
+ }
+
+ new_node->fops = dvbdevfops;
+ new_node->type = type;
+ new_node->template = template;
+ list_add_tail(&new_node->list_head, &dvbdevfops_list);
+ }
+
+ memcpy(dvbdev, template, sizeof(struct dvb_device));
+ kref_init(&dvbdev->ref);
+ dvbdev->type = type;
+ dvbdev->id = id;
+ dvbdev->adapter = adap;
+ dvbdev->priv = priv;
+ dvbdev->fops = dvbdevfops;
+ init_waitqueue_head(&dvbdev->wait_queue);
+ dvbdevfops->owner = adap->module;
+ list_add_tail(&dvbdev->list_head, &adap->device_list);
+ down_write(&minor_rwsem);
+#ifdef CONFIG_DVB_DYNAMIC_MINORS
+ for (minor = 0; minor < MAX_DVB_MINORS; minor++)
+ if (!dvb_minors[minor])
+ break;
+#else
+ minor = nums2minor(adap->num, type, id);
+#endif
+ if (minor >= MAX_DVB_MINORS) {
+ if (new_node) {
+ list_del(&new_node->list_head);
+ kfree(dvbdevfops);
+ kfree(new_node);
+ }
+ list_del(&dvbdev->list_head);
+ kfree(dvbdev);
+ *pdvbdev = NULL;
+ up_write(&minor_rwsem);
+ mutex_unlock(&dvbdev_register_lock);
+ return -EINVAL;
+ }
+
+ dvbdev->minor = minor;
+ dvb_minors[minor] = dvb_device_get(dvbdev);
+ up_write(&minor_rwsem);
+ ret = dvb_register_media_device(dvbdev, type, minor, demux_sink_pads);
+ if (ret) {
+ pr_err("%s: dvb_register_media_device failed to create the mediagraph\n",
+ __func__);
+ if (new_node) {
+ list_del(&new_node->list_head);
+ kfree(dvbdevfops);
+ kfree(new_node);
+ }
+ dvb_media_device_free(dvbdev);
+ list_del(&dvbdev->list_head);
+ kfree(dvbdev);
+ *pdvbdev = NULL;
+ mutex_unlock(&dvbdev_register_lock);
+ return ret;
+ }
+
+ clsdev = device_create(dvb_class, adap->device,
+ MKDEV(DVB_MAJOR, minor),
+ dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id);
+ if (IS_ERR(clsdev)) {
+ pr_err("%s: failed to create device dvb%d.%s%d (%pe)\n",
+ __func__, adap->num, dnames[type], id, clsdev);
+ if (new_node) {
+ list_del(&new_node->list_head);
+ kfree(dvbdevfops);
+ kfree(new_node);
+ }
+ dvb_media_device_free(dvbdev);
+ list_del(&dvbdev->list_head);
+ kfree(dvbdev);
+ *pdvbdev = NULL;
+ mutex_unlock(&dvbdev_register_lock);
+ return PTR_ERR(clsdev);
+ }
+
+ dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
+ adap->num, dnames[type], id, minor, minor);
+
+ mutex_unlock(&dvbdev_register_lock);
+ return 0;
+}
+EXPORT_SYMBOL(dvb_register_device);
+
+void dvb_remove_device(struct dvb_device *dvbdev)
+{
+ if (!dvbdev)
+ return;
+
+ down_write(&minor_rwsem);
+ dvb_minors[dvbdev->minor] = NULL;
+ dvb_device_put(dvbdev);
+ up_write(&minor_rwsem);
+
+ dvb_media_device_free(dvbdev);
+
+ device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));
+
+ list_del(&dvbdev->list_head);
+}
+EXPORT_SYMBOL(dvb_remove_device);
+
+static void dvb_free_device(struct kref *ref)
+{
+ struct dvb_device *dvbdev = container_of(ref, struct dvb_device, ref);
+
+ kfree(dvbdev);
+}
+
+struct dvb_device *dvb_device_get(struct dvb_device *dvbdev)
+{
+ kref_get(&dvbdev->ref);
+ return dvbdev;
+}
+EXPORT_SYMBOL(dvb_device_get);
+
+void dvb_device_put(struct dvb_device *dvbdev)
+{
+ if (dvbdev)
+ kref_put(&dvbdev->ref, dvb_free_device);
+}
+
+void dvb_unregister_device(struct dvb_device *dvbdev)
+{
+ dvb_remove_device(dvbdev);
+ dvb_device_put(dvbdev);
+}
+EXPORT_SYMBOL(dvb_unregister_device);
+
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+
+static int dvb_create_io_intf_links(struct dvb_adapter *adap,
+ struct media_interface *intf,
+ char *name)
+{
+ struct media_device *mdev = adap->mdev;
+ struct media_entity *entity;
+ struct media_link *link;
+
+ media_device_for_each_entity(entity, mdev) {
+ if (entity->function == MEDIA_ENT_F_IO_DTV) {
+ if (strncmp(entity->name, name, strlen(name)))
+ continue;
+ link = media_create_intf_link(entity, intf,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (!link)
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+int dvb_create_media_graph(struct dvb_adapter *adap,
+ bool create_rf_connector)
+{
+ struct media_device *mdev = adap->mdev;
+ struct media_entity *entity, *tuner = NULL, *demod = NULL, *conn;
+ struct media_entity *demux = NULL, *ca = NULL;
+ struct media_link *link;
+ struct media_interface *intf;
+ unsigned int demux_pad = 0;
+ unsigned int dvr_pad = 0;
+ unsigned int ntuner = 0, ndemod = 0;
+ int ret, pad_source, pad_sink;
+ static const char *connector_name = "Television";
+
+ if (!mdev)
+ return 0;
+
+ media_device_for_each_entity(entity, mdev) {
+ switch (entity->function) {
+ case MEDIA_ENT_F_TUNER:
+ tuner = entity;
+ ntuner++;
+ break;
+ case MEDIA_ENT_F_DTV_DEMOD:
+ demod = entity;
+ ndemod++;
+ break;
+ case MEDIA_ENT_F_TS_DEMUX:
+ demux = entity;
+ break;
+ case MEDIA_ENT_F_DTV_CA:
+ ca = entity;
+ break;
+ }
+ }
+
+ /*
+ * Prepare to signalize to media_create_pad_links() that multiple
+ * entities of the same type exists and a 1:n or n:1 links need to be
+ * created.
+ * NOTE: if both tuner and demod have multiple instances, it is up
+ * to the caller driver to create such links.
+ */
+ if (ntuner > 1)
+ tuner = NULL;
+ if (ndemod > 1)
+ demod = NULL;
+
+ if (create_rf_connector) {
+ conn = kzalloc(sizeof(*conn), GFP_KERNEL);
+ if (!conn)
+ return -ENOMEM;
+ adap->conn = conn;
+
+ adap->conn_pads = kzalloc(sizeof(*adap->conn_pads), GFP_KERNEL);
+ if (!adap->conn_pads)
+ return -ENOMEM;
+
+ conn->flags = MEDIA_ENT_FL_CONNECTOR;
+ conn->function = MEDIA_ENT_F_CONN_RF;
+ conn->name = connector_name;
+ adap->conn_pads->flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(conn, 1, adap->conn_pads);
+ if (ret)
+ return ret;
+
+ ret = media_device_register_entity(mdev, conn);
+ if (ret)
+ return ret;
+
+ if (!ntuner) {
+ ret = media_create_pad_links(mdev,
+ MEDIA_ENT_F_CONN_RF,
+ conn, 0,
+ MEDIA_ENT_F_DTV_DEMOD,
+ demod, 0,
+ MEDIA_LNK_FL_ENABLED,
+ false);
+ } else {
+ pad_sink = media_get_pad_index(tuner, MEDIA_PAD_FL_SINK,
+ PAD_SIGNAL_ANALOG);
+ if (pad_sink < 0)
+ return -EINVAL;
+ ret = media_create_pad_links(mdev,
+ MEDIA_ENT_F_CONN_RF,
+ conn, 0,
+ MEDIA_ENT_F_TUNER,
+ tuner, pad_sink,
+ MEDIA_LNK_FL_ENABLED,
+ false);
+ }
+ if (ret)
+ return ret;
+ }
+
+ if (ntuner && ndemod) {
+ /* NOTE: first found tuner source pad presumed correct */
+ pad_source = media_get_pad_index(tuner, MEDIA_PAD_FL_SOURCE,
+ PAD_SIGNAL_ANALOG);
+ if (pad_source < 0)
+ return -EINVAL;
+ ret = media_create_pad_links(mdev,
+ MEDIA_ENT_F_TUNER,
+ tuner, pad_source,
+ MEDIA_ENT_F_DTV_DEMOD,
+ demod, 0, MEDIA_LNK_FL_ENABLED,
+ false);
+ if (ret)
+ return ret;
+ }
+
+ if (ndemod && demux) {
+ ret = media_create_pad_links(mdev,
+ MEDIA_ENT_F_DTV_DEMOD,
+ demod, 1,
+ MEDIA_ENT_F_TS_DEMUX,
+ demux, 0, MEDIA_LNK_FL_ENABLED,
+ false);
+ if (ret)
+ return ret;
+ }
+ if (demux && ca) {
+ ret = media_create_pad_link(demux, 1, ca,
+ 0, MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+ }
+
+ /* Create demux links for each ringbuffer/pad */
+ if (demux) {
+ media_device_for_each_entity(entity, mdev) {
+ if (entity->function == MEDIA_ENT_F_IO_DTV) {
+ if (!strncmp(entity->name, DVR_TSOUT,
+ strlen(DVR_TSOUT))) {
+ ret = media_create_pad_link(demux,
+ ++dvr_pad,
+ entity, 0, 0);
+ if (ret)
+ return ret;
+ }
+ if (!strncmp(entity->name, DEMUX_TSOUT,
+ strlen(DEMUX_TSOUT))) {
+ ret = media_create_pad_link(demux,
+ ++demux_pad,
+ entity, 0, 0);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+ }
+
+ /* Create interface links for FE->tuner, DVR->demux and CA->ca */
+ media_device_for_each_intf(intf, mdev) {
+ if (intf->type == MEDIA_INTF_T_DVB_CA && ca) {
+ link = media_create_intf_link(ca, intf,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (!link)
+ return -ENOMEM;
+ }
+
+ if (intf->type == MEDIA_INTF_T_DVB_FE && tuner) {
+ link = media_create_intf_link(tuner, intf,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (!link)
+ return -ENOMEM;
+ }
+#if 0
+ /*
+ * Indirect link - let's not create yet, as we don't know how
+ * to handle indirect links, nor if this will
+ * actually be needed.
+ */
+ if (intf->type == MEDIA_INTF_T_DVB_DVR && demux) {
+ link = media_create_intf_link(demux, intf,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (!link)
+ return -ENOMEM;
+ }
+#endif
+ if (intf->type == MEDIA_INTF_T_DVB_DVR) {
+ ret = dvb_create_io_intf_links(adap, intf, DVR_TSOUT);
+ if (ret)
+ return ret;
+ }
+ if (intf->type == MEDIA_INTF_T_DVB_DEMUX) {
+ ret = dvb_create_io_intf_links(adap, intf, DEMUX_TSOUT);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dvb_create_media_graph);
+#endif
+
+static int dvbdev_check_free_adapter_num(int num)
+{
+ struct list_head *entry;
+
+ list_for_each(entry, &dvb_adapter_list) {
+ struct dvb_adapter *adap;
+
+ adap = list_entry(entry, struct dvb_adapter, list_head);
+ if (adap->num == num)
+ return 0;
+ }
+ return 1;
+}
+
+static int dvbdev_get_free_adapter_num(void)
+{
+ int num = 0;
+
+ while (num < DVB_MAX_ADAPTERS) {
+ if (dvbdev_check_free_adapter_num(num))
+ return num;
+ num++;
+ }
+
+ return -ENFILE;
+}
+
+int dvb_register_adapter(struct dvb_adapter *adap, const char *name,
+ struct module *module, struct device *device,
+ short *adapter_nums)
+{
+ int i, num;
+
+ mutex_lock(&dvbdev_register_lock);
+
+ for (i = 0; i < DVB_MAX_ADAPTERS; ++i) {
+ num = adapter_nums[i];
+ if (num >= 0 && num < DVB_MAX_ADAPTERS) {
+ /* use the one the driver asked for */
+ if (dvbdev_check_free_adapter_num(num))
+ break;
+ } else {
+ num = dvbdev_get_free_adapter_num();
+ break;
+ }
+ num = -1;
+ }
+
+ if (num < 0) {
+ mutex_unlock(&dvbdev_register_lock);
+ return -ENFILE;
+ }
+
+ memset(adap, 0, sizeof(struct dvb_adapter));
+ INIT_LIST_HEAD(&adap->device_list);
+
+ pr_info("DVB: registering new adapter (%s)\n", name);
+
+ adap->num = num;
+ adap->name = name;
+ adap->module = module;
+ adap->device = device;
+ adap->mfe_shared = 0;
+ adap->mfe_dvbdev = NULL;
+ mutex_init(&adap->mfe_lock);
+
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ mutex_init(&adap->mdev_lock);
+#endif
+
+ list_add_tail(&adap->list_head, &dvb_adapter_list);
+
+ mutex_unlock(&dvbdev_register_lock);
+
+ return num;
+}
+EXPORT_SYMBOL(dvb_register_adapter);
+
+int dvb_unregister_adapter(struct dvb_adapter *adap)
+{
+ mutex_lock(&dvbdev_register_lock);
+ list_del(&adap->list_head);
+ mutex_unlock(&dvbdev_register_lock);
+ return 0;
+}
+EXPORT_SYMBOL(dvb_unregister_adapter);
+
+/*
+ * if the miracle happens and "generic_usercopy()" is included into
+ * the kernel, then this can vanish. please don't make the mistake and
+ * define this as video_usercopy(). this will introduce a dependency
+ * to the v4l "videodev.o" module, which is unnecessary for some
+ * cards (ie. the budget dvb-cards don't need the v4l module...)
+ */
+int dvb_usercopy(struct file *file,
+ unsigned int cmd, unsigned long arg,
+ int (*func)(struct file *file,
+ unsigned int cmd, void *arg))
+{
+ char sbuf[128] = {};
+ void *mbuf = NULL;
+ void *parg = NULL;
+ int err = -EINVAL;
+
+ /* Copy arguments into temp kernel buffer */
+ switch (_IOC_DIR(cmd)) {
+ case _IOC_NONE:
+ /*
+ * For this command, the pointer is actually an integer
+ * argument.
+ */
+ parg = (void *)arg;
+ break;
+ case _IOC_READ: /* some v4l ioctls are marked wrong ... */
+ case _IOC_WRITE:
+ case (_IOC_WRITE | _IOC_READ):
+ if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
+ parg = sbuf;
+ } else {
+ /* too big to allocate from stack */
+ mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
+ if (!mbuf)
+ return -ENOMEM;
+ parg = mbuf;
+ }
+
+ err = -EFAULT;
+ if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
+ goto out;
+ break;
+ }
+
+ /* call driver */
+ err = func(file, cmd, parg);
+ if (err == -ENOIOCTLCMD)
+ err = -ENOTTY;
+
+ if (err < 0)
+ goto out;
+
+ /* Copy results into user buffer */
+ switch (_IOC_DIR(cmd)) {
+ case _IOC_READ:
+ case (_IOC_WRITE | _IOC_READ):
+ if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
+ err = -EFAULT;
+ break;
+ }
+
+out:
+ kfree(mbuf);
+ return err;
+}
+
+#if IS_ENABLED(CONFIG_I2C)
+struct i2c_client *dvb_module_probe(const char *module_name,
+ const char *name,
+ struct i2c_adapter *adap,
+ unsigned char addr,
+ void *platform_data)
+{
+ struct i2c_client *client;
+ struct i2c_board_info *board_info;
+
+ board_info = kzalloc(sizeof(*board_info), GFP_KERNEL);
+ if (!board_info)
+ return NULL;
+
+ if (name)
+ strscpy(board_info->type, name, I2C_NAME_SIZE);
+ else
+ strscpy(board_info->type, module_name, I2C_NAME_SIZE);
+
+ board_info->addr = addr;
+ board_info->platform_data = platform_data;
+ request_module(module_name);
+ client = i2c_new_client_device(adap, board_info);
+ if (!i2c_client_has_driver(client)) {
+ kfree(board_info);
+ return NULL;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ client = NULL;
+ }
+
+ kfree(board_info);
+ return client;
+}
+EXPORT_SYMBOL_GPL(dvb_module_probe);
+
+void dvb_module_release(struct i2c_client *client)
+{
+ if (!client)
+ return;
+
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+}
+EXPORT_SYMBOL_GPL(dvb_module_release);
+#endif
+
+static int dvb_uevent(const struct device *dev, struct kobj_uevent_env *env)
+{
+ const struct dvb_device *dvbdev = dev_get_drvdata(dev);
+
+ add_uevent_var(env, "DVB_ADAPTER_NUM=%d", dvbdev->adapter->num);
+ add_uevent_var(env, "DVB_DEVICE_TYPE=%s", dnames[dvbdev->type]);
+ add_uevent_var(env, "DVB_DEVICE_NUM=%d", dvbdev->id);
+ return 0;
+}
+
+static char *dvb_devnode(const struct device *dev, umode_t *mode)
+{
+ const struct dvb_device *dvbdev = dev_get_drvdata(dev);
+
+ return kasprintf(GFP_KERNEL, "dvb/adapter%d/%s%d",
+ dvbdev->adapter->num, dnames[dvbdev->type], dvbdev->id);
+}
+
+static int __init init_dvbdev(void)
+{
+ int retval;
+ dev_t dev = MKDEV(DVB_MAJOR, 0);
+
+ retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB");
+ if (retval != 0) {
+ pr_err("dvb-core: unable to get major %d\n", DVB_MAJOR);
+ return retval;
+ }
+
+ cdev_init(&dvb_device_cdev, &dvb_device_fops);
+ retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS);
+ if (retval != 0) {
+ pr_err("dvb-core: unable register character device\n");
+ goto error;
+ }
+
+ dvb_class = class_create("dvb");
+ if (IS_ERR(dvb_class)) {
+ retval = PTR_ERR(dvb_class);
+ goto error;
+ }
+ dvb_class->dev_uevent = dvb_uevent;
+ dvb_class->devnode = dvb_devnode;
+ return 0;
+
+error:
+ cdev_del(&dvb_device_cdev);
+ unregister_chrdev_region(dev, MAX_DVB_MINORS);
+ return retval;
+}
+
+static void __exit exit_dvbdev(void)
+{
+ struct dvbdevfops_node *node, *next;
+
+ class_destroy(dvb_class);
+ cdev_del(&dvb_device_cdev);
+ unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS);
+
+ list_for_each_entry_safe(node, next, &dvbdevfops_list, list_head) {
+ list_del(&node->list_head);
+ kfree(node->fops);
+ kfree(node);
+ }
+}
+
+subsys_initcall(init_dvbdev);
+module_exit(exit_dvbdev);
+
+MODULE_DESCRIPTION("DVB Core Driver");
+MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
new file mode 100644
index 000000000000..bcc97ca86ed5
--- /dev/null
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -0,0 +1,957 @@
+# SPDX-License-Identifier: GPL-2.0
+
+if MEDIA_DIGITAL_TV_SUPPORT
+
+comment "DVB Frontend drivers auto-selected by 'Autoselect ancillary drivers'"
+ depends on MEDIA_HIDE_ANCILLARY_SUBDRV
+
+menu "Customise DVB Frontends"
+ visible if !MEDIA_HIDE_ANCILLARY_SUBDRV
+
+comment "Multistandard (satellite) frontends"
+ depends on DVB_CORE
+
+config DVB_M88DS3103
+ tristate "Montage Technology M88DS3103"
+ depends on DVB_CORE && I2C && I2C_MUX
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_MXL5XX
+ tristate "MaxLinear MxL5xx based tuner-demodulators"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ MaxLinear MxL5xx family of DVB-S/S2 tuners/demodulators.
+
+ Say Y when you want to support these frontends.
+
+config DVB_STB0899
+ tristate "STB0899 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S/S2/DSS Multistandard demodulator. Say Y when you want
+ to support this demodulator based frontends
+
+config DVB_STB6100
+ tristate "STB6100 based tuners"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A Silicon tuner from ST used in conjunction with the STB0899
+ demodulator. Say Y when you want to support this tuner.
+
+config DVB_STV090x
+ tristate "STV0900/STV0903(A/B) based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ DVB-S/S2/DSS Multistandard Professional/Broadcast demodulators.
+ Say Y when you want to support these frontends.
+
+config DVB_STV0910
+ tristate "STV0910 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ ST STV0910 DVB-S/S2 demodulator driver.
+
+ Say Y when you want to support these frontends.
+
+config DVB_STV6110x
+ tristate "STV6110/(A) based tuners"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A Silicon tuner that supports DVB-S and DVB-S2 modes
+
+config DVB_STV6111
+ tristate "STV6111 based tuners"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A Silicon tuner that supports DVB-S and DVB-S2 modes
+
+ Say Y when you want to support these frontends.
+
+comment "Multistandard (cable + terrestrial) frontends"
+ depends on DVB_CORE
+
+config DVB_DRXK
+ tristate "Micronas DRXK based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Micronas DRX-K DVB-C/T demodulator.
+
+ Say Y when you want to support this frontend.
+
+config DVB_MN88472
+ tristate "Panasonic MN88472"
+ depends on DVB_CORE && I2C
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_MN88473
+ tristate "Panasonic MN88473"
+ depends on DVB_CORE && I2C
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_SI2165
+ tristate "Silicon Labs si2165 based"
+ depends on DVB_CORE && I2C
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-C/T demodulator.
+
+ Say Y when you want to support this frontend.
+
+config DVB_TDA18271C2DD
+ tristate "NXP TDA18271C2 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ NXP TDA18271 silicon tuner.
+
+ Say Y when you want to support this tuner.
+
+comment "DVB-S (satellite) frontends"
+ depends on DVB_CORE
+
+config DVB_CX24110
+ tristate "Conexant CX24110 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_CX24116
+ tristate "Conexant CX24116 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
+config DVB_CX24117
+ tristate "Conexant CX24117 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A Dual DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
+config DVB_CX24120
+ tristate "Conexant CX24120 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
+config DVB_CX24123
+ tristate "Conexant CX24123 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_DS3000
+ tristate "Montage Technology DS3000 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
+config DVB_MB86A16
+ tristate "Fujitsu MB86A16 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S/DSS Direct Conversion reveiver.
+ Say Y when you want to support this frontend.
+
+config DVB_MT312
+ tristate "Zarlink VP310/MT312/ZL10313 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_S5H1420
+ tristate "Samsung S5H1420 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_SI21XX
+ tristate "Silicon Labs SI21XX based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_STB6000
+ tristate "ST STB6000 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S silicon tuner module. Say Y when you want to support this tuner.
+
+config DVB_STV0288
+ tristate "ST STV0288 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_STV0299
+ tristate "ST STV0299 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_STV0900
+ tristate "ST STV0900 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S/S2 demodulator. Say Y when you want to support this frontend.
+
+config DVB_STV6110
+ tristate "ST STV6110 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S silicon tuner module. Say Y when you want to support this tuner.
+
+config DVB_TDA10071
+ tristate "NXP TDA10071"
+ depends on DVB_CORE && I2C
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_TDA10086
+ tristate "Philips TDA10086 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA8083
+ tristate "Philips TDA8083 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA8261
+ tristate "Philips TDA8261 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA826X
+ tristate "Philips TDA826X silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S silicon tuner module. Say Y when you want to support this tuner.
+
+config DVB_TS2020
+ tristate "Montage Technology TS2020 based tuners"
+ depends on DVB_CORE && I2C
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S/S2 silicon tuner. Say Y when you want to support this tuner.
+
+config DVB_TUA6100
+ tristate "Infineon TUA6100 PLL"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S PLL chip.
+
+config DVB_TUNER_CX24113
+ tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TUNER_ITD1000
+ tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_VES1X93
+ tristate "VLSI VES1893 or VES1993 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_ZL10036
+ tristate "Zarlink ZL10036 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_ZL10039
+ tristate "Zarlink ZL10039 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+comment "DVB-T (terrestrial) frontends"
+ depends on DVB_CORE
+
+config DVB_AF9013
+ tristate "Afatech AF9013 demodulator"
+ depends on DVB_CORE && I2C && I2C_MUX
+ select REGMAP
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_AS102_FE
+ tristate
+ depends on DVB_CORE
+ default DVB_AS102
+
+config DVB_CX22700
+ tristate "Conexant CX22700 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_CX22702
+ tristate "Conexant cx22702 demodulator (OFDM)"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_CXD2820R
+ tristate "Sony CXD2820R"
+ depends on DVB_CORE && I2C
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_CXD2841ER
+ tristate "Sony CXD2841ER"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_DIB3000MB
+ tristate "DiBcom 3000M-B"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
+config DVB_DIB3000MC
+ tristate "DiBcom 3000P/M-C"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
+config DVB_DIB7000M
+ tristate "DiBcom 7000MA/MB/PA/PB/MC"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
+config DVB_DIB7000P
+ tristate "DiBcom 7000PC"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
+config DVB_DIB9000
+ tristate "DiBcom 9000"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
+config DVB_DRXD
+ tristate "Micronas DRXD driver"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+ Note: this driver was based on vendor driver reference code (released
+ under the GPL) as opposed to the existing drx397xd driver, which
+ was written via reverse engineering.
+
+config DVB_EC100
+ tristate "E3C EC100"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_GP8PSK_FE
+ tristate
+ depends on DVB_CORE
+ default DVB_USB_GP8PSK
+
+config DVB_L64781
+ tristate "LSI L64781"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_MT352
+ tristate "Zarlink MT352 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_NXT6000
+ tristate "NxtWave Communications NXT6000 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_RTL2830
+ tristate "Realtek RTL2830 DVB-T"
+ depends on DVB_CORE && I2C && I2C_MUX
+ select REGMAP
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_RTL2832
+ tristate "Realtek RTL2832 DVB-T"
+ depends on DVB_CORE && I2C && I2C_MUX
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_RTL2832_SDR
+ tristate "Realtek RTL2832 SDR"
+ depends on DVB_CORE && I2C && I2C_MUX && VIDEO_DEV && MEDIA_SDR_SUPPORT && USB
+ select DVB_RTL2832
+ select VIDEOBUF2_VMALLOC
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this SDR module.
+
+config DVB_S5H1432
+ tristate "Samsung s5h1432 demodulator (OFDM)"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_SI2168
+ tristate "Silicon Labs Si2168"
+ depends on DVB_CORE && I2C && I2C_MUX
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_SP887X
+ tristate "Spase sp887x based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+ This driver needs external firmware. Please use the command
+ "<kerneldir>/scripts/get_dvb_firmware sp887x" to
+ download/extract it, and then copy it to /usr/lib/hotplug/firmware
+ or /lib/firmware (depending on configuration of firmware hotplug).
+
+config DVB_STV0367
+ tristate "ST STV0367 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T/C tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA10048
+ tristate "Philips TDA10048HN based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA1004X
+ tristate "Philips TDA10045H/TDA10046H based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+ This driver needs external firmware. Please use the commands
+ "<kerneldir>/scripts/get_dvb_firmware tda10045",
+ "<kerneldir>/scripts/get_dvb_firmware tda10046" to
+ download/extract them, and then copy them to /usr/lib/hotplug/firmware
+ or /lib/firmware (depending on configuration of firmware hotplug).
+
+config DVB_ZD1301_DEMOD
+ tristate "ZyDAS ZD1301"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_ZL10353
+ tristate "Zarlink ZL10353 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+source "drivers/media/dvb-frontends/cxd2880/Kconfig"
+
+comment "DVB-C (cable) frontends"
+ depends on DVB_CORE
+
+config DVB_STV0297
+ tristate "ST STV0297 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-C tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA10021
+ tristate "Philips TDA10021 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-C tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA10023
+ tristate "Philips TDA10023 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-C tuner module. Say Y when you want to support this frontend.
+
+config DVB_VES1820
+ tristate "VLSI VES1820 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-C tuner module. Say Y when you want to support this frontend.
+
+comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends"
+ depends on DVB_CORE
+
+config DVB_AU8522
+ depends on DVB_CORE && I2C
+ tristate
+
+config DVB_AU8522_DTV
+ tristate "Auvitek AU8522 based DTV demod"
+ depends on DVB_CORE && I2C
+ select DVB_AU8522
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when
+ you want to enable DTV demodulation support for this frontend.
+
+config DVB_AU8522_V4L
+ tristate "Auvitek AU8522 based ATV demod"
+ depends on VIDEO_DEV && DVB_CORE && I2C
+ select DVB_AU8522
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when
+ you want to enable ATV demodulation support for this frontend.
+
+config DVB_BCM3510
+ tristate "Broadcom BCM3510"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to
+ support this frontend.
+
+config DVB_LG2160
+ tristate "LG Electronics LG216x based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC/MH demodulator module. Say Y when you want
+ to support this frontend.
+
+config DVB_LGDT3305
+ tristate "LG Electronics LGDT3304 and LGDT3305 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
+config DVB_LGDT3306A
+ tristate "LG Electronics LGDT3306A based"
+ depends on DVB_CORE && I2C && I2C_MUX
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB and QAM-B 64/256 demodulator module. Say Y when you want
+ to support this frontend.
+
+config DVB_LGDT330X
+ tristate "LG Electronics LGDT3302/LGDT3303 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
+config DVB_MXL692
+ tristate "MaxLinear MXL692 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ MaxLinear MxL692 is a combo tuner-demodulator that
+ supports ATSC 8VSB and QAM modes. Say Y when you want to
+ support this frontend.
+
+config DVB_NXT200X
+ tristate "NxtWave Communications NXT2002/NXT2004 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
+ This driver needs external firmware. Please use the commands
+ "<kerneldir>/scripts/get_dvb_firmware nxt2002" and
+ "<kerneldir>/scripts/get_dvb_firmware nxt2004" to
+ download/extract them, and then copy them to /usr/lib/hotplug/firmware
+ or /lib/firmware (depending on configuration of firmware hotplug).
+
+config DVB_OR51132
+ tristate "Oren OR51132 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
+ This driver needs external firmware. Please use the commands
+ "<kerneldir>/scripts/get_dvb_firmware or51132_vsb" and/or
+ "<kerneldir>/scripts/get_dvb_firmware or51132_qam" to
+ download firmwares for 8VSB and QAM64/256, respectively. Copy them to
+ /usr/lib/hotplug/firmware or /lib/firmware (depending on
+ configuration of firmware hotplug).
+
+config DVB_OR51211
+ tristate "Oren OR51211 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB tuner module. Say Y when you want to support this frontend.
+
+ This driver needs external firmware. Please use the command
+ "<kerneldir>/scripts/get_dvb_firmware or51211" to
+ download it, and then copy it to /usr/lib/hotplug/firmware
+ or /lib/firmware (depending on configuration of firmware hotplug).
+
+config DVB_S5H1409
+ tristate "Samsung S5H1409 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
+config DVB_S5H1411
+ tristate "Samsung S5H1411 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
+comment "ISDB-T (terrestrial) frontends"
+ depends on DVB_CORE
+
+config DVB_DIB8000
+ tristate "DiBcom 8000MB/MC"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for DiBcom's DiB8000 ISDB-T/ISDB-Tsb demodulator.
+ Say Y when you want to support this frontend.
+
+config DVB_MB86A20S
+ tristate "Fujitsu mb86a20s"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for Fujitsu mb86a20s ISDB-T/ISDB-Tsb demodulator.
+ Say Y when you want to support this frontend.
+
+config DVB_S921
+ tristate "Sharp S921 frontend"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module.
+ Say Y when you want to support this frontend.
+
+comment "ISDB-S (satellite) & ISDB-T (terrestrial) frontends"
+ depends on DVB_CORE
+
+config DVB_MN88443X
+ tristate "Socionext MN88443x"
+ depends on DVB_CORE && I2C
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for Socionext/Panasonic MN884433 and MN884434
+ ISDB-S + ISDB-T demodulator.
+ Say Y when you want to support this frontend.
+
+config DVB_TC90522
+ tristate "Toshiba TC90522"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Toshiba TC90522 2xISDB-S 8PSK + 2xISDB-T OFDM demodulator.
+ Say Y when you want to support this frontend.
+
+comment "Digital terrestrial only tuners/PLL"
+ depends on DVB_CORE
+
+config DVB_PLL
+ tristate "Generic I2C PLL based tuners"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ This module drives a number of tuners based on PLL chips with a
+ common I2C interface. Say Y when you want to support these tuners.
+
+config DVB_TUNER_DIB0070
+ tristate "DiBcom DiB0070 silicon base-band tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the silicon baseband tuner DiB0070 from DiBcom.
+ This device is only used inside a SiP called together with a
+ demodulator for now.
+
+config DVB_TUNER_DIB0090
+ tristate "DiBcom DiB0090 silicon base-band tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the silicon baseband tuner DiB0090 from DiBcom.
+ This device is only used inside a SiP called together with a
+ demodulator for now.
+
+comment "SEC control devices for DVB-S"
+ depends on DVB_CORE
+
+config DVB_A8293
+ tristate "Allegro A8293"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+
+config DVB_AF9033
+ tristate "Afatech AF9033 DVB-T demodulator"
+ depends on DVB_CORE && I2C
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+
+config DVB_ASCOT2E
+ tristate "Sony Ascot2E tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_ATBM8830
+ tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DMB-TH tuner module. Say Y when you want to support this frontend.
+
+config DVB_HELENE
+ tristate "Sony HELENE Sat/Ter tuner (CXD2858ER)"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_HORUS3A
+ tristate "Sony Horus3A tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_ISL6405
+ tristate "ISL6405 SEC controller"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An SEC control chip.
+
+config DVB_ISL6421
+ tristate "ISL6421 SEC controller"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An SEC control chip.
+
+config DVB_ISL6423
+ tristate "ISL6423 SEC controller"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A SEC controller chip from Intersil
+
+config DVB_IX2505V
+ tristate "Sharp IX2505V silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_LGS8GL5
+ tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DMB-TH tuner module. Say Y when you want to support this frontend.
+
+config DVB_LGS8GXX
+ tristate "Legend Silicon LGS8913/LGS8GL5/LGS8GXX DMB-TH demodulator"
+ depends on DVB_CORE && I2C
+ select FW_LOADER
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DMB-TH tuner module. Say Y when you want to support this frontend.
+
+config DVB_LNBH25
+ tristate "LNBH25 SEC controller"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An SEC control chip.
+ Say Y when you want to support this chip.
+
+config DVB_LNBH29
+ tristate "LNBH29 SEC controller"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ LNB power supply and control voltage
+ regulator chip with step-up converter
+ and I2C interface for STMicroelectronics LNBH29.
+ Say Y when you want to support this chip.
+
+config DVB_LNBP21
+ tristate "LNBP21/LNBH24 SEC controllers"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An SEC control chips.
+
+config DVB_LNBP22
+ tristate "LNBP22 SEC controllers"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ LNB power supply and control voltage
+ regulator chip with step-up converter
+ and I2C interface.
+ Say Y when you want to support this chip.
+
+config DVB_M88RS2000
+ tristate "M88RS2000 DVB-S demodulator and tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module.
+ Say Y when you want to support this frontend.
+
+config DVB_TDA665x
+ tristate "TDA665x tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for tuner modules based on Philips TDA6650/TDA6651 chips.
+ Say Y when you want to support this chip.
+
+ Currently supported tuners:
+ * Panasonic ENV57H12D5 (ET-50DT)
+
+source "drivers/media/dvb-frontends/drx39xyj/Kconfig"
+
+comment "Common Interface (EN50221) controller drivers"
+ depends on DVB_CORE
+
+config DVB_CXD2099
+ tristate "Sony CXD2099AR Common Interface driver"
+ depends on DVB_CORE && I2C
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the CI controller currently found mostly on
+ Digital Devices DuoFlex CI (single) addon modules.
+
+ Say Y when you want to support these devices.
+
+config DVB_SP2
+ tristate "CIMaX SP2"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ CIMaX SP2/SP2HF Common Interface module.
+
+endmenu # Customise DVB Frontends
+
+endif # MEDIA_DIGITAL_TV_SUPPORT
+
+comment "Tools to develop new frontends"
+ depends on MEDIA_TEST_SUPPORT
+
+config DVB_DUMMY_FE
+ tristate "Dummy frontend driver"
+ depends on MEDIA_TEST_SUPPORT && DVB_CORE
+ help
+ Dummy skeleton frontend driver.
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
new file mode 100644
index 000000000000..a93146cb428c
--- /dev/null
+++ b/drivers/media/dvb-frontends/Makefile
@@ -0,0 +1,138 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the kernel DVB frontend device drivers.
+#
+
+ccflags-y += -I$(srctree)/drivers/media/tuners/
+
+# FIXME: RTL2832 SDR driver uses power management directly from USB IF driver
+ifdef CONFIG_DVB_RTL2832_SDR
+ ccflags-y += -I$(srctree)/drivers/media/usb/dvb-usb-v2
+endif
+
+cxd2820r-objs := cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o
+drxd-objs := drxd_firm.o drxd_hard.o
+drxk-objs := drxk_hard.o
+stb0899-objs := stb0899_drv.o stb0899_algo.o
+stv0900-objs := stv0900_core.o stv0900_sw.o
+
+# Please keep it alphabetically sorted by Kconfig name
+# (e. g. LC_ALL=C sort Makefile)
+
+obj-$(CONFIG_DVB_A8293) += a8293.o
+obj-$(CONFIG_DVB_AF9013) += af9013.o
+obj-$(CONFIG_DVB_AF9033) += af9033.o
+obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o
+obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o
+obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o
+obj-$(CONFIG_DVB_AU8522) += au8522_common.o
+obj-$(CONFIG_DVB_AU8522_DTV) += au8522_dig.o
+obj-$(CONFIG_DVB_AU8522_V4L) += au8522_decoder.o
+obj-$(CONFIG_DVB_BCM3510) += bcm3510.o
+obj-$(CONFIG_DVB_CX22700) += cx22700.o
+obj-$(CONFIG_DVB_CX22702) += cx22702.o
+obj-$(CONFIG_DVB_CX24110) += cx24110.o
+obj-$(CONFIG_DVB_CX24116) += cx24116.o
+obj-$(CONFIG_DVB_CX24117) += cx24117.o
+obj-$(CONFIG_DVB_CX24120) += cx24120.o
+obj-$(CONFIG_DVB_CX24123) += cx24123.o
+obj-$(CONFIG_DVB_CXD2099) += cxd2099.o
+obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o
+obj-$(CONFIG_DVB_CXD2841ER) += cxd2841er.o
+obj-$(CONFIG_DVB_CXD2880) += cxd2880/
+obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o
+obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o
+obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o
+obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o
+obj-$(CONFIG_DVB_DIB8000) += dib8000.o dibx000_common.o
+obj-$(CONFIG_DVB_DIB9000) += dib9000.o dibx000_common.o
+obj-$(CONFIG_DVB_DRX39XYJ) += drx39xyj/
+obj-$(CONFIG_DVB_DRXD) += drxd.o
+obj-$(CONFIG_DVB_DRXK) += drxk.o
+obj-$(CONFIG_DVB_DS3000) += ds3000.o
+obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o
+obj-$(CONFIG_DVB_EC100) += ec100.o
+obj-$(CONFIG_DVB_GP8PSK_FE) += gp8psk-fe.o
+obj-$(CONFIG_DVB_HELENE) += helene.o
+obj-$(CONFIG_DVB_HORUS3A) += horus3a.o
+obj-$(CONFIG_DVB_ISL6405) += isl6405.o
+obj-$(CONFIG_DVB_ISL6421) += isl6421.o
+obj-$(CONFIG_DVB_ISL6423) += isl6423.o
+obj-$(CONFIG_DVB_IX2505V) += ix2505v.o
+obj-$(CONFIG_DVB_L64781) += l64781.o
+obj-$(CONFIG_DVB_LG2160) += lg2160.o
+obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o
+obj-$(CONFIG_DVB_LGDT3306A) += lgdt3306a.o
+obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o
+obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
+obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o
+obj-$(CONFIG_DVB_LNBH25) += lnbh25.o
+obj-$(CONFIG_DVB_LNBH29) += lnbh29.o
+obj-$(CONFIG_DVB_LNBP21) += lnbp21.o
+obj-$(CONFIG_DVB_LNBP22) += lnbp22.o
+obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o
+obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
+obj-$(CONFIG_DVB_MB86A16) += mb86a16.o
+obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o
+obj-$(CONFIG_DVB_MN88443X) += mn88443x.o
+obj-$(CONFIG_DVB_MN88472) += mn88472.o
+obj-$(CONFIG_DVB_MN88473) += mn88473.o
+obj-$(CONFIG_DVB_MT312) += mt312.o
+obj-$(CONFIG_DVB_MT352) += mt352.o
+obj-$(CONFIG_DVB_MXL5XX) += mxl5xx.o
+obj-$(CONFIG_DVB_MXL692) += mxl692.o
+obj-$(CONFIG_DVB_NXT200X) += nxt200x.o
+obj-$(CONFIG_DVB_NXT6000) += nxt6000.o
+obj-$(CONFIG_DVB_OR51132) += or51132.o
+obj-$(CONFIG_DVB_OR51211) += or51211.o
+obj-$(CONFIG_DVB_PLL) += dvb-pll.o
+obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
+obj-$(CONFIG_DVB_RTL2832) += rtl2832.o
+obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o
+obj-$(CONFIG_DVB_S5H1409) += s5h1409.o
+obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
+obj-$(CONFIG_DVB_S5H1420) += s5h1420.o
+obj-$(CONFIG_DVB_S5H1432) += s5h1432.o
+obj-$(CONFIG_DVB_S921) += s921.o
+obj-$(CONFIG_DVB_SI2165) += si2165.o
+obj-$(CONFIG_DVB_SI2168) += si2168.o
+obj-$(CONFIG_DVB_SI21XX) += si21xx.o
+obj-$(CONFIG_DVB_SP2) += sp2.o
+obj-$(CONFIG_DVB_SP887X) += sp887x.o
+obj-$(CONFIG_DVB_STB0899) += stb0899.o
+obj-$(CONFIG_DVB_STB6000) += stb6000.o
+obj-$(CONFIG_DVB_STB6100) += stb6100.o
+obj-$(CONFIG_DVB_STV0288) += stv0288.o
+obj-$(CONFIG_DVB_STV0297) += stv0297.o
+obj-$(CONFIG_DVB_STV0299) += stv0299.o
+obj-$(CONFIG_DVB_STV0367) += stv0367.o
+obj-$(CONFIG_DVB_STV0900) += stv0900.o
+obj-$(CONFIG_DVB_STV090x) += stv090x.o
+obj-$(CONFIG_DVB_STV0910) += stv0910.o
+obj-$(CONFIG_DVB_STV6110) += stv6110.o
+obj-$(CONFIG_DVB_STV6110x) += stv6110x.o
+obj-$(CONFIG_DVB_STV6111) += stv6111.o
+obj-$(CONFIG_DVB_TC90522) += tc90522.o
+obj-$(CONFIG_DVB_TDA10021) += tda10021.o
+obj-$(CONFIG_DVB_TDA10023) += tda10023.o
+obj-$(CONFIG_DVB_TDA10048) += tda10048.o
+obj-$(CONFIG_DVB_TDA1004X) += tda1004x.o
+obj-$(CONFIG_DVB_TDA10071) += tda10071.o
+obj-$(CONFIG_DVB_TDA10086) += tda10086.o
+obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
+obj-$(CONFIG_DVB_TDA665x) += tda665x.o
+obj-$(CONFIG_DVB_TDA8083) += tda8083.o
+obj-$(CONFIG_DVB_TDA8261) += tda8261.o
+obj-$(CONFIG_DVB_TDA826X) += tda826x.o
+obj-$(CONFIG_DVB_TS2020) += ts2020.o
+obj-$(CONFIG_DVB_TUA6100) += tua6100.o
+obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o
+obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o
+obj-$(CONFIG_DVB_TUNER_DIB0090) += dib0090.o
+obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o
+obj-$(CONFIG_DVB_VES1820) += ves1820.o
+obj-$(CONFIG_DVB_VES1X93) += ves1x93.o
+obj-$(CONFIG_DVB_ZD1301_DEMOD) += zd1301_demod.o
+obj-$(CONFIG_DVB_ZL10036) += zl10036.o
+obj-$(CONFIG_DVB_ZL10039) += zl10039.o
+obj-$(CONFIG_DVB_ZL10353) += zl10353.o
diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c
new file mode 100644
index 000000000000..bf2773c5b97a
--- /dev/null
+++ b/drivers/media/dvb-frontends/a8293.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Allegro A8293 SEC driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ */
+
+#include "a8293.h"
+
+#define A8293_FLAG_ODT 0x10
+
+struct a8293_dev {
+ struct i2c_client *client;
+ u8 reg[2];
+ int volt_slew_nanos_per_mv;
+};
+
+/*
+ * When increasing voltage, do so in minimal steps over time, minimizing
+ * risk of vIN undervoltage.
+ */
+
+static int a8293_set_voltage_slew(struct a8293_dev *dev,
+ struct i2c_client *client,
+ enum fe_sec_voltage fe_sec_voltage,
+ int min_nanos_per_mv)
+{
+ int ret;
+ u8 reg0, reg1;
+ int new_volt_idx;
+ const int idx_to_mv[] = {
+ 0, 12709, 13042, 13375, 14042, 15042, 18042, 18709, 19042
+ };
+ const u8 idx_to_reg[] = {
+ 0x00, 0x20, 0x21, 0x22, 0x24, 0x27, 0x28, 0x2A, 0x2B
+ };
+ int this_volt_idx;
+ u8 status;
+ int prev_volt_idx;
+
+ dev_dbg(&client->dev, "set_voltage_slew fe_sec_voltage=%d\n",
+ fe_sec_voltage);
+
+ /* Read status register to clear any stale faults. */
+ ret = i2c_master_recv(client, &status, 1);
+ if (ret < 0)
+ goto err;
+
+ /* Determine previous voltage */
+ switch (dev->reg[0] & 0x2F) {
+ case 0x00:
+ prev_volt_idx = 0;
+ break;
+ case 0x20:
+ prev_volt_idx = 1;
+ break;
+ case 0x21:
+ prev_volt_idx = 2;
+ break;
+ case 0x22:
+ prev_volt_idx = 3;
+ break;
+ case 0x24:
+ prev_volt_idx = 4;
+ break;
+ case 0x27:
+ prev_volt_idx = 5;
+ break;
+ case 0x28:
+ prev_volt_idx = 6;
+ break;
+ case 0x2A:
+ prev_volt_idx = 7;
+ break;
+ case 0x2B:
+ prev_volt_idx = 8;
+ break;
+ default:
+ prev_volt_idx = 0;
+ }
+
+ /* Determine new voltage */
+ switch (fe_sec_voltage) {
+ case SEC_VOLTAGE_OFF:
+ new_volt_idx = 0;
+ break;
+ case SEC_VOLTAGE_13:
+ new_volt_idx = 2;
+ break;
+ case SEC_VOLTAGE_18:
+ new_volt_idx = 6;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Slew to new voltage if new voltage is greater than current voltage */
+ this_volt_idx = prev_volt_idx;
+ if (this_volt_idx < new_volt_idx) {
+ while (this_volt_idx < new_volt_idx) {
+ int delta_mv = idx_to_mv[this_volt_idx+1] - idx_to_mv[this_volt_idx];
+ int min_wait_time = delta_mv * min_nanos_per_mv;
+
+ reg0 = idx_to_reg[this_volt_idx+1];
+ reg0 |= A8293_FLAG_ODT;
+
+ ret = i2c_master_send(client, &reg0, 1);
+ if (ret < 0)
+ goto err;
+ dev->reg[0] = reg0;
+ this_volt_idx++;
+ usleep_range(min_wait_time, min_wait_time * 2);
+ }
+ } else { /* Else just set the voltage */
+ reg0 = idx_to_reg[new_volt_idx];
+ reg0 |= A8293_FLAG_ODT;
+ ret = i2c_master_send(client, &reg0, 1);
+ if (ret < 0)
+ goto err;
+ dev->reg[0] = reg0;
+ }
+
+ /* TMODE=0, TGATE=1 */
+ reg1 = 0x82;
+ if (reg1 != dev->reg[1]) {
+ ret = i2c_master_send(client, &reg1, 1);
+ if (ret < 0)
+ goto err;
+ dev->reg[1] = reg1;
+ }
+
+ usleep_range(1500, 5000);
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+
+static int a8293_set_voltage_noslew(struct dvb_frontend *fe,
+ enum fe_sec_voltage fe_sec_voltage)
+{
+ struct a8293_dev *dev = fe->sec_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+ u8 reg0, reg1;
+
+ dev_dbg(&client->dev, "set_voltage_noslew fe_sec_voltage=%d\n",
+ fe_sec_voltage);
+
+ switch (fe_sec_voltage) {
+ case SEC_VOLTAGE_OFF:
+ /* ENB=0 */
+ reg0 = 0x10;
+ break;
+ case SEC_VOLTAGE_13:
+ /* VSEL0=1, VSEL1=0, VSEL2=0, VSEL3=0, ENB=1*/
+ reg0 = 0x31;
+ break;
+ case SEC_VOLTAGE_18:
+ /* VSEL0=0, VSEL1=0, VSEL2=0, VSEL3=1, ENB=1*/
+ reg0 = 0x38;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+ if (reg0 != dev->reg[0]) {
+ ret = i2c_master_send(client, &reg0, 1);
+ if (ret < 0)
+ goto err;
+ dev->reg[0] = reg0;
+ }
+
+ /* TMODE=0, TGATE=1 */
+ reg1 = 0x82;
+ if (reg1 != dev->reg[1]) {
+ ret = i2c_master_send(client, &reg1, 1);
+ if (ret < 0)
+ goto err;
+ dev->reg[1] = reg1;
+ }
+
+ usleep_range(1500, 50000);
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int a8293_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage fe_sec_voltage)
+{
+ struct a8293_dev *dev = fe->sec_priv;
+ struct i2c_client *client = dev->client;
+ int volt_slew_nanos_per_mv = dev->volt_slew_nanos_per_mv;
+
+ dev_dbg(&client->dev, "set_voltage volt_slew_nanos_per_mv=%d\n",
+ volt_slew_nanos_per_mv);
+
+ /* Use slew version if slew rate is set to a sane value */
+ if (volt_slew_nanos_per_mv > 0 && volt_slew_nanos_per_mv < 1600)
+ a8293_set_voltage_slew(dev, client, fe_sec_voltage,
+ volt_slew_nanos_per_mv);
+ else
+ a8293_set_voltage_noslew(fe, fe_sec_voltage);
+
+ return 0;
+}
+
+static int a8293_probe(struct i2c_client *client)
+{
+ struct a8293_dev *dev;
+ struct a8293_platform_data *pdata = client->dev.platform_data;
+ struct dvb_frontend *fe = pdata->dvb_frontend;
+ int ret;
+ u8 buf[2];
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev->client = client;
+ dev->volt_slew_nanos_per_mv = pdata->volt_slew_nanos_per_mv;
+
+ /* check if the SEC is there */
+ ret = i2c_master_recv(client, buf, 2);
+ if (ret < 0)
+ goto err_kfree;
+
+ /* override frontend ops */
+ fe->ops.set_voltage = a8293_set_voltage;
+ fe->sec_priv = dev;
+ i2c_set_clientdata(client, dev);
+
+ dev_info(&client->dev, "Allegro A8293 SEC successfully attached\n");
+ return 0;
+err_kfree:
+ kfree(dev);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static void a8293_remove(struct i2c_client *client)
+{
+ struct a8293_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ kfree(dev);
+}
+
+static const struct i2c_device_id a8293_id_table[] = {
+ { "a8293" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, a8293_id_table);
+
+static struct i2c_driver a8293_driver = {
+ .driver = {
+ .name = "a8293",
+ .suppress_bind_attrs = true,
+ },
+ .probe = a8293_probe,
+ .remove = a8293_remove,
+ .id_table = a8293_id_table,
+};
+
+module_i2c_driver(a8293_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Allegro A8293 SEC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/a8293.h b/drivers/media/dvb-frontends/a8293.h
new file mode 100644
index 000000000000..7fbac9735f7b
--- /dev/null
+++ b/drivers/media/dvb-frontends/a8293.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Allegro A8293 SEC driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ */
+
+#ifndef A8293_H
+#define A8293_H
+
+#include <media/dvb_frontend.h>
+
+/*
+ * I2C address
+ * 0x08, 0x09, 0x0a, 0x0b
+ */
+
+/**
+ * struct a8293_platform_data - Platform data for the a8293 driver
+ * @dvb_frontend: DVB frontend.
+ * @volt_slew_nanos_per_mv: Slew rate when increasing LNB voltage,
+ * in nanoseconds per millivolt.
+ */
+struct a8293_platform_data {
+ struct dvb_frontend *dvb_frontend;
+ int volt_slew_nanos_per_mv;
+};
+
+#endif /* A8293_H */
diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c
new file mode 100644
index 000000000000..befd6a4eafd9
--- /dev/null
+++ b/drivers/media/dvb-frontends/af9013.c
@@ -0,0 +1,1576 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Afatech AF9013 demodulator driver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ */
+
+#include "af9013_priv.h"
+
+struct af9013_state {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct i2c_mux_core *muxc;
+ struct dvb_frontend fe;
+ u32 clk;
+ u8 tuner;
+ u32 if_frequency;
+ u8 ts_mode;
+ u8 ts_output_pin;
+ bool spec_inv;
+ u8 api_version[4];
+ u8 gpio[4];
+
+ u32 bandwidth_hz;
+ enum fe_status fe_status;
+ /* RF and IF AGC limits used for signal strength calc */
+ u8 strength_en, rf_agc_50, rf_agc_80, if_agc_50, if_agc_80;
+ unsigned long set_frontend_jiffies;
+ unsigned long read_status_jiffies;
+ unsigned long strength_jiffies;
+ unsigned long cnr_jiffies;
+ unsigned long ber_ucb_jiffies;
+ u16 dvbv3_snr;
+ u16 dvbv3_strength;
+ u32 dvbv3_ber;
+ u32 dvbv3_ucblocks;
+ bool first_tune;
+};
+
+static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval)
+{
+ struct i2c_client *client = state->client;
+ int ret;
+ u8 pos;
+ u16 addr;
+
+ dev_dbg(&client->dev, "gpio %u, gpioval %02x\n", gpio, gpioval);
+
+ /*
+ * GPIO0 & GPIO1 0xd735
+ * GPIO2 & GPIO3 0xd736
+ */
+
+ switch (gpio) {
+ case 0:
+ case 1:
+ addr = 0xd735;
+ break;
+ case 2:
+ case 3:
+ addr = 0xd736;
+ break;
+
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ switch (gpio) {
+ case 0:
+ case 2:
+ pos = 0;
+ break;
+ case 1:
+ case 3:
+ default:
+ pos = 4;
+ break;
+ }
+
+ ret = regmap_update_bits(state->regmap, addr, 0x0f << pos,
+ gpioval << pos);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ fesettings->min_delay_ms = 800;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+
+ return 0;
+}
+
+static int af9013_set_frontend(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, sampling_freq;
+ bool auto_mode, spec_inv;
+ u8 buf[6];
+ u32 if_frequency, freq_cw;
+
+ dev_dbg(&client->dev, "frequency %u, bandwidth_hz %u\n",
+ c->frequency, c->bandwidth_hz);
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params) {
+ ret = fe->ops.tuner_ops.set_params(fe);
+ if (ret)
+ goto err;
+ }
+
+ /* program CFOE coefficients */
+ if (c->bandwidth_hz != state->bandwidth_hz) {
+ for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) {
+ if (coeff_lut[i].clock == state->clk &&
+ coeff_lut[i].bandwidth_hz == c->bandwidth_hz) {
+ break;
+ }
+ }
+
+ /* Return an error if can't find bandwidth or the right clock */
+ if (i == ARRAY_SIZE(coeff_lut)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = regmap_bulk_write(state->regmap, 0xae00, coeff_lut[i].val,
+ sizeof(coeff_lut[i].val));
+ if (ret)
+ goto err;
+ }
+
+ /* program frequency control */
+ if (c->bandwidth_hz != state->bandwidth_hz || state->first_tune) {
+ /* get used IF frequency */
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ ret = fe->ops.tuner_ops.get_if_frequency(fe,
+ &if_frequency);
+ if (ret)
+ goto err;
+ } else {
+ if_frequency = state->if_frequency;
+ }
+
+ dev_dbg(&client->dev, "if_frequency %u\n", if_frequency);
+
+ sampling_freq = if_frequency;
+
+ while (sampling_freq > (state->clk / 2))
+ sampling_freq -= state->clk;
+
+ if (sampling_freq < 0) {
+ sampling_freq *= -1;
+ spec_inv = state->spec_inv;
+ } else {
+ spec_inv = !state->spec_inv;
+ }
+
+ freq_cw = DIV_ROUND_CLOSEST_ULL((u64)sampling_freq * 0x800000,
+ state->clk);
+
+ if (spec_inv)
+ freq_cw = 0x800000 - freq_cw;
+
+ buf[0] = (freq_cw >> 0) & 0xff;
+ buf[1] = (freq_cw >> 8) & 0xff;
+ buf[2] = (freq_cw >> 16) & 0x7f;
+
+ freq_cw = 0x800000 - freq_cw;
+
+ buf[3] = (freq_cw >> 0) & 0xff;
+ buf[4] = (freq_cw >> 8) & 0xff;
+ buf[5] = (freq_cw >> 16) & 0x7f;
+
+ ret = regmap_bulk_write(state->regmap, 0xd140, buf, 3);
+ if (ret)
+ goto err;
+
+ ret = regmap_bulk_write(state->regmap, 0x9be7, buf, 6);
+ if (ret)
+ goto err;
+ }
+
+ /* clear TPS lock flag */
+ ret = regmap_update_bits(state->regmap, 0xd330, 0x08, 0x08);
+ if (ret)
+ goto err;
+
+ /* clear MPEG2 lock flag */
+ ret = regmap_update_bits(state->regmap, 0xd507, 0x40, 0x00);
+ if (ret)
+ goto err;
+
+ /* empty channel function */
+ ret = regmap_update_bits(state->regmap, 0x9bfe, 0x01, 0x00);
+ if (ret)
+ goto err;
+
+ /* empty DVB-T channel function */
+ ret = regmap_update_bits(state->regmap, 0x9bc2, 0x01, 0x00);
+ if (ret)
+ goto err;
+
+ /* transmission parameters */
+ auto_mode = false;
+ memset(buf, 0, 3);
+
+ switch (c->transmission_mode) {
+ case TRANSMISSION_MODE_AUTO:
+ auto_mode = true;
+ break;
+ case TRANSMISSION_MODE_2K:
+ break;
+ case TRANSMISSION_MODE_8K:
+ buf[0] |= (1 << 0);
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid transmission_mode\n");
+ auto_mode = true;
+ }
+
+ switch (c->guard_interval) {
+ case GUARD_INTERVAL_AUTO:
+ auto_mode = true;
+ break;
+ case GUARD_INTERVAL_1_32:
+ break;
+ case GUARD_INTERVAL_1_16:
+ buf[0] |= (1 << 2);
+ break;
+ case GUARD_INTERVAL_1_8:
+ buf[0] |= (2 << 2);
+ break;
+ case GUARD_INTERVAL_1_4:
+ buf[0] |= (3 << 2);
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid guard_interval\n");
+ auto_mode = true;
+ }
+
+ switch (c->hierarchy) {
+ case HIERARCHY_AUTO:
+ auto_mode = true;
+ break;
+ case HIERARCHY_NONE:
+ break;
+ case HIERARCHY_1:
+ buf[0] |= (1 << 4);
+ break;
+ case HIERARCHY_2:
+ buf[0] |= (2 << 4);
+ break;
+ case HIERARCHY_4:
+ buf[0] |= (3 << 4);
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid hierarchy\n");
+ auto_mode = true;
+ }
+
+ switch (c->modulation) {
+ case QAM_AUTO:
+ auto_mode = true;
+ break;
+ case QPSK:
+ break;
+ case QAM_16:
+ buf[1] |= (1 << 6);
+ break;
+ case QAM_64:
+ buf[1] |= (2 << 6);
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid modulation\n");
+ auto_mode = true;
+ }
+
+ /* Use HP. How and which case we can switch to LP? */
+ buf[1] |= (1 << 4);
+
+ switch (c->code_rate_HP) {
+ case FEC_AUTO:
+ auto_mode = true;
+ break;
+ case FEC_1_2:
+ break;
+ case FEC_2_3:
+ buf[2] |= (1 << 0);
+ break;
+ case FEC_3_4:
+ buf[2] |= (2 << 0);
+ break;
+ case FEC_5_6:
+ buf[2] |= (3 << 0);
+ break;
+ case FEC_7_8:
+ buf[2] |= (4 << 0);
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid code_rate_HP\n");
+ auto_mode = true;
+ }
+
+ switch (c->code_rate_LP) {
+ case FEC_AUTO:
+ auto_mode = true;
+ break;
+ case FEC_1_2:
+ break;
+ case FEC_2_3:
+ buf[2] |= (1 << 3);
+ break;
+ case FEC_3_4:
+ buf[2] |= (2 << 3);
+ break;
+ case FEC_5_6:
+ buf[2] |= (3 << 3);
+ break;
+ case FEC_7_8:
+ buf[2] |= (4 << 3);
+ break;
+ case FEC_NONE:
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid code_rate_LP\n");
+ auto_mode = true;
+ }
+
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ break;
+ case 7000000:
+ buf[1] |= (1 << 2);
+ break;
+ case 8000000:
+ buf[1] |= (2 << 2);
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid bandwidth_hz\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = regmap_bulk_write(state->regmap, 0xd3c0, buf, 3);
+ if (ret)
+ goto err;
+
+ if (auto_mode) {
+ /* clear easy mode flag */
+ ret = regmap_write(state->regmap, 0xaefd, 0x00);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "auto params\n");
+ } else {
+ /* set easy mode flag */
+ ret = regmap_write(state->regmap, 0xaefd, 0x01);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(state->regmap, 0xaefe, 0x00);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "manual params\n");
+ }
+
+ /* Reset FSM */
+ ret = regmap_write(state->regmap, 0xffff, 0x00);
+ if (ret)
+ goto err;
+
+ state->bandwidth_hz = c->bandwidth_hz;
+ state->set_frontend_jiffies = jiffies;
+ state->first_tune = false;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
+ int ret;
+ u8 buf[3];
+
+ dev_dbg(&client->dev, "\n");
+
+ ret = regmap_bulk_read(state->regmap, 0xd3c0, buf, 3);
+ if (ret)
+ goto err;
+
+ switch ((buf[1] >> 6) & 3) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = QAM_16;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ }
+
+ switch ((buf[0] >> 0) & 3) {
+ case 0:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ }
+
+ switch ((buf[0] >> 2) & 3) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[0] >> 4) & 7) {
+ case 0:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[2] >> 0) & 7) {
+ case 0:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[2] >> 3) & 7) {
+ case 0:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[1] >> 2) & 3) {
+ case 0:
+ c->bandwidth_hz = 6000000;
+ break;
+ case 1:
+ c->bandwidth_hz = 7000000;
+ break;
+ case 2:
+ c->bandwidth_hz = 8000000;
+ break;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, stmp1;
+ unsigned int utmp, utmp1, utmp2, utmp3, utmp4;
+ u8 buf[7];
+
+ dev_dbg(&client->dev, "\n");
+
+ /*
+ * Return status from the cache if it is younger than 2000ms with the
+ * exception of last tune is done during 4000ms.
+ */
+ if (time_is_after_jiffies(state->read_status_jiffies + msecs_to_jiffies(2000)) &&
+ time_is_before_jiffies(state->set_frontend_jiffies + msecs_to_jiffies(4000))) {
+ *status = state->fe_status;
+ } else {
+ /* MPEG2 lock */
+ ret = regmap_read(state->regmap, 0xd507, &utmp);
+ if (ret)
+ goto err;
+
+ if ((utmp >> 6) & 0x01) {
+ utmp1 = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ } else {
+ /* TPS lock */
+ ret = regmap_read(state->regmap, 0xd330, &utmp);
+ if (ret)
+ goto err;
+
+ if ((utmp >> 3) & 0x01)
+ utmp1 = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+ else
+ utmp1 = 0;
+ }
+
+ dev_dbg(&client->dev, "fe_status %02x\n", utmp1);
+
+ state->read_status_jiffies = jiffies;
+
+ state->fe_status = utmp1;
+ *status = utmp1;
+ }
+
+ /* Signal strength */
+ switch (state->strength_en) {
+ case 0:
+ /* Check if we support signal strength */
+ ret = regmap_read(state->regmap, 0x9bee, &utmp);
+ if (ret)
+ goto err;
+
+ if ((utmp >> 0) & 0x01) {
+ /* Read agc values for signal strength estimation */
+ ret = regmap_read(state->regmap, 0x9bbd, &utmp1);
+ if (ret)
+ goto err;
+ ret = regmap_read(state->regmap, 0x9bd0, &utmp2);
+ if (ret)
+ goto err;
+ ret = regmap_read(state->regmap, 0x9be2, &utmp3);
+ if (ret)
+ goto err;
+ ret = regmap_read(state->regmap, 0x9be4, &utmp4);
+ if (ret)
+ goto err;
+
+ state->rf_agc_50 = utmp1;
+ state->rf_agc_80 = utmp2;
+ state->if_agc_50 = utmp3;
+ state->if_agc_80 = utmp4;
+ dev_dbg(&client->dev,
+ "rf_agc_50 %u, rf_agc_80 %u, if_agc_50 %u, if_agc_80 %u\n",
+ utmp1, utmp2, utmp3, utmp4);
+
+ state->strength_en = 1;
+ } else {
+ /* Signal strength is not supported */
+ state->strength_en = 2;
+ break;
+ }
+ fallthrough;
+ case 1:
+ if (time_is_after_jiffies(state->strength_jiffies + msecs_to_jiffies(2000)))
+ break;
+
+ /* Read value */
+ ret = regmap_bulk_read(state->regmap, 0xd07c, buf, 2);
+ if (ret)
+ goto err;
+
+ /*
+ * Construct line equation from tuner dependent -80/-50 dBm agc
+ * limits and use it to map current agc value to dBm estimate
+ */
+ #define agc_gain (buf[0] + buf[1])
+ #define agc_gain_50dbm (state->rf_agc_50 + state->if_agc_50)
+ #define agc_gain_80dbm (state->rf_agc_80 + state->if_agc_80)
+ stmp1 = 30000 * (agc_gain - agc_gain_80dbm) /
+ (agc_gain_50dbm - agc_gain_80dbm) - 80000;
+
+ dev_dbg(&client->dev,
+ "strength %d, agc_gain %d, agc_gain_50dbm %d, agc_gain_80dbm %d\n",
+ stmp1, agc_gain, agc_gain_50dbm, agc_gain_80dbm);
+
+ state->strength_jiffies = jiffies;
+ /* Convert [-90, -30] dBm to [0x0000, 0xffff] for dvbv3 */
+ utmp1 = clamp(stmp1 + 90000, 0, 60000);
+ state->dvbv3_strength = div_u64((u64)utmp1 * 0xffff, 60000);
+
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->strength.stat[0].svalue = stmp1;
+ break;
+ default:
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ break;
+ }
+
+ /* CNR */
+ switch (state->fe_status & FE_HAS_VITERBI) {
+ case FE_HAS_VITERBI:
+ if (time_is_after_jiffies(state->cnr_jiffies + msecs_to_jiffies(2000)))
+ break;
+
+ /* Check if cnr ready */
+ ret = regmap_read(state->regmap, 0xd2e1, &utmp);
+ if (ret)
+ goto err;
+
+ if (!((utmp >> 3) & 0x01)) {
+ dev_dbg(&client->dev, "cnr not ready\n");
+ break;
+ }
+
+ /* Read value */
+ ret = regmap_bulk_read(state->regmap, 0xd2e3, buf, 3);
+ if (ret)
+ goto err;
+
+ utmp1 = buf[2] << 16 | buf[1] << 8 | buf[0] << 0;
+
+ /* Read current modulation */
+ ret = regmap_read(state->regmap, 0xd3c1, &utmp);
+ if (ret)
+ goto err;
+
+ switch ((utmp >> 6) & 3) {
+ case 0:
+ /*
+ * QPSK
+ * CNR[dB] 13 * -log10((1690000 - value) / value) + 2.6
+ * value [653799, 1689999], 2.6 / 13 = 3355443
+ */
+ utmp1 = clamp(utmp1, 653799U, 1689999U);
+ utmp1 = ((u64)(intlog10(utmp1)
+ - intlog10(1690000 - utmp1)
+ + 3355443) * 13 * 1000) >> 24;
+ break;
+ case 1:
+ /*
+ * QAM-16
+ * CNR[dB] 6 * log10((value - 370000) / (828000 - value)) + 15.7
+ * value [371105, 827999], 15.7 / 6 = 43900382
+ */
+ utmp1 = clamp(utmp1, 371105U, 827999U);
+ utmp1 = ((u64)(intlog10(utmp1 - 370000)
+ - intlog10(828000 - utmp1)
+ + 43900382) * 6 * 1000) >> 24;
+ break;
+ case 2:
+ /*
+ * QAM-64
+ * CNR[dB] 8 * log10((value - 193000) / (425000 - value)) + 23.8
+ * value [193246, 424999], 23.8 / 8 = 49912218
+ */
+ utmp1 = clamp(utmp1, 193246U, 424999U);
+ utmp1 = ((u64)(intlog10(utmp1 - 193000)
+ - intlog10(425000 - utmp1)
+ + 49912218) * 8 * 1000) >> 24;
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid modulation %u\n",
+ (utmp >> 6) & 3);
+ utmp1 = 0;
+ break;
+ }
+
+ dev_dbg(&client->dev, "cnr %u\n", utmp1);
+
+ state->cnr_jiffies = jiffies;
+ state->dvbv3_snr = utmp1 / 100;
+
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = utmp1;
+ break;
+ default:
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ break;
+ }
+
+ /* BER / PER */
+ switch (state->fe_status & FE_HAS_SYNC) {
+ case FE_HAS_SYNC:
+ if (time_is_after_jiffies(state->ber_ucb_jiffies + msecs_to_jiffies(2000)))
+ break;
+
+ /* Check if ber / ucb is ready */
+ ret = regmap_read(state->regmap, 0xd391, &utmp);
+ if (ret)
+ goto err;
+
+ if (!((utmp >> 4) & 0x01)) {
+ dev_dbg(&client->dev, "ber not ready\n");
+ break;
+ }
+
+ /* Read value */
+ ret = regmap_bulk_read(state->regmap, 0xd385, buf, 7);
+ if (ret)
+ goto err;
+
+ utmp1 = buf[4] << 16 | buf[3] << 8 | buf[2] << 0;
+ utmp2 = (buf[1] << 8 | buf[0] << 0) * 204 * 8;
+ utmp3 = buf[6] << 8 | buf[5] << 0;
+ utmp4 = buf[1] << 8 | buf[0] << 0;
+
+ /* Use 10000 TS packets for measure */
+ if (utmp4 != 10000) {
+ buf[0] = (10000 >> 0) & 0xff;
+ buf[1] = (10000 >> 8) & 0xff;
+ ret = regmap_bulk_write(state->regmap, 0xd385, buf, 2);
+ if (ret)
+ goto err;
+ }
+
+ /* Reset ber / ucb counter */
+ ret = regmap_update_bits(state->regmap, 0xd391, 0x20, 0x20);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "post_bit_error %u, post_bit_count %u\n",
+ utmp1, utmp2);
+ dev_dbg(&client->dev, "block_error %u, block_count %u\n",
+ utmp3, utmp4);
+
+ state->ber_ucb_jiffies = jiffies;
+ state->dvbv3_ber = utmp1;
+ state->dvbv3_ucblocks += utmp3;
+
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += utmp1;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue += utmp2;
+
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue += utmp3;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue += utmp4;
+ break;
+ default:
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ break;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+
+ *snr = state->dvbv3_snr;
+
+ return 0;
+}
+
+static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+
+ *strength = state->dvbv3_strength;
+
+ return 0;
+}
+
+static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+
+ *ber = state->dvbv3_ber;
+
+ return 0;
+}
+
+static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+
+ *ucblocks = state->dvbv3_ucblocks;
+
+ return 0;
+}
+
+static int af9013_init(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
+ int ret, i, len;
+ unsigned int utmp;
+ u8 buf[3];
+ const struct af9013_reg_mask_val *tab;
+
+ dev_dbg(&client->dev, "\n");
+
+ /* ADC on */
+ ret = regmap_update_bits(state->regmap, 0xd73a, 0x08, 0x00);
+ if (ret)
+ goto err;
+
+ /* Clear reset */
+ ret = regmap_update_bits(state->regmap, 0xd417, 0x02, 0x00);
+ if (ret)
+ goto err;
+
+ /* Disable reset */
+ ret = regmap_update_bits(state->regmap, 0xd417, 0x10, 0x00);
+ if (ret)
+ goto err;
+
+ /* write API version to firmware */
+ ret = regmap_bulk_write(state->regmap, 0x9bf2, state->api_version, 4);
+ if (ret)
+ goto err;
+
+ /* program ADC control */
+ switch (state->clk) {
+ case 28800000: /* 28.800 MHz */
+ utmp = 0;
+ break;
+ case 20480000: /* 20.480 MHz */
+ utmp = 1;
+ break;
+ case 28000000: /* 28.000 MHz */
+ utmp = 2;
+ break;
+ case 25000000: /* 25.000 MHz */
+ utmp = 3;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = regmap_update_bits(state->regmap, 0x9bd2, 0x0f, utmp);
+ if (ret)
+ goto err;
+
+ utmp = div_u64((u64)state->clk * 0x80000, 1000000);
+ buf[0] = (utmp >> 0) & 0xff;
+ buf[1] = (utmp >> 8) & 0xff;
+ buf[2] = (utmp >> 16) & 0xff;
+ ret = regmap_bulk_write(state->regmap, 0xd180, buf, 3);
+ if (ret)
+ goto err;
+
+ /* Demod core settings */
+ dev_dbg(&client->dev, "load demod core settings\n");
+ len = ARRAY_SIZE(demod_init_tab);
+ tab = demod_init_tab;
+ for (i = 0; i < len; i++) {
+ ret = regmap_update_bits(state->regmap, tab[i].reg, tab[i].mask,
+ tab[i].val);
+ if (ret)
+ goto err;
+ }
+
+ /* Demod tuner specific settings */
+ dev_dbg(&client->dev, "load tuner specific settings\n");
+ switch (state->tuner) {
+ case AF9013_TUNER_MXL5003D:
+ len = ARRAY_SIZE(tuner_init_tab_mxl5003d);
+ tab = tuner_init_tab_mxl5003d;
+ break;
+ case AF9013_TUNER_MXL5005D:
+ case AF9013_TUNER_MXL5005R:
+ case AF9013_TUNER_MXL5007T:
+ len = ARRAY_SIZE(tuner_init_tab_mxl5005);
+ tab = tuner_init_tab_mxl5005;
+ break;
+ case AF9013_TUNER_ENV77H11D5:
+ len = ARRAY_SIZE(tuner_init_tab_env77h11d5);
+ tab = tuner_init_tab_env77h11d5;
+ break;
+ case AF9013_TUNER_MT2060:
+ len = ARRAY_SIZE(tuner_init_tab_mt2060);
+ tab = tuner_init_tab_mt2060;
+ break;
+ case AF9013_TUNER_MC44S803:
+ len = ARRAY_SIZE(tuner_init_tab_mc44s803);
+ tab = tuner_init_tab_mc44s803;
+ break;
+ case AF9013_TUNER_QT1010:
+ case AF9013_TUNER_QT1010A:
+ len = ARRAY_SIZE(tuner_init_tab_qt1010);
+ tab = tuner_init_tab_qt1010;
+ break;
+ case AF9013_TUNER_MT2060_2:
+ len = ARRAY_SIZE(tuner_init_tab_mt2060_2);
+ tab = tuner_init_tab_mt2060_2;
+ break;
+ case AF9013_TUNER_TDA18271:
+ case AF9013_TUNER_TDA18218:
+ len = ARRAY_SIZE(tuner_init_tab_tda18271);
+ tab = tuner_init_tab_tda18271;
+ break;
+ case AF9013_TUNER_UNKNOWN:
+ default:
+ len = ARRAY_SIZE(tuner_init_tab_unknown);
+ tab = tuner_init_tab_unknown;
+ break;
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = regmap_update_bits(state->regmap, tab[i].reg, tab[i].mask,
+ tab[i].val);
+ if (ret)
+ goto err;
+ }
+
+ /* TS interface */
+ if (state->ts_output_pin == 7)
+ utmp = 1 << 3 | state->ts_mode << 1;
+ else
+ utmp = 0 << 3 | state->ts_mode << 1;
+ ret = regmap_update_bits(state->regmap, 0xd500, 0x0e, utmp);
+ if (ret)
+ goto err;
+
+ /* enable lock led */
+ ret = regmap_update_bits(state->regmap, 0xd730, 0x01, 0x01);
+ if (ret)
+ goto err;
+
+ state->first_tune = true;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_sleep(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
+ int ret;
+ unsigned int utmp;
+
+ dev_dbg(&client->dev, "\n");
+
+ /* disable lock led */
+ ret = regmap_update_bits(state->regmap, 0xd730, 0x01, 0x00);
+ if (ret)
+ goto err;
+
+ /* Enable reset */
+ ret = regmap_update_bits(state->regmap, 0xd417, 0x10, 0x10);
+ if (ret)
+ goto err;
+
+ /* Start reset execution */
+ ret = regmap_write(state->regmap, 0xaeff, 0x01);
+ if (ret)
+ goto err;
+
+ /* Wait reset performs */
+ ret = regmap_read_poll_timeout(state->regmap, 0xd417, utmp,
+ (utmp >> 1) & 0x01, 5000, 1000000);
+ if (ret)
+ goto err;
+
+ if (!((utmp >> 1) & 0x01)) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ /* ADC off */
+ ret = regmap_update_bits(state->regmap, 0xd73a, 0x08, 0x08);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static const struct dvb_frontend_ops af9013_ops;
+
+static int af9013_download_firmware(struct af9013_state *state)
+{
+ struct i2c_client *client = state->client;
+ int ret, i, len, rem;
+ unsigned int utmp;
+ u8 buf[4];
+ u16 checksum = 0;
+ const struct firmware *firmware;
+ const char *name = AF9013_FIRMWARE;
+
+ dev_dbg(&client->dev, "\n");
+
+ /* Check whether firmware is already running */
+ ret = regmap_read(state->regmap, 0x98be, &utmp);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "firmware status %02x\n", utmp);
+
+ if (utmp == 0x0c)
+ return 0;
+
+ dev_info(&client->dev, "found a '%s' in cold state, will try to load a firmware\n",
+ af9013_ops.info.name);
+
+ /* Request the firmware, will block and timeout */
+ ret = request_firmware(&firmware, name, &client->dev);
+ if (ret) {
+ dev_info(&client->dev, "firmware file '%s' not found %d\n",
+ name, ret);
+ goto err;
+ }
+
+ dev_info(&client->dev, "downloading firmware from file '%s'\n",
+ name);
+
+ /* Write firmware checksum & size */
+ for (i = 0; i < firmware->size; i++)
+ checksum += firmware->data[i];
+
+ buf[0] = (checksum >> 8) & 0xff;
+ buf[1] = (checksum >> 0) & 0xff;
+ buf[2] = (firmware->size >> 8) & 0xff;
+ buf[3] = (firmware->size >> 0) & 0xff;
+ ret = regmap_bulk_write(state->regmap, 0x50fc, buf, 4);
+ if (ret)
+ goto err_release_firmware;
+
+ /* Download firmware */
+ #define LEN_MAX 16
+ for (rem = firmware->size; rem > 0; rem -= LEN_MAX) {
+ len = min(LEN_MAX, rem);
+ ret = regmap_bulk_write(state->regmap,
+ 0x5100 + firmware->size - rem,
+ &firmware->data[firmware->size - rem],
+ len);
+ if (ret) {
+ dev_err(&client->dev, "firmware download failed %d\n",
+ ret);
+ goto err_release_firmware;
+ }
+ }
+
+ release_firmware(firmware);
+
+ /* Boot firmware */
+ ret = regmap_write(state->regmap, 0xe205, 0x01);
+ if (ret)
+ goto err;
+
+ /* Check firmware status. 0c=OK, 04=fail */
+ ret = regmap_read_poll_timeout(state->regmap, 0x98be, utmp,
+ (utmp == 0x0c || utmp == 0x04),
+ 5000, 1000000);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "firmware status %02x\n", utmp);
+
+ if (utmp == 0x04) {
+ ret = -ENODEV;
+ dev_err(&client->dev, "firmware did not run\n");
+ goto err;
+ } else if (utmp != 0x0c) {
+ ret = -ENODEV;
+ dev_err(&client->dev, "firmware boot timeout\n");
+ goto err;
+ }
+
+ dev_info(&client->dev, "found a '%s' in warm state\n",
+ af9013_ops.info.name);
+
+ return 0;
+err_release_firmware:
+ release_firmware(firmware);
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static const struct dvb_frontend_ops af9013_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Afatech AF9013",
+ .frequency_min_hz = 174 * MHz,
+ .frequency_max_hz = 862 * MHz,
+ .frequency_stepsize_hz = 250 * kHz,
+ .caps = FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .init = af9013_init,
+ .sleep = af9013_sleep,
+
+ .get_tune_settings = af9013_get_tune_settings,
+ .set_frontend = af9013_set_frontend,
+ .get_frontend = af9013_get_frontend,
+
+ .read_status = af9013_read_status,
+ .read_snr = af9013_read_snr,
+ .read_signal_strength = af9013_read_signal_strength,
+ .read_ber = af9013_read_ber,
+ .read_ucblocks = af9013_read_ucblocks,
+};
+
+static int af9013_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
+ int ret;
+
+ dev_dbg(&client->dev, "onoff %d\n", onoff);
+
+ ret = regmap_update_bits(state->regmap, 0xd503, 0x01, onoff);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid,
+ int onoff)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
+ int ret;
+ u8 buf[2];
+
+ dev_dbg(&client->dev, "index %d, pid %04x, onoff %d\n",
+ index, pid, onoff);
+
+ if (pid > 0x1fff) {
+ /* 0x2000 is kernel virtual pid for whole ts (all pids) */
+ ret = 0;
+ goto err;
+ }
+
+ buf[0] = (pid >> 0) & 0xff;
+ buf[1] = (pid >> 8) & 0xff;
+ ret = regmap_bulk_write(state->regmap, 0xd505, buf, 2);
+ if (ret)
+ goto err;
+ ret = regmap_write(state->regmap, 0xd504, onoff << 5 | index << 0);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static struct dvb_frontend *af9013_get_dvb_frontend(struct i2c_client *client)
+{
+ struct af9013_state *state = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ return &state->fe;
+}
+
+static struct i2c_adapter *af9013_get_i2c_adapter(struct i2c_client *client)
+{
+ struct af9013_state *state = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ return state->muxc->adapter[0];
+}
+
+/*
+ * XXX: Hackish solution. We use virtual register, reg bit 16, to carry info
+ * about i2c adapter locking. Own locking is needed because i2c mux call has
+ * already locked i2c adapter.
+ */
+static int af9013_select(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct af9013_state *state = i2c_mux_priv(muxc);
+ struct i2c_client *client = state->client;
+ int ret;
+
+ dev_dbg(&client->dev, "\n");
+
+ if (state->ts_mode == AF9013_TS_MODE_USB)
+ ret = regmap_update_bits(state->regmap, 0x1d417, 0x08, 0x08);
+ else
+ ret = regmap_update_bits(state->regmap, 0x1d607, 0x04, 0x04);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_deselect(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct af9013_state *state = i2c_mux_priv(muxc);
+ struct i2c_client *client = state->client;
+ int ret;
+
+ dev_dbg(&client->dev, "\n");
+
+ if (state->ts_mode == AF9013_TS_MODE_USB)
+ ret = regmap_update_bits(state->regmap, 0x1d417, 0x08, 0x00);
+ else
+ ret = regmap_update_bits(state->regmap, 0x1d607, 0x04, 0x00);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+/* Own I2C access routines needed for regmap as chip uses extra command byte */
+static int af9013_wregs(struct i2c_client *client, u8 cmd, u16 reg,
+ const u8 *val, int len, u8 lock)
+{
+ int ret;
+ u8 buf[21];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 3 + len,
+ .buf = buf,
+ }
+ };
+
+ if (3 + len > sizeof(buf)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ buf[0] = (reg >> 8) & 0xff;
+ buf[1] = (reg >> 0) & 0xff;
+ buf[2] = cmd;
+ memcpy(&buf[3], val, len);
+
+ if (lock)
+ i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
+ ret = __i2c_transfer(client->adapter, msg, 1);
+ if (lock)
+ i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
+ if (ret < 0) {
+ goto err;
+ } else if (ret != 1) {
+ ret = -EREMOTEIO;
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_rregs(struct i2c_client *client, u8 cmd, u16 reg,
+ u8 *val, int len, u8 lock)
+{
+ int ret;
+ u8 buf[3];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 3,
+ .buf = buf,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ buf[0] = (reg >> 8) & 0xff;
+ buf[1] = (reg >> 0) & 0xff;
+ buf[2] = cmd;
+
+ if (lock)
+ i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
+ ret = __i2c_transfer(client->adapter, msg, 2);
+ if (lock)
+ i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
+ if (ret < 0) {
+ goto err;
+ } else if (ret != 2) {
+ ret = -EREMOTEIO;
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_regmap_write(void *context, const void *data, size_t count)
+{
+ struct i2c_client *client = context;
+ struct af9013_state *state = i2c_get_clientdata(client);
+ int ret, i;
+ u8 cmd;
+ u8 lock = !((u8 *)data)[0];
+ u16 reg = ((u8 *)data)[1] << 8 | ((u8 *)data)[2] << 0;
+ u8 *val = &((u8 *)data)[3];
+ const unsigned int len = count - 3;
+
+ if (state->ts_mode == AF9013_TS_MODE_USB && (reg & 0xff00) != 0xae00) {
+ cmd = 0 << 7|0 << 6|(len - 1) << 2|1 << 1|1 << 0;
+ ret = af9013_wregs(client, cmd, reg, val, len, lock);
+ if (ret)
+ goto err;
+ } else if (reg >= 0x5100 && reg < 0x8fff) {
+ /* Firmware download */
+ cmd = 1 << 7|1 << 6|(len - 1) << 2|1 << 1|1 << 0;
+ ret = af9013_wregs(client, cmd, reg, val, len, lock);
+ if (ret)
+ goto err;
+ } else {
+ cmd = 0 << 7|0 << 6|(1 - 1) << 2|1 << 1|1 << 0;
+ for (i = 0; i < len; i++) {
+ ret = af9013_wregs(client, cmd, reg + i, val + i, 1,
+ lock);
+ if (ret)
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_regmap_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf, size_t val_size)
+{
+ struct i2c_client *client = context;
+ struct af9013_state *state = i2c_get_clientdata(client);
+ int ret, i;
+ u8 cmd;
+ u8 lock = !((u8 *)reg_buf)[0];
+ u16 reg = ((u8 *)reg_buf)[1] << 8 | ((u8 *)reg_buf)[2] << 0;
+ u8 *val = &((u8 *)val_buf)[0];
+ const unsigned int len = val_size;
+
+ if (state->ts_mode == AF9013_TS_MODE_USB && (reg & 0xff00) != 0xae00) {
+ cmd = 0 << 7|0 << 6|(len - 1) << 2|1 << 1|0 << 0;
+ ret = af9013_rregs(client, cmd, reg, val_buf, len, lock);
+ if (ret)
+ goto err;
+ } else {
+ cmd = 0 << 7|0 << 6|(1 - 1) << 2|1 << 1|0 << 0;
+ for (i = 0; i < len; i++) {
+ ret = af9013_rregs(client, cmd, reg + i, val + i, 1,
+ lock);
+ if (ret)
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_probe(struct i2c_client *client)
+{
+ struct af9013_state *state;
+ struct af9013_platform_data *pdata = client->dev.platform_data;
+ struct dtv_frontend_properties *c;
+ int ret, i;
+ u8 firmware_version[4];
+ static const struct regmap_bus regmap_bus = {
+ .read = af9013_regmap_read,
+ .write = af9013_regmap_write,
+ };
+ static const struct regmap_config regmap_config = {
+ /* Actual reg is 16 bits, see i2c adapter lock */
+ .reg_bits = 24,
+ .val_bits = 8,
+ };
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev_dbg(&client->dev, "\n");
+
+ /* Setup the state */
+ state->client = client;
+ i2c_set_clientdata(client, state);
+ state->clk = pdata->clk;
+ state->tuner = pdata->tuner;
+ state->if_frequency = pdata->if_frequency;
+ state->ts_mode = pdata->ts_mode;
+ state->ts_output_pin = pdata->ts_output_pin;
+ state->spec_inv = pdata->spec_inv;
+ memcpy(&state->api_version, pdata->api_version, sizeof(state->api_version));
+ memcpy(&state->gpio, pdata->gpio, sizeof(state->gpio));
+ state->regmap = regmap_init(&client->dev, &regmap_bus, client,
+ &regmap_config);
+ if (IS_ERR(state->regmap)) {
+ ret = PTR_ERR(state->regmap);
+ goto err_kfree;
+ }
+ /* Create mux i2c adapter */
+ state->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0,
+ af9013_select, af9013_deselect);
+ if (!state->muxc) {
+ ret = -ENOMEM;
+ goto err_regmap_exit;
+ }
+ state->muxc->priv = state;
+ ret = i2c_mux_add_adapter(state->muxc, 0, 0);
+ if (ret)
+ goto err_regmap_exit;
+
+ /* Download firmware */
+ if (state->ts_mode != AF9013_TS_MODE_USB) {
+ ret = af9013_download_firmware(state);
+ if (ret)
+ goto err_i2c_mux_del_adapters;
+ }
+
+ /* Firmware version */
+ ret = regmap_bulk_read(state->regmap, 0x5103, firmware_version,
+ sizeof(firmware_version));
+ if (ret)
+ goto err_i2c_mux_del_adapters;
+
+ /* Set GPIOs */
+ for (i = 0; i < sizeof(state->gpio); i++) {
+ ret = af9013_set_gpio(state, i, state->gpio[i]);
+ if (ret)
+ goto err_i2c_mux_del_adapters;
+ }
+
+ /* Create dvb frontend */
+ memcpy(&state->fe.ops, &af9013_ops, sizeof(state->fe.ops));
+ state->fe.demodulator_priv = state;
+
+ /* Setup callbacks */
+ pdata->get_dvb_frontend = af9013_get_dvb_frontend;
+ pdata->get_i2c_adapter = af9013_get_i2c_adapter;
+ pdata->pid_filter = af9013_pid_filter;
+ pdata->pid_filter_ctrl = af9013_pid_filter_ctrl;
+
+ /* Init stats to indicate which stats are supported */
+ c = &state->fe.dtv_property_cache;
+ c->strength.len = 1;
+ c->cnr.len = 1;
+ c->post_bit_error.len = 1;
+ c->post_bit_count.len = 1;
+ c->block_error.len = 1;
+ c->block_count.len = 1;
+
+ dev_info(&client->dev, "Afatech AF9013 successfully attached\n");
+ dev_info(&client->dev, "firmware version: %d.%d.%d.%d\n",
+ firmware_version[0], firmware_version[1],
+ firmware_version[2], firmware_version[3]);
+ return 0;
+err_i2c_mux_del_adapters:
+ i2c_mux_del_adapters(state->muxc);
+err_regmap_exit:
+ regmap_exit(state->regmap);
+err_kfree:
+ kfree(state);
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static void af9013_remove(struct i2c_client *client)
+{
+ struct af9013_state *state = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ i2c_mux_del_adapters(state->muxc);
+
+ regmap_exit(state->regmap);
+
+ kfree(state);
+}
+
+static const struct i2c_device_id af9013_id_table[] = {
+ { "af9013" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, af9013_id_table);
+
+static struct i2c_driver af9013_driver = {
+ .driver = {
+ .name = "af9013",
+ .suppress_bind_attrs = true,
+ },
+ .probe = af9013_probe,
+ .remove = af9013_remove,
+ .id_table = af9013_id_table,
+};
+
+module_i2c_driver(af9013_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Afatech AF9013 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(AF9013_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/af9013.h b/drivers/media/dvb-frontends/af9013.h
new file mode 100644
index 000000000000..bbf1ccee7255
--- /dev/null
+++ b/drivers/media/dvb-frontends/af9013.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Afatech AF9013 demodulator driver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ */
+
+#ifndef AF9013_H
+#define AF9013_H
+
+#include <linux/dvb/frontend.h>
+
+/*
+ * I2C address: 0x1c, 0x1d
+ */
+
+/**
+ * struct af9013_platform_data - Platform data for the af9013 driver
+ * @clk: Clock frequency.
+ * @tuner: Used tuner model.
+ * @if_frequency: IF frequency.
+ * @ts_mode: TS mode.
+ * @ts_output_pin: TS output pin.
+ * @spec_inv: Input spectrum inverted.
+ * @api_version: Firmware API version.
+ * @gpio: GPIOs.
+ * @get_dvb_frontend: Get DVB frontend callback.
+ * @get_i2c_adapter: Get I2C adapter.
+ * @pid_filter_ctrl: Control PID filter.
+ * @pid_filter: Set PID to PID filter.
+ */
+struct af9013_platform_data {
+ /*
+ * 20480000, 25000000, 28000000, 28800000
+ */
+ u32 clk;
+#define AF9013_TUNER_MXL5003D 3 /* MaxLinear */
+#define AF9013_TUNER_MXL5005D 13 /* MaxLinear */
+#define AF9013_TUNER_MXL5005R 30 /* MaxLinear */
+#define AF9013_TUNER_ENV77H11D5 129 /* Panasonic */
+#define AF9013_TUNER_MT2060 130 /* Microtune */
+#define AF9013_TUNER_MC44S803 133 /* Freescale */
+#define AF9013_TUNER_QT1010 134 /* Quantek */
+#define AF9013_TUNER_UNKNOWN 140 /* for can tuners ? */
+#define AF9013_TUNER_MT2060_2 147 /* Microtune */
+#define AF9013_TUNER_TDA18271 156 /* NXP */
+#define AF9013_TUNER_QT1010A 162 /* Quantek */
+#define AF9013_TUNER_MXL5007T 177 /* MaxLinear */
+#define AF9013_TUNER_TDA18218 179 /* NXP */
+ u8 tuner;
+ u32 if_frequency;
+#define AF9013_TS_MODE_USB 0
+#define AF9013_TS_MODE_PARALLEL 1
+#define AF9013_TS_MODE_SERIAL 2
+ u8 ts_mode;
+ u8 ts_output_pin;
+ bool spec_inv;
+ u8 api_version[4];
+#define AF9013_GPIO_ON (1 << 0)
+#define AF9013_GPIO_EN (1 << 1)
+#define AF9013_GPIO_O (1 << 2)
+#define AF9013_GPIO_I (1 << 3)
+#define AF9013_GPIO_LO (AF9013_GPIO_ON|AF9013_GPIO_EN)
+#define AF9013_GPIO_HI (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
+#define AF9013_GPIO_TUNER_ON (AF9013_GPIO_ON|AF9013_GPIO_EN)
+#define AF9013_GPIO_TUNER_OFF (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
+ u8 gpio[4];
+
+ struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
+ struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *);
+ int (*pid_filter_ctrl)(struct dvb_frontend *, int);
+ int (*pid_filter)(struct dvb_frontend *, u8, u16, int);
+};
+
+/*
+ * AF9013/5 GPIOs (mostly guessed)
+ * demod#1-gpio#0 - set demod#2 i2c-addr for dual devices
+ * demod#1-gpio#1 - xtal setting (?)
+ * demod#1-gpio#3 - tuner#1
+ * demod#2-gpio#0 - tuner#2
+ * demod#2-gpio#1 - xtal setting (?)
+ */
+
+#endif /* AF9013_H */
diff --git a/drivers/media/dvb-frontends/af9013_priv.h b/drivers/media/dvb-frontends/af9013_priv.h
new file mode 100644
index 000000000000..bba7a9693a23
--- /dev/null
+++ b/drivers/media/dvb-frontends/af9013_priv.h
@@ -0,0 +1,851 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Afatech AF9013 demodulator driver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ */
+
+#ifndef AF9013_PRIV_H
+#define AF9013_PRIV_H
+
+#include <media/dvb_frontend.h>
+#include <linux/int_log.h>
+#include "af9013.h"
+#include <linux/firmware.h>
+#include <linux/i2c-mux.h>
+#include <linux/math64.h>
+#include <linux/regmap.h>
+
+#define AF9013_FIRMWARE "dvb-fe-af9013.fw"
+
+struct af9013_reg_mask_val {
+ u16 reg;
+ u8 mask;
+ u8 val;
+};
+
+struct af9013_coeff {
+ u32 clock;
+ u32 bandwidth_hz;
+ u8 val[24];
+};
+
+/* pre-calculated coeff lookup table */
+static const struct af9013_coeff coeff_lut[] = {
+ /* 28.800 MHz */
+ { 28800000, 8000000, { 0x02, 0x8a, 0x28, 0xa3, 0x05, 0x14,
+ 0x51, 0x11, 0x00, 0xa2, 0x8f, 0x3d, 0x00, 0xa2, 0x8a,
+ 0x29, 0x00, 0xa2, 0x85, 0x14, 0x01, 0x45, 0x14, 0x14 } },
+ { 28800000, 7000000, { 0x02, 0x38, 0xe3, 0x8e, 0x04, 0x71,
+ 0xc7, 0x07, 0x00, 0x8e, 0x3d, 0x55, 0x00, 0x8e, 0x38,
+ 0xe4, 0x00, 0x8e, 0x34, 0x72, 0x01, 0x1c, 0x71, 0x32 } },
+ { 28800000, 6000000, { 0x01, 0xe7, 0x9e, 0x7a, 0x03, 0xcf,
+ 0x3c, 0x3d, 0x00, 0x79, 0xeb, 0x6e, 0x00, 0x79, 0xe7,
+ 0x9e, 0x00, 0x79, 0xe3, 0xcf, 0x00, 0xf3, 0xcf, 0x0f } },
+ /* 20.480 MHz */
+ { 20480000, 8000000, { 0x03, 0x92, 0x49, 0x26, 0x07, 0x24,
+ 0x92, 0x13, 0x00, 0xe4, 0x99, 0x6e, 0x00, 0xe4, 0x92,
+ 0x49, 0x00, 0xe4, 0x8b, 0x25, 0x01, 0xc9, 0x24, 0x25 } },
+ { 20480000, 7000000, { 0x03, 0x20, 0x00, 0x01, 0x06, 0x40,
+ 0x00, 0x00, 0x00, 0xc8, 0x06, 0x40, 0x00, 0xc8, 0x00,
+ 0x00, 0x00, 0xc7, 0xf9, 0xc0, 0x01, 0x90, 0x00, 0x00 } },
+ { 20480000, 6000000, { 0x02, 0xad, 0xb6, 0xdc, 0x05, 0x5b,
+ 0x6d, 0x2e, 0x00, 0xab, 0x73, 0x13, 0x00, 0xab, 0x6d,
+ 0xb7, 0x00, 0xab, 0x68, 0x5c, 0x01, 0x56, 0xdb, 0x1c } },
+ /* 28.000 MHz */
+ { 28000000, 8000000, { 0x02, 0x9c, 0xbc, 0x15, 0x05, 0x39,
+ 0x78, 0x0a, 0x00, 0xa7, 0x34, 0x3f, 0x00, 0xa7, 0x2f,
+ 0x05, 0x00, 0xa7, 0x29, 0xcc, 0x01, 0x4e, 0x5e, 0x03 } },
+ { 28000000, 7000000, { 0x02, 0x49, 0x24, 0x92, 0x04, 0x92,
+ 0x49, 0x09, 0x00, 0x92, 0x4d, 0xb7, 0x00, 0x92, 0x49,
+ 0x25, 0x00, 0x92, 0x44, 0x92, 0x01, 0x24, 0x92, 0x12 } },
+ { 28000000, 6000000, { 0x01, 0xf5, 0x8d, 0x10, 0x03, 0xeb,
+ 0x1a, 0x08, 0x00, 0x7d, 0x67, 0x2f, 0x00, 0x7d, 0x63,
+ 0x44, 0x00, 0x7d, 0x5f, 0x59, 0x00, 0xfa, 0xc6, 0x22 } },
+ /* 25.000 MHz */
+ { 25000000, 8000000, { 0x02, 0xec, 0xfb, 0x9d, 0x05, 0xd9,
+ 0xf7, 0x0e, 0x00, 0xbb, 0x44, 0xc1, 0x00, 0xbb, 0x3e,
+ 0xe7, 0x00, 0xbb, 0x39, 0x0d, 0x01, 0x76, 0x7d, 0x34 } },
+ { 25000000, 7000000, { 0x02, 0x8f, 0x5c, 0x29, 0x05, 0x1e,
+ 0xb8, 0x14, 0x00, 0xa3, 0xdc, 0x29, 0x00, 0xa3, 0xd7,
+ 0x0a, 0x00, 0xa3, 0xd1, 0xec, 0x01, 0x47, 0xae, 0x05 } },
+ { 25000000, 6000000, { 0x02, 0x31, 0xbc, 0xb5, 0x04, 0x63,
+ 0x79, 0x1b, 0x00, 0x8c, 0x73, 0x91, 0x00, 0x8c, 0x6f,
+ 0x2d, 0x00, 0x8c, 0x6a, 0xca, 0x01, 0x18, 0xde, 0x17 } },
+};
+
+/*
+ * Afatech AF9013 demod init
+ */
+static const struct af9013_reg_mask_val demod_init_tab[] = {
+ {0xd73a, 0xff, 0xa1},
+ {0xd73b, 0xff, 0x1f},
+ {0xd73c, 0xf0, 0xa0},
+ {0xd732, 0x08, 0x00},
+ {0xd731, 0x30, 0x30},
+ {0xd73d, 0x80, 0x80},
+ {0xd740, 0x01, 0x00},
+ {0xd740, 0x02, 0x00},
+ {0xd740, 0x04, 0x00},
+ {0xd740, 0x08, 0x08},
+ {0xd3c1, 0x10, 0x10},
+ {0x9124, 0xff, 0x58},
+ {0x9125, 0x03, 0x02},
+ {0xd3a2, 0xff, 0x00},
+ {0xd3a3, 0xff, 0x04},
+ {0xd305, 0xff, 0x32},
+ {0xd306, 0xff, 0x10},
+ {0xd304, 0xff, 0x04},
+ {0x9112, 0x01, 0x01},
+ {0x911d, 0x01, 0x01},
+ {0x911a, 0x01, 0x01},
+ {0x911b, 0x01, 0x01},
+ {0x9bce, 0x0f, 0x02},
+ {0x9116, 0x01, 0x01},
+ {0x9122, 0xff, 0xd0},
+ {0xd2e0, 0xff, 0xd0},
+ {0xd2e9, 0x0f, 0x0d},
+ {0xd38c, 0xff, 0xfc},
+ {0xd38d, 0xff, 0x00},
+ {0xd38e, 0xff, 0x7e},
+ {0xd38f, 0xff, 0x00},
+ {0xd390, 0xff, 0x2f},
+ {0xd145, 0x10, 0x10},
+ {0xd1a9, 0x10, 0x10},
+ {0xd158, 0xe0, 0x20},
+ {0xd159, 0x3f, 0x06},
+ {0xd167, 0xff, 0x00},
+ {0xd168, 0x0f, 0x07},
+ {0xd1c3, 0xe0, 0x00},
+ {0xd1c4, 0x3f, 0x00},
+ {0xd1c5, 0x7f, 0x10},
+ {0xd1c6, 0x07, 0x02},
+ {0xd080, 0x7c, 0x0c},
+ {0xd081, 0xf0, 0x90},
+ {0xd098, 0xf0, 0xf0},
+ {0xd098, 0x0f, 0x03},
+ {0xdbc0, 0x10, 0x10},
+ {0xdbc7, 0xff, 0x08},
+ {0xdbc8, 0xf0, 0x00},
+ {0xdbc9, 0x1f, 0x01},
+ {0xd280, 0xff, 0xe0},
+ {0xd281, 0xff, 0xff},
+ {0xd282, 0xff, 0xff},
+ {0xd283, 0xff, 0xc3},
+ {0xd284, 0xff, 0xff},
+ {0xd285, 0x0f, 0x01},
+ {0xd0f0, 0x7f, 0x1a},
+ {0xd0f1, 0x10, 0x10},
+ {0xd0f2, 0xff, 0x0c},
+ {0xd101, 0xe0, 0xc0},
+ {0xd103, 0x0f, 0x08},
+ {0xd0f8, 0x7f, 0x20},
+ {0xd111, 0x20, 0x00},
+ {0xd111, 0x40, 0x00},
+ {0x910b, 0xff, 0x0a},
+ {0x9115, 0xff, 0x02},
+ {0x910c, 0xff, 0x02},
+ {0x910d, 0xff, 0x08},
+ {0x910e, 0xff, 0x0a},
+ {0x9bf6, 0xff, 0x06},
+ {0x9bf8, 0xff, 0x02},
+ {0x9bf7, 0xff, 0x05},
+ {0x9bf9, 0xff, 0x0f},
+ {0x9bfc, 0xff, 0x13},
+ {0x9bd3, 0xff, 0xff},
+ {0x9bbe, 0x01, 0x01},
+ {0x9bcc, 0x01, 0x01},
+};
+
+/*
+ * Panasonic ENV77H11D5 tuner init
+ * AF9013_TUNER_ENV77H11D5 0x81
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_env77h11d5[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x03},
+ {0x9bbe, 0xff, 0x01},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x00},
+ {0x9be3, 0xff, 0x00},
+ {0xd015, 0xff, 0x50},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x46},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0xdf},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x44},
+ {0xd007, 0x03, 0x01},
+ {0xd00c, 0xff, 0xeb},
+ {0xd00d, 0x03, 0x02},
+ {0xd00a, 0xff, 0xf4},
+ {0xd00b, 0x03, 0x01},
+ {0x9bba, 0xff, 0xf9},
+ {0x9bc3, 0xff, 0xdf},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0xeb},
+ {0x9bc6, 0xff, 0x02},
+ {0x9bc9, 0xff, 0x52},
+ {0xd011, 0xff, 0x3c},
+ {0xd012, 0x03, 0x01},
+ {0xd013, 0xff, 0xf7},
+ {0xd014, 0x03, 0x02},
+ {0xd040, 0xff, 0x0b},
+ {0xd041, 0x03, 0x02},
+ {0xd042, 0xff, 0x4d},
+ {0xd043, 0x03, 0x00},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0x01, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
+};
+
+/*
+ * Microtune MT2060 tuner init
+ * AF9013_TUNER_MT2060 0x82
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mt2060[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x07},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x00},
+ {0x9be3, 0xff, 0x00},
+ {0x9bbe, 0x01, 0x00},
+ {0x9bcc, 0x01, 0x00},
+ {0x9bb9, 0xff, 0x75},
+ {0x9bcd, 0xff, 0x24},
+ {0x9bff, 0xff, 0x30},
+ {0xd015, 0xff, 0x46},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x46},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0x0f},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x32},
+ {0xd007, 0x03, 0x01},
+ {0xd00c, 0xff, 0x36},
+ {0xd00d, 0x03, 0x03},
+ {0xd00a, 0xff, 0x35},
+ {0xd00b, 0x03, 0x01},
+ {0x9bc7, 0xff, 0x07},
+ {0x9bc8, 0xff, 0x90},
+ {0x9bc3, 0xff, 0x0f},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0x36},
+ {0x9bc6, 0xff, 0x03},
+ {0x9bba, 0xff, 0xc9},
+ {0x9bc9, 0xff, 0x79},
+ {0xd011, 0xff, 0x10},
+ {0xd012, 0x03, 0x01},
+ {0xd013, 0xff, 0x45},
+ {0xd014, 0x03, 0x03},
+ {0xd040, 0xff, 0x98},
+ {0xd041, 0x03, 0x00},
+ {0xd042, 0xff, 0xcf},
+ {0xd043, 0x03, 0x03},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0x01, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
+ {0x9bd0, 0xff, 0xcc},
+ {0x9be4, 0xff, 0xa0},
+ {0x9bbd, 0xff, 0x8e},
+ {0x9be2, 0xff, 0x4d},
+ {0x9bee, 0x01, 0x01},
+};
+
+/*
+ * Microtune MT2060 tuner init
+ * AF9013_TUNER_MT2060_2 0x93
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mt2060_2[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x06},
+ {0x9bbe, 0xff, 0x01},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0xd015, 0xff, 0x46},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x46},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0x0f},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x32},
+ {0xd007, 0x03, 0x01},
+ {0xd00c, 0xff, 0x36},
+ {0xd00d, 0x03, 0x03},
+ {0xd00a, 0xff, 0x35},
+ {0xd00b, 0x03, 0x01},
+ {0x9bc7, 0xff, 0x07},
+ {0x9bc8, 0xff, 0x90},
+ {0x9bc3, 0xff, 0x0f},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0x36},
+ {0x9bc6, 0xff, 0x03},
+ {0x9bba, 0xff, 0xc9},
+ {0x9bc9, 0xff, 0x79},
+ {0xd011, 0xff, 0x10},
+ {0xd012, 0x03, 0x01},
+ {0xd013, 0xff, 0x45},
+ {0xd014, 0x03, 0x03},
+ {0xd040, 0xff, 0x98},
+ {0xd041, 0x03, 0x00},
+ {0xd042, 0xff, 0xcf},
+ {0xd043, 0x03, 0x03},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0xff, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x96},
+ {0xd054, 0xff, 0x46},
+ {0xd045, 0x80, 0x00},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
+};
+
+/*
+ * MaxLinear MXL5003 tuner init
+ * AF9013_TUNER_MXL5003D 0x03
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mxl5003d[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x09},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x00},
+ {0x9be3, 0xff, 0x00},
+ {0x9bfc, 0xff, 0x0f},
+ {0x9bf6, 0xff, 0x01},
+ {0x9bbe, 0x01, 0x01},
+ {0xd015, 0xff, 0x33},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x40},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0x0f},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x6c},
+ {0xd007, 0x03, 0x00},
+ {0xd00c, 0xff, 0x3d},
+ {0xd00d, 0x03, 0x00},
+ {0xd00a, 0xff, 0x45},
+ {0xd00b, 0x03, 0x01},
+ {0x9bc7, 0xff, 0x07},
+ {0x9bc8, 0xff, 0x52},
+ {0x9bc3, 0xff, 0x0f},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0x3d},
+ {0x9bc6, 0xff, 0x00},
+ {0x9bba, 0xff, 0xa2},
+ {0x9bc9, 0xff, 0xa0},
+ {0xd011, 0xff, 0x56},
+ {0xd012, 0x03, 0x00},
+ {0xd013, 0xff, 0x50},
+ {0xd014, 0x03, 0x00},
+ {0xd040, 0xff, 0x56},
+ {0xd041, 0x03, 0x00},
+ {0xd042, 0xff, 0x50},
+ {0xd043, 0x03, 0x00},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0xff, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
+};
+
+/*
+ * MaxLinear MXL5005S & MXL5007T tuner init
+ * AF9013_TUNER_MXL5005D 0x0d
+ * AF9013_TUNER_MXL5005R 0x1e
+ * AF9013_TUNER_MXL5007T 0xb1
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mxl5005[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x07},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x01},
+ {0x9be3, 0xff, 0x01},
+ {0x9bbe, 0x01, 0x01},
+ {0x9bcc, 0x01, 0x01},
+ {0x9bb9, 0xff, 0x00},
+ {0x9bcd, 0xff, 0x28},
+ {0x9bff, 0xff, 0x24},
+ {0xd015, 0xff, 0x40},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x40},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0x0f},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x73},
+ {0xd007, 0x03, 0x01},
+ {0xd00c, 0xff, 0xfa},
+ {0xd00d, 0x03, 0x01},
+ {0xd00a, 0xff, 0xff},
+ {0xd00b, 0x03, 0x01},
+ {0x9bc7, 0xff, 0x23},
+ {0x9bc8, 0xff, 0x55},
+ {0x9bc3, 0xff, 0x01},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0xfa},
+ {0x9bc6, 0xff, 0x01},
+ {0x9bba, 0xff, 0xff},
+ {0x9bc9, 0xff, 0xff},
+ {0x9bd3, 0xff, 0x95},
+ {0xd011, 0xff, 0x70},
+ {0xd012, 0x03, 0x01},
+ {0xd013, 0xff, 0xfb},
+ {0xd014, 0x03, 0x01},
+ {0xd040, 0xff, 0x70},
+ {0xd041, 0x03, 0x01},
+ {0xd042, 0xff, 0xfb},
+ {0xd043, 0x03, 0x01},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0x01, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
+ {0x9bd0, 0xff, 0x93},
+ {0x9be4, 0xff, 0xfe},
+ {0x9bbd, 0xff, 0x63},
+ {0x9be2, 0xff, 0xfe},
+ {0x9bee, 0x01, 0x01},
+};
+
+/*
+ * Quantek QT1010 tuner init
+ * AF9013_TUNER_QT1010 0x86
+ * AF9013_TUNER_QT1010A 0xa2
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_qt1010[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x09},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x01},
+ {0x9be3, 0xff, 0x01},
+ {0xd015, 0xff, 0x46},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x46},
+ {0xd045, 0x01, 0x00},
+ {0x9bbe, 0x01, 0x01},
+ {0x9bcc, 0x01, 0x01},
+ {0x9bb9, 0xff, 0x00},
+ {0x9bcd, 0xff, 0x28},
+ {0x9bff, 0xff, 0x20},
+ {0xd008, 0xff, 0x0f},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x99},
+ {0xd007, 0x03, 0x01},
+ {0xd00c, 0xff, 0x0f},
+ {0xd00d, 0x03, 0x02},
+ {0xd00a, 0xff, 0x50},
+ {0xd00b, 0x03, 0x01},
+ {0x9bc7, 0xff, 0x00},
+ {0x9bc8, 0xff, 0x00},
+ {0x9bc3, 0xff, 0x0f},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0x0f},
+ {0x9bc6, 0xff, 0x02},
+ {0x9bba, 0xff, 0xc5},
+ {0x9bc9, 0xff, 0xff},
+ {0xd011, 0xff, 0x58},
+ {0xd012, 0x03, 0x02},
+ {0xd013, 0xff, 0x89},
+ {0xd014, 0x03, 0x01},
+ {0xd040, 0xff, 0x58},
+ {0xd041, 0x03, 0x02},
+ {0xd042, 0xff, 0x89},
+ {0xd043, 0x03, 0x01},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0x01, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
+ {0x9bd0, 0xff, 0xcd},
+ {0x9be4, 0xff, 0xbb},
+ {0x9bbd, 0xff, 0x93},
+ {0x9be2, 0xff, 0x80},
+ {0x9bee, 0x01, 0x01},
+};
+
+/*
+ * Freescale MC44S803 tuner init
+ * AF9013_TUNER_MC44S803 0x85
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mc44s803[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x06},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x00},
+ {0x9be3, 0xff, 0x00},
+ {0x9bf6, 0xff, 0x01},
+ {0x9bf8, 0xff, 0x02},
+ {0x9bf9, 0xff, 0x02},
+ {0x9bfc, 0xff, 0x1f},
+ {0x9bbe, 0x01, 0x01},
+ {0x9bcc, 0x01, 0x01},
+ {0x9bb9, 0xff, 0x00},
+ {0x9bcd, 0xff, 0x24},
+ {0x9bff, 0xff, 0x24},
+ {0xd015, 0xff, 0x46},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x46},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0x01},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x7b},
+ {0xd007, 0x03, 0x00},
+ {0xd00c, 0xff, 0x7c},
+ {0xd00d, 0x03, 0x02},
+ {0xd00a, 0xff, 0xfe},
+ {0xd00b, 0x03, 0x01},
+ {0x9bc7, 0xff, 0x08},
+ {0x9bc8, 0xff, 0x9a},
+ {0x9bc3, 0xff, 0x01},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0x7c},
+ {0x9bc6, 0xff, 0x02},
+ {0x9bba, 0xff, 0xfc},
+ {0x9bc9, 0xff, 0xaa},
+ {0xd011, 0xff, 0x6b},
+ {0xd012, 0x03, 0x00},
+ {0xd013, 0xff, 0x88},
+ {0xd014, 0x03, 0x02},
+ {0xd040, 0xff, 0x6b},
+ {0xd041, 0x03, 0x00},
+ {0xd042, 0xff, 0x7c},
+ {0xd043, 0x03, 0x02},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0x01, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
+ {0x9bd0, 0xff, 0x9e},
+ {0x9be4, 0xff, 0xff},
+ {0x9bbd, 0xff, 0x9e},
+ {0x9be2, 0xff, 0x25},
+ {0x9bee, 0x01, 0x01},
+ {0xd73b, 0x08, 0x00},
+};
+
+/*
+ * Unknown, probably for tin can tuner, tuner init
+ * AF9013_TUNER_UNKNOWN 0x8c
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_unknown[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x02},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x01},
+ {0x9be3, 0xff, 0x01},
+ {0xd1a0, 0x02, 0x00},
+ {0x9bbe, 0x01, 0x01},
+ {0x9bcc, 0x01, 0x01},
+ {0x9bb9, 0xff, 0x00},
+ {0x9bcd, 0xff, 0x18},
+ {0x9bff, 0xff, 0x2c},
+ {0xd015, 0xff, 0x46},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x46},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0xdf},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x44},
+ {0xd007, 0x03, 0x01},
+ {0xd00c, 0xff, 0x00},
+ {0xd00d, 0x03, 0x02},
+ {0xd00a, 0xff, 0xf6},
+ {0xd00b, 0x03, 0x01},
+ {0x9bba, 0xff, 0xf9},
+ {0x9bc8, 0xff, 0xaa},
+ {0x9bc3, 0xff, 0xdf},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0x00},
+ {0x9bc6, 0xff, 0x02},
+ {0x9bc9, 0xff, 0xf0},
+ {0xd011, 0xff, 0x3c},
+ {0xd012, 0x03, 0x01},
+ {0xd013, 0xff, 0xf7},
+ {0xd014, 0x03, 0x02},
+ {0xd040, 0xff, 0x0b},
+ {0xd041, 0x03, 0x02},
+ {0xd042, 0xff, 0x4d},
+ {0xd043, 0x03, 0x00},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0x01, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
+};
+
+/*
+ * NXP TDA18271 & TDA18218 tuner init
+ * AF9013_TUNER_TDA18271 0x9c
+ * AF9013_TUNER_TDA18218 0xb3
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_tda18271[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x04},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x01},
+ {0x9be3, 0xff, 0x01},
+ {0xd1a0, 0x02, 0x00},
+ {0x9bbe, 0x01, 0x01},
+ {0x9bcc, 0x01, 0x01},
+ {0x9bb9, 0xff, 0x00},
+ {0x9bcd, 0xff, 0x18},
+ {0x9bff, 0xff, 0x2c},
+ {0xd015, 0xff, 0x46},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x46},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0xdf},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x44},
+ {0xd007, 0x03, 0x01},
+ {0xd00c, 0xff, 0x00},
+ {0xd00d, 0x03, 0x02},
+ {0xd00a, 0xff, 0xf6},
+ {0xd00b, 0x03, 0x01},
+ {0x9bba, 0xff, 0xf9},
+ {0x9bc8, 0xff, 0xaa},
+ {0x9bc3, 0xff, 0xdf},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0x00},
+ {0x9bc6, 0xff, 0x02},
+ {0x9bc9, 0xff, 0xf0},
+ {0xd011, 0xff, 0x3c},
+ {0xd012, 0x03, 0x01},
+ {0xd013, 0xff, 0xf7},
+ {0xd014, 0x03, 0x02},
+ {0xd040, 0xff, 0x0b},
+ {0xd041, 0x03, 0x02},
+ {0xd042, 0xff, 0x4d},
+ {0xd043, 0x03, 0x00},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0x01, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
+ {0x9bd0, 0xff, 0xa8},
+ {0x9be4, 0xff, 0x7f},
+ {0x9bbd, 0xff, 0xa8},
+ {0x9be2, 0xff, 0x20},
+ {0x9bee, 0x01, 0x01},
+};
+
+#endif /* AF9013_PRIV_H */
diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c
new file mode 100644
index 000000000000..eed2ea4da8fa
--- /dev/null
+++ b/drivers/media/dvb-frontends/af9033.c
@@ -0,0 +1,1195 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Afatech AF9033 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ */
+
+#include "af9033_priv.h"
+
+struct af9033_dev {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct dvb_frontend fe;
+ struct af9033_config cfg;
+ bool is_af9035;
+ bool is_it9135;
+
+ u32 bandwidth_hz;
+ bool ts_mode_parallel;
+ bool ts_mode_serial;
+
+ enum fe_status fe_status;
+ u64 post_bit_error_prev; /* for old read_ber we return (curr - prev) */
+ u64 post_bit_error;
+ u64 post_bit_count;
+ u64 error_block_count;
+ u64 total_block_count;
+};
+
+/* Write reg val table using reg addr auto increment */
+static int af9033_wr_reg_val_tab(struct af9033_dev *dev,
+ const struct reg_val *tab, int tab_len)
+{
+ struct i2c_client *client = dev->client;
+#define MAX_TAB_LEN 212
+ int ret, i, j;
+ u8 buf[1 + MAX_TAB_LEN];
+
+ dev_dbg(&client->dev, "tab_len=%d\n", tab_len);
+
+ if (tab_len > sizeof(buf)) {
+ dev_warn(&client->dev, "tab len %d is too big\n", tab_len);
+ return -EINVAL;
+ }
+
+ for (i = 0, j = 0; i < tab_len; i++) {
+ buf[j] = tab[i].val;
+
+ if (i == tab_len - 1 || tab[i].reg != tab[i + 1].reg - 1) {
+ ret = regmap_bulk_write(dev->regmap, tab[i].reg - j,
+ buf, j + 1);
+ if (ret)
+ goto err;
+
+ j = 0;
+ } else {
+ j++;
+ }
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int af9033_init(struct dvb_frontend *fe)
+{
+ struct af9033_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, len;
+ unsigned int utmp;
+ const struct reg_val *init;
+ u8 buf[4];
+ struct reg_val_mask tab[] = {
+ { 0x80fb24, 0x00, 0x08 },
+ { 0x80004c, 0x00, 0xff },
+ { 0x00f641, dev->cfg.tuner, 0xff },
+ { 0x80f5ca, 0x01, 0x01 },
+ { 0x80f715, 0x01, 0x01 },
+ { 0x00f41f, 0x04, 0x04 },
+ { 0x00f41a, 0x01, 0x01 },
+ { 0x80f731, 0x00, 0x01 },
+ { 0x00d91e, 0x00, 0x01 },
+ { 0x00d919, 0x00, 0x01 },
+ { 0x80f732, 0x00, 0x01 },
+ { 0x00d91f, 0x00, 0x01 },
+ { 0x00d91a, 0x00, 0x01 },
+ { 0x80f730, 0x00, 0x01 },
+ { 0x80f778, 0x00, 0xff },
+ { 0x80f73c, 0x01, 0x01 },
+ { 0x80f776, 0x00, 0x01 },
+ { 0x00d8fd, 0x01, 0xff },
+ { 0x00d830, 0x01, 0xff },
+ { 0x00d831, 0x00, 0xff },
+ { 0x00d832, 0x00, 0xff },
+ { 0x80f985, dev->ts_mode_serial, 0x01 },
+ { 0x80f986, dev->ts_mode_parallel, 0x01 },
+ { 0x00d827, 0x00, 0xff },
+ { 0x00d829, 0x00, 0xff },
+ { 0x800045, dev->cfg.adc_multiplier, 0xff },
+ };
+
+ dev_dbg(&client->dev, "\n");
+
+ /* Main clk control */
+ utmp = div_u64((u64)dev->cfg.clock * 0x80000, 1000000);
+ buf[0] = (utmp >> 0) & 0xff;
+ buf[1] = (utmp >> 8) & 0xff;
+ buf[2] = (utmp >> 16) & 0xff;
+ buf[3] = (utmp >> 24) & 0xff;
+ ret = regmap_bulk_write(dev->regmap, 0x800025, buf, 4);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "clk=%u clk_cw=%08x\n", dev->cfg.clock, utmp);
+
+ /* ADC clk control */
+ for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) {
+ if (clock_adc_lut[i].clock == dev->cfg.clock)
+ break;
+ }
+ if (i == ARRAY_SIZE(clock_adc_lut)) {
+ dev_err(&client->dev, "Couldn't find ADC config for clock %d\n",
+ dev->cfg.clock);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ utmp = div_u64((u64)clock_adc_lut[i].adc * 0x80000, 1000000);
+ buf[0] = (utmp >> 0) & 0xff;
+ buf[1] = (utmp >> 8) & 0xff;
+ buf[2] = (utmp >> 16) & 0xff;
+ ret = regmap_bulk_write(dev->regmap, 0x80f1cd, buf, 3);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "adc=%u adc_cw=%06x\n",
+ clock_adc_lut[i].adc, utmp);
+
+ /* Config register table */
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = regmap_update_bits(dev->regmap, tab[i].reg, tab[i].mask,
+ tab[i].val);
+ if (ret)
+ goto err;
+ }
+
+ /* Demod clk output */
+ if (dev->cfg.dyn0_clk) {
+ ret = regmap_write(dev->regmap, 0x80fba8, 0x00);
+ if (ret)
+ goto err;
+ }
+
+ /* TS interface */
+ if (dev->cfg.ts_mode == AF9033_TS_MODE_USB) {
+ ret = regmap_update_bits(dev->regmap, 0x80f9a5, 0x01, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(dev->regmap, 0x80f9b5, 0x01, 0x01);
+ if (ret)
+ goto err;
+ } else {
+ ret = regmap_update_bits(dev->regmap, 0x80f990, 0x01, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(dev->regmap, 0x80f9b5, 0x01, 0x00);
+ if (ret)
+ goto err;
+ }
+
+ /* Demod core settings */
+ dev_dbg(&client->dev, "load ofsm settings\n");
+ switch (dev->cfg.tuner) {
+ case AF9033_TUNER_IT9135_38:
+ case AF9033_TUNER_IT9135_51:
+ case AF9033_TUNER_IT9135_52:
+ len = ARRAY_SIZE(ofsm_init_it9135_v1);
+ init = ofsm_init_it9135_v1;
+ break;
+ case AF9033_TUNER_IT9135_60:
+ case AF9033_TUNER_IT9135_61:
+ case AF9033_TUNER_IT9135_62:
+ len = ARRAY_SIZE(ofsm_init_it9135_v2);
+ init = ofsm_init_it9135_v2;
+ break;
+ default:
+ len = ARRAY_SIZE(ofsm_init);
+ init = ofsm_init;
+ break;
+ }
+
+ ret = af9033_wr_reg_val_tab(dev, init, len);
+ if (ret)
+ goto err;
+
+ /* Demod tuner specific settings */
+ dev_dbg(&client->dev, "load tuner specific settings\n");
+ switch (dev->cfg.tuner) {
+ case AF9033_TUNER_TUA9001:
+ len = ARRAY_SIZE(tuner_init_tua9001);
+ init = tuner_init_tua9001;
+ break;
+ case AF9033_TUNER_FC0011:
+ len = ARRAY_SIZE(tuner_init_fc0011);
+ init = tuner_init_fc0011;
+ break;
+ case AF9033_TUNER_MXL5007T:
+ len = ARRAY_SIZE(tuner_init_mxl5007t);
+ init = tuner_init_mxl5007t;
+ break;
+ case AF9033_TUNER_TDA18218:
+ len = ARRAY_SIZE(tuner_init_tda18218);
+ init = tuner_init_tda18218;
+ break;
+ case AF9033_TUNER_FC2580:
+ len = ARRAY_SIZE(tuner_init_fc2580);
+ init = tuner_init_fc2580;
+ break;
+ case AF9033_TUNER_FC0012:
+ len = ARRAY_SIZE(tuner_init_fc0012);
+ init = tuner_init_fc0012;
+ break;
+ case AF9033_TUNER_IT9135_38:
+ len = ARRAY_SIZE(tuner_init_it9135_38);
+ init = tuner_init_it9135_38;
+ break;
+ case AF9033_TUNER_IT9135_51:
+ len = ARRAY_SIZE(tuner_init_it9135_51);
+ init = tuner_init_it9135_51;
+ break;
+ case AF9033_TUNER_IT9135_52:
+ len = ARRAY_SIZE(tuner_init_it9135_52);
+ init = tuner_init_it9135_52;
+ break;
+ case AF9033_TUNER_IT9135_60:
+ len = ARRAY_SIZE(tuner_init_it9135_60);
+ init = tuner_init_it9135_60;
+ break;
+ case AF9033_TUNER_IT9135_61:
+ len = ARRAY_SIZE(tuner_init_it9135_61);
+ init = tuner_init_it9135_61;
+ break;
+ case AF9033_TUNER_IT9135_62:
+ len = ARRAY_SIZE(tuner_init_it9135_62);
+ init = tuner_init_it9135_62;
+ break;
+ default:
+ dev_dbg(&client->dev, "unsupported tuner ID=%d\n",
+ dev->cfg.tuner);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = af9033_wr_reg_val_tab(dev, init, len);
+ if (ret)
+ goto err;
+
+ if (dev->cfg.ts_mode == AF9033_TS_MODE_SERIAL) {
+ ret = regmap_update_bits(dev->regmap, 0x00d91c, 0x01, 0x01);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(dev->regmap, 0x00d917, 0x01, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(dev->regmap, 0x00d916, 0x01, 0x00);
+ if (ret)
+ goto err;
+ }
+
+ switch (dev->cfg.tuner) {
+ case AF9033_TUNER_IT9135_60:
+ case AF9033_TUNER_IT9135_61:
+ case AF9033_TUNER_IT9135_62:
+ ret = regmap_write(dev->regmap, 0x800000, 0x01);
+ if (ret)
+ goto err;
+ }
+
+ dev->bandwidth_hz = 0; /* Force to program all parameters */
+ /* Init stats here in order signal app which stats are supported */
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.len = 1;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.len = 1;
+ c->block_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->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int af9033_sleep(struct dvb_frontend *fe)
+{
+ struct af9033_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+ unsigned int utmp;
+
+ dev_dbg(&client->dev, "\n");
+
+ ret = regmap_write(dev->regmap, 0x80004c, 0x01);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x800000, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_read_poll_timeout(dev->regmap, 0x80004c, utmp, utmp == 0,
+ 5000, 1000000);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(dev->regmap, 0x80fb24, 0x08, 0x08);
+ if (ret)
+ goto err;
+
+ /* Prevent current leak by setting TS interface to parallel mode */
+ if (dev->cfg.ts_mode == AF9033_TS_MODE_SERIAL) {
+ /* Enable parallel TS */
+ ret = regmap_update_bits(dev->regmap, 0x00d917, 0x01, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(dev->regmap, 0x00d916, 0x01, 0x01);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int af9033_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ /* 800 => 2000 because IT9135 v2 is slow to gain lock */
+ fesettings->min_delay_ms = 2000;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+
+ return 0;
+}
+
+static int af9033_set_frontend(struct dvb_frontend *fe)
+{
+ struct af9033_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ unsigned int utmp, adc_freq;
+ u8 tmp, buf[3], bandwidth_reg_val;
+ u32 if_frequency;
+
+ dev_dbg(&client->dev, "frequency=%u bandwidth_hz=%u\n",
+ c->frequency, c->bandwidth_hz);
+
+ /* Check bandwidth */
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ bandwidth_reg_val = 0x00;
+ break;
+ case 7000000:
+ bandwidth_reg_val = 0x01;
+ break;
+ case 8000000:
+ bandwidth_reg_val = 0x02;
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid bandwidth_hz\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ /* Coefficients */
+ if (c->bandwidth_hz != dev->bandwidth_hz) {
+ for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) {
+ if (coeff_lut[i].clock == dev->cfg.clock &&
+ coeff_lut[i].bandwidth_hz == c->bandwidth_hz) {
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(coeff_lut)) {
+ dev_err(&client->dev,
+ "Couldn't find config for clock %u\n",
+ dev->cfg.clock);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = regmap_bulk_write(dev->regmap, 0x800001, coeff_lut[i].val,
+ sizeof(coeff_lut[i].val));
+ if (ret)
+ goto err;
+ }
+
+ /* IF frequency control */
+ if (c->bandwidth_hz != dev->bandwidth_hz) {
+ for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) {
+ if (clock_adc_lut[i].clock == dev->cfg.clock)
+ break;
+ }
+ if (i == ARRAY_SIZE(clock_adc_lut)) {
+ dev_err(&client->dev,
+ "Couldn't find ADC clock for clock %u\n",
+ dev->cfg.clock);
+ ret = -EINVAL;
+ goto err;
+ }
+ adc_freq = clock_adc_lut[i].adc;
+
+ if (dev->cfg.adc_multiplier == AF9033_ADC_MULTIPLIER_2X)
+ adc_freq = 2 * adc_freq;
+
+ /* Get used IF frequency */
+ if (fe->ops.tuner_ops.get_if_frequency)
+ fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+ else
+ if_frequency = 0;
+
+ utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x800000,
+ adc_freq);
+
+ if (!dev->cfg.spec_inv && if_frequency)
+ utmp = 0x800000 - utmp;
+
+ buf[0] = (utmp >> 0) & 0xff;
+ buf[1] = (utmp >> 8) & 0xff;
+ buf[2] = (utmp >> 16) & 0xff;
+ ret = regmap_bulk_write(dev->regmap, 0x800029, buf, 3);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "if_frequency_cw=%06x\n", utmp);
+
+ dev->bandwidth_hz = c->bandwidth_hz;
+ }
+
+ ret = regmap_update_bits(dev->regmap, 0x80f904, 0x03,
+ bandwidth_reg_val);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x800040, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x800047, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(dev->regmap, 0x80f999, 0x01, 0x00);
+ if (ret)
+ goto err;
+
+ if (c->frequency <= 230000000)
+ tmp = 0x00; /* VHF */
+ else
+ tmp = 0x01; /* UHF */
+
+ ret = regmap_write(dev->regmap, 0x80004b, tmp);
+ if (ret)
+ goto err;
+ /* Reset FSM */
+ ret = regmap_write(dev->regmap, 0x800000, 0x00);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int af9033_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ struct af9033_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+ u8 buf[8];
+
+ dev_dbg(&client->dev, "\n");
+
+ /* Read all needed TPS registers */
+ ret = regmap_bulk_read(dev->regmap, 0x80f900, buf, 8);
+ if (ret)
+ goto err;
+
+ switch ((buf[0] >> 0) & 3) {
+ case 0:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ }
+
+ switch ((buf[1] >> 0) & 3) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[2] >> 0) & 7) {
+ case 0:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[3] >> 0) & 3) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = QAM_16;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ }
+
+ switch ((buf[4] >> 0) & 3) {
+ case 0:
+ c->bandwidth_hz = 6000000;
+ break;
+ case 1:
+ c->bandwidth_hz = 7000000;
+ break;
+ case 2:
+ c->bandwidth_hz = 8000000;
+ break;
+ }
+
+ switch ((buf[6] >> 0) & 7) {
+ case 0:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ case 5:
+ c->code_rate_HP = FEC_NONE;
+ break;
+ }
+
+ switch ((buf[7] >> 0) & 7) {
+ case 0:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ case 5:
+ c->code_rate_LP = FEC_NONE;
+ break;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int af9033_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct af9033_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, tmp = 0;
+ u8 buf[7];
+ unsigned int utmp, utmp1;
+
+ dev_dbg(&client->dev, "\n");
+
+ *status = 0;
+
+ /* Radio channel status: 0=no result, 1=has signal, 2=no signal */
+ ret = regmap_read(dev->regmap, 0x800047, &utmp);
+ if (ret)
+ goto err;
+
+ /* Has signal */
+ if (utmp == 0x01)
+ *status |= FE_HAS_SIGNAL;
+
+ if (utmp != 0x02) {
+ /* TPS lock */
+ ret = regmap_read(dev->regmap, 0x80f5a9, &utmp);
+ if (ret)
+ goto err;
+
+ if ((utmp >> 0) & 0x01)
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+
+ /* Full lock */
+ ret = regmap_read(dev->regmap, 0x80f999, &utmp);
+ if (ret)
+ goto err;
+
+ if ((utmp >> 0) & 0x01)
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ }
+
+ dev->fe_status = *status;
+
+ /* Signal strength */
+ if (dev->fe_status & FE_HAS_SIGNAL) {
+ if (dev->is_af9035) {
+ ret = regmap_read(dev->regmap, 0x80004a, &utmp);
+ if (ret)
+ goto err;
+ tmp = -utmp * 1000;
+ } else {
+ ret = regmap_read(dev->regmap, 0x8000f7, &utmp);
+ if (ret)
+ goto err;
+ tmp = (utmp - 100) * 1000;
+ }
+
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->strength.stat[0].svalue = tmp;
+ } else {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* CNR */
+ if (dev->fe_status & FE_HAS_VITERBI) {
+ /* Read raw SNR value */
+ ret = regmap_bulk_read(dev->regmap, 0x80002c, buf, 3);
+ if (ret)
+ goto err;
+
+ utmp1 = buf[2] << 16 | buf[1] << 8 | buf[0] << 0;
+
+ /* Read superframe number */
+ ret = regmap_read(dev->regmap, 0x80f78b, &utmp);
+ if (ret)
+ goto err;
+
+ if (utmp)
+ utmp1 /= utmp;
+
+ /* Read current transmission mode */
+ ret = regmap_read(dev->regmap, 0x80f900, &utmp);
+ if (ret)
+ goto err;
+
+ switch ((utmp >> 0) & 3) {
+ case 0:
+ /* 2k */
+ utmp1 *= 4;
+ break;
+ case 1:
+ /* 8k */
+ utmp1 *= 1;
+ break;
+ case 2:
+ /* 4k */
+ utmp1 *= 2;
+ break;
+ default:
+ utmp1 *= 0;
+ break;
+ }
+
+ /* Read current modulation */
+ ret = regmap_read(dev->regmap, 0x80f903, &utmp);
+ if (ret)
+ goto err;
+
+ switch ((utmp >> 0) & 3) {
+ case 0:
+ /*
+ * QPSK
+ * CNR[dB] 13 * -log10((1690000 - value) / value) + 2.6
+ * value [653799, 1689999], 2.6 / 13 = 3355443
+ */
+ utmp1 = clamp(utmp1, 653799U, 1689999U);
+ utmp1 = ((u64)(intlog10(utmp1)
+ - intlog10(1690000 - utmp1)
+ + 3355443) * 13 * 1000) >> 24;
+ break;
+ case 1:
+ /*
+ * QAM-16
+ * CNR[dB] 6 * log10((value - 370000) / (828000 - value)) + 15.7
+ * value [371105, 827999], 15.7 / 6 = 43900382
+ */
+ utmp1 = clamp(utmp1, 371105U, 827999U);
+ utmp1 = ((u64)(intlog10(utmp1 - 370000)
+ - intlog10(828000 - utmp1)
+ + 43900382) * 6 * 1000) >> 24;
+ break;
+ case 2:
+ /*
+ * QAM-64
+ * CNR[dB] 8 * log10((value - 193000) / (425000 - value)) + 23.8
+ * value [193246, 424999], 23.8 / 8 = 49912218
+ */
+ utmp1 = clamp(utmp1, 193246U, 424999U);
+ utmp1 = ((u64)(intlog10(utmp1 - 193000)
+ - intlog10(425000 - utmp1)
+ + 49912218) * 8 * 1000) >> 24;
+ break;
+ default:
+ utmp1 = 0;
+ break;
+ }
+
+ dev_dbg(&client->dev, "cnr=%u\n", utmp1);
+
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = utmp1;
+ } else {
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* UCB/PER/BER */
+ if (dev->fe_status & FE_HAS_LOCK) {
+ /* Outer FEC, 204 byte packets */
+ u16 abort_packet_count, rsd_packet_count;
+ /* Inner FEC, bits */
+ u32 rsd_bit_err_count;
+
+ /*
+ * Packet count used for measurement is 10000
+ * (rsd_packet_count). Maybe it should be increased?
+ */
+
+ ret = regmap_bulk_read(dev->regmap, 0x800032, buf, 7);
+ if (ret)
+ goto err;
+
+ abort_packet_count = (buf[1] << 8) | (buf[0] << 0);
+ rsd_bit_err_count = (buf[4] << 16) | (buf[3] << 8) | buf[2];
+ rsd_packet_count = (buf[6] << 8) | (buf[5] << 0);
+
+ dev->error_block_count += abort_packet_count;
+ dev->total_block_count += rsd_packet_count;
+ dev->post_bit_error += rsd_bit_err_count;
+ dev->post_bit_count += rsd_packet_count * 204 * 8;
+
+ c->block_count.len = 1;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue = dev->total_block_count;
+
+ c->block_error.len = 1;
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue = dev->error_block_count;
+
+ c->post_bit_count.len = 1;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
+
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int af9033_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct af9033_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
+ int ret;
+ unsigned int utmp;
+
+ dev_dbg(&client->dev, "\n");
+
+ /* Use DVBv5 CNR */
+ if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) {
+ /* Return 0.1 dB for AF9030 and 0-0xffff for IT9130. */
+ if (dev->is_af9035) {
+ /* 1000x => 10x (0.1 dB) */
+ *snr = div_s64(c->cnr.stat[0].svalue, 100);
+ } else {
+ /* 1000x => 1x (1 dB) */
+ *snr = div_s64(c->cnr.stat[0].svalue, 1000);
+
+ /* Read current modulation */
+ ret = regmap_read(dev->regmap, 0x80f903, &utmp);
+ if (ret)
+ goto err;
+
+ /* scale value to 0x0000-0xffff */
+ switch ((utmp >> 0) & 3) {
+ case 0:
+ *snr = *snr * 0xffff / 23;
+ break;
+ case 1:
+ *snr = *snr * 0xffff / 26;
+ break;
+ case 2:
+ *snr = *snr * 0xffff / 32;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ } else {
+ *snr = 0;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct af9033_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
+ int ret, tmp, power_real;
+ unsigned int utmp;
+ u8 gain_offset, buf[7];
+
+ dev_dbg(&client->dev, "\n");
+
+ if (dev->is_af9035) {
+ /* Read signal strength of 0-100 scale */
+ ret = regmap_read(dev->regmap, 0x800048, &utmp);
+ if (ret)
+ goto err;
+
+ /* Scale value to 0x0000-0xffff */
+ *strength = utmp * 0xffff / 100;
+ } else {
+ ret = regmap_read(dev->regmap, 0x8000f7, &utmp);
+ if (ret)
+ goto err;
+
+ ret = regmap_bulk_read(dev->regmap, 0x80f900, buf, 7);
+ if (ret)
+ goto err;
+
+ if (c->frequency <= 300000000)
+ gain_offset = 7; /* VHF */
+ else
+ gain_offset = 4; /* UHF */
+
+ power_real = (utmp - 100 - gain_offset) -
+ power_reference[((buf[3] >> 0) & 3)][((buf[6] >> 0) & 7)];
+
+ if (power_real < -15)
+ tmp = 0;
+ else if ((power_real >= -15) && (power_real < 0))
+ tmp = (2 * (power_real + 15)) / 3;
+ else if ((power_real >= 0) && (power_real < 20))
+ tmp = 4 * power_real + 10;
+ else if ((power_real >= 20) && (power_real < 35))
+ tmp = (2 * (power_real - 20)) / 3 + 90;
+ else
+ tmp = 100;
+
+ /* Scale value to 0x0000-0xffff */
+ *strength = tmp * 0xffff / 100;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int af9033_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct af9033_dev *dev = fe->demodulator_priv;
+
+ *ber = (dev->post_bit_error - dev->post_bit_error_prev);
+ dev->post_bit_error_prev = dev->post_bit_error;
+
+ return 0;
+}
+
+static int af9033_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct af9033_dev *dev = fe->demodulator_priv;
+
+ *ucblocks = dev->error_block_count;
+
+ return 0;
+}
+
+static int af9033_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct af9033_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+
+ dev_dbg(&client->dev, "enable=%d\n", enable);
+
+ ret = regmap_update_bits(dev->regmap, 0x00fa04, 0x01, enable);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int af9033_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct af9033_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+
+ dev_dbg(&client->dev, "onoff=%d\n", onoff);
+
+ ret = regmap_update_bits(dev->regmap, 0x80f993, 0x01, onoff);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int af9033_pid_filter(struct dvb_frontend *fe, int index, u16 pid,
+ int onoff)
+{
+ struct af9033_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+ u8 wbuf[2] = {(pid >> 0) & 0xff, (pid >> 8) & 0xff};
+
+ dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d\n",
+ index, pid, onoff);
+
+ if (pid > 0x1fff)
+ return 0;
+
+ ret = regmap_bulk_write(dev->regmap, 0x80f996, wbuf, 2);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x80f994, onoff);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x80f995, index);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static const struct dvb_frontend_ops af9033_ops = {
+ .delsys = {SYS_DVBT},
+ .info = {
+ .name = "Afatech AF9033 (DVB-T)",
+ .frequency_min_hz = 174 * MHz,
+ .frequency_max_hz = 862 * MHz,
+ .frequency_stepsize_hz = 250 * kHz,
+ .caps = FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .init = af9033_init,
+ .sleep = af9033_sleep,
+
+ .get_tune_settings = af9033_get_tune_settings,
+ .set_frontend = af9033_set_frontend,
+ .get_frontend = af9033_get_frontend,
+
+ .read_status = af9033_read_status,
+ .read_snr = af9033_read_snr,
+ .read_signal_strength = af9033_read_signal_strength,
+ .read_ber = af9033_read_ber,
+ .read_ucblocks = af9033_read_ucblocks,
+
+ .i2c_gate_ctrl = af9033_i2c_gate_ctrl,
+};
+
+static int af9033_probe(struct i2c_client *client)
+{
+ struct af9033_config *cfg = client->dev.platform_data;
+ struct af9033_dev *dev;
+ int ret;
+ u8 buf[8];
+ u32 reg;
+ static const struct regmap_config regmap_config = {
+ .reg_bits = 24,
+ .val_bits = 8,
+ };
+
+ /* Allocate memory for the internal state */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* Setup the state */
+ dev->client = client;
+ memcpy(&dev->cfg, cfg, sizeof(dev->cfg));
+ switch (dev->cfg.ts_mode) {
+ case AF9033_TS_MODE_PARALLEL:
+ dev->ts_mode_parallel = true;
+ break;
+ case AF9033_TS_MODE_SERIAL:
+ dev->ts_mode_serial = true;
+ break;
+ case AF9033_TS_MODE_USB:
+ /* USB mode for AF9035 */
+ default:
+ break;
+ }
+
+ if (dev->cfg.clock != 12000000) {
+ ret = -ENODEV;
+ dev_err(&client->dev,
+ "Unsupported clock %u Hz. Only 12000000 Hz is supported currently\n",
+ dev->cfg.clock);
+ goto err_kfree;
+ }
+
+ /* Create regmap */
+ dev->regmap = regmap_init_i2c(client, &regmap_config);
+ if (IS_ERR(dev->regmap)) {
+ ret = PTR_ERR(dev->regmap);
+ goto err_kfree;
+ }
+
+ /* Firmware version */
+ switch (dev->cfg.tuner) {
+ case AF9033_TUNER_IT9135_38:
+ case AF9033_TUNER_IT9135_51:
+ case AF9033_TUNER_IT9135_52:
+ case AF9033_TUNER_IT9135_60:
+ case AF9033_TUNER_IT9135_61:
+ case AF9033_TUNER_IT9135_62:
+ dev->is_it9135 = true;
+ reg = 0x004bfc;
+ break;
+ default:
+ dev->is_af9035 = true;
+ reg = 0x0083e9;
+ break;
+ }
+
+ ret = regmap_bulk_read(dev->regmap, reg, &buf[0], 4);
+ if (ret)
+ goto err_regmap_exit;
+ ret = regmap_bulk_read(dev->regmap, 0x804191, &buf[4], 4);
+ if (ret)
+ goto err_regmap_exit;
+
+ dev_info(&client->dev,
+ "firmware version: LINK %d.%d.%d.%d - OFDM %d.%d.%d.%d\n",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+
+ /* Sleep as chip seems to be partly active by default */
+ /* IT9135 did not like to sleep at that early */
+ if (dev->is_af9035) {
+ ret = regmap_write(dev->regmap, 0x80004c, 0x01);
+ if (ret)
+ goto err_regmap_exit;
+ ret = regmap_write(dev->regmap, 0x800000, 0x00);
+ if (ret)
+ goto err_regmap_exit;
+ }
+
+ /* Create dvb frontend */
+ memcpy(&dev->fe.ops, &af9033_ops, sizeof(dev->fe.ops));
+ dev->fe.demodulator_priv = dev;
+ *cfg->fe = &dev->fe;
+ if (cfg->ops) {
+ cfg->ops->pid_filter = af9033_pid_filter;
+ cfg->ops->pid_filter_ctrl = af9033_pid_filter_ctrl;
+ }
+ cfg->regmap = dev->regmap;
+ i2c_set_clientdata(client, dev);
+
+ dev_info(&client->dev, "Afatech AF9033 successfully attached\n");
+
+ return 0;
+err_regmap_exit:
+ regmap_exit(dev->regmap);
+err_kfree:
+ kfree(dev);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static void af9033_remove(struct i2c_client *client)
+{
+ struct af9033_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ regmap_exit(dev->regmap);
+ kfree(dev);
+}
+
+static const struct i2c_device_id af9033_id_table[] = {
+ { "af9033" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, af9033_id_table);
+
+static struct i2c_driver af9033_driver = {
+ .driver = {
+ .name = "af9033",
+ .suppress_bind_attrs = true,
+ },
+ .probe = af9033_probe,
+ .remove = af9033_remove,
+ .id_table = af9033_id_table,
+};
+
+module_i2c_driver(af9033_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Afatech AF9033 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/af9033.h b/drivers/media/dvb-frontends/af9033.h
new file mode 100644
index 000000000000..4f2277af1a4f
--- /dev/null
+++ b/drivers/media/dvb-frontends/af9033.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Afatech AF9033 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ */
+
+#ifndef AF9033_H
+#define AF9033_H
+
+/*
+ * I2C address: 0x1c, 0x1d, 0x1e, 0x1f
+ */
+struct af9033_config {
+ /*
+ * clock Hz
+ * 12000000, 22000000, 24000000, 34000000, 32000000, 28000000, 26000000,
+ * 30000000, 36000000, 20480000, 16384000
+ */
+ u32 clock;
+
+ /*
+ * ADC multiplier
+ */
+#define AF9033_ADC_MULTIPLIER_1X 0
+#define AF9033_ADC_MULTIPLIER_2X 1
+ u8 adc_multiplier;
+
+ /*
+ * tuner
+ */
+#define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */
+#define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */
+#define AF9033_TUNER_FC0012 0x2e /* Fitipower FC0012 */
+#define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */
+#define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */
+#define AF9033_TUNER_FC2580 0x32 /* FCI FC2580 */
+/* 50-5f Omega */
+#define AF9033_TUNER_IT9135_38 0x38 /* Omega */
+#define AF9033_TUNER_IT9135_51 0x51 /* Omega LNA config 1 */
+#define AF9033_TUNER_IT9135_52 0x52 /* Omega LNA config 2 */
+/* 60-6f Omega v2 */
+#define AF9033_TUNER_IT9135_60 0x60 /* Omega v2 */
+#define AF9033_TUNER_IT9135_61 0x61 /* Omega v2 LNA config 1 */
+#define AF9033_TUNER_IT9135_62 0x62 /* Omega v2 LNA config 2 */
+ u8 tuner;
+
+ /*
+ * TS settings
+ */
+#define AF9033_TS_MODE_USB 0
+#define AF9033_TS_MODE_PARALLEL 1
+#define AF9033_TS_MODE_SERIAL 2
+ u8 ts_mode:2;
+
+ /*
+ * input spectrum inversion
+ */
+ bool spec_inv;
+
+ /*
+ *
+ */
+ bool dyn0_clk;
+
+ /*
+ * PID filter ops
+ */
+ struct af9033_ops *ops;
+
+ /*
+ * frontend
+ * returned by that driver
+ */
+ struct dvb_frontend **fe;
+
+ /*
+ * regmap for IT913x integrated tuner driver
+ * returned by that driver
+ */
+ struct regmap *regmap;
+};
+
+struct af9033_ops {
+ int (*pid_filter_ctrl)(struct dvb_frontend *fe, int onoff);
+ int (*pid_filter)(struct dvb_frontend *fe, int index, u16 pid,
+ int onoff);
+};
+
+#endif /* AF9033_H */
diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h
new file mode 100644
index 000000000000..7560da75ef00
--- /dev/null
+++ b/drivers/media/dvb-frontends/af9033_priv.h
@@ -0,0 +1,1991 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Afatech AF9033 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ */
+
+#ifndef AF9033_PRIV_H
+#define AF9033_PRIV_H
+
+#include <media/dvb_frontend.h>
+#include "af9033.h"
+#include <linux/math64.h>
+#include <linux/regmap.h>
+#include <linux/kernel.h>
+#include <linux/int_log.h>
+
+struct reg_val {
+ u32 reg;
+ u8 val;
+};
+
+struct reg_val_mask {
+ u32 reg;
+ u8 val;
+ u8 mask;
+};
+
+struct coeff {
+ u32 clock;
+ u32 bandwidth_hz;
+ u8 val[36];
+};
+
+struct clock_adc {
+ u32 clock;
+ u32 adc;
+};
+
+struct val_snr {
+ u32 val;
+ u8 snr;
+};
+
+/* Xtal clock vs. ADC clock lookup table */
+static const struct clock_adc clock_adc_lut[] = {
+ { 16384000, 20480000 },
+ { 20480000, 20480000 },
+ { 36000000, 20250000 },
+ { 30000000, 20156250 },
+ { 26000000, 20583333 },
+ { 28000000, 20416667 },
+ { 32000000, 20500000 },
+ { 34000000, 20187500 },
+ { 24000000, 20500000 },
+ { 22000000, 20625000 },
+ { 12000000, 20250000 },
+};
+
+/* Pre-calculated coeff lookup table */
+static const struct coeff coeff_lut[] = {
+ /* 12.000 MHz */
+ { 12000000, 8000000, {
+ 0x01, 0xce, 0x55, 0xc9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73,
+ 0x99, 0x0f, 0x00, 0x73, 0x95, 0x72, 0x00, 0x73, 0x91, 0xd5,
+ 0x00, 0x39, 0xca, 0xb9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73,
+ 0x95, 0x72, 0x37, 0x02, 0xce, 0x01 }
+ },
+ { 12000000, 7000000, {
+ 0x01, 0x94, 0x8b, 0x10, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65,
+ 0x25, 0xed, 0x00, 0x65, 0x22, 0xc4, 0x00, 0x65, 0x1f, 0x9b,
+ 0x00, 0x32, 0x91, 0x62, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65,
+ 0x22, 0xc4, 0x88, 0x02, 0x95, 0x01 }
+ },
+ { 12000000, 6000000, {
+ 0x01, 0x5a, 0xc0, 0x56, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56,
+ 0xb2, 0xcb, 0x00, 0x56, 0xb0, 0x15, 0x00, 0x56, 0xad, 0x60,
+ 0x00, 0x2b, 0x58, 0x0b, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56,
+ 0xb0, 0x15, 0xf4, 0x02, 0x5b, 0x01 }
+ },
+};
+
+/*
+ * Afatech AF9033 demod init
+ */
+static const struct reg_val ofsm_init[] = {
+ { 0x800051, 0x01 },
+ { 0x800070, 0x0a },
+ { 0x80007e, 0x04 },
+ { 0x800081, 0x0a },
+ { 0x80008a, 0x01 },
+ { 0x80008e, 0x01 },
+ { 0x800092, 0x06 },
+ { 0x800099, 0x01 },
+ { 0x80009f, 0xe1 },
+ { 0x8000a0, 0xcf },
+ { 0x8000a3, 0x01 },
+ { 0x8000a5, 0x01 },
+ { 0x8000a6, 0x01 },
+ { 0x8000a9, 0x00 },
+ { 0x8000aa, 0x01 },
+ { 0x8000b0, 0x01 },
+ { 0x8000c4, 0x05 },
+ { 0x8000c8, 0x19 },
+ { 0x80f000, 0x0f },
+ { 0x80f016, 0x10 },
+ { 0x80f017, 0x04 },
+ { 0x80f018, 0x05 },
+ { 0x80f019, 0x04 },
+ { 0x80f01a, 0x05 },
+ { 0x80f021, 0x03 },
+ { 0x80f022, 0x0a },
+ { 0x80f023, 0x0a },
+ { 0x80f02b, 0x00 },
+ { 0x80f02c, 0x01 },
+ { 0x80f064, 0x03 },
+ { 0x80f065, 0xf9 },
+ { 0x80f066, 0x03 },
+ { 0x80f067, 0x01 },
+ { 0x80f06f, 0xe0 },
+ { 0x80f070, 0x03 },
+ { 0x80f072, 0x0f },
+ { 0x80f073, 0x03 },
+ { 0x80f078, 0x00 },
+ { 0x80f087, 0x00 },
+ { 0x80f09b, 0x3f },
+ { 0x80f09c, 0x00 },
+ { 0x80f09d, 0x20 },
+ { 0x80f09e, 0x00 },
+ { 0x80f09f, 0x0c },
+ { 0x80f0a0, 0x00 },
+ { 0x80f130, 0x04 },
+ { 0x80f132, 0x04 },
+ { 0x80f144, 0x1a },
+ { 0x80f146, 0x00 },
+ { 0x80f14a, 0x01 },
+ { 0x80f14c, 0x00 },
+ { 0x80f14d, 0x00 },
+ { 0x80f14f, 0x04 },
+ { 0x80f158, 0x7f },
+ { 0x80f15a, 0x00 },
+ { 0x80f15b, 0x08 },
+ { 0x80f15d, 0x03 },
+ { 0x80f15e, 0x05 },
+ { 0x80f163, 0x05 },
+ { 0x80f166, 0x01 },
+ { 0x80f167, 0x40 },
+ { 0x80f168, 0x0f },
+ { 0x80f17a, 0x00 },
+ { 0x80f17b, 0x00 },
+ { 0x80f183, 0x01 },
+ { 0x80f19d, 0x40 },
+ { 0x80f1bc, 0x36 },
+ { 0x80f1bd, 0x00 },
+ { 0x80f1cb, 0xa0 },
+ { 0x80f1cc, 0x01 },
+ { 0x80f204, 0x10 },
+ { 0x80f214, 0x00 },
+ { 0x80f40e, 0x0a },
+ { 0x80f40f, 0x40 },
+ { 0x80f410, 0x08 },
+ { 0x80f55f, 0x0a },
+ { 0x80f561, 0x15 },
+ { 0x80f562, 0x20 },
+ { 0x80f5df, 0xfb },
+ { 0x80f5e0, 0x00 },
+ { 0x80f5e3, 0x09 },
+ { 0x80f5e4, 0x01 },
+ { 0x80f5e5, 0x01 },
+ { 0x80f5f8, 0x01 },
+ { 0x80f5fd, 0x01 },
+ { 0x80f600, 0x05 },
+ { 0x80f601, 0x08 },
+ { 0x80f602, 0x0b },
+ { 0x80f603, 0x0e },
+ { 0x80f604, 0x11 },
+ { 0x80f605, 0x14 },
+ { 0x80f606, 0x17 },
+ { 0x80f607, 0x1f },
+ { 0x80f60e, 0x00 },
+ { 0x80f60f, 0x04 },
+ { 0x80f610, 0x32 },
+ { 0x80f611, 0x10 },
+ { 0x80f707, 0xfc },
+ { 0x80f708, 0x00 },
+ { 0x80f709, 0x37 },
+ { 0x80f70a, 0x00 },
+ { 0x80f78b, 0x01 },
+ { 0x80f80f, 0x40 },
+ { 0x80f810, 0x54 },
+ { 0x80f811, 0x5a },
+ { 0x80f905, 0x01 },
+ { 0x80fb06, 0x03 },
+ { 0x80fd8b, 0x00 },
+};
+
+/*
+ * Infineon TUA 9001 tuner init
+ * AF9033_TUNER_TUA9001 = 0x27
+ */
+static const struct reg_val tuner_init_tua9001[] = {
+ { 0x800046, 0x27 },
+ { 0x800057, 0x00 },
+ { 0x800058, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x80006d, 0x00 },
+ { 0x800071, 0x05 },
+ { 0x800072, 0x02 },
+ { 0x800074, 0x01 },
+ { 0x800075, 0x03 },
+ { 0x800076, 0x02 },
+ { 0x800077, 0x00 },
+ { 0x800078, 0x01 },
+ { 0x800079, 0x00 },
+ { 0x80007a, 0x7e },
+ { 0x80007b, 0x3e },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x01 },
+ { 0x800095, 0x02 },
+ { 0x800096, 0x01 },
+ { 0x800098, 0x0a },
+ { 0x80009b, 0x05 },
+ { 0x80009c, 0x80 },
+ { 0x8000b3, 0x00 },
+ { 0x8000c5, 0x01 },
+ { 0x8000c6, 0x00 },
+ { 0x8000c9, 0x5d },
+ { 0x80f007, 0x00 },
+ { 0x80f01f, 0x82 },
+ { 0x80f020, 0x00 },
+ { 0x80f029, 0x82 },
+ { 0x80f02a, 0x00 },
+ { 0x80f047, 0x00 },
+ { 0x80f054, 0x00 },
+ { 0x80f055, 0x00 },
+ { 0x80f077, 0x01 },
+ { 0x80f1e6, 0x00 },
+};
+
+/*
+ * Fitipower FC0011 tuner init
+ * AF9033_TUNER_FC0011 = 0x28
+ */
+static const struct reg_val tuner_init_fc0011[] = {
+ { 0x800046, 0x28 },
+ { 0x800057, 0x00 },
+ { 0x800058, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x800068, 0xa5 },
+ { 0x80006e, 0x01 },
+ { 0x800071, 0x0a },
+ { 0x800072, 0x02 },
+ { 0x800074, 0x01 },
+ { 0x800079, 0x01 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x00 },
+ { 0x800095, 0x00 },
+ { 0x800096, 0x00 },
+ { 0x80009b, 0x2d },
+ { 0x80009c, 0x60 },
+ { 0x80009d, 0x23 },
+ { 0x8000a4, 0x50 },
+ { 0x8000ad, 0x50 },
+ { 0x8000b3, 0x01 },
+ { 0x8000b7, 0x88 },
+ { 0x8000b8, 0xa6 },
+ { 0x8000c5, 0x01 },
+ { 0x8000c6, 0x01 },
+ { 0x8000c9, 0x69 },
+ { 0x80f007, 0x00 },
+ { 0x80f00a, 0x1b },
+ { 0x80f00b, 0x1b },
+ { 0x80f00c, 0x1b },
+ { 0x80f00d, 0x1b },
+ { 0x80f00e, 0xff },
+ { 0x80f00f, 0x01 },
+ { 0x80f010, 0x00 },
+ { 0x80f011, 0x02 },
+ { 0x80f012, 0xff },
+ { 0x80f013, 0x01 },
+ { 0x80f014, 0x00 },
+ { 0x80f015, 0x02 },
+ { 0x80f01b, 0xef },
+ { 0x80f01c, 0x01 },
+ { 0x80f01d, 0x0f },
+ { 0x80f01e, 0x02 },
+ { 0x80f01f, 0x6e },
+ { 0x80f020, 0x00 },
+ { 0x80f025, 0xde },
+ { 0x80f026, 0x00 },
+ { 0x80f027, 0x0a },
+ { 0x80f028, 0x03 },
+ { 0x80f029, 0x6e },
+ { 0x80f02a, 0x00 },
+ { 0x80f047, 0x00 },
+ { 0x80f054, 0x00 },
+ { 0x80f055, 0x00 },
+ { 0x80f077, 0x01 },
+ { 0x80f1e6, 0x00 },
+};
+
+/*
+ * Fitipower FC0012 tuner init
+ * AF9033_TUNER_FC0012 = 0x2e
+ */
+static const struct reg_val tuner_init_fc0012[] = {
+ { 0x800046, 0x2e },
+ { 0x800057, 0x00 },
+ { 0x800058, 0x01 },
+ { 0x800059, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x80006d, 0x00 },
+ { 0x800071, 0x05 },
+ { 0x800072, 0x02 },
+ { 0x800074, 0x01 },
+ { 0x800075, 0x03 },
+ { 0x800076, 0x02 },
+ { 0x800077, 0x01 },
+ { 0x800078, 0x00 },
+ { 0x800079, 0x00 },
+ { 0x80007a, 0x90 },
+ { 0x80007b, 0x90 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x01 },
+ { 0x800095, 0x02 },
+ { 0x800096, 0x01 },
+ { 0x800098, 0x0a },
+ { 0x80009b, 0x05 },
+ { 0x80009c, 0x80 },
+ { 0x8000b3, 0x00 },
+ { 0x8000c5, 0x01 },
+ { 0x8000c6, 0x00 },
+ { 0x8000c9, 0x5d },
+ { 0x80f007, 0x00 },
+ { 0x80f01f, 0xa0 },
+ { 0x80f020, 0x00 },
+ { 0x80f029, 0x82 },
+ { 0x80f02a, 0x00 },
+ { 0x80f047, 0x00 },
+ { 0x80f054, 0x00 },
+ { 0x80f055, 0x00 },
+ { 0x80f077, 0x01 },
+ { 0x80f1e6, 0x00 },
+};
+
+/*
+ * MaxLinear MxL5007T tuner init
+ * AF9033_TUNER_MXL5007T = 0xa0
+ */
+static const struct reg_val tuner_init_mxl5007t[] = {
+ { 0x800046, 0x1b },
+ { 0x800057, 0x01 },
+ { 0x800058, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x800068, 0x96 },
+ { 0x800071, 0x05 },
+ { 0x800072, 0x02 },
+ { 0x800074, 0x01 },
+ { 0x800079, 0x01 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x00 },
+ { 0x800095, 0x00 },
+ { 0x800096, 0x00 },
+ { 0x8000b3, 0x01 },
+ { 0x8000c1, 0x01 },
+ { 0x8000c2, 0x00 },
+ { 0x80f007, 0x00 },
+ { 0x80f00c, 0x19 },
+ { 0x80f00d, 0x1a },
+ { 0x80f012, 0xda },
+ { 0x80f013, 0x00 },
+ { 0x80f014, 0x00 },
+ { 0x80f015, 0x02 },
+ { 0x80f01f, 0x82 },
+ { 0x80f020, 0x00 },
+ { 0x80f029, 0x82 },
+ { 0x80f02a, 0x00 },
+ { 0x80f077, 0x02 },
+ { 0x80f1e6, 0x00 },
+};
+
+/*
+ * NXP TDA18218HN tuner init
+ * AF9033_TUNER_TDA18218 = 0xa1
+ */
+static const struct reg_val tuner_init_tda18218[] = {
+ {0x800046, 0xa1},
+ {0x800057, 0x01},
+ {0x800058, 0x01},
+ {0x80005f, 0x00},
+ {0x800060, 0x00},
+ {0x800071, 0x05},
+ {0x800072, 0x02},
+ {0x800074, 0x01},
+ {0x800079, 0x01},
+ {0x800093, 0x00},
+ {0x800094, 0x00},
+ {0x800095, 0x00},
+ {0x800096, 0x00},
+ {0x8000b3, 0x01},
+ {0x8000c3, 0x01},
+ {0x8000c4, 0x00},
+ {0x80f007, 0x00},
+ {0x80f00c, 0x19},
+ {0x80f00d, 0x1a},
+ {0x80f012, 0xda},
+ {0x80f013, 0x00},
+ {0x80f014, 0x00},
+ {0x80f015, 0x02},
+ {0x80f01f, 0x82},
+ {0x80f020, 0x00},
+ {0x80f029, 0x82},
+ {0x80f02a, 0x00},
+ {0x80f077, 0x02},
+ {0x80f1e6, 0x00},
+};
+
+/*
+ * FCI FC2580 tuner init
+ * AF9033_TUNER_FC2580 = 0x32
+ */
+static const struct reg_val tuner_init_fc2580[] = {
+ { 0x800046, 0x32 },
+ { 0x800057, 0x01 },
+ { 0x800058, 0x00 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x800071, 0x05 },
+ { 0x800072, 0x02 },
+ { 0x800074, 0x01 },
+ { 0x800079, 0x01 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x00 },
+ { 0x800095, 0x00 },
+ { 0x800096, 0x05 },
+ { 0x8000b3, 0x01 },
+ { 0x8000c5, 0x01 },
+ { 0x8000c6, 0x00 },
+ { 0x8000d1, 0x01 },
+ { 0x80f007, 0x00 },
+ { 0x80f00c, 0x19 },
+ { 0x80f00d, 0x1a },
+ { 0x80f00e, 0x00 },
+ { 0x80f00f, 0x02 },
+ { 0x80f010, 0x00 },
+ { 0x80f011, 0x02 },
+ { 0x80f012, 0x00 },
+ { 0x80f013, 0x02 },
+ { 0x80f014, 0x00 },
+ { 0x80f015, 0x02 },
+ { 0x80f01f, 0x96 },
+ { 0x80f020, 0x00 },
+ { 0x80f029, 0x96 },
+ { 0x80f02a, 0x00 },
+ { 0x80f077, 0x01 },
+ { 0x80f1e6, 0x01 },
+};
+
+/*
+ * IT9133 AX demod init
+ */
+static const struct reg_val ofsm_init_it9135_v1[] = {
+ { 0x800051, 0x01 },
+ { 0x800070, 0x0a },
+ { 0x80007e, 0x04 },
+ { 0x800081, 0x0a },
+ { 0x80008a, 0x01 },
+ { 0x80008e, 0x01 },
+ { 0x800092, 0x06 },
+ { 0x800099, 0x01 },
+ { 0x80009f, 0xe1 },
+ { 0x8000a0, 0xcf },
+ { 0x8000a3, 0x01 },
+ { 0x8000a5, 0x01 },
+ { 0x8000a6, 0x01 },
+ { 0x8000a9, 0x00 },
+ { 0x8000aa, 0x01 },
+ { 0x8000b0, 0x01 },
+ { 0x8000c2, 0x05 },
+ { 0x8000c6, 0x19 },
+ { 0x80f000, 0x0f },
+ { 0x80f016, 0x10 },
+ { 0x80f017, 0x04 },
+ { 0x80f018, 0x05 },
+ { 0x80f019, 0x04 },
+ { 0x80f01a, 0x05 },
+ { 0x80f021, 0x03 },
+ { 0x80f022, 0x0a },
+ { 0x80f023, 0x0a },
+ { 0x80f02b, 0x00 },
+ { 0x80f02c, 0x01 },
+ { 0x80f064, 0x03 },
+ { 0x80f065, 0xf9 },
+ { 0x80f066, 0x03 },
+ { 0x80f067, 0x01 },
+ { 0x80f06f, 0xe0 },
+ { 0x80f070, 0x03 },
+ { 0x80f072, 0x0f },
+ { 0x80f073, 0x03 },
+ { 0x80f078, 0x00 },
+ { 0x80f087, 0x00 },
+ { 0x80f09b, 0x3f },
+ { 0x80f09c, 0x00 },
+ { 0x80f09d, 0x20 },
+ { 0x80f09e, 0x00 },
+ { 0x80f09f, 0x0c },
+ { 0x80f0a0, 0x00 },
+ { 0x80f130, 0x04 },
+ { 0x80f132, 0x04 },
+ { 0x80f144, 0x1a },
+ { 0x80f146, 0x00 },
+ { 0x80f14a, 0x01 },
+ { 0x80f14c, 0x00 },
+ { 0x80f14d, 0x00 },
+ { 0x80f14f, 0x04 },
+ { 0x80f158, 0x7f },
+ { 0x80f15a, 0x00 },
+ { 0x80f15b, 0x08 },
+ { 0x80f15d, 0x03 },
+ { 0x80f15e, 0x05 },
+ { 0x80f163, 0x05 },
+ { 0x80f166, 0x01 },
+ { 0x80f167, 0x40 },
+ { 0x80f168, 0x0f },
+ { 0x80f17a, 0x00 },
+ { 0x80f17b, 0x00 },
+ { 0x80f183, 0x01 },
+ { 0x80f19d, 0x40 },
+ { 0x80f1bc, 0x36 },
+ { 0x80f1bd, 0x00 },
+ { 0x80f1cb, 0xa0 },
+ { 0x80f1cc, 0x01 },
+ { 0x80f204, 0x10 },
+ { 0x80f214, 0x00 },
+ { 0x80f40e, 0x0a },
+ { 0x80f40f, 0x40 },
+ { 0x80f410, 0x08 },
+ { 0x80f55f, 0x0a },
+ { 0x80f561, 0x15 },
+ { 0x80f562, 0x20 },
+ { 0x80f5df, 0xfb },
+ { 0x80f5e0, 0x00 },
+ { 0x80f5e3, 0x09 },
+ { 0x80f5e4, 0x01 },
+ { 0x80f5e5, 0x01 },
+ { 0x80f5f8, 0x01 },
+ { 0x80f5fd, 0x01 },
+ { 0x80f600, 0x05 },
+ { 0x80f601, 0x08 },
+ { 0x80f602, 0x0b },
+ { 0x80f603, 0x0e },
+ { 0x80f604, 0x11 },
+ { 0x80f605, 0x14 },
+ { 0x80f606, 0x17 },
+ { 0x80f607, 0x1f },
+ { 0x80f60e, 0x00 },
+ { 0x80f60f, 0x04 },
+ { 0x80f610, 0x32 },
+ { 0x80f611, 0x10 },
+ { 0x80f707, 0xfc },
+ { 0x80f708, 0x00 },
+ { 0x80f709, 0x37 },
+ { 0x80f70a, 0x00 },
+ { 0x80f78b, 0x01 },
+ { 0x80f80f, 0x40 },
+ { 0x80f810, 0x54 },
+ { 0x80f811, 0x5a },
+ { 0x80f905, 0x01 },
+ { 0x80fb06, 0x03 },
+ { 0x80fd8b, 0x00 },
+};
+
+/*
+ * ITE Tech IT9133 AX Omega tuner init
+ * AF9033_TUNER_IT9135_38 = 0x38
+ */
+static const struct reg_val tuner_init_it9135_38[] = {
+ { 0x800043, 0x00 },
+ { 0x800046, 0x38 },
+ { 0x800051, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x800068, 0x0a },
+ { 0x800070, 0x0a },
+ { 0x800071, 0x05 },
+ { 0x800072, 0x02 },
+ { 0x800075, 0x8c },
+ { 0x800076, 0x8c },
+ { 0x800077, 0x8c },
+ { 0x800078, 0xc8 },
+ { 0x800079, 0x01 },
+ { 0x80007e, 0x04 },
+ { 0x80007f, 0x00 },
+ { 0x800081, 0x0a },
+ { 0x800082, 0x12 },
+ { 0x800083, 0x02 },
+ { 0x800084, 0x0a },
+ { 0x800085, 0x03 },
+ { 0x800086, 0xc8 },
+ { 0x800087, 0xb8 },
+ { 0x800088, 0xd0 },
+ { 0x800089, 0xc3 },
+ { 0x80008a, 0x01 },
+ { 0x80008e, 0x01 },
+ { 0x800092, 0x06 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x00 },
+ { 0x800095, 0x00 },
+ { 0x800096, 0x00 },
+ { 0x800099, 0x01 },
+ { 0x80009b, 0x3c },
+ { 0x80009c, 0x28 },
+ { 0x80009f, 0xe1 },
+ { 0x8000a0, 0xcf },
+ { 0x8000a3, 0x01 },
+ { 0x8000a4, 0x5a },
+ { 0x8000a5, 0x01 },
+ { 0x8000a6, 0x01 },
+ { 0x8000a9, 0x00 },
+ { 0x8000aa, 0x01 },
+ { 0x8000b0, 0x01 },
+ { 0x8000b3, 0x02 },
+ { 0x8000b4, 0x32 },
+ { 0x8000b6, 0x14 },
+ { 0x8000c0, 0x11 },
+ { 0x8000c1, 0x00 },
+ { 0x8000c2, 0x05 },
+ { 0x8000c4, 0x00 },
+ { 0x8000c6, 0x19 },
+ { 0x8000c7, 0x00 },
+ { 0x8000cc, 0x2e },
+ { 0x8000cd, 0x51 },
+ { 0x8000ce, 0x33 },
+ { 0x8000f3, 0x05 },
+ { 0x8000f4, 0x8c },
+ { 0x8000f5, 0x8c },
+ { 0x8000f8, 0x03 },
+ { 0x8000f9, 0x06 },
+ { 0x8000fa, 0x06 },
+ { 0x8000fc, 0x02 },
+ { 0x8000fd, 0x02 },
+ { 0x8000fe, 0x02 },
+ { 0x8000ff, 0x09 },
+ { 0x800100, 0x50 },
+ { 0x800101, 0x7b },
+ { 0x800102, 0x77 },
+ { 0x800103, 0x00 },
+ { 0x800104, 0x02 },
+ { 0x800105, 0xc8 },
+ { 0x800106, 0x05 },
+ { 0x800107, 0x7b },
+ { 0x800109, 0x02 },
+ { 0x800115, 0x0a },
+ { 0x800116, 0x03 },
+ { 0x800117, 0x02 },
+ { 0x800118, 0x80 },
+ { 0x80011a, 0xc8 },
+ { 0x80011b, 0x7b },
+ { 0x80011c, 0x8a },
+ { 0x80011d, 0xa0 },
+ { 0x800122, 0x02 },
+ { 0x800123, 0x18 },
+ { 0x800124, 0xc3 },
+ { 0x800127, 0x00 },
+ { 0x800128, 0x07 },
+ { 0x80012a, 0x53 },
+ { 0x80012b, 0x51 },
+ { 0x80012c, 0x4e },
+ { 0x80012d, 0x43 },
+ { 0x800137, 0x01 },
+ { 0x800138, 0x00 },
+ { 0x800139, 0x07 },
+ { 0x80013a, 0x00 },
+ { 0x80013b, 0x06 },
+ { 0x80013d, 0x00 },
+ { 0x80013e, 0x01 },
+ { 0x80013f, 0x5b },
+ { 0x800140, 0xc8 },
+ { 0x800141, 0x59 },
+ { 0x80f000, 0x0f },
+ { 0x80f016, 0x10 },
+ { 0x80f017, 0x04 },
+ { 0x80f018, 0x05 },
+ { 0x80f019, 0x04 },
+ { 0x80f01a, 0x05 },
+ { 0x80f01f, 0x8c },
+ { 0x80f020, 0x00 },
+ { 0x80f021, 0x03 },
+ { 0x80f022, 0x0a },
+ { 0x80f023, 0x0a },
+ { 0x80f029, 0x8c },
+ { 0x80f02a, 0x00 },
+ { 0x80f02b, 0x00 },
+ { 0x80f02c, 0x01 },
+ { 0x80f064, 0x03 },
+ { 0x80f065, 0xf9 },
+ { 0x80f066, 0x03 },
+ { 0x80f067, 0x01 },
+ { 0x80f06f, 0xe0 },
+ { 0x80f070, 0x03 },
+ { 0x80f072, 0x0f },
+ { 0x80f073, 0x03 },
+ { 0x80f077, 0x01 },
+ { 0x80f078, 0x00 },
+ { 0x80f085, 0x00 },
+ { 0x80f086, 0x02 },
+ { 0x80f087, 0x00 },
+ { 0x80f09b, 0x3f },
+ { 0x80f09c, 0x00 },
+ { 0x80f09d, 0x20 },
+ { 0x80f09e, 0x00 },
+ { 0x80f09f, 0x0c },
+ { 0x80f0a0, 0x00 },
+ { 0x80f130, 0x04 },
+ { 0x80f132, 0x04 },
+ { 0x80f144, 0x1a },
+ { 0x80f146, 0x00 },
+ { 0x80f14a, 0x01 },
+ { 0x80f14c, 0x00 },
+ { 0x80f14d, 0x00 },
+ { 0x80f14f, 0x04 },
+ { 0x80f158, 0x7f },
+ { 0x80f15a, 0x00 },
+ { 0x80f15b, 0x08 },
+ { 0x80f15d, 0x03 },
+ { 0x80f15e, 0x05 },
+ { 0x80f163, 0x05 },
+ { 0x80f166, 0x01 },
+ { 0x80f167, 0x40 },
+ { 0x80f168, 0x0f },
+ { 0x80f17a, 0x00 },
+ { 0x80f17b, 0x00 },
+ { 0x80f183, 0x01 },
+ { 0x80f19d, 0x40 },
+ { 0x80f1bc, 0x36 },
+ { 0x80f1bd, 0x00 },
+ { 0x80f1cb, 0xa0 },
+ { 0x80f1cc, 0x01 },
+ { 0x80f204, 0x10 },
+ { 0x80f214, 0x00 },
+ { 0x80f24c, 0x88 },
+ { 0x80f24d, 0x95 },
+ { 0x80f24e, 0x9a },
+ { 0x80f24f, 0x90 },
+ { 0x80f25a, 0x07 },
+ { 0x80f25b, 0xe8 },
+ { 0x80f25c, 0x03 },
+ { 0x80f25d, 0xb0 },
+ { 0x80f25e, 0x04 },
+ { 0x80f270, 0x01 },
+ { 0x80f271, 0x02 },
+ { 0x80f272, 0x01 },
+ { 0x80f273, 0x02 },
+ { 0x80f40e, 0x0a },
+ { 0x80f40f, 0x40 },
+ { 0x80f410, 0x08 },
+ { 0x80f55f, 0x0a },
+ { 0x80f561, 0x15 },
+ { 0x80f562, 0x20 },
+ { 0x80f5df, 0xfb },
+ { 0x80f5e0, 0x00 },
+ { 0x80f5e3, 0x09 },
+ { 0x80f5e4, 0x01 },
+ { 0x80f5e5, 0x01 },
+ { 0x80f5f8, 0x01 },
+ { 0x80f5fd, 0x01 },
+ { 0x80f600, 0x05 },
+ { 0x80f601, 0x08 },
+ { 0x80f602, 0x0b },
+ { 0x80f603, 0x0e },
+ { 0x80f604, 0x11 },
+ { 0x80f605, 0x14 },
+ { 0x80f606, 0x17 },
+ { 0x80f607, 0x1f },
+ { 0x80f60e, 0x00 },
+ { 0x80f60f, 0x04 },
+ { 0x80f610, 0x32 },
+ { 0x80f611, 0x10 },
+ { 0x80f707, 0xfc },
+ { 0x80f708, 0x00 },
+ { 0x80f709, 0x37 },
+ { 0x80f70a, 0x00 },
+ { 0x80f78b, 0x01 },
+ { 0x80f80f, 0x40 },
+ { 0x80f810, 0x54 },
+ { 0x80f811, 0x5a },
+ { 0x80f905, 0x01 },
+ { 0x80fb06, 0x03 },
+ { 0x80fd8b, 0x00 },
+};
+
+/*
+ * ITE Tech IT9133 AX Omega LNA config 1 tuner init
+ * AF9033_TUNER_IT9135_51 = 0x51
+ */
+static const struct reg_val tuner_init_it9135_51[] = {
+ { 0x800043, 0x00 },
+ { 0x800046, 0x51 },
+ { 0x800051, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x800068, 0x0a },
+ { 0x800070, 0x0a },
+ { 0x800071, 0x06 },
+ { 0x800072, 0x02 },
+ { 0x800075, 0x8c },
+ { 0x800076, 0x8c },
+ { 0x800077, 0x8c },
+ { 0x800078, 0xc8 },
+ { 0x800079, 0x01 },
+ { 0x80007e, 0x04 },
+ { 0x80007f, 0x00 },
+ { 0x800081, 0x0a },
+ { 0x800082, 0x12 },
+ { 0x800083, 0x02 },
+ { 0x800084, 0x0a },
+ { 0x800085, 0x03 },
+ { 0x800086, 0xc0 },
+ { 0x800087, 0x96 },
+ { 0x800088, 0xcf },
+ { 0x800089, 0xc3 },
+ { 0x80008a, 0x01 },
+ { 0x80008e, 0x01 },
+ { 0x800092, 0x06 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x00 },
+ { 0x800095, 0x00 },
+ { 0x800096, 0x00 },
+ { 0x800099, 0x01 },
+ { 0x80009b, 0x3c },
+ { 0x80009c, 0x28 },
+ { 0x80009f, 0xe1 },
+ { 0x8000a0, 0xcf },
+ { 0x8000a3, 0x01 },
+ { 0x8000a4, 0x5a },
+ { 0x8000a5, 0x01 },
+ { 0x8000a6, 0x01 },
+ { 0x8000a9, 0x00 },
+ { 0x8000aa, 0x01 },
+ { 0x8000b0, 0x01 },
+ { 0x8000b3, 0x02 },
+ { 0x8000b4, 0x3c },
+ { 0x8000b6, 0x14 },
+ { 0x8000c0, 0x11 },
+ { 0x8000c1, 0x00 },
+ { 0x8000c2, 0x05 },
+ { 0x8000c4, 0x00 },
+ { 0x8000c6, 0x19 },
+ { 0x8000c7, 0x00 },
+ { 0x8000cc, 0x2e },
+ { 0x8000cd, 0x51 },
+ { 0x8000ce, 0x33 },
+ { 0x8000f3, 0x05 },
+ { 0x8000f4, 0x8c },
+ { 0x8000f5, 0x8c },
+ { 0x8000f8, 0x03 },
+ { 0x8000f9, 0x06 },
+ { 0x8000fa, 0x06 },
+ { 0x8000fc, 0x03 },
+ { 0x8000fd, 0x02 },
+ { 0x8000fe, 0x02 },
+ { 0x8000ff, 0x09 },
+ { 0x800100, 0x50 },
+ { 0x800101, 0x7a },
+ { 0x800102, 0x77 },
+ { 0x800103, 0x01 },
+ { 0x800104, 0x02 },
+ { 0x800105, 0xb0 },
+ { 0x800106, 0x02 },
+ { 0x800107, 0x7a },
+ { 0x800109, 0x02 },
+ { 0x800115, 0x0a },
+ { 0x800116, 0x03 },
+ { 0x800117, 0x02 },
+ { 0x800118, 0x80 },
+ { 0x80011a, 0xc0 },
+ { 0x80011b, 0x7a },
+ { 0x80011c, 0xac },
+ { 0x80011d, 0x8c },
+ { 0x800122, 0x02 },
+ { 0x800123, 0x70 },
+ { 0x800124, 0xa4 },
+ { 0x800127, 0x00 },
+ { 0x800128, 0x07 },
+ { 0x80012a, 0x53 },
+ { 0x80012b, 0x51 },
+ { 0x80012c, 0x4e },
+ { 0x80012d, 0x43 },
+ { 0x800137, 0x01 },
+ { 0x800138, 0x00 },
+ { 0x800139, 0x07 },
+ { 0x80013a, 0x00 },
+ { 0x80013b, 0x06 },
+ { 0x80013d, 0x00 },
+ { 0x80013e, 0x01 },
+ { 0x80013f, 0x5b },
+ { 0x800140, 0xc0 },
+ { 0x800141, 0x59 },
+ { 0x80f000, 0x0f },
+ { 0x80f016, 0x10 },
+ { 0x80f017, 0x04 },
+ { 0x80f018, 0x05 },
+ { 0x80f019, 0x04 },
+ { 0x80f01a, 0x05 },
+ { 0x80f01f, 0x8c },
+ { 0x80f020, 0x00 },
+ { 0x80f021, 0x03 },
+ { 0x80f022, 0x0a },
+ { 0x80f023, 0x0a },
+ { 0x80f029, 0x8c },
+ { 0x80f02a, 0x00 },
+ { 0x80f02b, 0x00 },
+ { 0x80f02c, 0x01 },
+ { 0x80f064, 0x03 },
+ { 0x80f065, 0xf9 },
+ { 0x80f066, 0x03 },
+ { 0x80f067, 0x01 },
+ { 0x80f06f, 0xe0 },
+ { 0x80f070, 0x03 },
+ { 0x80f072, 0x0f },
+ { 0x80f073, 0x03 },
+ { 0x80f077, 0x01 },
+ { 0x80f078, 0x00 },
+ { 0x80f085, 0xc0 },
+ { 0x80f086, 0x01 },
+ { 0x80f087, 0x00 },
+ { 0x80f09b, 0x3f },
+ { 0x80f09c, 0x00 },
+ { 0x80f09d, 0x20 },
+ { 0x80f09e, 0x00 },
+ { 0x80f09f, 0x0c },
+ { 0x80f0a0, 0x00 },
+ { 0x80f130, 0x04 },
+ { 0x80f132, 0x04 },
+ { 0x80f144, 0x1a },
+ { 0x80f146, 0x00 },
+ { 0x80f14a, 0x01 },
+ { 0x80f14c, 0x00 },
+ { 0x80f14d, 0x00 },
+ { 0x80f14f, 0x04 },
+ { 0x80f158, 0x7f },
+ { 0x80f15a, 0x00 },
+ { 0x80f15b, 0x08 },
+ { 0x80f15d, 0x03 },
+ { 0x80f15e, 0x05 },
+ { 0x80f163, 0x05 },
+ { 0x80f166, 0x01 },
+ { 0x80f167, 0x40 },
+ { 0x80f168, 0x0f },
+ { 0x80f17a, 0x00 },
+ { 0x80f17b, 0x00 },
+ { 0x80f183, 0x01 },
+ { 0x80f19d, 0x40 },
+ { 0x80f1bc, 0x36 },
+ { 0x80f1bd, 0x00 },
+ { 0x80f1cb, 0xa0 },
+ { 0x80f1cc, 0x01 },
+ { 0x80f204, 0x10 },
+ { 0x80f214, 0x00 },
+ { 0x80f24c, 0x88 },
+ { 0x80f24d, 0x95 },
+ { 0x80f24e, 0x9a },
+ { 0x80f24f, 0x90 },
+ { 0x80f25a, 0x07 },
+ { 0x80f25b, 0xe8 },
+ { 0x80f25c, 0x03 },
+ { 0x80f25d, 0xb0 },
+ { 0x80f25e, 0x04 },
+ { 0x80f270, 0x01 },
+ { 0x80f271, 0x02 },
+ { 0x80f272, 0x01 },
+ { 0x80f273, 0x02 },
+ { 0x80f40e, 0x0a },
+ { 0x80f40f, 0x40 },
+ { 0x80f410, 0x08 },
+ { 0x80f55f, 0x0a },
+ { 0x80f561, 0x15 },
+ { 0x80f562, 0x20 },
+ { 0x80f5df, 0xfb },
+ { 0x80f5e0, 0x00 },
+ { 0x80f5e3, 0x09 },
+ { 0x80f5e4, 0x01 },
+ { 0x80f5e5, 0x01 },
+ { 0x80f5f8, 0x01 },
+ { 0x80f5fd, 0x01 },
+ { 0x80f600, 0x05 },
+ { 0x80f601, 0x08 },
+ { 0x80f602, 0x0b },
+ { 0x80f603, 0x0e },
+ { 0x80f604, 0x11 },
+ { 0x80f605, 0x14 },
+ { 0x80f606, 0x17 },
+ { 0x80f607, 0x1f },
+ { 0x80f60e, 0x00 },
+ { 0x80f60f, 0x04 },
+ { 0x80f610, 0x32 },
+ { 0x80f611, 0x10 },
+ { 0x80f707, 0xfc },
+ { 0x80f708, 0x00 },
+ { 0x80f709, 0x37 },
+ { 0x80f70a, 0x00 },
+ { 0x80f78b, 0x01 },
+ { 0x80f80f, 0x40 },
+ { 0x80f810, 0x54 },
+ { 0x80f811, 0x5a },
+ { 0x80f905, 0x01 },
+ { 0x80fb06, 0x03 },
+ { 0x80fd8b, 0x00 },
+};
+
+/*
+ * ITE Tech IT9133 AX Omega LNA config 2 tuner init
+ * AF9033_TUNER_IT9135_52 = 0x52
+ */
+static const struct reg_val tuner_init_it9135_52[] = {
+ { 0x800043, 0x00 },
+ { 0x800046, 0x52 },
+ { 0x800051, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x800068, 0x10 },
+ { 0x800070, 0x0a },
+ { 0x800071, 0x05 },
+ { 0x800072, 0x02 },
+ { 0x800075, 0x8c },
+ { 0x800076, 0x8c },
+ { 0x800077, 0x8c },
+ { 0x800078, 0xa0 },
+ { 0x800079, 0x01 },
+ { 0x80007e, 0x04 },
+ { 0x80007f, 0x00 },
+ { 0x800081, 0x0a },
+ { 0x800082, 0x17 },
+ { 0x800083, 0x03 },
+ { 0x800084, 0x0a },
+ { 0x800085, 0x03 },
+ { 0x800086, 0xb3 },
+ { 0x800087, 0x97 },
+ { 0x800088, 0xc0 },
+ { 0x800089, 0x9e },
+ { 0x80008a, 0x01 },
+ { 0x80008e, 0x01 },
+ { 0x800092, 0x06 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x00 },
+ { 0x800095, 0x00 },
+ { 0x800096, 0x00 },
+ { 0x800099, 0x01 },
+ { 0x80009b, 0x3c },
+ { 0x80009c, 0x28 },
+ { 0x80009f, 0xe1 },
+ { 0x8000a0, 0xcf },
+ { 0x8000a3, 0x01 },
+ { 0x8000a4, 0x5c },
+ { 0x8000a5, 0x01 },
+ { 0x8000a6, 0x01 },
+ { 0x8000a9, 0x00 },
+ { 0x8000aa, 0x01 },
+ { 0x8000b0, 0x01 },
+ { 0x8000b3, 0x02 },
+ { 0x8000b4, 0x3c },
+ { 0x8000b6, 0x14 },
+ { 0x8000c0, 0x11 },
+ { 0x8000c1, 0x00 },
+ { 0x8000c2, 0x05 },
+ { 0x8000c4, 0x00 },
+ { 0x8000c6, 0x19 },
+ { 0x8000c7, 0x00 },
+ { 0x8000cc, 0x2e },
+ { 0x8000cd, 0x51 },
+ { 0x8000ce, 0x33 },
+ { 0x8000f3, 0x05 },
+ { 0x8000f4, 0x91 },
+ { 0x8000f5, 0x8c },
+ { 0x8000f8, 0x03 },
+ { 0x8000f9, 0x06 },
+ { 0x8000fa, 0x06 },
+ { 0x8000fc, 0x03 },
+ { 0x8000fd, 0x02 },
+ { 0x8000fe, 0x02 },
+ { 0x8000ff, 0x09 },
+ { 0x800100, 0x50 },
+ { 0x800101, 0x74 },
+ { 0x800102, 0x77 },
+ { 0x800103, 0x02 },
+ { 0x800104, 0x02 },
+ { 0x800105, 0xa4 },
+ { 0x800106, 0x02 },
+ { 0x800107, 0x6e },
+ { 0x800109, 0x02 },
+ { 0x800115, 0x0a },
+ { 0x800116, 0x03 },
+ { 0x800117, 0x02 },
+ { 0x800118, 0x80 },
+ { 0x80011a, 0xcd },
+ { 0x80011b, 0x62 },
+ { 0x80011c, 0xa4 },
+ { 0x80011d, 0x8c },
+ { 0x800122, 0x03 },
+ { 0x800123, 0x18 },
+ { 0x800124, 0x9e },
+ { 0x800127, 0x00 },
+ { 0x800128, 0x07 },
+ { 0x80012a, 0x53 },
+ { 0x80012b, 0x51 },
+ { 0x80012c, 0x4e },
+ { 0x80012d, 0x43 },
+ { 0x800137, 0x00 },
+ { 0x800138, 0x00 },
+ { 0x800139, 0x07 },
+ { 0x80013a, 0x00 },
+ { 0x80013b, 0x06 },
+ { 0x80013d, 0x00 },
+ { 0x80013e, 0x01 },
+ { 0x80013f, 0x5b },
+ { 0x800140, 0xb6 },
+ { 0x800141, 0x59 },
+ { 0x80f000, 0x0f },
+ { 0x80f016, 0x10 },
+ { 0x80f017, 0x04 },
+ { 0x80f018, 0x05 },
+ { 0x80f019, 0x04 },
+ { 0x80f01a, 0x05 },
+ { 0x80f01f, 0x8c },
+ { 0x80f020, 0x00 },
+ { 0x80f021, 0x03 },
+ { 0x80f022, 0x0a },
+ { 0x80f023, 0x0a },
+ { 0x80f029, 0x8c },
+ { 0x80f02a, 0x00 },
+ { 0x80f02b, 0x00 },
+ { 0x80f02c, 0x01 },
+ { 0x80f064, 0x03 },
+ { 0x80f065, 0xf9 },
+ { 0x80f066, 0x03 },
+ { 0x80f067, 0x01 },
+ { 0x80f06f, 0xe0 },
+ { 0x80f070, 0x03 },
+ { 0x80f072, 0x0f },
+ { 0x80f073, 0x03 },
+ { 0x80f077, 0x01 },
+ { 0x80f078, 0x00 },
+ { 0x80f085, 0xc0 },
+ { 0x80f086, 0x01 },
+ { 0x80f087, 0x00 },
+ { 0x80f09b, 0x3f },
+ { 0x80f09c, 0x00 },
+ { 0x80f09d, 0x20 },
+ { 0x80f09e, 0x00 },
+ { 0x80f09f, 0x0c },
+ { 0x80f0a0, 0x00 },
+ { 0x80f130, 0x04 },
+ { 0x80f132, 0x04 },
+ { 0x80f144, 0x1a },
+ { 0x80f146, 0x00 },
+ { 0x80f14a, 0x01 },
+ { 0x80f14c, 0x00 },
+ { 0x80f14d, 0x00 },
+ { 0x80f14f, 0x04 },
+ { 0x80f158, 0x7f },
+ { 0x80f15a, 0x00 },
+ { 0x80f15b, 0x08 },
+ { 0x80f15d, 0x03 },
+ { 0x80f15e, 0x05 },
+ { 0x80f163, 0x05 },
+ { 0x80f166, 0x01 },
+ { 0x80f167, 0x40 },
+ { 0x80f168, 0x0f },
+ { 0x80f17a, 0x00 },
+ { 0x80f17b, 0x00 },
+ { 0x80f183, 0x01 },
+ { 0x80f19d, 0x40 },
+ { 0x80f1bc, 0x36 },
+ { 0x80f1bd, 0x00 },
+ { 0x80f1cb, 0xa0 },
+ { 0x80f1cc, 0x01 },
+ { 0x80f204, 0x10 },
+ { 0x80f214, 0x00 },
+ { 0x80f24c, 0x88 },
+ { 0x80f24d, 0x95 },
+ { 0x80f24e, 0x9a },
+ { 0x80f24f, 0x90 },
+ { 0x80f25a, 0x07 },
+ { 0x80f25b, 0xe8 },
+ { 0x80f25c, 0x03 },
+ { 0x80f25d, 0xb0 },
+ { 0x80f25e, 0x04 },
+ { 0x80f270, 0x01 },
+ { 0x80f271, 0x02 },
+ { 0x80f272, 0x01 },
+ { 0x80f273, 0x02 },
+ { 0x80f40e, 0x0a },
+ { 0x80f40f, 0x40 },
+ { 0x80f410, 0x08 },
+ { 0x80f55f, 0x0a },
+ { 0x80f561, 0x15 },
+ { 0x80f562, 0x20 },
+ { 0x80f5df, 0xfb },
+ { 0x80f5e0, 0x00 },
+ { 0x80f5e3, 0x09 },
+ { 0x80f5e4, 0x01 },
+ { 0x80f5e5, 0x01 },
+ { 0x80f5f8, 0x01 },
+ { 0x80f5fd, 0x01 },
+ { 0x80f600, 0x05 },
+ { 0x80f601, 0x08 },
+ { 0x80f602, 0x0b },
+ { 0x80f603, 0x0e },
+ { 0x80f604, 0x11 },
+ { 0x80f605, 0x14 },
+ { 0x80f606, 0x17 },
+ { 0x80f607, 0x1f },
+ { 0x80f60e, 0x00 },
+ { 0x80f60f, 0x04 },
+ { 0x80f610, 0x32 },
+ { 0x80f611, 0x10 },
+ { 0x80f707, 0xfc },
+ { 0x80f708, 0x00 },
+ { 0x80f709, 0x37 },
+ { 0x80f70a, 0x00 },
+ { 0x80f78b, 0x01 },
+ { 0x80f80f, 0x40 },
+ { 0x80f810, 0x54 },
+ { 0x80f811, 0x5a },
+ { 0x80f905, 0x01 },
+ { 0x80fb06, 0x03 },
+ { 0x80fd8b, 0x00 },
+};
+
+/*
+ * ITE Tech IT9133 BX demod init
+ */
+static const struct reg_val ofsm_init_it9135_v2[] = {
+ { 0x800051, 0x01 },
+ { 0x800070, 0x0a },
+ { 0x80007e, 0x04 },
+ { 0x800081, 0x0a },
+ { 0x80008a, 0x01 },
+ { 0x80008e, 0x01 },
+ { 0x800092, 0x06 },
+ { 0x800099, 0x01 },
+ { 0x80009f, 0xe1 },
+ { 0x8000a0, 0xcf },
+ { 0x8000a3, 0x01 },
+ { 0x8000a5, 0x01 },
+ { 0x8000a6, 0x01 },
+ { 0x8000a9, 0x00 },
+ { 0x8000aa, 0x01 },
+ { 0x8000b0, 0x01 },
+ { 0x8000c2, 0x05 },
+ { 0x8000c6, 0x19 },
+ { 0x80f000, 0x0f },
+ { 0x80f02b, 0x00 },
+ { 0x80f064, 0x03 },
+ { 0x80f065, 0xf9 },
+ { 0x80f066, 0x03 },
+ { 0x80f067, 0x01 },
+ { 0x80f06f, 0xe0 },
+ { 0x80f070, 0x03 },
+ { 0x80f072, 0x0f },
+ { 0x80f073, 0x03 },
+ { 0x80f078, 0x00 },
+ { 0x80f087, 0x00 },
+ { 0x80f09b, 0x3f },
+ { 0x80f09c, 0x00 },
+ { 0x80f09d, 0x20 },
+ { 0x80f09e, 0x00 },
+ { 0x80f09f, 0x0c },
+ { 0x80f0a0, 0x00 },
+ { 0x80f130, 0x04 },
+ { 0x80f132, 0x04 },
+ { 0x80f144, 0x1a },
+ { 0x80f146, 0x00 },
+ { 0x80f14a, 0x01 },
+ { 0x80f14c, 0x00 },
+ { 0x80f14d, 0x00 },
+ { 0x80f14f, 0x04 },
+ { 0x80f158, 0x7f },
+ { 0x80f15a, 0x00 },
+ { 0x80f15b, 0x08 },
+ { 0x80f15d, 0x03 },
+ { 0x80f15e, 0x05 },
+ { 0x80f163, 0x05 },
+ { 0x80f166, 0x01 },
+ { 0x80f167, 0x40 },
+ { 0x80f168, 0x0f },
+ { 0x80f17a, 0x00 },
+ { 0x80f17b, 0x00 },
+ { 0x80f183, 0x01 },
+ { 0x80f19d, 0x40 },
+ { 0x80f1bc, 0x36 },
+ { 0x80f1bd, 0x00 },
+ { 0x80f1cb, 0xa0 },
+ { 0x80f1cc, 0x01 },
+ { 0x80f204, 0x10 },
+ { 0x80f214, 0x00 },
+ { 0x80f40e, 0x0a },
+ { 0x80f40f, 0x40 },
+ { 0x80f410, 0x08 },
+ { 0x80f55f, 0x0a },
+ { 0x80f561, 0x15 },
+ { 0x80f562, 0x20 },
+ { 0x80f5e3, 0x09 },
+ { 0x80f5e4, 0x01 },
+ { 0x80f5e5, 0x01 },
+ { 0x80f600, 0x05 },
+ { 0x80f601, 0x08 },
+ { 0x80f602, 0x0b },
+ { 0x80f603, 0x0e },
+ { 0x80f604, 0x11 },
+ { 0x80f605, 0x14 },
+ { 0x80f606, 0x17 },
+ { 0x80f607, 0x1f },
+ { 0x80f60e, 0x00 },
+ { 0x80f60f, 0x04 },
+ { 0x80f610, 0x32 },
+ { 0x80f611, 0x10 },
+ { 0x80f707, 0xfc },
+ { 0x80f708, 0x00 },
+ { 0x80f709, 0x37 },
+ { 0x80f70a, 0x00 },
+ { 0x80f78b, 0x01 },
+ { 0x80f80f, 0x40 },
+ { 0x80f810, 0x54 },
+ { 0x80f811, 0x5a },
+ { 0x80f905, 0x01 },
+ { 0x80fb06, 0x03 },
+ { 0x80fd8b, 0x00 },
+};
+
+/*
+ * ITE Tech IT9133 BX Omega tuner init
+ * AF9033_TUNER_IT9135_60 = 0x60
+ */
+static const struct reg_val tuner_init_it9135_60[] = {
+ { 0x800043, 0x00 },
+ { 0x800046, 0x60 },
+ { 0x800051, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x800068, 0x0a },
+ { 0x80006a, 0x03 },
+ { 0x800070, 0x0a },
+ { 0x800071, 0x0a },
+ { 0x800072, 0x02 },
+ { 0x800075, 0x8c },
+ { 0x800076, 0x8c },
+ { 0x800077, 0x8c },
+ { 0x800078, 0x8c },
+ { 0x800079, 0x01 },
+ { 0x80007e, 0x04 },
+ { 0x800081, 0x0a },
+ { 0x800082, 0x18 },
+ { 0x800084, 0x0a },
+ { 0x800085, 0x33 },
+ { 0x800086, 0xbe },
+ { 0x800087, 0xa0 },
+ { 0x800088, 0xc6 },
+ { 0x800089, 0xb6 },
+ { 0x80008a, 0x01 },
+ { 0x80008e, 0x01 },
+ { 0x800092, 0x06 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x00 },
+ { 0x800095, 0x00 },
+ { 0x800096, 0x00 },
+ { 0x800099, 0x01 },
+ { 0x80009b, 0x3c },
+ { 0x80009c, 0x28 },
+ { 0x80009f, 0xe1 },
+ { 0x8000a0, 0xcf },
+ { 0x8000a3, 0x01 },
+ { 0x8000a4, 0x5a },
+ { 0x8000a5, 0x01 },
+ { 0x8000a6, 0x01 },
+ { 0x8000a9, 0x00 },
+ { 0x8000aa, 0x01 },
+ { 0x8000b0, 0x01 },
+ { 0x8000b3, 0x02 },
+ { 0x8000b4, 0x3a },
+ { 0x8000b6, 0x14 },
+ { 0x8000c0, 0x11 },
+ { 0x8000c1, 0x00 },
+ { 0x8000c2, 0x05 },
+ { 0x8000c3, 0x01 },
+ { 0x8000c4, 0x00 },
+ { 0x8000c6, 0x19 },
+ { 0x8000c7, 0x00 },
+ { 0x8000cb, 0x32 },
+ { 0x8000cc, 0x2c },
+ { 0x8000cd, 0x4f },
+ { 0x8000ce, 0x30 },
+ { 0x8000f3, 0x05 },
+ { 0x8000f4, 0xa0 },
+ { 0x8000f5, 0x8c },
+ { 0x8000f8, 0x03 },
+ { 0x8000f9, 0x06 },
+ { 0x8000fa, 0x06 },
+ { 0x8000fc, 0x03 },
+ { 0x8000fd, 0x03 },
+ { 0x8000fe, 0x02 },
+ { 0x8000ff, 0x0a },
+ { 0x800100, 0x50 },
+ { 0x800101, 0x7b },
+ { 0x800102, 0x8c },
+ { 0x800103, 0x00 },
+ { 0x800104, 0x02 },
+ { 0x800105, 0xbe },
+ { 0x800106, 0x00 },
+ { 0x800115, 0x0a },
+ { 0x800116, 0x03 },
+ { 0x80011a, 0xbe },
+ { 0x800124, 0xae },
+ { 0x800127, 0x00 },
+ { 0x80012a, 0x56 },
+ { 0x80012b, 0x50 },
+ { 0x80012c, 0x47 },
+ { 0x80012d, 0x42 },
+ { 0x800137, 0x00 },
+ { 0x80013b, 0x08 },
+ { 0x80013f, 0x5b },
+ { 0x800141, 0x59 },
+ { 0x800142, 0xf9 },
+ { 0x800143, 0x19 },
+ { 0x800144, 0x00 },
+ { 0x800145, 0x8c },
+ { 0x800146, 0x8c },
+ { 0x800147, 0x8c },
+ { 0x800148, 0x6e },
+ { 0x800149, 0x8c },
+ { 0x80014a, 0x50 },
+ { 0x80014b, 0x8c },
+ { 0x80014d, 0xac },
+ { 0x80014e, 0xc6 },
+ { 0x800151, 0x1e },
+ { 0x800153, 0xbc },
+ { 0x800178, 0x09 },
+ { 0x800181, 0x94 },
+ { 0x800182, 0x6e },
+ { 0x800185, 0x24 },
+ { 0x800189, 0xbe },
+ { 0x80018c, 0x03 },
+ { 0x80018d, 0x5f },
+ { 0x80018f, 0xa0 },
+ { 0x800190, 0x5a },
+ { 0x800191, 0x00 },
+ { 0x80ed02, 0x40 },
+ { 0x80ee42, 0x40 },
+ { 0x80ee82, 0x40 },
+ { 0x80f000, 0x0f },
+ { 0x80f01f, 0x8c },
+ { 0x80f020, 0x00 },
+ { 0x80f029, 0x8c },
+ { 0x80f02a, 0x00 },
+ { 0x80f02b, 0x00 },
+ { 0x80f064, 0x03 },
+ { 0x80f065, 0xf9 },
+ { 0x80f066, 0x03 },
+ { 0x80f067, 0x01 },
+ { 0x80f06f, 0xe0 },
+ { 0x80f070, 0x03 },
+ { 0x80f072, 0x0f },
+ { 0x80f073, 0x03 },
+ { 0x80f077, 0x01 },
+ { 0x80f078, 0x00 },
+ { 0x80f087, 0x00 },
+ { 0x80f09b, 0x3f },
+ { 0x80f09c, 0x00 },
+ { 0x80f09d, 0x20 },
+ { 0x80f09e, 0x00 },
+ { 0x80f09f, 0x0c },
+ { 0x80f0a0, 0x00 },
+ { 0x80f130, 0x04 },
+ { 0x80f132, 0x04 },
+ { 0x80f144, 0x1a },
+ { 0x80f146, 0x00 },
+ { 0x80f14a, 0x01 },
+ { 0x80f14c, 0x00 },
+ { 0x80f14d, 0x00 },
+ { 0x80f14f, 0x04 },
+ { 0x80f158, 0x7f },
+ { 0x80f15a, 0x00 },
+ { 0x80f15b, 0x08 },
+ { 0x80f15d, 0x03 },
+ { 0x80f15e, 0x05 },
+ { 0x80f163, 0x05 },
+ { 0x80f166, 0x01 },
+ { 0x80f167, 0x40 },
+ { 0x80f168, 0x0f },
+ { 0x80f17a, 0x00 },
+ { 0x80f17b, 0x00 },
+ { 0x80f183, 0x01 },
+ { 0x80f19d, 0x40 },
+ { 0x80f1bc, 0x36 },
+ { 0x80f1bd, 0x00 },
+ { 0x80f1cb, 0xa0 },
+ { 0x80f1cc, 0x01 },
+ { 0x80f204, 0x10 },
+ { 0x80f214, 0x00 },
+ { 0x80f24c, 0x88 },
+ { 0x80f24d, 0x95 },
+ { 0x80f24e, 0x9a },
+ { 0x80f24f, 0x90 },
+ { 0x80f25a, 0x07 },
+ { 0x80f25b, 0xe8 },
+ { 0x80f25c, 0x03 },
+ { 0x80f25d, 0xb0 },
+ { 0x80f25e, 0x04 },
+ { 0x80f270, 0x01 },
+ { 0x80f271, 0x02 },
+ { 0x80f272, 0x01 },
+ { 0x80f273, 0x02 },
+ { 0x80f40e, 0x0a },
+ { 0x80f40f, 0x40 },
+ { 0x80f410, 0x08 },
+ { 0x80f55f, 0x0a },
+ { 0x80f561, 0x15 },
+ { 0x80f562, 0x20 },
+ { 0x80f5e3, 0x09 },
+ { 0x80f5e4, 0x01 },
+ { 0x80f5e5, 0x01 },
+ { 0x80f600, 0x05 },
+ { 0x80f601, 0x08 },
+ { 0x80f602, 0x0b },
+ { 0x80f603, 0x0e },
+ { 0x80f604, 0x11 },
+ { 0x80f605, 0x14 },
+ { 0x80f606, 0x17 },
+ { 0x80f607, 0x1f },
+ { 0x80f60e, 0x00 },
+ { 0x80f60f, 0x04 },
+ { 0x80f610, 0x32 },
+ { 0x80f611, 0x10 },
+ { 0x80f707, 0xfc },
+ { 0x80f708, 0x00 },
+ { 0x80f709, 0x37 },
+ { 0x80f70a, 0x00 },
+ { 0x80f78b, 0x01 },
+ { 0x80f80f, 0x40 },
+ { 0x80f810, 0x54 },
+ { 0x80f811, 0x5a },
+ { 0x80f905, 0x01 },
+ { 0x80fb06, 0x03 },
+ { 0x80fd8b, 0x00 },
+};
+
+/*
+ * ITE Tech IT9133 BX Omega LNA config 1 tuner init
+ * AF9033_TUNER_IT9135_61 = 0x61
+ */
+static const struct reg_val tuner_init_it9135_61[] = {
+ { 0x800043, 0x00 },
+ { 0x800046, 0x61 },
+ { 0x800051, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x800068, 0x06 },
+ { 0x80006a, 0x03 },
+ { 0x800070, 0x0a },
+ { 0x800071, 0x05 },
+ { 0x800072, 0x02 },
+ { 0x800075, 0x8c },
+ { 0x800076, 0x8c },
+ { 0x800077, 0x8c },
+ { 0x800078, 0x90 },
+ { 0x800079, 0x01 },
+ { 0x80007e, 0x04 },
+ { 0x800081, 0x0a },
+ { 0x800082, 0x12 },
+ { 0x800084, 0x0a },
+ { 0x800085, 0x33 },
+ { 0x800086, 0xbc },
+ { 0x800087, 0x9c },
+ { 0x800088, 0xcc },
+ { 0x800089, 0xa8 },
+ { 0x80008a, 0x01 },
+ { 0x80008e, 0x01 },
+ { 0x800092, 0x06 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x00 },
+ { 0x800095, 0x00 },
+ { 0x800096, 0x00 },
+ { 0x800099, 0x01 },
+ { 0x80009b, 0x3c },
+ { 0x80009c, 0x28 },
+ { 0x80009f, 0xe1 },
+ { 0x8000a0, 0xcf },
+ { 0x8000a3, 0x01 },
+ { 0x8000a4, 0x5c },
+ { 0x8000a5, 0x01 },
+ { 0x8000a6, 0x01 },
+ { 0x8000a9, 0x00 },
+ { 0x8000aa, 0x01 },
+ { 0x8000b0, 0x01 },
+ { 0x8000b3, 0x02 },
+ { 0x8000b4, 0x3a },
+ { 0x8000b6, 0x14 },
+ { 0x8000c0, 0x11 },
+ { 0x8000c1, 0x00 },
+ { 0x8000c2, 0x05 },
+ { 0x8000c3, 0x01 },
+ { 0x8000c4, 0x00 },
+ { 0x8000c6, 0x19 },
+ { 0x8000c7, 0x00 },
+ { 0x8000cb, 0x32 },
+ { 0x8000cc, 0x2c },
+ { 0x8000cd, 0x4f },
+ { 0x8000ce, 0x30 },
+ { 0x8000f3, 0x05 },
+ { 0x8000f4, 0xa0 },
+ { 0x8000f5, 0x8c },
+ { 0x8000f8, 0x03 },
+ { 0x8000f9, 0x06 },
+ { 0x8000fa, 0x06 },
+ { 0x8000fc, 0x03 },
+ { 0x8000fd, 0x03 },
+ { 0x8000fe, 0x02 },
+ { 0x8000ff, 0x08 },
+ { 0x800100, 0x50 },
+ { 0x800101, 0x7b },
+ { 0x800102, 0x8c },
+ { 0x800103, 0x01 },
+ { 0x800104, 0x02 },
+ { 0x800105, 0xc8 },
+ { 0x800106, 0x00 },
+ { 0x800115, 0x0a },
+ { 0x800116, 0x03 },
+ { 0x80011a, 0xc6 },
+ { 0x800124, 0xa8 },
+ { 0x800127, 0x00 },
+ { 0x80012a, 0x59 },
+ { 0x80012b, 0x50 },
+ { 0x80012c, 0x47 },
+ { 0x80012d, 0x42 },
+ { 0x800137, 0x00 },
+ { 0x80013b, 0x05 },
+ { 0x80013f, 0x5b },
+ { 0x800141, 0x59 },
+ { 0x800142, 0xf9 },
+ { 0x800143, 0x59 },
+ { 0x800144, 0x01 },
+ { 0x800145, 0x8c },
+ { 0x800146, 0x8c },
+ { 0x800147, 0x8c },
+ { 0x800148, 0x7b },
+ { 0x800149, 0x8c },
+ { 0x80014a, 0x50 },
+ { 0x80014b, 0x8c },
+ { 0x80014d, 0xa8 },
+ { 0x80014e, 0xc6 },
+ { 0x800151, 0x28 },
+ { 0x800153, 0xcc },
+ { 0x800178, 0x09 },
+ { 0x800181, 0x9c },
+ { 0x800182, 0x76 },
+ { 0x800185, 0x28 },
+ { 0x800189, 0xaa },
+ { 0x80018c, 0x03 },
+ { 0x80018d, 0x5f },
+ { 0x80018f, 0xfb },
+ { 0x800190, 0x5c },
+ { 0x800191, 0x00 },
+ { 0x80ed02, 0x40 },
+ { 0x80ee42, 0x40 },
+ { 0x80ee82, 0x40 },
+ { 0x80f000, 0x0f },
+ { 0x80f01f, 0x8c },
+ { 0x80f020, 0x00 },
+ { 0x80f029, 0x8c },
+ { 0x80f02a, 0x00 },
+ { 0x80f02b, 0x00 },
+ { 0x80f064, 0x03 },
+ { 0x80f065, 0xf9 },
+ { 0x80f066, 0x03 },
+ { 0x80f067, 0x01 },
+ { 0x80f06f, 0xe0 },
+ { 0x80f070, 0x03 },
+ { 0x80f072, 0x0f },
+ { 0x80f073, 0x03 },
+ { 0x80f077, 0x01 },
+ { 0x80f078, 0x00 },
+ { 0x80f087, 0x00 },
+ { 0x80f09b, 0x3f },
+ { 0x80f09c, 0x00 },
+ { 0x80f09d, 0x20 },
+ { 0x80f09e, 0x00 },
+ { 0x80f09f, 0x0c },
+ { 0x80f0a0, 0x00 },
+ { 0x80f130, 0x04 },
+ { 0x80f132, 0x04 },
+ { 0x80f144, 0x1a },
+ { 0x80f146, 0x00 },
+ { 0x80f14a, 0x01 },
+ { 0x80f14c, 0x00 },
+ { 0x80f14d, 0x00 },
+ { 0x80f14f, 0x04 },
+ { 0x80f158, 0x7f },
+ { 0x80f15a, 0x00 },
+ { 0x80f15b, 0x08 },
+ { 0x80f15d, 0x03 },
+ { 0x80f15e, 0x05 },
+ { 0x80f163, 0x05 },
+ { 0x80f166, 0x01 },
+ { 0x80f167, 0x40 },
+ { 0x80f168, 0x0f },
+ { 0x80f17a, 0x00 },
+ { 0x80f17b, 0x00 },
+ { 0x80f183, 0x01 },
+ { 0x80f19d, 0x40 },
+ { 0x80f1bc, 0x36 },
+ { 0x80f1bd, 0x00 },
+ { 0x80f1cb, 0xa0 },
+ { 0x80f1cc, 0x01 },
+ { 0x80f204, 0x10 },
+ { 0x80f214, 0x00 },
+ { 0x80f24c, 0x88 },
+ { 0x80f24d, 0x95 },
+ { 0x80f24e, 0x9a },
+ { 0x80f24f, 0x90 },
+ { 0x80f25a, 0x07 },
+ { 0x80f25b, 0xe8 },
+ { 0x80f25c, 0x03 },
+ { 0x80f25d, 0xb0 },
+ { 0x80f25e, 0x04 },
+ { 0x80f270, 0x01 },
+ { 0x80f271, 0x02 },
+ { 0x80f272, 0x01 },
+ { 0x80f273, 0x02 },
+ { 0x80f40e, 0x0a },
+ { 0x80f40f, 0x40 },
+ { 0x80f410, 0x08 },
+ { 0x80f55f, 0x0a },
+ { 0x80f561, 0x15 },
+ { 0x80f562, 0x20 },
+ { 0x80f5e3, 0x09 },
+ { 0x80f5e4, 0x01 },
+ { 0x80f5e5, 0x01 },
+ { 0x80f600, 0x05 },
+ { 0x80f601, 0x08 },
+ { 0x80f602, 0x0b },
+ { 0x80f603, 0x0e },
+ { 0x80f604, 0x11 },
+ { 0x80f605, 0x14 },
+ { 0x80f606, 0x17 },
+ { 0x80f607, 0x1f },
+ { 0x80f60e, 0x00 },
+ { 0x80f60f, 0x04 },
+ { 0x80f610, 0x32 },
+ { 0x80f611, 0x10 },
+ { 0x80f707, 0xfc },
+ { 0x80f708, 0x00 },
+ { 0x80f709, 0x37 },
+ { 0x80f70a, 0x00 },
+ { 0x80f78b, 0x01 },
+ { 0x80f80f, 0x40 },
+ { 0x80f810, 0x54 },
+ { 0x80f811, 0x5a },
+ { 0x80f905, 0x01 },
+ { 0x80fb06, 0x03 },
+ { 0x80fd8b, 0x00 },
+};
+
+/*
+ * ITE Tech IT9133 BX Omega LNA config 2 tuner init
+ * AF9033_TUNER_IT9135_62 = 0x62
+ */
+static const struct reg_val tuner_init_it9135_62[] = {
+ { 0x800043, 0x00 },
+ { 0x800046, 0x62 },
+ { 0x800051, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x800068, 0x0a },
+ { 0x80006a, 0x03 },
+ { 0x800070, 0x0a },
+ { 0x800071, 0x05 },
+ { 0x800072, 0x02 },
+ { 0x800075, 0x8c },
+ { 0x800076, 0x8c },
+ { 0x800077, 0x8c },
+ { 0x800078, 0x8c },
+ { 0x800079, 0x01 },
+ { 0x80007e, 0x04 },
+ { 0x800081, 0x0a },
+ { 0x800082, 0x12 },
+ { 0x800084, 0x0a },
+ { 0x800085, 0x33 },
+ { 0x800086, 0xb8 },
+ { 0x800087, 0x9c },
+ { 0x800088, 0xb2 },
+ { 0x800089, 0xa6 },
+ { 0x80008a, 0x01 },
+ { 0x80008e, 0x01 },
+ { 0x800092, 0x06 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x00 },
+ { 0x800095, 0x00 },
+ { 0x800096, 0x00 },
+ { 0x800099, 0x01 },
+ { 0x80009b, 0x3c },
+ { 0x80009c, 0x28 },
+ { 0x80009f, 0xe1 },
+ { 0x8000a0, 0xcf },
+ { 0x8000a3, 0x01 },
+ { 0x8000a4, 0x5a },
+ { 0x8000a5, 0x01 },
+ { 0x8000a6, 0x01 },
+ { 0x8000a9, 0x00 },
+ { 0x8000aa, 0x01 },
+ { 0x8000b0, 0x01 },
+ { 0x8000b3, 0x02 },
+ { 0x8000b4, 0x3a },
+ { 0x8000b6, 0x14 },
+ { 0x8000c0, 0x11 },
+ { 0x8000c1, 0x00 },
+ { 0x8000c2, 0x05 },
+ { 0x8000c3, 0x01 },
+ { 0x8000c4, 0x00 },
+ { 0x8000c6, 0x19 },
+ { 0x8000c7, 0x00 },
+ { 0x8000cb, 0x32 },
+ { 0x8000cc, 0x2c },
+ { 0x8000cd, 0x4f },
+ { 0x8000ce, 0x30 },
+ { 0x8000f3, 0x05 },
+ { 0x8000f4, 0x8c },
+ { 0x8000f5, 0x8c },
+ { 0x8000f8, 0x03 },
+ { 0x8000f9, 0x06 },
+ { 0x8000fa, 0x06 },
+ { 0x8000fc, 0x02 },
+ { 0x8000fd, 0x03 },
+ { 0x8000fe, 0x02 },
+ { 0x8000ff, 0x09 },
+ { 0x800100, 0x50 },
+ { 0x800101, 0x6e },
+ { 0x800102, 0x8c },
+ { 0x800103, 0x02 },
+ { 0x800104, 0x02 },
+ { 0x800105, 0xc2 },
+ { 0x800106, 0x00 },
+ { 0x800109, 0x02 },
+ { 0x800115, 0x0a },
+ { 0x800116, 0x03 },
+ { 0x80011a, 0xb8 },
+ { 0x800124, 0xa8 },
+ { 0x800127, 0x00 },
+ { 0x80012a, 0x53 },
+ { 0x80012b, 0x51 },
+ { 0x80012c, 0x4e },
+ { 0x80012d, 0x43 },
+ { 0x800137, 0x00 },
+ { 0x80013b, 0x05 },
+ { 0x80013f, 0x5b },
+ { 0x800141, 0x59 },
+ { 0x800142, 0xf9 },
+ { 0x800143, 0x59 },
+ { 0x800144, 0x00 },
+ { 0x800145, 0x8c },
+ { 0x800146, 0x8c },
+ { 0x800147, 0x8c },
+ { 0x800148, 0x7b },
+ { 0x800149, 0x8c },
+ { 0x80014a, 0x50 },
+ { 0x80014b, 0x70 },
+ { 0x80014d, 0x96 },
+ { 0x80014e, 0xd0 },
+ { 0x80014f, 0x03 },
+ { 0x800151, 0x28 },
+ { 0x800153, 0xb2 },
+ { 0x800178, 0x09 },
+ { 0x800181, 0x9c },
+ { 0x800182, 0x6e },
+ { 0x800185, 0x24 },
+ { 0x800189, 0xb8 },
+ { 0x80018c, 0x03 },
+ { 0x80018d, 0x5f },
+ { 0x80018f, 0xfb },
+ { 0x800190, 0x5a },
+ { 0x80ed02, 0xff },
+ { 0x80ee42, 0xff },
+ { 0x80ee82, 0xff },
+ { 0x80f000, 0x0f },
+ { 0x80f01f, 0x8c },
+ { 0x80f020, 0x00 },
+ { 0x80f029, 0x8c },
+ { 0x80f02a, 0x00 },
+ { 0x80f02b, 0x00 },
+ { 0x80f064, 0x03 },
+ { 0x80f065, 0xf9 },
+ { 0x80f066, 0x03 },
+ { 0x80f067, 0x01 },
+ { 0x80f06f, 0xe0 },
+ { 0x80f070, 0x03 },
+ { 0x80f072, 0x0f },
+ { 0x80f073, 0x03 },
+ { 0x80f077, 0x01 },
+ { 0x80f078, 0x00 },
+ { 0x80f087, 0x00 },
+ { 0x80f09b, 0x3f },
+ { 0x80f09c, 0x00 },
+ { 0x80f09d, 0x20 },
+ { 0x80f09e, 0x00 },
+ { 0x80f09f, 0x0c },
+ { 0x80f0a0, 0x00 },
+ { 0x80f130, 0x04 },
+ { 0x80f132, 0x04 },
+ { 0x80f144, 0x1a },
+ { 0x80f146, 0x00 },
+ { 0x80f14a, 0x01 },
+ { 0x80f14c, 0x00 },
+ { 0x80f14d, 0x00 },
+ { 0x80f14f, 0x04 },
+ { 0x80f158, 0x7f },
+ { 0x80f15a, 0x00 },
+ { 0x80f15b, 0x08 },
+ { 0x80f15d, 0x03 },
+ { 0x80f15e, 0x05 },
+ { 0x80f163, 0x05 },
+ { 0x80f166, 0x01 },
+ { 0x80f167, 0x40 },
+ { 0x80f168, 0x0f },
+ { 0x80f17a, 0x00 },
+ { 0x80f17b, 0x00 },
+ { 0x80f183, 0x01 },
+ { 0x80f19d, 0x40 },
+ { 0x80f1bc, 0x36 },
+ { 0x80f1bd, 0x00 },
+ { 0x80f1cb, 0xa0 },
+ { 0x80f1cc, 0x01 },
+ { 0x80f204, 0x10 },
+ { 0x80f214, 0x00 },
+ { 0x80f24c, 0x88 },
+ { 0x80f24d, 0x95 },
+ { 0x80f24e, 0x9a },
+ { 0x80f24f, 0x90 },
+ { 0x80f25a, 0x07 },
+ { 0x80f25b, 0xe8 },
+ { 0x80f25c, 0x03 },
+ { 0x80f25d, 0xb0 },
+ { 0x80f25e, 0x04 },
+ { 0x80f270, 0x01 },
+ { 0x80f271, 0x02 },
+ { 0x80f272, 0x01 },
+ { 0x80f273, 0x02 },
+ { 0x80f40e, 0x0a },
+ { 0x80f40f, 0x40 },
+ { 0x80f410, 0x08 },
+ { 0x80f55f, 0x0a },
+ { 0x80f561, 0x15 },
+ { 0x80f562, 0x20 },
+ { 0x80f5e3, 0x09 },
+ { 0x80f5e4, 0x01 },
+ { 0x80f5e5, 0x01 },
+ { 0x80f600, 0x05 },
+ { 0x80f601, 0x08 },
+ { 0x80f602, 0x0b },
+ { 0x80f603, 0x0e },
+ { 0x80f604, 0x11 },
+ { 0x80f605, 0x14 },
+ { 0x80f606, 0x17 },
+ { 0x80f607, 0x1f },
+ { 0x80f60e, 0x00 },
+ { 0x80f60f, 0x04 },
+ { 0x80f610, 0x32 },
+ { 0x80f611, 0x10 },
+ { 0x80f707, 0xfc },
+ { 0x80f708, 0x00 },
+ { 0x80f709, 0x37 },
+ { 0x80f70a, 0x00 },
+ { 0x80f78b, 0x01 },
+ { 0x80f80f, 0x40 },
+ { 0x80f810, 0x54 },
+ { 0x80f811, 0x5a },
+ { 0x80f905, 0x01 },
+ { 0x80fb06, 0x03 },
+ { 0x80fd8b, 0x00 },
+};
+
+/* NorDig power reference table */
+static const int power_reference[][5] = {
+ {-93, -91, -90, -89, -88}, /* QPSK 1/2 ~ 7/8 */
+ {-87, -85, -84, -83, -82}, /* 16QAM 1/2 ~ 7/8 */
+ {-82, -80, -78, -77, -76}, /* 64QAM 1/2 ~ 7/8 */
+};
+#endif /* AF9033_PRIV_H */
diff --git a/drivers/media/dvb-frontends/as102_fe.c b/drivers/media/dvb-frontends/as102_fe.c
new file mode 100644
index 000000000000..bc72d954dc1f
--- /dev/null
+++ b/drivers/media/dvb-frontends/as102_fe.c
@@ -0,0 +1,471 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ */
+
+#include <media/dvb_frontend.h>
+
+#include "as102_fe.h"
+
+struct as102_state {
+ struct dvb_frontend frontend;
+ struct as10x_demod_stats demod_stats;
+
+ const struct as102_fe_ops *ops;
+ void *priv;
+ uint8_t elna_cfg;
+
+ /* signal strength */
+ uint16_t signal_strength;
+ /* bit error rate */
+ uint32_t ber;
+};
+
+static uint8_t as102_fe_get_code_rate(enum fe_code_rate arg)
+{
+ uint8_t c;
+
+ switch (arg) {
+ case FEC_1_2:
+ c = CODE_RATE_1_2;
+ break;
+ case FEC_2_3:
+ c = CODE_RATE_2_3;
+ break;
+ case FEC_3_4:
+ c = CODE_RATE_3_4;
+ break;
+ case FEC_5_6:
+ c = CODE_RATE_5_6;
+ break;
+ case FEC_7_8:
+ c = CODE_RATE_7_8;
+ break;
+ default:
+ c = CODE_RATE_UNKNOWN;
+ break;
+ }
+
+ return c;
+}
+
+static int as102_fe_set_frontend(struct dvb_frontend *fe)
+{
+ struct as102_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct as10x_tune_args tune_args = { 0 };
+
+ /* set frequency */
+ tune_args.freq = c->frequency / 1000;
+
+ /* fix interleaving_mode */
+ tune_args.interleaving_mode = INTLV_NATIVE;
+
+ switch (c->bandwidth_hz) {
+ case 8000000:
+ tune_args.bandwidth = BW_8_MHZ;
+ break;
+ case 7000000:
+ tune_args.bandwidth = BW_7_MHZ;
+ break;
+ case 6000000:
+ tune_args.bandwidth = BW_6_MHZ;
+ break;
+ default:
+ tune_args.bandwidth = BW_8_MHZ;
+ }
+
+ switch (c->guard_interval) {
+ case GUARD_INTERVAL_1_32:
+ tune_args.guard_interval = GUARD_INT_1_32;
+ break;
+ case GUARD_INTERVAL_1_16:
+ tune_args.guard_interval = GUARD_INT_1_16;
+ break;
+ case GUARD_INTERVAL_1_8:
+ tune_args.guard_interval = GUARD_INT_1_8;
+ break;
+ case GUARD_INTERVAL_1_4:
+ tune_args.guard_interval = GUARD_INT_1_4;
+ break;
+ case GUARD_INTERVAL_AUTO:
+ default:
+ tune_args.guard_interval = GUARD_UNKNOWN;
+ break;
+ }
+
+ switch (c->modulation) {
+ case QPSK:
+ tune_args.modulation = CONST_QPSK;
+ break;
+ case QAM_16:
+ tune_args.modulation = CONST_QAM16;
+ break;
+ case QAM_64:
+ tune_args.modulation = CONST_QAM64;
+ break;
+ default:
+ tune_args.modulation = CONST_UNKNOWN;
+ break;
+ }
+
+ switch (c->transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ tune_args.transmission_mode = TRANS_MODE_2K;
+ break;
+ case TRANSMISSION_MODE_8K:
+ tune_args.transmission_mode = TRANS_MODE_8K;
+ break;
+ default:
+ tune_args.transmission_mode = TRANS_MODE_UNKNOWN;
+ }
+
+ switch (c->hierarchy) {
+ case HIERARCHY_NONE:
+ tune_args.hierarchy = HIER_NONE;
+ break;
+ case HIERARCHY_1:
+ tune_args.hierarchy = HIER_ALPHA_1;
+ break;
+ case HIERARCHY_2:
+ tune_args.hierarchy = HIER_ALPHA_2;
+ break;
+ case HIERARCHY_4:
+ tune_args.hierarchy = HIER_ALPHA_4;
+ break;
+ case HIERARCHY_AUTO:
+ tune_args.hierarchy = HIER_UNKNOWN;
+ break;
+ }
+
+ pr_debug("as102: tuner parameters: freq: %d bw: 0x%02x gi: 0x%02x\n",
+ c->frequency,
+ tune_args.bandwidth,
+ tune_args.guard_interval);
+
+ /*
+ * Detect a hierarchy selection
+ * if HP/LP are both set to FEC_NONE, HP will be selected.
+ */
+ if ((tune_args.hierarchy != HIER_NONE) &&
+ ((c->code_rate_LP == FEC_NONE) ||
+ (c->code_rate_HP == FEC_NONE))) {
+
+ if (c->code_rate_LP == FEC_NONE) {
+ tune_args.hier_select = HIER_HIGH_PRIORITY;
+ tune_args.code_rate =
+ as102_fe_get_code_rate(c->code_rate_HP);
+ }
+
+ if (c->code_rate_HP == FEC_NONE) {
+ tune_args.hier_select = HIER_LOW_PRIORITY;
+ tune_args.code_rate =
+ as102_fe_get_code_rate(c->code_rate_LP);
+ }
+
+ pr_debug("as102: \thierarchy: 0x%02x selected: %s code_rate_%s: 0x%02x\n",
+ tune_args.hierarchy,
+ tune_args.hier_select == HIER_HIGH_PRIORITY ?
+ "HP" : "LP",
+ tune_args.hier_select == HIER_HIGH_PRIORITY ?
+ "HP" : "LP",
+ tune_args.code_rate);
+ } else {
+ tune_args.code_rate =
+ as102_fe_get_code_rate(c->code_rate_HP);
+ }
+
+ /* Set frontend arguments */
+ return state->ops->set_tune(state->priv, &tune_args);
+}
+
+static int as102_fe_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ struct as102_state *state = fe->demodulator_priv;
+ int ret = 0;
+ struct as10x_tps tps = { 0 };
+
+ /* send abilis command: GET_TPS */
+ ret = state->ops->get_tps(state->priv, &tps);
+ if (ret < 0)
+ return ret;
+
+ /* extract constellation */
+ switch (tps.modulation) {
+ case CONST_QPSK:
+ c->modulation = QPSK;
+ break;
+ case CONST_QAM16:
+ c->modulation = QAM_16;
+ break;
+ case CONST_QAM64:
+ c->modulation = QAM_64;
+ break;
+ }
+
+ /* extract hierarchy */
+ switch (tps.hierarchy) {
+ case HIER_NONE:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case HIER_ALPHA_1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case HIER_ALPHA_2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case HIER_ALPHA_4:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ }
+
+ /* extract code rate HP */
+ switch (tps.code_rate_HP) {
+ case CODE_RATE_1_2:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case CODE_RATE_2_3:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case CODE_RATE_3_4:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case CODE_RATE_5_6:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case CODE_RATE_7_8:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ }
+
+ /* extract code rate LP */
+ switch (tps.code_rate_LP) {
+ case CODE_RATE_1_2:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case CODE_RATE_2_3:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case CODE_RATE_3_4:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case CODE_RATE_5_6:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case CODE_RATE_7_8:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ }
+
+ /* extract guard interval */
+ switch (tps.guard_interval) {
+ case GUARD_INT_1_32:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case GUARD_INT_1_16:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case GUARD_INT_1_8:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case GUARD_INT_1_4:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ /* extract transmission mode */
+ switch (tps.transmission_mode) {
+ case TRANS_MODE_2K:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case TRANS_MODE_8K:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ }
+
+ return 0;
+}
+
+static int as102_fe_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *settings)
+{
+
+ settings->min_delay_ms = 1000;
+
+ return 0;
+}
+
+static int as102_fe_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ int ret = 0;
+ struct as102_state *state = fe->demodulator_priv;
+ struct as10x_tune_status tstate = { 0 };
+
+ /* send abilis command: GET_TUNE_STATUS */
+ ret = state->ops->get_status(state->priv, &tstate);
+ if (ret < 0)
+ return ret;
+
+ state->signal_strength = tstate.signal_strength;
+ state->ber = tstate.BER;
+
+ switch (tstate.tune_state) {
+ case TUNE_STATUS_SIGNAL_DVB_OK:
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ break;
+ case TUNE_STATUS_STREAM_DETECTED:
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC |
+ FE_HAS_VITERBI;
+ break;
+ case TUNE_STATUS_STREAM_TUNED:
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC |
+ FE_HAS_LOCK | FE_HAS_VITERBI;
+ break;
+ default:
+ *status = TUNE_STATUS_NOT_TUNED;
+ }
+
+ pr_debug("as102: tuner status: 0x%02x, strength %d, per: %d, ber: %d\n",
+ tstate.tune_state, tstate.signal_strength,
+ tstate.PER, tstate.BER);
+
+ if (!(*status & FE_HAS_LOCK)) {
+ memset(&state->demod_stats, 0, sizeof(state->demod_stats));
+ return 0;
+ }
+
+ ret = state->ops->get_stats(state->priv, &state->demod_stats);
+ if (ret < 0)
+ memset(&state->demod_stats, 0, sizeof(state->demod_stats));
+
+ return ret;
+}
+
+/*
+ * Note:
+ * - in AS102 SNR=MER
+ * - the SNR will be returned in linear terms, i.e. not in dB
+ * - the accuracy equals ±2dB for a SNR range from 4dB to 30dB
+ * - the accuracy is >2dB for SNR values outside this range
+ */
+static int as102_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct as102_state *state = fe->demodulator_priv;
+
+ *snr = state->demod_stats.mer;
+
+ return 0;
+}
+
+static int as102_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct as102_state *state = fe->demodulator_priv;
+
+ *ber = state->ber;
+
+ return 0;
+}
+
+static int as102_fe_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct as102_state *state = fe->demodulator_priv;
+
+ *strength = (((0xffff * 400) * state->signal_strength + 41000) * 2);
+
+ return 0;
+}
+
+static int as102_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct as102_state *state = fe->demodulator_priv;
+
+ if (state->demod_stats.has_started)
+ *ucblocks = state->demod_stats.bad_frame_count;
+ else
+ *ucblocks = 0;
+
+ return 0;
+}
+
+static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+ struct as102_state *state = fe->demodulator_priv;
+
+ return state->ops->stream_ctrl(state->priv, acquire,
+ state->elna_cfg);
+}
+
+static void as102_fe_release(struct dvb_frontend *fe)
+{
+ struct as102_state *state = fe->demodulator_priv;
+
+ kfree(state);
+}
+
+
+static const struct dvb_frontend_ops as102_fe_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Abilis AS102 DVB-T",
+ .frequency_min_hz = 174 * MHz,
+ .frequency_max_hz = 862 * MHz,
+ .frequency_stepsize_hz = 166667,
+ .caps = FE_CAN_INVERSION_AUTO
+ | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4
+ | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO
+ | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QPSK
+ | FE_CAN_QAM_AUTO
+ | FE_CAN_TRANSMISSION_MODE_AUTO
+ | FE_CAN_GUARD_INTERVAL_AUTO
+ | FE_CAN_HIERARCHY_AUTO
+ | FE_CAN_RECOVER
+ | FE_CAN_MUTE_TS
+ },
+
+ .set_frontend = as102_fe_set_frontend,
+ .get_frontend = as102_fe_get_frontend,
+ .get_tune_settings = as102_fe_get_tune_settings,
+
+ .read_status = as102_fe_read_status,
+ .read_snr = as102_fe_read_snr,
+ .read_ber = as102_fe_read_ber,
+ .read_signal_strength = as102_fe_read_signal_strength,
+ .read_ucblocks = as102_fe_read_ucblocks,
+ .ts_bus_ctrl = as102_fe_ts_bus_ctrl,
+ .release = as102_fe_release,
+};
+
+struct dvb_frontend *as102_attach(const char *name,
+ const struct as102_fe_ops *ops,
+ void *priv,
+ uint8_t elna_cfg)
+{
+ struct as102_state *state;
+ struct dvb_frontend *fe;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ fe = &state->frontend;
+ fe->demodulator_priv = state;
+ state->ops = ops;
+ state->priv = priv;
+ state->elna_cfg = elna_cfg;
+
+ /* init frontend callback ops */
+ memcpy(&fe->ops, &as102_fe_ops, sizeof(struct dvb_frontend_ops));
+ strscpy(fe->ops.info.name, name, sizeof(fe->ops.info.name));
+
+ return fe;
+
+}
+EXPORT_SYMBOL_GPL(as102_attach);
+
+MODULE_DESCRIPTION("as102-fe");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>");
diff --git a/drivers/media/dvb-frontends/as102_fe.h b/drivers/media/dvb-frontends/as102_fe.h
new file mode 100644
index 000000000000..a6409b7ca8c8
--- /dev/null
+++ b/drivers/media/dvb-frontends/as102_fe.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2014 Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+ */
+
+#include "as102_fe_types.h"
+
+struct as102_fe_ops {
+ int (*set_tune)(void *priv, struct as10x_tune_args *tune_args);
+ int (*get_tps)(void *priv, struct as10x_tps *tps);
+ int (*get_status)(void *priv, struct as10x_tune_status *tstate);
+ int (*get_stats)(void *priv, struct as10x_demod_stats *demod_stats);
+ int (*stream_ctrl)(void *priv, int acquire, uint32_t elna_cfg);
+};
+
+struct dvb_frontend *as102_attach(const char *name,
+ const struct as102_fe_ops *ops,
+ void *priv,
+ uint8_t elna_cfg);
diff --git a/drivers/media/dvb-frontends/as102_fe_types.h b/drivers/media/dvb-frontends/as102_fe_types.h
new file mode 100644
index 000000000000..8a4e392c8896
--- /dev/null
+++ b/drivers/media/dvb-frontends/as102_fe_types.h
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ */
+#ifndef _AS10X_TYPES_H_
+#define _AS10X_TYPES_H_
+
+/*********************************/
+/* MACRO DEFINITIONS */
+/*********************************/
+
+/* bandwidth constant values */
+#define BW_5_MHZ 0x00
+#define BW_6_MHZ 0x01
+#define BW_7_MHZ 0x02
+#define BW_8_MHZ 0x03
+
+/* hierarchy priority selection values */
+#define HIER_NO_PRIORITY 0x00
+#define HIER_LOW_PRIORITY 0x01
+#define HIER_HIGH_PRIORITY 0x02
+
+/* constellation available values */
+#define CONST_QPSK 0x00
+#define CONST_QAM16 0x01
+#define CONST_QAM64 0x02
+#define CONST_UNKNOWN 0xFF
+
+/* hierarchy available values */
+#define HIER_NONE 0x00
+#define HIER_ALPHA_1 0x01
+#define HIER_ALPHA_2 0x02
+#define HIER_ALPHA_4 0x03
+#define HIER_UNKNOWN 0xFF
+
+/* interleaving available values */
+#define INTLV_NATIVE 0x00
+#define INTLV_IN_DEPTH 0x01
+#define INTLV_UNKNOWN 0xFF
+
+/* code rate available values */
+#define CODE_RATE_1_2 0x00
+#define CODE_RATE_2_3 0x01
+#define CODE_RATE_3_4 0x02
+#define CODE_RATE_5_6 0x03
+#define CODE_RATE_7_8 0x04
+#define CODE_RATE_UNKNOWN 0xFF
+
+/* guard interval available values */
+#define GUARD_INT_1_32 0x00
+#define GUARD_INT_1_16 0x01
+#define GUARD_INT_1_8 0x02
+#define GUARD_INT_1_4 0x03
+#define GUARD_UNKNOWN 0xFF
+
+/* transmission mode available values */
+#define TRANS_MODE_2K 0x00
+#define TRANS_MODE_8K 0x01
+#define TRANS_MODE_4K 0x02
+#define TRANS_MODE_UNKNOWN 0xFF
+
+/* DVBH signalling available values */
+#define TIMESLICING_PRESENT 0x01
+#define MPE_FEC_PRESENT 0x02
+
+/* tune state available */
+#define TUNE_STATUS_NOT_TUNED 0x00
+#define TUNE_STATUS_IDLE 0x01
+#define TUNE_STATUS_LOCKING 0x02
+#define TUNE_STATUS_SIGNAL_DVB_OK 0x03
+#define TUNE_STATUS_STREAM_DETECTED 0x04
+#define TUNE_STATUS_STREAM_TUNED 0x05
+#define TUNE_STATUS_ERROR 0xFF
+
+/* available TS FID filter types */
+#define TS_PID_TYPE_TS 0
+#define TS_PID_TYPE_PSI_SI 1
+#define TS_PID_TYPE_MPE 2
+
+/* number of echos available */
+#define MAX_ECHOS 15
+
+/* Context types */
+#define CONTEXT_LNA 1010
+#define CONTEXT_ELNA_HYSTERESIS 4003
+#define CONTEXT_ELNA_GAIN 4004
+#define CONTEXT_MER_THRESHOLD 5005
+#define CONTEXT_MER_OFFSET 5006
+#define CONTEXT_IR_STATE 7000
+#define CONTEXT_TSOUT_MSB_FIRST 7004
+#define CONTEXT_TSOUT_FALLING_EDGE 7005
+
+/* Configuration modes */
+#define CFG_MODE_ON 0
+#define CFG_MODE_OFF 1
+#define CFG_MODE_AUTO 2
+
+struct as10x_tps {
+ uint8_t modulation;
+ uint8_t hierarchy;
+ uint8_t interleaving_mode;
+ uint8_t code_rate_HP;
+ uint8_t code_rate_LP;
+ uint8_t guard_interval;
+ uint8_t transmission_mode;
+ uint8_t DVBH_mask_HP;
+ uint8_t DVBH_mask_LP;
+ uint16_t cell_ID;
+} __packed;
+
+struct as10x_tune_args {
+ /* frequency */
+ uint32_t freq;
+ /* bandwidth */
+ uint8_t bandwidth;
+ /* hierarchy selection */
+ uint8_t hier_select;
+ /* constellation */
+ uint8_t modulation;
+ /* hierarchy */
+ uint8_t hierarchy;
+ /* interleaving mode */
+ uint8_t interleaving_mode;
+ /* code rate */
+ uint8_t code_rate;
+ /* guard interval */
+ uint8_t guard_interval;
+ /* transmission mode */
+ uint8_t transmission_mode;
+} __packed;
+
+struct as10x_tune_status {
+ /* tune status */
+ uint8_t tune_state;
+ /* signal strength */
+ int16_t signal_strength;
+ /* packet error rate 10^-4 */
+ uint16_t PER;
+ /* bit error rate 10^-4 */
+ uint16_t BER;
+} __packed;
+
+struct as10x_demod_stats {
+ /* frame counter */
+ uint32_t frame_count;
+ /* Bad frame counter */
+ uint32_t bad_frame_count;
+ /* Number of wrong bytes fixed by Reed-Solomon */
+ uint32_t bytes_fixed_by_rs;
+ /* Averaged MER */
+ uint16_t mer;
+ /* statistics calculation state indicator (started or not) */
+ uint8_t has_started;
+} __packed;
+
+struct as10x_ts_filter {
+ uint16_t pid; /* valid PID value 0x00 : 0x2000 */
+ uint8_t type; /* Red TS_PID_TYPE_<N> values */
+ uint8_t idx; /* index in filtering table */
+} __packed;
+
+struct as10x_register_value {
+ uint8_t mode;
+ union {
+ uint8_t value8; /* 8 bit value */
+ uint16_t value16; /* 16 bit value */
+ uint32_t value32; /* 32 bit value */
+ } __packed u;
+} __packed;
+
+struct as10x_register_addr {
+ /* register addr */
+ uint32_t addr;
+ /* register mode access */
+ uint8_t mode;
+} __packed;
+
+#endif
diff --git a/drivers/media/dvb-frontends/ascot2e.c b/drivers/media/dvb-frontends/ascot2e.c
new file mode 100644
index 000000000000..cf8e5f1bd101
--- /dev/null
+++ b/drivers/media/dvb-frontends/ascot2e.c
@@ -0,0 +1,540 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ascot2e.c
+ *
+ * Sony Ascot3E DVB-T/T2/C/C2 tuner driver
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dvb/frontend.h>
+#include <linux/types.h>
+#include "ascot2e.h"
+#include <media/dvb_frontend.h>
+
+#define MAX_WRITE_REGSIZE 10
+
+enum ascot2e_state {
+ STATE_UNKNOWN,
+ STATE_SLEEP,
+ STATE_ACTIVE
+};
+
+struct ascot2e_priv {
+ u32 frequency;
+ u8 i2c_address;
+ struct i2c_adapter *i2c;
+ enum ascot2e_state state;
+ void *set_tuner_data;
+ int (*set_tuner)(void *, int);
+};
+
+enum ascot2e_tv_system_t {
+ ASCOT2E_DTV_DVBT_5,
+ ASCOT2E_DTV_DVBT_6,
+ ASCOT2E_DTV_DVBT_7,
+ ASCOT2E_DTV_DVBT_8,
+ ASCOT2E_DTV_DVBT2_1_7,
+ ASCOT2E_DTV_DVBT2_5,
+ ASCOT2E_DTV_DVBT2_6,
+ ASCOT2E_DTV_DVBT2_7,
+ ASCOT2E_DTV_DVBT2_8,
+ ASCOT2E_DTV_DVBC_6,
+ ASCOT2E_DTV_DVBC_8,
+ ASCOT2E_DTV_DVBC2_6,
+ ASCOT2E_DTV_DVBC2_8,
+ ASCOT2E_DTV_UNKNOWN
+};
+
+struct ascot2e_band_sett {
+ u8 if_out_sel;
+ u8 agc_sel;
+ u8 mix_oll;
+ u8 rf_gain;
+ u8 if_bpf_gc;
+ u8 fif_offset;
+ u8 bw_offset;
+ u8 bw;
+ u8 rf_oldet;
+ u8 if_bpf_f0;
+};
+
+#define ASCOT2E_AUTO 0xff
+#define ASCOT2E_OFFSET(ofs) ((u8)(ofs) & 0x1F)
+#define ASCOT2E_BW_6 0x00
+#define ASCOT2E_BW_7 0x01
+#define ASCOT2E_BW_8 0x02
+#define ASCOT2E_BW_1_7 0x03
+
+static struct ascot2e_band_sett ascot2e_sett[] = {
+ { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+ ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 },
+ { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+ ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 },
+ { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+ ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_7, 0x0B, 0x00 },
+ { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+ ASCOT2E_OFFSET(-4), ASCOT2E_OFFSET(-2), ASCOT2E_BW_8, 0x0B, 0x00 },
+ { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+ ASCOT2E_OFFSET(-10), ASCOT2E_OFFSET(-16), ASCOT2E_BW_1_7, 0x0B, 0x00 },
+ { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+ ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 },
+ { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+ ASCOT2E_OFFSET(-8), ASCOT2E_OFFSET(-6), ASCOT2E_BW_6, 0x0B, 0x00 },
+ { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+ ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_7, 0x0B, 0x00 },
+ { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x06,
+ ASCOT2E_OFFSET(-4), ASCOT2E_OFFSET(-2), ASCOT2E_BW_8, 0x0B, 0x00 },
+ { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x02, ASCOT2E_AUTO, 0x03,
+ ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-8), ASCOT2E_BW_6, 0x09, 0x00 },
+ { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x02, ASCOT2E_AUTO, 0x03,
+ ASCOT2E_OFFSET(-2), ASCOT2E_OFFSET(-1), ASCOT2E_BW_8, 0x09, 0x00 },
+ { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x01,
+ ASCOT2E_OFFSET(-6), ASCOT2E_OFFSET(-4), ASCOT2E_BW_6, 0x09, 0x00 },
+ { ASCOT2E_AUTO, ASCOT2E_AUTO, 0x03, ASCOT2E_AUTO, 0x01,
+ ASCOT2E_OFFSET(-2), ASCOT2E_OFFSET(2), ASCOT2E_BW_8, 0x09, 0x00 }
+};
+
+static void ascot2e_i2c_debug(struct ascot2e_priv *priv,
+ u8 reg, u8 write, const u8 *data, u32 len)
+{
+ dev_dbg(&priv->i2c->dev, "ascot2e: I2C %s reg 0x%02x size %d\n",
+ (write == 0 ? "read" : "write"), reg, len);
+ print_hex_dump_bytes("ascot2e: I2C data: ",
+ DUMP_PREFIX_OFFSET, data, len);
+}
+
+static int ascot2e_write_regs(struct ascot2e_priv *priv,
+ u8 reg, const u8 *data, u32 len)
+{
+ int ret;
+ u8 buf[MAX_WRITE_REGSIZE + 1];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->i2c_address,
+ .flags = 0,
+ .len = len + 1,
+ .buf = buf,
+ }
+ };
+
+ if (len + 1 > sizeof(buf)) {
+ dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n",
+ reg, len + 1);
+ return -E2BIG;
+ }
+
+ ascot2e_i2c_debug(priv, reg, 1, data, len);
+ buf[0] = reg;
+ memcpy(&buf[1], data, len);
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret >= 0 && ret != 1)
+ ret = -EREMOTEIO;
+ if (ret < 0) {
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c wr failed=%d reg=%02x len=%d\n",
+ KBUILD_MODNAME, ret, reg, len);
+ return ret;
+ }
+ return 0;
+}
+
+static int ascot2e_write_reg(struct ascot2e_priv *priv, u8 reg, u8 val)
+{
+ u8 tmp = val; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */
+
+ return ascot2e_write_regs(priv, reg, &tmp, 1);
+}
+
+static int ascot2e_read_regs(struct ascot2e_priv *priv,
+ u8 reg, u8 *val, u32 len)
+{
+ int ret;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->i2c_address,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = priv->i2c_address,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, &msg[0], 1);
+ if (ret >= 0 && ret != 1)
+ ret = -EREMOTEIO;
+ if (ret < 0) {
+ dev_warn(&priv->i2c->dev,
+ "%s: I2C rw failed=%d addr=%02x reg=%02x\n",
+ KBUILD_MODNAME, ret, priv->i2c_address, reg);
+ return ret;
+ }
+ ret = i2c_transfer(priv->i2c, &msg[1], 1);
+ if (ret >= 0 && ret != 1)
+ ret = -EREMOTEIO;
+ if (ret < 0) {
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c rd failed=%d addr=%02x reg=%02x\n",
+ KBUILD_MODNAME, ret, priv->i2c_address, reg);
+ return ret;
+ }
+ ascot2e_i2c_debug(priv, reg, 0, val, len);
+ return 0;
+}
+
+static int ascot2e_read_reg(struct ascot2e_priv *priv, u8 reg, u8 *val)
+{
+ return ascot2e_read_regs(priv, reg, val, 1);
+}
+
+static int ascot2e_set_reg_bits(struct ascot2e_priv *priv,
+ u8 reg, u8 data, u8 mask)
+{
+ int res;
+ u8 rdata;
+
+ if (mask != 0xff) {
+ res = ascot2e_read_reg(priv, reg, &rdata);
+ if (res != 0)
+ return res;
+ data = ((data & mask) | (rdata & (mask ^ 0xFF)));
+ }
+ return ascot2e_write_reg(priv, reg, data);
+}
+
+static int ascot2e_enter_power_save(struct ascot2e_priv *priv)
+{
+ u8 data[2];
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state == STATE_SLEEP)
+ return 0;
+ data[0] = 0x00;
+ data[1] = 0x04;
+ ascot2e_write_regs(priv, 0x14, data, 2);
+ ascot2e_write_reg(priv, 0x50, 0x01);
+ priv->state = STATE_SLEEP;
+ return 0;
+}
+
+static int ascot2e_leave_power_save(struct ascot2e_priv *priv)
+{
+ u8 data[2] = { 0xFB, 0x0F };
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state == STATE_ACTIVE)
+ return 0;
+ ascot2e_write_regs(priv, 0x14, data, 2);
+ ascot2e_write_reg(priv, 0x50, 0x00);
+ priv->state = STATE_ACTIVE;
+ return 0;
+}
+
+static int ascot2e_init(struct dvb_frontend *fe)
+{
+ struct ascot2e_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ return ascot2e_leave_power_save(priv);
+}
+
+static void ascot2e_release(struct dvb_frontend *fe)
+{
+ struct ascot2e_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+}
+
+static int ascot2e_sleep(struct dvb_frontend *fe)
+{
+ struct ascot2e_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ ascot2e_enter_power_save(priv);
+ return 0;
+}
+
+static enum ascot2e_tv_system_t ascot2e_get_tv_system(struct dvb_frontend *fe)
+{
+ enum ascot2e_tv_system_t system = ASCOT2E_DTV_UNKNOWN;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct ascot2e_priv *priv = fe->tuner_priv;
+
+ if (p->delivery_system == SYS_DVBT) {
+ if (p->bandwidth_hz <= 5000000)
+ system = ASCOT2E_DTV_DVBT_5;
+ else if (p->bandwidth_hz <= 6000000)
+ system = ASCOT2E_DTV_DVBT_6;
+ else if (p->bandwidth_hz <= 7000000)
+ system = ASCOT2E_DTV_DVBT_7;
+ else if (p->bandwidth_hz <= 8000000)
+ system = ASCOT2E_DTV_DVBT_8;
+ else {
+ system = ASCOT2E_DTV_DVBT_8;
+ p->bandwidth_hz = 8000000;
+ }
+ } else if (p->delivery_system == SYS_DVBT2) {
+ if (p->bandwidth_hz <= 5000000)
+ system = ASCOT2E_DTV_DVBT2_5;
+ else if (p->bandwidth_hz <= 6000000)
+ system = ASCOT2E_DTV_DVBT2_6;
+ else if (p->bandwidth_hz <= 7000000)
+ system = ASCOT2E_DTV_DVBT2_7;
+ else if (p->bandwidth_hz <= 8000000)
+ system = ASCOT2E_DTV_DVBT2_8;
+ else {
+ system = ASCOT2E_DTV_DVBT2_8;
+ p->bandwidth_hz = 8000000;
+ }
+ } else if (p->delivery_system == SYS_DVBC_ANNEX_A) {
+ if (p->bandwidth_hz <= 6000000)
+ system = ASCOT2E_DTV_DVBC_6;
+ else if (p->bandwidth_hz <= 8000000)
+ system = ASCOT2E_DTV_DVBC_8;
+ }
+ dev_dbg(&priv->i2c->dev,
+ "%s(): ASCOT2E DTV system %d (delsys %d, bandwidth %d)\n",
+ __func__, (int)system, p->delivery_system, p->bandwidth_hz);
+ return system;
+}
+
+static int ascot2e_set_params(struct dvb_frontend *fe)
+{
+ u8 data[10];
+ u32 frequency;
+ enum ascot2e_tv_system_t tv_system;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct ascot2e_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz\n",
+ __func__, p->frequency / 1000);
+ tv_system = ascot2e_get_tv_system(fe);
+
+ if (tv_system == ASCOT2E_DTV_UNKNOWN) {
+ dev_dbg(&priv->i2c->dev, "%s(): unknown DTV system\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (priv->set_tuner)
+ priv->set_tuner(priv->set_tuner_data, 1);
+ frequency = roundup(p->frequency / 1000, 25);
+ if (priv->state == STATE_SLEEP)
+ ascot2e_leave_power_save(priv);
+
+ /* IF_OUT_SEL / AGC_SEL setting */
+ data[0] = 0x00;
+ if (ascot2e_sett[tv_system].agc_sel != ASCOT2E_AUTO) {
+ /* AGC pin setting from parameter table */
+ data[0] |= (u8)(
+ (ascot2e_sett[tv_system].agc_sel & 0x03) << 3);
+ }
+ if (ascot2e_sett[tv_system].if_out_sel != ASCOT2E_AUTO) {
+ /* IFOUT pin setting from parameter table */
+ data[0] |= (u8)(
+ (ascot2e_sett[tv_system].if_out_sel & 0x01) << 2);
+ }
+ /* Set bit[4:2] only */
+ ascot2e_set_reg_bits(priv, 0x05, data[0], 0x1c);
+ /* 0x06 - 0x0F */
+ /* REF_R setting (0x06) */
+ if (tv_system == ASCOT2E_DTV_DVBC_6 ||
+ tv_system == ASCOT2E_DTV_DVBC_8) {
+ /* xtal, xtal*2 */
+ data[0] = (frequency > 500000) ? 16 : 32;
+ } else {
+ /* xtal/8, xtal/4 */
+ data[0] = (frequency > 500000) ? 2 : 4;
+ }
+ /* XOSC_SEL=100uA */
+ data[1] = 0x04;
+ /* KBW setting (0x08), KC0 setting (0x09), KC1 setting (0x0A) */
+ if (tv_system == ASCOT2E_DTV_DVBC_6 ||
+ tv_system == ASCOT2E_DTV_DVBC_8) {
+ data[2] = 18;
+ data[3] = 120;
+ data[4] = 20;
+ } else {
+ data[2] = 48;
+ data[3] = 10;
+ data[4] = 30;
+ }
+ /* ORDER/R2_RANGE/R2_BANK/C2_BANK setting (0x0B) */
+ if (tv_system == ASCOT2E_DTV_DVBC_6 ||
+ tv_system == ASCOT2E_DTV_DVBC_8)
+ data[5] = (frequency > 500000) ? 0x08 : 0x0c;
+ else
+ data[5] = (frequency > 500000) ? 0x30 : 0x38;
+ /* Set MIX_OLL (0x0C) value from parameter table */
+ data[6] = ascot2e_sett[tv_system].mix_oll;
+ /* Set RF_GAIN (0x0D) setting from parameter table */
+ if (ascot2e_sett[tv_system].rf_gain == ASCOT2E_AUTO) {
+ /* RF_GAIN auto control enable */
+ ascot2e_write_reg(priv, 0x4E, 0x01);
+ /* RF_GAIN Default value */
+ data[7] = 0x00;
+ } else {
+ /* RF_GAIN auto control disable */
+ ascot2e_write_reg(priv, 0x4E, 0x00);
+ data[7] = ascot2e_sett[tv_system].rf_gain;
+ }
+ /* Set IF_BPF_GC/FIF_OFFSET (0x0E) value from parameter table */
+ data[8] = (u8)((ascot2e_sett[tv_system].fif_offset << 3) |
+ (ascot2e_sett[tv_system].if_bpf_gc & 0x07));
+ /* Set BW_OFFSET (0x0F) value from parameter table */
+ data[9] = ascot2e_sett[tv_system].bw_offset;
+ ascot2e_write_regs(priv, 0x06, data, 10);
+ /*
+ * 0x45 - 0x47
+ * LNA optimization setting
+ * RF_LNA_DIST1-5, RF_LNA_CM
+ */
+ if (tv_system == ASCOT2E_DTV_DVBC_6 ||
+ tv_system == ASCOT2E_DTV_DVBC_8) {
+ data[0] = 0x0F;
+ data[1] = 0x00;
+ data[2] = 0x01;
+ } else {
+ data[0] = 0x0F;
+ data[1] = 0x00;
+ data[2] = 0x03;
+ }
+ ascot2e_write_regs(priv, 0x45, data, 3);
+ /* 0x49 - 0x4A
+ Set RF_OLDET_ENX/RF_OLDET_OLL value from parameter table */
+ data[0] = ascot2e_sett[tv_system].rf_oldet;
+ /* Set IF_BPF_F0 value from parameter table */
+ data[1] = ascot2e_sett[tv_system].if_bpf_f0;
+ ascot2e_write_regs(priv, 0x49, data, 2);
+ /*
+ * Tune now
+ * RFAGC fast mode / RFAGC auto control enable
+ * (set bit[7], bit[5:4] only)
+ * vco_cal = 1, set MIX_OL_CPU_EN
+ */
+ ascot2e_set_reg_bits(priv, 0x0c, 0x90, 0xb0);
+ /* Logic wake up, CPU wake up */
+ data[0] = 0xc4;
+ data[1] = 0x40;
+ ascot2e_write_regs(priv, 0x03, data, 2);
+ /* 0x10 - 0x14 */
+ data[0] = (u8)(frequency & 0xFF); /* 0x10: FRF_L */
+ data[1] = (u8)((frequency >> 8) & 0xFF); /* 0x11: FRF_M */
+ data[2] = (u8)((frequency >> 16) & 0x0F); /* 0x12: FRF_H (bit[3:0]) */
+ /* 0x12: BW (bit[5:4]) */
+ data[2] |= (u8)(ascot2e_sett[tv_system].bw << 4);
+ data[3] = 0xFF; /* 0x13: VCO calibration enable */
+ data[4] = 0xFF; /* 0x14: Analog block enable */
+ /* Tune (Burst write) */
+ ascot2e_write_regs(priv, 0x10, data, 5);
+ msleep(50);
+ /* CPU deep sleep */
+ ascot2e_write_reg(priv, 0x04, 0x00);
+ /* Logic sleep */
+ ascot2e_write_reg(priv, 0x03, 0xC0);
+ /* RFAGC normal mode (set bit[5:4] only) */
+ ascot2e_set_reg_bits(priv, 0x0C, 0x00, 0x30);
+ priv->frequency = frequency;
+ return 0;
+}
+
+static int ascot2e_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct ascot2e_priv *priv = fe->tuner_priv;
+
+ *frequency = priv->frequency * 1000;
+ return 0;
+}
+
+static const struct dvb_tuner_ops ascot2e_tuner_ops = {
+ .info = {
+ .name = "Sony ASCOT2E",
+ .frequency_min_hz = 1 * MHz,
+ .frequency_max_hz = 1200 * MHz,
+ .frequency_step_hz = 25 * kHz,
+ },
+ .init = ascot2e_init,
+ .release = ascot2e_release,
+ .sleep = ascot2e_sleep,
+ .set_params = ascot2e_set_params,
+ .get_frequency = ascot2e_get_frequency,
+};
+
+struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe,
+ const struct ascot2e_config *config,
+ struct i2c_adapter *i2c)
+{
+ u8 data[4];
+ struct ascot2e_priv *priv = NULL;
+
+ priv = kzalloc(sizeof(struct ascot2e_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+ priv->i2c_address = (config->i2c_address >> 1);
+ priv->i2c = i2c;
+ priv->set_tuner_data = config->set_tuner_priv;
+ priv->set_tuner = config->set_tuner_callback;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ /* 16 MHz xTal frequency */
+ data[0] = 16;
+ /* VCO current setting */
+ data[1] = 0x06;
+ /* Logic wake up, CPU boot */
+ data[2] = 0xC4;
+ data[3] = 0x40;
+ ascot2e_write_regs(priv, 0x01, data, 4);
+ /* RFVGA optimization setting (RF_DIST0 - RF_DIST2) */
+ data[0] = 0x10;
+ data[1] = 0x3F;
+ data[2] = 0x25;
+ ascot2e_write_regs(priv, 0x22, data, 3);
+ /* PLL mode setting */
+ ascot2e_write_reg(priv, 0x28, 0x1e);
+ /* RSSI setting */
+ ascot2e_write_reg(priv, 0x59, 0x04);
+ /* TODO check CPU HW error state here */
+ msleep(80);
+ /* Xtal oscillator current control setting */
+ ascot2e_write_reg(priv, 0x4c, 0x01);
+ /* XOSC_SEL=100uA */
+ ascot2e_write_reg(priv, 0x07, 0x04);
+ /* CPU deep sleep */
+ ascot2e_write_reg(priv, 0x04, 0x00);
+ /* Logic sleep */
+ ascot2e_write_reg(priv, 0x03, 0xc0);
+ /* Power save setting */
+ data[0] = 0x00;
+ data[1] = 0x04;
+ ascot2e_write_regs(priv, 0x14, data, 2);
+ ascot2e_write_reg(priv, 0x50, 0x01);
+ priv->state = STATE_SLEEP;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ memcpy(&fe->ops.tuner_ops, &ascot2e_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ fe->tuner_priv = priv;
+ dev_info(&priv->i2c->dev,
+ "Sony ASCOT2E attached on addr=%x at I2C adapter %p\n",
+ priv->i2c_address, priv->i2c);
+ return fe;
+}
+EXPORT_SYMBOL_GPL(ascot2e_attach);
+
+MODULE_DESCRIPTION("Sony ASCOT2E terr/cab tuner driver");
+MODULE_AUTHOR("info@netup.ru");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/ascot2e.h b/drivers/media/dvb-frontends/ascot2e.h
new file mode 100644
index 000000000000..d86b3de85c6a
--- /dev/null
+++ b/drivers/media/dvb-frontends/ascot2e.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ascot2e.h
+ *
+ * Sony Ascot3E DVB-T/T2/C/C2 tuner driver
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
+ */
+
+#ifndef __DVB_ASCOT2E_H__
+#define __DVB_ASCOT2E_H__
+
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+/**
+ * struct ascot2e_config - the configuration of Ascot2E tuner driver
+ * @i2c_address: I2C address of the tuner
+ * @xtal_freq_mhz: Oscillator frequency, MHz
+ * @set_tuner_priv: Callback function private context
+ * @set_tuner_callback: Callback function that notifies the parent driver
+ * which tuner is active now
+ */
+struct ascot2e_config {
+ u8 i2c_address;
+ u8 xtal_freq_mhz;
+ void *set_tuner_priv;
+ int (*set_tuner_callback)(void *, int);
+};
+
+#if IS_REACHABLE(CONFIG_DVB_ASCOT2E)
+/**
+ * ascot2e_attach - Attach an ascot2e tuner
+ *
+ * @fe: frontend to be attached
+ * @config: pointer to &struct ascot2e_config with tuner configuration.
+ * @i2c: i2c adapter to use.
+ *
+ * return: FE pointer on success, NULL on failure.
+ */
+extern struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe,
+ const struct ascot2e_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe,
+ const struct ascot2e_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/frontends/atbm8830.c b/drivers/media/dvb-frontends/atbm8830.c
index a2261ea2cf82..778c865085bf 100644
--- a/drivers/media/dvb/frontends/atbm8830.c
+++ b/drivers/media/dvb-frontends/atbm8830.c
@@ -1,26 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator
* ATBM8830, ATBM8831
*
* Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <asm/div64.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "atbm8830.h"
#include "atbm8830_priv.h"
@@ -297,9 +284,9 @@ static int atbm8830_set_fe(struct dvb_frontend *fe)
return 0;
}
-static int atbm8830_get_fe(struct dvb_frontend *fe)
+static int atbm8830_get_fe(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
dprintk("%s\n", __func__);
/* TODO: get real readings from device */
@@ -335,7 +322,8 @@ static int atbm8830_get_tune_settings(struct dvb_frontend *fe,
return 0;
}
-static int atbm8830_read_status(struct dvb_frontend *fe, fe_status_t *fe_status)
+static int atbm8830_read_status(struct dvb_frontend *fe,
+ enum fe_status *fe_status)
{
struct atbm_state *priv = fe->demodulator_priv;
u8 locked = 0;
@@ -427,13 +415,13 @@ static int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0);
}
-static struct dvb_frontend_ops atbm8830_ops = {
- .delsys = { SYS_DMBTH },
+static const struct dvb_frontend_ops atbm8830_ops = {
+ .delsys = { SYS_DTMB },
.info = {
.name = "AltoBeam ATBM8830/8831 DMB-TH",
- .frequency_min = 474000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 10000,
+ .frequency_min_hz = 474 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 10 * kHz,
.caps =
FE_CAN_FEC_AUTO |
FE_CAN_QAM_AUTO |
@@ -501,7 +489,7 @@ error_out:
return NULL;
}
-EXPORT_SYMBOL(atbm8830_attach);
+EXPORT_SYMBOL_GPL(atbm8830_attach);
MODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver");
MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
diff --git a/drivers/media/dvb-frontends/atbm8830.h b/drivers/media/dvb-frontends/atbm8830.h
new file mode 100644
index 000000000000..7cb0d982ff9b
--- /dev/null
+++ b/drivers/media/dvb-frontends/atbm8830.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator
+ * ATBM8830, ATBM8831
+ *
+ * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com>
+ */
+
+#ifndef __ATBM8830_H__
+#define __ATBM8830_H__
+
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+#define ATBM8830_PROD_8830 0
+#define ATBM8830_PROD_8831 1
+
+struct atbm8830_config {
+
+ /* product type */
+ u8 prod;
+
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* parallel or serial transport stream */
+ u8 serial_ts;
+
+ /* transport stream clock output only when receiving valid stream */
+ u8 ts_clk_gated;
+
+ /* Decoder sample TS data at rising edge of clock */
+ u8 ts_sampling_edge;
+
+ /* Oscillator clock frequency */
+ u32 osc_clk_freq; /* in kHz */
+
+ /* IF frequency */
+ u32 if_freq; /* in kHz */
+
+ /* Swap I/Q for zero IF */
+ u8 zif_swap_iq;
+
+ /* Tuner AGC settings */
+ u8 agc_min;
+ u8 agc_max;
+ u8 agc_hold_loop;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_ATBM8830)
+extern struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline
+struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config,
+ struct i2c_adapter *i2c) {
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_ATBM8830 */
+
+#endif /* __ATBM8830_H__ */
diff --git a/drivers/media/dvb-frontends/atbm8830_priv.h b/drivers/media/dvb-frontends/atbm8830_priv.h
new file mode 100644
index 000000000000..a8cd39196d1f
--- /dev/null
+++ b/drivers/media/dvb-frontends/atbm8830_priv.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator
+ * ATBM8830, ATBM8831
+ *
+ * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com>
+ */
+
+#ifndef __ATBM8830_PRIV_H
+#define __ATBM8830_PRIV_H
+
+struct atbm_state {
+ struct i2c_adapter *i2c;
+ /* configuration settings */
+ const struct atbm8830_config *config;
+ struct dvb_frontend frontend;
+};
+
+#define REG_CHIP_ID 0x0000
+#define REG_TUNER_BASEBAND 0x0001
+#define REG_DEMOD_RUN 0x0004
+#define REG_DSP_RESET 0x0005
+#define REG_RAM_RESET 0x0006
+#define REG_ADC_RESET 0x0007
+#define REG_TSPORT_RESET 0x0008
+#define REG_BLKERR_POL 0x000C
+#define REG_I2C_GATE 0x0103
+#define REG_TS_SAMPLE_EDGE 0x0301
+#define REG_TS_PKT_LEN_204 0x0302
+#define REG_TS_PKT_LEN_AUTO 0x0303
+#define REG_TS_SERIAL 0x0305
+#define REG_TS_CLK_FREERUN 0x0306
+#define REG_TS_VALID_MODE 0x0307
+#define REG_TS_CLK_MODE 0x030B /* 1 for serial, 0 for parallel */
+
+#define REG_TS_ERRBIT_USE 0x030C
+#define REG_LOCK_STATUS 0x030D
+#define REG_ADC_CONFIG 0x0602
+#define REG_CARRIER_OFFSET 0x0827 /* 0x0827-0x0829 little endian */
+#define REG_DETECTED_PN_MODE 0x082D
+#define REG_READ_LATCH 0x084D
+#define REG_IF_FREQ 0x0A00 /* 0x0A00-0x0A02 little endian */
+#define REG_OSC_CLK 0x0A03 /* 0x0A03-0x0A05 little endian */
+#define REG_BYPASS_CCI 0x0A06
+#define REG_ANALOG_LUMA_DETECTED 0x0A25
+#define REG_ANALOG_AUDIO_DETECTED 0x0A26
+#define REG_ANALOG_CHROMA_DETECTED 0x0A39
+#define REG_FRAME_ERR_CNT 0x0B04
+#define REG_USE_EXT_ADC 0x0C00
+#define REG_SWAP_I_Q 0x0C01
+#define REG_TPS_MANUAL 0x0D01
+#define REG_TPS_CONFIG 0x0D02
+#define REG_BYPASS_DEINTERLEAVER 0x0E00
+#define REG_AGC_TARGET 0x1003 /* 0x1003-0x1005 little endian */
+#define REG_AGC_MIN 0x1020
+#define REG_AGC_MAX 0x1023
+#define REG_AGC_LOCK 0x1027
+#define REG_AGC_PWM_VAL 0x1028 /* 0x1028-0x1029 little endian */
+#define REG_AGC_HOLD_LOOP 0x1031
+
+#endif
+
diff --git a/drivers/media/dvb-frontends/au8522.h b/drivers/media/dvb-frontends/au8522.h
new file mode 100644
index 000000000000..72e33285726f
--- /dev/null
+++ b/drivers/media/dvb-frontends/au8522.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Auvitek AU8522 QAM/8VSB demodulator driver
+
+ Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
+
+
+*/
+
+#ifndef __AU8522_H__
+#define __AU8522_H__
+
+#include <linux/dvb/frontend.h>
+
+enum au8522_if_freq {
+ AU8522_IF_6MHZ = 0,
+ AU8522_IF_4MHZ,
+ AU8522_IF_3_25MHZ,
+};
+
+struct au8522_led_config {
+ u16 vsb8_strong;
+ u16 qam64_strong;
+ u16 qam256_strong;
+
+ u16 gpio_output;
+ /* unset hi bits, set low bits */
+ u16 gpio_output_enable;
+ u16 gpio_output_disable;
+
+ u16 gpio_leds;
+ u8 *led_states;
+ unsigned int num_led_states;
+};
+
+struct au8522_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* Return lock status based on tuner lock, or demod lock */
+#define AU8522_TUNERLOCKING 0
+#define AU8522_DEMODLOCKING 1
+ u8 status_mode;
+
+ struct au8522_led_config *led_cfg;
+
+ enum au8522_if_freq vsb_if;
+ enum au8522_if_freq qam_if;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_AU8522_DTV)
+extern struct dvb_frontend *au8522_attach(const struct au8522_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline
+struct dvb_frontend *au8522_attach(const struct au8522_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_AU8522 */
+
+/* Other modes may need to be added later */
+enum au8522_video_input {
+ AU8522_COMPOSITE_CH1 = 1,
+ AU8522_COMPOSITE_CH2,
+ AU8522_COMPOSITE_CH3,
+ AU8522_COMPOSITE_CH4,
+ AU8522_COMPOSITE_CH4_SIF,
+ AU8522_SVIDEO_CH13,
+ AU8522_SVIDEO_CH24,
+};
+
+enum au8522_audio_input {
+ AU8522_AUDIO_NONE,
+ AU8522_AUDIO_SIF,
+};
+#endif /* __AU8522_H__ */
diff --git a/drivers/media/dvb/frontends/au8522_common.c b/drivers/media/dvb-frontends/au8522_common.c
index 5cfe151ee394..b9e77e86f44e 100644
--- a/drivers/media/dvb/frontends/au8522_common.c
+++ b/drivers/media/dvb-frontends/au8522_common.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Auvitek AU8522 QAM/8VSB demodulator driver
@@ -6,28 +7,13 @@
Copyright (C) 2005-2008 Auvitek International, Ltd.
Copyright (C) 2012 Michael Krufky <mkrufky@linuxtv.org>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/i2c.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "au8522_priv.h"
-MODULE_LICENSE("GPL");
-
static int debug;
#define dprintk(arg...)\
@@ -46,14 +32,14 @@ int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
int ret;
u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
- struct i2c_msg msg = { .addr = state->config->demod_address,
+ struct i2c_msg msg = { .addr = state->config.demod_address,
.flags = 0, .buf = buf, .len = 3 };
ret = i2c_transfer(state->i2c, &msg, 1);
if (ret != 1)
- printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, "
- "ret == %i)\n", __func__, reg, data, ret);
+ printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, ret == %i)\n",
+ __func__, reg, data, ret);
return (ret != 1) ? -1 : 0;
}
@@ -66,9 +52,9 @@ u8 au8522_readreg(struct au8522_state *state, u16 reg)
u8 b1[] = { 0 };
struct i2c_msg msg[] = {
- { .addr = state->config->demod_address, .flags = 0,
+ { .addr = state->config.demod_address, .flags = 0,
.buf = b0, .len = 2 },
- { .addr = state->config->demod_address, .flags = I2C_M_RD,
+ { .addr = state->config.demod_address, .flags = I2C_M_RD,
.buf = b1, .len = 1 } };
ret = i2c_transfer(state->i2c, msg, 2);
@@ -101,6 +87,19 @@ int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
}
EXPORT_SYMBOL(au8522_i2c_gate_ctrl);
+int au8522_analog_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+
+ dprintk("%s(%d)\n", __func__, enable);
+
+ if (enable)
+ return au8522_writereg(state, 0x106, 1);
+ else
+ return au8522_writereg(state, 0x106, 0);
+}
+EXPORT_SYMBOL(au8522_analog_i2c_gate_ctrl);
+
/* Reset the demod hardware and reset all of the configuration registers
to a default state. */
int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
@@ -129,7 +128,7 @@ EXPORT_SYMBOL(au8522_release_state);
static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
{
- struct au8522_led_config *led_config = state->config->led_cfg;
+ struct au8522_led_config *led_config = state->config.led_cfg;
u8 val;
/* bail out if we can't control an LED */
@@ -159,7 +158,7 @@ static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
*/
int au8522_led_ctrl(struct au8522_state *state, int led)
{
- struct au8522_led_config *led_config = state->config->led_cfg;
+ struct au8522_led_config *led_config = state->config.led_cfg;
int i, ret = 0;
/* bail out if we can't control an LED */
@@ -223,6 +222,7 @@ int au8522_init(struct dvb_frontend *fe)
chip, so that when it gets powered back up it won't think
that it is already tuned */
state->current_frequency = 0;
+ state->current_modulation = VSB_8;
au8522_writereg(state, 0xa4, 1 << 5);
@@ -257,3 +257,10 @@ int au8522_sleep(struct dvb_frontend *fe)
return 0;
}
EXPORT_SYMBOL(au8522_sleep);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable verbose debug messages");
+
+MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
+MODULE_AUTHOR("Steven Toth");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c
new file mode 100644
index 000000000000..58c4c489bf97
--- /dev/null
+++ b/drivers/media/dvb-frontends/au8522_decoder.c
@@ -0,0 +1,785 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Auvitek AU8522 QAM/8VSB demodulator driver and video decoder
+ *
+ * Copyright (C) 2009 Devin Heitmueller <dheitmueller@linuxtv.org>
+ * Copyright (C) 2005-2008 Auvitek International, Ltd.
+ */
+
+/* Developer notes:
+ *
+ * Enough is implemented here for CVBS and S-Video inputs, but the actual
+ * analog demodulator code isn't implemented (not needed for xc5000 since it
+ * has its own demodulator and outputs CVBS)
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include "au8522.h"
+#include "au8522_priv.h"
+
+MODULE_AUTHOR("Devin Heitmueller");
+MODULE_DESCRIPTION("Auvitek AU8522 QAM/8VSB demodulator driver and video decoder");
+MODULE_LICENSE("GPL");
+
+static int au8522_analog_debug;
+
+
+module_param_named(analog_debug, au8522_analog_debug, int, 0644);
+
+MODULE_PARM_DESC(analog_debug,
+ "Analog debugging messages [0=Off (default) 1=On]");
+
+struct au8522_register_config {
+ u16 reg_name;
+ u8 reg_val[8];
+};
+
+
+/* Video Decoder Filter Coefficients
+ The values are as follows from left to right
+ 0="ATV RF" 1="ATV RF13" 2="CVBS" 3="S-Video" 4="PAL" 5=CVBS13" 6="SVideo13"
+*/
+static const struct au8522_register_config filter_coef[] = {
+ {AU8522_FILTER_COEF_R410, {0x25, 0x00, 0x25, 0x25, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R411, {0x20, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R412, {0x03, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R413, {0xe6, 0x00, 0xe6, 0xe6, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R414, {0x40, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R415, {0x1b, 0x00, 0x1b, 0x1b, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R416, {0xc0, 0x00, 0xc0, 0x04, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R417, {0x04, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R418, {0x8c, 0x00, 0x8c, 0x8c, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R419, {0xa0, 0x40, 0xa0, 0xa0, 0x40, 0x40, 0x40} },
+ {AU8522_FILTER_COEF_R41A, {0x21, 0x09, 0x21, 0x21, 0x09, 0x09, 0x09} },
+ {AU8522_FILTER_COEF_R41B, {0x6c, 0x38, 0x6c, 0x6c, 0x38, 0x38, 0x38} },
+ {AU8522_FILTER_COEF_R41C, {0x03, 0xff, 0x03, 0x03, 0xff, 0xff, 0xff} },
+ {AU8522_FILTER_COEF_R41D, {0xbf, 0xc7, 0xbf, 0xbf, 0xc7, 0xc7, 0xc7} },
+ {AU8522_FILTER_COEF_R41E, {0xa0, 0xdf, 0xa0, 0xa0, 0xdf, 0xdf, 0xdf} },
+ {AU8522_FILTER_COEF_R41F, {0x10, 0x06, 0x10, 0x10, 0x06, 0x06, 0x06} },
+ {AU8522_FILTER_COEF_R420, {0xae, 0x30, 0xae, 0xae, 0x30, 0x30, 0x30} },
+ {AU8522_FILTER_COEF_R421, {0xc4, 0x01, 0xc4, 0xc4, 0x01, 0x01, 0x01} },
+ {AU8522_FILTER_COEF_R422, {0x54, 0xdd, 0x54, 0x54, 0xdd, 0xdd, 0xdd} },
+ {AU8522_FILTER_COEF_R423, {0xd0, 0xaf, 0xd0, 0xd0, 0xaf, 0xaf, 0xaf} },
+ {AU8522_FILTER_COEF_R424, {0x1c, 0xf7, 0x1c, 0x1c, 0xf7, 0xf7, 0xf7} },
+ {AU8522_FILTER_COEF_R425, {0x76, 0xdb, 0x76, 0x76, 0xdb, 0xdb, 0xdb} },
+ {AU8522_FILTER_COEF_R426, {0x61, 0xc0, 0x61, 0x61, 0xc0, 0xc0, 0xc0} },
+ {AU8522_FILTER_COEF_R427, {0xd1, 0x2f, 0xd1, 0xd1, 0x2f, 0x2f, 0x2f} },
+ {AU8522_FILTER_COEF_R428, {0x84, 0xd8, 0x84, 0x84, 0xd8, 0xd8, 0xd8} },
+ {AU8522_FILTER_COEF_R429, {0x06, 0xfb, 0x06, 0x06, 0xfb, 0xfb, 0xfb} },
+ {AU8522_FILTER_COEF_R42A, {0x21, 0xd5, 0x21, 0x21, 0xd5, 0xd5, 0xd5} },
+ {AU8522_FILTER_COEF_R42B, {0x0a, 0x3e, 0x0a, 0x0a, 0x3e, 0x3e, 0x3e} },
+ {AU8522_FILTER_COEF_R42C, {0xe6, 0x15, 0xe6, 0xe6, 0x15, 0x15, 0x15} },
+ {AU8522_FILTER_COEF_R42D, {0x01, 0x34, 0x01, 0x01, 0x34, 0x34, 0x34} },
+
+};
+#define NUM_FILTER_COEF (sizeof(filter_coef)\
+ / sizeof(struct au8522_register_config))
+
+
+/* Registers 0x060b through 0x0652 are the LP Filter coefficients
+ The values are as follows from left to right
+ 0="SIF" 1="ATVRF/ATVRF13"
+ Note: the "ATVRF/ATVRF13" mode has never been tested
+*/
+static const struct au8522_register_config lpfilter_coef[] = {
+ {0x060b, {0x21, 0x0b} },
+ {0x060c, {0xad, 0xad} },
+ {0x060d, {0x70, 0xf0} },
+ {0x060e, {0xea, 0xe9} },
+ {0x060f, {0xdd, 0xdd} },
+ {0x0610, {0x08, 0x64} },
+ {0x0611, {0x60, 0x60} },
+ {0x0612, {0xf8, 0xb2} },
+ {0x0613, {0x01, 0x02} },
+ {0x0614, {0xe4, 0xb4} },
+ {0x0615, {0x19, 0x02} },
+ {0x0616, {0xae, 0x2e} },
+ {0x0617, {0xee, 0xc5} },
+ {0x0618, {0x56, 0x56} },
+ {0x0619, {0x30, 0x58} },
+ {0x061a, {0xf9, 0xf8} },
+ {0x061b, {0x24, 0x64} },
+ {0x061c, {0x07, 0x07} },
+ {0x061d, {0x30, 0x30} },
+ {0x061e, {0xa9, 0xed} },
+ {0x061f, {0x09, 0x0b} },
+ {0x0620, {0x42, 0xc2} },
+ {0x0621, {0x1d, 0x2a} },
+ {0x0622, {0xd6, 0x56} },
+ {0x0623, {0x95, 0x8b} },
+ {0x0624, {0x2b, 0x2b} },
+ {0x0625, {0x30, 0x24} },
+ {0x0626, {0x3e, 0x3e} },
+ {0x0627, {0x62, 0xe2} },
+ {0x0628, {0xe9, 0xf5} },
+ {0x0629, {0x99, 0x19} },
+ {0x062a, {0xd4, 0x11} },
+ {0x062b, {0x03, 0x04} },
+ {0x062c, {0xb5, 0x85} },
+ {0x062d, {0x1e, 0x20} },
+ {0x062e, {0x2a, 0xea} },
+ {0x062f, {0xd7, 0xd2} },
+ {0x0630, {0x15, 0x15} },
+ {0x0631, {0xa3, 0xa9} },
+ {0x0632, {0x1f, 0x1f} },
+ {0x0633, {0xf9, 0xd1} },
+ {0x0634, {0xc0, 0xc3} },
+ {0x0635, {0x4d, 0x8d} },
+ {0x0636, {0x21, 0x31} },
+ {0x0637, {0x83, 0x83} },
+ {0x0638, {0x08, 0x8c} },
+ {0x0639, {0x19, 0x19} },
+ {0x063a, {0x45, 0xa5} },
+ {0x063b, {0xef, 0xec} },
+ {0x063c, {0x8a, 0x8a} },
+ {0x063d, {0xf4, 0xf6} },
+ {0x063e, {0x8f, 0x8f} },
+ {0x063f, {0x44, 0x0c} },
+ {0x0640, {0xef, 0xf0} },
+ {0x0641, {0x66, 0x66} },
+ {0x0642, {0xcc, 0xd2} },
+ {0x0643, {0x41, 0x41} },
+ {0x0644, {0x63, 0x93} },
+ {0x0645, {0x8e, 0x8e} },
+ {0x0646, {0xa2, 0x42} },
+ {0x0647, {0x7b, 0x7b} },
+ {0x0648, {0x04, 0x04} },
+ {0x0649, {0x00, 0x00} },
+ {0x064a, {0x40, 0x40} },
+ {0x064b, {0x8c, 0x98} },
+ {0x064c, {0x00, 0x00} },
+ {0x064d, {0x63, 0xc3} },
+ {0x064e, {0x04, 0x04} },
+ {0x064f, {0x20, 0x20} },
+ {0x0650, {0x00, 0x00} },
+ {0x0651, {0x40, 0x40} },
+ {0x0652, {0x01, 0x01} },
+};
+#define NUM_LPFILTER_COEF (sizeof(lpfilter_coef)\
+ / sizeof(struct au8522_register_config))
+
+static inline struct au8522_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct au8522_state, sd);
+}
+
+static void setup_decoder_defaults(struct au8522_state *state, bool is_svideo)
+{
+ int i;
+ int filter_coef_type;
+
+ /* Provide reasonable defaults for picture tuning values */
+ au8522_writereg(state, AU8522_TVDEC_SHARPNESSREG009H, 0x07);
+ au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH, 0xed);
+ au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH, 0x79);
+ au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, 0x80);
+ au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, 0x80);
+ au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, 0x00);
+ au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, 0x00);
+
+ /* Other decoder registers */
+ au8522_writereg(state, AU8522_TVDEC_INT_MASK_REG010H, 0x00);
+
+ if (is_svideo)
+ au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x04);
+ else
+ au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x00);
+
+ au8522_writereg(state, AU8522_TVDEC_PGA_REG012H,
+ AU8522_TVDEC_PGA_REG012H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_MODE_REG015H,
+ AU8522_TVDEC_COMB_MODE_REG015H_CVBS);
+ au8522_writereg(state, AU8522_TVDED_DBG_MODE_REG060H,
+ AU8522_TVDED_DBG_MODE_REG060H_CVBS);
+
+ if (state->std == V4L2_STD_PAL_M) {
+ au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL1_REG061H,
+ AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_525 |
+ AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_492 |
+ AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_AUTO);
+ au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL2_REG062H,
+ AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_PAL_M);
+ } else {
+ /* NTSC */
+ au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL1_REG061H,
+ AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_525 |
+ AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_492 |
+ AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_MN);
+ au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL2_REG062H,
+ AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_NTSC);
+ }
+ au8522_writereg(state, AU8522_TVDEC_VCR_DET_LLIM_REG063H,
+ AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_VCR_DET_HLIM_REG064H,
+ AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR1_REG065H,
+ AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR2_REG066H,
+ AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR3_REG067H,
+ AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_NOTCH_THR_REG068H,
+ AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR1_REG069H,
+ AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR2_REG06AH,
+ AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR3_REG06BH,
+ AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS);
+ if (is_svideo) {
+ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH,
+ AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_SVIDEO);
+ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH,
+ AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_SVIDEO);
+ } else {
+ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH,
+ AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH,
+ AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS);
+ }
+ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH,
+ AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_UV_SEP_THR_REG06FH,
+ AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H,
+ AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS);
+ au8522_writereg(state, AU8522_REG071H, AU8522_REG071H_CVBS);
+ au8522_writereg(state, AU8522_REG072H, AU8522_REG072H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H,
+ AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS);
+ au8522_writereg(state, AU8522_REG074H, AU8522_REG074H_CVBS);
+ au8522_writereg(state, AU8522_REG075H, AU8522_REG075H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_DCAGC_CTRL_REG077H,
+ AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_PIC_START_ADJ_REG078H,
+ AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H,
+ AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH,
+ AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_INTRP_CTRL_REG07BH,
+ AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H,
+ AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS);
+ au8522_writereg(state, AU8522_TOREGAAGC_REG0E5H,
+ AU8522_TOREGAAGC_REG0E5H_CVBS);
+ au8522_writereg(state, AU8522_REG016H, AU8522_REG016H_CVBS);
+
+ /*
+ * Despite what the table says, for the HVR-950q we still need
+ * to be in CVBS mode for the S-Video input (reason unknown).
+ */
+ /* filter_coef_type = 3; */
+ filter_coef_type = 5;
+
+ /* Load the Video Decoder Filter Coefficients */
+ for (i = 0; i < NUM_FILTER_COEF; i++) {
+ au8522_writereg(state, filter_coef[i].reg_name,
+ filter_coef[i].reg_val[filter_coef_type]);
+ }
+
+ /* It's not clear what these registers are for, but they are always
+ set to the same value regardless of what mode we're in */
+ au8522_writereg(state, AU8522_REG42EH, 0x87);
+ au8522_writereg(state, AU8522_REG42FH, 0xa2);
+ au8522_writereg(state, AU8522_REG430H, 0xbf);
+ au8522_writereg(state, AU8522_REG431H, 0xcb);
+ au8522_writereg(state, AU8522_REG432H, 0xa1);
+ au8522_writereg(state, AU8522_REG433H, 0x41);
+ au8522_writereg(state, AU8522_REG434H, 0x88);
+ au8522_writereg(state, AU8522_REG435H, 0xc2);
+ au8522_writereg(state, AU8522_REG436H, 0x3c);
+}
+
+static void au8522_setup_cvbs_mode(struct au8522_state *state, u8 input_mode)
+{
+ /* here we're going to try the pre-programmed route */
+ au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,
+ AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS);
+
+ /* PGA in automatic mode */
+ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00);
+
+ /* Enable clamping control */
+ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00);
+
+ au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, input_mode);
+
+ setup_decoder_defaults(state, false);
+
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
+ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
+}
+
+static void au8522_setup_cvbs_tuner_mode(struct au8522_state *state,
+ u8 input_mode)
+{
+ /* here we're going to try the pre-programmed route */
+ au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,
+ AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS);
+
+ /* It's not clear why we have to have the PGA in automatic mode while
+ enabling clamp control, but it's what Windows does */
+ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00);
+
+ /* Enable clamping control */
+ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x0e);
+
+ /* Disable automatic PGA (since the CVBS is coming from the tuner) */
+ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x10);
+
+ /* Set input mode to CVBS on channel 4 with SIF audio input enabled */
+ au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, input_mode);
+
+ setup_decoder_defaults(state, false);
+
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
+ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
+}
+
+static void au8522_setup_svideo_mode(struct au8522_state *state,
+ u8 input_mode)
+{
+ au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,
+ AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO);
+
+ /* Set input to Y on Channe1, C on Channel 3 */
+ au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, input_mode);
+
+ /* PGA in automatic mode */
+ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00);
+
+ /* Enable clamping control */
+ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00);
+
+ setup_decoder_defaults(state, true);
+
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
+ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void disable_audio_input(struct au8522_state *state)
+{
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00);
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00);
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00);
+
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x04);
+ au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0x02);
+
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
+ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_SVIDEO);
+}
+
+/* 0=disable, 1=SIF */
+static void set_audio_input(struct au8522_state *state)
+{
+ int aud_input = state->aud_input;
+ int i;
+
+ /* Note that this function needs to be used in conjunction with setting
+ the input routing via register 0x81 */
+
+ if (aud_input == AU8522_AUDIO_NONE) {
+ disable_audio_input(state);
+ return;
+ }
+
+ if (aud_input != AU8522_AUDIO_SIF) {
+ /* The caller asked for a mode we don't currently support */
+ printk(KERN_ERR "Unsupported audio mode requested! mode=%d\n",
+ aud_input);
+ return;
+ }
+
+ /* Load the Audio Decoder Filter Coefficients */
+ for (i = 0; i < NUM_LPFILTER_COEF; i++) {
+ au8522_writereg(state, lpfilter_coef[i].reg_name,
+ lpfilter_coef[i].reg_val[0]);
+ }
+
+ /* Set the volume */
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F);
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F);
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0xff);
+
+ /* Not sure what this does */
+ au8522_writereg(state, AU8522_REG0F9H, AU8522_REG0F9H_AUDIO);
+
+ /* Setup the audio mode to stereo DBX */
+ au8522_writereg(state, AU8522_AUDIO_MODE_REG0F1H, 0x82);
+ msleep(70);
+
+ /* Start the audio processing module */
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x9d);
+
+ /* Set the audio frequency to 48 KHz */
+ au8522_writereg(state, AU8522_AUDIOFREQ_REG606H, 0x03);
+
+ /* Set the I2S parameters (WS, LSB, mode, sample rate */
+ au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0xc2);
+
+ /* Enable the I2S output */
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x09);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int au8522_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct au8522_state *state =
+ container_of(ctrl->handler, struct au8522_state, hdl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH,
+ ctrl->val - 128);
+ break;
+ case V4L2_CID_CONTRAST:
+ au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH,
+ ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH,
+ ctrl->val);
+ au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH,
+ ctrl->val);
+ break;
+ case V4L2_CID_HUE:
+ au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH,
+ ctrl->val >> 8);
+ au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH,
+ ctrl->val & 0xFF);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int au8522_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct au8522_state *state = to_state(sd);
+
+ reg->val = au8522_readreg(state, reg->reg & 0xffff);
+ return 0;
+}
+
+static int au8522_s_register(struct v4l2_subdev *sd,
+ const struct v4l2_dbg_register *reg)
+{
+ struct au8522_state *state = to_state(sd);
+
+ au8522_writereg(state, reg->reg, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static void au8522_video_set(struct au8522_state *state)
+{
+ u8 input_mode;
+
+ au8522_writereg(state, 0xa4, 1 << 5);
+
+ switch (state->vid_input) {
+ case AU8522_COMPOSITE_CH1:
+ input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH1;
+ au8522_setup_cvbs_mode(state, input_mode);
+ break;
+ case AU8522_COMPOSITE_CH2:
+ input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH2;
+ au8522_setup_cvbs_mode(state, input_mode);
+ break;
+ case AU8522_COMPOSITE_CH3:
+ input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH3;
+ au8522_setup_cvbs_mode(state, input_mode);
+ break;
+ case AU8522_COMPOSITE_CH4:
+ input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH4;
+ au8522_setup_cvbs_mode(state, input_mode);
+ break;
+ case AU8522_SVIDEO_CH13:
+ input_mode = AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13;
+ au8522_setup_svideo_mode(state, input_mode);
+ break;
+ case AU8522_SVIDEO_CH24:
+ input_mode = AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24;
+ au8522_setup_svideo_mode(state, input_mode);
+ break;
+ default:
+ case AU8522_COMPOSITE_CH4_SIF:
+ input_mode = AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF;
+ au8522_setup_cvbs_tuner_mode(state, input_mode);
+ break;
+ }
+}
+
+static int au8522_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct au8522_state *state = to_state(sd);
+
+ if (enable) {
+ /*
+ * Clear out any state associated with the digital side of the
+ * chip, so that when it gets powered back up it won't think
+ * that it is already tuned
+ */
+ state->current_frequency = 0;
+
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
+ 0x01);
+ msleep(10);
+
+ au8522_video_set(state);
+ set_audio_input(state);
+
+ state->operational_mode = AU8522_ANALOG_MODE;
+ } else {
+ /* This does not completely power down the device
+ (it only reduces it from around 140ma to 80ma) */
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
+ 1 << 5);
+ state->operational_mode = AU8522_SUSPEND_MODE;
+ }
+ return 0;
+}
+
+static int au8522_s_video_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct au8522_state *state = to_state(sd);
+
+ switch (input) {
+ case AU8522_COMPOSITE_CH1:
+ case AU8522_SVIDEO_CH13:
+ case AU8522_COMPOSITE_CH4_SIF:
+ state->vid_input = input;
+ break;
+ default:
+ printk(KERN_ERR "au8522 mode not currently supported\n");
+ return -EINVAL;
+ }
+
+ if (state->operational_mode == AU8522_ANALOG_MODE)
+ au8522_video_set(state);
+
+ return 0;
+}
+
+static int au8522_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct au8522_state *state = to_state(sd);
+
+ if ((std & (V4L2_STD_PAL_M | V4L2_STD_NTSC_M)) == 0)
+ return -EINVAL;
+
+ state->std = std;
+
+ if (state->operational_mode == AU8522_ANALOG_MODE)
+ au8522_video_set(state);
+
+ return 0;
+}
+
+static int au8522_s_audio_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct au8522_state *state = to_state(sd);
+
+ state->aud_input = input;
+
+ if (state->operational_mode == AU8522_ANALOG_MODE)
+ set_audio_input(state);
+
+ return 0;
+}
+
+static int au8522_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ int val = 0;
+ struct au8522_state *state = to_state(sd);
+ u8 lock_status;
+ u8 pll_status;
+
+ /* Interrogate the decoder to see if we are getting a real signal */
+ lock_status = au8522_readreg(state, 0x00);
+ pll_status = au8522_readreg(state, 0x7e);
+ if ((lock_status == 0xa2) && (pll_status & 0x10))
+ vt->signal = 0xffff;
+ else
+ vt->signal = 0x00;
+
+ vt->capability |=
+ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+
+ val = V4L2_TUNER_SUB_MONO;
+ vt->rxsubchans = val;
+ vt->audmode = V4L2_TUNER_MODE_STEREO;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops au8522_core_ops = {
+ .log_status = v4l2_ctrl_subdev_log_status,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = au8522_g_register,
+ .s_register = au8522_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops au8522_tuner_ops = {
+ .g_tuner = au8522_g_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops au8522_audio_ops = {
+ .s_routing = au8522_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops au8522_video_ops = {
+ .s_routing = au8522_s_video_routing,
+ .s_stream = au8522_s_stream,
+ .s_std = au8522_s_std,
+};
+
+static const struct v4l2_subdev_ops au8522_ops = {
+ .core = &au8522_core_ops,
+ .tuner = &au8522_tuner_ops,
+ .audio = &au8522_audio_ops,
+ .video = &au8522_video_ops,
+};
+
+static const struct v4l2_ctrl_ops au8522_ctrl_ops = {
+ .s_ctrl = au8522_s_ctrl,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int au8522_probe(struct i2c_client *client)
+{
+ struct au8522_state *state;
+ struct v4l2_ctrl_handler *hdl;
+ struct v4l2_subdev *sd;
+ int instance;
+#ifdef CONFIG_MEDIA_CONTROLLER
+ int ret;
+#endif
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ return -EIO;
+ }
+
+ /* allocate memory for the internal state */
+ instance = au8522_get_state(&state, client->adapter, client->addr);
+ switch (instance) {
+ case 0:
+ printk(KERN_ERR "au8522_decoder allocation failed\n");
+ return -EIO;
+ case 1:
+ /* new demod instance */
+ printk(KERN_INFO "au8522_decoder creating new instance...\n");
+ break;
+ default:
+ /* existing demod instance */
+ printk(KERN_INFO "au8522_decoder attach existing instance.\n");
+ break;
+ }
+
+ state->config.demod_address = 0x8e >> 1;
+ state->i2c = client->adapter;
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &au8522_ops);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+
+ state->pads[AU8522_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK;
+ state->pads[AU8522_PAD_IF_INPUT].sig_type = PAD_SIGNAL_ANALOG;
+ state->pads[AU8522_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ state->pads[AU8522_PAD_VID_OUT].sig_type = PAD_SIGNAL_DV;
+ state->pads[AU8522_PAD_AUDIO_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ state->pads[AU8522_PAD_AUDIO_OUT].sig_type = PAD_SIGNAL_AUDIO;
+ sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
+
+ ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(state->pads),
+ state->pads);
+ if (ret < 0) {
+ v4l_info(client, "failed to initialize media entity!\n");
+ return ret;
+ }
+#endif
+
+ hdl = &state->hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &au8522_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 109);
+ v4l2_ctrl_new_std(hdl, &au8522_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1,
+ AU8522_TVDEC_CONTRAST_REG00BH_CVBS);
+ v4l2_ctrl_new_std(hdl, &au8522_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &au8522_ctrl_ops,
+ V4L2_CID_HUE, -32768, 32767, 1, 0);
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ au8522_release_state(state);
+ return err;
+ }
+
+ state->c = client;
+ state->std = V4L2_STD_NTSC_M;
+ state->vid_input = AU8522_COMPOSITE_CH1;
+ state->aud_input = AU8522_AUDIO_NONE;
+ state->id = 8522;
+ state->rev = 0;
+
+ /* Jam open the i2c gate to the tuner */
+ au8522_writereg(state, 0x106, 1);
+
+ return 0;
+}
+
+static void au8522_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ au8522_release_state(to_state(sd));
+}
+
+static const struct i2c_device_id au8522_id[] = {
+ { "au8522" },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, au8522_id);
+
+static struct i2c_driver au8522_driver = {
+ .driver = {
+ .name = "au8522",
+ },
+ .probe = au8522_probe,
+ .remove = au8522_remove,
+ .id_table = au8522_id,
+};
+
+module_i2c_driver(au8522_driver);
diff --git a/drivers/media/dvb-frontends/au8522_dig.c b/drivers/media/dvb-frontends/au8522_dig.c
new file mode 100644
index 000000000000..230436bf6cbd
--- /dev/null
+++ b/drivers/media/dvb-frontends/au8522_dig.c
@@ -0,0 +1,918 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ Auvitek AU8522 QAM/8VSB demodulator driver
+
+ Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
+
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <media/dvb_frontend.h>
+#include "au8522.h"
+#include "au8522_priv.h"
+
+static int debug;
+static int zv_mode = 1; /* default to on */
+
+#define dprintk(arg...)\
+ do { if (debug)\
+ printk(arg);\
+ } while (0)
+
+struct mse2snr_tab {
+ u16 val;
+ u16 data;
+};
+
+/* VSB SNR lookup table */
+static struct mse2snr_tab vsb_mse2snr_tab[] = {
+ { 0, 270 },
+ { 2, 250 },
+ { 3, 240 },
+ { 5, 230 },
+ { 7, 220 },
+ { 9, 210 },
+ { 12, 200 },
+ { 13, 195 },
+ { 15, 190 },
+ { 17, 185 },
+ { 19, 180 },
+ { 21, 175 },
+ { 24, 170 },
+ { 27, 165 },
+ { 31, 160 },
+ { 32, 158 },
+ { 33, 156 },
+ { 36, 152 },
+ { 37, 150 },
+ { 39, 148 },
+ { 40, 146 },
+ { 41, 144 },
+ { 43, 142 },
+ { 44, 140 },
+ { 48, 135 },
+ { 50, 130 },
+ { 43, 142 },
+ { 53, 125 },
+ { 56, 120 },
+ { 256, 115 },
+};
+
+/* QAM64 SNR lookup table */
+static struct mse2snr_tab qam64_mse2snr_tab[] = {
+ { 15, 0 },
+ { 16, 290 },
+ { 17, 288 },
+ { 18, 286 },
+ { 19, 284 },
+ { 20, 282 },
+ { 21, 281 },
+ { 22, 279 },
+ { 23, 277 },
+ { 24, 275 },
+ { 25, 273 },
+ { 26, 271 },
+ { 27, 269 },
+ { 28, 268 },
+ { 29, 266 },
+ { 30, 264 },
+ { 31, 262 },
+ { 32, 260 },
+ { 33, 259 },
+ { 34, 258 },
+ { 35, 256 },
+ { 36, 255 },
+ { 37, 254 },
+ { 38, 252 },
+ { 39, 251 },
+ { 40, 250 },
+ { 41, 249 },
+ { 42, 248 },
+ { 43, 246 },
+ { 44, 245 },
+ { 45, 244 },
+ { 46, 242 },
+ { 47, 241 },
+ { 48, 240 },
+ { 50, 239 },
+ { 51, 238 },
+ { 53, 237 },
+ { 54, 236 },
+ { 56, 235 },
+ { 57, 234 },
+ { 59, 233 },
+ { 60, 232 },
+ { 62, 231 },
+ { 63, 230 },
+ { 65, 229 },
+ { 67, 228 },
+ { 68, 227 },
+ { 70, 226 },
+ { 71, 225 },
+ { 73, 224 },
+ { 74, 223 },
+ { 76, 222 },
+ { 78, 221 },
+ { 80, 220 },
+ { 82, 219 },
+ { 85, 218 },
+ { 88, 217 },
+ { 90, 216 },
+ { 92, 215 },
+ { 93, 214 },
+ { 94, 212 },
+ { 95, 211 },
+ { 97, 210 },
+ { 99, 209 },
+ { 101, 208 },
+ { 102, 207 },
+ { 104, 206 },
+ { 107, 205 },
+ { 111, 204 },
+ { 114, 203 },
+ { 118, 202 },
+ { 122, 201 },
+ { 125, 200 },
+ { 128, 199 },
+ { 130, 198 },
+ { 132, 197 },
+ { 256, 190 },
+};
+
+/* QAM256 SNR lookup table */
+static struct mse2snr_tab qam256_mse2snr_tab[] = {
+ { 15, 0 },
+ { 16, 400 },
+ { 17, 398 },
+ { 18, 396 },
+ { 19, 394 },
+ { 20, 392 },
+ { 21, 390 },
+ { 22, 388 },
+ { 23, 386 },
+ { 24, 384 },
+ { 25, 382 },
+ { 26, 380 },
+ { 27, 379 },
+ { 28, 378 },
+ { 29, 377 },
+ { 30, 376 },
+ { 31, 375 },
+ { 32, 374 },
+ { 33, 373 },
+ { 34, 372 },
+ { 35, 371 },
+ { 36, 370 },
+ { 37, 362 },
+ { 38, 354 },
+ { 39, 346 },
+ { 40, 338 },
+ { 41, 330 },
+ { 42, 328 },
+ { 43, 326 },
+ { 44, 324 },
+ { 45, 322 },
+ { 46, 320 },
+ { 47, 319 },
+ { 48, 318 },
+ { 49, 317 },
+ { 50, 316 },
+ { 51, 315 },
+ { 52, 314 },
+ { 53, 313 },
+ { 54, 312 },
+ { 55, 311 },
+ { 56, 310 },
+ { 57, 308 },
+ { 58, 306 },
+ { 59, 304 },
+ { 60, 302 },
+ { 61, 300 },
+ { 62, 298 },
+ { 65, 295 },
+ { 68, 294 },
+ { 70, 293 },
+ { 73, 292 },
+ { 76, 291 },
+ { 78, 290 },
+ { 79, 289 },
+ { 81, 288 },
+ { 82, 287 },
+ { 83, 286 },
+ { 84, 285 },
+ { 85, 284 },
+ { 86, 283 },
+ { 88, 282 },
+ { 89, 281 },
+ { 256, 280 },
+};
+
+static int au8522_mse2snr_lookup(struct mse2snr_tab *tab, int sz, int mse,
+ u16 *snr)
+{
+ int i, ret = -EINVAL;
+ dprintk("%s()\n", __func__);
+
+ for (i = 0; i < sz; i++) {
+ if (mse < tab[i].val) {
+ *snr = tab[i].data;
+ ret = 0;
+ break;
+ }
+ }
+ dprintk("%s() snr=%d\n", __func__, *snr);
+ return ret;
+}
+
+static int au8522_set_if(struct dvb_frontend *fe, enum au8522_if_freq if_freq)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+ u8 r0b5, r0b6, r0b7;
+ char *ifmhz;
+
+ switch (if_freq) {
+ case AU8522_IF_3_25MHZ:
+ ifmhz = "3.25";
+ r0b5 = 0x00;
+ r0b6 = 0x3d;
+ r0b7 = 0xa0;
+ break;
+ case AU8522_IF_4MHZ:
+ ifmhz = "4.00";
+ r0b5 = 0x00;
+ r0b6 = 0x4b;
+ r0b7 = 0xd9;
+ break;
+ case AU8522_IF_6MHZ:
+ ifmhz = "6.00";
+ r0b5 = 0xfb;
+ r0b6 = 0x8e;
+ r0b7 = 0x39;
+ break;
+ default:
+ dprintk("%s() IF Frequency not supported\n", __func__);
+ return -EINVAL;
+ }
+ dprintk("%s() %s MHz\n", __func__, ifmhz);
+ au8522_writereg(state, 0x00b5, r0b5);
+ au8522_writereg(state, 0x00b6, r0b6);
+ au8522_writereg(state, 0x00b7, r0b7);
+
+ return 0;
+}
+
+/* VSB Modulation table */
+static struct {
+ u16 reg;
+ u16 data;
+} VSB_mod_tab[] = {
+ { 0x0090, 0x84 },
+ { 0x2005, 0x00 },
+ { 0x0091, 0x80 },
+ { 0x00a3, 0x0c },
+ { 0x00a4, 0xe8 },
+ { 0x0081, 0xc4 },
+ { 0x00a5, 0x40 },
+ { 0x00a7, 0x40 },
+ { 0x00a6, 0x67 },
+ { 0x0262, 0x20 },
+ { 0x021c, 0x30 },
+ { 0x00d8, 0x1a },
+ { 0x0227, 0xa0 },
+ { 0x0121, 0xff },
+ { 0x00a8, 0xf0 },
+ { 0x00a9, 0x05 },
+ { 0x00aa, 0x77 },
+ { 0x00ab, 0xf0 },
+ { 0x00ac, 0x05 },
+ { 0x00ad, 0x77 },
+ { 0x00ae, 0x41 },
+ { 0x00af, 0x66 },
+ { 0x021b, 0xcc },
+ { 0x021d, 0x80 },
+ { 0x00a4, 0xe8 },
+ { 0x0231, 0x13 },
+};
+
+/* QAM64 Modulation table */
+static struct {
+ u16 reg;
+ u16 data;
+} QAM64_mod_tab[] = {
+ { 0x00a3, 0x09 },
+ { 0x00a4, 0x00 },
+ { 0x0081, 0xc4 },
+ { 0x00a5, 0x40 },
+ { 0x00aa, 0x77 },
+ { 0x00ad, 0x77 },
+ { 0x00a6, 0x67 },
+ { 0x0262, 0x20 },
+ { 0x021c, 0x30 },
+ { 0x00b8, 0x3e },
+ { 0x00b9, 0xf0 },
+ { 0x00ba, 0x01 },
+ { 0x00bb, 0x18 },
+ { 0x00bc, 0x50 },
+ { 0x00bd, 0x00 },
+ { 0x00be, 0xea },
+ { 0x00bf, 0xef },
+ { 0x00c0, 0xfc },
+ { 0x00c1, 0xbd },
+ { 0x00c2, 0x1f },
+ { 0x00c3, 0xfc },
+ { 0x00c4, 0xdd },
+ { 0x00c5, 0xaf },
+ { 0x00c6, 0x00 },
+ { 0x00c7, 0x38 },
+ { 0x00c8, 0x30 },
+ { 0x00c9, 0x05 },
+ { 0x00ca, 0x4a },
+ { 0x00cb, 0xd0 },
+ { 0x00cc, 0x01 },
+ { 0x00cd, 0xd9 },
+ { 0x00ce, 0x6f },
+ { 0x00cf, 0xf9 },
+ { 0x00d0, 0x70 },
+ { 0x00d1, 0xdf },
+ { 0x00d2, 0xf7 },
+ { 0x00d3, 0xc2 },
+ { 0x00d4, 0xdf },
+ { 0x00d5, 0x02 },
+ { 0x00d6, 0x9a },
+ { 0x00d7, 0xd0 },
+ { 0x0250, 0x0d },
+ { 0x0251, 0xcd },
+ { 0x0252, 0xe0 },
+ { 0x0253, 0x05 },
+ { 0x0254, 0xa7 },
+ { 0x0255, 0xff },
+ { 0x0256, 0xed },
+ { 0x0257, 0x5b },
+ { 0x0258, 0xae },
+ { 0x0259, 0xe6 },
+ { 0x025a, 0x3d },
+ { 0x025b, 0x0f },
+ { 0x025c, 0x0d },
+ { 0x025d, 0xea },
+ { 0x025e, 0xf2 },
+ { 0x025f, 0x51 },
+ { 0x0260, 0xf5 },
+ { 0x0261, 0x06 },
+ { 0x021a, 0x00 },
+ { 0x0546, 0x40 },
+ { 0x0210, 0xc7 },
+ { 0x0211, 0xaa },
+ { 0x0212, 0xab },
+ { 0x0213, 0x02 },
+ { 0x0502, 0x00 },
+ { 0x0121, 0x04 },
+ { 0x0122, 0x04 },
+ { 0x052e, 0x10 },
+ { 0x00a4, 0xca },
+ { 0x00a7, 0x40 },
+ { 0x0526, 0x01 },
+};
+
+/* QAM256 Modulation table */
+static struct {
+ u16 reg;
+ u16 data;
+} QAM256_mod_tab[] = {
+ { 0x00a3, 0x09 },
+ { 0x00a4, 0x00 },
+ { 0x0081, 0xc4 },
+ { 0x00a5, 0x40 },
+ { 0x00aa, 0x77 },
+ { 0x00ad, 0x77 },
+ { 0x00a6, 0x67 },
+ { 0x0262, 0x20 },
+ { 0x021c, 0x30 },
+ { 0x00b8, 0x3e },
+ { 0x00b9, 0xf0 },
+ { 0x00ba, 0x01 },
+ { 0x00bb, 0x18 },
+ { 0x00bc, 0x50 },
+ { 0x00bd, 0x00 },
+ { 0x00be, 0xea },
+ { 0x00bf, 0xef },
+ { 0x00c0, 0xfc },
+ { 0x00c1, 0xbd },
+ { 0x00c2, 0x1f },
+ { 0x00c3, 0xfc },
+ { 0x00c4, 0xdd },
+ { 0x00c5, 0xaf },
+ { 0x00c6, 0x00 },
+ { 0x00c7, 0x38 },
+ { 0x00c8, 0x30 },
+ { 0x00c9, 0x05 },
+ { 0x00ca, 0x4a },
+ { 0x00cb, 0xd0 },
+ { 0x00cc, 0x01 },
+ { 0x00cd, 0xd9 },
+ { 0x00ce, 0x6f },
+ { 0x00cf, 0xf9 },
+ { 0x00d0, 0x70 },
+ { 0x00d1, 0xdf },
+ { 0x00d2, 0xf7 },
+ { 0x00d3, 0xc2 },
+ { 0x00d4, 0xdf },
+ { 0x00d5, 0x02 },
+ { 0x00d6, 0x9a },
+ { 0x00d7, 0xd0 },
+ { 0x0250, 0x0d },
+ { 0x0251, 0xcd },
+ { 0x0252, 0xe0 },
+ { 0x0253, 0x05 },
+ { 0x0254, 0xa7 },
+ { 0x0255, 0xff },
+ { 0x0256, 0xed },
+ { 0x0257, 0x5b },
+ { 0x0258, 0xae },
+ { 0x0259, 0xe6 },
+ { 0x025a, 0x3d },
+ { 0x025b, 0x0f },
+ { 0x025c, 0x0d },
+ { 0x025d, 0xea },
+ { 0x025e, 0xf2 },
+ { 0x025f, 0x51 },
+ { 0x0260, 0xf5 },
+ { 0x0261, 0x06 },
+ { 0x021a, 0x00 },
+ { 0x0546, 0x40 },
+ { 0x0210, 0x26 },
+ { 0x0211, 0xf6 },
+ { 0x0212, 0x84 },
+ { 0x0213, 0x02 },
+ { 0x0502, 0x01 },
+ { 0x0121, 0x04 },
+ { 0x0122, 0x04 },
+ { 0x052e, 0x10 },
+ { 0x00a4, 0xca },
+ { 0x00a7, 0x40 },
+ { 0x0526, 0x01 },
+};
+
+static struct {
+ u16 reg;
+ u16 data;
+} QAM256_mod_tab_zv_mode[] = {
+ { 0x80a3, 0x09 },
+ { 0x80a4, 0x00 },
+ { 0x8081, 0xc4 },
+ { 0x80a5, 0x40 },
+ { 0x80b5, 0xfb },
+ { 0x80b6, 0x8e },
+ { 0x80b7, 0x39 },
+ { 0x80aa, 0x77 },
+ { 0x80ad, 0x77 },
+ { 0x80a6, 0x67 },
+ { 0x8262, 0x20 },
+ { 0x821c, 0x30 },
+ { 0x80b8, 0x3e },
+ { 0x80b9, 0xf0 },
+ { 0x80ba, 0x01 },
+ { 0x80bb, 0x18 },
+ { 0x80bc, 0x50 },
+ { 0x80bd, 0x00 },
+ { 0x80be, 0xea },
+ { 0x80bf, 0xef },
+ { 0x80c0, 0xfc },
+ { 0x80c1, 0xbd },
+ { 0x80c2, 0x1f },
+ { 0x80c3, 0xfc },
+ { 0x80c4, 0xdd },
+ { 0x80c5, 0xaf },
+ { 0x80c6, 0x00 },
+ { 0x80c7, 0x38 },
+ { 0x80c8, 0x30 },
+ { 0x80c9, 0x05 },
+ { 0x80ca, 0x4a },
+ { 0x80cb, 0xd0 },
+ { 0x80cc, 0x01 },
+ { 0x80cd, 0xd9 },
+ { 0x80ce, 0x6f },
+ { 0x80cf, 0xf9 },
+ { 0x80d0, 0x70 },
+ { 0x80d1, 0xdf },
+ { 0x80d2, 0xf7 },
+ { 0x80d3, 0xc2 },
+ { 0x80d4, 0xdf },
+ { 0x80d5, 0x02 },
+ { 0x80d6, 0x9a },
+ { 0x80d7, 0xd0 },
+ { 0x8250, 0x0d },
+ { 0x8251, 0xcd },
+ { 0x8252, 0xe0 },
+ { 0x8253, 0x05 },
+ { 0x8254, 0xa7 },
+ { 0x8255, 0xff },
+ { 0x8256, 0xed },
+ { 0x8257, 0x5b },
+ { 0x8258, 0xae },
+ { 0x8259, 0xe6 },
+ { 0x825a, 0x3d },
+ { 0x825b, 0x0f },
+ { 0x825c, 0x0d },
+ { 0x825d, 0xea },
+ { 0x825e, 0xf2 },
+ { 0x825f, 0x51 },
+ { 0x8260, 0xf5 },
+ { 0x8261, 0x06 },
+ { 0x821a, 0x01 },
+ { 0x8546, 0x40 },
+ { 0x8210, 0x26 },
+ { 0x8211, 0xf6 },
+ { 0x8212, 0x84 },
+ { 0x8213, 0x02 },
+ { 0x8502, 0x01 },
+ { 0x8121, 0x04 },
+ { 0x8122, 0x04 },
+ { 0x852e, 0x10 },
+ { 0x80a4, 0xca },
+ { 0x80a7, 0x40 },
+ { 0x8526, 0x01 },
+};
+
+static int au8522_enable_modulation(struct dvb_frontend *fe,
+ enum fe_modulation m)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+ int i;
+
+ dprintk("%s(0x%08x)\n", __func__, m);
+
+ switch (m) {
+ case VSB_8:
+ dprintk("%s() VSB_8\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(VSB_mod_tab); i++)
+ au8522_writereg(state,
+ VSB_mod_tab[i].reg,
+ VSB_mod_tab[i].data);
+ au8522_set_if(fe, state->config.vsb_if);
+ break;
+ case QAM_64:
+ dprintk("%s() QAM 64\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(QAM64_mod_tab); i++)
+ au8522_writereg(state,
+ QAM64_mod_tab[i].reg,
+ QAM64_mod_tab[i].data);
+ au8522_set_if(fe, state->config.qam_if);
+ break;
+ case QAM_256:
+ if (zv_mode) {
+ dprintk("%s() QAM 256 (zv_mode)\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(QAM256_mod_tab_zv_mode); i++)
+ au8522_writereg(state,
+ QAM256_mod_tab_zv_mode[i].reg,
+ QAM256_mod_tab_zv_mode[i].data);
+ au8522_set_if(fe, state->config.qam_if);
+ msleep(100);
+ au8522_writereg(state, 0x821a, 0x00);
+ } else {
+ dprintk("%s() QAM 256\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(QAM256_mod_tab); i++)
+ au8522_writereg(state,
+ QAM256_mod_tab[i].reg,
+ QAM256_mod_tab[i].data);
+ au8522_set_if(fe, state->config.qam_if);
+ }
+ break;
+ default:
+ dprintk("%s() Invalid modulation\n", __func__);
+ return -EINVAL;
+ }
+
+ state->current_modulation = m;
+
+ return 0;
+}
+
+/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
+static int au8522_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct au8522_state *state = fe->demodulator_priv;
+ int ret = -EINVAL;
+
+ dprintk("%s(frequency=%d)\n", __func__, c->frequency);
+
+ if ((state->current_frequency == c->frequency) &&
+ (state->current_modulation == c->modulation))
+ return 0;
+
+ if (fe->ops.tuner_ops.set_params) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ ret = fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ /* Allow the tuner to settle */
+ if (zv_mode) {
+ dprintk("%s() increase tuner settling time for zv_mode\n",
+ __func__);
+ msleep(250);
+ } else
+ msleep(100);
+
+ au8522_enable_modulation(fe, c->modulation);
+
+ state->current_frequency = c->frequency;
+
+ return 0;
+}
+
+static int au8522_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+ u8 reg;
+ u32 tuner_status = 0;
+
+ *status = 0;
+
+ if (state->current_modulation == VSB_8) {
+ dprintk("%s() Checking VSB_8\n", __func__);
+ reg = au8522_readreg(state, 0x0088);
+ if ((reg & 0x03) == 0x03)
+ *status |= FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI;
+ } else {
+ dprintk("%s() Checking QAM\n", __func__);
+ reg = au8522_readreg(state, 0x0541);
+ if (reg & 0x80)
+ *status |= FE_HAS_VITERBI;
+ if (reg & 0x20)
+ *status |= FE_HAS_LOCK | FE_HAS_SYNC;
+ }
+
+ switch (state->config.status_mode) {
+ case AU8522_DEMODLOCKING:
+ dprintk("%s() DEMODLOCKING\n", __func__);
+ if (*status & FE_HAS_VITERBI)
+ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
+ break;
+ case AU8522_TUNERLOCKING:
+ /* Get the tuner status */
+ dprintk("%s() TUNERLOCKING\n", __func__);
+ if (fe->ops.tuner_ops.get_status) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ fe->ops.tuner_ops.get_status(fe, &tuner_status);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+ if (tuner_status)
+ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
+ break;
+ }
+ state->fe_status = *status;
+
+ if (*status & FE_HAS_LOCK)
+ /* turn on LED, if it isn't on already */
+ au8522_led_ctrl(state, -1);
+ else
+ /* turn off LED */
+ au8522_led_ctrl(state, 0);
+
+ dprintk("%s() status 0x%08x\n", __func__, *status);
+
+ return 0;
+}
+
+static int au8522_led_status(struct au8522_state *state, const u16 *snr)
+{
+ struct au8522_led_config *led_config = state->config.led_cfg;
+ int led;
+ u16 strong;
+
+ /* bail out if we can't control an LED */
+ if (!led_config)
+ return 0;
+
+ if (0 == (state->fe_status & FE_HAS_LOCK))
+ return au8522_led_ctrl(state, 0);
+ else if (state->current_modulation == QAM_256)
+ strong = led_config->qam256_strong;
+ else if (state->current_modulation == QAM_64)
+ strong = led_config->qam64_strong;
+ else /* (state->current_modulation == VSB_8) */
+ strong = led_config->vsb8_strong;
+
+ if (*snr >= strong)
+ led = 2;
+ else
+ led = 1;
+
+ if ((state->led_state) &&
+ (((strong < *snr) ? (*snr - strong) : (strong - *snr)) <= 10))
+ /* snr didn't change enough to bother
+ * changing the color of the led */
+ return 0;
+
+ return au8522_led_ctrl(state, led);
+}
+
+static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+ int ret = -EINVAL;
+
+ dprintk("%s()\n", __func__);
+
+ if (state->current_modulation == QAM_256)
+ ret = au8522_mse2snr_lookup(qam256_mse2snr_tab,
+ ARRAY_SIZE(qam256_mse2snr_tab),
+ au8522_readreg(state, 0x0522),
+ snr);
+ else if (state->current_modulation == QAM_64)
+ ret = au8522_mse2snr_lookup(qam64_mse2snr_tab,
+ ARRAY_SIZE(qam64_mse2snr_tab),
+ au8522_readreg(state, 0x0522),
+ snr);
+ else /* VSB_8 */
+ ret = au8522_mse2snr_lookup(vsb_mse2snr_tab,
+ ARRAY_SIZE(vsb_mse2snr_tab),
+ au8522_readreg(state, 0x0311),
+ snr);
+
+ if (state->config.led_cfg)
+ au8522_led_status(state, snr);
+
+ return ret;
+}
+
+static int au8522_read_signal_strength(struct dvb_frontend *fe,
+ u16 *signal_strength)
+{
+ /* borrowed from lgdt330x.c
+ *
+ * Calculate strength from SNR up to 35dB
+ * Even though the SNR can go higher than 35dB,
+ * there is some comfort factor in having a range of
+ * strong signals that can show at 100%
+ */
+ u16 snr;
+ u32 tmp;
+ int ret = au8522_read_snr(fe, &snr);
+
+ *signal_strength = 0;
+
+ if (0 == ret) {
+ /* The following calculation method was chosen
+ * purely for the sake of code re-use from the
+ * other demod drivers that use this method */
+
+ /* Convert from SNR in dB * 10 to 8.24 fixed-point */
+ tmp = (snr * ((1 << 24) / 10));
+
+ /* Convert from 8.24 fixed-point to
+ * scale the range 0 - 35*2^24 into 0 - 65535*/
+ if (tmp >= 8960 * 0x10000)
+ *signal_strength = 0xffff;
+ else
+ *signal_strength = tmp / 8960;
+ }
+
+ return ret;
+}
+
+static int au8522_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+
+ if (state->current_modulation == VSB_8)
+ *ucblocks = au8522_readreg(state, 0x0087);
+ else
+ *ucblocks = au8522_readreg(state, 0x0543);
+
+ return 0;
+}
+
+static int au8522_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ return au8522_read_ucblocks(fe, ber);
+}
+
+static int au8522_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+
+ c->frequency = state->current_frequency;
+ c->modulation = state->current_modulation;
+
+ return 0;
+}
+
+static int au8522_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ return 0;
+}
+
+static const struct dvb_frontend_ops au8522_ops;
+
+
+static void au8522_release(struct dvb_frontend *fe)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+ au8522_release_state(state);
+}
+
+struct dvb_frontend *au8522_attach(const struct au8522_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct au8522_state *state = NULL;
+ int instance;
+
+ /* allocate memory for the internal state */
+ instance = au8522_get_state(&state, i2c, config->demod_address);
+ switch (instance) {
+ case 0:
+ dprintk("%s state allocation failed\n", __func__);
+ break;
+ case 1:
+ /* new demod instance */
+ dprintk("%s using new instance\n", __func__);
+ break;
+ default:
+ /* existing demod instance */
+ dprintk("%s using existing instance\n", __func__);
+ break;
+ }
+
+ /* setup the state */
+ state->config = *config;
+ state->i2c = i2c;
+ state->operational_mode = AU8522_DIGITAL_MODE;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &au8522_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ state->frontend.ops.analog_ops.i2c_gate_ctrl = au8522_analog_i2c_gate_ctrl;
+
+ if (au8522_init(&state->frontend) != 0) {
+ printk(KERN_ERR "%s: Failed to initialize correctly\n",
+ __func__);
+ goto error;
+ }
+
+ /* Note: Leaving the I2C gate open here. */
+ au8522_i2c_gate_ctrl(&state->frontend, 1);
+
+ return &state->frontend;
+
+error:
+ au8522_release_state(state);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(au8522_attach);
+
+static const struct dvb_frontend_ops au8522_ops = {
+ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
+ .info = {
+ .name = "Auvitek AU8522 QAM/8VSB Frontend",
+ .frequency_min_hz = 54 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 62500,
+ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+ },
+
+ .init = au8522_init,
+ .sleep = au8522_sleep,
+ .i2c_gate_ctrl = au8522_i2c_gate_ctrl,
+ .set_frontend = au8522_set_frontend,
+ .get_frontend = au8522_get_frontend,
+ .get_tune_settings = au8522_get_tune_settings,
+ .read_status = au8522_read_status,
+ .read_ber = au8522_read_ber,
+ .read_signal_strength = au8522_read_signal_strength,
+ .read_snr = au8522_read_snr,
+ .read_ucblocks = au8522_read_ucblocks,
+ .release = au8522_release,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable verbose debug messages");
+
+module_param(zv_mode, int, 0644);
+MODULE_PARM_DESC(zv_mode, "Turn on/off ZeeVee modulator compatibility mode (default:on).\n"
+ "\t\ton - modified AU8522 QAM256 initialization.\n"
+ "\t\tProvides faster lock when using ZeeVee modulator based sources");
+
+MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
+MODULE_AUTHOR("Steven Toth");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/au8522_priv.h b/drivers/media/dvb-frontends/au8522_priv.h
new file mode 100644
index 000000000000..19887aa809ef
--- /dev/null
+++ b/drivers/media/dvb-frontends/au8522_priv.h
@@ -0,0 +1,446 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Auvitek AU8522 QAM/8VSB demodulator driver
+
+ Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
+ Copyright (C) 2008 Devin Heitmueller <dheitmueller@linuxtv.org>
+ Copyright (C) 2005-2008 Auvitek International, Ltd.
+
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-mc.h>
+#include <linux/i2c.h>
+#include <media/dvb_frontend.h>
+#include "au8522.h"
+#include "tuner-i2c.h"
+
+#define AU8522_ANALOG_MODE 0
+#define AU8522_DIGITAL_MODE 1
+#define AU8522_SUSPEND_MODE 2
+
+enum au8522_pads {
+ AU8522_PAD_IF_INPUT,
+ AU8522_PAD_VID_OUT,
+ AU8522_PAD_AUDIO_OUT,
+ AU8522_NUM_PADS
+};
+
+struct au8522_state {
+ struct i2c_client *c;
+ struct i2c_adapter *i2c;
+
+ u8 operational_mode;
+
+ /* Used for sharing of the state between analog and digital mode */
+ struct tuner_i2c_props i2c_props;
+ struct list_head hybrid_tuner_instance_list;
+
+ /* configuration settings */
+ struct au8522_config config;
+
+ struct dvb_frontend frontend;
+
+ u32 current_frequency;
+ enum fe_modulation current_modulation;
+
+ u32 fe_status;
+ unsigned int led_state;
+
+ /* Analog settings */
+ struct v4l2_subdev sd;
+ v4l2_std_id std;
+ int vid_input;
+ int aud_input;
+ u32 id;
+ u32 rev;
+ struct v4l2_ctrl_handler hdl;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_pad pads[AU8522_NUM_PADS];
+#endif
+};
+
+/* These are routines shared by both the VSB/QAM demodulator and the analog
+ decoder */
+int au8522_writereg(struct au8522_state *state, u16 reg, u8 data);
+u8 au8522_readreg(struct au8522_state *state, u16 reg);
+int au8522_init(struct dvb_frontend *fe);
+int au8522_sleep(struct dvb_frontend *fe);
+
+int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
+ u8 client_address);
+void au8522_release_state(struct au8522_state *state);
+int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable);
+int au8522_analog_i2c_gate_ctrl(struct dvb_frontend *fe, int enable);
+int au8522_led_ctrl(struct au8522_state *state, int led);
+
+/* REGISTERS */
+#define AU8522_INPUT_CONTROL_REG081H 0x081
+#define AU8522_PGA_CONTROL_REG082H 0x082
+#define AU8522_CLAMPING_CONTROL_REG083H 0x083
+
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H 0x0A3
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H 0x0A4
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H 0x0A5
+#define AU8522_AGC_CONTROL_RANGE_REG0A6H 0x0A6
+#define AU8522_SYSTEM_GAIN_CONTROL_REG0A7H 0x0A7
+#define AU8522_TUNER_AGC_RF_STOP_REG0A8H 0x0A8
+#define AU8522_TUNER_AGC_RF_START_REG0A9H 0x0A9
+#define AU8522_TUNER_RF_AGC_DEFAULT_REG0AAH 0x0AA
+#define AU8522_TUNER_AGC_IF_STOP_REG0ABH 0x0AB
+#define AU8522_TUNER_AGC_IF_START_REG0ACH 0x0AC
+#define AU8522_TUNER_AGC_IF_DEFAULT_REG0ADH 0x0AD
+#define AU8522_TUNER_AGC_STEP_REG0AEH 0x0AE
+#define AU8522_TUNER_GAIN_STEP_REG0AFH 0x0AF
+
+/* Receiver registers */
+#define AU8522_FRMREGTHRD1_REG0B0H 0x0B0
+#define AU8522_FRMREGAGC1H_REG0B1H 0x0B1
+#define AU8522_FRMREGSHIFT1_REG0B2H 0x0B2
+#define AU8522_TOREGAGC1_REG0B3H 0x0B3
+#define AU8522_TOREGASHIFT1_REG0B4H 0x0B4
+#define AU8522_FRMREGBBH_REG0B5H 0x0B5
+#define AU8522_FRMREGBBM_REG0B6H 0x0B6
+#define AU8522_FRMREGBBL_REG0B7H 0x0B7
+/* 0xB8 TO 0xD7 are the filter coefficients */
+#define AU8522_FRMREGTHRD2_REG0D8H 0x0D8
+#define AU8522_FRMREGAGC2H_REG0D9H 0x0D9
+#define AU8522_TOREGAGC2_REG0DAH 0x0DA
+#define AU8522_TOREGSHIFT2_REG0DBH 0x0DB
+#define AU8522_FRMREGPILOTH_REG0DCH 0x0DC
+#define AU8522_FRMREGPILOTM_REG0DDH 0x0DD
+#define AU8522_FRMREGPILOTL_REG0DEH 0x0DE
+#define AU8522_TOREGFREQ_REG0DFH 0x0DF
+
+#define AU8522_RX_PGA_RFOUT_REG0EBH 0x0EB
+#define AU8522_RX_PGA_IFOUT_REG0ECH 0x0EC
+#define AU8522_RX_PGA_PGAOUT_REG0EDH 0x0ED
+
+#define AU8522_CHIP_MODE_REG0FEH 0x0FE
+
+/* I2C bus control registers */
+#define AU8522_I2C_CONTROL_REG0_REG090H 0x090
+#define AU8522_I2C_CONTROL_REG1_REG091H 0x091
+#define AU8522_I2C_STATUS_REG092H 0x092
+#define AU8522_I2C_WR_DATA0_REG093H 0x093
+#define AU8522_I2C_WR_DATA1_REG094H 0x094
+#define AU8522_I2C_WR_DATA2_REG095H 0x095
+#define AU8522_I2C_WR_DATA3_REG096H 0x096
+#define AU8522_I2C_WR_DATA4_REG097H 0x097
+#define AU8522_I2C_WR_DATA5_REG098H 0x098
+#define AU8522_I2C_WR_DATA6_REG099H 0x099
+#define AU8522_I2C_WR_DATA7_REG09AH 0x09A
+#define AU8522_I2C_RD_DATA0_REG09BH 0x09B
+#define AU8522_I2C_RD_DATA1_REG09CH 0x09C
+#define AU8522_I2C_RD_DATA2_REG09DH 0x09D
+#define AU8522_I2C_RD_DATA3_REG09EH 0x09E
+#define AU8522_I2C_RD_DATA4_REG09FH 0x09F
+#define AU8522_I2C_RD_DATA5_REG0A0H 0x0A0
+#define AU8522_I2C_RD_DATA6_REG0A1H 0x0A1
+#define AU8522_I2C_RD_DATA7_REG0A2H 0x0A2
+
+#define AU8522_ENA_USB_REG101H 0x101
+
+#define AU8522_I2S_CTRL_0_REG110H 0x110
+#define AU8522_I2S_CTRL_1_REG111H 0x111
+#define AU8522_I2S_CTRL_2_REG112H 0x112
+
+#define AU8522_FRMREGFFECONTROL_REG121H 0x121
+#define AU8522_FRMREGDFECONTROL_REG122H 0x122
+
+#define AU8522_CARRFREQOFFSET0_REG201H 0x201
+#define AU8522_CARRFREQOFFSET1_REG202H 0x202
+
+#define AU8522_DECIMATION_GAIN_REG21AH 0x21A
+#define AU8522_FRMREGIFSLP_REG21BH 0x21B
+#define AU8522_FRMREGTHRDL2_REG21CH 0x21C
+#define AU8522_FRMREGSTEP3DB_REG21DH 0x21D
+#define AU8522_DAGC_GAIN_ADJUSTMENT_REG21EH 0x21E
+#define AU8522_FRMREGPLLMODE_REG21FH 0x21F
+#define AU8522_FRMREGCSTHRD_REG220H 0x220
+#define AU8522_FRMREGCRLOCKDMAX_REG221H 0x221
+#define AU8522_FRMREGCRPERIODMASK_REG222H 0x222
+#define AU8522_FRMREGCRLOCK0THH_REG223H 0x223
+#define AU8522_FRMREGCRLOCK1THH_REG224H 0x224
+#define AU8522_FRMREGCRLOCK0THL_REG225H 0x225
+#define AU8522_FRMREGCRLOCK1THL_REG226H 0x226
+#define AU_FRMREGPLLACQPHASESCL_REG227H 0x227
+#define AU8522_FRMREGFREQFBCTRL_REG228H 0x228
+
+/* Analog TV Decoder */
+#define AU8522_TVDEC_STATUS_REG000H 0x000
+#define AU8522_TVDEC_INT_STATUS_REG001H 0x001
+#define AU8522_TVDEC_MACROVISION_STATUS_REG002H 0x002
+#define AU8522_TVDEC_SHARPNESSREG009H 0x009
+#define AU8522_TVDEC_BRIGHTNESS_REG00AH 0x00A
+#define AU8522_TVDEC_CONTRAST_REG00BH 0x00B
+#define AU8522_TVDEC_SATURATION_CB_REG00CH 0x00C
+#define AU8522_TVDEC_SATURATION_CR_REG00DH 0x00D
+#define AU8522_TVDEC_HUE_H_REG00EH 0x00E
+#define AU8522_TVDEC_HUE_L_REG00FH 0x00F
+#define AU8522_TVDEC_INT_MASK_REG010H 0x010
+#define AU8522_VIDEO_MODE_REG011H 0x011
+#define AU8522_TVDEC_PGA_REG012H 0x012
+#define AU8522_TVDEC_COMB_MODE_REG015H 0x015
+#define AU8522_REG016H 0x016
+#define AU8522_TVDED_DBG_MODE_REG060H 0x060
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H 0x061
+#define AU8522_TVDEC_FORMAT_CTRL2_REG062H 0x062
+#define AU8522_TVDEC_VCR_DET_LLIM_REG063H 0x063
+#define AU8522_TVDEC_VCR_DET_HLIM_REG064H 0x064
+#define AU8522_TVDEC_COMB_VDIF_THR1_REG065H 0x065
+#define AU8522_TVDEC_COMB_VDIF_THR2_REG066H 0x066
+#define AU8522_TVDEC_COMB_VDIF_THR3_REG067H 0x067
+#define AU8522_TVDEC_COMB_NOTCH_THR_REG068H 0x068
+#define AU8522_TVDEC_COMB_HDIF_THR1_REG069H 0x069
+#define AU8522_TVDEC_COMB_HDIF_THR2_REG06AH 0x06A
+#define AU8522_TVDEC_COMB_HDIF_THR3_REG06BH 0x06B
+#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH 0x06C
+#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH 0x06D
+#define AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH 0x06E
+#define AU8522_TVDEC_UV_SEP_THR_REG06FH 0x06F
+#define AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H 0x070
+#define AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H 0x073
+#define AU8522_TVDEC_DCAGC_CTRL_REG077H 0x077
+#define AU8522_TVDEC_PIC_START_ADJ_REG078H 0x078
+#define AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H 0x079
+#define AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH 0x07A
+#define AU8522_TVDEC_INTRP_CTRL_REG07BH 0x07B
+#define AU8522_TVDEC_PLL_STATUS_REG07EH 0x07E
+#define AU8522_TVDEC_FSC_FREQ_REG07FH 0x07F
+
+#define AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H 0x0E4
+#define AU8522_TOREGAAGC_REG0E5H 0x0E5
+
+#define AU8522_TVDEC_CHROMA_AGC_REG401H 0x401
+#define AU8522_TVDEC_CHROMA_SFT_REG402H 0x402
+#define AU8522_FILTER_COEF_R410 0x410
+#define AU8522_FILTER_COEF_R411 0x411
+#define AU8522_FILTER_COEF_R412 0x412
+#define AU8522_FILTER_COEF_R413 0x413
+#define AU8522_FILTER_COEF_R414 0x414
+#define AU8522_FILTER_COEF_R415 0x415
+#define AU8522_FILTER_COEF_R416 0x416
+#define AU8522_FILTER_COEF_R417 0x417
+#define AU8522_FILTER_COEF_R418 0x418
+#define AU8522_FILTER_COEF_R419 0x419
+#define AU8522_FILTER_COEF_R41A 0x41A
+#define AU8522_FILTER_COEF_R41B 0x41B
+#define AU8522_FILTER_COEF_R41C 0x41C
+#define AU8522_FILTER_COEF_R41D 0x41D
+#define AU8522_FILTER_COEF_R41E 0x41E
+#define AU8522_FILTER_COEF_R41F 0x41F
+#define AU8522_FILTER_COEF_R420 0x420
+#define AU8522_FILTER_COEF_R421 0x421
+#define AU8522_FILTER_COEF_R422 0x422
+#define AU8522_FILTER_COEF_R423 0x423
+#define AU8522_FILTER_COEF_R424 0x424
+#define AU8522_FILTER_COEF_R425 0x425
+#define AU8522_FILTER_COEF_R426 0x426
+#define AU8522_FILTER_COEF_R427 0x427
+#define AU8522_FILTER_COEF_R428 0x428
+#define AU8522_FILTER_COEF_R429 0x429
+#define AU8522_FILTER_COEF_R42A 0x42A
+#define AU8522_FILTER_COEF_R42B 0x42B
+#define AU8522_FILTER_COEF_R42C 0x42C
+#define AU8522_FILTER_COEF_R42D 0x42D
+
+/* VBI Control Registers */
+#define AU8522_TVDEC_VBI_RX_FIFO_CONTAIN_REG004H 0x004
+#define AU8522_TVDEC_VBI_TX_FIFO_CONTAIN_REG005H 0x005
+#define AU8522_TVDEC_VBI_RX_FIFO_READ_REG006H 0x006
+#define AU8522_TVDEC_VBI_FIFO_STATUS_REG007H 0x007
+#define AU8522_TVDEC_VBI_CTRL_H_REG017H 0x017
+#define AU8522_TVDEC_VBI_CTRL_L_REG018H 0x018
+#define AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H 0x019
+#define AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH 0x01A
+#define AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH 0x01B
+#define AU8522_TVDEC_VBI_USER_THRESH1_REG01CH 0x01C
+#define AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH 0x01E
+#define AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH 0x01F
+#define AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H 0x020
+#define AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H 0x021
+#define AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H 0x022
+#define AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H 0x023
+
+#define AU8522_REG071H 0x071
+#define AU8522_REG072H 0x072
+#define AU8522_REG074H 0x074
+#define AU8522_REG075H 0x075
+
+/* Digital Demodulator Registers */
+#define AU8522_FRAME_COUNT0_REG084H 0x084
+#define AU8522_RS_STATUS_G0_REG085H 0x085
+#define AU8522_RS_STATUS_B0_REG086H 0x086
+#define AU8522_RS_STATUS_E_REG087H 0x087
+#define AU8522_DEMODULATION_STATUS_REG088H 0x088
+#define AU8522_TOREGTRESTATUS_REG0E6H 0x0E6
+#define AU8522_TSPORT_CONTROL_REG10BH 0x10B
+#define AU8522_TSTHES_REG10CH 0x10C
+#define AU8522_FRMREGDFEKEEP_REG301H 0x301
+#define AU8522_DFE_AVERAGE_REG302H 0x302
+#define AU8522_FRMREGEQLERRWIN_REG303H 0x303
+#define AU8522_FRMREGFFEKEEP_REG304H 0x304
+#define AU8522_FRMREGDFECONTROL1_REG305H 0x305
+#define AU8522_FRMREGEQLERRLOW_REG306H 0x306
+
+#define AU8522_REG42EH 0x42E
+#define AU8522_REG42FH 0x42F
+#define AU8522_REG430H 0x430
+#define AU8522_REG431H 0x431
+#define AU8522_REG432H 0x432
+#define AU8522_REG433H 0x433
+#define AU8522_REG434H 0x434
+#define AU8522_REG435H 0x435
+#define AU8522_REG436H 0x436
+
+/* GPIO Registers */
+#define AU8522_GPIO_CONTROL_REG0E0H 0x0E0
+#define AU8522_GPIO_STATUS_REG0E1H 0x0E1
+#define AU8522_GPIO_DATA_REG0E2H 0x0E2
+
+/* Audio Control Registers */
+#define AU8522_AUDIOAGC_REG0EEH 0x0EE
+#define AU8522_AUDIO_STATUS_REG0F0H 0x0F0
+#define AU8522_AUDIO_MODE_REG0F1H 0x0F1
+#define AU8522_AUDIO_VOLUME_L_REG0F2H 0x0F2
+#define AU8522_AUDIO_VOLUME_R_REG0F3H 0x0F3
+#define AU8522_AUDIO_VOLUME_REG0F4H 0x0F4
+#define AU8522_FRMREGAUPHASE_REG0F7H 0x0F7
+#define AU8522_REG0F9H 0x0F9
+
+#define AU8522_AUDIOAGC2_REG605H 0x605
+#define AU8522_AUDIOFREQ_REG606H 0x606
+
+
+/**************************************************************/
+
+/* Format control 1 */
+
+/* VCR Mode 7-6 */
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_VCR_MODE_YES 0x80
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_VCR_MODE_NO 0x40
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_VCR_MODE_AUTO 0x00
+/* Field len 5-4 */
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_625 0x20
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_525 0x10
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_AUTO 0x00
+/* Line len (us) 3-2 */
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_64_000 0x0b
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_492 0x08
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_556 0x04
+/* Subcarrier freq 1-0 */
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_AUTO 0x03
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_443 0x02
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_MN 0x01
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_50 0x00
+
+/* Format control 2 */
+#define AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_AUTODETECT 0x00
+#define AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_NTSC 0x01
+#define AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_PAL_M 0x02
+
+
+#define AU8522_INPUT_CONTROL_REG081H_ATSC 0xC4
+#define AU8522_INPUT_CONTROL_REG081H_ATVRF 0xC4
+#define AU8522_INPUT_CONTROL_REG081H_ATVRF13 0xC4
+#define AU8522_INPUT_CONTROL_REG081H_J83B64 0xC4
+#define AU8522_INPUT_CONTROL_REG081H_J83B256 0xC4
+#define AU8522_INPUT_CONTROL_REG081H_CVBS 0x20
+#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH1 0xA2
+#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH2 0xA0
+#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH3 0x69
+#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH4 0x68
+#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF 0x28
+/* CH1 AS Y,CH3 AS C */
+#define AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 0x23
+/* CH2 AS Y,CH4 AS C */
+#define AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24 0x20
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATSC 0x0C
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_J83B64 0x09
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_J83B256 0x09
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS 0x12
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATVRF 0x1A
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATVRF13 0x1A
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO 0x02
+
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CLEAR 0x00
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_SVIDEO 0x9C
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS 0x9D
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATSC 0xE8
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_J83B256 0xCA
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_J83B64 0xCA
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATVRF 0xDD
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATVRF13 0xDD
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_PAL 0xDD
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_FM 0xDD
+
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATSC 0x80
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_J83B256 0x80
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_J83B64 0x80
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_ATSC 0x40
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_J83B256 0x40
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_J83B64 0x40
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_CLEAR 0x00
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATVRF 0x01
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATVRF13 0x01
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_SVIDEO 0x04
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_CVBS 0x01
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_PWM 0x03
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_IIS 0x09
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_PAL 0x01
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_FM 0x01
+
+/* STILL NEED TO BE REFACTORED @@@@@@@@@@@@@@ */
+#define AU8522_TVDEC_CONTRAST_REG00BH_CVBS 0x79
+#define AU8522_TVDEC_SATURATION_CB_REG00CH_CVBS 0x80
+#define AU8522_TVDEC_SATURATION_CR_REG00DH_CVBS 0x80
+#define AU8522_TVDEC_HUE_H_REG00EH_CVBS 0x00
+#define AU8522_TVDEC_HUE_L_REG00FH_CVBS 0x00
+#define AU8522_TVDEC_PGA_REG012H_CVBS 0x0F
+#define AU8522_TVDEC_COMB_MODE_REG015H_CVBS 0x00
+#define AU8522_REG016H_CVBS 0x00
+#define AU8522_TVDED_DBG_MODE_REG060H_CVBS 0x00
+#define AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS 0x19
+#define AU8522_REG0F9H_AUDIO 0x20
+#define AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS 0xA7
+#define AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS 0x0A
+#define AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS 0x32
+#define AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS 0x19
+#define AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS 0x23
+#define AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS 0x41
+#define AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS 0x0A
+#define AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS 0x32
+#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS 0x34
+#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_SVIDEO 0x2a
+#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS 0x05
+#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_SVIDEO 0x15
+#define AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS 0x6E
+#define AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS 0x0F
+#define AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS 0x80
+#define AU8522_REG071H_CVBS 0x18
+#define AU8522_REG072H_CVBS 0x30
+#define AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS 0xF0
+#define AU8522_REG074H_CVBS 0x80
+#define AU8522_REG075H_CVBS 0xF0
+#define AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS 0xFB
+#define AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS 0x04
+#define AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS 0x00
+#define AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS 0x00
+#define AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS 0xEE
+#define AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS 0xFE
+#define AU8522_TOREGAAGC_REG0E5H_CVBS 0x00
+#define AU8522_TVDEC_VBI6A_REG035H_CVBS 0x40
+
+/* Enables Closed captioning */
+#define AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON 0x21
diff --git a/drivers/media/dvb/frontends/bcm3510.c b/drivers/media/dvb-frontends/bcm3510.c
index 033cd7ad3ca2..d935fb10e620 100644
--- a/drivers/media/dvb/frontends/bcm3510.c
+++ b/drivers/media/dvb-frontends/bcm3510.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2001-5, B2C2 inc.
*
- * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@desy.de>
+ * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@posteo.de>
*
* This driver is "hard-coded" to be used with the 1st generation of
* Technisat/B2C2's Air2PC ATSC PCI/USB cards/boxes. The pll-programming
@@ -40,10 +40,13 @@
#include <linux/slab.h>
#include <linux/mutex.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "bcm3510.h"
#include "bcm3510_priv.h"
+/* Max transfer size done by bcm3510_do_hab_cmd() function */
+#define MAX_XFER_SIZE 128
+
struct bcm3510_state {
struct i2c_adapter* i2c;
@@ -201,9 +204,19 @@ static int bcm3510_hab_send_request(struct bcm3510_state *st, u8 *buf, int len)
static int bcm3510_do_hab_cmd(struct bcm3510_state *st, u8 cmd, u8 msgid, u8 *obuf, u8 olen, u8 *ibuf, u8 ilen)
{
- u8 ob[olen+2],ib[ilen+2];
+ u8 ob[MAX_XFER_SIZE], ib[MAX_XFER_SIZE];
int ret = 0;
+ if (ilen + 2 > sizeof(ib)) {
+ deb_hab("do_hab_cmd: ilen=%d is too big!\n", ilen);
+ return -EINVAL;
+ }
+
+ if (olen + 2 > sizeof(ob)) {
+ deb_hab("do_hab_cmd: olen=%d is too big!\n", olen);
+ return -EINVAL;
+ }
+
ob[0] = cmd;
ob[1] = msgid;
memcpy(&ob[2],obuf,olen);
@@ -276,7 +289,7 @@ static int bcm3510_refresh_state(struct bcm3510_state *st)
return 0;
}
-static int bcm3510_read_status(struct dvb_frontend *fe, fe_status_t *status)
+static int bcm3510_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct bcm3510_state* st = fe->demodulator_priv;
bcm3510_refresh_state(st);
@@ -525,9 +538,10 @@ static int bcm3510_set_frontend(struct dvb_frontend *fe)
cmd.ACQUIRE0.MODE = 0x9;
cmd.ACQUIRE1.SYM_RATE = 0x0;
cmd.ACQUIRE1.IF_FREQ = 0x0;
+ break;
default:
return -EINVAL;
- };
+ }
cmd.ACQUIRE0.OFFSET = 0;
cmd.ACQUIRE0.NTSCSWEEP = 1;
cmd.ACQUIRE0.FA = 1;
@@ -626,15 +640,16 @@ static int bcm3510_download_firmware(struct dvb_frontend* fe)
err("could not load firmware (%s): %d",BCM3510_DEFAULT_FIRMWARE,ret);
return ret;
}
- deb_info("got firmware: %zd\n",fw->size);
+ deb_info("got firmware: %zu\n", fw->size);
b = fw->data;
for (i = 0; i < fw->size;) {
- addr = le16_to_cpu( *( (u16 *)&b[i] ) );
- len = le16_to_cpu( *( (u16 *)&b[i+2] ) );
+ addr = le16_to_cpu(*((__le16 *)&b[i]));
+ len = le16_to_cpu(*((__le16 *)&b[i+2]));
deb_info("firmware chunk, addr: 0x%04x, len: 0x%04x, total length: 0x%04zx\n",addr,len,fw->size);
if ((ret = bcm3510_write_ram(st,addr,&b[i+4],len)) < 0) {
err("firmware download failed: %d\n",ret);
+ release_firmware(fw);
return ret;
}
i += 4 + len;
@@ -672,7 +687,7 @@ static int bcm3510_reset(struct bcm3510_state *st)
if ((ret = bcm3510_writeB(st,0xa0,v)) < 0)
return ret;
- t = jiffies + 3*HZ;
+ t = jiffies + 3*HZ;
while (time_before(jiffies, t)) {
msleep(10);
if ((ret = bcm3510_readB(st,0xa2,&v)) < 0)
@@ -695,7 +710,7 @@ static int bcm3510_clear_reset(struct bcm3510_state *st)
if ((ret = bcm3510_writeB(st,0xa0,v)) < 0)
return ret;
- t = jiffies + 3*HZ;
+ t = jiffies + 3*HZ;
while (time_before(jiffies, t)) {
msleep(10);
if ((ret = bcm3510_readB(st,0xa2,&v)) < 0)
@@ -714,7 +729,7 @@ static int bcm3510_init_cold(struct bcm3510_state *st)
int ret;
bcm3510_register_value v;
- /* read Acquisation Processor status register and check it is not in RUN mode */
+ /* read Acquisition Processor status register and check it is not in RUN mode */
if ((ret = bcm3510_readB(st,0xa2,&v)) < 0)
return ret;
if (v.APSTAT1_a2.RUN) {
@@ -759,7 +774,8 @@ static int bcm3510_init(struct dvb_frontend* fe)
deb_info("attempting to download firmware\n");
if ((ret = bcm3510_init_cold(st)) < 0)
return ret;
- case JDEC_EEPROM_LOAD_WAIT: /* fall-through is wanted */
+ fallthrough;
+ case JDEC_EEPROM_LOAD_WAIT:
deb_info("firmware is loaded\n");
bcm3510_check_firmware_version(st);
break;
@@ -775,13 +791,12 @@ static int bcm3510_init(struct dvb_frontend* fe)
}
-static struct dvb_frontend_ops bcm3510_ops;
+static const struct dvb_frontend_ops bcm3510_ops;
struct dvb_frontend* bcm3510_attach(const struct bcm3510_config *config,
struct i2c_adapter *i2c)
{
struct bcm3510_state* state = NULL;
- int ret;
bcm3510_register_value v;
/* allocate memory for the internal state */
@@ -800,7 +815,7 @@ struct dvb_frontend* bcm3510_attach(const struct bcm3510_config *config,
mutex_init(&state->hab_mutex);
- if ((ret = bcm3510_readB(state,0xe0,&v)) < 0)
+ if (bcm3510_readB(state, 0xe0, &v) < 0)
goto error;
deb_info("Revision: 0x%1x, Layer: 0x%1x.\n",v.REVID_e0.REV,v.REVID_e0.LAYER);
@@ -819,16 +834,14 @@ error:
kfree(state);
return NULL;
}
-EXPORT_SYMBOL(bcm3510_attach);
+EXPORT_SYMBOL_GPL(bcm3510_attach);
-static struct dvb_frontend_ops bcm3510_ops = {
+static const struct dvb_frontend_ops bcm3510_ops = {
.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
.info = {
.name = "Broadcom BCM3510 VSB/QAM frontend",
- .frequency_min = 54000000,
- .frequency_max = 803000000,
- /* stepsize is just a guess */
- .frequency_stepsize = 0,
+ .frequency_min_hz = 54 * MHz,
+ .frequency_max_hz = 803 * MHz,
.caps =
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
@@ -852,5 +865,5 @@ static struct dvb_frontend_ops bcm3510_ops = {
};
MODULE_DESCRIPTION("Broadcom BCM3510 ATSC (8VSB/16VSB & ITU J83 AnnexB FEC QAM64/256) demodulator driver");
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/bcm3510.h b/drivers/media/dvb-frontends/bcm3510.h
new file mode 100644
index 000000000000..43a7a8b4796d
--- /dev/null
+++ b/drivers/media/dvb-frontends/bcm3510.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC)
+ *
+ * Copyright (C) 2001-5, B2C2 inc.
+ *
+ * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@posteo.de>
+ */
+#ifndef BCM3510_H
+#define BCM3510_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct bcm3510_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* request firmware for device */
+ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+#if IS_REACHABLE(CONFIG_DVB_BCM3510)
+extern struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config,
+ struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_BCM3510
+
+#endif
diff --git a/drivers/media/dvb/frontends/bcm3510_priv.h b/drivers/media/dvb-frontends/bcm3510_priv.h
index 3bb1bc2a04f0..89c71bc42a0f 100644
--- a/drivers/media/dvb/frontends/bcm3510_priv.h
+++ b/drivers/media/dvb-frontends/bcm3510_priv.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC)
*
* Copyright (C) 2001-5, B2C2 inc.
*
- * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@desy.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@posteo.de>
*/
#ifndef __BCM3510_PRIV_H__
#define __BCM3510_PRIV_H__
@@ -25,11 +12,11 @@
#define PACKED __attribute__((packed))
#undef err
-#define err(format, arg...) printk(KERN_ERR "bcm3510: " format "\n" , ## arg)
+#define err(format, arg...) printk(KERN_ERR "bcm3510: " format "\n", ## arg)
#undef info
-#define info(format, arg...) printk(KERN_INFO "bcm3510: " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO "bcm3510: " format "\n", ## arg)
#undef warn
-#define warn(format, arg...) printk(KERN_WARNING "bcm3510: " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "bcm3510: " format "\n", ## arg)
#define PANASONIC_FIRST_IF_BASE_IN_KHz 1407500
diff --git a/drivers/media/dvb-frontends/bsbe1-d01a.h b/drivers/media/dvb-frontends/bsbe1-d01a.h
new file mode 100644
index 000000000000..700b429b7b8d
--- /dev/null
+++ b/drivers/media/dvb-frontends/bsbe1-d01a.h
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * bsbe1-d01a.h - ALPS BSBE1-D01A tuner support
+ *
+ * Copyright (C) 2011 Oliver Endriss <o.endriss@gmx.de>
+ *
+ * the project's page is at https://linuxtv.org
+ */
+
+#ifndef BSBE1_D01A_H
+#define BSBE1_D01A_H
+
+#include "stb6000.h"
+#include "stv0288.h"
+
+static u8 stv0288_bsbe1_d01a_inittab[] = {
+ 0x01, 0x15,
+ 0x02, 0x20,
+ 0x09, 0x0,
+ 0x0a, 0x4,
+ 0x0b, 0x0,
+ 0x0c, 0x0,
+ 0x0d, 0x0,
+ 0x0e, 0xd4,
+ 0x0f, 0x30,
+ 0x11, 0x80,
+ 0x12, 0x03,
+ 0x13, 0x48,
+ 0x14, 0x84,
+ 0x15, 0x45,
+ 0x16, 0xb7,
+ 0x17, 0x9c,
+ 0x18, 0x0,
+ 0x19, 0xa6,
+ 0x1a, 0x88,
+ 0x1b, 0x8f,
+ 0x1c, 0xf0,
+ 0x20, 0x0b,
+ 0x21, 0x54,
+ 0x22, 0x0,
+ 0x23, 0x0,
+ 0x2b, 0xff,
+ 0x2c, 0xf7,
+ 0x30, 0x0,
+ 0x31, 0x1e,
+ 0x32, 0x14,
+ 0x33, 0x0f,
+ 0x34, 0x09,
+ 0x35, 0x0c,
+ 0x36, 0x05,
+ 0x37, 0x2f,
+ 0x38, 0x16,
+ 0x39, 0xbd,
+ 0x3a, 0x03,
+ 0x3b, 0x13,
+ 0x3c, 0x11,
+ 0x3d, 0x30,
+ 0x40, 0x63,
+ 0x41, 0x04,
+ 0x42, 0x60,
+ 0x43, 0x00,
+ 0x44, 0x00,
+ 0x45, 0x00,
+ 0x46, 0x00,
+ 0x47, 0x00,
+ 0x4a, 0x00,
+ 0x50, 0x10,
+ 0x51, 0x36,
+ 0x52, 0x09,
+ 0x53, 0x94,
+ 0x54, 0x62,
+ 0x55, 0x29,
+ 0x56, 0x64,
+ 0x57, 0x2b,
+ 0x58, 0x54,
+ 0x59, 0x86,
+ 0x5a, 0x0,
+ 0x5b, 0x9b,
+ 0x5c, 0x08,
+ 0x5d, 0x7f,
+ 0x5e, 0x0,
+ 0x5f, 0xff,
+ 0x70, 0x0,
+ 0x71, 0x0,
+ 0x72, 0x0,
+ 0x74, 0x0,
+ 0x75, 0x0,
+ 0x76, 0x0,
+ 0x81, 0x0,
+ 0x82, 0x3f,
+ 0x83, 0x3f,
+ 0x84, 0x0,
+ 0x85, 0x0,
+ 0x88, 0x0,
+ 0x89, 0x0,
+ 0x8a, 0x0,
+ 0x8b, 0x0,
+ 0x8c, 0x0,
+ 0x90, 0x0,
+ 0x91, 0x0,
+ 0x92, 0x0,
+ 0x93, 0x0,
+ 0x94, 0x1c,
+ 0x97, 0x0,
+ 0xa0, 0x48,
+ 0xa1, 0x0,
+ 0xb0, 0xb8,
+ 0xb1, 0x3a,
+ 0xb2, 0x10,
+ 0xb3, 0x82,
+ 0xb4, 0x80,
+ 0xb5, 0x82,
+ 0xb6, 0x82,
+ 0xb7, 0x82,
+ 0xb8, 0x20,
+ 0xb9, 0x0,
+ 0xf0, 0x0,
+ 0xf1, 0x0,
+ 0xf2, 0xc0,
+ 0xff, 0xff,
+};
+
+static struct stv0288_config stv0288_bsbe1_d01a_config = {
+ .demod_address = 0x68,
+ .min_delay_ms = 100,
+ .inittab = stv0288_bsbe1_d01a_inittab,
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/bsbe1.h b/drivers/media/dvb-frontends/bsbe1.h
new file mode 100644
index 000000000000..b4b81e1dd828
--- /dev/null
+++ b/drivers/media/dvb-frontends/bsbe1.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * bsbe1.h - ALPS BSBE1 tuner support
+ *
+ * the project's page is at https://linuxtv.org
+ */
+
+#ifndef BSBE1_H
+#define BSBE1_H
+
+static u8 alps_bsbe1_inittab[] = {
+ 0x01, 0x15, /* XTAL = 4MHz, VCO = 352 MHz */
+ 0x02, 0x30, /* MCLK = 88 MHz */
+ 0x03, 0x00, /* ACR output 0 */
+ 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */
+ 0x05, 0x05, /* I2CT = 0, SCLT = 1, SDAT = 1 */
+ 0x06, 0x00, /* DAC output 0 */
+ 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */
+ 0x09, 0x00, /* FIFO */
+ 0x0c, 0x51, /* OP1/OP0 normal, val = 1 (LNB power on) */
+ 0x0d, 0x82, /* DC offset compensation = on, beta_agc1 = 2 */
+ 0x0f, 0x92, /* AGC1R */
+ 0x10, 0x34, /* AGC2O */
+ 0x11, 0x84, /* TLSR */
+ 0x12, 0xb9, /* CFD */
+ 0x15, 0xc9, /* lock detector threshold */
+ 0x28, 0x00, /* out imp: normal, type: parallel, FEC mode: QPSK */
+ 0x33, 0xfc, /* RS control */
+ 0x34, 0x93, /* count viterbi bit errors per 2E18 bytes */
+ 0xff, 0xff
+};
+
+
+static int alps_bsbe1_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio)
+{
+ u8 aclk = 0;
+ u8 bclk = 0;
+
+ if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
+ else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
+ else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
+ else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
+ else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
+ else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
+
+ stv0299_writereg(fe, 0x13, aclk);
+ stv0299_writereg(fe, 0x14, bclk);
+ stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+ stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+ stv0299_writereg(fe, 0x21, (ratio ) & 0xf0);
+
+ return 0;
+}
+
+static int alps_bsbe1_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ int ret;
+ u8 data[4];
+ u32 div;
+ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+ struct i2c_adapter *i2c = fe->tuner_priv;
+
+ if ((p->frequency < 950000) || (p->frequency > 2150000))
+ return -EINVAL;
+
+ div = p->frequency / 1000;
+ data[0] = (div >> 8) & 0x7f;
+ data[1] = div & 0xff;
+ data[2] = 0x80 | ((div & 0x18000) >> 10) | 0x1;
+ data[3] = 0xe0;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ ret = i2c_transfer(i2c, &msg, 1);
+ return (ret != 1) ? -EIO : 0;
+}
+
+static struct stv0299_config alps_bsbe1_config = {
+ .demod_address = 0x68,
+ .inittab = alps_bsbe1_inittab,
+ .mclk = 88000000UL,
+ .invert = 1,
+ .skip_reinit = 0,
+ .min_delay_ms = 100,
+ .set_symbol_rate = alps_bsbe1_set_symbol_rate,
+};
+
+#endif
diff --git a/drivers/media/dvb/frontends/bsru6.h b/drivers/media/dvb-frontends/bsru6.h
index c2a578e1314d..a5ea22ff45d3 100644
--- a/drivers/media/dvb/frontends/bsru6.h
+++ b/drivers/media/dvb-frontends/bsru6.h
@@ -1,25 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* bsru6.h - ALPS BSRU6 tuner support (moved from budget-ci.c)
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- *
- *
- * the project's page is at http://www.linuxtv.org
+ * the project's page is at https://linuxtv.org
*/
#ifndef BSRU6_H
diff --git a/drivers/media/dvb/frontends/cx22700.c b/drivers/media/dvb-frontends/cx22700.c
index f2a90f990ce3..1d04c0a652b2 100644
--- a/drivers/media/dvb/frontends/cx22700.c
+++ b/drivers/media/dvb-frontends/cx22700.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Conexant cx22700 DVB OFDM demodulator driver
Copyright (C) 2001-2002 Convergence Integrated Media GmbH
Holger Waechtler <holger@convergence.de>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
@@ -25,7 +13,7 @@
#include <linux/module.h>
#include <linux/string.h>
#include <linux/slab.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "cx22700.h"
@@ -139,7 +127,7 @@ static int cx22700_set_tps(struct cx22700_state *state,
if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5)
return -EINVAL;
- if (p->guard_interval < GUARD_INTERVAL_1_32 ||
+ if ((int)p->guard_interval < GUARD_INTERVAL_1_32 ||
p->guard_interval > GUARD_INTERVAL_1_4)
return -EINVAL;
@@ -152,7 +140,7 @@ static int cx22700_set_tps(struct cx22700_state *state,
p->modulation != QAM_64)
return -EINVAL;
- if (p->hierarchy < HIERARCHY_NONE ||
+ if ((int)p->hierarchy < HIERARCHY_NONE ||
p->hierarchy > HIERARCHY_4)
return -EINVAL;
@@ -169,6 +157,9 @@ static int cx22700_set_tps(struct cx22700_state *state,
cx22700_writereg (state, 0x04, val);
+ if (p->code_rate_HP - FEC_1_2 >= sizeof(fec_tab) ||
+ p->code_rate_LP - FEC_1_2 >= sizeof(fec_tab))
+ return -EINVAL;
val = fec_tab[p->code_rate_HP - FEC_1_2] << 3;
val |= fec_tab[p->code_rate_LP - FEC_1_2];
@@ -188,9 +179,10 @@ static int cx22700_set_tps(struct cx22700_state *state,
static int cx22700_get_tps(struct cx22700_state *state,
struct dtv_frontend_properties *p)
{
- static const fe_modulation_t qam_tab [3] = { QPSK, QAM_16, QAM_64 };
- static const fe_code_rate_t fec_tab [5] = { FEC_1_2, FEC_2_3, FEC_3_4,
- FEC_5_6, FEC_7_8 };
+ static const enum fe_modulation qam_tab[3] = { QPSK, QAM_16, QAM_64 };
+ static const enum fe_code_rate fec_tab[5] = {
+ FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8
+ };
u8 val;
dprintk ("%s\n", __func__);
@@ -250,7 +242,7 @@ static int cx22700_init (struct dvb_frontend* fe)
return 0;
}
-static int cx22700_read_status(struct dvb_frontend* fe, fe_status_t* status)
+static int cx22700_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct cx22700_state* state = fe->demodulator_priv;
@@ -341,9 +333,9 @@ static int cx22700_set_frontend(struct dvb_frontend *fe)
return 0;
}
-static int cx22700_get_frontend(struct dvb_frontend *fe)
+static int cx22700_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct cx22700_state* state = fe->demodulator_priv;
u8 reg09 = cx22700_readreg (state, 0x09);
@@ -376,7 +368,7 @@ static void cx22700_release(struct dvb_frontend* fe)
kfree(state);
}
-static struct dvb_frontend_ops cx22700_ops;
+static const struct dvb_frontend_ops cx22700_ops;
struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
struct i2c_adapter* i2c)
@@ -404,13 +396,13 @@ error:
return NULL;
}
-static struct dvb_frontend_ops cx22700_ops = {
+static const struct dvb_frontend_ops cx22700_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = "Conexant CX22700 DVB-T",
- .frequency_min = 470000000,
- .frequency_max = 860000000,
- .frequency_stepsize = 166667,
+ .frequency_min_hz = 470 * MHz,
+ .frequency_max_hz = 860 * MHz,
+ .frequency_stepsize_hz = 166667,
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
@@ -440,4 +432,4 @@ MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver");
MODULE_AUTHOR("Holger Waechtler");
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(cx22700_attach);
+EXPORT_SYMBOL_GPL(cx22700_attach);
diff --git a/drivers/media/dvb-frontends/cx22700.h b/drivers/media/dvb-frontends/cx22700.h
new file mode 100644
index 000000000000..178dd6b3aff2
--- /dev/null
+++ b/drivers/media/dvb-frontends/cx22700.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Conexant CX22700 DVB OFDM demodulator driver
+
+ Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+ Holger Waechtler <holger@convergence.de>
+
+
+*/
+
+#ifndef CX22700_H
+#define CX22700_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx22700_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_CX22700)
+extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
+ struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_CX22700
+
+#endif // CX22700_H
diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb-frontends/cx22702.c
index edc8eafc5c09..61ad34b7004b 100644
--- a/drivers/media/dvb/frontends/cx22702.c
+++ b/drivers/media/dvb-frontends/cx22702.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Conexant 22702 DVB OFDM demodulator driver
@@ -9,19 +10,6 @@
Copyright (C) 2004 Steven Toth <stoth@linuxtv.org>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
@@ -31,7 +19,7 @@
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "cx22702.h"
struct cx22702_state {
@@ -452,7 +440,7 @@ static int cx22702_init(struct dvb_frontend *fe)
return 0;
}
-static int cx22702_read_status(struct dvb_frontend *fe, fe_status_t *status)
+static int cx22702_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct cx22702_state *state = fe->demodulator_priv;
u8 reg0A;
@@ -562,9 +550,9 @@ static int cx22702_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
return 0;
}
-static int cx22702_get_frontend(struct dvb_frontend *fe)
+static int cx22702_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct cx22702_state *state = fe->demodulator_priv;
u8 reg0C = cx22702_readreg(state, 0x0C);
@@ -616,15 +604,15 @@ error:
kfree(state);
return NULL;
}
-EXPORT_SYMBOL(cx22702_attach);
+EXPORT_SYMBOL_GPL(cx22702_attach);
static const struct dvb_frontend_ops cx22702_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = "Conexant CX22702 DVB-T",
- .frequency_min = 177000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 166666,
+ .frequency_min_hz = 177 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 166666,
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
diff --git a/drivers/media/dvb-frontends/cx22702.h b/drivers/media/dvb-frontends/cx22702.h
new file mode 100644
index 000000000000..cfae96738e8b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cx22702.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Conexant 22702 DVB OFDM demodulator driver
+
+ based on:
+ Alps TDMB7 DVB OFDM demodulator driver
+
+ Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+ Holger Waechtler <holger@convergence.de>
+
+ Copyright (C) 2004 Steven Toth <stoth@linuxtv.org>
+
+
+*/
+
+#ifndef CX22702_H
+#define CX22702_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx22702_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* serial/parallel output */
+#define CX22702_PARALLEL_OUTPUT 0
+#define CX22702_SERIAL_OUTPUT 1
+ u8 output_mode;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_CX22702)
+extern struct dvb_frontend *cx22702_attach(
+ const struct cx22702_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *cx22702_attach(
+ const struct cx22702_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb-frontends/cx24110.c
index 3180f5b2a6a6..65dd9b72ea55 100644
--- a/drivers/media/dvb/frontends/cx24110.c
+++ b/drivers/media/dvb-frontends/cx24110.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
cx24110 - Single Chip Satellite Channel Receiver driver module
@@ -5,20 +6,6 @@
work
Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
@@ -27,7 +14,7 @@
#include <linux/module.h>
#include <linux/init.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "cx24110.h"
@@ -120,8 +107,8 @@ static int cx24110_writereg (struct cx24110_state* state, int reg, int data)
int err;
if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
- dprintk ("%s: writereg error (err == %i, reg == 0x%02x,"
- " data == 0x%02x)\n", __func__, err, reg, data);
+ dprintk("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n",
+ __func__, err, reg, data);
return -EREMOTEIO;
}
@@ -143,7 +130,8 @@ static int cx24110_readreg (struct cx24110_state* state, u8 reg)
return b1[0];
}
-static int cx24110_set_inversion (struct cx24110_state* state, fe_spectral_inversion_t inversion)
+static int cx24110_set_inversion(struct cx24110_state *state,
+ enum fe_spectral_inversion inversion)
{
/* fixme (low): error handling */
@@ -177,52 +165,50 @@ static int cx24110_set_inversion (struct cx24110_state* state, fe_spectral_inver
return 0;
}
-static int cx24110_set_fec (struct cx24110_state* state, fe_code_rate_t fec)
+static int cx24110_set_fec(struct cx24110_state *state, enum fe_code_rate fec)
{
-/* fixme (low): error handling */
-
- static const int rate[]={-1,1,2,3,5,7,-1};
- static const int g1[]={-1,0x01,0x02,0x05,0x15,0x45,-1};
- static const int g2[]={-1,0x01,0x03,0x06,0x1a,0x7a,-1};
+ static const int rate[FEC_AUTO] = {-1, 1, 2, 3, 5, 7, -1};
+ static const int g1[FEC_AUTO] = {-1, 0x01, 0x02, 0x05, 0x15, 0x45, -1};
+ static const int g2[FEC_AUTO] = {-1, 0x01, 0x03, 0x06, 0x1a, 0x7a, -1};
/* Well, the AutoAcq engine of the cx24106 and 24110 automatically
searches all enabled viterbi rates, and can handle non-standard
rates as well. */
- if (fec>FEC_AUTO)
- fec=FEC_AUTO;
+ if (fec > FEC_AUTO)
+ fec = FEC_AUTO;
- if (fec==FEC_AUTO) { /* (re-)establish AutoAcq behaviour */
- cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xdf);
+ if (fec == FEC_AUTO) { /* (re-)establish AutoAcq behaviour */
+ cx24110_writereg(state, 0x37, cx24110_readreg(state, 0x37) & 0xdf);
/* clear AcqVitDis bit */
- cx24110_writereg(state,0x18,0xae);
+ cx24110_writereg(state, 0x18, 0xae);
/* allow all DVB standard code rates */
- cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|0x3);
+ cx24110_writereg(state, 0x05, (cx24110_readreg(state, 0x05) & 0xf0) | 0x3);
/* set nominal Viterbi rate 3/4 */
- cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|0x3);
+ cx24110_writereg(state, 0x22, (cx24110_readreg(state, 0x22) & 0xf0) | 0x3);
/* set current Viterbi rate 3/4 */
- cx24110_writereg(state,0x1a,0x05); cx24110_writereg(state,0x1b,0x06);
+ cx24110_writereg(state, 0x1a, 0x05);
+ cx24110_writereg(state, 0x1b, 0x06);
/* set the puncture registers for code rate 3/4 */
return 0;
} else {
- cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x20);
+ cx24110_writereg(state, 0x37, cx24110_readreg(state, 0x37) | 0x20);
/* set AcqVitDis bit */
- if(rate[fec]>0) {
- cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|rate[fec]);
- /* set nominal Viterbi rate */
- cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|rate[fec]);
- /* set current Viterbi rate */
- cx24110_writereg(state,0x1a,g1[fec]);
- cx24110_writereg(state,0x1b,g2[fec]);
- /* not sure if this is the right way: I always used AutoAcq mode */
- } else
- return -EOPNOTSUPP;
-/* fixme (low): which is the correct return code? */
- };
+ if (rate[fec] < 0)
+ return -EINVAL;
+
+ cx24110_writereg(state, 0x05, (cx24110_readreg(state, 0x05) & 0xf0) | rate[fec]);
+ /* set nominal Viterbi rate */
+ cx24110_writereg(state, 0x22, (cx24110_readreg(state, 0x22) & 0xf0) | rate[fec]);
+ /* set current Viterbi rate */
+ cx24110_writereg(state, 0x1a, g1[fec]);
+ cx24110_writereg(state, 0x1b, g2[fec]);
+ /* not sure if this is the right way: I always used AutoAcq mode */
+ }
return 0;
}
-static fe_code_rate_t cx24110_get_fec (struct cx24110_state* state)
+static enum fe_code_rate cx24110_get_fec(struct cx24110_state *state)
{
int i;
@@ -238,13 +224,13 @@ static fe_code_rate_t cx24110_get_fec (struct cx24110_state* state)
}
}
-static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate)
+static int cx24110_set_symbolrate (struct cx24110_state *state, u32 srate)
{
/* fixme (low): add error handling */
u32 ratio;
u32 tmp, fclk, BDRI;
- static const u32 bands[]={5000000UL,15000000UL,90999000UL/2};
+ static const u32 bands[] = {5000000UL, 15000000UL, 90999000UL/2};
int i;
dprintk("cx24110 debug: entering %s(%d)\n",__func__,srate);
@@ -275,7 +261,7 @@ static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate)
cx24110_writereg(state,0x07,tmp|0x3);
cx24110_writereg(state,0x06,0x78);
fclk=90999000UL;
- };
+ }
dprintk("cx24110 debug: fclk %d Hz\n",fclk);
/* we need to divide two integers with approx. 27 bits in 32 bit
arithmetic giving a 25 bit result */
@@ -362,12 +348,13 @@ static int cx24110_initfe(struct dvb_frontend* fe)
for(i = 0; i < ARRAY_SIZE(cx24110_regdata); i++) {
cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data);
- };
+ }
return 0;
}
-static int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+static int cx24110_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage)
{
struct cx24110_state *state = fe->demodulator_priv;
@@ -378,10 +365,11 @@ static int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltag
return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0x40);
default:
return -EINVAL;
- };
+ }
}
-static int cx24110_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
+static int cx24110_diseqc_send_burst(struct dvb_frontend *fe,
+ enum fe_sec_mini_cmd burst)
{
int rv, bit;
struct cx24110_state *state = fe->demodulator_priv;
@@ -436,7 +424,8 @@ static int cx24110_send_diseqc_msg(struct dvb_frontend* fe,
return 0;
}
-static int cx24110_read_status(struct dvb_frontend* fe, fe_status_t* status)
+static int cx24110_read_status(struct dvb_frontend *fe,
+ enum fe_status *status)
{
struct cx24110_state *state = fe->demodulator_priv;
@@ -548,9 +537,9 @@ static int cx24110_set_frontend(struct dvb_frontend *fe)
return 0;
}
-static int cx24110_get_frontend(struct dvb_frontend *fe)
+static int cx24110_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct cx24110_state *state = fe->demodulator_priv;
s32 afc; unsigned sclk;
@@ -576,7 +565,8 @@ static int cx24110_get_frontend(struct dvb_frontend *fe)
return 0;
}
-static int cx24110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+static int cx24110_set_tone(struct dvb_frontend *fe,
+ enum fe_sec_tone_mode tone)
{
struct cx24110_state *state = fe->demodulator_priv;
@@ -589,7 +579,7 @@ static void cx24110_release(struct dvb_frontend* fe)
kfree(state);
}
-static struct dvb_frontend_ops cx24110_ops;
+static const struct dvb_frontend_ops cx24110_ops;
struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
struct i2c_adapter* i2c)
@@ -622,14 +612,14 @@ error:
return NULL;
}
-static struct dvb_frontend_ops cx24110_ops = {
+static const struct dvb_frontend_ops cx24110_ops = {
.delsys = { SYS_DVBS },
.info = {
.name = "Conexant CX24110 DVB-S",
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .frequency_stepsize = 1011, /* kHz for QPSK frontends */
- .frequency_tolerance = 29500,
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
+ .frequency_stepsize_hz = 1011 * kHz,
+ .frequency_tolerance_hz = 29500 * kHz,
.symbol_rate_min = 1000000,
.symbol_rate_max = 45000000,
.caps = FE_CAN_INVERSION_AUTO |
@@ -663,4 +653,4 @@ MODULE_DESCRIPTION("Conexant CX24110 DVB-S Demodulator driver");
MODULE_AUTHOR("Peter Hettkamp");
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(cx24110_attach);
+EXPORT_SYMBOL_GPL(cx24110_attach);
diff --git a/drivers/media/dvb-frontends/cx24110.h b/drivers/media/dvb-frontends/cx24110.h
new file mode 100644
index 000000000000..839551841893
--- /dev/null
+++ b/drivers/media/dvb-frontends/cx24110.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ cx24110 - Single Chip Satellite Channel Receiver driver module
+
+ Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de> based on
+ work
+ Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+
+
+*/
+
+#ifndef CX24110_H
+#define CX24110_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx24110_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+};
+
+static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val)
+{
+ u8 buf[] = {
+ (u8)((val >> 24) & 0xff),
+ (u8)((val >> 16) & 0xff),
+ (u8)((val >> 8) & 0xff)
+ };
+
+ if (fe->ops.write)
+ return fe->ops.write(fe, buf, 3);
+ return 0;
+}
+
+#if IS_REACHABLE(CONFIG_DVB_CX24110)
+extern struct dvb_frontend *cx24110_attach(const struct cx24110_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *cx24110_attach(const struct cx24110_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_CX24110
+
+#endif // CX24110_H
diff --git a/drivers/media/dvb/frontends/cx24113.c b/drivers/media/dvb-frontends/cx24113.c
index 3883c3b31aef..203cb6b3f941 100644
--- a/drivers/media/dvb/frontends/cx24113.c
+++ b/drivers/media/dvb-frontends/cx24113.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Conexant CX24113/CX24128 Tuner (Satellite)
*
* Copyright (C) 2007-8 Patrick Boettcher <pb@linuxtv.org>
*
* Developed for BBTI / Technisat
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/slab.h>
@@ -26,7 +12,7 @@
#include <linux/module.h>
#include <linux/init.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "cx24113.h"
static int debug;
@@ -108,8 +94,8 @@ static int cx24113_writereg(struct cx24113_state *state, int reg, int data)
.flags = 0, .buf = buf, .len = 2 };
int err = i2c_transfer(state->i2c, &msg, 1);
if (err != 1) {
- printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x,"
- " data == 0x%02x)\n", __func__, err, reg, data);
+ printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x, data == 0x%02x)\n",
+ __func__, err, reg, data);
return err;
}
@@ -392,7 +378,7 @@ static void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r)
static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency)
{
- u8 r = 1; /* or 2 */
+ u8 r;
u16 n = 6;
s32 f = 0;
@@ -527,21 +513,20 @@ static int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency)
return 0;
}
-static int cx24113_release(struct dvb_frontend *fe)
+static void cx24113_release(struct dvb_frontend *fe)
{
struct cx24113_state *state = fe->tuner_priv;
dprintk("\n");
fe->tuner_priv = NULL;
kfree(state);
- return 0;
}
static const struct dvb_tuner_ops cx24113_tuner_ops = {
.info = {
- .name = "Conexant CX24113",
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .frequency_step = 125,
+ .name = "Conexant CX24113",
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
+ .frequency_step_hz = 125 * kHz,
},
.release = cx24113_release,
@@ -557,13 +542,11 @@ struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
const struct cx24113_config *config, struct i2c_adapter *i2c)
{
/* allocate memory for the internal state */
- struct cx24113_state *state =
- kzalloc(sizeof(struct cx24113_state), GFP_KERNEL);
+ struct cx24113_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
int rc;
- if (state == NULL) {
- cx_err("Unable to kzalloc\n");
- goto error;
- }
+
+ if (!state)
+ return NULL;
/* setup the state */
state->config = config;
@@ -607,7 +590,7 @@ error:
return NULL;
}
-EXPORT_SYMBOL(cx24113_attach);
+EXPORT_SYMBOL_GPL(cx24113_attach);
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
diff --git a/drivers/media/dvb-frontends/cx24113.h b/drivers/media/dvb-frontends/cx24113.h
new file mode 100644
index 000000000000..c5460c2aec7b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cx24113.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Conexant CX24113/CX24128 Tuner (Satellite)
+ *
+ * Copyright (C) 2007-8 Patrick Boettcher <pb@linuxtv.org>
+ */
+
+#ifndef CX24113_H
+#define CX24113_H
+
+struct dvb_frontend;
+
+struct cx24113_config {
+ u8 i2c_addr; /* 0x14 or 0x54 */
+
+ u32 xtal_khz;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_TUNER_CX24113)
+extern struct dvb_frontend *cx24113_attach(struct dvb_frontend *,
+ const struct cx24113_config *config, struct i2c_adapter *i2c);
+
+extern void cx24113_agc_callback(struct dvb_frontend *fe);
+#else
+static inline struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
+ const struct cx24113_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline void cx24113_agc_callback(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+}
+#endif
+
+#endif /* CX24113_H */
diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb-frontends/cx24116.c
index b48879186537..f5dd3a81725a 100644
--- a/drivers/media/dvb/frontends/cx24116.c
+++ b/drivers/media/dvb-frontends/cx24116.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver
@@ -19,19 +20,6 @@
Fill set_voltage with actually control voltage code.
Correct set tone to not affect voltage.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/slab.h>
@@ -41,7 +29,7 @@
#include <linux/init.h>
#include <linux/firmware.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "cx24116.h"
static int debug;
@@ -160,13 +148,13 @@ enum cmds {
struct cx24116_tuning {
u32 frequency;
u32 symbol_rate;
- fe_spectral_inversion_t inversion;
- fe_code_rate_t fec;
+ enum fe_spectral_inversion inversion;
+ enum fe_code_rate fec;
- fe_delivery_system_t delsys;
- fe_modulation_t modulation;
- fe_pilot_t pilot;
- fe_rolloff_t rolloff;
+ enum fe_delivery_system delsys;
+ enum fe_modulation modulation;
+ enum fe_pilot pilot;
+ enum fe_rolloff rolloff;
/* Demod values */
u8 fec_val;
@@ -209,8 +197,8 @@ static int cx24116_writereg(struct cx24116_state *state, int reg, int data)
err = i2c_transfer(state->i2c, &msg, 1);
if (err != 1) {
- printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x,"
- " value == 0x%02x)\n", __func__, err, reg, data);
+ printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n",
+ __func__, err, reg, data);
return -EREMOTEIO;
}
@@ -221,16 +209,13 @@ static int cx24116_writereg(struct cx24116_state *state, int reg, int data)
static int cx24116_writeregN(struct cx24116_state *state, int reg,
const u8 *data, u16 len)
{
- int ret = -EREMOTEIO;
+ int ret;
struct i2c_msg msg;
u8 *buf;
buf = kmalloc(len + 1, GFP_KERNEL);
- if (buf == NULL) {
- printk("Unable to kmalloc\n");
- ret = -ENOMEM;
- goto error;
- }
+ if (!buf)
+ return -ENOMEM;
*(buf) = reg;
memcpy(buf + 1, data, len);
@@ -251,7 +236,6 @@ static int cx24116_writeregN(struct cx24116_state *state, int reg,
ret = -EREMOTEIO;
}
-error:
kfree(buf);
return ret;
@@ -285,7 +269,7 @@ static int cx24116_readreg(struct cx24116_state *state, u8 reg)
}
static int cx24116_set_inversion(struct cx24116_state *state,
- fe_spectral_inversion_t inversion)
+ enum fe_spectral_inversion inversion)
{
dprintk("%s(%d)\n", __func__, inversion);
@@ -373,9 +357,9 @@ static int cx24116_set_inversion(struct cx24116_state *state,
* a scheme are support. Especially, no auto detect when in S2 mode.
*/
static struct cx24116_modfec {
- fe_delivery_system_t delivery_system;
- fe_modulation_t modulation;
- fe_code_rate_t fec;
+ enum fe_delivery_system delivery_system;
+ enum fe_modulation modulation;
+ enum fe_code_rate fec;
u8 mask; /* In DVBS mode this is used to autodetect */
u8 val; /* Passed to the firmware to indicate mode selection */
} CX24116_MODFEC_MODES[] = {
@@ -415,7 +399,7 @@ static struct cx24116_modfec {
};
static int cx24116_lookup_fecmod(struct cx24116_state *state,
- fe_delivery_system_t d, fe_modulation_t m, fe_code_rate_t f)
+ enum fe_delivery_system d, enum fe_modulation m, enum fe_code_rate f)
{
int i, ret = -EOPNOTSUPP;
@@ -434,7 +418,9 @@ static int cx24116_lookup_fecmod(struct cx24116_state *state,
}
static int cx24116_set_fec(struct cx24116_state *state,
- fe_delivery_system_t delsys, fe_modulation_t mod, fe_code_rate_t fec)
+ enum fe_delivery_system delsys,
+ enum fe_modulation mod,
+ enum fe_code_rate fec)
{
int ret = 0;
@@ -496,8 +482,8 @@ static int cx24116_firmware_ondemand(struct dvb_frontend *fe)
printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n",
__func__);
if (ret) {
- printk(KERN_ERR "%s: No firmware uploaded "
- "(timeout or file not found?)\n", __func__);
+ printk(KERN_ERR "%s: No firmware uploaded (timeout or file not found?)\n",
+ __func__);
return ret;
}
@@ -683,7 +669,7 @@ static int cx24116_load_firmware(struct dvb_frontend *fe,
return 0;
}
-static int cx24116_read_status(struct dvb_frontend *fe, fe_status_t *status)
+static int cx24116_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct cx24116_state *state = fe->demodulator_priv;
@@ -755,6 +741,7 @@ static int cx24116_read_snr_pct(struct dvb_frontend *fe, u16 *snr)
{
struct cx24116_state *state = fe->demodulator_priv;
u8 snr_reading;
+ int ret;
static const u32 snr_tab[] = { /* 10 x Table (rounded up) */
0x00000, 0x0199A, 0x03333, 0x04ccD, 0x06667,
0x08000, 0x0999A, 0x0b333, 0x0cccD, 0x0e667,
@@ -763,7 +750,11 @@ static int cx24116_read_snr_pct(struct dvb_frontend *fe, u16 *snr)
dprintk("%s()\n", __func__);
- snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY0);
+ ret = cx24116_readreg(state, CX24116_REG_QUALITY0);
+ if (ret < 0)
+ return ret;
+
+ snr_reading = ret;
if (snr_reading >= 0xa0 /* 100% */)
*snr = 0xffff;
@@ -819,7 +810,7 @@ static int cx24116_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
static void cx24116_clone_params(struct dvb_frontend *fe)
{
struct cx24116_state *state = fe->demodulator_priv;
- memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur));
+ state->dcur = state->dnxt;
}
/* Wait for LNB */
@@ -844,7 +835,7 @@ static int cx24116_wait_for_lnb(struct dvb_frontend *fe)
}
static int cx24116_set_voltage(struct dvb_frontend *fe,
- fe_sec_voltage_t voltage)
+ enum fe_sec_voltage voltage)
{
struct cx24116_cmd cmd;
int ret;
@@ -872,7 +863,7 @@ static int cx24116_set_voltage(struct dvb_frontend *fe,
}
static int cx24116_set_tone(struct dvb_frontend *fe,
- fe_sec_tone_mode_t tone)
+ enum fe_sec_tone_mode tone)
{
struct cx24116_cmd cmd;
int ret;
@@ -963,6 +954,10 @@ static int cx24116_send_diseqc_msg(struct dvb_frontend *fe,
struct cx24116_state *state = fe->demodulator_priv;
int i, ret;
+ /* Validate length */
+ if (d->msg_len > sizeof(d->msg))
+ return -EINVAL;
+
/* Dump DiSEqC message */
if (debug) {
printk(KERN_INFO "cx24116: %s(", __func__);
@@ -974,10 +969,6 @@ static int cx24116_send_diseqc_msg(struct dvb_frontend *fe,
printk(") toneburst=%d\n", toneburst);
}
- /* Validate length */
- if (d->msg_len > (CX24116_ARGLEN - CX24116_DISEQC_MSGOFS))
- return -EINVAL;
-
/* DiSEqC message */
for (i = 0; i < d->msg_len; i++)
state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i];
@@ -1055,7 +1046,7 @@ static int cx24116_send_diseqc_msg(struct dvb_frontend *fe,
/* Send DiSEqC burst */
static int cx24116_diseqc_send_burst(struct dvb_frontend *fe,
- fe_sec_mini_cmd_t burst)
+ enum fe_sec_mini_cmd burst)
{
struct cx24116_state *state = fe->demodulator_priv;
int ret;
@@ -1114,20 +1105,20 @@ static void cx24116_release(struct dvb_frontend *fe)
kfree(state);
}
-static struct dvb_frontend_ops cx24116_ops;
+static const struct dvb_frontend_ops cx24116_ops;
struct dvb_frontend *cx24116_attach(const struct cx24116_config *config,
struct i2c_adapter *i2c)
{
- struct cx24116_state *state = NULL;
+ struct cx24116_state *state;
int ret;
dprintk("%s\n", __func__);
/* allocate memory for the internal state */
- state = kzalloc(sizeof(struct cx24116_state), GFP_KERNEL);
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
if (state == NULL)
- goto error1;
+ return NULL;
state->config = config;
state->i2c = i2c;
@@ -1136,8 +1127,9 @@ struct dvb_frontend *cx24116_attach(const struct cx24116_config *config,
ret = (cx24116_readreg(state, 0xFF) << 8) |
cx24116_readreg(state, 0xFE);
if (ret != 0x0501) {
+ kfree(state);
printk(KERN_INFO "Invalid probe, probably not a CX24116 device\n");
- goto error2;
+ return NULL;
}
/* create dvb_frontend */
@@ -1145,11 +1137,8 @@ struct dvb_frontend *cx24116_attach(const struct cx24116_config *config,
sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
return &state->frontend;
-
-error2: kfree(state);
-error1: return NULL;
}
-EXPORT_SYMBOL(cx24116_attach);
+EXPORT_SYMBOL_GPL(cx24116_attach);
/*
* Initialise or wake up device
@@ -1220,7 +1209,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe)
struct cx24116_state *state = fe->demodulator_priv;
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct cx24116_cmd cmd;
- fe_status_t tunerstat;
+ enum fe_status tunerstat;
int i, status, ret, retune = 1;
dprintk("%s()\n", __func__);
@@ -1441,7 +1430,7 @@ tuned: /* Set/Reset B/W */
}
static int cx24116_tune(struct dvb_frontend *fe, bool re_tune,
- unsigned int mode_flags, unsigned int *delay, fe_status_t *status)
+ unsigned int mode_flags, unsigned int *delay, enum fe_status *status)
{
/*
* It is safe to discard "params" here, as the DVB core will sync
@@ -1460,19 +1449,19 @@ static int cx24116_tune(struct dvb_frontend *fe, bool re_tune,
return cx24116_read_status(fe, status);
}
-static int cx24116_get_algo(struct dvb_frontend *fe)
+static enum dvbfe_algo cx24116_get_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
}
-static struct dvb_frontend_ops cx24116_ops = {
+static const struct dvb_frontend_ops cx24116_ops = {
.delsys = { SYS_DVBS, SYS_DVBS2 },
.info = {
.name = "Conexant CX24116/CX24118",
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .frequency_stepsize = 1011, /* kHz for QPSK frontends */
- .frequency_tolerance = 5000,
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
+ .frequency_stepsize_hz = 1011 * kHz,
+ .frequency_tolerance_hz = 5 * MHz,
.symbol_rate_min = 1000000,
.symbol_rate_max = 45000000,
.caps = FE_CAN_INVERSION_AUTO |
diff --git a/drivers/media/dvb-frontends/cx24116.h b/drivers/media/dvb-frontends/cx24116.h
new file mode 100644
index 000000000000..1d2fab594508
--- /dev/null
+++ b/drivers/media/dvb-frontends/cx24116.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver
+
+ Copyright (C) 2006 Steven Toth <stoth@linuxtv.com>
+
+*/
+
+#ifndef CX24116_H
+#define CX24116_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx24116_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* Need to set device param for start_dma */
+ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
+
+ /* Need to reset device during firmware loading */
+ int (*reset_device)(struct dvb_frontend *fe);
+
+ /* Need to set MPEG parameters */
+ u8 mpg_clk_pos_pol:0x02;
+
+ /* max bytes I2C provider can write at once */
+ u16 i2c_wr_max;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_CX24116)
+extern struct dvb_frontend *cx24116_attach(
+ const struct cx24116_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *cx24116_attach(
+ const struct cx24116_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* CX24116_H */
diff --git a/drivers/media/dvb-frontends/cx24117.c b/drivers/media/dvb-frontends/cx24117.c
new file mode 100644
index 000000000000..75fc7ad263d0
--- /dev/null
+++ b/drivers/media/dvb-frontends/cx24117.c
@@ -0,0 +1,1654 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ Conexant cx24117/cx24132 - Dual DVBS/S2 Satellite demod/tuner driver
+
+ Copyright (C) 2013 Luis Alves <ljalvs@gmail.com>
+ July, 6th 2013
+ First release based on cx24116 driver by:
+ Steven Toth and Georg Acher, Darron Broad, Igor Liplianin
+ Cards currently supported:
+ TBS6980 - Dual DVBS/S2 PCIe card
+ TBS6981 - Dual DVBS/S2 PCIe card
+
+*/
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "tuner-i2c.h"
+#include <media/dvb_frontend.h>
+#include "cx24117.h"
+
+
+#define CX24117_DEFAULT_FIRMWARE "dvb-fe-cx24117.fw"
+#define CX24117_SEARCH_RANGE_KHZ 5000
+
+/* known registers */
+#define CX24117_REG_COMMAND (0x00) /* command buffer */
+#define CX24117_REG_EXECUTE (0x1f) /* execute command */
+
+#define CX24117_REG_FREQ3_0 (0x34) /* frequency */
+#define CX24117_REG_FREQ2_0 (0x35)
+#define CX24117_REG_FREQ1_0 (0x36)
+#define CX24117_REG_STATE0 (0x39)
+#define CX24117_REG_SSTATUS0 (0x3a) /* demod0 signal high / status */
+#define CX24117_REG_SIGNAL0 (0x3b)
+#define CX24117_REG_FREQ5_0 (0x3c) /* +-freq */
+#define CX24117_REG_FREQ6_0 (0x3d)
+#define CX24117_REG_SRATE2_0 (0x3e) /* +- 1000 * srate */
+#define CX24117_REG_SRATE1_0 (0x3f)
+#define CX24117_REG_QUALITY2_0 (0x40)
+#define CX24117_REG_QUALITY1_0 (0x41)
+
+#define CX24117_REG_BER4_0 (0x47)
+#define CX24117_REG_BER3_0 (0x48)
+#define CX24117_REG_BER2_0 (0x49)
+#define CX24117_REG_BER1_0 (0x4a)
+#define CX24117_REG_DVBS_UCB2_0 (0x4b)
+#define CX24117_REG_DVBS_UCB1_0 (0x4c)
+#define CX24117_REG_DVBS2_UCB2_0 (0x50)
+#define CX24117_REG_DVBS2_UCB1_0 (0x51)
+#define CX24117_REG_QSTATUS0 (0x93)
+#define CX24117_REG_CLKDIV0 (0xe6)
+#define CX24117_REG_RATEDIV0 (0xf0)
+
+
+#define CX24117_REG_FREQ3_1 (0x55) /* frequency */
+#define CX24117_REG_FREQ2_1 (0x56)
+#define CX24117_REG_FREQ1_1 (0x57)
+#define CX24117_REG_STATE1 (0x5a)
+#define CX24117_REG_SSTATUS1 (0x5b) /* demod1 signal high / status */
+#define CX24117_REG_SIGNAL1 (0x5c)
+#define CX24117_REG_FREQ5_1 (0x5d) /* +- freq */
+#define CX24117_REG_FREQ4_1 (0x5e)
+#define CX24117_REG_SRATE2_1 (0x5f)
+#define CX24117_REG_SRATE1_1 (0x60)
+#define CX24117_REG_QUALITY2_1 (0x61)
+#define CX24117_REG_QUALITY1_1 (0x62)
+#define CX24117_REG_BER4_1 (0x68)
+#define CX24117_REG_BER3_1 (0x69)
+#define CX24117_REG_BER2_1 (0x6a)
+#define CX24117_REG_BER1_1 (0x6b)
+#define CX24117_REG_DVBS_UCB2_1 (0x6c)
+#define CX24117_REG_DVBS_UCB1_1 (0x6d)
+#define CX24117_REG_DVBS2_UCB2_1 (0x71)
+#define CX24117_REG_DVBS2_UCB1_1 (0x72)
+#define CX24117_REG_QSTATUS1 (0x9f)
+#define CX24117_REG_CLKDIV1 (0xe7)
+#define CX24117_REG_RATEDIV1 (0xf1)
+
+
+/* arg buffer size */
+#define CX24117_ARGLEN (0x1e)
+
+/* rolloff */
+#define CX24117_ROLLOFF_020 (0x00)
+#define CX24117_ROLLOFF_025 (0x01)
+#define CX24117_ROLLOFF_035 (0x02)
+
+/* pilot bit */
+#define CX24117_PILOT_OFF (0x00)
+#define CX24117_PILOT_ON (0x40)
+#define CX24117_PILOT_AUTO (0x80)
+
+/* signal status */
+#define CX24117_HAS_SIGNAL (0x01)
+#define CX24117_HAS_CARRIER (0x02)
+#define CX24117_HAS_VITERBI (0x04)
+#define CX24117_HAS_SYNCLOCK (0x08)
+#define CX24117_STATUS_MASK (0x0f)
+#define CX24117_SIGNAL_MASK (0xc0)
+
+
+/* arg offset for DiSEqC */
+#define CX24117_DISEQC_DEMOD (1)
+#define CX24117_DISEQC_BURST (2)
+#define CX24117_DISEQC_ARG3_2 (3) /* unknown value=2 */
+#define CX24117_DISEQC_ARG4_0 (4) /* unknown value=0 */
+#define CX24117_DISEQC_ARG5_0 (5) /* unknown value=0 */
+#define CX24117_DISEQC_MSGLEN (6)
+#define CX24117_DISEQC_MSGOFS (7)
+
+/* DiSEqC burst */
+#define CX24117_DISEQC_MINI_A (0)
+#define CX24117_DISEQC_MINI_B (1)
+
+
+#define CX24117_PNE (0) /* 0 disabled / 2 enabled */
+#define CX24117_OCC (1) /* 0 disabled / 1 enabled */
+
+
+enum cmds {
+ CMD_SET_VCOFREQ = 0x10,
+ CMD_TUNEREQUEST = 0x11,
+ CMD_GLOBAL_MPEGCFG = 0x13,
+ CMD_MPEGCFG = 0x14,
+ CMD_TUNERINIT = 0x15,
+ CMD_GET_SRATE = 0x18,
+ CMD_SET_GOLDCODE = 0x19,
+ CMD_GET_AGCACC = 0x1a,
+ CMD_DEMODINIT = 0x1b,
+ CMD_GETCTLACC = 0x1c,
+
+ CMD_LNBCONFIG = 0x20,
+ CMD_LNBSEND = 0x21,
+ CMD_LNBDCLEVEL = 0x22,
+ CMD_LNBPCBCONFIG = 0x23,
+ CMD_LNBSENDTONEBST = 0x24,
+ CMD_LNBUPDREPLY = 0x25,
+
+ CMD_SET_GPIOMODE = 0x30,
+ CMD_SET_GPIOEN = 0x31,
+ CMD_SET_GPIODIR = 0x32,
+ CMD_SET_GPIOOUT = 0x33,
+ CMD_ENABLERSCORR = 0x34,
+ CMD_FWVERSION = 0x35,
+ CMD_SET_SLEEPMODE = 0x36,
+ CMD_BERCTRL = 0x3c,
+ CMD_EVENTCTRL = 0x3d,
+};
+
+static LIST_HEAD(hybrid_tuner_instance_list);
+static DEFINE_MUTEX(cx24117_list_mutex);
+
+/* The Demod/Tuner can't easily provide these, we cache them */
+struct cx24117_tuning {
+ u32 frequency;
+ u32 symbol_rate;
+ enum fe_spectral_inversion inversion;
+ enum fe_code_rate fec;
+
+ enum fe_delivery_system delsys;
+ enum fe_modulation modulation;
+ enum fe_pilot pilot;
+ enum fe_rolloff rolloff;
+
+ /* Demod values */
+ u8 fec_val;
+ u8 fec_mask;
+ u8 inversion_val;
+ u8 pilot_val;
+ u8 rolloff_val;
+};
+
+/* Basic commands that are sent to the firmware */
+struct cx24117_cmd {
+ u8 len;
+ u8 args[CX24117_ARGLEN];
+};
+
+/* common to both fe's */
+struct cx24117_priv {
+ u8 demod_address;
+ struct i2c_adapter *i2c;
+ u8 skip_fw_load;
+ struct mutex fe_lock;
+
+ /* Used for sharing this struct between demods */
+ struct tuner_i2c_props i2c_props;
+ struct list_head hybrid_tuner_instance_list;
+};
+
+/* one per each fe */
+struct cx24117_state {
+ struct cx24117_priv *priv;
+ struct dvb_frontend frontend;
+
+ struct cx24117_tuning dcur;
+ struct cx24117_tuning dnxt;
+ struct cx24117_cmd dsec_cmd;
+
+ int demod;
+};
+
+/* modfec (modulation and FEC) lookup table */
+/* Check cx24116.c for a detailed description of each field */
+static struct cx24117_modfec {
+ enum fe_delivery_system delivery_system;
+ enum fe_modulation modulation;
+ enum fe_code_rate fec;
+ u8 mask; /* In DVBS mode this is used to autodetect */
+ u8 val; /* Passed to the firmware to indicate mode selection */
+} cx24117_modfec_modes[] = {
+ /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */
+
+ /*mod fec mask val */
+ { SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 },
+ { SYS_DVBS, QPSK, FEC_1_2, 0x02, 0x2e }, /* 00000010 00101110 */
+ { SYS_DVBS, QPSK, FEC_2_3, 0x04, 0x2f }, /* 00000100 00101111 */
+ { SYS_DVBS, QPSK, FEC_3_4, 0x08, 0x30 }, /* 00001000 00110000 */
+ { SYS_DVBS, QPSK, FEC_4_5, 0xfe, 0x30 }, /* 000?0000 ? */
+ { SYS_DVBS, QPSK, FEC_5_6, 0x20, 0x31 }, /* 00100000 00110001 */
+ { SYS_DVBS, QPSK, FEC_6_7, 0xfe, 0x30 }, /* 0?000000 ? */
+ { SYS_DVBS, QPSK, FEC_7_8, 0x80, 0x32 }, /* 10000000 00110010 */
+ { SYS_DVBS, QPSK, FEC_8_9, 0xfe, 0x30 }, /* 0000000? ? */
+ { SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 },
+ /* NBC-QPSK */
+ { SYS_DVBS2, QPSK, FEC_NONE, 0x00, 0x00 },
+ { SYS_DVBS2, QPSK, FEC_1_2, 0x00, 0x04 },
+ { SYS_DVBS2, QPSK, FEC_3_5, 0x00, 0x05 },
+ { SYS_DVBS2, QPSK, FEC_2_3, 0x00, 0x06 },
+ { SYS_DVBS2, QPSK, FEC_3_4, 0x00, 0x07 },
+ { SYS_DVBS2, QPSK, FEC_4_5, 0x00, 0x08 },
+ { SYS_DVBS2, QPSK, FEC_5_6, 0x00, 0x09 },
+ { SYS_DVBS2, QPSK, FEC_8_9, 0x00, 0x0a },
+ { SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b },
+ { SYS_DVBS2, QPSK, FEC_AUTO, 0x00, 0x00 },
+ /* 8PSK */
+ { SYS_DVBS2, PSK_8, FEC_NONE, 0x00, 0x00 },
+ { SYS_DVBS2, PSK_8, FEC_3_5, 0x00, 0x0c },
+ { SYS_DVBS2, PSK_8, FEC_2_3, 0x00, 0x0d },
+ { SYS_DVBS2, PSK_8, FEC_3_4, 0x00, 0x0e },
+ { SYS_DVBS2, PSK_8, FEC_5_6, 0x00, 0x0f },
+ { SYS_DVBS2, PSK_8, FEC_8_9, 0x00, 0x10 },
+ { SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 },
+ { SYS_DVBS2, PSK_8, FEC_AUTO, 0x00, 0x00 },
+ /*
+ * 'val' can be found in the FECSTATUS register when tuning.
+ * FECSTATUS will give the actual FEC in use if tuning was successful.
+ */
+};
+
+
+static int cx24117_writereg(struct cx24117_state *state, u8 reg, u8 data)
+{
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = { .addr = state->priv->demod_address,
+ .flags = 0, .buf = buf, .len = 2 };
+ int ret;
+
+ dev_dbg(&state->priv->i2c->dev,
+ "%s() demod%d i2c wr @0x%02x=0x%02x\n",
+ __func__, state->demod, reg, data);
+
+ ret = i2c_transfer(state->priv->i2c, &msg, 1);
+ if (ret < 0) {
+ dev_warn(&state->priv->i2c->dev,
+ "%s: demod%d i2c wr err(%i) @0x%02x=0x%02x\n",
+ KBUILD_MODNAME, state->demod, ret, reg, data);
+ return ret;
+ }
+ return 0;
+}
+
+static int cx24117_writecmd(struct cx24117_state *state,
+ struct cx24117_cmd *cmd)
+{
+ struct i2c_msg msg;
+ u8 buf[CX24117_ARGLEN+1];
+ int ret;
+
+ dev_dbg(&state->priv->i2c->dev,
+ "%s() demod%d i2c wr cmd len=%d\n",
+ __func__, state->demod, cmd->len);
+
+ buf[0] = CX24117_REG_COMMAND;
+ memcpy(&buf[1], cmd->args, cmd->len);
+
+ msg.addr = state->priv->demod_address;
+ msg.flags = 0;
+ msg.len = cmd->len+1;
+ msg.buf = buf;
+ ret = i2c_transfer(state->priv->i2c, &msg, 1);
+ if (ret < 0) {
+ dev_warn(&state->priv->i2c->dev,
+ "%s: demod%d i2c wr cmd err(%i) len=%d\n",
+ KBUILD_MODNAME, state->demod, ret, cmd->len);
+ return ret;
+ }
+ return 0;
+}
+
+static int cx24117_readreg(struct cx24117_state *state, u8 reg)
+{
+ int ret;
+ u8 recv = 0;
+ struct i2c_msg msg[] = {
+ { .addr = state->priv->demod_address, .flags = 0,
+ .buf = &reg, .len = 1 },
+ { .addr = state->priv->demod_address, .flags = I2C_M_RD,
+ .buf = &recv, .len = 1 }
+ };
+
+ ret = i2c_transfer(state->priv->i2c, msg, 2);
+ if (ret < 0) {
+ dev_warn(&state->priv->i2c->dev,
+ "%s: demod%d i2c rd err(%d) @0x%x\n",
+ KBUILD_MODNAME, state->demod, ret, reg);
+ return ret;
+ }
+
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d i2c rd @0x%02x=0x%02x\n",
+ __func__, state->demod, reg, recv);
+
+ return recv;
+}
+
+static int cx24117_readregN(struct cx24117_state *state,
+ u8 reg, u8 *buf, int len)
+{
+ int ret;
+ struct i2c_msg msg[] = {
+ { .addr = state->priv->demod_address, .flags = 0,
+ .buf = &reg, .len = 1 },
+ { .addr = state->priv->demod_address, .flags = I2C_M_RD,
+ .buf = buf, .len = len }
+ };
+
+ ret = i2c_transfer(state->priv->i2c, msg, 2);
+ if (ret < 0) {
+ dev_warn(&state->priv->i2c->dev,
+ "%s: demod%d i2c rd err(%d) @0x%x\n",
+ KBUILD_MODNAME, state->demod, ret, reg);
+ return ret;
+ }
+ return 0;
+}
+
+static int cx24117_set_inversion(struct cx24117_state *state,
+ enum fe_spectral_inversion inversion)
+{
+ dev_dbg(&state->priv->i2c->dev, "%s(%d) demod%d\n",
+ __func__, inversion, state->demod);
+
+ switch (inversion) {
+ case INVERSION_OFF:
+ state->dnxt.inversion_val = 0x00;
+ break;
+ case INVERSION_ON:
+ state->dnxt.inversion_val = 0x04;
+ break;
+ case INVERSION_AUTO:
+ state->dnxt.inversion_val = 0x0C;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ state->dnxt.inversion = inversion;
+
+ return 0;
+}
+
+static int cx24117_lookup_fecmod(struct cx24117_state *state,
+ enum fe_delivery_system d, enum fe_modulation m, enum fe_code_rate f)
+{
+ int i, ret = -EINVAL;
+
+ dev_dbg(&state->priv->i2c->dev,
+ "%s(demod(0x%02x,0x%02x) demod%d\n",
+ __func__, m, f, state->demod);
+
+ for (i = 0; i < ARRAY_SIZE(cx24117_modfec_modes); i++) {
+ if ((d == cx24117_modfec_modes[i].delivery_system) &&
+ (m == cx24117_modfec_modes[i].modulation) &&
+ (f == cx24117_modfec_modes[i].fec)) {
+ ret = i;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int cx24117_set_fec(struct cx24117_state *state,
+ enum fe_delivery_system delsys,
+ enum fe_modulation mod,
+ enum fe_code_rate fec)
+{
+ int ret;
+
+ dev_dbg(&state->priv->i2c->dev,
+ "%s(0x%02x,0x%02x) demod%d\n",
+ __func__, mod, fec, state->demod);
+
+ ret = cx24117_lookup_fecmod(state, delsys, mod, fec);
+ if (ret < 0)
+ return ret;
+
+ state->dnxt.fec = fec;
+ state->dnxt.fec_val = cx24117_modfec_modes[ret].val;
+ state->dnxt.fec_mask = cx24117_modfec_modes[ret].mask;
+ dev_dbg(&state->priv->i2c->dev,
+ "%s() demod%d mask/val = 0x%02x/0x%02x\n", __func__,
+ state->demod, state->dnxt.fec_mask, state->dnxt.fec_val);
+
+ return 0;
+}
+
+static int cx24117_set_symbolrate(struct cx24117_state *state, u32 rate)
+{
+ dev_dbg(&state->priv->i2c->dev, "%s(%d) demod%d\n",
+ __func__, rate, state->demod);
+
+ state->dnxt.symbol_rate = rate;
+
+ dev_dbg(&state->priv->i2c->dev,
+ "%s() demod%d symbol_rate = %d\n",
+ __func__, state->demod, rate);
+
+ return 0;
+}
+
+static int cx24117_load_firmware(struct dvb_frontend *fe,
+ const struct firmware *fw);
+
+static int cx24117_firmware_ondemand(struct dvb_frontend *fe)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ const struct firmware *fw;
+ int ret = 0;
+
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d skip_fw_load=%d\n",
+ __func__, state->demod, state->priv->skip_fw_load);
+
+ if (state->priv->skip_fw_load)
+ return 0;
+
+ /* check if firmware is already running */
+ if (cx24117_readreg(state, 0xeb) != 0xa) {
+ /* Load firmware */
+ /* request the firmware, this will block until loaded */
+ dev_dbg(&state->priv->i2c->dev,
+ "%s: Waiting for firmware upload (%s)...\n",
+ __func__, CX24117_DEFAULT_FIRMWARE);
+ ret = request_firmware(&fw, CX24117_DEFAULT_FIRMWARE,
+ state->priv->i2c->dev.parent);
+ dev_dbg(&state->priv->i2c->dev,
+ "%s: Waiting for firmware upload(2)...\n", __func__);
+ if (ret) {
+ dev_err(&state->priv->i2c->dev,
+ "%s: No firmware uploaded (timeout or file not found?)\n",
+__func__);
+ return ret;
+ }
+
+ /* Make sure we don't recurse back through here
+ * during loading */
+ state->priv->skip_fw_load = 1;
+
+ ret = cx24117_load_firmware(fe, fw);
+ if (ret)
+ dev_err(&state->priv->i2c->dev,
+ "%s: Writing firmware failed\n", __func__);
+ release_firmware(fw);
+
+ dev_info(&state->priv->i2c->dev,
+ "%s: Firmware upload %s\n", __func__,
+ ret == 0 ? "complete" : "failed");
+
+ /* Ensure firmware is always loaded if required */
+ state->priv->skip_fw_load = 0;
+ }
+
+ return ret;
+}
+
+/* Take a basic firmware command structure, format it
+ * and forward it for processing
+ */
+static int cx24117_cmd_execute_nolock(struct dvb_frontend *fe,
+ struct cx24117_cmd *cmd)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ int i, ret;
+
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
+ __func__, state->demod);
+
+ /* Load the firmware if required */
+ ret = cx24117_firmware_ondemand(fe);
+ if (ret != 0)
+ return ret;
+
+ /* Write the command */
+ cx24117_writecmd(state, cmd);
+
+ /* Start execution and wait for cmd to terminate */
+ cx24117_writereg(state, CX24117_REG_EXECUTE, 0x01);
+ i = 0;
+ while (cx24117_readreg(state, CX24117_REG_EXECUTE)) {
+ msleep(20);
+ if (i++ > 40) {
+ /* Avoid looping forever if the firmware does
+ not respond */
+ dev_warn(&state->priv->i2c->dev,
+ "%s() Firmware not responding\n", __func__);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+static int cx24117_cmd_execute(struct dvb_frontend *fe, struct cx24117_cmd *cmd)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ int ret;
+
+ mutex_lock(&state->priv->fe_lock);
+ ret = cx24117_cmd_execute_nolock(fe, cmd);
+ mutex_unlock(&state->priv->fe_lock);
+
+ return ret;
+}
+
+static int cx24117_load_firmware(struct dvb_frontend *fe,
+ const struct firmware *fw)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ struct cx24117_cmd cmd;
+ int i, ret;
+ unsigned char vers[4];
+
+ struct i2c_msg msg;
+ u8 *buf;
+
+ dev_dbg(&state->priv->i2c->dev,
+ "%s() demod%d FW is %zu bytes (%02x %02x .. %02x %02x)\n",
+ __func__, state->demod, fw->size, fw->data[0], fw->data[1],
+ fw->data[fw->size - 2], fw->data[fw->size - 1]);
+
+ cx24117_writereg(state, 0xea, 0x00);
+ cx24117_writereg(state, 0xea, 0x01);
+ cx24117_writereg(state, 0xea, 0x00);
+
+ cx24117_writereg(state, 0xce, 0x92);
+
+ cx24117_writereg(state, 0xfb, 0x00);
+ cx24117_writereg(state, 0xfc, 0x00);
+
+ cx24117_writereg(state, 0xc3, 0x04);
+ cx24117_writereg(state, 0xc4, 0x04);
+
+ cx24117_writereg(state, 0xce, 0x00);
+ cx24117_writereg(state, 0xcf, 0x00);
+
+ cx24117_writereg(state, 0xea, 0x00);
+ cx24117_writereg(state, 0xeb, 0x0c);
+ cx24117_writereg(state, 0xec, 0x06);
+ cx24117_writereg(state, 0xed, 0x05);
+ cx24117_writereg(state, 0xee, 0x03);
+ cx24117_writereg(state, 0xef, 0x05);
+
+ cx24117_writereg(state, 0xf3, 0x03);
+ cx24117_writereg(state, 0xf4, 0x44);
+
+ cx24117_writereg(state, CX24117_REG_RATEDIV0, 0x04);
+ cx24117_writereg(state, CX24117_REG_CLKDIV0, 0x02);
+
+ cx24117_writereg(state, CX24117_REG_RATEDIV1, 0x04);
+ cx24117_writereg(state, CX24117_REG_CLKDIV1, 0x02);
+
+ cx24117_writereg(state, 0xf2, 0x04);
+ cx24117_writereg(state, 0xe8, 0x02);
+ cx24117_writereg(state, 0xea, 0x01);
+ cx24117_writereg(state, 0xc8, 0x00);
+ cx24117_writereg(state, 0xc9, 0x00);
+ cx24117_writereg(state, 0xca, 0x00);
+ cx24117_writereg(state, 0xcb, 0x00);
+ cx24117_writereg(state, 0xcc, 0x00);
+ cx24117_writereg(state, 0xcd, 0x00);
+ cx24117_writereg(state, 0xe4, 0x03);
+ cx24117_writereg(state, 0xeb, 0x0a);
+
+ cx24117_writereg(state, 0xfb, 0x00);
+ cx24117_writereg(state, 0xe0, 0x76);
+ cx24117_writereg(state, 0xf7, 0x81);
+ cx24117_writereg(state, 0xf8, 0x00);
+ cx24117_writereg(state, 0xf9, 0x00);
+
+ buf = kmalloc(fw->size + 1, GFP_KERNEL);
+ if (buf == NULL) {
+ state->priv->skip_fw_load = 0;
+ return -ENOMEM;
+ }
+
+ /* fw upload reg */
+ buf[0] = 0xfa;
+ memcpy(&buf[1], fw->data, fw->size);
+
+ /* prepare i2c message to send */
+ msg.addr = state->priv->demod_address;
+ msg.flags = 0;
+ msg.len = fw->size + 1;
+ msg.buf = buf;
+
+ /* send fw */
+ ret = i2c_transfer(state->priv->i2c, &msg, 1);
+ if (ret < 0) {
+ kfree(buf);
+ return ret;
+ }
+
+ kfree(buf);
+
+ cx24117_writereg(state, 0xf7, 0x0c);
+ cx24117_writereg(state, 0xe0, 0x00);
+
+ /* Init demodulator */
+ cmd.args[0] = CMD_DEMODINIT;
+ cmd.args[1] = 0x00;
+ cmd.args[2] = 0x01;
+ cmd.args[3] = 0x00;
+ cmd.len = 4;
+ ret = cx24117_cmd_execute_nolock(fe, &cmd);
+ if (ret != 0)
+ goto error;
+
+ /* Set VCO frequency */
+ cmd.args[0] = CMD_SET_VCOFREQ;
+ cmd.args[1] = 0x06;
+ cmd.args[2] = 0x2b;
+ cmd.args[3] = 0xd8;
+ cmd.args[4] = 0xa5;
+ cmd.args[5] = 0xee;
+ cmd.args[6] = 0x03;
+ cmd.args[7] = 0x9d;
+ cmd.args[8] = 0xfc;
+ cmd.args[9] = 0x06;
+ cmd.args[10] = 0x02;
+ cmd.args[11] = 0x9d;
+ cmd.args[12] = 0xfc;
+ cmd.len = 13;
+ ret = cx24117_cmd_execute_nolock(fe, &cmd);
+ if (ret != 0)
+ goto error;
+
+ /* Tuner init */
+ cmd.args[0] = CMD_TUNERINIT;
+ cmd.args[1] = 0x00;
+ cmd.args[2] = 0x01;
+ cmd.args[3] = 0x00;
+ cmd.args[4] = 0x00;
+ cmd.args[5] = 0x01;
+ cmd.args[6] = 0x01;
+ cmd.args[7] = 0x01;
+ cmd.args[8] = 0x00;
+ cmd.args[9] = 0x05;
+ cmd.args[10] = 0x02;
+ cmd.args[11] = 0x02;
+ cmd.args[12] = 0x00;
+ cmd.len = 13;
+ ret = cx24117_cmd_execute_nolock(fe, &cmd);
+ if (ret != 0)
+ goto error;
+
+ /* Global MPEG config */
+ cmd.args[0] = CMD_GLOBAL_MPEGCFG;
+ cmd.args[1] = 0x00;
+ cmd.args[2] = 0x00;
+ cmd.args[3] = 0x00;
+ cmd.args[4] = 0x01;
+ cmd.args[5] = 0x00;
+ cmd.len = 6;
+ ret = cx24117_cmd_execute_nolock(fe, &cmd);
+ if (ret != 0)
+ goto error;
+
+ /* MPEG config for each demod */
+ for (i = 0; i < 2; i++) {
+ cmd.args[0] = CMD_MPEGCFG;
+ cmd.args[1] = (u8) i;
+ cmd.args[2] = 0x00;
+ cmd.args[3] = 0x05;
+ cmd.args[4] = 0x00;
+ cmd.args[5] = 0x00;
+ cmd.args[6] = 0x55;
+ cmd.args[7] = 0x00;
+ cmd.len = 8;
+ ret = cx24117_cmd_execute_nolock(fe, &cmd);
+ if (ret != 0)
+ goto error;
+ }
+
+ cx24117_writereg(state, 0xce, 0xc0);
+ cx24117_writereg(state, 0xcf, 0x00);
+ cx24117_writereg(state, 0xe5, 0x04);
+
+ /* Get firmware version */
+ cmd.args[0] = CMD_FWVERSION;
+ cmd.len = 2;
+ for (i = 0; i < 4; i++) {
+ cmd.args[1] = i;
+ ret = cx24117_cmd_execute_nolock(fe, &cmd);
+ if (ret != 0)
+ goto error;
+ vers[i] = cx24117_readreg(state, 0x33);
+ }
+ dev_info(&state->priv->i2c->dev,
+ "%s: FW version %i.%i.%i.%i\n", __func__,
+ vers[0], vers[1], vers[2], vers[3]);
+ return 0;
+error:
+ state->priv->skip_fw_load = 0;
+ dev_err(&state->priv->i2c->dev, "%s() Error running FW.\n", __func__);
+ return ret;
+}
+
+static int cx24117_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ int lock;
+
+ lock = cx24117_readreg(state,
+ (state->demod == 0) ? CX24117_REG_SSTATUS0 :
+ CX24117_REG_SSTATUS1) &
+ CX24117_STATUS_MASK;
+
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d status = 0x%02x\n",
+ __func__, state->demod, lock);
+
+ *status = 0;
+
+ if (lock & CX24117_HAS_SIGNAL)
+ *status |= FE_HAS_SIGNAL;
+ if (lock & CX24117_HAS_CARRIER)
+ *status |= FE_HAS_CARRIER;
+ if (lock & CX24117_HAS_VITERBI)
+ *status |= FE_HAS_VITERBI;
+ if (lock & CX24117_HAS_SYNCLOCK)
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int cx24117_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ int ret;
+ u8 buf[4];
+ u8 base_reg = (state->demod == 0) ?
+ CX24117_REG_BER4_0 :
+ CX24117_REG_BER4_1;
+
+ ret = cx24117_readregN(state, base_reg, buf, 4);
+ if (ret != 0)
+ return ret;
+
+ *ber = (buf[0] << 24) | (buf[1] << 16) |
+ (buf[1] << 8) | buf[0];
+
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d ber=0x%04x\n",
+ __func__, state->demod, *ber);
+
+ return 0;
+}
+
+static int cx24117_read_signal_strength(struct dvb_frontend *fe,
+ u16 *signal_strength)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ struct cx24117_cmd cmd;
+ int ret;
+ u16 sig_reading;
+ u8 buf[2];
+ u8 reg = (state->demod == 0) ?
+ CX24117_REG_SSTATUS0 : CX24117_REG_SSTATUS1;
+
+ /* Read AGC accumulator register */
+ cmd.args[0] = CMD_GET_AGCACC;
+ cmd.args[1] = (u8) state->demod;
+ cmd.len = 2;
+ ret = cx24117_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ ret = cx24117_readregN(state, reg, buf, 2);
+ if (ret != 0)
+ return ret;
+ sig_reading = ((buf[0] & CX24117_SIGNAL_MASK) << 2) | buf[1];
+
+ *signal_strength = -100 * sig_reading + 94324;
+
+ dev_dbg(&state->priv->i2c->dev,
+ "%s() demod%d raw / cooked = 0x%04x / 0x%04x\n",
+ __func__, state->demod, sig_reading, *signal_strength);
+
+ return 0;
+}
+
+static int cx24117_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+ u8 reg = (state->demod == 0) ?
+ CX24117_REG_QUALITY2_0 : CX24117_REG_QUALITY2_1;
+
+ ret = cx24117_readregN(state, reg, buf, 2);
+ if (ret != 0)
+ return ret;
+
+ *snr = (buf[0] << 8) | buf[1];
+
+ dev_dbg(&state->priv->i2c->dev,
+ "%s() demod%d snr = 0x%04x\n",
+ __func__, state->demod, *snr);
+
+ return ret;
+}
+
+static int cx24117_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ enum fe_delivery_system delsys = fe->dtv_property_cache.delivery_system;
+ int ret;
+ u8 buf[2];
+ u8 reg = (state->demod == 0) ?
+ CX24117_REG_DVBS_UCB2_0 :
+ CX24117_REG_DVBS_UCB2_1;
+
+ switch (delsys) {
+ case SYS_DVBS:
+ break;
+ case SYS_DVBS2:
+ reg += (CX24117_REG_DVBS2_UCB2_0 - CX24117_REG_DVBS_UCB2_0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = cx24117_readregN(state, reg, buf, 2);
+ if (ret != 0)
+ return ret;
+ *ucblocks = (buf[0] << 8) | buf[1];
+
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d ucb=0x%04x\n",
+ __func__, state->demod, *ucblocks);
+
+ return 0;
+}
+
+/* Overwrite the current tuning params, we are about to tune */
+static void cx24117_clone_params(struct dvb_frontend *fe)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ state->dcur = state->dnxt;
+}
+
+/* Wait for LNB */
+static int cx24117_wait_for_lnb(struct dvb_frontend *fe)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ int i;
+ u8 val, reg = (state->demod == 0) ? CX24117_REG_QSTATUS0 :
+ CX24117_REG_QSTATUS1;
+
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d qstatus = 0x%02x\n",
+ __func__, state->demod, cx24117_readreg(state, reg));
+
+ /* Wait for up to 300 ms */
+ for (i = 0; i < 10; i++) {
+ val = cx24117_readreg(state, reg) & 0x01;
+ if (val != 0)
+ return 0;
+ msleep(30);
+ }
+
+ dev_warn(&state->priv->i2c->dev, "%s: demod%d LNB not ready\n",
+ KBUILD_MODNAME, state->demod);
+
+ return -ETIMEDOUT; /* -EBUSY ? */
+}
+
+static int cx24117_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ struct cx24117_cmd cmd;
+ int ret;
+ u8 reg = (state->demod == 0) ? 0x10 : 0x20;
+
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d %s\n",
+ __func__, state->demod,
+ voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
+ voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" :
+ "SEC_VOLTAGE_OFF");
+
+ /* Prepare a set GPIO logic level CMD */
+ cmd.args[0] = CMD_SET_GPIOOUT;
+ cmd.args[2] = reg; /* mask */
+ cmd.len = 3;
+
+ if ((voltage == SEC_VOLTAGE_13) ||
+ (voltage == SEC_VOLTAGE_18)) {
+ /* power on LNB */
+ cmd.args[1] = reg;
+ ret = cx24117_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ ret = cx24117_wait_for_lnb(fe);
+ if (ret != 0)
+ return ret;
+
+ /* Wait for voltage/min repeat delay */
+ msleep(100);
+
+ /* Set 13V/18V select pin */
+ cmd.args[0] = CMD_LNBDCLEVEL;
+ cmd.args[1] = state->demod ? 0 : 1;
+ cmd.args[2] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00);
+ cmd.len = 3;
+ ret = cx24117_cmd_execute(fe, &cmd);
+
+ /* Min delay time before DiSEqC send */
+ msleep(20);
+ } else {
+ /* power off LNB */
+ cmd.args[1] = 0x00;
+ ret = cx24117_cmd_execute(fe, &cmd);
+ }
+
+ return ret;
+}
+
+static int cx24117_set_tone(struct dvb_frontend *fe,
+ enum fe_sec_tone_mode tone)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ struct cx24117_cmd cmd;
+ int ret;
+
+ dev_dbg(&state->priv->i2c->dev, "%s(%d) demod%d\n",
+ __func__, state->demod, tone);
+ if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
+ dev_warn(&state->priv->i2c->dev, "%s: demod%d invalid tone=%d\n",
+ KBUILD_MODNAME, state->demod, tone);
+ return -EINVAL;
+ }
+
+ /* Wait for LNB ready */
+ ret = cx24117_wait_for_lnb(fe);
+ if (ret != 0)
+ return ret;
+
+ /* Min delay time after DiSEqC send */
+ msleep(20);
+
+ /* Set the tone */
+ cmd.args[0] = CMD_LNBPCBCONFIG;
+ cmd.args[1] = (state->demod ? 0 : 1);
+ cmd.args[2] = 0x00;
+ cmd.args[3] = 0x00;
+ cmd.len = 5;
+ switch (tone) {
+ case SEC_TONE_ON:
+ cmd.args[4] = 0x01;
+ break;
+ case SEC_TONE_OFF:
+ cmd.args[4] = 0x00;
+ break;
+ }
+
+ msleep(20);
+
+ return cx24117_cmd_execute(fe, &cmd);
+}
+
+/* Initialise DiSEqC */
+static int cx24117_diseqc_init(struct dvb_frontend *fe)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+
+ /* Prepare a DiSEqC command */
+ state->dsec_cmd.args[0] = CMD_LNBSEND;
+
+ /* demod */
+ state->dsec_cmd.args[CX24117_DISEQC_DEMOD] = state->demod ? 0 : 1;
+
+ /* DiSEqC burst */
+ state->dsec_cmd.args[CX24117_DISEQC_BURST] = CX24117_DISEQC_MINI_A;
+
+ /* Unknown */
+ state->dsec_cmd.args[CX24117_DISEQC_ARG3_2] = 0x02;
+ state->dsec_cmd.args[CX24117_DISEQC_ARG4_0] = 0x00;
+
+ /* Continuation flag? */
+ state->dsec_cmd.args[CX24117_DISEQC_ARG5_0] = 0x00;
+
+ /* DiSEqC message length */
+ state->dsec_cmd.args[CX24117_DISEQC_MSGLEN] = 0x00;
+
+ /* Command length */
+ state->dsec_cmd.len = 7;
+
+ return 0;
+}
+
+/* Send DiSEqC message */
+static int cx24117_send_diseqc_msg(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *d)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ int i, ret;
+
+ /* Dump DiSEqC message */
+ dev_dbg(&state->priv->i2c->dev, "%s: demod %d (",
+ __func__, state->demod);
+ for (i = 0; i < d->msg_len; i++)
+ dev_dbg(&state->priv->i2c->dev, "0x%02x ", d->msg[i]);
+ dev_dbg(&state->priv->i2c->dev, ")\n");
+
+ /* Validate length */
+ if (d->msg_len > sizeof(d->msg))
+ return -EINVAL;
+
+ /* DiSEqC message */
+ for (i = 0; i < d->msg_len; i++)
+ state->dsec_cmd.args[CX24117_DISEQC_MSGOFS + i] = d->msg[i];
+
+ /* DiSEqC message length */
+ state->dsec_cmd.args[CX24117_DISEQC_MSGLEN] = d->msg_len;
+
+ /* Command length */
+ state->dsec_cmd.len = CX24117_DISEQC_MSGOFS +
+ state->dsec_cmd.args[CX24117_DISEQC_MSGLEN];
+
+ /*
+ * Message is sent with derived else cached burst
+ *
+ * WRITE PORT GROUP COMMAND 38
+ *
+ * 0/A/A: E0 10 38 F0..F3
+ * 1/B/B: E0 10 38 F4..F7
+ * 2/C/A: E0 10 38 F8..FB
+ * 3/D/B: E0 10 38 FC..FF
+ *
+ * databyte[3]= 8421:8421
+ * ABCD:WXYZ
+ * CLR :SET
+ *
+ * WX= PORT SELECT 0..3 (X=TONEBURST)
+ * Y = VOLTAGE (0=13V, 1=18V)
+ * Z = BAND (0=LOW, 1=HIGH(22K))
+ */
+ if (d->msg_len >= 4 && d->msg[2] == 0x38)
+ state->dsec_cmd.args[CX24117_DISEQC_BURST] =
+ ((d->msg[3] & 4) >> 2);
+
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d burst=%d\n",
+ __func__, state->demod,
+ state->dsec_cmd.args[CX24117_DISEQC_BURST]);
+
+ /* Wait for LNB ready */
+ ret = cx24117_wait_for_lnb(fe);
+ if (ret != 0)
+ return ret;
+
+ /* Wait for voltage/min repeat delay */
+ msleep(100);
+
+ /* Command */
+ ret = cx24117_cmd_execute(fe, &state->dsec_cmd);
+ if (ret != 0)
+ return ret;
+ /*
+ * Wait for send
+ *
+ * Eutelsat spec:
+ * >15ms delay + (XXX determine if FW does this, see set_tone)
+ * 13.5ms per byte +
+ * >15ms delay +
+ * 12.5ms burst +
+ * >15ms delay (XXX determine if FW does this, see set_tone)
+ */
+ msleep((state->dsec_cmd.args[CX24117_DISEQC_MSGLEN] << 4) + 60);
+
+ return 0;
+}
+
+/* Send DiSEqC burst */
+static int cx24117_diseqc_send_burst(struct dvb_frontend *fe,
+ enum fe_sec_mini_cmd burst)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+
+ dev_dbg(&state->priv->i2c->dev, "%s(%d) demod=%d\n",
+ __func__, burst, state->demod);
+
+ /* DiSEqC burst */
+ if (burst == SEC_MINI_A)
+ state->dsec_cmd.args[CX24117_DISEQC_BURST] =
+ CX24117_DISEQC_MINI_A;
+ else if (burst == SEC_MINI_B)
+ state->dsec_cmd.args[CX24117_DISEQC_BURST] =
+ CX24117_DISEQC_MINI_B;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int cx24117_get_priv(struct cx24117_priv **priv,
+ struct i2c_adapter *i2c, u8 client_address)
+{
+ int ret;
+
+ mutex_lock(&cx24117_list_mutex);
+ ret = hybrid_tuner_request_state(struct cx24117_priv, (*priv),
+ hybrid_tuner_instance_list, i2c, client_address, "cx24117");
+ mutex_unlock(&cx24117_list_mutex);
+
+ return ret;
+}
+
+static void cx24117_release_priv(struct cx24117_priv *priv)
+{
+ mutex_lock(&cx24117_list_mutex);
+ if (priv != NULL)
+ hybrid_tuner_release_state(priv);
+ mutex_unlock(&cx24117_list_mutex);
+}
+
+static void cx24117_release(struct dvb_frontend *fe)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ dev_dbg(&state->priv->i2c->dev, "%s demod%d\n",
+ __func__, state->demod);
+ cx24117_release_priv(state->priv);
+ kfree(state);
+}
+
+static const struct dvb_frontend_ops cx24117_ops;
+
+struct dvb_frontend *cx24117_attach(const struct cx24117_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct cx24117_state *state = NULL;
+ struct cx24117_priv *priv = NULL;
+ int demod = 0;
+
+ /* get the common data struct for both demods */
+ demod = cx24117_get_priv(&priv, i2c, config->demod_address);
+
+ switch (demod) {
+ case 0:
+ dev_err(&i2c->dev,
+ "%s: Error attaching frontend %d\n",
+ KBUILD_MODNAME, demod);
+ goto error1;
+ case 1:
+ /* new priv instance */
+ priv->i2c = i2c;
+ priv->demod_address = config->demod_address;
+ mutex_init(&priv->fe_lock);
+ break;
+ default:
+ /* existing priv instance */
+ break;
+ }
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct cx24117_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error2;
+
+ state->demod = demod - 1;
+ state->priv = priv;
+
+ dev_info(&state->priv->i2c->dev,
+ "%s: Attaching frontend %d\n",
+ KBUILD_MODNAME, state->demod);
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &cx24117_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error2:
+ cx24117_release_priv(priv);
+error1:
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(cx24117_attach);
+
+/*
+ * Initialise or wake up device
+ *
+ * Power config will reset and load initial firmware if required
+ */
+static int cx24117_initfe(struct dvb_frontend *fe)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ struct cx24117_cmd cmd;
+ int ret;
+
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
+ __func__, state->demod);
+
+ mutex_lock(&state->priv->fe_lock);
+
+ /* Set sleep mode off */
+ cmd.args[0] = CMD_SET_SLEEPMODE;
+ cmd.args[1] = (state->demod ? 1 : 0);
+ cmd.args[2] = 0;
+ cmd.len = 3;
+ ret = cx24117_cmd_execute_nolock(fe, &cmd);
+ if (ret != 0)
+ goto exit;
+
+ ret = cx24117_diseqc_init(fe);
+ if (ret != 0)
+ goto exit;
+
+ /* Set BER control */
+ cmd.args[0] = CMD_BERCTRL;
+ cmd.args[1] = (state->demod ? 1 : 0);
+ cmd.args[2] = 0x10;
+ cmd.args[3] = 0x10;
+ cmd.len = 4;
+ ret = cx24117_cmd_execute_nolock(fe, &cmd);
+ if (ret != 0)
+ goto exit;
+
+ /* Set RS correction (enable/disable) */
+ cmd.args[0] = CMD_ENABLERSCORR;
+ cmd.args[1] = (state->demod ? 1 : 0);
+ cmd.args[2] = CX24117_OCC;
+ cmd.len = 3;
+ ret = cx24117_cmd_execute_nolock(fe, &cmd);
+ if (ret != 0)
+ goto exit;
+
+ /* Set GPIO direction */
+ /* Set as output - controls LNB power on/off */
+ cmd.args[0] = CMD_SET_GPIODIR;
+ cmd.args[1] = 0x30;
+ cmd.args[2] = 0x30;
+ cmd.len = 3;
+ ret = cx24117_cmd_execute_nolock(fe, &cmd);
+
+exit:
+ mutex_unlock(&state->priv->fe_lock);
+
+ return ret;
+}
+
+/*
+ * Put device to sleep
+ */
+static int cx24117_sleep(struct dvb_frontend *fe)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ struct cx24117_cmd cmd;
+
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
+ __func__, state->demod);
+
+ /* Set sleep mode on */
+ cmd.args[0] = CMD_SET_SLEEPMODE;
+ cmd.args[1] = (state->demod ? 1 : 0);
+ cmd.args[2] = 1;
+ cmd.len = 3;
+ return cx24117_cmd_execute(fe, &cmd);
+}
+
+/* dvb-core told us to tune, the tv property cache will be complete,
+ * it's safe for is to pull values and use them for tuning purposes.
+ */
+static int cx24117_set_frontend(struct dvb_frontend *fe)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct cx24117_cmd cmd;
+ enum fe_status tunerstat;
+ int i, status, ret, retune = 1;
+ u8 reg_clkdiv, reg_ratediv;
+
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
+ __func__, state->demod);
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d DVB-S\n",
+ __func__, state->demod);
+
+ /* Only QPSK is supported for DVB-S */
+ if (c->modulation != QPSK) {
+ dev_dbg(&state->priv->i2c->dev,
+ "%s() demod%d unsupported modulation (%d)\n",
+ __func__, state->demod, c->modulation);
+ return -EINVAL;
+ }
+
+ /* Pilot doesn't exist in DVB-S, turn bit off */
+ state->dnxt.pilot_val = CX24117_PILOT_OFF;
+
+ /* DVB-S only supports 0.35 */
+ state->dnxt.rolloff_val = CX24117_ROLLOFF_035;
+ break;
+
+ case SYS_DVBS2:
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d DVB-S2\n",
+ __func__, state->demod);
+
+ /*
+ * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2,
+ * but not hardware auto detection
+ */
+ if (c->modulation != PSK_8 && c->modulation != QPSK) {
+ dev_dbg(&state->priv->i2c->dev,
+ "%s() demod%d unsupported modulation (%d)\n",
+ __func__, state->demod, c->modulation);
+ return -EOPNOTSUPP;
+ }
+
+ switch (c->pilot) {
+ case PILOT_AUTO:
+ state->dnxt.pilot_val = CX24117_PILOT_AUTO;
+ break;
+ case PILOT_OFF:
+ state->dnxt.pilot_val = CX24117_PILOT_OFF;
+ break;
+ case PILOT_ON:
+ state->dnxt.pilot_val = CX24117_PILOT_ON;
+ break;
+ default:
+ dev_dbg(&state->priv->i2c->dev,
+ "%s() demod%d unsupported pilot mode (%d)\n",
+ __func__, state->demod, c->pilot);
+ return -EOPNOTSUPP;
+ }
+
+ switch (c->rolloff) {
+ case ROLLOFF_20:
+ state->dnxt.rolloff_val = CX24117_ROLLOFF_020;
+ break;
+ case ROLLOFF_25:
+ state->dnxt.rolloff_val = CX24117_ROLLOFF_025;
+ break;
+ case ROLLOFF_35:
+ state->dnxt.rolloff_val = CX24117_ROLLOFF_035;
+ break;
+ case ROLLOFF_AUTO:
+ state->dnxt.rolloff_val = CX24117_ROLLOFF_035;
+ /* soft-auto rolloff */
+ retune = 3;
+ break;
+ default:
+ dev_warn(&state->priv->i2c->dev,
+ "%s: demod%d unsupported rolloff (%d)\n",
+ KBUILD_MODNAME, state->demod, c->rolloff);
+ return -EOPNOTSUPP;
+ }
+ break;
+
+ default:
+ dev_warn(&state->priv->i2c->dev,
+ "%s: demod %d unsupported delivery system (%d)\n",
+ KBUILD_MODNAME, state->demod, c->delivery_system);
+ return -EINVAL;
+ }
+
+ state->dnxt.delsys = c->delivery_system;
+ state->dnxt.modulation = c->modulation;
+ state->dnxt.frequency = c->frequency;
+ state->dnxt.pilot = c->pilot;
+ state->dnxt.rolloff = c->rolloff;
+
+ ret = cx24117_set_inversion(state, c->inversion);
+ if (ret != 0)
+ return ret;
+
+ ret = cx24117_set_fec(state,
+ c->delivery_system, c->modulation, c->fec_inner);
+ if (ret != 0)
+ return ret;
+
+ ret = cx24117_set_symbolrate(state, c->symbol_rate);
+ if (ret != 0)
+ return ret;
+
+ /* discard the 'current' tuning parameters and prepare to tune */
+ cx24117_clone_params(fe);
+
+ dev_dbg(&state->priv->i2c->dev,
+ "%s: delsys = %d\n", __func__, state->dcur.delsys);
+ dev_dbg(&state->priv->i2c->dev,
+ "%s: modulation = %d\n", __func__, state->dcur.modulation);
+ dev_dbg(&state->priv->i2c->dev,
+ "%s: frequency = %d\n", __func__, state->dcur.frequency);
+ dev_dbg(&state->priv->i2c->dev,
+ "%s: pilot = %d (val = 0x%02x)\n", __func__,
+ state->dcur.pilot, state->dcur.pilot_val);
+ dev_dbg(&state->priv->i2c->dev,
+ "%s: retune = %d\n", __func__, retune);
+ dev_dbg(&state->priv->i2c->dev,
+ "%s: rolloff = %d (val = 0x%02x)\n", __func__,
+ state->dcur.rolloff, state->dcur.rolloff_val);
+ dev_dbg(&state->priv->i2c->dev,
+ "%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate);
+ dev_dbg(&state->priv->i2c->dev,
+ "%s: FEC = %d (mask/val = 0x%02x/0x%02x)\n", __func__,
+ state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val);
+ dev_dbg(&state->priv->i2c->dev,
+ "%s: Inversion = %d (val = 0x%02x)\n", __func__,
+ state->dcur.inversion, state->dcur.inversion_val);
+
+ /* Prepare a tune request */
+ cmd.args[0] = CMD_TUNEREQUEST;
+
+ /* demod */
+ cmd.args[1] = state->demod;
+
+ /* Frequency */
+ cmd.args[2] = (state->dcur.frequency & 0xff0000) >> 16;
+ cmd.args[3] = (state->dcur.frequency & 0x00ff00) >> 8;
+ cmd.args[4] = (state->dcur.frequency & 0x0000ff);
+
+ /* Symbol Rate */
+ cmd.args[5] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8;
+ cmd.args[6] = ((state->dcur.symbol_rate / 1000) & 0x00ff);
+
+ /* Automatic Inversion */
+ cmd.args[7] = state->dcur.inversion_val;
+
+ /* Modulation / FEC / Pilot */
+ cmd.args[8] = state->dcur.fec_val | state->dcur.pilot_val;
+
+ cmd.args[9] = CX24117_SEARCH_RANGE_KHZ >> 8;
+ cmd.args[10] = CX24117_SEARCH_RANGE_KHZ & 0xff;
+
+ cmd.args[11] = state->dcur.rolloff_val;
+ cmd.args[12] = state->dcur.fec_mask;
+
+ if (state->dcur.symbol_rate > 30000000) {
+ reg_ratediv = 0x04;
+ reg_clkdiv = 0x02;
+ } else if (state->dcur.symbol_rate > 10000000) {
+ reg_ratediv = 0x06;
+ reg_clkdiv = 0x03;
+ } else {
+ reg_ratediv = 0x0a;
+ reg_clkdiv = 0x05;
+ }
+
+ cmd.args[13] = reg_ratediv;
+ cmd.args[14] = reg_clkdiv;
+
+ cx24117_writereg(state, (state->demod == 0) ?
+ CX24117_REG_CLKDIV0 : CX24117_REG_CLKDIV1, reg_clkdiv);
+ cx24117_writereg(state, (state->demod == 0) ?
+ CX24117_REG_RATEDIV0 : CX24117_REG_RATEDIV1, reg_ratediv);
+
+ cmd.args[15] = CX24117_PNE;
+ cmd.len = 16;
+
+ do {
+ /* Reset status register */
+ status = cx24117_readreg(state, (state->demod == 0) ?
+ CX24117_REG_SSTATUS0 : CX24117_REG_SSTATUS1) &
+ CX24117_SIGNAL_MASK;
+
+ dev_dbg(&state->priv->i2c->dev,
+ "%s() demod%d status_setfe = %02x\n",
+ __func__, state->demod, status);
+
+ cx24117_writereg(state, (state->demod == 0) ?
+ CX24117_REG_SSTATUS0 : CX24117_REG_SSTATUS1, status);
+
+ /* Tune */
+ ret = cx24117_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ break;
+
+ /*
+ * Wait for up to 500 ms before retrying
+ *
+ * If we are able to tune then generally it occurs within 100ms.
+ * If it takes longer, try a different rolloff setting.
+ */
+ for (i = 0; i < 50; i++) {
+ cx24117_read_status(fe, &tunerstat);
+ status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC);
+ if (status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) {
+ dev_dbg(&state->priv->i2c->dev,
+ "%s() demod%d tuned\n",
+ __func__, state->demod);
+ return 0;
+ }
+ msleep(20);
+ }
+
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d not tuned\n",
+ __func__, state->demod);
+
+ /* try next rolloff value */
+ if (state->dcur.rolloff == 3)
+ cmd.args[11]--;
+
+ } while (--retune);
+ return -EINVAL;
+}
+
+static int cx24117_tune(struct dvb_frontend *fe, bool re_tune,
+ unsigned int mode_flags, unsigned int *delay, enum fe_status *status)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+
+ dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
+ __func__, state->demod);
+
+ *delay = HZ / 5;
+ if (re_tune) {
+ int ret = cx24117_set_frontend(fe);
+ if (ret)
+ return ret;
+ }
+ return cx24117_read_status(fe, status);
+}
+
+static enum dvbfe_algo cx24117_get_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
+static int cx24117_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ struct cx24117_state *state = fe->demodulator_priv;
+ struct cx24117_cmd cmd;
+ u8 reg, st, inv;
+ int ret, idx;
+ unsigned int freq;
+ short srate_os, freq_os;
+
+ u8 buf[0x1f-4];
+
+ /* Read current tune parameters */
+ cmd.args[0] = CMD_GETCTLACC;
+ cmd.args[1] = (u8) state->demod;
+ cmd.len = 2;
+ ret = cx24117_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ /* read all required regs at once */
+ reg = (state->demod == 0) ? CX24117_REG_FREQ3_0 : CX24117_REG_FREQ3_1;
+ ret = cx24117_readregN(state, reg, buf, 0x1f-4);
+ if (ret != 0)
+ return ret;
+
+ st = buf[5];
+
+ /* get spectral inversion */
+ inv = (((state->demod == 0) ? ~st : st) >> 6) & 1;
+ if (inv == 0)
+ c->inversion = INVERSION_OFF;
+ else
+ c->inversion = INVERSION_ON;
+
+ /* modulation and fec */
+ idx = st & 0x3f;
+ if (c->delivery_system == SYS_DVBS2) {
+ if (idx > 11)
+ idx += 9;
+ else
+ idx += 7;
+ }
+
+ c->modulation = cx24117_modfec_modes[idx].modulation;
+ c->fec_inner = cx24117_modfec_modes[idx].fec;
+
+ /* frequency */
+ freq = (buf[0] << 16) | (buf[1] << 8) | buf[2];
+ freq_os = (buf[8] << 8) | buf[9];
+ c->frequency = freq + freq_os;
+
+ /* symbol rate */
+ srate_os = (buf[10] << 8) | buf[11];
+ c->symbol_rate = -1000 * srate_os + state->dcur.symbol_rate;
+ return 0;
+}
+
+static const struct dvb_frontend_ops cx24117_ops = {
+ .delsys = { SYS_DVBS, SYS_DVBS2 },
+ .info = {
+ .name = "Conexant CX24117/CX24132",
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
+ .frequency_stepsize_hz = 1011 * kHz,
+ .frequency_tolerance_hz = 5 * MHz,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .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_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_2G_MODULATION |
+ FE_CAN_QPSK | FE_CAN_RECOVER
+ },
+
+ .release = cx24117_release,
+
+ .init = cx24117_initfe,
+ .sleep = cx24117_sleep,
+ .read_status = cx24117_read_status,
+ .read_ber = cx24117_read_ber,
+ .read_signal_strength = cx24117_read_signal_strength,
+ .read_snr = cx24117_read_snr,
+ .read_ucblocks = cx24117_read_ucblocks,
+ .set_tone = cx24117_set_tone,
+ .set_voltage = cx24117_set_voltage,
+ .diseqc_send_master_cmd = cx24117_send_diseqc_msg,
+ .diseqc_send_burst = cx24117_diseqc_send_burst,
+ .get_frontend_algo = cx24117_get_algo,
+ .tune = cx24117_tune,
+
+ .set_frontend = cx24117_set_frontend,
+ .get_frontend = cx24117_get_frontend,
+};
+
+
+MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24117/cx24132 hardware");
+MODULE_AUTHOR("Luis Alves <ljalvs@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.1");
+MODULE_FIRMWARE(CX24117_DEFAULT_FIRMWARE);
+
diff --git a/drivers/media/dvb-frontends/cx24117.h b/drivers/media/dvb-frontends/cx24117.h
new file mode 100644
index 000000000000..a613a33dbbe7
--- /dev/null
+++ b/drivers/media/dvb-frontends/cx24117.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Conexant cx24117/cx24132 - Dual DVBS/S2 Satellite demod/tuner driver
+
+ Copyright (C) 2013 Luis Alves <ljalvs@gmail.com>
+ (based on cx24116.h by Steven Toth)
+
+*/
+
+#ifndef CX24117_H
+#define CX24117_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx24117_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_CX24117)
+extern struct dvb_frontend *cx24117_attach(
+ const struct cx24117_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *cx24117_attach(
+ const struct cx24117_config *config,
+ struct i2c_adapter *i2c)
+{
+ dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* CX24117_H */
diff --git a/drivers/media/dvb-frontends/cx24120.c b/drivers/media/dvb-frontends/cx24120.c
new file mode 100644
index 000000000000..44515fdbe91d
--- /dev/null
+++ b/drivers/media/dvb-frontends/cx24120.c
@@ -0,0 +1,1591 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner driver
+
+ Copyright (C) 2008 Patrick Boettcher <pb@linuxtv.org>
+ Copyright (C) 2009 Sergey Tyurin <forum.free-x.de>
+ Updated 2012 by Jannis Achstetter <jannis_achstetter@web.de>
+ Copyright (C) 2015 Jemma Denson <jdenson@gmail.com>
+ April 2015
+ Refactored & simplified driver
+ Updated to work with delivery system supplied by DVBv5
+ Add frequency, fec & pilot to get_frontend
+
+ Cards supported: Technisat Skystar S2
+
+*/
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <media/dvb_frontend.h>
+#include "cx24120.h"
+
+#define CX24120_SEARCH_RANGE_KHZ 5000
+#define CX24120_FIRMWARE "dvb-fe-cx24120-1.20.58.2.fw"
+
+/* cx24120 i2c registers */
+#define CX24120_REG_CMD_START 0x00 /* write cmd_id */
+#define CX24120_REG_CMD_ARGS 0x01 /* write command arguments */
+#define CX24120_REG_CMD_END 0x1f /* write 0x01 for end */
+
+#define CX24120_REG_MAILBOX 0x33
+#define CX24120_REG_FREQ3 0x34 /* frequency */
+#define CX24120_REG_FREQ2 0x35
+#define CX24120_REG_FREQ1 0x36
+
+#define CX24120_REG_FECMODE 0x39 /* FEC status */
+#define CX24120_REG_STATUS 0x3a /* Tuner status */
+#define CX24120_REG_SIGSTR_H 0x3a /* Signal strength high */
+#define CX24120_REG_SIGSTR_L 0x3b /* Signal strength low byte */
+#define CX24120_REG_QUALITY_H 0x40 /* SNR high byte */
+#define CX24120_REG_QUALITY_L 0x41 /* SNR low byte */
+
+#define CX24120_REG_BER_HH 0x47 /* BER high byte of high word */
+#define CX24120_REG_BER_HL 0x48 /* BER low byte of high word */
+#define CX24120_REG_BER_LH 0x49 /* BER high byte of low word */
+#define CX24120_REG_BER_LL 0x4a /* BER low byte of low word */
+
+#define CX24120_REG_UCB_H 0x50 /* UCB high byte */
+#define CX24120_REG_UCB_L 0x51 /* UCB low byte */
+
+#define CX24120_REG_CLKDIV 0xe6
+#define CX24120_REG_RATEDIV 0xf0
+
+#define CX24120_REG_REVISION 0xff /* Chip revision (ro) */
+
+/* Command messages */
+enum command_message_id {
+ CMD_VCO_SET = 0x10, /* cmd.len = 12; */
+ CMD_TUNEREQUEST = 0x11, /* cmd.len = 15; */
+
+ CMD_MPEG_ONOFF = 0x13, /* cmd.len = 4; */
+ CMD_MPEG_INIT = 0x14, /* cmd.len = 7; */
+ CMD_BANDWIDTH = 0x15, /* cmd.len = 12; */
+ CMD_CLOCK_READ = 0x16, /* read clock */
+ CMD_CLOCK_SET = 0x17, /* cmd.len = 10; */
+
+ CMD_DISEQC_MSG1 = 0x20, /* cmd.len = 11; */
+ CMD_DISEQC_MSG2 = 0x21, /* cmd.len = d->msg_len + 6; */
+ CMD_SETVOLTAGE = 0x22, /* cmd.len = 2; */
+ CMD_SETTONE = 0x23, /* cmd.len = 4; */
+ CMD_DISEQC_BURST = 0x24, /* cmd.len not used !!! */
+
+ CMD_READ_SNR = 0x1a, /* Read signal strength */
+ CMD_START_TUNER = 0x1b, /* ??? */
+
+ CMD_FWVERSION = 0x35,
+
+ CMD_BER_CTRL = 0x3c, /* cmd.len = 0x03; */
+};
+
+#define CX24120_MAX_CMD_LEN 30
+
+/* pilot mask */
+#define CX24120_PILOT_OFF 0x00
+#define CX24120_PILOT_ON 0x40
+#define CX24120_PILOT_AUTO 0x80
+
+/* signal status */
+#define CX24120_HAS_SIGNAL 0x01
+#define CX24120_HAS_CARRIER 0x02
+#define CX24120_HAS_VITERBI 0x04
+#define CX24120_HAS_LOCK 0x08
+#define CX24120_HAS_UNK1 0x10
+#define CX24120_HAS_UNK2 0x20
+#define CX24120_STATUS_MASK 0x0f
+#define CX24120_SIGNAL_MASK 0xc0
+
+/* ber window */
+#define CX24120_BER_WINDOW 16
+#define CX24120_BER_WSIZE ((1 << CX24120_BER_WINDOW) * 208 * 8)
+
+#define info(args...) pr_info("cx24120: " args)
+#define err(args...) pr_err("cx24120: ### ERROR: " args)
+
+/* The Demod/Tuner can't easily provide these, we cache them */
+struct cx24120_tuning {
+ u32 frequency;
+ u32 symbol_rate;
+ enum fe_spectral_inversion inversion;
+ enum fe_code_rate fec;
+
+ enum fe_delivery_system delsys;
+ enum fe_modulation modulation;
+ enum fe_pilot pilot;
+
+ /* Demod values */
+ u8 fec_val;
+ u8 fec_mask;
+ u8 clkdiv;
+ u8 ratediv;
+ u8 inversion_val;
+ u8 pilot_val;
+};
+
+/* Private state */
+struct cx24120_state {
+ struct i2c_adapter *i2c;
+ const struct cx24120_config *config;
+ struct dvb_frontend frontend;
+
+ u8 cold_init;
+ u8 mpeg_enabled;
+ u8 need_clock_set;
+
+ /* current and next tuning parameters */
+ struct cx24120_tuning dcur;
+ struct cx24120_tuning dnxt;
+
+ enum fe_status fe_status;
+
+ /* dvbv5 stats calculations */
+ u32 bitrate;
+ u32 berw_usecs;
+ u32 ber_prev;
+ u32 ucb_offset;
+ unsigned long ber_jiffies_stats;
+ unsigned long per_jiffies_stats;
+};
+
+/* Command message to firmware */
+struct cx24120_cmd {
+ u8 id;
+ u8 len;
+ u8 arg[CX24120_MAX_CMD_LEN];
+};
+
+/* Read single register */
+static int cx24120_readreg(struct cx24120_state *state, u8 reg)
+{
+ int ret;
+ u8 buf = 0;
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->i2c_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg
+ }, {
+ .addr = state->config->i2c_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = &buf
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret != 2) {
+ err("Read error: reg=0x%02x, ret=%i)\n", reg, ret);
+ return ret;
+ }
+
+ dev_dbg(&state->i2c->dev, "reg=0x%02x; data=0x%02x\n", reg, buf);
+
+ return buf;
+}
+
+/* Write single register */
+static int cx24120_writereg(struct cx24120_state *state, u8 reg, u8 data)
+{
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = {
+ .addr = state->config->i2c_addr,
+ .flags = 0,
+ .buf = buf,
+ .len = 2
+ };
+ int ret;
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+ if (ret != 1) {
+ err("Write error: i2c_write error(err == %i, 0x%02x: 0x%02x)\n",
+ ret, reg, data);
+ return ret;
+ }
+
+ dev_dbg(&state->i2c->dev, "reg=0x%02x; data=0x%02x\n", reg, data);
+
+ return 0;
+}
+
+/* Write multiple registers in chunks of i2c_wr_max-sized buffers */
+static int cx24120_writeregs(struct cx24120_state *state,
+ u8 reg, const u8 *values, u16 len, u8 incr)
+{
+ int ret;
+ u16 max = state->config->i2c_wr_max > 0 ?
+ state->config->i2c_wr_max :
+ len;
+
+ struct i2c_msg msg = {
+ .addr = state->config->i2c_addr,
+ .flags = 0,
+ };
+
+ msg.buf = kmalloc(max + 1, GFP_KERNEL);
+ if (!msg.buf)
+ return -ENOMEM;
+
+ while (len) {
+ msg.buf[0] = reg;
+ msg.len = len > max ? max : len;
+ memcpy(&msg.buf[1], values, msg.len);
+
+ len -= msg.len; /* data length revers counter */
+ values += msg.len; /* incr data pointer */
+
+ if (incr)
+ reg += msg.len;
+ msg.len++; /* don't forget the addr byte */
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+ if (ret != 1) {
+ err("i2c_write error(err == %i, 0x%02x)\n", ret, reg);
+ goto out;
+ }
+
+ dev_dbg(&state->i2c->dev, "reg=0x%02x; data=%*ph\n",
+ reg, msg.len - 1, msg.buf + 1);
+ }
+
+ ret = 0;
+
+out:
+ kfree(msg.buf);
+ return ret;
+}
+
+static const struct dvb_frontend_ops cx24120_ops;
+
+struct dvb_frontend *cx24120_attach(const struct cx24120_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct cx24120_state *state;
+ int demod_rev;
+
+ info("Conexant cx24120/cx24118 - DVBS/S2 Satellite demod/tuner\n");
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state) {
+ err("Unable to allocate memory for cx24120_state\n");
+ goto error;
+ }
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+
+ /* check if the demod is present and has proper type */
+ demod_rev = cx24120_readreg(state, CX24120_REG_REVISION);
+ switch (demod_rev) {
+ case 0x07:
+ info("Demod cx24120 rev. 0x07 detected.\n");
+ break;
+ case 0x05:
+ info("Demod cx24120 rev. 0x05 detected.\n");
+ break;
+ default:
+ err("Unsupported demod revision: 0x%x detected.\n", demod_rev);
+ goto error;
+ }
+
+ /* create dvb_frontend */
+ state->cold_init = 0;
+ memcpy(&state->frontend.ops, &cx24120_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ info("Conexant cx24120/cx24118 attached.\n");
+ return &state->frontend;
+
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(cx24120_attach);
+
+static int cx24120_test_rom(struct cx24120_state *state)
+{
+ int err, ret;
+
+ err = cx24120_readreg(state, 0xfd);
+ if (err & 4) {
+ ret = cx24120_readreg(state, 0xdf) & 0xfe;
+ err = cx24120_writereg(state, 0xdf, ret);
+ }
+ return err;
+}
+
+static int cx24120_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (c->cnr.stat[0].scale != FE_SCALE_DECIBEL)
+ *snr = 0;
+ else
+ *snr = div_s64(c->cnr.stat[0].svalue, 100);
+
+ return 0;
+}
+
+static int cx24120_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct cx24120_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (c->post_bit_error.stat[0].scale != FE_SCALE_COUNTER) {
+ *ber = 0;
+ return 0;
+ }
+
+ *ber = c->post_bit_error.stat[0].uvalue - state->ber_prev;
+ state->ber_prev = c->post_bit_error.stat[0].uvalue;
+
+ return 0;
+}
+
+static int cx24120_msg_mpeg_output_global_config(struct cx24120_state *state,
+ u8 flag);
+
+/* Check if we're running a command that needs to disable mpeg out */
+static void cx24120_check_cmd(struct cx24120_state *state, u8 id)
+{
+ switch (id) {
+ case CMD_TUNEREQUEST:
+ case CMD_CLOCK_READ:
+ case CMD_DISEQC_MSG1:
+ case CMD_DISEQC_MSG2:
+ case CMD_SETVOLTAGE:
+ case CMD_SETTONE:
+ case CMD_DISEQC_BURST:
+ cx24120_msg_mpeg_output_global_config(state, 0);
+ /* Old driver would do a msleep(100) here */
+ return;
+ default:
+ return;
+ }
+}
+
+/* Send a message to the firmware */
+static int cx24120_message_send(struct cx24120_state *state,
+ struct cx24120_cmd *cmd)
+{
+ int ficus;
+
+ if (state->mpeg_enabled) {
+ /* Disable mpeg out on certain commands */
+ cx24120_check_cmd(state, cmd->id);
+ }
+
+ cx24120_writereg(state, CX24120_REG_CMD_START, cmd->id);
+ cx24120_writeregs(state, CX24120_REG_CMD_ARGS, &cmd->arg[0],
+ cmd->len, 1);
+ cx24120_writereg(state, CX24120_REG_CMD_END, 0x01);
+
+ ficus = 1000;
+ while (cx24120_readreg(state, CX24120_REG_CMD_END)) {
+ msleep(20);
+ ficus -= 20;
+ if (ficus == 0) {
+ err("Error sending message to firmware\n");
+ return -EREMOTEIO;
+ }
+ }
+ dev_dbg(&state->i2c->dev, "sent message 0x%02x\n", cmd->id);
+
+ return 0;
+}
+
+/* Send a message and fill arg[] with the results */
+static int cx24120_message_sendrcv(struct cx24120_state *state,
+ struct cx24120_cmd *cmd, u8 numreg)
+{
+ int ret, i;
+
+ if (numreg > CX24120_MAX_CMD_LEN) {
+ err("Too many registers to read. cmd->reg = %d", numreg);
+ return -EREMOTEIO;
+ }
+
+ ret = cx24120_message_send(state, cmd);
+ if (ret != 0)
+ return ret;
+
+ if (!numreg)
+ return 0;
+
+ /* Read numreg registers starting from register cmd->len */
+ for (i = 0; i < numreg; i++)
+ cmd->arg[i] = cx24120_readreg(state, (cmd->len + i + 1));
+
+ return 0;
+}
+
+static int cx24120_read_signal_strength(struct dvb_frontend *fe,
+ u16 *signal_strength)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (c->strength.stat[0].scale != FE_SCALE_RELATIVE)
+ *signal_strength = 0;
+ else
+ *signal_strength = c->strength.stat[0].uvalue;
+
+ return 0;
+}
+
+static int cx24120_msg_mpeg_output_global_config(struct cx24120_state *state,
+ u8 enable)
+{
+ struct cx24120_cmd cmd;
+ int ret;
+
+ cmd.id = CMD_MPEG_ONOFF;
+ cmd.len = 4;
+ cmd.arg[0] = 0x01;
+ cmd.arg[1] = 0x00;
+ cmd.arg[2] = enable ? 0 : (u8)(-1);
+ cmd.arg[3] = 0x01;
+
+ ret = cx24120_message_send(state, &cmd);
+ if (ret != 0) {
+ dev_dbg(&state->i2c->dev, "failed to %s MPEG output\n",
+ enable ? "enable" : "disable");
+ return ret;
+ }
+
+ state->mpeg_enabled = enable;
+ dev_dbg(&state->i2c->dev, "MPEG output %s\n",
+ enable ? "enabled" : "disabled");
+
+ return 0;
+}
+
+static int cx24120_msg_mpeg_output_config(struct cx24120_state *state, u8 seq)
+{
+ struct cx24120_cmd cmd;
+ struct cx24120_initial_mpeg_config i =
+ state->config->initial_mpeg_config;
+
+ cmd.id = CMD_MPEG_INIT;
+ cmd.len = 7;
+ cmd.arg[0] = seq; /* sequental number - can be 0,1,2 */
+ cmd.arg[1] = ((i.x1 & 0x01) << 1) | ((i.x1 >> 1) & 0x01);
+ cmd.arg[2] = 0x05;
+ cmd.arg[3] = 0x02;
+ cmd.arg[4] = ((i.x2 >> 1) & 0x01);
+ cmd.arg[5] = (i.x2 & 0xf0) | (i.x3 & 0x0f);
+ cmd.arg[6] = 0x10;
+
+ return cx24120_message_send(state, &cmd);
+}
+
+static int cx24120_diseqc_send_burst(struct dvb_frontend *fe,
+ enum fe_sec_mini_cmd burst)
+{
+ struct cx24120_state *state = fe->demodulator_priv;
+ struct cx24120_cmd cmd;
+
+ dev_dbg(&state->i2c->dev, "\n");
+
+ /*
+ * Yes, cmd.len is set to zero. The old driver
+ * didn't specify any len, but also had a
+ * memset 0 before every use of the cmd struct
+ * which would have set it to zero.
+ * This quite probably needs looking into.
+ */
+ cmd.id = CMD_DISEQC_BURST;
+ cmd.len = 0;
+ cmd.arg[0] = 0x00;
+ cmd.arg[1] = (burst == SEC_MINI_B) ? 0x01 : 0x00;
+
+ return cx24120_message_send(state, &cmd);
+}
+
+static int cx24120_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
+{
+ struct cx24120_state *state = fe->demodulator_priv;
+ struct cx24120_cmd cmd;
+
+ dev_dbg(&state->i2c->dev, "(%d)\n", tone);
+
+ if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
+ err("Invalid tone=%d\n", tone);
+ return -EINVAL;
+ }
+
+ cmd.id = CMD_SETTONE;
+ cmd.len = 4;
+ cmd.arg[0] = 0x00;
+ cmd.arg[1] = 0x00;
+ cmd.arg[2] = 0x00;
+ cmd.arg[3] = (tone == SEC_TONE_ON) ? 0x01 : 0x00;
+
+ return cx24120_message_send(state, &cmd);
+}
+
+static int cx24120_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage)
+{
+ struct cx24120_state *state = fe->demodulator_priv;
+ struct cx24120_cmd cmd;
+
+ dev_dbg(&state->i2c->dev, "(%d)\n", voltage);
+
+ cmd.id = CMD_SETVOLTAGE;
+ cmd.len = 2;
+ cmd.arg[0] = 0x00;
+ cmd.arg[1] = (voltage == SEC_VOLTAGE_18) ? 0x01 : 0x00;
+
+ return cx24120_message_send(state, &cmd);
+}
+
+static int cx24120_send_diseqc_msg(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *d)
+{
+ struct cx24120_state *state = fe->demodulator_priv;
+ struct cx24120_cmd cmd;
+ int back_count;
+
+ dev_dbg(&state->i2c->dev, "\n");
+
+ cmd.id = CMD_DISEQC_MSG1;
+ cmd.len = 11;
+ cmd.arg[0] = 0x00;
+ cmd.arg[1] = 0x00;
+ cmd.arg[2] = 0x03;
+ cmd.arg[3] = 0x16;
+ cmd.arg[4] = 0x28;
+ cmd.arg[5] = 0x01;
+ cmd.arg[6] = 0x01;
+ cmd.arg[7] = 0x14;
+ cmd.arg[8] = 0x19;
+ cmd.arg[9] = 0x14;
+ cmd.arg[10] = 0x1e;
+
+ if (cx24120_message_send(state, &cmd)) {
+ err("send 1st message(0x%x) failed\n", cmd.id);
+ return -EREMOTEIO;
+ }
+
+ cmd.id = CMD_DISEQC_MSG2;
+ cmd.len = d->msg_len + 6;
+ cmd.arg[0] = 0x00;
+ cmd.arg[1] = 0x01;
+ cmd.arg[2] = 0x02;
+ cmd.arg[3] = 0x00;
+ cmd.arg[4] = 0x00;
+ cmd.arg[5] = d->msg_len;
+
+ memcpy(&cmd.arg[6], &d->msg, d->msg_len);
+
+ if (cx24120_message_send(state, &cmd)) {
+ err("send 2nd message(0x%x) failed\n", cmd.id);
+ return -EREMOTEIO;
+ }
+
+ back_count = 500;
+ do {
+ if (!(cx24120_readreg(state, 0x93) & 0x01)) {
+ dev_dbg(&state->i2c->dev, "diseqc sequence sent\n");
+ return 0;
+ }
+ msleep(20);
+ back_count -= 20;
+ } while (back_count);
+
+ err("Too long waiting for diseqc.\n");
+ return -ETIMEDOUT;
+}
+
+static void cx24120_get_stats(struct cx24120_state *state)
+{
+ struct dvb_frontend *fe = &state->frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct cx24120_cmd cmd;
+ int ret, cnr, msecs;
+ u16 sig, ucb;
+ u32 ber;
+
+ dev_dbg(&state->i2c->dev, "\n");
+
+ /* signal strength */
+ if (state->fe_status & FE_HAS_SIGNAL) {
+ cmd.id = CMD_READ_SNR;
+ cmd.len = 1;
+ cmd.arg[0] = 0x00;
+
+ ret = cx24120_message_send(state, &cmd);
+ if (ret != 0) {
+ err("error reading signal strength\n");
+ return;
+ }
+
+ /* raw */
+ sig = cx24120_readreg(state, CX24120_REG_SIGSTR_H) >> 6;
+ sig = sig << 8;
+ sig |= cx24120_readreg(state, CX24120_REG_SIGSTR_L);
+ dev_dbg(&state->i2c->dev,
+ "signal strength from firmware = 0x%x\n", sig);
+
+ /* cooked */
+ sig = -100 * sig + 94324;
+
+ c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ c->strength.stat[0].uvalue = sig;
+ } else {
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* CNR */
+ if (state->fe_status & FE_HAS_VITERBI) {
+ cnr = cx24120_readreg(state, CX24120_REG_QUALITY_H) << 8;
+ cnr |= cx24120_readreg(state, CX24120_REG_QUALITY_L);
+ dev_dbg(&state->i2c->dev, "read SNR index = %d\n", cnr);
+
+ /* guessed - seems about right */
+ cnr = cnr * 100;
+
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = cnr;
+ } else {
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* BER & UCB require lock */
+ if (!(state->fe_status & FE_HAS_LOCK)) {
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return;
+ }
+
+ /* BER */
+ if (time_after(jiffies, state->ber_jiffies_stats)) {
+ msecs = (state->berw_usecs + 500) / 1000;
+ state->ber_jiffies_stats = jiffies + msecs_to_jiffies(msecs);
+
+ ber = cx24120_readreg(state, CX24120_REG_BER_HH) << 24;
+ ber |= cx24120_readreg(state, CX24120_REG_BER_HL) << 16;
+ ber |= cx24120_readreg(state, CX24120_REG_BER_LH) << 8;
+ ber |= cx24120_readreg(state, CX24120_REG_BER_LL);
+ dev_dbg(&state->i2c->dev, "read BER index = %d\n", ber);
+
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += ber;
+
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue += CX24120_BER_WSIZE;
+ }
+
+ /* UCB */
+ if (time_after(jiffies, state->per_jiffies_stats)) {
+ state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000);
+
+ ucb = cx24120_readreg(state, CX24120_REG_UCB_H) << 8;
+ ucb |= cx24120_readreg(state, CX24120_REG_UCB_L);
+ dev_dbg(&state->i2c->dev, "ucblocks = %d\n", ucb);
+
+ /* handle reset */
+ if (ucb < state->ucb_offset)
+ state->ucb_offset = c->block_error.stat[0].uvalue;
+
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue = ucb + state->ucb_offset;
+
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue += state->bitrate / 8 / 208;
+ }
+}
+
+static void cx24120_set_clock_ratios(struct dvb_frontend *fe);
+
+/* Read current tuning status */
+static int cx24120_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct cx24120_state *state = fe->demodulator_priv;
+ int lock;
+
+ lock = cx24120_readreg(state, CX24120_REG_STATUS);
+
+ dev_dbg(&state->i2c->dev, "status = 0x%02x\n", lock);
+
+ *status = 0;
+
+ if (lock & CX24120_HAS_SIGNAL)
+ *status = FE_HAS_SIGNAL;
+ if (lock & CX24120_HAS_CARRIER)
+ *status |= FE_HAS_CARRIER;
+ if (lock & CX24120_HAS_VITERBI)
+ *status |= FE_HAS_VITERBI | FE_HAS_SYNC;
+ if (lock & CX24120_HAS_LOCK)
+ *status |= FE_HAS_LOCK;
+
+ /*
+ * TODO: is FE_HAS_SYNC in the right place?
+ * Other cx241xx drivers have this slightly
+ * different
+ */
+
+ state->fe_status = *status;
+ cx24120_get_stats(state);
+
+ /* Set the clock once tuned in */
+ if (state->need_clock_set && *status & FE_HAS_LOCK) {
+ /* Set clock ratios */
+ cx24120_set_clock_ratios(fe);
+
+ /* Old driver would do a msleep(200) here */
+
+ /* Renable mpeg output */
+ if (!state->mpeg_enabled)
+ cx24120_msg_mpeg_output_global_config(state, 1);
+
+ state->need_clock_set = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * FEC & modulation lookup table
+ * Used for decoding the REG_FECMODE register
+ * once tuned in.
+ */
+struct cx24120_modfec {
+ enum fe_delivery_system delsys;
+ enum fe_modulation mod;
+ enum fe_code_rate fec;
+ u8 val;
+};
+
+static const struct cx24120_modfec modfec_lookup_table[] = {
+ /*delsys mod fec val */
+ { SYS_DVBS, QPSK, FEC_1_2, 0x01 },
+ { SYS_DVBS, QPSK, FEC_2_3, 0x02 },
+ { SYS_DVBS, QPSK, FEC_3_4, 0x03 },
+ { SYS_DVBS, QPSK, FEC_4_5, 0x04 },
+ { SYS_DVBS, QPSK, FEC_5_6, 0x05 },
+ { SYS_DVBS, QPSK, FEC_6_7, 0x06 },
+ { SYS_DVBS, QPSK, FEC_7_8, 0x07 },
+
+ { SYS_DVBS2, QPSK, FEC_1_2, 0x04 },
+ { SYS_DVBS2, QPSK, FEC_3_5, 0x05 },
+ { SYS_DVBS2, QPSK, FEC_2_3, 0x06 },
+ { SYS_DVBS2, QPSK, FEC_3_4, 0x07 },
+ { SYS_DVBS2, QPSK, FEC_4_5, 0x08 },
+ { SYS_DVBS2, QPSK, FEC_5_6, 0x09 },
+ { SYS_DVBS2, QPSK, FEC_8_9, 0x0a },
+ { SYS_DVBS2, QPSK, FEC_9_10, 0x0b },
+
+ { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c },
+ { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d },
+ { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e },
+ { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f },
+ { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 },
+ { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 },
+};
+
+/* Retrieve current fec, modulation & pilot values */
+static int cx24120_get_fec(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct cx24120_state *state = fe->demodulator_priv;
+ int idx;
+ int ret;
+ int fec;
+
+ ret = cx24120_readreg(state, CX24120_REG_FECMODE);
+ fec = ret & 0x3f; /* Lower 6 bits */
+
+ dev_dbg(&state->i2c->dev, "raw fec = %d\n", fec);
+
+ for (idx = 0; idx < ARRAY_SIZE(modfec_lookup_table); idx++) {
+ if (modfec_lookup_table[idx].delsys != state->dcur.delsys)
+ continue;
+ if (modfec_lookup_table[idx].val != fec)
+ continue;
+
+ break; /* found */
+ }
+
+ if (idx >= ARRAY_SIZE(modfec_lookup_table)) {
+ dev_dbg(&state->i2c->dev, "couldn't find fec!\n");
+ return -EINVAL;
+ }
+
+ /* save values back to cache */
+ c->modulation = modfec_lookup_table[idx].mod;
+ c->fec_inner = modfec_lookup_table[idx].fec;
+ c->pilot = (ret & 0x80) ? PILOT_ON : PILOT_OFF;
+
+ dev_dbg(&state->i2c->dev, "mod(%d), fec(%d), pilot(%d)\n",
+ c->modulation, c->fec_inner, c->pilot);
+
+ return 0;
+}
+
+/* Calculate ber window time */
+static void cx24120_calculate_ber_window(struct cx24120_state *state, u32 rate)
+{
+ struct dvb_frontend *fe = &state->frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u64 tmp;
+
+ /*
+ * Calculate bitrate from rate in the clock ratios table.
+ * This isn't *exactly* right but close enough.
+ */
+ tmp = (u64)c->symbol_rate * rate;
+ do_div(tmp, 256);
+ state->bitrate = tmp;
+
+ /* usecs per ber window */
+ tmp = 1000000ULL * CX24120_BER_WSIZE;
+ do_div(tmp, state->bitrate);
+ state->berw_usecs = tmp;
+
+ dev_dbg(&state->i2c->dev, "bitrate: %u, berw_usecs: %u\n",
+ state->bitrate, state->berw_usecs);
+}
+
+/*
+ * Clock ratios lookup table
+ *
+ * Values obtained from much larger table in old driver
+ * which had numerous entries which would never match.
+ *
+ * There's probably some way of calculating these but I
+ * can't determine the pattern
+ */
+struct cx24120_clock_ratios_table {
+ enum fe_delivery_system delsys;
+ enum fe_pilot pilot;
+ enum fe_modulation mod;
+ enum fe_code_rate fec;
+ u32 m_rat;
+ u32 n_rat;
+ u32 rate;
+};
+
+static const struct cx24120_clock_ratios_table clock_ratios_table[] = {
+ /*delsys pilot mod fec m_rat n_rat rate */
+ { SYS_DVBS2, PILOT_OFF, QPSK, FEC_1_2, 273088, 254505, 274 },
+ { SYS_DVBS2, PILOT_OFF, QPSK, FEC_3_5, 17272, 13395, 330 },
+ { SYS_DVBS2, PILOT_OFF, QPSK, FEC_2_3, 24344, 16967, 367 },
+ { SYS_DVBS2, PILOT_OFF, QPSK, FEC_3_4, 410788, 254505, 413 },
+ { SYS_DVBS2, PILOT_OFF, QPSK, FEC_4_5, 438328, 254505, 440 },
+ { SYS_DVBS2, PILOT_OFF, QPSK, FEC_5_6, 30464, 16967, 459 },
+ { SYS_DVBS2, PILOT_OFF, QPSK, FEC_8_9, 487832, 254505, 490 },
+ { SYS_DVBS2, PILOT_OFF, QPSK, FEC_9_10, 493952, 254505, 496 },
+ { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_3_5, 328168, 169905, 494 },
+ { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_2_3, 24344, 11327, 550 },
+ { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_3_4, 410788, 169905, 618 },
+ { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_5_6, 30464, 11327, 688 },
+ { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_8_9, 487832, 169905, 735 },
+ { SYS_DVBS2, PILOT_OFF, PSK_8, FEC_9_10, 493952, 169905, 744 },
+ { SYS_DVBS2, PILOT_ON, QPSK, FEC_1_2, 273088, 260709, 268 },
+ { SYS_DVBS2, PILOT_ON, QPSK, FEC_3_5, 328168, 260709, 322 },
+ { SYS_DVBS2, PILOT_ON, QPSK, FEC_2_3, 121720, 86903, 358 },
+ { SYS_DVBS2, PILOT_ON, QPSK, FEC_3_4, 410788, 260709, 403 },
+ { SYS_DVBS2, PILOT_ON, QPSK, FEC_4_5, 438328, 260709, 430 },
+ { SYS_DVBS2, PILOT_ON, QPSK, FEC_5_6, 152320, 86903, 448 },
+ { SYS_DVBS2, PILOT_ON, QPSK, FEC_8_9, 487832, 260709, 479 },
+ { SYS_DVBS2, PILOT_ON, QPSK, FEC_9_10, 493952, 260709, 485 },
+ { SYS_DVBS2, PILOT_ON, PSK_8, FEC_3_5, 328168, 173853, 483 },
+ { SYS_DVBS2, PILOT_ON, PSK_8, FEC_2_3, 121720, 57951, 537 },
+ { SYS_DVBS2, PILOT_ON, PSK_8, FEC_3_4, 410788, 173853, 604 },
+ { SYS_DVBS2, PILOT_ON, PSK_8, FEC_5_6, 152320, 57951, 672 },
+ { SYS_DVBS2, PILOT_ON, PSK_8, FEC_8_9, 487832, 173853, 718 },
+ { SYS_DVBS2, PILOT_ON, PSK_8, FEC_9_10, 493952, 173853, 727 },
+ { SYS_DVBS, PILOT_OFF, QPSK, FEC_1_2, 152592, 152592, 256 },
+ { SYS_DVBS, PILOT_OFF, QPSK, FEC_2_3, 305184, 228888, 341 },
+ { SYS_DVBS, PILOT_OFF, QPSK, FEC_3_4, 457776, 305184, 384 },
+ { SYS_DVBS, PILOT_OFF, QPSK, FEC_5_6, 762960, 457776, 427 },
+ { SYS_DVBS, PILOT_OFF, QPSK, FEC_7_8, 1068144, 610368, 448 },
+};
+
+/* Set clock ratio from lookup table */
+static void cx24120_set_clock_ratios(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct cx24120_state *state = fe->demodulator_priv;
+ struct cx24120_cmd cmd;
+ int ret, idx;
+
+ /* Find fec, modulation, pilot */
+ ret = cx24120_get_fec(fe);
+ if (ret != 0)
+ return;
+
+ /* Find the clock ratios in the lookup table */
+ for (idx = 0; idx < ARRAY_SIZE(clock_ratios_table); idx++) {
+ if (clock_ratios_table[idx].delsys != state->dcur.delsys)
+ continue;
+ if (clock_ratios_table[idx].mod != c->modulation)
+ continue;
+ if (clock_ratios_table[idx].fec != c->fec_inner)
+ continue;
+ if (clock_ratios_table[idx].pilot != c->pilot)
+ continue;
+
+ break; /* found */
+ }
+
+ if (idx >= ARRAY_SIZE(clock_ratios_table)) {
+ info("Clock ratio not found - data reception in danger\n");
+ return;
+ }
+
+ /* Read current values? */
+ cmd.id = CMD_CLOCK_READ;
+ cmd.len = 1;
+ cmd.arg[0] = 0x00;
+ ret = cx24120_message_sendrcv(state, &cmd, 6);
+ if (ret != 0)
+ return;
+ /* in cmd[0]-[5] - result */
+
+ dev_dbg(&state->i2c->dev, "m=%d, n=%d; idx: %d m=%d, n=%d, rate=%d\n",
+ cmd.arg[2] | (cmd.arg[1] << 8) | (cmd.arg[0] << 16),
+ cmd.arg[5] | (cmd.arg[4] << 8) | (cmd.arg[3] << 16),
+ idx,
+ clock_ratios_table[idx].m_rat,
+ clock_ratios_table[idx].n_rat,
+ clock_ratios_table[idx].rate);
+
+ /* Set the clock */
+ cmd.id = CMD_CLOCK_SET;
+ cmd.len = 10;
+ cmd.arg[0] = 0;
+ cmd.arg[1] = 0x10;
+ cmd.arg[2] = (clock_ratios_table[idx].m_rat >> 16) & 0xff;
+ cmd.arg[3] = (clock_ratios_table[idx].m_rat >> 8) & 0xff;
+ cmd.arg[4] = (clock_ratios_table[idx].m_rat >> 0) & 0xff;
+ cmd.arg[5] = (clock_ratios_table[idx].n_rat >> 16) & 0xff;
+ cmd.arg[6] = (clock_ratios_table[idx].n_rat >> 8) & 0xff;
+ cmd.arg[7] = (clock_ratios_table[idx].n_rat >> 0) & 0xff;
+ cmd.arg[8] = (clock_ratios_table[idx].rate >> 8) & 0xff;
+ cmd.arg[9] = (clock_ratios_table[idx].rate >> 0) & 0xff;
+
+ ret = cx24120_message_send(state, &cmd);
+ if (ret != 0)
+ return;
+
+ /* Calculate ber window rates for stat work */
+ cx24120_calculate_ber_window(state, clock_ratios_table[idx].rate);
+}
+
+/* Set inversion value */
+static int cx24120_set_inversion(struct cx24120_state *state,
+ enum fe_spectral_inversion inversion)
+{
+ dev_dbg(&state->i2c->dev, "(%d)\n", inversion);
+
+ switch (inversion) {
+ case INVERSION_OFF:
+ state->dnxt.inversion_val = 0x00;
+ break;
+ case INVERSION_ON:
+ state->dnxt.inversion_val = 0x04;
+ break;
+ case INVERSION_AUTO:
+ state->dnxt.inversion_val = 0x0c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ state->dnxt.inversion = inversion;
+
+ return 0;
+}
+
+/* FEC lookup table for tuning */
+struct cx24120_modfec_table {
+ enum fe_delivery_system delsys;
+ enum fe_modulation mod;
+ enum fe_code_rate fec;
+ u8 val;
+};
+
+static const struct cx24120_modfec_table modfec_table[] = {
+ /*delsys mod fec val */
+ { SYS_DVBS, QPSK, FEC_1_2, 0x2e },
+ { SYS_DVBS, QPSK, FEC_2_3, 0x2f },
+ { SYS_DVBS, QPSK, FEC_3_4, 0x30 },
+ { SYS_DVBS, QPSK, FEC_5_6, 0x31 },
+ { SYS_DVBS, QPSK, FEC_6_7, 0x32 },
+ { SYS_DVBS, QPSK, FEC_7_8, 0x33 },
+
+ { SYS_DVBS2, QPSK, FEC_1_2, 0x04 },
+ { SYS_DVBS2, QPSK, FEC_3_5, 0x05 },
+ { SYS_DVBS2, QPSK, FEC_2_3, 0x06 },
+ { SYS_DVBS2, QPSK, FEC_3_4, 0x07 },
+ { SYS_DVBS2, QPSK, FEC_4_5, 0x08 },
+ { SYS_DVBS2, QPSK, FEC_5_6, 0x09 },
+ { SYS_DVBS2, QPSK, FEC_8_9, 0x0a },
+ { SYS_DVBS2, QPSK, FEC_9_10, 0x0b },
+
+ { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c },
+ { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d },
+ { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e },
+ { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f },
+ { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 },
+ { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 },
+};
+
+/* Set fec_val & fec_mask values from delsys, modulation & fec */
+static int cx24120_set_fec(struct cx24120_state *state, enum fe_modulation mod,
+ enum fe_code_rate fec)
+{
+ int idx;
+
+ dev_dbg(&state->i2c->dev, "(0x%02x,0x%02x)\n", mod, fec);
+
+ state->dnxt.fec = fec;
+
+ /* Lookup fec_val from modfec table */
+ for (idx = 0; idx < ARRAY_SIZE(modfec_table); idx++) {
+ if (modfec_table[idx].delsys != state->dnxt.delsys)
+ continue;
+ if (modfec_table[idx].mod != mod)
+ continue;
+ if (modfec_table[idx].fec != fec)
+ continue;
+
+ /* found */
+ state->dnxt.fec_mask = 0x00;
+ state->dnxt.fec_val = modfec_table[idx].val;
+ return 0;
+ }
+
+ if (state->dnxt.delsys == SYS_DVBS2) {
+ /* DVBS2 auto is 0x00/0x00 */
+ state->dnxt.fec_mask = 0x00;
+ state->dnxt.fec_val = 0x00;
+ } else {
+ /* Set DVB-S to auto */
+ state->dnxt.fec_val = 0x2e;
+ state->dnxt.fec_mask = 0xac;
+ }
+
+ return 0;
+}
+
+/* Set pilot */
+static int cx24120_set_pilot(struct cx24120_state *state, enum fe_pilot pilot)
+{
+ dev_dbg(&state->i2c->dev, "(%d)\n", pilot);
+
+ /* Pilot only valid in DVBS2 */
+ if (state->dnxt.delsys != SYS_DVBS2) {
+ state->dnxt.pilot_val = CX24120_PILOT_OFF;
+ return 0;
+ }
+
+ switch (pilot) {
+ case PILOT_OFF:
+ state->dnxt.pilot_val = CX24120_PILOT_OFF;
+ break;
+ case PILOT_ON:
+ state->dnxt.pilot_val = CX24120_PILOT_ON;
+ break;
+ case PILOT_AUTO:
+ default:
+ state->dnxt.pilot_val = CX24120_PILOT_AUTO;
+ }
+
+ return 0;
+}
+
+/* Set symbol rate */
+static int cx24120_set_symbolrate(struct cx24120_state *state, u32 rate)
+{
+ dev_dbg(&state->i2c->dev, "(%d)\n", rate);
+
+ state->dnxt.symbol_rate = rate;
+
+ /* Check symbol rate */
+ if (rate > 31000000) {
+ state->dnxt.clkdiv = (-(rate < 31000001) & 3) + 2;
+ state->dnxt.ratediv = (-(rate < 31000001) & 6) + 4;
+ } else {
+ state->dnxt.clkdiv = 3;
+ state->dnxt.ratediv = 6;
+ }
+
+ return 0;
+}
+
+/* Overwrite the current tuning params, we are about to tune */
+static void cx24120_clone_params(struct dvb_frontend *fe)
+{
+ struct cx24120_state *state = fe->demodulator_priv;
+
+ state->dcur = state->dnxt;
+}
+
+static int cx24120_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct cx24120_state *state = fe->demodulator_priv;
+ struct cx24120_cmd cmd;
+ int ret;
+
+ switch (c->delivery_system) {
+ case SYS_DVBS2:
+ dev_dbg(&state->i2c->dev, "DVB-S2\n");
+ break;
+ case SYS_DVBS:
+ dev_dbg(&state->i2c->dev, "DVB-S\n");
+ break;
+ default:
+ dev_dbg(&state->i2c->dev,
+ "delivery system(%d) not supported\n",
+ c->delivery_system);
+ return -EINVAL;
+ }
+
+ state->dnxt.delsys = c->delivery_system;
+ state->dnxt.modulation = c->modulation;
+ state->dnxt.frequency = c->frequency;
+ state->dnxt.pilot = c->pilot;
+
+ ret = cx24120_set_inversion(state, c->inversion);
+ if (ret != 0)
+ return ret;
+
+ ret = cx24120_set_fec(state, c->modulation, c->fec_inner);
+ if (ret != 0)
+ return ret;
+
+ ret = cx24120_set_pilot(state, c->pilot);
+ if (ret != 0)
+ return ret;
+
+ ret = cx24120_set_symbolrate(state, c->symbol_rate);
+ if (ret != 0)
+ return ret;
+
+ /* discard the 'current' tuning parameters and prepare to tune */
+ cx24120_clone_params(fe);
+
+ dev_dbg(&state->i2c->dev,
+ "delsys = %d\n", state->dcur.delsys);
+ dev_dbg(&state->i2c->dev,
+ "modulation = %d\n", state->dcur.modulation);
+ dev_dbg(&state->i2c->dev,
+ "frequency = %d\n", state->dcur.frequency);
+ dev_dbg(&state->i2c->dev,
+ "pilot = %d (val = 0x%02x)\n",
+ state->dcur.pilot, state->dcur.pilot_val);
+ dev_dbg(&state->i2c->dev,
+ "symbol_rate = %d (clkdiv/ratediv = 0x%02x/0x%02x)\n",
+ state->dcur.symbol_rate,
+ state->dcur.clkdiv, state->dcur.ratediv);
+ dev_dbg(&state->i2c->dev,
+ "FEC = %d (mask/val = 0x%02x/0x%02x)\n",
+ state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val);
+ dev_dbg(&state->i2c->dev,
+ "Inversion = %d (val = 0x%02x)\n",
+ state->dcur.inversion, state->dcur.inversion_val);
+
+ /* Flag that clock needs to be set after tune */
+ state->need_clock_set = 1;
+
+ /* Tune in */
+ cmd.id = CMD_TUNEREQUEST;
+ cmd.len = 15;
+ cmd.arg[0] = 0;
+ cmd.arg[1] = (state->dcur.frequency & 0xff0000) >> 16;
+ cmd.arg[2] = (state->dcur.frequency & 0x00ff00) >> 8;
+ cmd.arg[3] = (state->dcur.frequency & 0x0000ff);
+ cmd.arg[4] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8;
+ cmd.arg[5] = ((state->dcur.symbol_rate / 1000) & 0x00ff);
+ cmd.arg[6] = state->dcur.inversion;
+ cmd.arg[7] = state->dcur.fec_val | state->dcur.pilot_val;
+ cmd.arg[8] = CX24120_SEARCH_RANGE_KHZ >> 8;
+ cmd.arg[9] = CX24120_SEARCH_RANGE_KHZ & 0xff;
+ cmd.arg[10] = 0; /* maybe rolloff? */
+ cmd.arg[11] = state->dcur.fec_mask;
+ cmd.arg[12] = state->dcur.ratediv;
+ cmd.arg[13] = state->dcur.clkdiv;
+ cmd.arg[14] = 0;
+
+ /* Send tune command */
+ ret = cx24120_message_send(state, &cmd);
+ if (ret != 0)
+ return ret;
+
+ /* Write symbol rate values */
+ ret = cx24120_writereg(state, CX24120_REG_CLKDIV, state->dcur.clkdiv);
+ ret = cx24120_readreg(state, CX24120_REG_RATEDIV);
+ ret &= 0xfffffff0;
+ ret |= state->dcur.ratediv;
+ ret = cx24120_writereg(state, CX24120_REG_RATEDIV, ret);
+
+ return 0;
+}
+
+/* Set vco from config */
+static int cx24120_set_vco(struct cx24120_state *state)
+{
+ struct cx24120_cmd cmd;
+ u32 nxtal_khz, vco;
+ u64 inv_vco;
+ u32 xtal_khz = state->config->xtal_khz;
+
+ nxtal_khz = xtal_khz * 4;
+ vco = nxtal_khz * 10;
+ inv_vco = DIV_ROUND_CLOSEST_ULL(0x400000000ULL, vco);
+
+ dev_dbg(&state->i2c->dev, "xtal=%d, vco=%d, inv_vco=%lld\n",
+ xtal_khz, vco, inv_vco);
+
+ cmd.id = CMD_VCO_SET;
+ cmd.len = 12;
+ cmd.arg[0] = (vco >> 16) & 0xff;
+ cmd.arg[1] = (vco >> 8) & 0xff;
+ cmd.arg[2] = vco & 0xff;
+ cmd.arg[3] = (inv_vco >> 8) & 0xff;
+ cmd.arg[4] = (inv_vco) & 0xff;
+ cmd.arg[5] = 0x03;
+ cmd.arg[6] = (nxtal_khz >> 8) & 0xff;
+ cmd.arg[7] = nxtal_khz & 0xff;
+ cmd.arg[8] = 0x06;
+ cmd.arg[9] = 0x03;
+ cmd.arg[10] = (xtal_khz >> 16) & 0xff;
+ cmd.arg[11] = xtal_khz & 0xff;
+
+ return cx24120_message_send(state, &cmd);
+}
+
+static int cx24120_init(struct dvb_frontend *fe)
+{
+ const struct firmware *fw;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct cx24120_state *state = fe->demodulator_priv;
+ struct cx24120_cmd cmd;
+ u8 reg;
+ int ret, i;
+ unsigned char vers[4];
+
+ if (state->cold_init)
+ return 0;
+
+ /* ???? */
+ cx24120_writereg(state, 0xea, 0x00);
+ cx24120_test_rom(state);
+ reg = cx24120_readreg(state, 0xfb) & 0xfe;
+ cx24120_writereg(state, 0xfb, reg);
+ reg = cx24120_readreg(state, 0xfc) & 0xfe;
+ cx24120_writereg(state, 0xfc, reg);
+ cx24120_writereg(state, 0xc3, 0x04);
+ cx24120_writereg(state, 0xc4, 0x04);
+ cx24120_writereg(state, 0xce, 0x00);
+ cx24120_writereg(state, 0xcf, 0x00);
+ reg = cx24120_readreg(state, 0xea) & 0xfe;
+ cx24120_writereg(state, 0xea, reg);
+ cx24120_writereg(state, 0xeb, 0x0c);
+ cx24120_writereg(state, 0xec, 0x06);
+ cx24120_writereg(state, 0xed, 0x05);
+ cx24120_writereg(state, 0xee, 0x03);
+ cx24120_writereg(state, 0xef, 0x05);
+ cx24120_writereg(state, 0xf3, 0x03);
+ cx24120_writereg(state, 0xf4, 0x44);
+
+ for (i = 0; i < 3; i++) {
+ cx24120_writereg(state, 0xf0 + i, 0x04);
+ cx24120_writereg(state, 0xe6 + i, 0x02);
+ }
+
+ cx24120_writereg(state, 0xea, (reg | 0x01));
+ for (i = 0; i < 6; i += 2) {
+ cx24120_writereg(state, 0xc5 + i, 0x00);
+ cx24120_writereg(state, 0xc6 + i, 0x00);
+ }
+
+ cx24120_writereg(state, 0xe4, 0x03);
+ cx24120_writereg(state, 0xeb, 0x0a);
+
+ dev_dbg(&state->i2c->dev, "requesting firmware (%s) to download...\n",
+ CX24120_FIRMWARE);
+
+ ret = state->config->request_firmware(fe, &fw, CX24120_FIRMWARE);
+ if (ret) {
+ err("Could not load firmware (%s): %d\n", CX24120_FIRMWARE,
+ ret);
+ return ret;
+ }
+
+ dev_dbg(&state->i2c->dev,
+ "Firmware found, size %d bytes (%02x %02x .. %02x %02x)\n",
+ (int)fw->size, /* firmware_size in bytes */
+ fw->data[0], /* fw 1st byte */
+ fw->data[1], /* fw 2d byte */
+ fw->data[fw->size - 2], /* fw before last byte */
+ fw->data[fw->size - 1]); /* fw last byte */
+
+ cx24120_test_rom(state);
+ reg = cx24120_readreg(state, 0xfb) & 0xfe;
+ cx24120_writereg(state, 0xfb, reg);
+ cx24120_writereg(state, 0xe0, 0x76);
+ cx24120_writereg(state, 0xf7, 0x81);
+ cx24120_writereg(state, 0xf8, 0x00);
+ cx24120_writereg(state, 0xf9, 0x00);
+ cx24120_writeregs(state, 0xfa, fw->data, (fw->size - 1), 0x00);
+ cx24120_writereg(state, 0xf7, 0xc0);
+ cx24120_writereg(state, 0xe0, 0x00);
+ reg = (fw->size - 2) & 0x00ff;
+ cx24120_writereg(state, 0xf8, reg);
+ reg = ((fw->size - 2) >> 8) & 0x00ff;
+ cx24120_writereg(state, 0xf9, reg);
+ cx24120_writereg(state, 0xf7, 0x00);
+ cx24120_writereg(state, 0xdc, 0x00);
+ cx24120_writereg(state, 0xdc, 0x07);
+ msleep(500);
+
+ /* Check final byte matches final byte of firmware */
+ reg = cx24120_readreg(state, 0xe1);
+ if (reg == fw->data[fw->size - 1]) {
+ dev_dbg(&state->i2c->dev, "Firmware uploaded successfully\n");
+ ret = 0;
+ } else {
+ err("Firmware upload failed. Last byte returned=0x%x\n", ret);
+ ret = -EREMOTEIO;
+ }
+ cx24120_writereg(state, 0xdc, 0x00);
+ release_firmware(fw);
+ if (ret != 0)
+ return ret;
+
+ /* Start tuner */
+ cmd.id = CMD_START_TUNER;
+ cmd.len = 3;
+ cmd.arg[0] = 0x00;
+ cmd.arg[1] = 0x00;
+ cmd.arg[2] = 0x00;
+
+ if (cx24120_message_send(state, &cmd) != 0) {
+ err("Error tuner start! :(\n");
+ return -EREMOTEIO;
+ }
+
+ /* Set VCO */
+ ret = cx24120_set_vco(state);
+ if (ret != 0) {
+ err("Error set VCO! :(\n");
+ return ret;
+ }
+
+ /* set bandwidth */
+ cmd.id = CMD_BANDWIDTH;
+ cmd.len = 12;
+ cmd.arg[0] = 0x00;
+ cmd.arg[1] = 0x00;
+ cmd.arg[2] = 0x00;
+ cmd.arg[3] = 0x00;
+ cmd.arg[4] = 0x05;
+ cmd.arg[5] = 0x02;
+ cmd.arg[6] = 0x02;
+ cmd.arg[7] = 0x00;
+ cmd.arg[8] = 0x05;
+ cmd.arg[9] = 0x02;
+ cmd.arg[10] = 0x02;
+ cmd.arg[11] = 0x00;
+
+ if (cx24120_message_send(state, &cmd)) {
+ err("Error set bandwidth!\n");
+ return -EREMOTEIO;
+ }
+
+ reg = cx24120_readreg(state, 0xba);
+ if (reg > 3) {
+ dev_dbg(&state->i2c->dev, "Reset-readreg 0xba: %x\n", ret);
+ err("Error initialising tuner!\n");
+ return -EREMOTEIO;
+ }
+
+ dev_dbg(&state->i2c->dev, "Tuner initialised correctly.\n");
+
+ /* Initialise mpeg outputs */
+ cx24120_writereg(state, 0xeb, 0x0a);
+ if (cx24120_msg_mpeg_output_global_config(state, 0) ||
+ cx24120_msg_mpeg_output_config(state, 0) ||
+ cx24120_msg_mpeg_output_config(state, 1) ||
+ cx24120_msg_mpeg_output_config(state, 2)) {
+ err("Error initialising mpeg output. :(\n");
+ return -EREMOTEIO;
+ }
+
+ /* Set size of BER window */
+ cmd.id = CMD_BER_CTRL;
+ cmd.len = 3;
+ cmd.arg[0] = 0x00;
+ cmd.arg[1] = CX24120_BER_WINDOW;
+ cmd.arg[2] = CX24120_BER_WINDOW;
+ if (cx24120_message_send(state, &cmd)) {
+ err("Error setting ber window\n");
+ return -EREMOTEIO;
+ }
+
+ /* Firmware CMD 35: Get firmware version */
+ cmd.id = CMD_FWVERSION;
+ cmd.len = 1;
+ for (i = 0; i < 4; i++) {
+ cmd.arg[0] = i;
+ ret = cx24120_message_send(state, &cmd);
+ if (ret != 0)
+ return ret;
+ vers[i] = cx24120_readreg(state, CX24120_REG_MAILBOX);
+ }
+ info("FW version %i.%i.%i.%i\n", vers[0], vers[1], vers[2], vers[3]);
+
+ /* init stats here in order signal app which stats are supported */
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->cnr.len = 1;
+ c->cnr.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;
+
+ state->cold_init = 1;
+
+ return 0;
+}
+
+static int cx24120_tune(struct dvb_frontend *fe, bool re_tune,
+ unsigned int mode_flags, unsigned int *delay,
+ enum fe_status *status)
+{
+ struct cx24120_state *state = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&state->i2c->dev, "(%d)\n", re_tune);
+
+ /* TODO: Do we need to set delay? */
+
+ if (re_tune) {
+ ret = cx24120_set_frontend(fe);
+ if (ret)
+ return ret;
+ }
+
+ return cx24120_read_status(fe, status);
+}
+
+static enum dvbfe_algo cx24120_get_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
+static int cx24120_sleep(struct dvb_frontend *fe)
+{
+ return 0;
+}
+
+static int cx24120_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ struct cx24120_state *state = fe->demodulator_priv;
+ u8 freq1, freq2, freq3;
+ int status;
+
+ dev_dbg(&state->i2c->dev, "\n");
+
+ /* don't return empty data if we're not tuned in */
+ status = cx24120_readreg(state, CX24120_REG_STATUS);
+ if (!(status & CX24120_HAS_LOCK))
+ return 0;
+
+ /* Get frequency */
+ freq1 = cx24120_readreg(state, CX24120_REG_FREQ1);
+ freq2 = cx24120_readreg(state, CX24120_REG_FREQ2);
+ freq3 = cx24120_readreg(state, CX24120_REG_FREQ3);
+ c->frequency = (freq3 << 16) | (freq2 << 8) | freq1;
+ dev_dbg(&state->i2c->dev, "frequency = %d\n", c->frequency);
+
+ /* Get modulation, fec, pilot */
+ cx24120_get_fec(fe);
+
+ return 0;
+}
+
+static void cx24120_release(struct dvb_frontend *fe)
+{
+ struct cx24120_state *state = fe->demodulator_priv;
+
+ dev_dbg(&state->i2c->dev, "Clear state structure\n");
+ kfree(state);
+}
+
+static int cx24120_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct cx24120_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (c->block_error.stat[0].scale != FE_SCALE_COUNTER) {
+ *ucblocks = 0;
+ return 0;
+ }
+
+ *ucblocks = c->block_error.stat[0].uvalue - state->ucb_offset;
+
+ return 0;
+}
+
+static const struct dvb_frontend_ops cx24120_ops = {
+ .delsys = { SYS_DVBS, SYS_DVBS2 },
+ .info = {
+ .name = "Conexant CX24120/CX24118",
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
+ .frequency_stepsize_hz = 1011 * kHz,
+ .frequency_tolerance_hz = 5 * MHz,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .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_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_2G_MODULATION |
+ FE_CAN_QPSK | FE_CAN_RECOVER
+ },
+ .release = cx24120_release,
+
+ .init = cx24120_init,
+ .sleep = cx24120_sleep,
+
+ .tune = cx24120_tune,
+ .get_frontend_algo = cx24120_get_algo,
+ .set_frontend = cx24120_set_frontend,
+
+ .get_frontend = cx24120_get_frontend,
+ .read_status = cx24120_read_status,
+ .read_ber = cx24120_read_ber,
+ .read_signal_strength = cx24120_read_signal_strength,
+ .read_snr = cx24120_read_snr,
+ .read_ucblocks = cx24120_read_ucblocks,
+
+ .diseqc_send_master_cmd = cx24120_send_diseqc_msg,
+
+ .diseqc_send_burst = cx24120_diseqc_send_burst,
+ .set_tone = cx24120_set_tone,
+ .set_voltage = cx24120_set_voltage,
+};
+
+MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24120/CX24118 hardware");
+MODULE_AUTHOR("Jemma Denson");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/cx24120.h b/drivers/media/dvb-frontends/cx24120.h
new file mode 100644
index 000000000000..371b1d3d55ac
--- /dev/null
+++ b/drivers/media/dvb-frontends/cx24120.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Conexant CX24120/CX24118 - DVB-S/S2 demod/tuner driver
+ *
+ * Copyright (C) 2008 Patrick Boettcher <pb@linuxtv.org>
+ * Copyright (C) 2009 Sergey Tyurin <forum.free-x.de>
+ * Updated 2012 by Jannis Achstetter <jannis_achstetter@web.de>
+ * Copyright (C) 2015 Jemma Denson <jdenson@gmail.com>
+ */
+
+#ifndef CX24120_H
+#define CX24120_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct cx24120_initial_mpeg_config {
+ u8 x1;
+ u8 x2;
+ u8 x3;
+};
+
+struct cx24120_config {
+ u8 i2c_addr;
+ u32 xtal_khz;
+ struct cx24120_initial_mpeg_config initial_mpeg_config;
+
+ int (*request_firmware)(struct dvb_frontend *fe,
+ const struct firmware **fw, char *name);
+
+ /* max bytes I2C provider can write at once */
+ u16 i2c_wr_max;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_CX24120)
+struct dvb_frontend *cx24120_attach(const struct cx24120_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline
+struct dvb_frontend *cx24120_attach(const struct cx24120_config *config,
+ struct i2c_adapter *i2c)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* CX24120_H */
diff --git a/drivers/media/dvb/frontends/cx24123.c b/drivers/media/dvb-frontends/cx24123.c
index 7e28b4ee7d4f..539889e638cc 100644
--- a/drivers/media/dvb/frontends/cx24123.c
+++ b/drivers/media/dvb-frontends/cx24123.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Conexant cx24123/cx24109 - DVB QPSK Satellite demod/tuner driver
*
@@ -6,28 +7,15 @@
* Support for KWorld DVB-S 100 by Vadim Catana <skystar@moldova.cc>
*
* Support for CX24123/CX24113-NIM by Patrick Boettcher <pb@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
+#include <asm/div64.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "cx24123.h"
#define XTAL 10111000
@@ -254,8 +242,8 @@ static int cx24123_i2c_writereg(struct cx24123_state *state,
err = i2c_transfer(state->i2c, &msg, 1);
if (err != 1) {
- printk("%s: writereg error(err == %i, reg == 0x%02x,"
- " data == 0x%02x)\n", __func__, err, reg, data);
+ printk("%s: writereg error(err == %i, reg == 0x%02x, data == 0x%02x)\n",
+ __func__, err, reg, data);
return err;
}
@@ -289,7 +277,7 @@ static int cx24123_i2c_readreg(struct cx24123_state *state, u8 i2c_addr, u8 reg)
cx24123_i2c_writereg(state, state->config->demod_address, reg, val)
static int cx24123_set_inversion(struct cx24123_state *state,
- fe_spectral_inversion_t inversion)
+ enum fe_spectral_inversion inversion)
{
u8 nom_reg = cx24123_readreg(state, 0x0e);
u8 auto_reg = cx24123_readreg(state, 0x10);
@@ -317,7 +305,7 @@ static int cx24123_set_inversion(struct cx24123_state *state,
}
static int cx24123_get_inversion(struct cx24123_state *state,
- fe_spectral_inversion_t *inversion)
+ enum fe_spectral_inversion *inversion)
{
u8 val;
@@ -334,11 +322,11 @@ static int cx24123_get_inversion(struct cx24123_state *state,
return 0;
}
-static int cx24123_set_fec(struct cx24123_state *state, fe_code_rate_t fec)
+static int cx24123_set_fec(struct cx24123_state *state, enum fe_code_rate fec)
{
u8 nom_reg = cx24123_readreg(state, 0x0e) & ~0x07;
- if ((fec < FEC_NONE) || (fec > FEC_AUTO))
+ if (((int)fec < FEC_NONE) || (fec > FEC_AUTO))
fec = FEC_AUTO;
/* Set the soft decision threshold */
@@ -396,7 +384,7 @@ static int cx24123_set_fec(struct cx24123_state *state, fe_code_rate_t fec)
return 0;
}
-static int cx24123_get_fec(struct cx24123_state *state, fe_code_rate_t *fec)
+static int cx24123_get_fec(struct cx24123_state *state, enum fe_code_rate *fec)
{
int ret;
@@ -443,7 +431,7 @@ static u32 cx24123_int_log2(u32 a, u32 b)
u32 div = a / b;
if (a % b >= b / 2)
++div;
- if (div < (1 << 31)) {
+ if (div < (1UL << 31)) {
for (exp = 1; div > exp; nearest++)
exp += exp;
}
@@ -452,7 +440,8 @@ static u32 cx24123_int_log2(u32 a, u32 b)
static int cx24123_set_symbolrate(struct cx24123_state *state, u32 srate)
{
- u32 tmp, sample_rate, ratio, sample_gain;
+ u64 tmp;
+ u32 sample_rate, ratio, sample_gain;
u8 pll_mult;
/* check if symbol rate is within limits */
@@ -482,27 +471,11 @@ static int cx24123_set_symbolrate(struct cx24123_state *state, u32 srate)
sample_rate = pll_mult * XTAL;
- /*
- SYSSymbolRate[21:0] = (srate << 23) / sample_rate
-
- We have to use 32 bit unsigned arithmetic without precision loss.
- The maximum srate is 45000000 or 0x02AEA540. This number has
- only 6 clear bits on top, hence we can shift it left only 6 bits
- at a time. Borrowed from cx24110.c
- */
-
- tmp = srate << 6;
- ratio = tmp / sample_rate;
-
- tmp = (tmp % sample_rate) << 6;
- ratio = (ratio << 6) + (tmp / sample_rate);
-
- tmp = (tmp % sample_rate) << 6;
- ratio = (ratio << 6) + (tmp / sample_rate);
-
- tmp = (tmp % sample_rate) << 5;
- ratio = (ratio << 5) + (tmp / sample_rate);
+ /* SYSSymbolRate[21:0] = (srate << 23) / sample_rate */
+ tmp = ((u64)srate) << 23;
+ do_div(tmp, sample_rate);
+ ratio = (u32) tmp;
cx24123_writereg(state, 0x01, pll_mult * 6);
@@ -667,7 +640,7 @@ static int cx24123_pll_tune(struct dvb_frontend *fe)
dprintk("frequency=%i\n", p->frequency);
if (cx24123_pll_calculate(fe) != 0) {
- err("%s: cx24123_pll_calcutate failed\n", __func__);
+ err("%s: cx24123_pll_calculate failed\n", __func__);
return -EINVAL;
}
@@ -734,7 +707,7 @@ static int cx24123_initfe(struct dvb_frontend *fe)
}
static int cx24123_set_voltage(struct dvb_frontend *fe,
- fe_sec_voltage_t voltage)
+ enum fe_sec_voltage voltage)
{
struct cx24123_state *state = fe->demodulator_priv;
u8 val;
@@ -753,7 +726,7 @@ static int cx24123_set_voltage(struct dvb_frontend *fe,
return 0;
default:
return -EINVAL;
- };
+ }
return 0;
}
@@ -809,7 +782,7 @@ static int cx24123_send_diseqc_msg(struct dvb_frontend *fe,
}
static int cx24123_diseqc_send_burst(struct dvb_frontend *fe,
- fe_sec_mini_cmd_t burst)
+ enum fe_sec_mini_cmd burst)
{
struct cx24123_state *state = fe->demodulator_priv;
int val, tone;
@@ -845,7 +818,7 @@ static int cx24123_diseqc_send_burst(struct dvb_frontend *fe,
return 0;
}
-static int cx24123_read_status(struct dvb_frontend *fe, fe_status_t *status)
+static int cx24123_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct cx24123_state *state = fe->demodulator_priv;
int sync = cx24123_readreg(state, 0x14);
@@ -959,9 +932,9 @@ static int cx24123_set_frontend(struct dvb_frontend *fe)
return 0;
}
-static int cx24123_get_frontend(struct dvb_frontend *fe)
+static int cx24123_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct cx24123_state *state = fe->demodulator_priv;
dprintk("\n");
@@ -980,7 +953,7 @@ static int cx24123_get_frontend(struct dvb_frontend *fe)
return 0;
}
-static int cx24123_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+static int cx24123_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
{
struct cx24123_state *state = fe->demodulator_priv;
u8 val;
@@ -1009,7 +982,7 @@ static int cx24123_tune(struct dvb_frontend *fe,
bool re_tune,
unsigned int mode_flags,
unsigned int *delay,
- fe_status_t *status)
+ enum fe_status *status)
{
int retval = 0;
@@ -1023,9 +996,9 @@ static int cx24123_tune(struct dvb_frontend *fe,
return retval;
}
-static int cx24123_get_algo(struct dvb_frontend *fe)
+static enum dvbfe_algo cx24123_get_algo(struct dvb_frontend *fe)
{
- return 1; /* FE_ALGO_HW */
+ return DVBFE_ALGO_HW;
}
static void cx24123_release(struct dvb_frontend *fe)
@@ -1050,7 +1023,7 @@ static u32 cx24123_tuner_i2c_func(struct i2c_adapter *adapter)
return I2C_FUNC_I2C;
}
-static struct i2c_algorithm cx24123_tuner_i2c_algo = {
+static const struct i2c_algorithm cx24123_tuner_i2c_algo = {
.master_xfer = cx24123_tuner_i2c_tuner_xfer,
.functionality = cx24123_tuner_i2c_func,
};
@@ -1063,7 +1036,7 @@ struct i2c_adapter *
}
EXPORT_SYMBOL(cx24123_get_tuner_i2c_adapter);
-static struct dvb_frontend_ops cx24123_ops;
+static const struct dvb_frontend_ops cx24123_ops;
struct dvb_frontend *cx24123_attach(const struct cx24123_config *config,
struct i2c_adapter *i2c)
@@ -1105,10 +1078,11 @@ struct dvb_frontend *cx24123_attach(const struct cx24123_config *config,
if (config->dont_use_pll)
cx24123_repeater_mode(state, 1, 0);
- strlcpy(state->tuner_i2c_adapter.name, "CX24123 tuner I2C bus",
+ strscpy(state->tuner_i2c_adapter.name, "CX24123 tuner I2C bus",
sizeof(state->tuner_i2c_adapter.name));
state->tuner_i2c_adapter.algo = &cx24123_tuner_i2c_algo;
state->tuner_i2c_adapter.algo_data = NULL;
+ state->tuner_i2c_adapter.dev.parent = i2c->dev.parent;
i2c_set_adapdata(&state->tuner_i2c_adapter, state);
if (i2c_add_adapter(&state->tuner_i2c_adapter) < 0) {
err("tuner i2c bus could not be initialized\n");
@@ -1122,16 +1096,16 @@ error:
return NULL;
}
-EXPORT_SYMBOL(cx24123_attach);
+EXPORT_SYMBOL_GPL(cx24123_attach);
-static struct dvb_frontend_ops cx24123_ops = {
+static const struct dvb_frontend_ops cx24123_ops = {
.delsys = { SYS_DVBS },
.info = {
.name = "Conexant CX24123/CX24109",
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .frequency_stepsize = 1011, /* kHz for QPSK frontends */
- .frequency_tolerance = 5000,
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
+ .frequency_stepsize_hz = 1011 * kHz,
+ .frequency_tolerance_hz = 5 * MHz,
.symbol_rate_min = 1000000,
.symbol_rate_max = 45000000,
.caps = FE_CAN_INVERSION_AUTO |
diff --git a/drivers/media/dvb-frontends/cx24123.h b/drivers/media/dvb-frontends/cx24123.h
new file mode 100644
index 000000000000..dfda44efa40c
--- /dev/null
+++ b/drivers/media/dvb-frontends/cx24123.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Conexant cx24123/cx24109 - DVB QPSK Satellite demod/tuner driver
+
+ Copyright (C) 2005 Steven Toth <stoth@linuxtv.org>
+
+*/
+
+#ifndef CX24123_H
+#define CX24123_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx24123_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* Need to set device param for start_dma */
+ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
+
+ /* 0 = LNB voltage normal, 1 = LNB voltage inverted */
+ int lnb_polarity;
+
+ /* this device has another tuner */
+ u8 dont_use_pll;
+ void (*agc_callback) (struct dvb_frontend *);
+};
+
+#if IS_REACHABLE(CONFIG_DVB_CX24123)
+extern struct dvb_frontend *cx24123_attach(const struct cx24123_config *config,
+ struct i2c_adapter *i2c);
+extern struct i2c_adapter *cx24123_get_tuner_i2c_adapter(struct dvb_frontend *);
+#else
+static inline struct dvb_frontend *cx24123_attach(
+ const struct cx24123_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+static inline struct i2c_adapter *
+ cx24123_get_tuner_i2c_adapter(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* CX24123_H */
diff --git a/drivers/media/dvb-frontends/cxd2099.c b/drivers/media/dvb-frontends/cxd2099.c
new file mode 100644
index 000000000000..5e6e18819a0d
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2099.c
@@ -0,0 +1,693 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2099.c: Driver for the Sony CXD2099AR Common Interface Controller
+ *
+ * Copyright (C) 2010-2013 Digital Devices GmbH
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include "cxd2099.h"
+
+static int buffermode;
+module_param(buffermode, int, 0444);
+MODULE_PARM_DESC(buffermode, "Enable CXD2099AR buffer mode (default: disabled)");
+
+static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount);
+
+struct cxd {
+ struct dvb_ca_en50221 en;
+
+ struct cxd2099_cfg cfg;
+ struct i2c_client *client;
+ struct regmap *regmap;
+
+ u8 regs[0x23];
+ u8 lastaddress;
+ u8 clk_reg_f;
+ u8 clk_reg_b;
+ int mode;
+ int ready;
+ int dr;
+ int write_busy;
+ int slot_stat;
+
+ u8 amem[1024];
+ int amem_read;
+
+ int cammode;
+ struct mutex lock; /* device access lock */
+
+ u8 rbuf[1028];
+ u8 wbuf[1028];
+};
+
+static int read_block(struct cxd *ci, u8 adr, u8 *data, u16 n)
+{
+ int status = 0;
+
+ if (ci->lastaddress != adr)
+ status = regmap_write(ci->regmap, 0, adr);
+ if (!status) {
+ ci->lastaddress = adr;
+
+ while (n) {
+ int len = n;
+
+ if (ci->cfg.max_i2c && len > ci->cfg.max_i2c)
+ len = ci->cfg.max_i2c;
+ status = regmap_raw_read(ci->regmap, 1, data, len);
+ if (status)
+ return status;
+ data += len;
+ n -= len;
+ }
+ }
+ return status;
+}
+
+static int read_reg(struct cxd *ci, u8 reg, u8 *val)
+{
+ return read_block(ci, reg, val, 1);
+}
+
+static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
+{
+ int status;
+ u8 addr[2] = {address & 0xff, address >> 8};
+
+ status = regmap_raw_write(ci->regmap, 2, addr, 2);
+ if (!status)
+ status = regmap_raw_read(ci->regmap, 3, data, n);
+ return status;
+}
+
+static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
+{
+ int status;
+ u8 addr[2] = {address & 0xff, address >> 8};
+
+ status = regmap_raw_write(ci->regmap, 2, addr, 2);
+ if (!status) {
+ u8 buf[256];
+
+ memcpy(buf, data, n);
+ status = regmap_raw_write(ci->regmap, 3, buf, n);
+ }
+ return status;
+}
+
+static int read_io(struct cxd *ci, u16 address, unsigned int *val)
+{
+ int status;
+ u8 addr[2] = {address & 0xff, address >> 8};
+
+ status = regmap_raw_write(ci->regmap, 2, addr, 2);
+ if (!status)
+ status = regmap_read(ci->regmap, 3, val);
+ return status;
+}
+
+static int write_io(struct cxd *ci, u16 address, u8 val)
+{
+ int status;
+ u8 addr[2] = {address & 0xff, address >> 8};
+
+ status = regmap_raw_write(ci->regmap, 2, addr, 2);
+ if (!status)
+ status = regmap_write(ci->regmap, 3, val);
+ return status;
+}
+
+static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask)
+{
+ int status = 0;
+ unsigned int regval;
+
+ if (ci->lastaddress != reg)
+ status = regmap_write(ci->regmap, 0, reg);
+ if (!status && reg >= 6 && reg <= 8 && mask != 0xff) {
+ status = regmap_read(ci->regmap, 1, &regval);
+ ci->regs[reg] = regval;
+ }
+ ci->lastaddress = reg;
+ ci->regs[reg] = (ci->regs[reg] & (~mask)) | val;
+ if (!status)
+ status = regmap_write(ci->regmap, 1, ci->regs[reg]);
+ if (reg == 0x20)
+ ci->regs[reg] &= 0x7f;
+ return status;
+}
+
+static int write_reg(struct cxd *ci, u8 reg, u8 val)
+{
+ return write_regm(ci, reg, val, 0xff);
+}
+
+static int write_block(struct cxd *ci, u8 adr, u8 *data, u16 n)
+{
+ int status = 0;
+ u8 *buf = ci->wbuf;
+
+ if (ci->lastaddress != adr)
+ status = regmap_write(ci->regmap, 0, adr);
+ if (status)
+ return status;
+
+ ci->lastaddress = adr;
+ while (n) {
+ int len = n;
+
+ if (ci->cfg.max_i2c && (len + 1 > ci->cfg.max_i2c))
+ len = ci->cfg.max_i2c - 1;
+ memcpy(buf, data, len);
+ status = regmap_raw_write(ci->regmap, 1, buf, len);
+ if (status)
+ return status;
+ n -= len;
+ data += len;
+ }
+ return status;
+}
+
+static void set_mode(struct cxd *ci, int mode)
+{
+ if (mode == ci->mode)
+ return;
+
+ switch (mode) {
+ case 0x00: /* IO mem */
+ write_regm(ci, 0x06, 0x00, 0x07);
+ break;
+ case 0x01: /* ATT mem */
+ write_regm(ci, 0x06, 0x02, 0x07);
+ break;
+ default:
+ break;
+ }
+ ci->mode = mode;
+}
+
+static void cam_mode(struct cxd *ci, int mode)
+{
+ u8 dummy;
+
+ if (mode == ci->cammode)
+ return;
+
+ switch (mode) {
+ case 0x00:
+ write_regm(ci, 0x20, 0x80, 0x80);
+ break;
+ case 0x01:
+ if (!ci->en.read_data)
+ return;
+ ci->write_busy = 0;
+ dev_info(&ci->client->dev, "enable cam buffer mode\n");
+ write_reg(ci, 0x0d, 0x00);
+ write_reg(ci, 0x0e, 0x01);
+ write_regm(ci, 0x08, 0x40, 0x40);
+ read_reg(ci, 0x12, &dummy);
+ write_regm(ci, 0x08, 0x80, 0x80);
+ break;
+ default:
+ break;
+ }
+ ci->cammode = mode;
+}
+
+static int init(struct cxd *ci)
+{
+ int status;
+
+ mutex_lock(&ci->lock);
+ ci->mode = -1;
+ do {
+ status = write_reg(ci, 0x00, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x01, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x02, 0x10);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x03, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x05, 0xFF);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x06, 0x1F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x07, 0x1F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x08, 0x28);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x14, 0x20);
+ if (status < 0)
+ break;
+
+ /* TOSTRT = 8, Mode B (gated clock), falling Edge,
+ * Serial, POL=HIGH, MSB
+ */
+ status = write_reg(ci, 0x0A, 0xA7);
+ if (status < 0)
+ break;
+
+ status = write_reg(ci, 0x0B, 0x33);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x0C, 0x33);
+ if (status < 0)
+ break;
+
+ status = write_regm(ci, 0x14, 0x00, 0x0F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x15, ci->clk_reg_b);
+ if (status < 0)
+ break;
+ status = write_regm(ci, 0x16, 0x00, 0x0F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x17, ci->clk_reg_f);
+ if (status < 0)
+ break;
+
+ if (ci->cfg.clock_mode == 2) {
+ /* bitrate*2^13/ 72000 */
+ u32 reg = ((ci->cfg.bitrate << 13) + 71999) / 72000;
+
+ if (ci->cfg.polarity) {
+ status = write_reg(ci, 0x09, 0x6f);
+ if (status < 0)
+ break;
+ } else {
+ status = write_reg(ci, 0x09, 0x6d);
+ if (status < 0)
+ break;
+ }
+ status = write_reg(ci, 0x20, 0x08);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x21, (reg >> 8) & 0xff);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x22, reg & 0xff);
+ if (status < 0)
+ break;
+ } else if (ci->cfg.clock_mode == 1) {
+ if (ci->cfg.polarity) {
+ status = write_reg(ci, 0x09, 0x6f); /* D */
+ if (status < 0)
+ break;
+ } else {
+ status = write_reg(ci, 0x09, 0x6d);
+ if (status < 0)
+ break;
+ }
+ status = write_reg(ci, 0x20, 0x68);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x21, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x22, 0x02);
+ if (status < 0)
+ break;
+ } else {
+ if (ci->cfg.polarity) {
+ status = write_reg(ci, 0x09, 0x4f); /* C */
+ if (status < 0)
+ break;
+ } else {
+ status = write_reg(ci, 0x09, 0x4d);
+ if (status < 0)
+ break;
+ }
+ status = write_reg(ci, 0x20, 0x28);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x21, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x22, 0x07);
+ if (status < 0)
+ break;
+ }
+
+ status = write_regm(ci, 0x20, 0x80, 0x80);
+ if (status < 0)
+ break;
+ status = write_regm(ci, 0x03, 0x02, 0x02);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x01, 0x04);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x00, 0x31);
+ if (status < 0)
+ break;
+
+ /* Put TS in bypass */
+ status = write_regm(ci, 0x09, 0x08, 0x08);
+ if (status < 0)
+ break;
+ ci->cammode = -1;
+ cam_mode(ci, 0);
+ } while (0);
+ mutex_unlock(&ci->lock);
+
+ return 0;
+}
+
+static int read_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot, int address)
+{
+ struct cxd *ci = ca->data;
+ u8 val;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 1);
+ read_pccard(ci, address, &val, 1);
+ mutex_unlock(&ci->lock);
+ return val;
+}
+
+static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot,
+ int address, u8 value)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 1);
+ write_pccard(ci, address, &value, 1);
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int read_cam_control(struct dvb_ca_en50221 *ca,
+ int slot, u8 address)
+{
+ struct cxd *ci = ca->data;
+ unsigned int val;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 0);
+ read_io(ci, address, &val);
+ mutex_unlock(&ci->lock);
+ return val;
+}
+
+static int write_cam_control(struct dvb_ca_en50221 *ca, int slot,
+ u8 address, u8 value)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 0);
+ write_io(ci, address, value);
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ if (ci->cammode)
+ read_data(ca, slot, ci->rbuf, 0);
+
+ mutex_lock(&ci->lock);
+ cam_mode(ci, 0);
+ write_reg(ci, 0x00, 0x21);
+ write_reg(ci, 0x06, 0x1F);
+ write_reg(ci, 0x00, 0x31);
+ write_regm(ci, 0x20, 0x80, 0x80);
+ write_reg(ci, 0x03, 0x02);
+ ci->ready = 0;
+ ci->mode = -1;
+ {
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ usleep_range(10000, 11000);
+ if (ci->ready)
+ break;
+ }
+ }
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ dev_dbg(&ci->client->dev, "%s\n", __func__);
+ if (ci->cammode)
+ read_data(ca, slot, ci->rbuf, 0);
+ mutex_lock(&ci->lock);
+ write_reg(ci, 0x00, 0x21);
+ write_reg(ci, 0x06, 0x1F);
+ msleep(300);
+
+ write_regm(ci, 0x09, 0x08, 0x08);
+ write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */
+ write_regm(ci, 0x06, 0x07, 0x07); /* Clear IO Mode */
+
+ ci->mode = -1;
+ ci->write_busy = 0;
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ write_regm(ci, 0x09, 0x00, 0x08);
+ set_mode(ci, 0);
+ cam_mode(ci, 1);
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int campoll(struct cxd *ci)
+{
+ u8 istat;
+
+ read_reg(ci, 0x04, &istat);
+ if (!istat)
+ return 0;
+ write_reg(ci, 0x05, istat);
+
+ if (istat & 0x40)
+ ci->dr = 1;
+ if (istat & 0x20)
+ ci->write_busy = 0;
+
+ if (istat & 2) {
+ u8 slotstat;
+
+ read_reg(ci, 0x01, &slotstat);
+ if (!(2 & slotstat)) {
+ if (!ci->slot_stat) {
+ ci->slot_stat |=
+ DVB_CA_EN50221_POLL_CAM_PRESENT;
+ write_regm(ci, 0x03, 0x08, 0x08);
+ }
+
+ } else {
+ if (ci->slot_stat) {
+ ci->slot_stat = 0;
+ write_regm(ci, 0x03, 0x00, 0x08);
+ dev_info(&ci->client->dev, "NO CAM\n");
+ ci->ready = 0;
+ }
+ }
+ if ((istat & 8) &&
+ ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) {
+ ci->ready = 1;
+ ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY;
+ }
+ }
+ return 0;
+}
+
+static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+ struct cxd *ci = ca->data;
+ u8 slotstat;
+
+ mutex_lock(&ci->lock);
+ campoll(ci);
+ read_reg(ci, 0x01, &slotstat);
+ mutex_unlock(&ci->lock);
+
+ return ci->slot_stat;
+}
+
+static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
+{
+ struct cxd *ci = ca->data;
+ u8 msb, lsb;
+ u16 len;
+
+ mutex_lock(&ci->lock);
+ campoll(ci);
+ mutex_unlock(&ci->lock);
+
+ if (!ci->dr)
+ return 0;
+
+ mutex_lock(&ci->lock);
+ read_reg(ci, 0x0f, &msb);
+ read_reg(ci, 0x10, &lsb);
+ len = ((u16)msb << 8) | lsb;
+ if (len > ecount || len < 2) {
+ /* read it anyway or cxd may hang */
+ read_block(ci, 0x12, ci->rbuf, len);
+ mutex_unlock(&ci->lock);
+ return -EIO;
+ }
+ read_block(ci, 0x12, ebuf, len);
+ ci->dr = 0;
+ mutex_unlock(&ci->lock);
+ return len;
+}
+
+static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
+{
+ struct cxd *ci = ca->data;
+
+ if (ci->write_busy)
+ return -EAGAIN;
+ mutex_lock(&ci->lock);
+ write_reg(ci, 0x0d, ecount >> 8);
+ write_reg(ci, 0x0e, ecount & 0xff);
+ write_block(ci, 0x11, ebuf, ecount);
+ ci->write_busy = 1;
+ mutex_unlock(&ci->lock);
+ return ecount;
+}
+
+static const struct dvb_ca_en50221 en_templ = {
+ .read_attribute_mem = read_attribute_mem,
+ .write_attribute_mem = write_attribute_mem,
+ .read_cam_control = read_cam_control,
+ .write_cam_control = write_cam_control,
+ .slot_reset = slot_reset,
+ .slot_shutdown = slot_shutdown,
+ .slot_ts_enable = slot_ts_enable,
+ .poll_slot_status = poll_slot_status,
+ .read_data = read_data,
+ .write_data = write_data,
+};
+
+static int cxd2099_probe(struct i2c_client *client)
+{
+ struct cxd *ci;
+ struct cxd2099_cfg *cfg = client->dev.platform_data;
+ static const struct regmap_config rm_cfg = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ };
+ unsigned int val;
+ int ret;
+
+ ci = kzalloc(sizeof(*ci), GFP_KERNEL);
+ if (!ci) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ci->client = client;
+ memcpy(&ci->cfg, cfg, sizeof(ci->cfg));
+
+ ci->regmap = regmap_init_i2c(client, &rm_cfg);
+ if (IS_ERR(ci->regmap)) {
+ ret = PTR_ERR(ci->regmap);
+ goto err_kfree;
+ }
+
+ ret = regmap_read(ci->regmap, 0x00, &val);
+ if (ret < 0) {
+ dev_info(&client->dev, "No CXD2099AR detected at 0x%02x\n",
+ client->addr);
+ goto err_rmexit;
+ }
+
+ mutex_init(&ci->lock);
+ ci->lastaddress = 0xff;
+ ci->clk_reg_b = 0x4a;
+ ci->clk_reg_f = 0x1b;
+
+ ci->en = en_templ;
+ ci->en.data = ci;
+ init(ci);
+ dev_info(&client->dev, "Attached CXD2099AR at 0x%02x\n", client->addr);
+
+ *cfg->en = &ci->en;
+
+ if (!buffermode) {
+ ci->en.read_data = NULL;
+ ci->en.write_data = NULL;
+ } else {
+ dev_info(&client->dev, "Using CXD2099AR buffer mode");
+ }
+
+ i2c_set_clientdata(client, ci);
+
+ return 0;
+
+err_rmexit:
+ regmap_exit(ci->regmap);
+err_kfree:
+ kfree(ci);
+err:
+
+ return ret;
+}
+
+static void cxd2099_remove(struct i2c_client *client)
+{
+ struct cxd *ci = i2c_get_clientdata(client);
+
+ regmap_exit(ci->regmap);
+ kfree(ci);
+}
+
+static const struct i2c_device_id cxd2099_id[] = {
+ { "cxd2099" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cxd2099_id);
+
+static struct i2c_driver cxd2099_driver = {
+ .driver = {
+ .name = "cxd2099",
+ },
+ .probe = cxd2099_probe,
+ .remove = cxd2099_remove,
+ .id_table = cxd2099_id,
+};
+
+module_i2c_driver(cxd2099_driver);
+
+MODULE_DESCRIPTION("Sony CXD2099AR Common Interface controller driver");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/dvb-frontends/cxd2099.h b/drivers/media/dvb-frontends/cxd2099.h
new file mode 100644
index 000000000000..5d4060007c46
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2099.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2099.h: Driver for the Sony CXD2099AR Common Interface Controller
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ */
+
+#ifndef _CXD2099_H_
+#define _CXD2099_H_
+
+#include <media/dvb_ca_en50221.h>
+
+struct cxd2099_cfg {
+ u32 bitrate;
+ u8 polarity;
+ u8 clock_mode;
+
+ u32 max_i2c;
+
+ /* ptr to DVB CA struct */
+ struct dvb_ca_en50221 **en;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2820r.h b/drivers/media/dvb-frontends/cxd2820r.h
new file mode 100644
index 000000000000..4aa6cf4fb913
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2820r.h
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Sony CXD2820R demodulator driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ */
+
+
+#ifndef CXD2820R_H
+#define CXD2820R_H
+
+#include <linux/dvb/frontend.h>
+
+#define CXD2820R_GPIO_D (0 << 0) /* disable */
+#define CXD2820R_GPIO_E (1 << 0) /* enable */
+#define CXD2820R_GPIO_O (0 << 1) /* output */
+#define CXD2820R_GPIO_I (1 << 1) /* input */
+#define CXD2820R_GPIO_L (0 << 2) /* output low */
+#define CXD2820R_GPIO_H (1 << 2) /* output high */
+
+#define CXD2820R_TS_SERIAL 0x08
+#define CXD2820R_TS_SERIAL_MSB 0x28
+#define CXD2820R_TS_PARALLEL 0x30
+#define CXD2820R_TS_PARALLEL_MSB 0x70
+
+/*
+ * I2C address: 0x6c, 0x6d
+ */
+
+/**
+ * struct cxd2820r_platform_data - Platform data for the cxd2820r driver
+ * @ts_mode: TS mode.
+ * @ts_clk_inv: TS clock inverted.
+ * @if_agc_polarity: IF AGC polarity.
+ * @spec_inv: Input spectrum inverted.
+ * @gpio_chip_base: GPIO.
+ * @get_dvb_frontend: Get DVB frontend.
+ */
+struct cxd2820r_platform_data {
+ u8 ts_mode;
+ bool ts_clk_inv;
+ bool if_agc_polarity;
+ bool spec_inv;
+ int **gpio_chip_base;
+
+ struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
+/* private: For legacy media attach wrapper. Do not set value. */
+ bool attach_in_use;
+};
+
+/**
+ * struct cxd2820r_config - configuration for cxd2020r demod
+ *
+ * @i2c_address: Demodulator I2C address. Driver determines DVB-C slave I2C
+ * address automatically from master address.
+ * Default: none, must set. Values: 0x6c, 0x6d.
+ * @ts_mode: TS output mode. Default: none, must set. Values: FIXME?
+ * @ts_clock_inv: TS clock inverted. Default: 0. Values: 0, 1.
+ * @if_agc_polarity: Default: 0. Values: 0, 1
+ * @spec_inv: Spectrum inversion. Default: 0. Values: 0, 1.
+ */
+struct cxd2820r_config {
+ /* Demodulator I2C address.
+ * Driver determines DVB-C slave I2C address automatically from master
+ * address.
+ * Default: none, must set
+ * Values: 0x6c, 0x6d
+ */
+ u8 i2c_address;
+
+ /* TS output mode.
+ * Default: none, must set.
+ * Values:
+ */
+ u8 ts_mode;
+
+ /* TS clock inverted.
+ * Default: 0
+ * Values: 0, 1
+ */
+ bool ts_clock_inv;
+
+ /* IF AGC polarity.
+ * Default: 0
+ * Values: 0, 1
+ */
+ bool if_agc_polarity;
+
+ /* Spectrum inversion.
+ * Default: 0
+ * Values: 0, 1
+ */
+ bool spec_inv;
+};
+
+
+#if IS_REACHABLE(CONFIG_DVB_CXD2820R)
+/**
+ * cxd2820r_attach - Attach a cxd2820r demod
+ *
+ * @config: pointer to &struct cxd2820r_config with demod configuration.
+ * @i2c: i2c adapter to use.
+ * @gpio_chip_base: if zero, disables GPIO setting. Otherwise, if
+ * CONFIG_GPIOLIB is set dynamically allocate
+ * gpio base; if is not set, use its value to
+ * setup the GPIO pins.
+ *
+ * return: FE pointer on success, NULL on failure.
+ */
+extern struct dvb_frontend *cxd2820r_attach(
+ const struct cxd2820r_config *config,
+ struct i2c_adapter *i2c,
+ int *gpio_chip_base
+);
+#else
+static inline struct dvb_frontend *cxd2820r_attach(
+ const struct cxd2820r_config *config,
+ struct i2c_adapter *i2c,
+ int *gpio_chip_base
+)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+#endif
+
+#endif /* CXD2820R_H */
diff --git a/drivers/media/dvb-frontends/cxd2820r_c.c b/drivers/media/dvb-frontends/cxd2820r_c.c
new file mode 100644
index 000000000000..0ba382948c51
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2820r_c.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Sony CXD2820R demodulator driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ */
+
+
+#include "cxd2820r_priv.h"
+
+int cxd2820r_set_frontend_c(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ unsigned int utmp;
+ u8 buf[2];
+ u32 if_frequency;
+ struct reg_val_mask tab[] = {
+ { 0x00080, 0x01, 0xff },
+ { 0x00081, 0x05, 0xff },
+ { 0x00085, 0x07, 0xff },
+ { 0x00088, 0x01, 0xff },
+
+ { 0x00082, 0x20, 0x60 },
+ { 0x1016a, 0x48, 0xff },
+ { 0x100a5, 0x00, 0x01 },
+ { 0x10020, 0x06, 0x07 },
+ { 0x10059, 0x50, 0xff },
+ { 0x10087, 0x0c, 0x3c },
+ { 0x1008b, 0x07, 0xff },
+ { 0x1001f, priv->if_agc_polarity << 7, 0x80 },
+ { 0x10070, priv->ts_mode, 0xff },
+ { 0x10071, !priv->ts_clk_inv << 4, 0x10 },
+ };
+
+ dev_dbg(&client->dev,
+ "delivery_system=%d modulation=%d frequency=%u symbol_rate=%u inversion=%d\n",
+ c->delivery_system, c->modulation, c->frequency,
+ c->symbol_rate, c->inversion);
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ if (priv->delivery_system != SYS_DVBC_ANNEX_A) {
+ ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab));
+ if (ret)
+ goto error;
+ }
+
+ priv->delivery_system = SYS_DVBC_ANNEX_A;
+ priv->ber_running = false; /* tune stops BER counter */
+
+ /* program IF frequency */
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+ if (ret)
+ goto error;
+ dev_dbg(&client->dev, "if_frequency=%u\n", if_frequency);
+ } else {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ utmp = 0x4000 - DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x4000, CXD2820R_CLK);
+ buf[0] = (utmp >> 8) & 0xff;
+ buf[1] = (utmp >> 0) & 0xff;
+ ret = regmap_bulk_write(priv->regmap[1], 0x0042, buf, 2);
+ if (ret)
+ goto error;
+
+ ret = regmap_write(priv->regmap[0], 0x00ff, 0x08);
+ if (ret)
+ goto error;
+
+ ret = regmap_write(priv->regmap[0], 0x00fe, 0x01);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+int cxd2820r_get_frontend_c(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ int ret;
+ unsigned int utmp;
+ u8 buf[2];
+
+ dev_dbg(&client->dev, "\n");
+
+ ret = regmap_bulk_read(priv->regmap[1], 0x001a, buf, 2);
+ if (ret)
+ goto error;
+
+ c->symbol_rate = 2500 * ((buf[0] & 0x0f) << 8 | buf[1]);
+
+ ret = regmap_read(priv->regmap[1], 0x0019, &utmp);
+ if (ret)
+ goto error;
+
+ switch ((utmp >> 0) & 0x07) {
+ case 0:
+ c->modulation = QAM_16;
+ break;
+ case 1:
+ c->modulation = QAM_32;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ case 3:
+ c->modulation = QAM_128;
+ break;
+ case 4:
+ c->modulation = QAM_256;
+ break;
+ }
+
+ switch ((utmp >> 7) & 0x01) {
+ case 0:
+ c->inversion = INVERSION_OFF;
+ break;
+ case 1:
+ c->inversion = INVERSION_ON;
+ break;
+ }
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+int cxd2820r_read_status_c(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ unsigned int utmp, utmp1, utmp2;
+ u8 buf[3];
+
+ /* Lock detection */
+ ret = regmap_bulk_read(priv->regmap[1], 0x0088, &buf[0], 1);
+ if (ret)
+ goto error;
+ ret = regmap_bulk_read(priv->regmap[1], 0x0073, &buf[1], 1);
+ if (ret)
+ goto error;
+
+ utmp1 = (buf[0] >> 0) & 0x01;
+ utmp2 = (buf[1] >> 3) & 0x01;
+
+ if (utmp1 == 1 && utmp2 == 1) {
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ } else if (utmp1 == 1 || utmp2 == 1) {
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC;
+ } else {
+ *status = 0;
+ }
+
+ dev_dbg(&client->dev, "status=%02x raw=%*ph sync=%u ts=%u\n",
+ *status, 2, buf, utmp1, utmp2);
+
+ /* Signal strength */
+ if (*status & FE_HAS_SIGNAL) {
+ unsigned int strength;
+
+ ret = regmap_bulk_read(priv->regmap[1], 0x0049, buf, 2);
+ if (ret)
+ goto error;
+
+ utmp = buf[0] << 8 | buf[1] << 0;
+ utmp = 511 - sign_extend32(utmp, 9);
+ /* Scale value to 0x0000-0xffff */
+ strength = utmp << 6 | utmp >> 4;
+
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ c->strength.stat[0].uvalue = strength;
+ } else {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* CNR */
+ if (*status & FE_HAS_VITERBI) {
+ unsigned int cnr, const_a, const_b;
+
+ ret = regmap_read(priv->regmap[1], 0x0019, &utmp);
+ if (ret)
+ goto error;
+
+ if (((utmp >> 0) & 0x03) % 2) {
+ const_a = 8750;
+ const_b = 650;
+ } else {
+ const_a = 9500;
+ const_b = 760;
+ }
+
+ ret = regmap_read(priv->regmap[1], 0x004d, &utmp);
+ if (ret)
+ goto error;
+
+ #define CXD2820R_LOG2_E_24 24204406 /* log2(e) << 24 */
+ if (utmp)
+ cnr = div_u64((u64)(intlog2(const_b) - intlog2(utmp))
+ * const_a, CXD2820R_LOG2_E_24);
+ else
+ cnr = 0;
+
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = cnr;
+ } else {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* BER */
+ if (*status & FE_HAS_SYNC) {
+ unsigned int post_bit_error;
+ bool start_ber;
+
+ if (priv->ber_running) {
+ ret = regmap_bulk_read(priv->regmap[1], 0x0076, buf, 3);
+ if (ret)
+ goto error;
+
+ if ((buf[2] >> 7) & 0x01) {
+ post_bit_error = buf[2] << 16 | buf[1] << 8 |
+ buf[0] << 0;
+ post_bit_error &= 0x0fffff;
+ start_ber = true;
+ } else {
+ post_bit_error = 0;
+ start_ber = false;
+ }
+ } else {
+ post_bit_error = 0;
+ start_ber = true;
+ }
+
+ if (start_ber) {
+ ret = regmap_write(priv->regmap[1], 0x0079, 0x01);
+ if (ret)
+ goto error;
+ priv->ber_running = true;
+ }
+
+ priv->post_bit_error += post_bit_error;
+
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = priv->post_bit_error;
+ } else {
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+int cxd2820r_init_c(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ int ret;
+
+ dev_dbg(&client->dev, "\n");
+
+ ret = regmap_write(priv->regmap[0], 0x0085, 0x07);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+int cxd2820r_sleep_c(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ int ret;
+ static const struct reg_val_mask tab[] = {
+ { 0x000ff, 0x1f, 0xff },
+ { 0x00085, 0x00, 0xff },
+ { 0x00088, 0x01, 0xff },
+ { 0x00081, 0x00, 0xff },
+ { 0x00080, 0x00, 0xff },
+ };
+
+ dev_dbg(&client->dev, "\n");
+
+ priv->delivery_system = SYS_UNDEFINED;
+
+ ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab));
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 500;
+ s->step_size = 0; /* no zigzag */
+ s->max_drift = 0;
+
+ return 0;
+}
diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c
new file mode 100644
index 000000000000..5aa3d45a691a
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2820r_core.c
@@ -0,0 +1,745 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Sony CXD2820R demodulator driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ */
+
+
+#include "cxd2820r_priv.h"
+
+/* Write register table */
+int cxd2820r_wr_reg_val_mask_tab(struct cxd2820r_priv *priv,
+ const struct reg_val_mask *tab, int tab_len)
+{
+ struct i2c_client *client = priv->client[0];
+ int ret;
+ unsigned int i, reg, mask, val;
+ struct regmap *regmap;
+
+ dev_dbg(&client->dev, "tab_len=%d\n", tab_len);
+
+ for (i = 0; i < tab_len; i++) {
+ if ((tab[i].reg >> 16) & 0x1)
+ regmap = priv->regmap[1];
+ else
+ regmap = priv->regmap[0];
+
+ reg = (tab[i].reg >> 0) & 0xffff;
+ val = tab[i].val;
+ mask = tab[i].mask;
+
+ if (mask == 0xff)
+ ret = regmap_write(regmap, reg, val);
+ else
+ ret = regmap_write_bits(regmap, reg, mask, val);
+ if (ret)
+ goto error;
+ }
+
+ return 0;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ u8 tmp0, tmp1;
+
+ dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system);
+
+ /* update GPIOs only when needed */
+ if (!memcmp(gpio, priv->gpio, sizeof(priv->gpio)))
+ return 0;
+
+ tmp0 = 0x00;
+ tmp1 = 0x00;
+ for (i = 0; i < sizeof(priv->gpio); i++) {
+ /* enable / disable */
+ if (gpio[i] & CXD2820R_GPIO_E)
+ tmp0 |= (2 << 6) >> (2 * i);
+ else
+ tmp0 |= (1 << 6) >> (2 * i);
+
+ /* input / output */
+ if (gpio[i] & CXD2820R_GPIO_I)
+ tmp1 |= (1 << (3 + i));
+ else
+ tmp1 |= (0 << (3 + i));
+
+ /* high / low */
+ if (gpio[i] & CXD2820R_GPIO_H)
+ tmp1 |= (1 << (0 + i));
+ else
+ tmp1 |= (0 << (0 + i));
+
+ dev_dbg(&client->dev, "gpio i=%d %02x %02x\n", i, tmp0, tmp1);
+ }
+
+ dev_dbg(&client->dev, "wr gpio=%02x %02x\n", tmp0, tmp1);
+
+ /* write bits [7:2] */
+ ret = regmap_update_bits(priv->regmap[0], 0x0089, 0xfc, tmp0);
+ if (ret)
+ goto error;
+
+ /* write bits [5:0] */
+ ret = regmap_update_bits(priv->regmap[0], 0x008e, 0x3f, tmp1);
+ if (ret)
+ goto error;
+
+ memcpy(priv->gpio, gpio, sizeof(priv->gpio));
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int cxd2820r_set_frontend(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+
+ dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system);
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2820r_init_t(fe);
+ if (ret < 0)
+ goto err;
+ ret = cxd2820r_set_frontend_t(fe);
+ if (ret < 0)
+ goto err;
+ break;
+ case SYS_DVBT2:
+ ret = cxd2820r_init_t(fe);
+ if (ret < 0)
+ goto err;
+ ret = cxd2820r_set_frontend_t2(fe);
+ if (ret < 0)
+ goto err;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2820r_init_c(fe);
+ if (ret < 0)
+ goto err;
+ ret = cxd2820r_set_frontend_c(fe);
+ if (ret < 0)
+ goto err;
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid delivery_system\n");
+ ret = -EINVAL;
+ break;
+ }
+err:
+ return ret;
+}
+
+static int cxd2820r_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+
+ dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system);
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2820r_read_status_t(fe, status);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2820r_read_status_t2(fe, status);
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2820r_read_status_c(fe, status);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int cxd2820r_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+
+ dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system);
+
+ if (priv->delivery_system == SYS_UNDEFINED)
+ return 0;
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2820r_get_frontend_t(fe, p);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2820r_get_frontend_t2(fe, p);
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2820r_get_frontend_c(fe, p);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int cxd2820r_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system);
+
+ *ber = (priv->post_bit_error - priv->post_bit_error_prev_dvbv3);
+ priv->post_bit_error_prev_dvbv3 = priv->post_bit_error;
+
+ return 0;
+}
+
+static int cxd2820r_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system);
+
+ if (c->strength.stat[0].scale == FE_SCALE_RELATIVE)
+ *strength = c->strength.stat[0].uvalue;
+ else
+ *strength = 0;
+
+ return 0;
+}
+
+static int cxd2820r_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system);
+
+ if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL)
+ *snr = div_s64(c->cnr.stat[0].svalue, 100);
+ else
+ *snr = 0;
+
+ return 0;
+}
+
+static int cxd2820r_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system);
+
+ *ucblocks = 0;
+
+ return 0;
+}
+
+static int cxd2820r_init(struct dvb_frontend *fe)
+{
+ return 0;
+}
+
+static int cxd2820r_sleep(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+
+ dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system);
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2820r_sleep_t(fe);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2820r_sleep_t2(fe);
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2820r_sleep_c(fe);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int cxd2820r_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+
+ dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system);
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2820r_get_tune_settings_t(fe, s);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2820r_get_tune_settings_t2(fe, s);
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2820r_get_tune_settings_c(fe, s);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ enum fe_status status = 0;
+
+ dev_dbg(&client->dev, "delivery_system=%d\n", c->delivery_system);
+
+ /* switch between DVB-T and DVB-T2 when tune fails */
+ if (priv->last_tune_failed) {
+ if (priv->delivery_system == SYS_DVBT) {
+ ret = cxd2820r_sleep_t(fe);
+ if (ret)
+ goto error;
+
+ c->delivery_system = SYS_DVBT2;
+ } else if (priv->delivery_system == SYS_DVBT2) {
+ ret = cxd2820r_sleep_t2(fe);
+ if (ret)
+ goto error;
+
+ c->delivery_system = SYS_DVBT;
+ }
+ }
+
+ /* set frontend */
+ ret = cxd2820r_set_frontend(fe);
+ if (ret)
+ goto error;
+
+ /* frontend lock wait loop count */
+ switch (priv->delivery_system) {
+ case SYS_DVBT:
+ case SYS_DVBC_ANNEX_A:
+ i = 20;
+ break;
+ case SYS_DVBT2:
+ i = 40;
+ break;
+ case SYS_UNDEFINED:
+ default:
+ i = 0;
+ break;
+ }
+
+ /* wait frontend lock */
+ for (; i > 0; i--) {
+ dev_dbg(&client->dev, "loop=%d\n", i);
+ msleep(50);
+ ret = cxd2820r_read_status(fe, &status);
+ if (ret)
+ goto error;
+
+ if (status & FE_HAS_LOCK)
+ break;
+ }
+
+ /* check if we have a valid signal */
+ if (status & FE_HAS_LOCK) {
+ priv->last_tune_failed = false;
+ return DVBFE_ALGO_SEARCH_SUCCESS;
+ } else {
+ priv->last_tune_failed = true;
+ return DVBFE_ALGO_SEARCH_AGAIN;
+ }
+
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return DVBFE_ALGO_SEARCH_ERROR;
+}
+
+static enum dvbfe_algo cxd2820r_get_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_CUSTOM;
+}
+
+static void cxd2820r_release(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+
+ dev_dbg(&client->dev, "\n");
+
+ i2c_unregister_device(client);
+
+ return;
+}
+
+static int cxd2820r_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+
+ dev_dbg_ratelimited(&client->dev, "enable=%d\n", enable);
+
+ return regmap_update_bits(priv->regmap[0], 0x00db, 0x01, enable ? 1 : 0);
+}
+
+#ifdef CONFIG_GPIOLIB
+static int cxd2820r_gpio_direction_output(struct gpio_chip *chip, unsigned nr,
+ int val)
+{
+ struct cxd2820r_priv *priv = gpiochip_get_data(chip);
+ struct i2c_client *client = priv->client[0];
+ u8 gpio[GPIO_COUNT];
+
+ dev_dbg(&client->dev, "nr=%u val=%d\n", nr, val);
+
+ memcpy(gpio, priv->gpio, sizeof(gpio));
+ gpio[nr] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | (val << 2);
+
+ return cxd2820r_gpio(&priv->fe, gpio);
+}
+
+static int cxd2820r_gpio_set(struct gpio_chip *chip, unsigned int nr, int val)
+{
+ struct cxd2820r_priv *priv = gpiochip_get_data(chip);
+ struct i2c_client *client = priv->client[0];
+ u8 gpio[GPIO_COUNT];
+
+ dev_dbg(&client->dev, "nr=%u val=%d\n", nr, val);
+
+ memcpy(gpio, priv->gpio, sizeof(gpio));
+ gpio[nr] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | (val << 2);
+
+ (void) cxd2820r_gpio(&priv->fe, gpio);
+
+ return 0;
+}
+
+static int cxd2820r_gpio_get(struct gpio_chip *chip, unsigned nr)
+{
+ struct cxd2820r_priv *priv = gpiochip_get_data(chip);
+ struct i2c_client *client = priv->client[0];
+
+ dev_dbg(&client->dev, "nr=%u\n", nr);
+
+ return (priv->gpio[nr] >> 2) & 0x01;
+}
+#endif
+
+static const struct dvb_frontend_ops cxd2820r_ops = {
+ .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A },
+ /* default: DVB-T/T2 */
+ .info = {
+ .name = "Sony CXD2820R",
+
+ .caps = FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ 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_HIERARCHY_AUTO |
+ FE_CAN_MUTE_TS |
+ FE_CAN_2G_MODULATION |
+ FE_CAN_MULTISTREAM
+ },
+
+ .release = cxd2820r_release,
+ .init = cxd2820r_init,
+ .sleep = cxd2820r_sleep,
+
+ .get_tune_settings = cxd2820r_get_tune_settings,
+ .i2c_gate_ctrl = cxd2820r_i2c_gate_ctrl,
+
+ .get_frontend = cxd2820r_get_frontend,
+
+ .get_frontend_algo = cxd2820r_get_frontend_algo,
+ .search = cxd2820r_search,
+
+ .read_status = cxd2820r_read_status,
+ .read_snr = cxd2820r_read_snr,
+ .read_ber = cxd2820r_read_ber,
+ .read_ucblocks = cxd2820r_read_ucblocks,
+ .read_signal_strength = cxd2820r_read_signal_strength,
+};
+
+/*
+ * XXX: That is wrapper to cxd2820r_probe() via driver core in order to provide
+ * proper I2C client for legacy media attach binding.
+ * New users must use I2C client binding directly!
+ */
+struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *config,
+ struct i2c_adapter *adapter,
+ int *gpio_chip_base)
+{
+ struct i2c_client *client;
+ struct i2c_board_info board_info;
+ struct cxd2820r_platform_data pdata;
+
+ pdata.ts_mode = config->ts_mode;
+ pdata.ts_clk_inv = config->ts_clock_inv;
+ pdata.if_agc_polarity = config->if_agc_polarity;
+ pdata.spec_inv = config->spec_inv;
+ pdata.gpio_chip_base = &gpio_chip_base;
+ pdata.attach_in_use = true;
+
+ memset(&board_info, 0, sizeof(board_info));
+ strscpy(board_info.type, "cxd2820r", I2C_NAME_SIZE);
+ board_info.addr = config->i2c_address;
+ board_info.platform_data = &pdata;
+ client = i2c_new_client_device(adapter, &board_info);
+ if (!i2c_client_has_driver(client))
+ return NULL;
+
+ return pdata.get_dvb_frontend(client);
+}
+EXPORT_SYMBOL_GPL(cxd2820r_attach);
+
+static struct dvb_frontend *cxd2820r_get_dvb_frontend(struct i2c_client *client)
+{
+ struct cxd2820r_priv *priv = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ return &priv->fe;
+}
+
+static int cxd2820r_probe(struct i2c_client *client)
+{
+ struct cxd2820r_platform_data *pdata = client->dev.platform_data;
+ struct cxd2820r_priv *priv;
+ int ret, *gpio_chip_base;
+ unsigned int utmp;
+ static const struct regmap_range_cfg regmap_range_cfg0[] = {
+ {
+ .range_min = 0x0000,
+ .range_max = 0x3fff,
+ .selector_reg = 0x00,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0x00,
+ .window_len = 0x100,
+ },
+ };
+ static const struct regmap_range_cfg regmap_range_cfg1[] = {
+ {
+ .range_min = 0x0000,
+ .range_max = 0x01ff,
+ .selector_reg = 0x00,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0x00,
+ .window_len = 0x100,
+ },
+ };
+ static const struct regmap_config regmap_config0 = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x3fff,
+ .ranges = regmap_range_cfg0,
+ .num_ranges = ARRAY_SIZE(regmap_range_cfg0),
+ .cache_type = REGCACHE_NONE,
+ };
+ static const struct regmap_config regmap_config1 = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x01ff,
+ .ranges = regmap_range_cfg1,
+ .num_ranges = ARRAY_SIZE(regmap_range_cfg1),
+ .cache_type = REGCACHE_NONE,
+ };
+
+ dev_dbg(&client->dev, "\n");
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ priv->client[0] = client;
+ priv->fe.demodulator_priv = priv;
+ priv->i2c = client->adapter;
+ priv->ts_mode = pdata->ts_mode;
+ priv->ts_clk_inv = pdata->ts_clk_inv;
+ priv->if_agc_polarity = pdata->if_agc_polarity;
+ priv->spec_inv = pdata->spec_inv;
+ gpio_chip_base = *pdata->gpio_chip_base;
+ priv->regmap[0] = regmap_init_i2c(priv->client[0], &regmap_config0);
+ if (IS_ERR(priv->regmap[0])) {
+ ret = PTR_ERR(priv->regmap[0]);
+ goto err_kfree;
+ }
+
+ /* Check demod answers with correct chip id */
+ ret = regmap_read(priv->regmap[0], 0x00fd, &utmp);
+ if (ret)
+ goto err_regmap_0_regmap_exit;
+
+ dev_dbg(&client->dev, "chip_id=%02x\n", utmp);
+
+ if (utmp != 0xe1) {
+ ret = -ENODEV;
+ goto err_regmap_0_regmap_exit;
+ }
+
+ /*
+ * Chip has two I2C addresses for different register banks. We register
+ * one dummy I2C client in order to get own I2C client for each
+ * register bank.
+ */
+ priv->client[1] = i2c_new_dummy_device(client->adapter, client->addr | (1 << 1));
+ if (IS_ERR(priv->client[1])) {
+ ret = PTR_ERR(priv->client[1]);
+ dev_err(&client->dev, "I2C registration failed\n");
+ goto err_regmap_0_regmap_exit;
+ }
+
+ priv->regmap[1] = regmap_init_i2c(priv->client[1], &regmap_config1);
+ if (IS_ERR(priv->regmap[1])) {
+ ret = PTR_ERR(priv->regmap[1]);
+ goto err_client_1_i2c_unregister_device;
+ }
+
+ if (gpio_chip_base) {
+#ifdef CONFIG_GPIOLIB
+ /* Add GPIOs */
+ priv->gpio_chip.label = KBUILD_MODNAME;
+ priv->gpio_chip.parent = &client->dev;
+ priv->gpio_chip.owner = THIS_MODULE;
+ priv->gpio_chip.direction_output = cxd2820r_gpio_direction_output;
+ priv->gpio_chip.set = cxd2820r_gpio_set;
+ priv->gpio_chip.get = cxd2820r_gpio_get;
+ priv->gpio_chip.base = -1; /* Dynamic allocation */
+ priv->gpio_chip.ngpio = GPIO_COUNT;
+ priv->gpio_chip.can_sleep = 1;
+ ret = gpiochip_add_data(&priv->gpio_chip, priv);
+ if (ret)
+ goto err_regmap_1_regmap_exit;
+
+ dev_dbg(&client->dev, "gpio_chip.base=%d\n",
+ priv->gpio_chip.base);
+
+ *gpio_chip_base = priv->gpio_chip.base;
+#else
+ /*
+ * Use static GPIO configuration if GPIOLIB is undefined.
+ * This is fallback condition.
+ */
+ u8 gpio[GPIO_COUNT];
+ gpio[0] = (*gpio_chip_base >> 0) & 0x07;
+ gpio[1] = (*gpio_chip_base >> 3) & 0x07;
+ gpio[2] = 0;
+ ret = cxd2820r_gpio(&priv->fe, gpio);
+ if (ret)
+ goto err_regmap_1_regmap_exit;
+#endif
+ }
+
+ /* Create dvb frontend */
+ memcpy(&priv->fe.ops, &cxd2820r_ops, sizeof(priv->fe.ops));
+ if (!pdata->attach_in_use)
+ priv->fe.ops.release = NULL;
+ i2c_set_clientdata(client, priv);
+
+ /* Setup callbacks */
+ pdata->get_dvb_frontend = cxd2820r_get_dvb_frontend;
+
+ dev_info(&client->dev, "Sony CXD2820R successfully identified\n");
+
+ return 0;
+err_regmap_1_regmap_exit:
+ regmap_exit(priv->regmap[1]);
+err_client_1_i2c_unregister_device:
+ i2c_unregister_device(priv->client[1]);
+err_regmap_0_regmap_exit:
+ regmap_exit(priv->regmap[0]);
+err_kfree:
+ kfree(priv);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static void cxd2820r_remove(struct i2c_client *client)
+{
+ struct cxd2820r_priv *priv = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+#ifdef CONFIG_GPIOLIB
+ if (priv->gpio_chip.label)
+ gpiochip_remove(&priv->gpio_chip);
+#endif
+ regmap_exit(priv->regmap[1]);
+ i2c_unregister_device(priv->client[1]);
+
+ regmap_exit(priv->regmap[0]);
+
+ kfree(priv);
+}
+
+static const struct i2c_device_id cxd2820r_id_table[] = {
+ { "cxd2820r" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cxd2820r_id_table);
+
+static struct i2c_driver cxd2820r_driver = {
+ .driver = {
+ .name = "cxd2820r",
+ .suppress_bind_attrs = true,
+ },
+ .probe = cxd2820r_probe,
+ .remove = cxd2820r_remove,
+ .id_table = cxd2820r_id_table,
+};
+
+module_i2c_driver(cxd2820r_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Sony CXD2820R demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/cxd2820r_priv.h b/drivers/media/dvb-frontends/cxd2820r_priv.h
new file mode 100644
index 000000000000..605320bbc12b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2820r_priv.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Sony CXD2820R demodulator driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ */
+
+
+#ifndef CXD2820R_PRIV_H
+#define CXD2820R_PRIV_H
+
+#include <linux/dvb/version.h>
+#include <media/dvb_frontend.h>
+#include <linux/int_log.h>
+#include "cxd2820r.h"
+#include <linux/gpio/driver.h> /* For gpio_chip */
+#include <linux/math64.h>
+#include <linux/regmap.h>
+
+struct reg_val_mask {
+ u32 reg;
+ u8 val;
+ u8 mask;
+};
+
+#define CXD2820R_CLK 41000000
+
+struct cxd2820r_priv {
+ struct i2c_client *client[2];
+ struct regmap *regmap[2];
+ struct i2c_adapter *i2c;
+ struct dvb_frontend fe;
+ u8 ts_mode;
+ bool ts_clk_inv;
+ bool if_agc_polarity;
+ bool spec_inv;
+
+ u64 post_bit_error_prev_dvbv3;
+ u64 post_bit_error;
+
+ bool ber_running;
+
+#define GPIO_COUNT 3
+ u8 gpio[GPIO_COUNT];
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio_chip;
+#endif
+
+ enum fe_delivery_system delivery_system;
+ bool last_tune_failed; /* for switch between T and T2 tune */
+};
+
+/* cxd2820r_core.c */
+
+int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio);
+
+int cxd2820r_wr_reg_val_mask_tab(struct cxd2820r_priv *priv,
+ const struct reg_val_mask *tab, int tab_len);
+
+int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val,
+ u8 mask);
+
+int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
+ int len);
+
+int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
+ int len);
+
+int cxd2820r_rd_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
+ int len);
+
+int cxd2820r_wr_reg(struct cxd2820r_priv *priv, u32 reg, u8 val);
+
+int cxd2820r_rd_reg(struct cxd2820r_priv *priv, u32 reg, u8 *val);
+
+/* cxd2820r_c.c */
+
+int cxd2820r_get_frontend_c(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p);
+
+int cxd2820r_set_frontend_c(struct dvb_frontend *fe);
+
+int cxd2820r_read_status_c(struct dvb_frontend *fe, enum fe_status *status);
+
+int cxd2820r_init_c(struct dvb_frontend *fe);
+
+int cxd2820r_sleep_c(struct dvb_frontend *fe);
+
+int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s);
+
+/* cxd2820r_t.c */
+
+int cxd2820r_get_frontend_t(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p);
+
+int cxd2820r_set_frontend_t(struct dvb_frontend *fe);
+
+int cxd2820r_read_status_t(struct dvb_frontend *fe, enum fe_status *status);
+
+int cxd2820r_init_t(struct dvb_frontend *fe);
+
+int cxd2820r_sleep_t(struct dvb_frontend *fe);
+
+int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s);
+
+/* cxd2820r_t2.c */
+
+int cxd2820r_get_frontend_t2(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p);
+
+int cxd2820r_set_frontend_t2(struct dvb_frontend *fe);
+
+int cxd2820r_read_status_t2(struct dvb_frontend *fe, enum fe_status *status);
+
+int cxd2820r_init_t2(struct dvb_frontend *fe);
+
+int cxd2820r_sleep_t2(struct dvb_frontend *fe);
+
+int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s);
+
+#endif /* CXD2820R_PRIV_H */
diff --git a/drivers/media/dvb-frontends/cxd2820r_t.c b/drivers/media/dvb-frontends/cxd2820r_t.c
new file mode 100644
index 000000000000..fbdfa6bf38dc
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2820r_t.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Sony CXD2820R demodulator driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ */
+
+
+#include "cxd2820r_priv.h"
+
+int cxd2820r_set_frontend_t(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, bw_i;
+ unsigned int utmp;
+ u32 if_frequency;
+ u8 buf[3], bw_param;
+ u8 bw_params1[][5] = {
+ { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */
+ { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */
+ { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */
+ };
+ u8 bw_params2[][2] = {
+ { 0x1f, 0xdc }, /* 6 MHz */
+ { 0x12, 0xf8 }, /* 7 MHz */
+ { 0x01, 0xe0 }, /* 8 MHz */
+ };
+ struct reg_val_mask tab[] = {
+ { 0x00080, 0x00, 0xff },
+ { 0x00081, 0x03, 0xff },
+ { 0x00085, 0x07, 0xff },
+ { 0x00088, 0x01, 0xff },
+
+ { 0x00070, priv->ts_mode, 0xff },
+ { 0x00071, !priv->ts_clk_inv << 4, 0x10 },
+ { 0x000cb, priv->if_agc_polarity << 6, 0x40 },
+ { 0x000a5, 0x00, 0x01 },
+ { 0x00082, 0x20, 0x60 },
+ { 0x000c2, 0xc3, 0xff },
+ { 0x0016a, 0x50, 0xff },
+ { 0x00427, 0x41, 0xff },
+ };
+
+ dev_dbg(&client->dev,
+ "delivery_system=%d modulation=%d frequency=%u bandwidth_hz=%u inversion=%d\n",
+ c->delivery_system, c->modulation, c->frequency,
+ c->bandwidth_hz, c->inversion);
+
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ bw_i = 0;
+ bw_param = 2;
+ break;
+ case 7000000:
+ bw_i = 1;
+ bw_param = 1;
+ break;
+ case 8000000:
+ bw_i = 2;
+ bw_param = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ if (priv->delivery_system != SYS_DVBT) {
+ ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab));
+ if (ret)
+ goto error;
+ }
+
+ priv->delivery_system = SYS_DVBT;
+ priv->ber_running = false; /* tune stops BER counter */
+
+ /* program IF frequency */
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+ if (ret)
+ goto error;
+ dev_dbg(&client->dev, "if_frequency=%u\n", if_frequency);
+ } else {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, CXD2820R_CLK);
+ buf[0] = (utmp >> 16) & 0xff;
+ buf[1] = (utmp >> 8) & 0xff;
+ buf[2] = (utmp >> 0) & 0xff;
+ ret = regmap_bulk_write(priv->regmap[0], 0x00b6, buf, 3);
+ if (ret)
+ goto error;
+
+ ret = regmap_bulk_write(priv->regmap[0], 0x009f, bw_params1[bw_i], 5);
+ if (ret)
+ goto error;
+
+ ret = regmap_update_bits(priv->regmap[0], 0x00d7, 0xc0, bw_param << 6);
+ if (ret)
+ goto error;
+
+ ret = regmap_bulk_write(priv->regmap[0], 0x00d9, bw_params2[bw_i], 2);
+ if (ret)
+ goto error;
+
+ ret = regmap_write(priv->regmap[0], 0x00ff, 0x08);
+ if (ret)
+ goto error;
+
+ ret = regmap_write(priv->regmap[0], 0x00fe, 0x01);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+int cxd2820r_get_frontend_t(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ int ret;
+ unsigned int utmp;
+ u8 buf[2];
+
+ dev_dbg(&client->dev, "\n");
+
+ ret = regmap_bulk_read(priv->regmap[0], 0x002f, buf, sizeof(buf));
+ if (ret)
+ goto error;
+
+ switch ((buf[0] >> 6) & 0x03) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = QAM_16;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ }
+
+ switch ((buf[1] >> 1) & 0x03) {
+ case 0:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ }
+
+ switch ((buf[1] >> 3) & 0x03) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[0] >> 3) & 0x07) {
+ case 0:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[0] >> 0) & 0x07) {
+ case 0:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[1] >> 5) & 0x07) {
+ case 0:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ }
+
+ ret = regmap_read(priv->regmap[0], 0x07c6, &utmp);
+ if (ret)
+ goto error;
+
+ switch ((utmp >> 0) & 0x01) {
+ case 0:
+ c->inversion = INVERSION_OFF;
+ break;
+ case 1:
+ c->inversion = INVERSION_ON;
+ break;
+ }
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+int cxd2820r_read_status_t(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ unsigned int utmp, utmp1, utmp2;
+ u8 buf[3];
+
+ /* Lock detection */
+ ret = regmap_bulk_read(priv->regmap[0], 0x0010, &buf[0], 1);
+ if (ret)
+ goto error;
+ ret = regmap_bulk_read(priv->regmap[0], 0x0073, &buf[1], 1);
+ if (ret)
+ goto error;
+
+ utmp1 = (buf[0] >> 0) & 0x07;
+ utmp2 = (buf[1] >> 3) & 0x01;
+
+ if (utmp1 == 6 && utmp2 == 1) {
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ } else if (utmp1 == 6 || utmp2 == 1) {
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC;
+ } else {
+ *status = 0;
+ }
+
+ dev_dbg(&client->dev, "status=%02x raw=%*ph sync=%u ts=%u\n",
+ *status, 2, buf, utmp1, utmp2);
+
+ /* Signal strength */
+ if (*status & FE_HAS_SIGNAL) {
+ unsigned int strength;
+
+ ret = regmap_bulk_read(priv->regmap[0], 0x0026, buf, 2);
+ if (ret)
+ goto error;
+
+ utmp = buf[0] << 8 | buf[1] << 0;
+ utmp = ~utmp & 0x0fff;
+ /* Scale value to 0x0000-0xffff */
+ strength = utmp << 4 | utmp >> 8;
+
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ c->strength.stat[0].uvalue = strength;
+ } else {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* CNR */
+ if (*status & FE_HAS_VITERBI) {
+ unsigned int cnr;
+
+ ret = regmap_bulk_read(priv->regmap[0], 0x002c, buf, 2);
+ if (ret)
+ goto error;
+
+ utmp = buf[0] << 8 | buf[1] << 0;
+ if (utmp)
+ cnr = div_u64((u64)(intlog10(utmp)
+ - intlog10(32000 - utmp) + 55532585)
+ * 10000, (1 << 24));
+ else
+ cnr = 0;
+
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = cnr;
+ } else {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* BER */
+ if (*status & FE_HAS_SYNC) {
+ unsigned int post_bit_error;
+ bool start_ber;
+
+ if (priv->ber_running) {
+ ret = regmap_bulk_read(priv->regmap[0], 0x0076, buf, 3);
+ if (ret)
+ goto error;
+
+ if ((buf[2] >> 7) & 0x01) {
+ post_bit_error = buf[2] << 16 | buf[1] << 8 |
+ buf[0] << 0;
+ post_bit_error &= 0x0fffff;
+ start_ber = true;
+ } else {
+ post_bit_error = 0;
+ start_ber = false;
+ }
+ } else {
+ post_bit_error = 0;
+ start_ber = true;
+ }
+
+ if (start_ber) {
+ ret = regmap_write(priv->regmap[0], 0x0079, 0x01);
+ if (ret)
+ goto error;
+ priv->ber_running = true;
+ }
+
+ priv->post_bit_error += post_bit_error;
+
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = priv->post_bit_error;
+ } else {
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+int cxd2820r_init_t(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ int ret;
+
+ dev_dbg(&client->dev, "\n");
+
+ ret = regmap_write(priv->regmap[0], 0x0085, 0x07);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+int cxd2820r_sleep_t(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ int ret;
+ static struct reg_val_mask tab[] = {
+ { 0x000ff, 0x1f, 0xff },
+ { 0x00085, 0x00, 0xff },
+ { 0x00088, 0x01, 0xff },
+ { 0x00081, 0x00, 0xff },
+ { 0x00080, 0x00, 0xff },
+ };
+
+ dev_dbg(&client->dev, "\n");
+
+ priv->delivery_system = SYS_UNDEFINED;
+
+ ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab));
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 500;
+ s->step_size = fe->ops.info.frequency_stepsize_hz * 2;
+ s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1;
+
+ return 0;
+}
diff --git a/drivers/media/dvb-frontends/cxd2820r_t2.c b/drivers/media/dvb-frontends/cxd2820r_t2.c
new file mode 100644
index 000000000000..34ef2bb2de34
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2820r_t2.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Sony CXD2820R demodulator driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ */
+
+
+#include "cxd2820r_priv.h"
+
+int cxd2820r_set_frontend_t2(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, bw_i;
+ unsigned int utmp;
+ u32 if_frequency;
+ u8 buf[3], bw_param;
+ u8 bw_params1[][5] = {
+ { 0x1c, 0xb3, 0x33, 0x33, 0x33 }, /* 5 MHz */
+ { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */
+ { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */
+ { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */
+ };
+ struct reg_val_mask tab[] = {
+ { 0x00080, 0x02, 0xff },
+ { 0x00081, 0x20, 0xff },
+ { 0x00085, 0x07, 0xff },
+ { 0x00088, 0x01, 0xff },
+ { 0x02069, 0x01, 0xff },
+
+ { 0x0207f, 0x2a, 0xff },
+ { 0x02082, 0x0a, 0xff },
+ { 0x02083, 0x0a, 0xff },
+ { 0x020cb, priv->if_agc_polarity << 6, 0x40 },
+ { 0x02070, priv->ts_mode, 0xff },
+ { 0x02071, !priv->ts_clk_inv << 6, 0x40 },
+ { 0x020b5, priv->spec_inv << 4, 0x10 },
+ { 0x02567, 0x07, 0x0f },
+ { 0x02569, 0x03, 0x03 },
+ { 0x02595, 0x1a, 0xff },
+ { 0x02596, 0x50, 0xff },
+ { 0x02a8c, 0x00, 0xff },
+ { 0x02a8d, 0x34, 0xff },
+ { 0x02a45, 0x06, 0x07 },
+ { 0x03f10, 0x0d, 0xff },
+ { 0x03f11, 0x02, 0xff },
+ { 0x03f12, 0x01, 0xff },
+ { 0x03f23, 0x2c, 0xff },
+ { 0x03f51, 0x13, 0xff },
+ { 0x03f52, 0x01, 0xff },
+ { 0x03f53, 0x00, 0xff },
+ { 0x027e6, 0x14, 0xff },
+ { 0x02786, 0x02, 0x07 },
+ { 0x02787, 0x40, 0xe0 },
+ { 0x027ef, 0x10, 0x18 },
+ };
+
+ dev_dbg(&client->dev,
+ "delivery_system=%d modulation=%d frequency=%u bandwidth_hz=%u inversion=%d stream_id=%u\n",
+ c->delivery_system, c->modulation, c->frequency,
+ c->bandwidth_hz, c->inversion, c->stream_id);
+
+ switch (c->bandwidth_hz) {
+ case 5000000:
+ bw_i = 0;
+ bw_param = 3;
+ break;
+ case 6000000:
+ bw_i = 1;
+ bw_param = 2;
+ break;
+ case 7000000:
+ bw_i = 2;
+ bw_param = 1;
+ break;
+ case 8000000:
+ bw_i = 3;
+ bw_param = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ if (priv->delivery_system != SYS_DVBT2) {
+ ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab));
+ if (ret)
+ goto error;
+ }
+
+ priv->delivery_system = SYS_DVBT2;
+
+ /* program IF frequency */
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+ if (ret)
+ goto error;
+ dev_dbg(&client->dev, "if_frequency=%u\n", if_frequency);
+ } else {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, CXD2820R_CLK);
+ buf[0] = (utmp >> 16) & 0xff;
+ buf[1] = (utmp >> 8) & 0xff;
+ buf[2] = (utmp >> 0) & 0xff;
+ ret = regmap_bulk_write(priv->regmap[0], 0x20b6, buf, 3);
+ if (ret)
+ goto error;
+
+ /* PLP filtering */
+ if (c->stream_id > 255) {
+ dev_dbg(&client->dev, "disable PLP filtering\n");
+ ret = regmap_write(priv->regmap[0], 0x23ad, 0x00);
+ if (ret)
+ goto error;
+ } else {
+ dev_dbg(&client->dev, "enable PLP filtering\n");
+ ret = regmap_write(priv->regmap[0], 0x23af, c->stream_id & 0xff);
+ if (ret)
+ goto error;
+ ret = regmap_write(priv->regmap[0], 0x23ad, 0x01);
+ if (ret)
+ goto error;
+ }
+
+ ret = regmap_bulk_write(priv->regmap[0], 0x209f, bw_params1[bw_i], 5);
+ if (ret)
+ goto error;
+
+ ret = regmap_update_bits(priv->regmap[0], 0x20d7, 0xc0, bw_param << 6);
+ if (ret)
+ goto error;
+
+ ret = regmap_write(priv->regmap[0], 0x00ff, 0x08);
+ if (ret)
+ goto error;
+
+ ret = regmap_write(priv->regmap[0], 0x00fe, 0x01);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+
+}
+
+int cxd2820r_get_frontend_t2(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ int ret;
+ unsigned int utmp;
+ u8 buf[2];
+
+ dev_dbg(&client->dev, "\n");
+
+ ret = regmap_bulk_read(priv->regmap[0], 0x205c, buf, 2);
+ if (ret)
+ goto error;
+
+ switch ((buf[0] >> 0) & 0x07) {
+ case 0:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ case 2:
+ c->transmission_mode = TRANSMISSION_MODE_4K;
+ break;
+ case 3:
+ c->transmission_mode = TRANSMISSION_MODE_1K;
+ break;
+ case 4:
+ c->transmission_mode = TRANSMISSION_MODE_16K;
+ break;
+ case 5:
+ c->transmission_mode = TRANSMISSION_MODE_32K;
+ break;
+ }
+
+ switch ((buf[1] >> 4) & 0x07) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ case 4:
+ c->guard_interval = GUARD_INTERVAL_1_128;
+ break;
+ case 5:
+ c->guard_interval = GUARD_INTERVAL_19_128;
+ break;
+ case 6:
+ c->guard_interval = GUARD_INTERVAL_19_256;
+ break;
+ }
+
+ ret = regmap_bulk_read(priv->regmap[0], 0x225b, buf, 2);
+ if (ret)
+ goto error;
+
+ switch ((buf[0] >> 0) & 0x07) {
+ case 0:
+ c->fec_inner = FEC_1_2;
+ break;
+ case 1:
+ c->fec_inner = FEC_3_5;
+ break;
+ case 2:
+ c->fec_inner = FEC_2_3;
+ break;
+ case 3:
+ c->fec_inner = FEC_3_4;
+ break;
+ case 4:
+ c->fec_inner = FEC_4_5;
+ break;
+ case 5:
+ c->fec_inner = FEC_5_6;
+ break;
+ }
+
+ switch ((buf[1] >> 0) & 0x07) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = QAM_16;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ case 3:
+ c->modulation = QAM_256;
+ break;
+ }
+
+ ret = regmap_read(priv->regmap[0], 0x20b5, &utmp);
+ if (ret)
+ goto error;
+
+ switch ((utmp >> 4) & 0x01) {
+ case 0:
+ c->inversion = INVERSION_OFF;
+ break;
+ case 1:
+ c->inversion = INVERSION_ON;
+ break;
+ }
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+int cxd2820r_read_status_t2(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct i2c_client *client = priv->client[0];
+ int ret;
+ unsigned int utmp, utmp1, utmp2;
+ u8 buf[4];
+
+ /* Lock detection */
+ ret = regmap_bulk_read(priv->regmap[0], 0x2010, &buf[0], 1);
+ if (ret)
+ goto error;
+
+ utmp1 = (buf[0] >> 0) & 0x07;
+ utmp2 = (buf[0] >> 5) & 0x01;
+
+ if (utmp1 == 6 && utmp2 == 1) {
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ } else if (utmp1 == 6 || utmp2 == 1) {
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC;
+ } else {
+ *status = 0;
+ }
+
+ dev_dbg(&client->dev, "status=%02x raw=%*ph sync=%u ts=%u\n",
+ *status, 1, buf, utmp1, utmp2);
+
+ /* Signal strength */
+ if (*status & FE_HAS_SIGNAL) {
+ unsigned int strength;
+
+ ret = regmap_bulk_read(priv->regmap[0], 0x2026, buf, 2);
+ if (ret)
+ goto error;
+
+ utmp = buf[0] << 8 | buf[1] << 0;
+ utmp = ~utmp & 0x0fff;
+ /* Scale value to 0x0000-0xffff */
+ strength = utmp << 4 | utmp >> 8;
+
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ c->strength.stat[0].uvalue = strength;
+ } else {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* CNR */
+ if (*status & FE_HAS_VITERBI) {
+ unsigned int cnr;
+
+ ret = regmap_bulk_read(priv->regmap[0], 0x2028, buf, 2);
+ if (ret)
+ goto error;
+
+ utmp = buf[0] << 8 | buf[1] << 0;
+ utmp = utmp & 0x0fff;
+ #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */
+ if (utmp)
+ cnr = div_u64((u64)(intlog10(utmp)
+ - CXD2820R_LOG10_8_24) * 10000,
+ (1 << 24));
+ else
+ cnr = 0;
+
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = cnr;
+ } else {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* BER */
+ if (*status & FE_HAS_SYNC) {
+ unsigned int post_bit_error;
+
+ ret = regmap_bulk_read(priv->regmap[0], 0x2039, buf, 4);
+ if (ret)
+ goto error;
+
+ if ((buf[0] >> 4) & 0x01) {
+ post_bit_error = buf[0] << 24 | buf[1] << 16 |
+ buf[2] << 8 | buf[3] << 0;
+ post_bit_error &= 0x0fffffff;
+ } else {
+ post_bit_error = 0;
+ }
+
+ priv->post_bit_error += post_bit_error;
+
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = priv->post_bit_error;
+ } else {
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+int cxd2820r_sleep_t2(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct i2c_client *client = priv->client[0];
+ int ret;
+ static const struct reg_val_mask tab[] = {
+ { 0x000ff, 0x1f, 0xff },
+ { 0x00085, 0x00, 0xff },
+ { 0x00088, 0x01, 0xff },
+ { 0x02069, 0x00, 0xff },
+ { 0x00081, 0x00, 0xff },
+ { 0x00080, 0x00, 0xff },
+ };
+
+ dev_dbg(&client->dev, "\n");
+
+ ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab));
+ if (ret)
+ goto error;
+
+ priv->delivery_system = SYS_UNDEFINED;
+
+ return ret;
+error:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 1500;
+ s->step_size = fe->ops.info.frequency_stepsize_hz * 2;
+ s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1;
+
+ return 0;
+}
diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c
new file mode 100644
index 000000000000..8fcb4417ba22
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2841er.c
@@ -0,0 +1,4006 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * cxd2841er.c
+ *
+ * Sony digital demodulator driver for
+ * CXD2841ER - DVB-S/S2/T/T2/C/C2
+ * CXD2854ER - DVB-S/S2/T/T2/C/C2, ISDB-T/S
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/math64.h>
+#include <linux/log2.h>
+#include <linux/dynamic_debug.h>
+#include <linux/kernel.h>
+
+#include <linux/int_log.h>
+#include <media/dvb_frontend.h>
+#include "cxd2841er.h"
+#include "cxd2841er_priv.h"
+
+#define MAX_WRITE_REGSIZE 16
+#define LOG2_E_100X 144
+
+#define INTLOG10X100(x) ((u32) (((u64) intlog10(x) * 100) >> 24))
+
+/* DVB-C constellation */
+enum sony_dvbc_constellation_t {
+ SONY_DVBC_CONSTELLATION_16QAM,
+ SONY_DVBC_CONSTELLATION_32QAM,
+ SONY_DVBC_CONSTELLATION_64QAM,
+ SONY_DVBC_CONSTELLATION_128QAM,
+ SONY_DVBC_CONSTELLATION_256QAM
+};
+
+enum cxd2841er_state {
+ STATE_SHUTDOWN = 0,
+ STATE_SLEEP_S,
+ STATE_ACTIVE_S,
+ STATE_SLEEP_TC,
+ STATE_ACTIVE_TC
+};
+
+struct cxd2841er_priv {
+ struct dvb_frontend frontend;
+ struct i2c_adapter *i2c;
+ u8 i2c_addr_slvx;
+ u8 i2c_addr_slvt;
+ const struct cxd2841er_config *config;
+ enum cxd2841er_state state;
+ u8 system;
+ enum cxd2841er_xtal xtal;
+ enum fe_caps caps;
+ u32 flags;
+ unsigned long stats_time;
+};
+
+static const struct cxd2841er_cnr_data s_cn_data[] = {
+ { 0x033e, 0 }, { 0x0339, 100 }, { 0x0333, 200 },
+ { 0x032e, 300 }, { 0x0329, 400 }, { 0x0324, 500 },
+ { 0x031e, 600 }, { 0x0319, 700 }, { 0x0314, 800 },
+ { 0x030f, 900 }, { 0x030a, 1000 }, { 0x02ff, 1100 },
+ { 0x02f4, 1200 }, { 0x02e9, 1300 }, { 0x02de, 1400 },
+ { 0x02d4, 1500 }, { 0x02c9, 1600 }, { 0x02bf, 1700 },
+ { 0x02b5, 1800 }, { 0x02ab, 1900 }, { 0x02a1, 2000 },
+ { 0x029b, 2100 }, { 0x0295, 2200 }, { 0x0290, 2300 },
+ { 0x028a, 2400 }, { 0x0284, 2500 }, { 0x027f, 2600 },
+ { 0x0279, 2700 }, { 0x0274, 2800 }, { 0x026e, 2900 },
+ { 0x0269, 3000 }, { 0x0262, 3100 }, { 0x025c, 3200 },
+ { 0x0255, 3300 }, { 0x024f, 3400 }, { 0x0249, 3500 },
+ { 0x0242, 3600 }, { 0x023c, 3700 }, { 0x0236, 3800 },
+ { 0x0230, 3900 }, { 0x022a, 4000 }, { 0x0223, 4100 },
+ { 0x021c, 4200 }, { 0x0215, 4300 }, { 0x020e, 4400 },
+ { 0x0207, 4500 }, { 0x0201, 4600 }, { 0x01fa, 4700 },
+ { 0x01f4, 4800 }, { 0x01ed, 4900 }, { 0x01e7, 5000 },
+ { 0x01e0, 5100 }, { 0x01d9, 5200 }, { 0x01d2, 5300 },
+ { 0x01cb, 5400 }, { 0x01c4, 5500 }, { 0x01be, 5600 },
+ { 0x01b7, 5700 }, { 0x01b1, 5800 }, { 0x01aa, 5900 },
+ { 0x01a4, 6000 }, { 0x019d, 6100 }, { 0x0196, 6200 },
+ { 0x018f, 6300 }, { 0x0189, 6400 }, { 0x0182, 6500 },
+ { 0x017c, 6600 }, { 0x0175, 6700 }, { 0x016f, 6800 },
+ { 0x0169, 6900 }, { 0x0163, 7000 }, { 0x015c, 7100 },
+ { 0x0156, 7200 }, { 0x0150, 7300 }, { 0x014a, 7400 },
+ { 0x0144, 7500 }, { 0x013e, 7600 }, { 0x0138, 7700 },
+ { 0x0132, 7800 }, { 0x012d, 7900 }, { 0x0127, 8000 },
+ { 0x0121, 8100 }, { 0x011c, 8200 }, { 0x0116, 8300 },
+ { 0x0111, 8400 }, { 0x010b, 8500 }, { 0x0106, 8600 },
+ { 0x0101, 8700 }, { 0x00fc, 8800 }, { 0x00f7, 8900 },
+ { 0x00f2, 9000 }, { 0x00ee, 9100 }, { 0x00ea, 9200 },
+ { 0x00e6, 9300 }, { 0x00e2, 9400 }, { 0x00de, 9500 },
+ { 0x00da, 9600 }, { 0x00d7, 9700 }, { 0x00d3, 9800 },
+ { 0x00d0, 9900 }, { 0x00cc, 10000 }, { 0x00c7, 10100 },
+ { 0x00c3, 10200 }, { 0x00bf, 10300 }, { 0x00ba, 10400 },
+ { 0x00b6, 10500 }, { 0x00b2, 10600 }, { 0x00ae, 10700 },
+ { 0x00aa, 10800 }, { 0x00a7, 10900 }, { 0x00a3, 11000 },
+ { 0x009f, 11100 }, { 0x009c, 11200 }, { 0x0098, 11300 },
+ { 0x0094, 11400 }, { 0x0091, 11500 }, { 0x008e, 11600 },
+ { 0x008a, 11700 }, { 0x0087, 11800 }, { 0x0084, 11900 },
+ { 0x0081, 12000 }, { 0x007e, 12100 }, { 0x007b, 12200 },
+ { 0x0079, 12300 }, { 0x0076, 12400 }, { 0x0073, 12500 },
+ { 0x0071, 12600 }, { 0x006e, 12700 }, { 0x006c, 12800 },
+ { 0x0069, 12900 }, { 0x0067, 13000 }, { 0x0065, 13100 },
+ { 0x0062, 13200 }, { 0x0060, 13300 }, { 0x005e, 13400 },
+ { 0x005c, 13500 }, { 0x005a, 13600 }, { 0x0058, 13700 },
+ { 0x0056, 13800 }, { 0x0054, 13900 }, { 0x0052, 14000 },
+ { 0x0050, 14100 }, { 0x004e, 14200 }, { 0x004c, 14300 },
+ { 0x004b, 14400 }, { 0x0049, 14500 }, { 0x0047, 14600 },
+ { 0x0046, 14700 }, { 0x0044, 14800 }, { 0x0043, 14900 },
+ { 0x0041, 15000 }, { 0x003f, 15100 }, { 0x003e, 15200 },
+ { 0x003c, 15300 }, { 0x003b, 15400 }, { 0x003a, 15500 },
+ { 0x0037, 15700 }, { 0x0036, 15800 }, { 0x0034, 15900 },
+ { 0x0033, 16000 }, { 0x0032, 16100 }, { 0x0031, 16200 },
+ { 0x0030, 16300 }, { 0x002f, 16400 }, { 0x002e, 16500 },
+ { 0x002d, 16600 }, { 0x002c, 16700 }, { 0x002b, 16800 },
+ { 0x002a, 16900 }, { 0x0029, 17000 }, { 0x0028, 17100 },
+ { 0x0027, 17200 }, { 0x0026, 17300 }, { 0x0025, 17400 },
+ { 0x0024, 17500 }, { 0x0023, 17600 }, { 0x0022, 17800 },
+ { 0x0021, 17900 }, { 0x0020, 18000 }, { 0x001f, 18200 },
+ { 0x001e, 18300 }, { 0x001d, 18500 }, { 0x001c, 18700 },
+ { 0x001b, 18900 }, { 0x001a, 19000 }, { 0x0019, 19200 },
+ { 0x0018, 19300 }, { 0x0017, 19500 }, { 0x0016, 19700 },
+ { 0x0015, 19900 }, { 0x0014, 20000 },
+};
+
+static const struct cxd2841er_cnr_data s2_cn_data[] = {
+ { 0x05af, 0 }, { 0x0597, 100 }, { 0x057e, 200 },
+ { 0x0567, 300 }, { 0x0550, 400 }, { 0x0539, 500 },
+ { 0x0522, 600 }, { 0x050c, 700 }, { 0x04f6, 800 },
+ { 0x04e1, 900 }, { 0x04cc, 1000 }, { 0x04b6, 1100 },
+ { 0x04a1, 1200 }, { 0x048c, 1300 }, { 0x0477, 1400 },
+ { 0x0463, 1500 }, { 0x044f, 1600 }, { 0x043c, 1700 },
+ { 0x0428, 1800 }, { 0x0416, 1900 }, { 0x0403, 2000 },
+ { 0x03ef, 2100 }, { 0x03dc, 2200 }, { 0x03c9, 2300 },
+ { 0x03b6, 2400 }, { 0x03a4, 2500 }, { 0x0392, 2600 },
+ { 0x0381, 2700 }, { 0x036f, 2800 }, { 0x035f, 2900 },
+ { 0x034e, 3000 }, { 0x033d, 3100 }, { 0x032d, 3200 },
+ { 0x031d, 3300 }, { 0x030d, 3400 }, { 0x02fd, 3500 },
+ { 0x02ee, 3600 }, { 0x02df, 3700 }, { 0x02d0, 3800 },
+ { 0x02c2, 3900 }, { 0x02b4, 4000 }, { 0x02a6, 4100 },
+ { 0x0299, 4200 }, { 0x028c, 4300 }, { 0x027f, 4400 },
+ { 0x0272, 4500 }, { 0x0265, 4600 }, { 0x0259, 4700 },
+ { 0x024d, 4800 }, { 0x0241, 4900 }, { 0x0236, 5000 },
+ { 0x022b, 5100 }, { 0x0220, 5200 }, { 0x0215, 5300 },
+ { 0x020a, 5400 }, { 0x0200, 5500 }, { 0x01f6, 5600 },
+ { 0x01ec, 5700 }, { 0x01e2, 5800 }, { 0x01d8, 5900 },
+ { 0x01cf, 6000 }, { 0x01c6, 6100 }, { 0x01bc, 6200 },
+ { 0x01b3, 6300 }, { 0x01aa, 6400 }, { 0x01a2, 6500 },
+ { 0x0199, 6600 }, { 0x0191, 6700 }, { 0x0189, 6800 },
+ { 0x0181, 6900 }, { 0x0179, 7000 }, { 0x0171, 7100 },
+ { 0x0169, 7200 }, { 0x0161, 7300 }, { 0x015a, 7400 },
+ { 0x0153, 7500 }, { 0x014b, 7600 }, { 0x0144, 7700 },
+ { 0x013d, 7800 }, { 0x0137, 7900 }, { 0x0130, 8000 },
+ { 0x012a, 8100 }, { 0x0124, 8200 }, { 0x011e, 8300 },
+ { 0x0118, 8400 }, { 0x0112, 8500 }, { 0x010c, 8600 },
+ { 0x0107, 8700 }, { 0x0101, 8800 }, { 0x00fc, 8900 },
+ { 0x00f7, 9000 }, { 0x00f2, 9100 }, { 0x00ec, 9200 },
+ { 0x00e7, 9300 }, { 0x00e2, 9400 }, { 0x00dd, 9500 },
+ { 0x00d8, 9600 }, { 0x00d4, 9700 }, { 0x00cf, 9800 },
+ { 0x00ca, 9900 }, { 0x00c6, 10000 }, { 0x00c2, 10100 },
+ { 0x00be, 10200 }, { 0x00b9, 10300 }, { 0x00b5, 10400 },
+ { 0x00b1, 10500 }, { 0x00ae, 10600 }, { 0x00aa, 10700 },
+ { 0x00a6, 10800 }, { 0x00a3, 10900 }, { 0x009f, 11000 },
+ { 0x009b, 11100 }, { 0x0098, 11200 }, { 0x0095, 11300 },
+ { 0x0091, 11400 }, { 0x008e, 11500 }, { 0x008b, 11600 },
+ { 0x0088, 11700 }, { 0x0085, 11800 }, { 0x0082, 11900 },
+ { 0x007f, 12000 }, { 0x007c, 12100 }, { 0x007a, 12200 },
+ { 0x0077, 12300 }, { 0x0074, 12400 }, { 0x0072, 12500 },
+ { 0x006f, 12600 }, { 0x006d, 12700 }, { 0x006b, 12800 },
+ { 0x0068, 12900 }, { 0x0066, 13000 }, { 0x0064, 13100 },
+ { 0x0061, 13200 }, { 0x005f, 13300 }, { 0x005d, 13400 },
+ { 0x005b, 13500 }, { 0x0059, 13600 }, { 0x0057, 13700 },
+ { 0x0055, 13800 }, { 0x0053, 13900 }, { 0x0051, 14000 },
+ { 0x004f, 14100 }, { 0x004e, 14200 }, { 0x004c, 14300 },
+ { 0x004a, 14400 }, { 0x0049, 14500 }, { 0x0047, 14600 },
+ { 0x0045, 14700 }, { 0x0044, 14800 }, { 0x0042, 14900 },
+ { 0x0041, 15000 }, { 0x003f, 15100 }, { 0x003e, 15200 },
+ { 0x003c, 15300 }, { 0x003b, 15400 }, { 0x003a, 15500 },
+ { 0x0038, 15600 }, { 0x0037, 15700 }, { 0x0036, 15800 },
+ { 0x0034, 15900 }, { 0x0033, 16000 }, { 0x0032, 16100 },
+ { 0x0031, 16200 }, { 0x0030, 16300 }, { 0x002f, 16400 },
+ { 0x002e, 16500 }, { 0x002d, 16600 }, { 0x002c, 16700 },
+ { 0x002b, 16800 }, { 0x002a, 16900 }, { 0x0029, 17000 },
+ { 0x0028, 17100 }, { 0x0027, 17200 }, { 0x0026, 17300 },
+ { 0x0025, 17400 }, { 0x0024, 17500 }, { 0x0023, 17600 },
+ { 0x0022, 17800 }, { 0x0021, 17900 }, { 0x0020, 18000 },
+ { 0x001f, 18200 }, { 0x001e, 18300 }, { 0x001d, 18500 },
+ { 0x001c, 18700 }, { 0x001b, 18900 }, { 0x001a, 19000 },
+ { 0x0019, 19200 }, { 0x0018, 19300 }, { 0x0017, 19500 },
+ { 0x0016, 19700 }, { 0x0015, 19900 }, { 0x0014, 20000 },
+};
+
+static int cxd2841er_freeze_regs(struct cxd2841er_priv *priv);
+static int cxd2841er_unfreeze_regs(struct cxd2841er_priv *priv);
+
+static void cxd2841er_i2c_debug(struct cxd2841er_priv *priv,
+ u8 addr, u8 reg, u8 write,
+ const u8 *data, u32 len)
+{
+ dev_dbg(&priv->i2c->dev,
+ "cxd2841er: I2C %s addr %02x reg 0x%02x size %d data %*ph\n",
+ (write == 0 ? "read" : "write"), addr, reg, len, len, data);
+}
+
+static int cxd2841er_write_regs(struct cxd2841er_priv *priv,
+ u8 addr, u8 reg, const u8 *data, u32 len)
+{
+ int ret;
+ u8 buf[MAX_WRITE_REGSIZE + 1];
+ u8 i2c_addr = (addr == I2C_SLVX ?
+ priv->i2c_addr_slvx : priv->i2c_addr_slvt);
+ struct i2c_msg msg[1] = {
+ {
+ .addr = i2c_addr,
+ .flags = 0,
+ .len = len + 1,
+ .buf = buf,
+ }
+ };
+
+ if (len + 1 >= sizeof(buf)) {
+ dev_warn(&priv->i2c->dev, "wr reg=%04x: len=%d is too big!\n",
+ reg, len + 1);
+ return -E2BIG;
+ }
+
+ cxd2841er_i2c_debug(priv, i2c_addr, reg, 1, data, len);
+ buf[0] = reg;
+ memcpy(&buf[1], data, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret >= 0 && ret != 1)
+ ret = -EIO;
+ if (ret < 0) {
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c wr failed=%d addr=%02x reg=%02x len=%d\n",
+ KBUILD_MODNAME, ret, i2c_addr, reg, len);
+ return ret;
+ }
+ return 0;
+}
+
+static int cxd2841er_write_reg(struct cxd2841er_priv *priv,
+ u8 addr, u8 reg, u8 val)
+{
+ u8 tmp = val; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */
+
+ return cxd2841er_write_regs(priv, addr, reg, &tmp, 1);
+}
+
+static int cxd2841er_read_regs(struct cxd2841er_priv *priv,
+ u8 addr, u8 reg, u8 *val, u32 len)
+{
+ int ret;
+ u8 i2c_addr = (addr == I2C_SLVX ?
+ priv->i2c_addr_slvx : priv->i2c_addr_slvt);
+ struct i2c_msg msg[2] = {
+ {
+ .addr = i2c_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = i2c_addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret >= 0 && ret != 2)
+ ret = -EIO;
+ if (ret < 0) {
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c rd failed=%d addr=%02x reg=%02x\n",
+ KBUILD_MODNAME, ret, i2c_addr, reg);
+ return ret;
+ }
+ cxd2841er_i2c_debug(priv, i2c_addr, reg, 0, val, len);
+ return 0;
+}
+
+static int cxd2841er_read_reg(struct cxd2841er_priv *priv,
+ u8 addr, u8 reg, u8 *val)
+{
+ return cxd2841er_read_regs(priv, addr, reg, val, 1);
+}
+
+static int cxd2841er_set_reg_bits(struct cxd2841er_priv *priv,
+ u8 addr, u8 reg, u8 data, u8 mask)
+{
+ int res;
+ u8 rdata;
+
+ if (mask != 0xff) {
+ res = cxd2841er_read_reg(priv, addr, reg, &rdata);
+ if (res)
+ return res;
+ data = ((data & mask) | (rdata & (mask ^ 0xFF)));
+ }
+ return cxd2841er_write_reg(priv, addr, reg, data);
+}
+
+static u32 cxd2841er_calc_iffreq_xtal(enum cxd2841er_xtal xtal, u32 ifhz)
+{
+ return div_u64(ifhz * 16777216ull,
+ (xtal == SONY_XTAL_24000) ? 48000000 : 41000000);
+}
+
+static u32 cxd2841er_calc_iffreq(u32 ifhz)
+{
+ return cxd2841er_calc_iffreq_xtal(SONY_XTAL_20500, ifhz);
+}
+
+static int cxd2841er_get_if_hz(struct cxd2841er_priv *priv, u32 def_hz)
+{
+ u32 hz;
+
+ if (priv->frontend.ops.tuner_ops.get_if_frequency
+ && (priv->flags & CXD2841ER_AUTO_IFHZ))
+ priv->frontend.ops.tuner_ops.get_if_frequency(
+ &priv->frontend, &hz);
+ else
+ hz = def_hz;
+
+ return hz;
+}
+
+static int cxd2841er_tuner_set(struct dvb_frontend *fe)
+{
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ if ((priv->flags & CXD2841ER_USE_GATECTRL) && fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+ if ((priv->flags & CXD2841ER_USE_GATECTRL) && fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+}
+
+static int cxd2841er_dvbs2_set_symbol_rate(struct cxd2841er_priv *priv,
+ u32 symbol_rate)
+{
+ u32 reg_value = 0;
+ u8 data[3] = {0, 0, 0};
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ /*
+ * regValue = (symbolRateKSps * 2^14 / 1000) + 0.5
+ * = ((symbolRateKSps * 2^14) + 500) / 1000
+ * = ((symbolRateKSps * 16384) + 500) / 1000
+ */
+ reg_value = DIV_ROUND_CLOSEST(symbol_rate * 16384, 1000);
+ if ((reg_value == 0) || (reg_value > 0xFFFFF)) {
+ dev_err(&priv->i2c->dev,
+ "%s(): reg_value is out of range\n", __func__);
+ return -EINVAL;
+ }
+ data[0] = (u8)((reg_value >> 16) & 0x0F);
+ data[1] = (u8)((reg_value >> 8) & 0xFF);
+ data[2] = (u8)(reg_value & 0xFF);
+ /* Set SLV-T Bank : 0xAE */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x20, data, 3);
+ return 0;
+}
+
+static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv,
+ u8 system);
+
+static int cxd2841er_sleep_s_to_active_s(struct cxd2841er_priv *priv,
+ u8 system, u32 symbol_rate)
+{
+ int ret;
+ u8 data[4] = { 0, 0, 0, 0 };
+
+ if (priv->state != STATE_SLEEP_S) {
+ dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, (int)priv->state);
+ return -EINVAL;
+ }
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ cxd2841er_set_ts_clock_mode(priv, SYS_DVBS);
+ /* Set demod mode */
+ if (system == SYS_DVBS) {
+ data[0] = 0x0A;
+ } else if (system == SYS_DVBS2) {
+ data[0] = 0x0B;
+ } else {
+ dev_err(&priv->i2c->dev, "%s(): invalid delsys %d\n",
+ __func__, system);
+ return -EINVAL;
+ }
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x17, data[0]);
+ /* DVB-S/S2 */
+ data[0] = 0x00;
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Enable S/S2 auto detection 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2d, data[0]);
+ /* Set SLV-T Bank : 0xAE */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae);
+ /* Enable S/S2 auto detection 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x30, data[0]);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Enable demod clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
+ /* Enable ADC clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x31, 0x01);
+ /* Enable ADC 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16);
+ /* Enable ADC 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x3f);
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* Enable ADC 3 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
+ /* Set SLV-T Bank : 0xA3 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa3);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xac, 0x00);
+ data[0] = 0x07;
+ data[1] = 0x3B;
+ data[2] = 0x08;
+ data[3] = 0xC5;
+ /* Set SLV-T Bank : 0xAB */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xab);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x98, data, 4);
+ data[0] = 0x05;
+ data[1] = 0x80;
+ data[2] = 0x0A;
+ data[3] = 0x80;
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xa8, data, 4);
+ data[0] = 0x0C;
+ data[1] = 0xCC;
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xc3, data, 2);
+ /* Set demod parameter */
+ ret = cxd2841er_dvbs2_set_symbol_rate(priv, symbol_rate);
+ if (ret != 0)
+ return ret;
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* disable Hi-Z setting 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x10);
+ /* disable Hi-Z setting 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00);
+ priv->state = STATE_ACTIVE_S;
+ return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_t_band(struct cxd2841er_priv *priv,
+ u32 bandwidth);
+
+static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv,
+ u32 bandwidth);
+
+static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv,
+ u32 bandwidth);
+
+static int cxd2841er_sleep_tc_to_active_i(struct cxd2841er_priv *priv,
+ u32 bandwidth);
+
+static int cxd2841er_active_i_to_sleep_tc(struct cxd2841er_priv *priv);
+
+static int cxd2841er_sleep_tc_to_shutdown(struct cxd2841er_priv *priv);
+
+static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv);
+
+static int cxd2841er_sleep_tc(struct dvb_frontend *fe);
+
+static int cxd2841er_retune_active(struct cxd2841er_priv *priv,
+ struct dtv_frontend_properties *p)
+{
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_S &&
+ priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* disable TS output */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
+ if (priv->state == STATE_ACTIVE_S)
+ return cxd2841er_dvbs2_set_symbol_rate(
+ priv, p->symbol_rate / 1000);
+ else if (priv->state == STATE_ACTIVE_TC) {
+ switch (priv->system) {
+ case SYS_DVBT:
+ return cxd2841er_sleep_tc_to_active_t_band(
+ priv, p->bandwidth_hz);
+ case SYS_DVBT2:
+ return cxd2841er_sleep_tc_to_active_t2_band(
+ priv, p->bandwidth_hz);
+ case SYS_DVBC_ANNEX_A:
+ return cxd2841er_sleep_tc_to_active_c_band(
+ priv, p->bandwidth_hz);
+ case SYS_ISDBT:
+ cxd2841er_active_i_to_sleep_tc(priv);
+ cxd2841er_sleep_tc_to_shutdown(priv);
+ cxd2841er_shutdown_to_sleep_tc(priv);
+ return cxd2841er_sleep_tc_to_active_i(
+ priv, p->bandwidth_hz);
+ }
+ }
+ dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n",
+ __func__, priv->system);
+ return -EINVAL;
+}
+
+static int cxd2841er_active_s_to_sleep_s(struct cxd2841er_priv *priv)
+{
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_S) {
+ dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* disable TS output */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
+ /* enable Hi-Z setting 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x1f);
+ /* enable Hi-Z setting 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff);
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* disable ADC 1 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* disable ADC clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x31, 0x00);
+ /* disable ADC 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16);
+ /* disable ADC 3 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27);
+ /* SADC Bias ON */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06);
+ /* disable demod clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00);
+ /* Set SLV-T Bank : 0xAE */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xae);
+ /* disable S/S2 auto detection1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* disable S/S2 auto detection2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2d, 0x00);
+ priv->state = STATE_SLEEP_S;
+ return 0;
+}
+
+static int cxd2841er_sleep_s_to_shutdown(struct cxd2841er_priv *priv)
+{
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_SLEEP_S) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Disable DSQOUT */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f);
+ /* Disable DSQIN */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x9c, 0x00);
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* Disable oscillator */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x15, 0x01);
+ /* Set demod mode */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01);
+ priv->state = STATE_SHUTDOWN;
+ return 0;
+}
+
+static int cxd2841er_sleep_tc_to_shutdown(struct cxd2841er_priv *priv)
+{
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_SLEEP_TC) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* Disable oscillator */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x15, 0x01);
+ /* Set demod mode */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01);
+ priv->state = STATE_SHUTDOWN;
+ return 0;
+}
+
+static int cxd2841er_active_t_to_sleep_tc(struct cxd2841er_priv *priv)
+{
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* disable TS output */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
+ /* enable Hi-Z setting 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f);
+ /* enable Hi-Z setting 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff);
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* disable ADC 1 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Disable ADC 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
+ /* Disable ADC 3 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
+ /* Disable ADC clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+ /* Disable RF level monitor */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
+ /* Disable demod clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00);
+ priv->state = STATE_SLEEP_TC;
+ return 0;
+}
+
+static int cxd2841er_active_t2_to_sleep_tc(struct cxd2841er_priv *priv)
+{
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* disable TS output */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
+ /* enable Hi-Z setting 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f);
+ /* enable Hi-Z setting 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff);
+ /* Cancel DVB-T2 setting */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x83, 0x40);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x86, 0x21);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9e, 0x09, 0x0f);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x9f, 0xfb);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2a);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x38, 0x00, 0x0f);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x00, 0x3f);
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* disable ADC 1 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Disable ADC 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
+ /* Disable ADC 3 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
+ /* Disable ADC clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+ /* Disable RF level monitor */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
+ /* Disable demod clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00);
+ priv->state = STATE_SLEEP_TC;
+ return 0;
+}
+
+static int cxd2841er_active_c_to_sleep_tc(struct cxd2841er_priv *priv)
+{
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* disable TS output */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
+ /* enable Hi-Z setting 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f);
+ /* enable Hi-Z setting 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff);
+ /* Cancel DVB-C setting */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa3, 0x00, 0x1f);
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* disable ADC 1 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Disable ADC 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
+ /* Disable ADC 3 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
+ /* Disable ADC clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+ /* Disable RF level monitor */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
+ /* Disable demod clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00);
+ priv->state = STATE_SLEEP_TC;
+ return 0;
+}
+
+static int cxd2841er_active_i_to_sleep_tc(struct cxd2841er_priv *priv)
+{
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* disable TS output */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
+ /* enable Hi-Z setting 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f);
+ /* enable Hi-Z setting 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff);
+
+ /* TODO: Cancel demod parameter */
+
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* disable ADC 1 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Disable ADC 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
+ /* Disable ADC 3 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
+ /* Disable ADC clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+ /* Disable RF level monitor */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
+ /* Disable demod clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00);
+ priv->state = STATE_SLEEP_TC;
+ return 0;
+}
+
+static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv)
+{
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_SHUTDOWN) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* Clear all demodulator registers */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x02, 0x00);
+ usleep_range(3000, 5000);
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* Set demod SW reset */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01);
+
+ switch (priv->xtal) {
+ case SONY_XTAL_20500:
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00);
+ break;
+ case SONY_XTAL_24000:
+ /* Select demod frequency */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00);
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x03);
+ break;
+ case SONY_XTAL_41000:
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x01);
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s(): invalid demod xtal %d\n",
+ __func__, priv->xtal);
+ return -EINVAL;
+ }
+
+ /* Set demod mode */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x0a);
+ /* Clear demod SW reset */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00);
+ usleep_range(1000, 2000);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* enable DSQOUT */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x1F);
+ /* enable DSQIN */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x9C, 0x40);
+ /* TADC Bias On */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
+ /* SADC Bias On */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06);
+ priv->state = STATE_SLEEP_S;
+ return 0;
+}
+
+static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv)
+{
+ u8 data = 0;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_SHUTDOWN) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* Clear all demodulator registers */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x02, 0x00);
+ usleep_range(3000, 5000);
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* Set demod SW reset */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01);
+ /* Select ADC clock mode */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x13, 0x00);
+
+ switch (priv->xtal) {
+ case SONY_XTAL_20500:
+ data = 0x0;
+ break;
+ case SONY_XTAL_24000:
+ /* Select demod frequency */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00);
+ data = 0x3;
+ break;
+ case SONY_XTAL_41000:
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00);
+ data = 0x1;
+ break;
+ }
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x14, data);
+ /* Clear demod SW reset */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00);
+ usleep_range(1000, 2000);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* TADC Bias On */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
+ /* SADC Bias On */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x63, 0x16);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x65, 0x27);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x69, 0x06);
+ priv->state = STATE_SLEEP_TC;
+ return 0;
+}
+
+static int cxd2841er_tune_done(struct cxd2841er_priv *priv)
+{
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0, 0);
+ /* SW Reset */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xfe, 0x01);
+ /* Enable TS output */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x00);
+ return 0;
+}
+
+/* Set TS parallel mode */
+static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv,
+ u8 system)
+{
+ u8 serial_ts, ts_rate_ctrl_off, ts_in_off;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ cxd2841er_read_reg(priv, I2C_SLVT, 0xc4, &serial_ts);
+ cxd2841er_read_reg(priv, I2C_SLVT, 0xd3, &ts_rate_ctrl_off);
+ cxd2841er_read_reg(priv, I2C_SLVT, 0xde, &ts_in_off);
+ dev_dbg(&priv->i2c->dev, "%s(): ser_ts=0x%02x rate_ctrl_off=0x%02x in_off=0x%02x\n",
+ __func__, serial_ts, ts_rate_ctrl_off, ts_in_off);
+
+ /*
+ * slave Bank Addr Bit default Name
+ * <SLV-T> 00h C4h [1:0] 2'b?? OSERCKMODE
+ */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4,
+ ((priv->flags & CXD2841ER_TS_SERIAL) ? 0x01 : 0x00), 0x03);
+ /*
+ * slave Bank Addr Bit default Name
+ * <SLV-T> 00h D1h [1:0] 2'b?? OSERDUTYMODE
+ */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd1,
+ ((priv->flags & CXD2841ER_TS_SERIAL) ? 0x01 : 0x00), 0x03);
+ /*
+ * slave Bank Addr Bit default Name
+ * <SLV-T> 00h D9h [7:0] 8'h08 OTSCKPERIOD
+ */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xd9, 0x08);
+ /*
+ * Disable TS IF Clock
+ * slave Bank Addr Bit default Name
+ * <SLV-T> 00h 32h [0] 1'b1 OREG_CK_TSIF_EN
+ */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x32, 0x00, 0x01);
+ /*
+ * slave Bank Addr Bit default Name
+ * <SLV-T> 00h 33h [1:0] 2'b01 OREG_CKSEL_TSIF
+ */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x33,
+ ((priv->flags & CXD2841ER_TS_SERIAL) ? 0x01 : 0x00), 0x03);
+ /*
+ * Enable TS IF Clock
+ * slave Bank Addr Bit default Name
+ * <SLV-T> 00h 32h [0] 1'b1 OREG_CK_TSIF_EN
+ */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x32, 0x01, 0x01);
+
+ if (system == SYS_DVBT) {
+ /* Enable parity period for DVB-T */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x66, 0x01, 0x01);
+ } else if (system == SYS_DVBC_ANNEX_A) {
+ /* Enable parity period for DVB-C */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x66, 0x01, 0x01);
+ }
+}
+
+static u8 cxd2841er_chip_id(struct cxd2841er_priv *priv)
+{
+ u8 chip_id = 0;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (cxd2841er_write_reg(priv, I2C_SLVT, 0, 0) == 0)
+ cxd2841er_read_reg(priv, I2C_SLVT, 0xfd, &chip_id);
+ else if (cxd2841er_write_reg(priv, I2C_SLVX, 0, 0) == 0)
+ cxd2841er_read_reg(priv, I2C_SLVX, 0xfd, &chip_id);
+
+ return chip_id;
+}
+
+static int cxd2841er_read_status_s(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ u8 reg = 0;
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ *status = 0;
+ if (priv->state != STATE_ACTIVE_S) {
+ dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ /* Set SLV-T Bank : 0xA0 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
+ /*
+ * slave Bank Addr Bit Signal name
+ * <SLV-T> A0h 11h [2] ITSLOCK
+ */
+ cxd2841er_read_reg(priv, I2C_SLVT, 0x11, &reg);
+ if (reg & 0x04) {
+ *status = FE_HAS_SIGNAL
+ | FE_HAS_CARRIER
+ | FE_HAS_VITERBI
+ | FE_HAS_SYNC
+ | FE_HAS_LOCK;
+ }
+ dev_dbg(&priv->i2c->dev, "%s(): result 0x%x\n", __func__, *status);
+ return 0;
+}
+
+static int cxd2841er_read_status_t_t2(struct cxd2841er_priv *priv,
+ u8 *sync, u8 *tslock, u8 *unlock)
+{
+ u8 data = 0;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_TC)
+ return -EINVAL;
+ if (priv->system == SYS_DVBT) {
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ } else {
+ /* Set SLV-T Bank : 0x20 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
+ }
+ cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data);
+ if ((data & 0x07) == 0x07) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): invalid hardware state detected\n", __func__);
+ *sync = 0;
+ *tslock = 0;
+ *unlock = 0;
+ } else {
+ *sync = ((data & 0x07) == 0x6 ? 1 : 0);
+ *tslock = ((data & 0x20) ? 1 : 0);
+ *unlock = ((data & 0x10) ? 1 : 0);
+ }
+ return 0;
+}
+
+static int cxd2841er_read_status_c(struct cxd2841er_priv *priv, u8 *tslock)
+{
+ u8 data;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_TC)
+ return -EINVAL;
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+ cxd2841er_read_reg(priv, I2C_SLVT, 0x88, &data);
+ if ((data & 0x01) == 0) {
+ *tslock = 0;
+ } else {
+ cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data);
+ *tslock = ((data & 0x20) ? 1 : 0);
+ }
+ return 0;
+}
+
+static int cxd2841er_read_status_i(struct cxd2841er_priv *priv,
+ u8 *sync, u8 *tslock, u8 *unlock)
+{
+ u8 data = 0;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_TC)
+ return -EINVAL;
+ /* Set SLV-T Bank : 0x60 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60);
+ cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data);
+ dev_dbg(&priv->i2c->dev,
+ "%s(): lock=0x%x\n", __func__, data);
+ *sync = ((data & 0x02) ? 1 : 0);
+ *tslock = ((data & 0x01) ? 1 : 0);
+ *unlock = ((data & 0x10) ? 1 : 0);
+ return 0;
+}
+
+static int cxd2841er_read_status_tc(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ int ret = 0;
+ u8 sync = 0;
+ u8 tslock = 0;
+ u8 unlock = 0;
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ *status = 0;
+ if (priv->state == STATE_ACTIVE_TC) {
+ if (priv->system == SYS_DVBT || priv->system == SYS_DVBT2) {
+ ret = cxd2841er_read_status_t_t2(
+ priv, &sync, &tslock, &unlock);
+ if (ret)
+ goto done;
+ if (unlock)
+ goto done;
+ if (sync)
+ *status = FE_HAS_SIGNAL |
+ FE_HAS_CARRIER |
+ FE_HAS_VITERBI |
+ FE_HAS_SYNC;
+ if (tslock)
+ *status |= FE_HAS_LOCK;
+ } else if (priv->system == SYS_ISDBT) {
+ ret = cxd2841er_read_status_i(
+ priv, &sync, &tslock, &unlock);
+ if (ret)
+ goto done;
+ if (unlock)
+ goto done;
+ if (sync)
+ *status = FE_HAS_SIGNAL |
+ FE_HAS_CARRIER |
+ FE_HAS_VITERBI |
+ FE_HAS_SYNC;
+ if (tslock)
+ *status |= FE_HAS_LOCK;
+ } else if (priv->system == SYS_DVBC_ANNEX_A) {
+ ret = cxd2841er_read_status_c(priv, &tslock);
+ if (ret)
+ goto done;
+ if (tslock)
+ *status = FE_HAS_SIGNAL |
+ FE_HAS_CARRIER |
+ FE_HAS_VITERBI |
+ FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ }
+ }
+done:
+ dev_dbg(&priv->i2c->dev, "%s(): status 0x%x\n", __func__, *status);
+ return ret;
+}
+
+static int cxd2841er_get_carrier_offset_s_s2(struct cxd2841er_priv *priv,
+ int *offset)
+{
+ u8 data[3];
+ u8 is_hs_mode;
+ s32 cfrl_ctrlval;
+ s32 temp_div, temp_q, temp_r;
+
+ if (priv->state != STATE_ACTIVE_S) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ /*
+ * Get High Sampling Rate mode
+ * slave Bank Addr Bit Signal name
+ * <SLV-T> A0h 10h [0] ITRL_LOCK
+ */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
+ cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data[0]);
+ if (data[0] & 0x01) {
+ /*
+ * slave Bank Addr Bit Signal name
+ * <SLV-T> A0h 50h [4] IHSMODE
+ */
+ cxd2841er_read_reg(priv, I2C_SLVT, 0x50, &data[0]);
+ is_hs_mode = (data[0] & 0x10 ? 1 : 0);
+ } else {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): unable to detect sampling rate mode\n",
+ __func__);
+ return -EINVAL;
+ }
+ /*
+ * slave Bank Addr Bit Signal name
+ * <SLV-T> A0h 45h [4:0] ICFRL_CTRLVAL[20:16]
+ * <SLV-T> A0h 46h [7:0] ICFRL_CTRLVAL[15:8]
+ * <SLV-T> A0h 47h [7:0] ICFRL_CTRLVAL[7:0]
+ */
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x45, data, 3);
+ cfrl_ctrlval = sign_extend32((((u32)data[0] & 0x1F) << 16) |
+ (((u32)data[1] & 0xFF) << 8) |
+ ((u32)data[2] & 0xFF), 20);
+ temp_div = (is_hs_mode ? 1048576 : 1572864);
+ if (cfrl_ctrlval > 0) {
+ temp_q = div_s64_rem(97375LL * cfrl_ctrlval,
+ temp_div, &temp_r);
+ } else {
+ temp_q = div_s64_rem(-97375LL * cfrl_ctrlval,
+ temp_div, &temp_r);
+ }
+ if (temp_r >= temp_div / 2)
+ temp_q++;
+ if (cfrl_ctrlval > 0)
+ temp_q *= -1;
+ *offset = temp_q;
+ return 0;
+}
+
+static int cxd2841er_get_carrier_offset_i(struct cxd2841er_priv *priv,
+ u32 bandwidth, int *offset)
+{
+ u8 data[4];
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ if (priv->system != SYS_ISDBT) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n",
+ __func__, priv->system);
+ return -EINVAL;
+ }
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data));
+ *offset = -1 * sign_extend32(
+ ((u32)(data[0] & 0x1F) << 24) | ((u32)data[1] << 16) |
+ ((u32)data[2] << 8) | (u32)data[3], 29);
+
+ switch (bandwidth) {
+ case 6000000:
+ *offset = -1 * ((*offset) * 8/264);
+ break;
+ case 7000000:
+ *offset = -1 * ((*offset) * 8/231);
+ break;
+ case 8000000:
+ *offset = -1 * ((*offset) * 8/198);
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n",
+ __func__, bandwidth);
+ return -EINVAL;
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s(): bandwidth %d offset %d\n",
+ __func__, bandwidth, *offset);
+
+ return 0;
+}
+
+static int cxd2841er_get_carrier_offset_t(struct cxd2841er_priv *priv,
+ u32 bandwidth, int *offset)
+{
+ u8 data[4];
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ if (priv->system != SYS_DVBT) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n",
+ __func__, priv->system);
+ return -EINVAL;
+ }
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data));
+ *offset = -1 * sign_extend32(
+ ((u32)(data[0] & 0x1F) << 24) | ((u32)data[1] << 16) |
+ ((u32)data[2] << 8) | (u32)data[3], 29);
+ *offset *= (bandwidth / 1000000);
+ *offset /= 235;
+ return 0;
+}
+
+static int cxd2841er_get_carrier_offset_t2(struct cxd2841er_priv *priv,
+ u32 bandwidth, int *offset)
+{
+ u8 data[4];
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ if (priv->system != SYS_DVBT2) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n",
+ __func__, priv->system);
+ return -EINVAL;
+ }
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data));
+ *offset = -1 * sign_extend32(
+ ((u32)(data[0] & 0x0F) << 24) | ((u32)data[1] << 16) |
+ ((u32)data[2] << 8) | (u32)data[3], 27);
+ switch (bandwidth) {
+ case 1712000:
+ *offset /= 582;
+ break;
+ case 5000000:
+ case 6000000:
+ case 7000000:
+ case 8000000:
+ *offset *= (bandwidth / 1000000);
+ *offset /= 940;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n",
+ __func__, bandwidth);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cxd2841er_get_carrier_offset_c(struct cxd2841er_priv *priv,
+ int *offset)
+{
+ u8 data[2];
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ if (priv->system != SYS_DVBC_ANNEX_A) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n",
+ __func__, priv->system);
+ return -EINVAL;
+ }
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x15, data, sizeof(data));
+ *offset = div_s64(41000LL * sign_extend32((((u32)data[0] & 0x3f) << 8)
+ | (u32)data[1], 13), 16384);
+ return 0;
+}
+
+static int cxd2841er_read_packet_errors_c(
+ struct cxd2841er_priv *priv, u32 *penum)
+{
+ u8 data[3];
+
+ *penum = 0;
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0xea, data, sizeof(data));
+ if (data[2] & 0x01)
+ *penum = ((u32)data[0] << 8) | (u32)data[1];
+ return 0;
+}
+
+static int cxd2841er_read_packet_errors_t(
+ struct cxd2841er_priv *priv, u32 *penum)
+{
+ u8 data[3];
+
+ *penum = 0;
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0xea, data, sizeof(data));
+ if (data[2] & 0x01)
+ *penum = ((u32)data[0] << 8) | (u32)data[1];
+ return 0;
+}
+
+static int cxd2841er_read_packet_errors_t2(
+ struct cxd2841er_priv *priv, u32 *penum)
+{
+ u8 data[3];
+
+ *penum = 0;
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x24);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0xfd, data, sizeof(data));
+ if (data[0] & 0x01)
+ *penum = ((u32)data[1] << 8) | (u32)data[2];
+ return 0;
+}
+
+static int cxd2841er_read_packet_errors_i(
+ struct cxd2841er_priv *priv, u32 *penum)
+{
+ u8 data[2];
+
+ *penum = 0;
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0xA1, data, 1);
+
+ if (!(data[0] & 0x01))
+ return 0;
+
+ /* Layer A */
+ cxd2841er_read_regs(priv, I2C_SLVT, 0xA2, data, sizeof(data));
+ *penum = ((u32)data[0] << 8) | (u32)data[1];
+
+ /* Layer B */
+ cxd2841er_read_regs(priv, I2C_SLVT, 0xA4, data, sizeof(data));
+ *penum += ((u32)data[0] << 8) | (u32)data[1];
+
+ /* Layer C */
+ cxd2841er_read_regs(priv, I2C_SLVT, 0xA6, data, sizeof(data));
+ *penum += ((u32)data[0] << 8) | (u32)data[1];
+
+ return 0;
+}
+
+static int cxd2841er_read_ber_c(struct cxd2841er_priv *priv,
+ u32 *bit_error, u32 *bit_count)
+{
+ u8 data[3];
+ u32 bit_err, period_exp;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x62, data, sizeof(data));
+ if (!(data[0] & 0x80)) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): no valid BER data\n", __func__);
+ return -EINVAL;
+ }
+ bit_err = ((u32)(data[0] & 0x3f) << 16) |
+ ((u32)data[1] << 8) |
+ (u32)data[2];
+ cxd2841er_read_reg(priv, I2C_SLVT, 0x60, data);
+ period_exp = data[0] & 0x1f;
+
+ if ((period_exp <= 11) && (bit_err > (1 << period_exp) * 204 * 8)) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): period_exp(%u) or bit_err(%u) not in range. no valid BER data\n",
+ __func__, period_exp, bit_err);
+ return -EINVAL;
+ }
+
+ dev_dbg(&priv->i2c->dev,
+ "%s(): period_exp(%u) or bit_err(%u) count=%d\n",
+ __func__, period_exp, bit_err,
+ ((1 << period_exp) * 204 * 8));
+
+ *bit_error = bit_err;
+ *bit_count = ((1 << period_exp) * 204 * 8);
+
+ return 0;
+}
+
+static int cxd2841er_read_ber_i(struct cxd2841er_priv *priv,
+ u32 *bit_error, u32 *bit_count)
+{
+ u8 data[3];
+ u8 pktnum[2];
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+
+ cxd2841er_freeze_regs(priv);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x5B, pktnum, sizeof(pktnum));
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x16, data, sizeof(data));
+ cxd2841er_unfreeze_regs(priv);
+
+ if (!pktnum[0] && !pktnum[1]) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): no valid BER data\n", __func__);
+ return -EINVAL;
+ }
+
+ *bit_error = ((u32)(data[0] & 0x7F) << 16) |
+ ((u32)data[1] << 8) | data[2];
+ *bit_count = ((((u32)pktnum[0] << 8) | pktnum[1]) * 204 * 8);
+ dev_dbg(&priv->i2c->dev, "%s(): bit_error=%u bit_count=%u\n",
+ __func__, *bit_error, *bit_count);
+
+ return 0;
+}
+
+static int cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv,
+ u32 *bit_error, u32 *bit_count)
+{
+ u8 data[11];
+
+ /* Set SLV-T Bank : 0xA0 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
+ /*
+ * slave Bank Addr Bit Signal name
+ * <SLV-T> A0h 35h [0] IFVBER_VALID
+ * <SLV-T> A0h 36h [5:0] IFVBER_BITERR[21:16]
+ * <SLV-T> A0h 37h [7:0] IFVBER_BITERR[15:8]
+ * <SLV-T> A0h 38h [7:0] IFVBER_BITERR[7:0]
+ * <SLV-T> A0h 3Dh [5:0] IFVBER_BITNUM[21:16]
+ * <SLV-T> A0h 3Eh [7:0] IFVBER_BITNUM[15:8]
+ * <SLV-T> A0h 3Fh [7:0] IFVBER_BITNUM[7:0]
+ */
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x35, data, 11);
+ if (data[0] & 0x01) {
+ *bit_error = ((u32)(data[1] & 0x3F) << 16) |
+ ((u32)(data[2] & 0xFF) << 8) |
+ (u32)(data[3] & 0xFF);
+ *bit_count = ((u32)(data[8] & 0x3F) << 16) |
+ ((u32)(data[9] & 0xFF) << 8) |
+ (u32)(data[10] & 0xFF);
+ if ((*bit_count == 0) || (*bit_error > *bit_count)) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): invalid bit_error %d, bit_count %d\n",
+ __func__, *bit_error, *bit_count);
+ return -EINVAL;
+ }
+ return 0;
+ }
+ dev_dbg(&priv->i2c->dev, "%s(): no data available\n", __func__);
+ return -EINVAL;
+}
+
+
+static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv,
+ u32 *bit_error, u32 *bit_count)
+{
+ u8 data[5];
+ u32 period;
+
+ /* Set SLV-T Bank : 0xB2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xb2);
+ /*
+ * slave Bank Addr Bit Signal name
+ * <SLV-T> B2h 30h [0] IFLBER_VALID
+ * <SLV-T> B2h 31h [3:0] IFLBER_BITERR[27:24]
+ * <SLV-T> B2h 32h [7:0] IFLBER_BITERR[23:16]
+ * <SLV-T> B2h 33h [7:0] IFLBER_BITERR[15:8]
+ * <SLV-T> B2h 34h [7:0] IFLBER_BITERR[7:0]
+ */
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x30, data, 5);
+ if (data[0] & 0x01) {
+ /* Bit error count */
+ *bit_error = ((u32)(data[1] & 0x0F) << 24) |
+ ((u32)(data[2] & 0xFF) << 16) |
+ ((u32)(data[3] & 0xFF) << 8) |
+ (u32)(data[4] & 0xFF);
+
+ /* Set SLV-T Bank : 0xA0 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
+ cxd2841er_read_reg(priv, I2C_SLVT, 0x7a, data);
+ /* Measurement period */
+ period = (u32)(1 << (data[0] & 0x0F));
+ if (period == 0) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): period is 0\n", __func__);
+ return -EINVAL;
+ }
+ if (*bit_error > (period * 64800)) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): invalid bit_err 0x%x period 0x%x\n",
+ __func__, *bit_error, period);
+ return -EINVAL;
+ }
+ *bit_count = period * 64800;
+
+ return 0;
+ } else {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): no data available\n", __func__);
+ }
+ return -EINVAL;
+}
+
+static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv,
+ u32 *bit_error, u32 *bit_count)
+{
+ u8 data[4];
+ u32 period_exp, n_ldpc;
+
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): invalid state %d\n", __func__, priv->state);
+ return -EINVAL;
+ }
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x39, data, sizeof(data));
+ if (!(data[0] & 0x10)) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): no valid BER data\n", __func__);
+ return -EINVAL;
+ }
+ *bit_error = ((u32)(data[0] & 0x0f) << 24) |
+ ((u32)data[1] << 16) |
+ ((u32)data[2] << 8) |
+ (u32)data[3];
+ cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data);
+ period_exp = data[0] & 0x0f;
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x22);
+ cxd2841er_read_reg(priv, I2C_SLVT, 0x5e, data);
+ n_ldpc = ((data[0] & 0x03) == 0 ? 16200 : 64800);
+ if (*bit_error > ((1U << period_exp) * n_ldpc)) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): invalid BER value\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * FIXME: the right thing would be to return bit_error untouched,
+ * but, as we don't know the scale returned by the counters, let's
+ * at least preserver BER = bit_error/bit_count.
+ */
+ if (period_exp >= 4) {
+ *bit_count = (1U << (period_exp - 4)) * (n_ldpc / 200);
+ *bit_error *= 3125ULL;
+ } else {
+ *bit_count = (1U << period_exp) * (n_ldpc / 200);
+ *bit_error *= 50000ULL;
+ }
+ return 0;
+}
+
+static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv,
+ u32 *bit_error, u32 *bit_count)
+{
+ u8 data[2];
+ u32 period;
+
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): invalid state %d\n", __func__, priv->state);
+ return -EINVAL;
+ }
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ cxd2841er_read_reg(priv, I2C_SLVT, 0x39, data);
+ if (!(data[0] & 0x01)) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): no valid BER data\n", __func__);
+ return 0;
+ }
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x22, data, sizeof(data));
+ *bit_error = ((u32)data[0] << 8) | (u32)data[1];
+ cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data);
+ period = ((data[0] & 0x07) == 0) ? 256 : (4096 << (data[0] & 0x07));
+
+ /*
+ * FIXME: the right thing would be to return bit_error untouched,
+ * but, as we don't know the scale returned by the counters, let's
+ * at least preserver BER = bit_error/bit_count.
+ */
+ *bit_count = period / 128;
+ *bit_error *= 78125ULL;
+ return 0;
+}
+
+static int cxd2841er_freeze_regs(struct cxd2841er_priv *priv)
+{
+ /*
+ * Freeze registers: ensure multiple separate register reads
+ * are from the same snapshot
+ */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x01, 0x01);
+ return 0;
+}
+
+static int cxd2841er_unfreeze_regs(struct cxd2841er_priv *priv)
+{
+ /*
+ * un-freeze registers
+ */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x01, 0x00);
+ return 0;
+}
+
+static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv,
+ u8 delsys, u32 *snr)
+{
+ u8 data[3];
+ u32 res = 0, value;
+ int min_index, max_index, index;
+ static const struct cxd2841er_cnr_data *cn_data;
+
+ cxd2841er_freeze_regs(priv);
+ /* Set SLV-T Bank : 0xA1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa1);
+ /*
+ * slave Bank Addr Bit Signal name
+ * <SLV-T> A1h 10h [0] ICPM_QUICKRDY
+ * <SLV-T> A1h 11h [4:0] ICPM_QUICKCNDT[12:8]
+ * <SLV-T> A1h 12h [7:0] ICPM_QUICKCNDT[7:0]
+ */
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x10, data, 3);
+ cxd2841er_unfreeze_regs(priv);
+
+ if (data[0] & 0x01) {
+ value = ((u32)(data[1] & 0x1F) << 8) | (u32)(data[2] & 0xFF);
+ min_index = 0;
+ if (delsys == SYS_DVBS) {
+ cn_data = s_cn_data;
+ max_index = ARRAY_SIZE(s_cn_data) - 1;
+ } else {
+ cn_data = s2_cn_data;
+ max_index = ARRAY_SIZE(s2_cn_data) - 1;
+ }
+ if (value >= cn_data[min_index].value) {
+ res = cn_data[min_index].cnr_x1000;
+ goto done;
+ }
+ if (value <= cn_data[max_index].value) {
+ res = cn_data[max_index].cnr_x1000;
+ goto done;
+ }
+ while ((max_index - min_index) > 1) {
+ index = (max_index + min_index) / 2;
+ if (value == cn_data[index].value) {
+ res = cn_data[index].cnr_x1000;
+ goto done;
+ } else if (value > cn_data[index].value)
+ max_index = index;
+ else
+ min_index = index;
+ if ((max_index - min_index) <= 1) {
+ if (value == cn_data[max_index].value) {
+ res = cn_data[max_index].cnr_x1000;
+ goto done;
+ } else {
+ res = cn_data[min_index].cnr_x1000;
+ goto done;
+ }
+ }
+ }
+ } else {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): no data available\n", __func__);
+ return -EINVAL;
+ }
+done:
+ *snr = res;
+ return 0;
+}
+
+static uint32_t sony_log(uint32_t x)
+{
+ return (((10000>>8)*(intlog2(x)>>16) + LOG2_E_100X/2)/LOG2_E_100X);
+}
+
+static int cxd2841er_read_snr_c(struct cxd2841er_priv *priv, u32 *snr)
+{
+ u32 reg;
+ u8 data[2];
+ enum sony_dvbc_constellation_t qam = SONY_DVBC_CONSTELLATION_16QAM;
+
+ *snr = 0;
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+
+ cxd2841er_freeze_regs(priv);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x19, data, 1);
+ qam = (enum sony_dvbc_constellation_t) (data[0] & 0x07);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x4C, data, 2);
+ cxd2841er_unfreeze_regs(priv);
+
+ reg = ((u32)(data[0]&0x1f) << 8) | (u32)data[1];
+ if (reg == 0) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): reg value out of range\n", __func__);
+ return 0;
+ }
+
+ switch (qam) {
+ case SONY_DVBC_CONSTELLATION_16QAM:
+ case SONY_DVBC_CONSTELLATION_64QAM:
+ case SONY_DVBC_CONSTELLATION_256QAM:
+ /* SNR(dB) = -9.50 * ln(IREG_SNR_ESTIMATE / (24320)) */
+ if (reg < 126)
+ reg = 126;
+ *snr = -95 * (int32_t)sony_log(reg) + 95941;
+ break;
+ case SONY_DVBC_CONSTELLATION_32QAM:
+ case SONY_DVBC_CONSTELLATION_128QAM:
+ /* SNR(dB) = -8.75 * ln(IREG_SNR_ESTIMATE / (20800)) */
+ if (reg < 69)
+ reg = 69;
+ *snr = -88 * (int32_t)sony_log(reg) + 86999;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cxd2841er_read_snr_t(struct cxd2841er_priv *priv, u32 *snr)
+{
+ u32 reg;
+ u8 data[2];
+
+ *snr = 0;
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): invalid state %d\n", __func__, priv->state);
+ return -EINVAL;
+ }
+
+ cxd2841er_freeze_regs(priv);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data));
+ cxd2841er_unfreeze_regs(priv);
+
+ reg = ((u32)data[0] << 8) | (u32)data[1];
+ if (reg == 0) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): reg value out of range\n", __func__);
+ return 0;
+ }
+ if (reg > 4996)
+ reg = 4996;
+ *snr = 100 * ((INTLOG10X100(reg) - INTLOG10X100(5350 - reg)) + 285);
+ return 0;
+}
+
+static int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr)
+{
+ u32 reg;
+ u8 data[2];
+
+ *snr = 0;
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): invalid state %d\n", __func__, priv->state);
+ return -EINVAL;
+ }
+
+ cxd2841er_freeze_regs(priv);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data));
+ cxd2841er_unfreeze_regs(priv);
+
+ reg = ((u32)data[0] << 8) | (u32)data[1];
+ if (reg == 0) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): reg value out of range\n", __func__);
+ return 0;
+ }
+ if (reg > 10876)
+ reg = 10876;
+ *snr = 100 * ((INTLOG10X100(reg) - INTLOG10X100(12600 - reg)) + 320);
+ return 0;
+}
+
+static int cxd2841er_read_snr_i(struct cxd2841er_priv *priv, u32 *snr)
+{
+ u32 reg;
+ u8 data[2];
+
+ *snr = 0;
+ if (priv->state != STATE_ACTIVE_TC) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): invalid state %d\n", __func__,
+ priv->state);
+ return -EINVAL;
+ }
+
+ cxd2841er_freeze_regs(priv);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data));
+ cxd2841er_unfreeze_regs(priv);
+
+ reg = ((u32)data[0] << 8) | (u32)data[1];
+ if (reg == 0) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): reg value out of range\n", __func__);
+ return 0;
+ }
+ *snr = 10000 * (intlog10(reg) >> 24) - 9031;
+ return 0;
+}
+
+static u16 cxd2841er_read_agc_gain_c(struct cxd2841er_priv *priv,
+ u8 delsys)
+{
+ u8 data[2];
+
+ cxd2841er_write_reg(
+ priv, I2C_SLVT, 0x00, 0x40);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x49, data, 2);
+ dev_dbg(&priv->i2c->dev,
+ "%s(): AGC value=%u\n",
+ __func__, (((u16)data[0] & 0x0F) << 8) |
+ (u16)(data[1] & 0xFF));
+ return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4;
+}
+
+static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv,
+ u8 delsys)
+{
+ u8 data[2];
+
+ cxd2841er_write_reg(
+ priv, I2C_SLVT, 0x00, (delsys == SYS_DVBT ? 0x10 : 0x20));
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2);
+ dev_dbg(&priv->i2c->dev,
+ "%s(): AGC value=%u\n",
+ __func__, (((u16)data[0] & 0x0F) << 8) |
+ (u16)(data[1] & 0xFF));
+ return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4;
+}
+
+static u16 cxd2841er_read_agc_gain_i(struct cxd2841er_priv *priv,
+ u8 delsys)
+{
+ u8 data[2];
+
+ cxd2841er_write_reg(
+ priv, I2C_SLVT, 0x00, 0x60);
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2);
+
+ dev_dbg(&priv->i2c->dev,
+ "%s(): AGC value=%u\n",
+ __func__, (((u16)data[0] & 0x0F) << 8) |
+ (u16)(data[1] & 0xFF));
+ return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4;
+}
+
+static u16 cxd2841er_read_agc_gain_s(struct cxd2841er_priv *priv)
+{
+ u8 data[2];
+
+ /* Set SLV-T Bank : 0xA0 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
+ /*
+ * slave Bank Addr Bit Signal name
+ * <SLV-T> A0h 1Fh [4:0] IRFAGC_GAIN[12:8]
+ * <SLV-T> A0h 20h [7:0] IRFAGC_GAIN[7:0]
+ */
+ cxd2841er_read_regs(priv, I2C_SLVT, 0x1f, data, 2);
+ return ((((u16)data[0] & 0x1F) << 8) | (u16)(data[1] & 0xFF)) << 3;
+}
+
+static void cxd2841er_read_ber(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+ u32 bit_error = 0, bit_count = 0;
+ int ret;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ switch (p->delivery_system) {
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_B:
+ case SYS_DVBC_ANNEX_C:
+ ret = cxd2841er_read_ber_c(priv, &bit_error, &bit_count);
+ break;
+ case SYS_ISDBT:
+ ret = cxd2841er_read_ber_i(priv, &bit_error, &bit_count);
+ break;
+ case SYS_DVBS:
+ ret = cxd2841er_mon_read_ber_s(priv, &bit_error, &bit_count);
+ break;
+ case SYS_DVBS2:
+ ret = cxd2841er_mon_read_ber_s2(priv, &bit_error, &bit_count);
+ break;
+ case SYS_DVBT:
+ ret = cxd2841er_read_ber_t(priv, &bit_error, &bit_count);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2841er_read_ber_t2(priv, &bit_error, &bit_count);
+ break;
+ default:
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return;
+ }
+
+ if (!ret) {
+ p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_error.stat[0].uvalue += bit_error;
+ p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_count.stat[0].uvalue += bit_count;
+ } else {
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+}
+
+static void cxd2841er_read_signal_strength(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+ s32 strength;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ switch (p->delivery_system) {
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ strength = cxd2841er_read_agc_gain_t_t2(priv,
+ p->delivery_system);
+ p->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ /* Formula was empirically determinated @ 410 MHz */
+ p->strength.stat[0].uvalue = strength * 366 / 100 - 89520;
+ break; /* Code moved out of the function */
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_B:
+ case SYS_DVBC_ANNEX_C:
+ strength = cxd2841er_read_agc_gain_c(priv,
+ p->delivery_system);
+ p->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ /*
+ * Formula was empirically determinated via linear regression,
+ * using frequencies: 175 MHz, 410 MHz and 800 MHz, and a
+ * stream modulated with QAM64
+ */
+ p->strength.stat[0].uvalue = strength * 4045 / 1000 - 85224;
+ break;
+ case SYS_ISDBT:
+ strength = cxd2841er_read_agc_gain_i(priv, p->delivery_system);
+ p->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ /*
+ * Formula was empirically determinated via linear regression,
+ * using frequencies: 175 MHz, 410 MHz and 800 MHz.
+ */
+ p->strength.stat[0].uvalue = strength * 3775 / 1000 - 90185;
+ break;
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ strength = 65535 - cxd2841er_read_agc_gain_s(priv);
+ p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ p->strength.stat[0].uvalue = strength;
+ break;
+ default:
+ p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ break;
+ }
+}
+
+static void cxd2841er_read_snr(struct dvb_frontend *fe)
+{
+ u32 tmp = 0;
+ int ret = 0;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ switch (p->delivery_system) {
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_B:
+ case SYS_DVBC_ANNEX_C:
+ ret = cxd2841er_read_snr_c(priv, &tmp);
+ break;
+ case SYS_DVBT:
+ ret = cxd2841er_read_snr_t(priv, &tmp);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2841er_read_snr_t2(priv, &tmp);
+ break;
+ case SYS_ISDBT:
+ ret = cxd2841er_read_snr_i(priv, &tmp);
+ break;
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ ret = cxd2841er_dvbs_read_snr(priv, p->delivery_system, &tmp);
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s(): unknown delivery system %d\n",
+ __func__, p->delivery_system);
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return;
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s(): snr=%d\n",
+ __func__, (int32_t)tmp);
+
+ if (!ret) {
+ p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ p->cnr.stat[0].svalue = tmp;
+ } else {
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+}
+
+static void cxd2841er_read_ucblocks(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+ u32 ucblocks = 0;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ switch (p->delivery_system) {
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_B:
+ case SYS_DVBC_ANNEX_C:
+ cxd2841er_read_packet_errors_c(priv, &ucblocks);
+ break;
+ case SYS_DVBT:
+ cxd2841er_read_packet_errors_t(priv, &ucblocks);
+ break;
+ case SYS_DVBT2:
+ cxd2841er_read_packet_errors_t2(priv, &ucblocks);
+ break;
+ case SYS_ISDBT:
+ cxd2841er_read_packet_errors_i(priv, &ucblocks);
+ break;
+ default:
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return;
+ }
+ dev_dbg(&priv->i2c->dev, "%s() ucblocks=%u\n", __func__, ucblocks);
+
+ p->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->block_error.stat[0].uvalue = ucblocks;
+}
+
+static int cxd2841er_dvbt2_set_profile(
+ struct cxd2841er_priv *priv, enum cxd2841er_dvbt2_profile_t profile)
+{
+ u8 tune_mode;
+ u8 seq_not2d_time;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ switch (profile) {
+ case DVBT2_PROFILE_BASE:
+ tune_mode = 0x01;
+ /* Set early unlock time */
+ seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x0E:0x0C;
+ break;
+ case DVBT2_PROFILE_LITE:
+ tune_mode = 0x05;
+ /* Set early unlock time */
+ seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x2E:0x28;
+ break;
+ case DVBT2_PROFILE_ANY:
+ tune_mode = 0x00;
+ /* Set early unlock time */
+ seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x2E:0x28;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* Set SLV-T Bank : 0x2E */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2e);
+ /* Set profile and tune mode */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x10, tune_mode, 0x07);
+ /* Set SLV-T Bank : 0x2B */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b);
+ /* Set early unlock detection time */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x9d, seq_not2d_time);
+ return 0;
+}
+
+static int cxd2841er_dvbt2_set_plp_config(struct cxd2841er_priv *priv,
+ u8 is_auto, u8 plp_id)
+{
+ if (is_auto) {
+ dev_dbg(&priv->i2c->dev,
+ "%s() using auto PLP selection\n", __func__);
+ } else {
+ dev_dbg(&priv->i2c->dev,
+ "%s() using manual PLP selection, ID %d\n",
+ __func__, plp_id);
+ }
+ /* Set SLV-T Bank : 0x23 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x23);
+ if (!is_auto) {
+ /* Manual PLP selection mode. Set the data PLP Id. */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xaf, plp_id);
+ }
+ /* Auto PLP select (Scanning mode = 0x00). Data PLP select = 0x01. */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xad, (is_auto ? 0x00 : 0x01));
+ return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv,
+ u32 bandwidth)
+{
+ u32 iffreq, ifhz;
+ u8 data[MAX_WRITE_REGSIZE];
+
+ static const uint8_t nominalRate8bw[3][5] = {
+ /* TRCG Nominal Rate [37:0] */
+ {0x11, 0xF0, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
+ {0x15, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+ {0x11, 0xF0, 0x00, 0x00, 0x00} /* 41MHz XTal */
+ };
+
+ static const uint8_t nominalRate7bw[3][5] = {
+ /* TRCG Nominal Rate [37:0] */
+ {0x14, 0x80, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
+ {0x18, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+ {0x14, 0x80, 0x00, 0x00, 0x00} /* 41MHz XTal */
+ };
+
+ static const uint8_t nominalRate6bw[3][5] = {
+ /* TRCG Nominal Rate [37:0] */
+ {0x17, 0xEA, 0xAA, 0xAA, 0xAA}, /* 20.5MHz XTal */
+ {0x1C, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+ {0x17, 0xEA, 0xAA, 0xAA, 0xAA} /* 41MHz XTal */
+ };
+
+ static const uint8_t nominalRate5bw[3][5] = {
+ /* TRCG Nominal Rate [37:0] */
+ {0x1C, 0xB3, 0x33, 0x33, 0x33}, /* 20.5MHz XTal */
+ {0x21, 0x99, 0x99, 0x99, 0x99}, /* 24MHz XTal */
+ {0x1C, 0xB3, 0x33, 0x33, 0x33} /* 41MHz XTal */
+ };
+
+ static const uint8_t nominalRate17bw[3][5] = {
+ /* TRCG Nominal Rate [37:0] */
+ {0x58, 0xE2, 0xAF, 0xE0, 0xBC}, /* 20.5MHz XTal */
+ {0x68, 0x0F, 0xA2, 0x32, 0xD0}, /* 24MHz XTal */
+ {0x58, 0xE2, 0xAF, 0xE0, 0xBC} /* 41MHz XTal */
+ };
+
+ static const uint8_t itbCoef8bw[3][14] = {
+ {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA,
+ 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8}, /* 20.5MHz XTal */
+ {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1,
+ 0x29, 0xA5, 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz XTal */
+ {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA,
+ 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8} /* 41MHz XTal */
+ };
+
+ static const uint8_t itbCoef7bw[3][14] = {
+ {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6,
+ 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5}, /* 20.5MHz XTal */
+ {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0,
+ 0x29, 0xA2, 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz XTal */
+ {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6,
+ 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5} /* 41MHz XTal */
+ };
+
+ static const uint8_t itbCoef6bw[3][14] = {
+ {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8,
+ 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */
+ {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E,
+ 0x29, 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */
+ {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8,
+ 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */
+ };
+
+ static const uint8_t itbCoef5bw[3][14] = {
+ {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8,
+ 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */
+ {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E,
+ 0x29, 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */
+ {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8,
+ 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */
+ };
+
+ static const uint8_t itbCoef17bw[3][14] = {
+ {0x25, 0xA0, 0x36, 0x8D, 0x2E, 0x94, 0x28, 0x9B,
+ 0x32, 0x90, 0x2C, 0x9D, 0x29, 0x99}, /* 20.5MHz XTal */
+ {0x33, 0x8E, 0x2B, 0x97, 0x2D, 0x95, 0x37, 0x8B,
+ 0x30, 0x97, 0x2D, 0x9A, 0x21, 0xA4}, /* 24MHz XTal */
+ {0x25, 0xA0, 0x36, 0x8D, 0x2E, 0x94, 0x28, 0x9B,
+ 0x32, 0x90, 0x2C, 0x9D, 0x29, 0x99} /* 41MHz XTal */
+ };
+
+ /* Set SLV-T Bank : 0x20 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
+
+ switch (bandwidth) {
+ case 8000000:
+ /* <Timing Recovery setting> */
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0x9F, nominalRate8bw[priv->xtal], 5);
+
+ /* Set SLV-T Bank : 0x27 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT,
+ 0x7a, 0x00, 0x0f);
+
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+
+ /* Group delay equaliser settings for
+ * ASCOT2D, ASCOT2E and ASCOT3 tuners
+ */
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0xA6, itbCoef8bw[priv->xtal], 14);
+ /* <IF freq setting> */
+ ifhz = cxd2841er_get_if_hz(priv, 4800000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
+ data[0] = (u8) ((iffreq >> 16) & 0xff);
+ data[1] = (u8)((iffreq >> 8) & 0xff);
+ data[2] = (u8)(iffreq & 0xff);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+ /* System bandwidth setting */
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVT, 0xD7, 0x00, 0x07);
+ break;
+ case 7000000:
+ /* <Timing Recovery setting> */
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0x9F, nominalRate7bw[priv->xtal], 5);
+
+ /* Set SLV-T Bank : 0x27 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT,
+ 0x7a, 0x00, 0x0f);
+
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+
+ /* Group delay equaliser settings for
+ * ASCOT2D, ASCOT2E and ASCOT3 tuners
+ */
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0xA6, itbCoef7bw[priv->xtal], 14);
+ /* <IF freq setting> */
+ ifhz = cxd2841er_get_if_hz(priv, 4200000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
+ data[0] = (u8) ((iffreq >> 16) & 0xff);
+ data[1] = (u8)((iffreq >> 8) & 0xff);
+ data[2] = (u8)(iffreq & 0xff);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+ /* System bandwidth setting */
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVT, 0xD7, 0x02, 0x07);
+ break;
+ case 6000000:
+ /* <Timing Recovery setting> */
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0x9F, nominalRate6bw[priv->xtal], 5);
+
+ /* Set SLV-T Bank : 0x27 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT,
+ 0x7a, 0x00, 0x0f);
+
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+
+ /* Group delay equaliser settings for
+ * ASCOT2D, ASCOT2E and ASCOT3 tuners
+ */
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0xA6, itbCoef6bw[priv->xtal], 14);
+ /* <IF freq setting> */
+ ifhz = cxd2841er_get_if_hz(priv, 3600000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
+ data[0] = (u8) ((iffreq >> 16) & 0xff);
+ data[1] = (u8)((iffreq >> 8) & 0xff);
+ data[2] = (u8)(iffreq & 0xff);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+ /* System bandwidth setting */
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVT, 0xD7, 0x04, 0x07);
+ break;
+ case 5000000:
+ /* <Timing Recovery setting> */
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0x9F, nominalRate5bw[priv->xtal], 5);
+
+ /* Set SLV-T Bank : 0x27 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT,
+ 0x7a, 0x00, 0x0f);
+
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+
+ /* Group delay equaliser settings for
+ * ASCOT2D, ASCOT2E and ASCOT3 tuners
+ */
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0xA6, itbCoef5bw[priv->xtal], 14);
+ /* <IF freq setting> */
+ ifhz = cxd2841er_get_if_hz(priv, 3600000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
+ data[0] = (u8) ((iffreq >> 16) & 0xff);
+ data[1] = (u8)((iffreq >> 8) & 0xff);
+ data[2] = (u8)(iffreq & 0xff);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+ /* System bandwidth setting */
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVT, 0xD7, 0x06, 0x07);
+ break;
+ case 1712000:
+ /* <Timing Recovery setting> */
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0x9F, nominalRate17bw[priv->xtal], 5);
+
+ /* Set SLV-T Bank : 0x27 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT,
+ 0x7a, 0x03, 0x0f);
+
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+
+ /* Group delay equaliser settings for
+ * ASCOT2D, ASCOT2E and ASCOT3 tuners
+ */
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0xA6, itbCoef17bw[priv->xtal], 14);
+ /* <IF freq setting> */
+ ifhz = cxd2841er_get_if_hz(priv, 3500000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
+ data[0] = (u8) ((iffreq >> 16) & 0xff);
+ data[1] = (u8)((iffreq >> 8) & 0xff);
+ data[2] = (u8)(iffreq & 0xff);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+ /* System bandwidth setting */
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVT, 0xD7, 0x03, 0x07);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_t_band(
+ struct cxd2841er_priv *priv, u32 bandwidth)
+{
+ u8 data[MAX_WRITE_REGSIZE];
+ u32 iffreq, ifhz;
+ static const u8 nominalRate8bw[3][5] = {
+ /* TRCG Nominal Rate [37:0] */
+ {0x11, 0xF0, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
+ {0x15, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+ {0x11, 0xF0, 0x00, 0x00, 0x00} /* 41MHz XTal */
+ };
+ static const u8 nominalRate7bw[3][5] = {
+ /* TRCG Nominal Rate [37:0] */
+ {0x14, 0x80, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
+ {0x18, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+ {0x14, 0x80, 0x00, 0x00, 0x00} /* 41MHz XTal */
+ };
+ static const u8 nominalRate6bw[3][5] = {
+ /* TRCG Nominal Rate [37:0] */
+ {0x17, 0xEA, 0xAA, 0xAA, 0xAA}, /* 20.5MHz XTal */
+ {0x1C, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+ {0x17, 0xEA, 0xAA, 0xAA, 0xAA} /* 41MHz XTal */
+ };
+ static const u8 nominalRate5bw[3][5] = {
+ /* TRCG Nominal Rate [37:0] */
+ {0x1C, 0xB3, 0x33, 0x33, 0x33}, /* 20.5MHz XTal */
+ {0x21, 0x99, 0x99, 0x99, 0x99}, /* 24MHz XTal */
+ {0x1C, 0xB3, 0x33, 0x33, 0x33} /* 41MHz XTal */
+ };
+
+ static const u8 itbCoef8bw[3][14] = {
+ {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, 0x23, 0xA9,
+ 0x1F, 0xA8, 0x2C, 0xC8}, /* 20.5MHz XTal */
+ {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, 0x29, 0xA5,
+ 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz XTal */
+ {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, 0x23, 0xA9,
+ 0x1F, 0xA8, 0x2C, 0xC8} /* 41MHz XTal */
+ };
+ static const u8 itbCoef7bw[3][14] = {
+ {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, 0x29, 0xB0,
+ 0x26, 0xA9, 0x21, 0xA5}, /* 20.5MHz XTal */
+ {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, 0x29, 0xA2,
+ 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz XTal */
+ {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, 0x29, 0xB0,
+ 0x26, 0xA9, 0x21, 0xA5} /* 41MHz XTal */
+ };
+ static const u8 itbCoef6bw[3][14] = {
+ {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF,
+ 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */
+ {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, 0xA4,
+ 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */
+ {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF,
+ 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */
+ };
+ static const u8 itbCoef5bw[3][14] = {
+ {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF,
+ 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */
+ {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, 0xA4,
+ 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal */
+ {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF,
+ 0x00, 0xE6, 0x23, 0xA4} /* 41MHz XTal */
+ };
+
+ /* Set SLV-T Bank : 0x13 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13);
+ /* Echo performance optimization setting */
+ data[0] = 0x01;
+ data[1] = 0x14;
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x9C, data, 2);
+
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+
+ switch (bandwidth) {
+ case 8000000:
+ /* <Timing Recovery setting> */
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0x9F, nominalRate8bw[priv->xtal], 5);
+ /* Group delay equaliser settings for
+ * ASCOT2D, ASCOT2E and ASCOT3 tuners
+ */
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0xA6, itbCoef8bw[priv->xtal], 14);
+ /* <IF freq setting> */
+ ifhz = cxd2841er_get_if_hz(priv, 4800000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
+ data[0] = (u8) ((iffreq >> 16) & 0xff);
+ data[1] = (u8)((iffreq >> 8) & 0xff);
+ data[2] = (u8)(iffreq & 0xff);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+ /* System bandwidth setting */
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVT, 0xD7, 0x00, 0x07);
+
+ /* Demod core latency setting */
+ if (priv->xtal == SONY_XTAL_24000) {
+ data[0] = 0x15;
+ data[1] = 0x28;
+ } else {
+ data[0] = 0x01;
+ data[1] = 0xE0;
+ }
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+ /* Notch filter setting */
+ data[0] = 0x01;
+ data[1] = 0x02;
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2);
+ break;
+ case 7000000:
+ /* <Timing Recovery setting> */
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0x9F, nominalRate7bw[priv->xtal], 5);
+ /* Group delay equaliser settings for
+ * ASCOT2D, ASCOT2E and ASCOT3 tuners
+ */
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0xA6, itbCoef7bw[priv->xtal], 14);
+ /* <IF freq setting> */
+ ifhz = cxd2841er_get_if_hz(priv, 4200000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
+ data[0] = (u8) ((iffreq >> 16) & 0xff);
+ data[1] = (u8)((iffreq >> 8) & 0xff);
+ data[2] = (u8)(iffreq & 0xff);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+ /* System bandwidth setting */
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVT, 0xD7, 0x02, 0x07);
+
+ /* Demod core latency setting */
+ if (priv->xtal == SONY_XTAL_24000) {
+ data[0] = 0x1F;
+ data[1] = 0xF8;
+ } else {
+ data[0] = 0x12;
+ data[1] = 0xF8;
+ }
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+ /* Notch filter setting */
+ data[0] = 0x00;
+ data[1] = 0x03;
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2);
+ break;
+ case 6000000:
+ /* <Timing Recovery setting> */
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0x9F, nominalRate6bw[priv->xtal], 5);
+ /* Group delay equaliser settings for
+ * ASCOT2D, ASCOT2E and ASCOT3 tuners
+ */
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0xA6, itbCoef6bw[priv->xtal], 14);
+ /* <IF freq setting> */
+ ifhz = cxd2841er_get_if_hz(priv, 3600000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
+ data[0] = (u8) ((iffreq >> 16) & 0xff);
+ data[1] = (u8)((iffreq >> 8) & 0xff);
+ data[2] = (u8)(iffreq & 0xff);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+ /* System bandwidth setting */
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVT, 0xD7, 0x04, 0x07);
+
+ /* Demod core latency setting */
+ if (priv->xtal == SONY_XTAL_24000) {
+ data[0] = 0x25;
+ data[1] = 0x4C;
+ } else {
+ data[0] = 0x1F;
+ data[1] = 0xDC;
+ }
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+ /* Notch filter setting */
+ data[0] = 0x00;
+ data[1] = 0x03;
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2);
+ break;
+ case 5000000:
+ /* <Timing Recovery setting> */
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0x9F, nominalRate5bw[priv->xtal], 5);
+ /* Group delay equaliser settings for
+ * ASCOT2D, ASCOT2E and ASCOT3 tuners
+ */
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0xA6, itbCoef5bw[priv->xtal], 14);
+ /* <IF freq setting> */
+ ifhz = cxd2841er_get_if_hz(priv, 3600000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
+ data[0] = (u8) ((iffreq >> 16) & 0xff);
+ data[1] = (u8)((iffreq >> 8) & 0xff);
+ data[2] = (u8)(iffreq & 0xff);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+ /* System bandwidth setting */
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVT, 0xD7, 0x06, 0x07);
+
+ /* Demod core latency setting */
+ if (priv->xtal == SONY_XTAL_24000) {
+ data[0] = 0x2C;
+ data[1] = 0xC2;
+ } else {
+ data[0] = 0x26;
+ data[1] = 0x3C;
+ }
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+ /* Notch filter setting */
+ data[0] = 0x00;
+ data[1] = 0x03;
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2);
+ break;
+ }
+
+ return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_i_band(
+ struct cxd2841er_priv *priv, u32 bandwidth)
+{
+ u32 iffreq, ifhz;
+ u8 data[3];
+
+ /* TRCG Nominal Rate */
+ static const u8 nominalRate8bw[3][5] = {
+ {0x00, 0x00, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
+ {0x11, 0xB8, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+ {0x00, 0x00, 0x00, 0x00, 0x00} /* 41MHz XTal */
+ };
+
+ static const u8 nominalRate7bw[3][5] = {
+ {0x00, 0x00, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
+ {0x14, 0x40, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+ {0x00, 0x00, 0x00, 0x00, 0x00} /* 41MHz XTal */
+ };
+
+ static const u8 nominalRate6bw[3][5] = {
+ {0x14, 0x2E, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
+ {0x17, 0xA0, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+ {0x14, 0x2E, 0x00, 0x00, 0x00} /* 41MHz XTal */
+ };
+
+ static const u8 itbCoef8bw[3][14] = {
+ {0x00}, /* 20.5MHz XTal */
+ {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, 0x29,
+ 0xA5, 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz Xtal */
+ {0x0}, /* 41MHz XTal */
+ };
+
+ static const u8 itbCoef7bw[3][14] = {
+ {0x00}, /* 20.5MHz XTal */
+ {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, 0x29,
+ 0xA2, 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz Xtal */
+ {0x00}, /* 41MHz XTal */
+ };
+
+ static const u8 itbCoef6bw[3][14] = {
+ {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00,
+ 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */
+ {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29,
+ 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz Xtal */
+ {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00,
+ 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 41MHz XTal */
+ };
+
+ dev_dbg(&priv->i2c->dev, "%s() bandwidth=%u\n", __func__, bandwidth);
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+
+ /* 20.5/41MHz Xtal support is not available
+ * on ISDB-T 7MHzBW and 8MHzBW
+ */
+ if (priv->xtal != SONY_XTAL_24000 && bandwidth > 6000000) {
+ dev_err(&priv->i2c->dev,
+ "%s(): bandwidth %d supported only for 24MHz xtal\n",
+ __func__, bandwidth);
+ return -EINVAL;
+ }
+
+ switch (bandwidth) {
+ case 8000000:
+ /* TRCG Nominal Rate */
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0x9F, nominalRate8bw[priv->xtal], 5);
+ /* Group delay equaliser settings for ASCOT tuners optimized */
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0xA6, itbCoef8bw[priv->xtal], 14);
+
+ /* IF freq setting */
+ ifhz = cxd2841er_get_if_hz(priv, 4750000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
+ data[0] = (u8) ((iffreq >> 16) & 0xff);
+ data[1] = (u8)((iffreq >> 8) & 0xff);
+ data[2] = (u8)(iffreq & 0xff);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+
+ /* System bandwidth setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x0, 0x7);
+
+ /* Demod core latency setting */
+ data[0] = 0x13;
+ data[1] = 0xFC;
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+ /* Acquisition optimization setting */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x03, 0x07);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x03);
+ break;
+ case 7000000:
+ /* TRCG Nominal Rate */
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0x9F, nominalRate7bw[priv->xtal], 5);
+ /* Group delay equaliser settings for ASCOT tuners optimized */
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0xA6, itbCoef7bw[priv->xtal], 14);
+
+ /* IF freq setting */
+ ifhz = cxd2841er_get_if_hz(priv, 4150000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
+ data[0] = (u8) ((iffreq >> 16) & 0xff);
+ data[1] = (u8)((iffreq >> 8) & 0xff);
+ data[2] = (u8)(iffreq & 0xff);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+
+ /* System bandwidth setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x02, 0x7);
+
+ /* Demod core latency setting */
+ data[0] = 0x1A;
+ data[1] = 0xFA;
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+ /* Acquisition optimization setting */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x03, 0x07);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x02);
+ break;
+ case 6000000:
+ /* TRCG Nominal Rate */
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0x9F, nominalRate6bw[priv->xtal], 5);
+ /* Group delay equaliser settings for ASCOT tuners optimized */
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(priv, I2C_SLVT,
+ 0xA6, itbCoef6bw[priv->xtal], 14);
+
+ /* IF freq setting */
+ ifhz = cxd2841er_get_if_hz(priv, 3550000);
+ iffreq = cxd2841er_calc_iffreq_xtal(priv->xtal, ifhz);
+ data[0] = (u8) ((iffreq >> 16) & 0xff);
+ data[1] = (u8)((iffreq >> 8) & 0xff);
+ data[2] = (u8)(iffreq & 0xff);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+
+ /* System bandwidth setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x04, 0x7);
+
+ /* Demod core latency setting */
+ if (priv->xtal == SONY_XTAL_24000) {
+ data[0] = 0x1F;
+ data[1] = 0x79;
+ } else {
+ data[0] = 0x1A;
+ data[1] = 0xE2;
+ }
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+ /* Acquisition optimization setting */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x07, 0x07);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x02);
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n",
+ __func__, bandwidth);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv,
+ u32 bandwidth)
+{
+ u8 bw7_8mhz_b10_a6[] = {
+ 0x2D, 0xC7, 0x04, 0xF4, 0x07, 0xC5, 0x2A, 0xB8,
+ 0x27, 0x9E, 0x27, 0xA4, 0x29, 0xAB };
+ u8 bw6mhz_b10_a6[] = {
+ 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8,
+ 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 };
+ u8 b10_b6[3];
+ u32 iffreq, ifhz;
+
+ if (bandwidth != 6000000 &&
+ bandwidth != 7000000 &&
+ bandwidth != 8000000) {
+ dev_info(&priv->i2c->dev, "%s(): unsupported bandwidth %d. Forcing 8Mhz!\n",
+ __func__, bandwidth);
+ bandwidth = 8000000;
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s() bw=%d\n", __func__, bandwidth);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ switch (bandwidth) {
+ case 8000000:
+ case 7000000:
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(
+ priv, I2C_SLVT, 0xa6,
+ bw7_8mhz_b10_a6, sizeof(bw7_8mhz_b10_a6));
+ ifhz = cxd2841er_get_if_hz(priv, 4900000);
+ iffreq = cxd2841er_calc_iffreq(ifhz);
+ break;
+ case 6000000:
+ if (priv->flags & CXD2841ER_ASCOT)
+ cxd2841er_write_regs(
+ priv, I2C_SLVT, 0xa6,
+ bw6mhz_b10_a6, sizeof(bw6mhz_b10_a6));
+ ifhz = cxd2841er_get_if_hz(priv, 3700000);
+ iffreq = cxd2841er_calc_iffreq(ifhz);
+ break;
+ default:
+ dev_err(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n",
+ __func__, bandwidth);
+ return -EINVAL;
+ }
+ /* <IF freq setting> */
+ b10_b6[0] = (u8) ((iffreq >> 16) & 0xff);
+ b10_b6[1] = (u8)((iffreq >> 8) & 0xff);
+ b10_b6[2] = (u8)(iffreq & 0xff);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6));
+ /* Set SLV-T Bank : 0x11 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
+ switch (bandwidth) {
+ case 8000000:
+ case 7000000:
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVT, 0xa3, 0x00, 0x1f);
+ break;
+ case 6000000:
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVT, 0xa3, 0x14, 0x1f);
+ break;
+ }
+ /* Set SLV-T Bank : 0x40 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+ switch (bandwidth) {
+ case 8000000:
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVT, 0x26, 0x0b, 0x0f);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x27, 0x3e);
+ break;
+ case 7000000:
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVT, 0x26, 0x09, 0x0f);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x27, 0xd6);
+ break;
+ case 6000000:
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVT, 0x26, 0x08, 0x0f);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x27, 0x6e);
+ break;
+ }
+ return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv,
+ u32 bandwidth)
+{
+ u8 data[2] = { 0x09, 0x54 };
+ u8 data24m[3] = {0xDC, 0x6C, 0x00};
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ cxd2841er_set_ts_clock_mode(priv, SYS_DVBT);
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* Set demod mode */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x01);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Enable demod clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
+ /* Disable RF level monitor */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
+ /* Enable ADC clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+ /* Enable ADC 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a);
+ /* Enable ADC 2 & 3 */
+ if (priv->xtal == SONY_XTAL_41000) {
+ data[0] = 0x0A;
+ data[1] = 0xD4;
+ }
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2);
+ /* Enable ADC 4 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ /* IFAGC gain settings */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x0c, 0x1f);
+ /* Set SLV-T Bank : 0x11 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
+ /* BBAGC TARGET level setting */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x50);
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ /* ASCOT setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5,
+ ((priv->flags & CXD2841ER_ASCOT) ? 0x01 : 0x00), 0x01);
+ /* Set SLV-T Bank : 0x18 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x18);
+ /* Pre-RS BER monitor setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x36, 0x40, 0x07);
+ /* FEC Auto Recovery setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x30, 0x01, 0x01);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x31, 0x01, 0x01);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* TSIF setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01);
+
+ if (priv->xtal == SONY_XTAL_24000) {
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xBF, 0x60);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x18);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x24, data24m, 3);
+ }
+
+ cxd2841er_sleep_tc_to_active_t_band(priv, bandwidth);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Disable HiZ Setting 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28);
+ /* Disable HiZ Setting 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00);
+ priv->state = STATE_ACTIVE_TC;
+ return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv,
+ u32 bandwidth)
+{
+ u8 data[MAX_WRITE_REGSIZE];
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ cxd2841er_set_ts_clock_mode(priv, SYS_DVBT2);
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* Set demod mode */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x02);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Enable demod clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
+ /* Disable RF level monitor */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x00);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
+ /* Enable ADC clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+ /* Enable ADC 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a);
+
+ if (priv->xtal == SONY_XTAL_41000) {
+ data[0] = 0x0A;
+ data[1] = 0xD4;
+ } else {
+ data[0] = 0x09;
+ data[1] = 0x54;
+ }
+
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2);
+ /* Enable ADC 4 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ /* IFAGC gain settings */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x0c, 0x1f);
+ /* Set SLV-T Bank : 0x11 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
+ /* BBAGC TARGET level setting */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x50);
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ /* ASCOT setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5,
+ ((priv->flags & CXD2841ER_ASCOT) ? 0x01 : 0x00), 0x01);
+ /* Set SLV-T Bank : 0x20 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
+ /* Acquisition optimization setting */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x8b, 0x3c);
+ /* Set SLV-T Bank : 0x2b */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x76, 0x20, 0x70);
+ /* Set SLV-T Bank : 0x23 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x23);
+ /* L1 Control setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xE6, 0x00, 0x03);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* TSIF setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01);
+ /* DVB-T2 initial setting */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x83, 0x10);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x86, 0x34);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9e, 0x09, 0x0f);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x9f, 0xd8);
+ /* Set SLV-T Bank : 0x2a */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2a);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x38, 0x04, 0x0f);
+ /* Set SLV-T Bank : 0x2b */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x20, 0x3f);
+
+ /* 24MHz Xtal setting */
+ if (priv->xtal == SONY_XTAL_24000) {
+ /* Set SLV-T Bank : 0x11 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
+ data[0] = 0xEB;
+ data[1] = 0x03;
+ data[2] = 0x3B;
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x33, data, 3);
+
+ /* Set SLV-T Bank : 0x20 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
+ data[0] = 0x5E;
+ data[1] = 0x5E;
+ data[2] = 0x47;
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x95, data, 3);
+
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x99, 0x18);
+
+ data[0] = 0x3F;
+ data[1] = 0xFF;
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+ /* Set SLV-T Bank : 0x24 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x24);
+ data[0] = 0x0B;
+ data[1] = 0x72;
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x34, data, 2);
+
+ data[0] = 0x93;
+ data[1] = 0xF3;
+ data[2] = 0x00;
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xD2, data, 3);
+
+ data[0] = 0x05;
+ data[1] = 0xB8;
+ data[2] = 0xD8;
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xDD, data, 3);
+
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xE0, 0x00);
+
+ /* Set SLV-T Bank : 0x25 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x25);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xED, 0x60);
+
+ /* Set SLV-T Bank : 0x27 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xFA, 0x34);
+
+ /* Set SLV-T Bank : 0x2B */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2B);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x4B, 0x2F);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x9E, 0x0E);
+
+ /* Set SLV-T Bank : 0x2D */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2D);
+ data[0] = 0x89;
+ data[1] = 0x89;
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x24, data, 2);
+
+ /* Set SLV-T Bank : 0x5E */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x5E);
+ data[0] = 0x24;
+ data[1] = 0x95;
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x8C, data, 2);
+ }
+
+ cxd2841er_sleep_tc_to_active_t2_band(priv, bandwidth);
+
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Disable HiZ Setting 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28);
+ /* Disable HiZ Setting 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00);
+ priv->state = STATE_ACTIVE_TC;
+ return 0;
+}
+
+/* ISDB-Tb part */
+static int cxd2841er_sleep_tc_to_active_i(struct cxd2841er_priv *priv,
+ u32 bandwidth)
+{
+ u8 data[2] = { 0x09, 0x54 };
+ u8 data24m[2] = {0x60, 0x00};
+ u8 data24m2[3] = {0xB7, 0x1B, 0x00};
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ cxd2841er_set_ts_clock_mode(priv, SYS_DVBT);
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* Set demod mode */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x06);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Enable demod clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
+ /* Enable RF level monitor */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x01);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x01);
+ /* Enable ADC clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+ /* Enable ADC 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a);
+ /* xtal freq 20.5MHz or 24M */
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2);
+ /* Enable ADC 4 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
+ /* ASCOT setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5,
+ ((priv->flags & CXD2841ER_ASCOT) ? 0x01 : 0x00), 0x01);
+ /* FEC Auto Recovery setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x30, 0x01, 0x01);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x31, 0x00, 0x01);
+ /* ISDB-T initial setting */
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x00, 0x01);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x00, 0x01);
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x69, 0x04, 0x07);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x6B, 0x03, 0x07);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9D, 0x50, 0xFF);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xD3, 0x06, 0x1F);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xED, 0x00, 0x01);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xE2, 0xCE, 0x80);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xF2, 0x13, 0x10);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xDE, 0x2E, 0x3F);
+ /* Set SLV-T Bank : 0x15 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xDE, 0x02, 0x03);
+ /* Set SLV-T Bank : 0x1E */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x1E);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x73, 0x68, 0xFF);
+ /* Set SLV-T Bank : 0x63 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x63);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x81, 0x00, 0x01);
+
+ /* for xtal 24MHz */
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xBF, data24m, 2);
+ /* Set SLV-T Bank : 0x60 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60);
+ cxd2841er_write_regs(priv, I2C_SLVT, 0xA8, data24m2, 3);
+
+ cxd2841er_sleep_tc_to_active_i_band(priv, bandwidth);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Disable HiZ Setting 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28);
+ /* Disable HiZ Setting 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00);
+ priv->state = STATE_ACTIVE_TC;
+ return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv,
+ u32 bandwidth)
+{
+ u8 data[2] = { 0x09, 0x54 };
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ cxd2841er_set_ts_clock_mode(priv, SYS_DVBC_ANNEX_A);
+ /* Set SLV-X Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+ /* Set demod mode */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x04);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Enable demod clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
+ /* Disable RF level monitor */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x00);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
+ /* Enable ADC clock */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+ /* Enable ADC 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a);
+ /* xtal freq 20.5MHz */
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2);
+ /* Enable ADC 4 */
+ cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ /* IFAGC gain settings */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd2, 0x09, 0x1f);
+ /* Set SLV-T Bank : 0x11 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
+ /* BBAGC TARGET level setting */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x6a, 0x48);
+ /* Set SLV-T Bank : 0x10 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ /* ASCOT setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5,
+ ((priv->flags & CXD2841ER_ASCOT) ? 0x01 : 0x00), 0x01);
+ /* Set SLV-T Bank : 0x40 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+ /* Demod setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc3, 0x00, 0x04);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* TSIF setting */
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01);
+
+ cxd2841er_sleep_tc_to_active_c_band(priv, bandwidth);
+ /* Set SLV-T Bank : 0x00 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ /* Disable HiZ Setting 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28);
+ /* Disable HiZ Setting 2 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00);
+ priv->state = STATE_ACTIVE_TC;
+ return 0;
+}
+
+static int cxd2841er_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
+{
+ enum fe_status status = 0;
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state == STATE_ACTIVE_S)
+ cxd2841er_read_status_s(fe, &status);
+ else if (priv->state == STATE_ACTIVE_TC)
+ cxd2841er_read_status_tc(fe, &status);
+
+ if (priv->state == STATE_ACTIVE_TC || priv->state == STATE_ACTIVE_S)
+ cxd2841er_read_signal_strength(fe);
+ else
+ p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ if (status & FE_HAS_LOCK) {
+ if (priv->stats_time &&
+ (!time_after(jiffies, priv->stats_time)))
+ return 0;
+
+ /* Prevent retrieving stats faster than once per second */
+ priv->stats_time = jiffies + msecs_to_jiffies(1000);
+
+ cxd2841er_read_snr(fe);
+ cxd2841er_read_ucblocks(fe);
+ cxd2841er_read_ber(fe);
+ } else {
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+ return 0;
+}
+
+static int cxd2841er_set_frontend_s(struct dvb_frontend *fe)
+{
+ int ret = 0, i, timeout, carr_offset;
+ enum fe_status status;
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u32 symbol_rate = p->symbol_rate/1000;
+
+ dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d xtal=%d\n",
+ __func__,
+ (p->delivery_system == SYS_DVBS ? "DVB-S" : "DVB-S2"),
+ p->frequency, symbol_rate, priv->xtal);
+
+ if (priv->flags & CXD2841ER_EARLY_TUNE)
+ cxd2841er_tuner_set(fe);
+
+ switch (priv->state) {
+ case STATE_SLEEP_S:
+ ret = cxd2841er_sleep_s_to_active_s(
+ priv, p->delivery_system, symbol_rate);
+ break;
+ case STATE_ACTIVE_S:
+ ret = cxd2841er_retune_active(priv, p);
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (ret) {
+ dev_dbg(&priv->i2c->dev, "%s(): tune failed\n", __func__);
+ goto done;
+ }
+
+ if (!(priv->flags & CXD2841ER_EARLY_TUNE))
+ cxd2841er_tuner_set(fe);
+
+ cxd2841er_tune_done(priv);
+ timeout = DIV_ROUND_UP(3000000, symbol_rate) + 150;
+
+ i = 0;
+ do {
+ usleep_range(CXD2841ER_DVBS_POLLING_INVL*1000,
+ (CXD2841ER_DVBS_POLLING_INVL + 2) * 1000);
+ cxd2841er_read_status_s(fe, &status);
+ if (status & FE_HAS_LOCK)
+ break;
+ i++;
+ } while (i < timeout / CXD2841ER_DVBS_POLLING_INVL);
+
+ if (status & FE_HAS_LOCK) {
+ if (cxd2841er_get_carrier_offset_s_s2(
+ priv, &carr_offset)) {
+ ret = -EINVAL;
+ goto done;
+ }
+ dev_dbg(&priv->i2c->dev, "%s(): carrier_offset=%d\n",
+ __func__, carr_offset);
+ }
+done:
+ /* Reset stats */
+ p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ /* Reset the wait for jiffies logic */
+ priv->stats_time = 0;
+
+ return ret;
+}
+
+static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe)
+{
+ int ret = 0, timeout;
+ enum fe_status status;
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ dev_dbg(&priv->i2c->dev, "%s() delivery_system=%d bandwidth_hz=%d\n",
+ __func__, p->delivery_system, p->bandwidth_hz);
+
+ if (priv->flags & CXD2841ER_EARLY_TUNE)
+ cxd2841er_tuner_set(fe);
+
+ /* deconfigure/put demod to sleep on delsys switch if active */
+ if (priv->state == STATE_ACTIVE_TC &&
+ priv->system != p->delivery_system) {
+ dev_dbg(&priv->i2c->dev, "%s(): old_delsys=%d, new_delsys=%d -> sleep\n",
+ __func__, priv->system, p->delivery_system);
+ cxd2841er_sleep_tc(fe);
+ }
+
+ if (p->delivery_system == SYS_DVBT) {
+ priv->system = SYS_DVBT;
+ switch (priv->state) {
+ case STATE_SLEEP_TC:
+ ret = cxd2841er_sleep_tc_to_active_t(
+ priv, p->bandwidth_hz);
+ break;
+ case STATE_ACTIVE_TC:
+ ret = cxd2841er_retune_active(priv, p);
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ ret = -EINVAL;
+ }
+ } else if (p->delivery_system == SYS_DVBT2) {
+ priv->system = SYS_DVBT2;
+ cxd2841er_dvbt2_set_plp_config(priv,
+ (int)(p->stream_id > 255), p->stream_id);
+ cxd2841er_dvbt2_set_profile(priv, DVBT2_PROFILE_BASE);
+ switch (priv->state) {
+ case STATE_SLEEP_TC:
+ ret = cxd2841er_sleep_tc_to_active_t2(priv,
+ p->bandwidth_hz);
+ break;
+ case STATE_ACTIVE_TC:
+ ret = cxd2841er_retune_active(priv, p);
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ ret = -EINVAL;
+ }
+ } else if (p->delivery_system == SYS_ISDBT) {
+ priv->system = SYS_ISDBT;
+ switch (priv->state) {
+ case STATE_SLEEP_TC:
+ ret = cxd2841er_sleep_tc_to_active_i(
+ priv, p->bandwidth_hz);
+ break;
+ case STATE_ACTIVE_TC:
+ ret = cxd2841er_retune_active(priv, p);
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ ret = -EINVAL;
+ }
+ } else if (p->delivery_system == SYS_DVBC_ANNEX_A ||
+ p->delivery_system == SYS_DVBC_ANNEX_C) {
+ priv->system = SYS_DVBC_ANNEX_A;
+ /* correct bandwidth */
+ if (p->bandwidth_hz != 6000000 &&
+ p->bandwidth_hz != 7000000 &&
+ p->bandwidth_hz != 8000000) {
+ p->bandwidth_hz = 8000000;
+ dev_dbg(&priv->i2c->dev, "%s(): forcing bandwidth to %d\n",
+ __func__, p->bandwidth_hz);
+ }
+
+ switch (priv->state) {
+ case STATE_SLEEP_TC:
+ ret = cxd2841er_sleep_tc_to_active_c(
+ priv, p->bandwidth_hz);
+ break;
+ case STATE_ACTIVE_TC:
+ ret = cxd2841er_retune_active(priv, p);
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ ret = -EINVAL;
+ }
+ } else {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): invalid delivery system %d\n",
+ __func__, p->delivery_system);
+ ret = -EINVAL;
+ }
+ if (ret)
+ goto done;
+
+ if (!(priv->flags & CXD2841ER_EARLY_TUNE))
+ cxd2841er_tuner_set(fe);
+
+ cxd2841er_tune_done(priv);
+
+ if (priv->flags & CXD2841ER_NO_WAIT_LOCK)
+ goto done;
+
+ timeout = 2500;
+ while (timeout > 0) {
+ ret = cxd2841er_read_status_tc(fe, &status);
+ if (ret)
+ goto done;
+ if (status & FE_HAS_LOCK)
+ break;
+ msleep(20);
+ timeout -= 20;
+ }
+ if (timeout < 0)
+ dev_dbg(&priv->i2c->dev,
+ "%s(): LOCK wait timeout\n", __func__);
+done:
+ return ret;
+}
+
+static int cxd2841er_tune_s(struct dvb_frontend *fe,
+ bool re_tune,
+ unsigned int mode_flags,
+ unsigned int *delay,
+ enum fe_status *status)
+{
+ int ret, carrier_offset;
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ dev_dbg(&priv->i2c->dev, "%s() re_tune=%d\n", __func__, re_tune);
+ if (re_tune) {
+ ret = cxd2841er_set_frontend_s(fe);
+ if (ret)
+ return ret;
+ cxd2841er_read_status_s(fe, status);
+ if (*status & FE_HAS_LOCK) {
+ if (cxd2841er_get_carrier_offset_s_s2(
+ priv, &carrier_offset))
+ return -EINVAL;
+ p->frequency += carrier_offset;
+ ret = cxd2841er_set_frontend_s(fe);
+ if (ret)
+ return ret;
+ }
+ }
+ *delay = HZ / 5;
+ return cxd2841er_read_status_s(fe, status);
+}
+
+static int cxd2841er_tune_tc(struct dvb_frontend *fe,
+ bool re_tune,
+ unsigned int mode_flags,
+ unsigned int *delay,
+ enum fe_status *status)
+{
+ int ret, carrier_offset;
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ dev_dbg(&priv->i2c->dev, "%s(): re_tune %d bandwidth=%d\n", __func__,
+ re_tune, p->bandwidth_hz);
+ if (re_tune) {
+ ret = cxd2841er_set_frontend_tc(fe);
+ if (ret)
+ return ret;
+ cxd2841er_read_status_tc(fe, status);
+ if (*status & FE_HAS_LOCK) {
+ switch (priv->system) {
+ case SYS_ISDBT:
+ ret = cxd2841er_get_carrier_offset_i(
+ priv, p->bandwidth_hz,
+ &carrier_offset);
+ if (ret)
+ return ret;
+ break;
+ case SYS_DVBT:
+ ret = cxd2841er_get_carrier_offset_t(
+ priv, p->bandwidth_hz,
+ &carrier_offset);
+ if (ret)
+ return ret;
+ break;
+ case SYS_DVBT2:
+ ret = cxd2841er_get_carrier_offset_t2(
+ priv, p->bandwidth_hz,
+ &carrier_offset);
+ if (ret)
+ return ret;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2841er_get_carrier_offset_c(
+ priv, &carrier_offset);
+ if (ret)
+ return ret;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev,
+ "%s(): invalid delivery system %d\n",
+ __func__, priv->system);
+ return -EINVAL;
+ }
+ dev_dbg(&priv->i2c->dev, "%s(): carrier offset %d\n",
+ __func__, carrier_offset);
+ p->frequency += carrier_offset;
+ ret = cxd2841er_set_frontend_tc(fe);
+ if (ret)
+ return ret;
+ }
+ }
+ *delay = HZ / 5;
+ return cxd2841er_read_status_tc(fe, status);
+}
+
+static int cxd2841er_sleep_s(struct dvb_frontend *fe)
+{
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ cxd2841er_active_s_to_sleep_s(fe->demodulator_priv);
+ cxd2841er_sleep_s_to_shutdown(fe->demodulator_priv);
+ return 0;
+}
+
+static int cxd2841er_sleep_tc(struct dvb_frontend *fe)
+{
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+
+ if (priv->state == STATE_ACTIVE_TC) {
+ switch (priv->system) {
+ case SYS_DVBT:
+ cxd2841er_active_t_to_sleep_tc(priv);
+ break;
+ case SYS_DVBT2:
+ cxd2841er_active_t2_to_sleep_tc(priv);
+ break;
+ case SYS_ISDBT:
+ cxd2841er_active_i_to_sleep_tc(priv);
+ break;
+ case SYS_DVBC_ANNEX_A:
+ cxd2841er_active_c_to_sleep_tc(priv);
+ break;
+ default:
+ dev_warn(&priv->i2c->dev,
+ "%s(): unknown delivery system %d\n",
+ __func__, priv->system);
+ }
+ }
+ if (priv->state != STATE_SLEEP_TC) {
+ dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cxd2841er_shutdown_tc(struct dvb_frontend *fe)
+{
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+
+ if (!cxd2841er_sleep_tc(fe))
+ cxd2841er_sleep_tc_to_shutdown(priv);
+ return 0;
+}
+
+static int cxd2841er_send_burst(struct dvb_frontend *fe,
+ enum fe_sec_mini_cmd burst)
+{
+ u8 data;
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s(): burst mode %s\n", __func__,
+ (burst == SEC_MINI_A ? "A" : "B"));
+ if (priv->state != STATE_SLEEP_S &&
+ priv->state != STATE_ACTIVE_S) {
+ dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ data = (burst == SEC_MINI_A ? 0 : 1);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x34, 0x01);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x35, data);
+ return 0;
+}
+
+static int cxd2841er_set_tone(struct dvb_frontend *fe,
+ enum fe_sec_tone_mode tone)
+{
+ u8 data;
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s(): tone %s\n", __func__,
+ (tone == SEC_TONE_ON ? "On" : "Off"));
+ if (priv->state != STATE_SLEEP_S &&
+ priv->state != STATE_ACTIVE_S) {
+ dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ data = (tone == SEC_TONE_ON ? 1 : 0);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x36, data);
+ return 0;
+}
+
+static int cxd2841er_send_diseqc_msg(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *cmd)
+{
+ int i;
+ u8 data[12];
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ if (priv->state != STATE_SLEEP_S &&
+ priv->state != STATE_ACTIVE_S) {
+ dev_err(&priv->i2c->dev, "%s(): invalid demod state %d\n",
+ __func__, priv->state);
+ return -EINVAL;
+ }
+ dev_dbg(&priv->i2c->dev,
+ "%s(): cmd->len %d\n", __func__, cmd->msg_len);
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xbb);
+ /* DiDEqC enable */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x33, 0x01);
+ /* cmd1 length & data */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x3d, cmd->msg_len);
+ memset(data, 0, sizeof(data));
+ for (i = 0; i < cmd->msg_len && i < sizeof(data); i++)
+ data[i] = cmd->msg[i];
+ cxd2841er_write_regs(priv, I2C_SLVT, 0x3e, data, sizeof(data));
+ /* repeat count for cmd1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x37, 1);
+ /* repeat count for cmd2: always 0 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x38, 0);
+ /* start transmit */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x32, 0x01);
+ /* wait for 1 sec timeout */
+ for (i = 0; i < 50; i++) {
+ cxd2841er_read_reg(priv, I2C_SLVT, 0x10, data);
+ if (!data[0]) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): DiSEqC cmd has been sent\n", __func__);
+ return 0;
+ }
+ msleep(20);
+ }
+ dev_dbg(&priv->i2c->dev,
+ "%s(): DiSEqC cmd transmit timeout\n", __func__);
+ return -ETIMEDOUT;
+}
+
+static void cxd2841er_release(struct dvb_frontend *fe)
+{
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ kfree(priv);
+}
+
+static int cxd2841er_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s(): enable=%d\n", __func__, enable);
+ cxd2841er_set_reg_bits(
+ priv, I2C_SLVX, 0x8, (enable ? 0x01 : 0x00), 0x01);
+ return 0;
+}
+
+static enum dvbfe_algo cxd2841er_get_algo(struct dvb_frontend *fe)
+{
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ return DVBFE_ALGO_HW;
+}
+
+static void cxd2841er_init_stats(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ p->strength.len = 1;
+ p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ p->cnr.len = 1;
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.len = 1;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.len = 1;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.len = 1;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+}
+
+
+static int cxd2841er_init_s(struct dvb_frontend *fe)
+{
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+
+ /* sanity. force demod to SHUTDOWN state */
+ if (priv->state == STATE_SLEEP_S) {
+ dev_dbg(&priv->i2c->dev, "%s() forcing sleep->shutdown\n",
+ __func__);
+ cxd2841er_sleep_s_to_shutdown(priv);
+ } else if (priv->state == STATE_ACTIVE_S) {
+ dev_dbg(&priv->i2c->dev, "%s() forcing active->sleep->shutdown\n",
+ __func__);
+ cxd2841er_active_s_to_sleep_s(priv);
+ cxd2841er_sleep_s_to_shutdown(priv);
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ cxd2841er_shutdown_to_sleep_s(priv);
+ /* SONY_DEMOD_CONFIG_SAT_IFAGCNEG set to 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xb9, 0x01, 0x01);
+
+ cxd2841er_init_stats(fe);
+
+ return 0;
+}
+
+static int cxd2841er_init_tc(struct dvb_frontend *fe)
+{
+ struct cxd2841er_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ dev_dbg(&priv->i2c->dev, "%s() bandwidth_hz=%d\n",
+ __func__, p->bandwidth_hz);
+ cxd2841er_shutdown_to_sleep_tc(priv);
+ /* SONY_DEMOD_CONFIG_IFAGCNEG = 1 (0 for NO_AGCNEG */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcb,
+ ((priv->flags & CXD2841ER_NO_AGCNEG) ? 0x00 : 0x40), 0x40);
+ /* SONY_DEMOD_CONFIG_IFAGC_ADC_FS = 0 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0xcd, 0x50);
+ /* SONY_DEMOD_CONFIG_PARALLEL_SEL = 1 */
+ cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4,
+ ((priv->flags & CXD2841ER_TS_SERIAL) ? 0x80 : 0x00), 0x80);
+
+ /* clear TSCFG bits 3+4 */
+ if (priv->flags & CXD2841ER_TSBITS)
+ cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4, 0x00, 0x18);
+
+ cxd2841er_init_stats(fe);
+
+ return 0;
+}
+
+static const struct dvb_frontend_ops cxd2841er_dvbs_s2_ops;
+static struct dvb_frontend_ops cxd2841er_t_c_ops;
+
+static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg,
+ struct i2c_adapter *i2c,
+ u8 system)
+{
+ u8 chip_id = 0;
+ const char *type;
+ const char *name;
+ struct cxd2841er_priv *priv = NULL;
+
+ /* allocate memory for the internal state */
+ priv = kzalloc(sizeof(struct cxd2841er_priv), GFP_KERNEL);
+ if (!priv)
+ return NULL;
+ priv->i2c = i2c;
+ priv->config = cfg;
+ priv->i2c_addr_slvx = (cfg->i2c_addr + 4) >> 1;
+ priv->i2c_addr_slvt = (cfg->i2c_addr) >> 1;
+ priv->xtal = cfg->xtal;
+ priv->flags = cfg->flags;
+ priv->frontend.demodulator_priv = priv;
+ dev_info(&priv->i2c->dev,
+ "%s(): I2C adapter %p SLVX addr %x SLVT addr %x\n",
+ __func__, priv->i2c,
+ priv->i2c_addr_slvx, priv->i2c_addr_slvt);
+ chip_id = cxd2841er_chip_id(priv);
+ switch (chip_id) {
+ case CXD2837ER_CHIP_ID:
+ snprintf(cxd2841er_t_c_ops.info.name, 128,
+ "Sony CXD2837ER DVB-T/T2/C demodulator");
+ name = "CXD2837ER";
+ type = "C/T/T2";
+ break;
+ case CXD2838ER_CHIP_ID:
+ snprintf(cxd2841er_t_c_ops.info.name, 128,
+ "Sony CXD2838ER ISDB-T demodulator");
+ cxd2841er_t_c_ops.delsys[0] = SYS_ISDBT;
+ cxd2841er_t_c_ops.delsys[1] = SYS_UNDEFINED;
+ cxd2841er_t_c_ops.delsys[2] = SYS_UNDEFINED;
+ name = "CXD2838ER";
+ type = "ISDB-T";
+ break;
+ case CXD2841ER_CHIP_ID:
+ snprintf(cxd2841er_t_c_ops.info.name, 128,
+ "Sony CXD2841ER DVB-T/T2/C demodulator");
+ name = "CXD2841ER";
+ type = "T/T2/C/ISDB-T";
+ break;
+ case CXD2843ER_CHIP_ID:
+ snprintf(cxd2841er_t_c_ops.info.name, 128,
+ "Sony CXD2843ER DVB-T/T2/C/C2 demodulator");
+ name = "CXD2843ER";
+ type = "C/C2/T/T2";
+ break;
+ case CXD2854ER_CHIP_ID:
+ snprintf(cxd2841er_t_c_ops.info.name, 128,
+ "Sony CXD2854ER DVB-T/T2/C and ISDB-T demodulator");
+ cxd2841er_t_c_ops.delsys[3] = SYS_ISDBT;
+ name = "CXD2854ER";
+ type = "C/C2/T/T2/ISDB-T";
+ break;
+ default:
+ dev_err(&priv->i2c->dev, "%s(): invalid chip ID 0x%02x\n",
+ __func__, chip_id);
+ priv->frontend.demodulator_priv = NULL;
+ kfree(priv);
+ return NULL;
+ }
+
+ /* create dvb_frontend */
+ if (system == SYS_DVBS) {
+ memcpy(&priv->frontend.ops,
+ &cxd2841er_dvbs_s2_ops,
+ sizeof(struct dvb_frontend_ops));
+ type = "S/S2";
+ } else {
+ memcpy(&priv->frontend.ops,
+ &cxd2841er_t_c_ops,
+ sizeof(struct dvb_frontend_ops));
+ }
+
+ dev_info(&priv->i2c->dev,
+ "%s(): attaching %s DVB-%s frontend\n",
+ __func__, name, type);
+ dev_info(&priv->i2c->dev, "%s(): chip ID 0x%02x OK.\n",
+ __func__, chip_id);
+ return &priv->frontend;
+}
+
+struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg,
+ struct i2c_adapter *i2c)
+{
+ return cxd2841er_attach(cfg, i2c, SYS_DVBS);
+}
+EXPORT_SYMBOL_GPL(cxd2841er_attach_s);
+
+struct dvb_frontend *cxd2841er_attach_t_c(struct cxd2841er_config *cfg,
+ struct i2c_adapter *i2c)
+{
+ return cxd2841er_attach(cfg, i2c, 0);
+}
+EXPORT_SYMBOL_GPL(cxd2841er_attach_t_c);
+
+static const struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = {
+ .delsys = { SYS_DVBS, SYS_DVBS2 },
+ .info = {
+ .name = "Sony CXD2841ER DVB-S/S2 demodulator",
+ .frequency_min_hz = 500 * MHz,
+ .frequency_max_hz = 2500 * MHz,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .symbol_rate_tolerance = 500,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK,
+ },
+ .init = cxd2841er_init_s,
+ .sleep = cxd2841er_sleep_s,
+ .release = cxd2841er_release,
+ .set_frontend = cxd2841er_set_frontend_s,
+ .get_frontend = cxd2841er_get_frontend,
+ .read_status = cxd2841er_read_status_s,
+ .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl,
+ .get_frontend_algo = cxd2841er_get_algo,
+ .set_tone = cxd2841er_set_tone,
+ .diseqc_send_burst = cxd2841er_send_burst,
+ .diseqc_send_master_cmd = cxd2841er_send_diseqc_msg,
+ .tune = cxd2841er_tune_s
+};
+
+static struct dvb_frontend_ops cxd2841er_t_c_ops = {
+ .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A },
+ .info = {
+ .name = "", /* will set in attach function */
+ .caps = FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ 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_HIERARCHY_AUTO |
+ FE_CAN_MUTE_TS |
+ FE_CAN_2G_MODULATION,
+ .frequency_min_hz = 42 * MHz,
+ .frequency_max_hz = 1002 * MHz,
+ .symbol_rate_min = 870000,
+ .symbol_rate_max = 11700000
+ },
+ .init = cxd2841er_init_tc,
+ .sleep = cxd2841er_shutdown_tc,
+ .release = cxd2841er_release,
+ .set_frontend = cxd2841er_set_frontend_tc,
+ .get_frontend = cxd2841er_get_frontend,
+ .read_status = cxd2841er_read_status_tc,
+ .tune = cxd2841er_tune_tc,
+ .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl,
+ .get_frontend_algo = cxd2841er_get_algo
+};
+
+MODULE_DESCRIPTION("Sony CXD2837/38/41/43/54ER DVB-C/C2/T/T2/S/S2 demodulator driver");
+MODULE_AUTHOR("Sergey Kozlov <serjk@netup.ru>, Abylay Ospan <aospan@netup.ru>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/cxd2841er.h b/drivers/media/dvb-frontends/cxd2841er.h
new file mode 100644
index 000000000000..eb5467f581b4
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2841er.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * cxd2841er.h
+ *
+ * Sony CXD2441ER digital demodulator driver public definitions
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
+ */
+
+#ifndef CXD2841ER_H
+#define CXD2841ER_H
+
+#include <linux/dvb/frontend.h>
+
+#define CXD2841ER_USE_GATECTRL 1 /* bit 0 */
+#define CXD2841ER_AUTO_IFHZ 2 /* bit 1 */
+#define CXD2841ER_TS_SERIAL 4 /* bit 2 */
+#define CXD2841ER_ASCOT 8 /* bit 3 */
+#define CXD2841ER_EARLY_TUNE 16 /* bit 4 */
+#define CXD2841ER_NO_WAIT_LOCK 32 /* bit 5 */
+#define CXD2841ER_NO_AGCNEG 64 /* bit 6 */
+#define CXD2841ER_TSBITS 128 /* bit 7 */
+
+enum cxd2841er_xtal {
+ SONY_XTAL_20500, /* 20.5 MHz */
+ SONY_XTAL_24000, /* 24 MHz */
+ SONY_XTAL_41000 /* 41 MHz */
+};
+
+struct cxd2841er_config {
+ u8 i2c_addr;
+ enum cxd2841er_xtal xtal;
+ u32 flags;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_CXD2841ER)
+extern struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg,
+ struct i2c_adapter *i2c);
+
+extern struct dvb_frontend *cxd2841er_attach_t_c(struct cxd2841er_config *cfg,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *cxd2841er_attach_s(
+ struct cxd2841er_config *cfg,
+ struct i2c_adapter *i2c)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline struct dvb_frontend *cxd2841er_attach_t_c(
+ struct cxd2841er_config *cfg, struct i2c_adapter *i2c)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+#endif
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2841er_priv.h b/drivers/media/dvb-frontends/cxd2841er_priv.h
new file mode 100644
index 000000000000..e030f456e21b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2841er_priv.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * cxd2841er_priv.h
+ *
+ * Sony CXD2441ER digital demodulator driver internal definitions
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
+ */
+
+#ifndef CXD2841ER_PRIV_H
+#define CXD2841ER_PRIV_H
+
+#define I2C_SLVX 0
+#define I2C_SLVT 1
+
+#define CXD2837ER_CHIP_ID 0xb1
+#define CXD2838ER_CHIP_ID 0xb0
+#define CXD2841ER_CHIP_ID 0xa7
+#define CXD2843ER_CHIP_ID 0xa4
+#define CXD2854ER_CHIP_ID 0xc1
+
+#define CXD2841ER_DVBS_POLLING_INVL 10
+
+struct cxd2841er_cnr_data {
+ u32 value;
+ int cnr_x1000;
+};
+
+enum cxd2841er_dvbt2_profile_t {
+ DVBT2_PROFILE_ANY = 0,
+ DVBT2_PROFILE_BASE = 1,
+ DVBT2_PROFILE_LITE = 2
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/Kconfig b/drivers/media/dvb-frontends/cxd2880/Kconfig
new file mode 100644
index 000000000000..94a8e0b936b9
--- /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.
diff --git a/drivers/media/dvb-frontends/cxd2880/Makefile b/drivers/media/dvb-frontends/cxd2880/Makefile
new file mode 100644
index 000000000000..646598b556d5
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/Makefile
@@ -0,0 +1,16 @@
+# 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
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..9dc15a5a9683
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
@@ -0,0 +1,20 @@
+/* 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/bits.h>
+#include <linux/string.h>
+
+int cxd2880_convert2s_complement(u32 value, u32 bitlen);
+
+#endif
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..aba59400859e
--- /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 = 0;
+ struct cxd2880_spi *spi = NULL;
+ u8 send_data[6];
+ u8 *read_data_top = data;
+
+ if (!io || !io->if_object || !data)
+ return -EINVAL;
+
+ if (sub_address + size > 0x100)
+ return -EINVAL;
+
+ spi = io->if_object;
+
+ if (tgt == CXD2880_IO_TGT_SYS)
+ send_data[0] = 0x0b;
+ else
+ send_data[0] = 0x0a;
+
+ send_data[3] = 0;
+ send_data[4] = 0;
+ send_data[5] = 0;
+
+ while (size > 0) {
+ send_data[1] = sub_address;
+ if (size > 255)
+ send_data[2] = 255;
+ else
+ send_data[2] = size;
+
+ ret =
+ spi->write_read(spi, send_data, sizeof(send_data),
+ read_data_top, send_data[2]);
+ if (ret)
+ return ret;
+
+ sub_address += send_data[2];
+ read_data_top += send_data[2];
+ size -= send_data[2];
+ }
+
+ return ret;
+}
+
+static int cxd2880_io_spi_write_reg(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ u8 sub_address,
+ const u8 *data, u32 size)
+{
+ int ret = 0;
+ struct cxd2880_spi *spi = NULL;
+ u8 send_data[BURST_WRITE_MAX + 4];
+ const u8 *write_data_top = data;
+
+ if (!io || !io->if_object || !data)
+ return -EINVAL;
+
+ if (size > BURST_WRITE_MAX)
+ return -EINVAL;
+
+ if (sub_address + size > 0x100)
+ return -EINVAL;
+
+ spi = io->if_object;
+
+ if (tgt == CXD2880_IO_TGT_SYS)
+ send_data[0] = 0x0f;
+ else
+ send_data[0] = 0x0e;
+
+ while (size > 0) {
+ send_data[1] = sub_address;
+ if (size > 255)
+ send_data[2] = 255;
+ else
+ send_data[2] = size;
+
+ memcpy(&send_data[3], write_data_top, send_data[2]);
+
+ if (tgt == CXD2880_IO_TGT_SYS) {
+ send_data[3 + send_data[2]] = 0x00;
+ ret = spi->write(spi, send_data, send_data[2] + 4);
+ } else {
+ ret = spi->write(spi, send_data, send_data[2] + 3);
+ }
+ if (ret)
+ return ret;
+
+ sub_address += send_data[2];
+ write_data_top += send_data[2];
+ size -= send_data[2];
+ }
+
+ return ret;
+}
+
+int cxd2880_io_spi_create(struct cxd2880_io *io,
+ struct cxd2880_spi *spi, u8 slave_select)
+{
+ if (!io || !spi)
+ return -EINVAL;
+
+ io->read_regs = cxd2880_io_spi_read_reg;
+ io->write_regs = cxd2880_io_spi_write_reg;
+ io->write_reg = cxd2880_io_common_write_one_reg;
+ io->if_object = spi;
+ io->i2c_address_sys = 0;
+ io->i2c_address_demod = 0;
+ io->slave_select = slave_select;
+
+ return 0;
+}
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_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_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_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_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
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
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 */
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..0a1f3899d72c
--- /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 <media/dvb_frontend.h>
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt2.h"
+
+static const struct cxd2880_reg_value p_init1_seq[] = {
+ {0x11, 0x16}, {0x00, 0x10},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq1[] = {
+ {0x4f, 0x18}, {0x61, 0x00}, {0x71, 0x00}, {0x9d, 0x01},
+ {0x7d, 0x02}, {0x8f, 0x01}, {0x8b, 0xc6}, {0x9a, 0x03},
+ {0x1c, 0x00},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq2[] = {
+ {0xb9, 0x07}, {0x33, 0x01}, {0xc1, 0x01}, {0xc4, 0x1e},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq3[] = {
+ {0x00, 0x10}, {0x51, 0x01}, {0xc5, 0x07}, {0x00, 0x11},
+ {0x70, 0xe9}, {0x76, 0x0a}, {0x78, 0x32}, {0x7a, 0x46},
+ {0x7c, 0x86}, {0x7e, 0xa4}, {0x00, 0x10}, {0xe1, 0x01},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq4[] = {
+ {0x15, 0x00}, {0x00, 0x16}
+};
+
+static const struct cxd2880_reg_value rf_init1_seq5[] = {
+ {0x00, 0x00}, {0x25, 0x00}
+};
+
+static const struct cxd2880_reg_value rf_init1_seq6[] = {
+ {0x02, 0x00}, {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe1},
+ {0x8f, 0x16}, {0x67, 0x60}, {0x6a, 0x0f}, {0x6c, 0x17}
+};
+
+static const struct cxd2880_reg_value rf_init1_seq7[] = {
+ {0x00, 0xe2}, {0x41, 0xa0}, {0x4b, 0x68}, {0x00, 0x00},
+ {0x21, 0x00}, {0x10, 0x01},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq8[] = {
+ {0x00, 0x10}, {0x25, 0x01},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq9[] = {
+ {0x00, 0x10}, {0x14, 0x01}, {0x00, 0x00}, {0x26, 0x00},
+};
+
+static const struct cxd2880_reg_value rf_init2_seq1[] = {
+ {0x00, 0x14}, {0x1b, 0x01},
+};
+
+static const struct cxd2880_reg_value rf_init2_seq2[] = {
+ {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe1}, {0xd3, 0x00},
+ {0x00, 0x00}, {0x21, 0x00},
+};
+
+static const struct cxd2880_reg_value x_tune1_seq1[] = {
+ {0x00, 0x00}, {0x10, 0x01},
+};
+
+static const struct cxd2880_reg_value x_tune1_seq2[] = {
+ {0x62, 0x00}, {0x00, 0x15},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq1[] = {
+ {0x00, 0x1a}, {0x29, 0x01},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq2[] = {
+ {0x62, 0x01}, {0x00, 0x11}, {0x2d, 0x00}, {0x2f, 0x00},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq3[] = {
+ {0x00, 0x00}, {0x10, 0x00}, {0x21, 0x01},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq4[] = {
+ {0x00, 0xe1}, {0x8a, 0x87},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq5[] = {
+ {0x00, 0x00}, {0x21, 0x00},
+};
+
+static const struct cxd2880_reg_value x_tune3_seq[] = {
+ {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe2}, {0x41, 0xa0},
+ {0x00, 0x00}, {0x21, 0x00}, {0xfe, 0x01},
+};
+
+static const struct cxd2880_reg_value x_tune4_seq[] = {
+ {0x00, 0x00}, {0xfe, 0x01},
+};
+
+static const struct cxd2880_reg_value x_sleep1_seq[] = {
+ {0x00, 0x00}, {0x57, 0x03},
+};
+
+static const struct cxd2880_reg_value x_sleep2_seq1[] = {
+ {0x00, 0x2d}, {0xb1, 0x01},
+};
+
+static const struct cxd2880_reg_value x_sleep2_seq2[] = {
+ {0x00, 0x10}, {0xf4, 0x00}, {0xf3, 0x00}, {0xf2, 0x00},
+ {0xf1, 0x00}, {0xf0, 0x00}, {0xef, 0x00},
+};
+
+static const struct cxd2880_reg_value x_sleep3_seq[] = {
+ {0x00, 0x00}, {0xfd, 0x00},
+};
+
+static const struct cxd2880_reg_value x_sleep4_seq[] = {
+ {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe2}, {0x41, 0x00},
+ {0x00, 0x00}, {0x21, 0x00},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq1[] = {
+ {0x00, 0x10}, {0x29, 0x01}, {0x28, 0x01}, {0x27, 0x01},
+ {0x26, 0x01},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq2[] = {
+ {0x00, 0x00}, {0x10, 0x00},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq3[] = {
+ {0x00, 0x00}, {0x27, 0x00}, {0x22, 0x01},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq4[] = {
+ {0x00, 0x00}, {0x27, 0x01},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq5[] = {
+ {0x00, 0x00}, {0x10, 0x01},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq1[] = {
+ {0x00, 0x10}, {0x29, 0x01}, {0x28, 0x01}, {0x27, 0x01},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq2[] = {
+ {0x00, 0x00}, {0x10, 0x00},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq3[] = {
+ {0x00, 0x00}, {0x27, 0x00}, {0x25, 0x01},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq4[] = {
+ {0x00, 0x00}, {0x2a, 0x00},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq5[] = {
+ {0x00, 0x00}, {0x25, 0x00},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq6[] = {
+ {0x00, 0x00}, {0x27, 0x01},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq7[] = {
+ {0x00, 0x00}, {0x10, 0x01},
+};
+
+static const struct cxd2880_reg_value set_ts_pin_seq[] = {
+ {0x50, 0x3f}, {0x52, 0x1f},
+
+};
+
+static const struct cxd2880_reg_value set_ts_output_seq1[] = {
+ {0x00, 0x00}, {0x52, 0x00},
+};
+
+static const struct cxd2880_reg_value set_ts_output_seq2[] = {
+ {0x00, 0x00}, {0xc3, 0x00},
+
+};
+
+static const struct cxd2880_reg_value set_ts_output_seq3[] = {
+ {0x00, 0x00}, {0xc3, 0x01},
+
+};
+
+static const struct cxd2880_reg_value set_ts_output_seq4[] = {
+ {0x00, 0x00}, {0x52, 0x1f},
+
+};
+
+static int p_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data = 0;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE ||
+ tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (tnr_dmd->create_param.ts_output_if) {
+ case CXD2880_TNRDMD_TSOUT_IF_TS:
+ data = 0x00;
+ break;
+ case CXD2880_TNRDMD_TSOUT_IF_SPI:
+ data = 0x01;
+ break;
+ case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+ data = 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x10, data);
+ if (ret)
+ return ret;
+ }
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ p_init1_seq,
+ ARRAY_SIZE(p_init1_seq));
+ if (ret)
+ return ret;
+
+ switch (tnr_dmd->chip_id) {
+ case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X:
+ data = 0x1a;
+ break;
+ case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11:
+ data = 0x16;
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x10, data);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->create_param.en_internal_ldo)
+ data = 0x01;
+ else
+ data = 0x00;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x11, data);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x13, data);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x12, data);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ switch (tnr_dmd->chip_id) {
+ case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X:
+ data = 0x01;
+ break;
+ case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11:
+ data = 0x00;
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x69, data);
+}
+
+static int p_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[6] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ data[0] = tnr_dmd->create_param.xosc_cap;
+ data[1] = tnr_dmd->create_param.xosc_i;
+ switch (tnr_dmd->create_param.xtal_share_type) {
+ case CXD2880_TNRDMD_XTAL_SHARE_NONE:
+ data[2] = 0x01;
+ data[3] = 0x00;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_EXTREF:
+ data[2] = 0x00;
+ data[3] = 0x00;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_MASTER:
+ data[2] = 0x01;
+ data[3] = 0x01;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_SLAVE:
+ data[2] = 0x00;
+ data[3] = 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+ data[4] = 0x06;
+ data[5] = 0x00;
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x13, data, 6);
+}
+
+static int p_init3(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[2] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ switch (tnr_dmd->diver_mode) {
+ case CXD2880_TNRDMD_DIVERMODE_SINGLE:
+ data[0] = 0x00;
+ break;
+ case CXD2880_TNRDMD_DIVERMODE_MAIN:
+ data[0] = 0x03;
+ break;
+ case CXD2880_TNRDMD_DIVERMODE_SUB:
+ data[0] = 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ data[1] = 0x01;
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x1f, data, 2);
+}
+
+static int rf_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[8] = { 0 };
+ static const u8 rf_init1_cdata1[40] = {
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03,
+ 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x04, 0x04, 0x04
+ };
+
+ static const u8 rf_init1_cdata2[5] = {0xff, 0x00, 0x00, 0x00, 0x00};
+ static const u8 rf_init1_cdata3[80] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x02, 0x00, 0x63, 0x00, 0x00,
+ 0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x09,
+ 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0d, 0x00,
+ 0x0d, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f,
+ 0x00, 0x10, 0x00, 0x79, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01,
+ 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
+ 0x04, 0x00, 0x04, 0x00, 0x06, 0x00, 0x05,
+ 0x00, 0x07, 0x00, 0x07, 0x00, 0x08, 0x00,
+ 0x0a, 0x03, 0xe0
+ };
+
+ static const u8 rf_init1_cdata4[8] = {
+ 0x20, 0x20, 0x30, 0x41, 0x50, 0x5f, 0x6f, 0x80
+ };
+
+ static const u8 rf_init1_cdata5[50] = {
+ 0x00, 0x09, 0x00, 0x08, 0x00, 0x07, 0x00,
+ 0x06, 0x00, 0x05, 0x00, 0x03, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0c,
+ 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0f, 0x00,
+ 0x0e, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x0f,
+ 0x00, 0x0e, 0x00, 0x10, 0x00, 0x0f, 0x00,
+ 0x0e
+ };
+
+ u8 addr = 0;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ data[0] = 0x01;
+ data[1] = 0x00;
+ data[2] = 0x01;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x21, data, 3);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+ data[0] = 0x01;
+ data[1] = 0x01;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x17, data, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->create_param.stationary_use) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x1a, 0x06);
+ if (ret)
+ return ret;
+ }
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init1_seq1,
+ ARRAY_SIZE(rf_init1_seq1));
+ if (ret)
+ return ret;
+
+ data[0] = 0x00;
+ if (tnr_dmd->create_param.is_cxd2881gg &&
+ tnr_dmd->create_param.xtal_share_type ==
+ CXD2880_TNRDMD_XTAL_SHARE_SLAVE)
+ data[1] = 0x00;
+ else
+ data[1] = 0x1f;
+ data[2] = 0x0a;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xb5, data, 3);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init1_seq2,
+ ARRAY_SIZE(rf_init1_seq2));
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) {
+ data[0] = 0x34;
+ data[1] = 0x2c;
+ } else {
+ data[0] = 0x2f;
+ data[1] = 0x25;
+ }
+ data[2] = 0x15;
+ data[3] = 0x19;
+ data[4] = 0x1b;
+ data[5] = 0x15;
+ data[6] = 0x19;
+ data[7] = 0x1b;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xd9, data, 8);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x11);
+ if (ret)
+ return ret;
+ data[0] = 0x6c;
+ data[1] = 0x10;
+ data[2] = 0xa6;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x44, data, 3);
+ if (ret)
+ return ret;
+ data[0] = 0x16;
+ data[1] = 0xa8;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x50, data, 2);
+ if (ret)
+ return ret;
+ data[0] = 0x00;
+ data[1] = 0x22;
+ data[2] = 0x00;
+ data[3] = 0x88;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x62, data, 4);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x74, 0x75);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x7f, rf_init1_cdata1, 40);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x16);
+ if (ret)
+ return ret;
+ data[0] = 0x00;
+ data[1] = 0x71;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x10, data, 2);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x23, 0x89);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x27, rf_init1_cdata2, 5);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x3a, rf_init1_cdata3, 80);
+ if (ret)
+ return ret;
+
+ data[0] = 0x03;
+ data[1] = 0xe0;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xbc, data, 2);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init1_seq3,
+ ARRAY_SIZE(rf_init1_seq3));
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->create_param.stationary_use) {
+ data[0] = 0x06;
+ data[1] = 0x07;
+ data[2] = 0x1a;
+ } else {
+ data[0] = 0x00;
+ data[1] = 0x08;
+ data[2] = 0x19;
+ }
+ data[3] = 0x0e;
+ data[4] = 0x09;
+ data[5] = 0x0e;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x12);
+ if (ret)
+ return ret;
+ for (addr = 0x10; addr < 0x9f; addr += 6) {
+ if (tnr_dmd->lna_thrs_tbl_air) {
+ u8 idx = 0;
+
+ idx = (addr - 0x10) / 6;
+ data[0] =
+ tnr_dmd->lna_thrs_tbl_air->thrs[idx].off_on;
+ data[1] =
+ tnr_dmd->lna_thrs_tbl_air->thrs[idx].on_off;
+ }
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ addr, data, 6);
+ if (ret)
+ return ret;
+ }
+
+ data[0] = 0x00;
+ data[1] = 0x08;
+ if (tnr_dmd->create_param.stationary_use)
+ data[2] = 0x1a;
+ else
+ data[2] = 0x19;
+ data[3] = 0x0e;
+ data[4] = 0x09;
+ data[5] = 0x0e;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x13);
+ if (ret)
+ return ret;
+ for (addr = 0x10; addr < 0xcf; addr += 6) {
+ if (tnr_dmd->lna_thrs_tbl_cable) {
+ u8 idx = 0;
+
+ idx = (addr - 0x10) / 6;
+ data[0] =
+ tnr_dmd->lna_thrs_tbl_cable->thrs[idx].off_on;
+ data[1] =
+ tnr_dmd->lna_thrs_tbl_cable->thrs[idx].on_off;
+ }
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ addr, data, 6);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x11);
+ if (ret)
+ return ret;
+ data[0] = 0x08;
+ data[1] = 0x09;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xbd, data, 2);
+ if (ret)
+ return ret;
+ data[0] = 0x08;
+ data[1] = 0x09;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xc4, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xc9, rf_init1_cdata4, 8);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x14);
+ if (ret)
+ return ret;
+ data[0] = 0x15;
+ data[1] = 0x18;
+ data[2] = 0x00;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x10, data, 3);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init1_seq4,
+ ARRAY_SIZE(rf_init1_seq4));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x12, rf_init1_cdata5, 50);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x10, data, 1);
+ if (ret)
+ return ret;
+ if ((data[0] & 0x01) == 0x00)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init1_seq5,
+ ARRAY_SIZE(rf_init1_seq5));
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x11, data, 1);
+ if (ret)
+ return ret;
+ if ((data[0] & 0x01) == 0x00)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ rf_init1_seq6,
+ ARRAY_SIZE(rf_init1_seq6));
+ if (ret)
+ return ret;
+
+ data[0] = 0x00;
+ data[1] = 0xfe;
+ data[2] = 0xee;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x6e, data, 3);
+ if (ret)
+ return ret;
+ data[0] = 0xa1;
+ data[1] = 0x8b;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x8d, data, 2);
+ if (ret)
+ return ret;
+ data[0] = 0x08;
+ data[1] = 0x09;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x77, data, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->create_param.stationary_use) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x80, 0xaa);
+ if (ret)
+ return ret;
+ }
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ rf_init1_seq7,
+ ARRAY_SIZE(rf_init1_seq7));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init1_seq8,
+ ARRAY_SIZE(rf_init1_seq8));
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x1a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x10, data, 1);
+ if (ret)
+ return ret;
+ if ((data[0] & 0x01) == 0x00)
+ return -EINVAL;
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init1_seq9,
+ ARRAY_SIZE(rf_init1_seq9));
+}
+
+static int rf_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[5] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+ data[0] = 0x40;
+ data[1] = 0x40;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xea, data, 2);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ data[0] = 0x00;
+ if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X)
+ data[1] = 0x00;
+ else
+ data[1] = 0x01;
+ data[2] = 0x01;
+ data[3] = 0x03;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x30, data, 4);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init2_seq1,
+ ARRAY_SIZE(rf_init2_seq1));
+ if (ret)
+ return ret;
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ rf_init2_seq2,
+ ARRAY_SIZE(rf_init2_seq2));
+}
+
+static int x_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys, u32 freq_khz,
+ enum cxd2880_dtv_bandwidth bandwidth,
+ u8 is_cable, int shift_frequency_khz)
+{
+ u8 data[11] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_tune1_seq1,
+ ARRAY_SIZE(x_tune1_seq1));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ data[2] = 0x0e;
+ data[4] = 0x03;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xe7, data, 5);
+ if (ret)
+ return ret;
+
+ data[0] = 0x1f;
+ data[1] = 0x80;
+ data[2] = 0x18;
+ data[3] = 0x00;
+ data[4] = 0x07;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xe7, data, 5);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ data[0] = 0x72;
+ data[1] = 0x81;
+ data[3] = 0x1d;
+ data[4] = 0x6f;
+ data[5] = 0x7e;
+ data[7] = 0x1c;
+ switch (sys) {
+ case CXD2880_DTV_SYS_DVBT:
+ data[2] = 0x94;
+ data[6] = 0x91;
+ break;
+ case CXD2880_DTV_SYS_DVBT2:
+ data[2] = 0x96;
+ data[6] = 0x93;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x44, data, 8);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ x_tune1_seq2,
+ ARRAY_SIZE(x_tune1_seq2));
+ if (ret)
+ return ret;
+
+ data[0] = 0x03;
+ data[1] = 0xe2;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x1e, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ data[0] = is_cable ? 0x01 : 0x00;
+ data[1] = 0x00;
+ data[2] = 0x6b;
+ data[3] = 0x4d;
+
+ switch (bandwidth) {
+ case CXD2880_DTV_BW_1_7_MHZ:
+ data[4] = 0x03;
+ break;
+ case CXD2880_DTV_BW_5_MHZ:
+ case CXD2880_DTV_BW_6_MHZ:
+ data[4] = 0x00;
+ break;
+ case CXD2880_DTV_BW_7_MHZ:
+ data[4] = 0x01;
+ break;
+ case CXD2880_DTV_BW_8_MHZ:
+ data[4] = 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ data[5] = 0x00;
+
+ freq_khz += shift_frequency_khz;
+
+ data[6] = (freq_khz >> 16) & 0x0f;
+ data[7] = (freq_khz >> 8) & 0xff;
+ data[8] = freq_khz & 0xff;
+ data[9] = 0xff;
+ data[10] = 0xfe;
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x52, data, 11);
+}
+
+static int x_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_bandwidth bandwidth,
+ enum cxd2880_tnrdmd_clockmode clk_mode,
+ int shift_frequency_khz)
+{
+ u8 data[3] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x11);
+ if (ret)
+ return ret;
+
+ data[0] = 0x01;
+ data[1] = 0x0e;
+ data[2] = 0x01;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x2d, data, 3);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ x_tune2_seq1,
+ ARRAY_SIZE(x_tune2_seq1));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x2c, data, 1);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x60, data[0]);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ x_tune2_seq2,
+ ARRAY_SIZE(x_tune2_seq2));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_tune2_seq3,
+ ARRAY_SIZE(x_tune2_seq3));
+ if (ret)
+ return ret;
+
+ if (shift_frequency_khz != 0) {
+ int shift_freq = 0;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xe1);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x60, data, 2);
+ if (ret)
+ return ret;
+
+ shift_freq = shift_frequency_khz * 1000;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ default:
+ if (shift_freq >= 0)
+ shift_freq = (shift_freq + 183 / 2) / 183;
+ else
+ shift_freq = (shift_freq - 183 / 2) / 183;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ if (shift_freq >= 0)
+ shift_freq = (shift_freq + 178 / 2) / 178;
+ else
+ shift_freq = (shift_freq - 178 / 2) / 178;
+ break;
+ }
+
+ shift_freq +=
+ cxd2880_convert2s_complement((data[0] << 8) | data[1], 16);
+
+ if (shift_freq > 32767)
+ shift_freq = 32767;
+ else if (shift_freq < -32768)
+ shift_freq = -32768;
+
+ data[0] = (shift_freq >> 8) & 0xff;
+ data[1] = shift_freq & 0xff;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x60, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x69, data, 1);
+ if (ret)
+ return ret;
+
+ shift_freq = -shift_frequency_khz;
+
+ if (bandwidth == CXD2880_DTV_BW_1_7_MHZ) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ default:
+ if (shift_freq >= 0)
+ shift_freq =
+ (shift_freq * 1000 +
+ 17578 / 2) / 17578;
+ else
+ shift_freq =
+ (shift_freq * 1000 -
+ 17578 / 2) / 17578;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ if (shift_freq >= 0)
+ shift_freq =
+ (shift_freq * 1000 +
+ 17090 / 2) / 17090;
+ else
+ shift_freq =
+ (shift_freq * 1000 -
+ 17090 / 2) / 17090;
+ break;
+ }
+ } else {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ default:
+ if (shift_freq >= 0)
+ shift_freq =
+ (shift_freq * 1000 +
+ 35156 / 2) / 35156;
+ else
+ shift_freq =
+ (shift_freq * 1000 -
+ 35156 / 2) / 35156;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ if (shift_freq >= 0)
+ shift_freq =
+ (shift_freq * 1000 +
+ 34180 / 2) / 34180;
+ else
+ shift_freq =
+ (shift_freq * 1000 -
+ 34180 / 2) / 34180;
+ break;
+ }
+ }
+
+ shift_freq += cxd2880_convert2s_complement(data[0], 8);
+
+ if (shift_freq > 127)
+ shift_freq = 127;
+ else if (shift_freq < -128)
+ shift_freq = -128;
+
+ data[0] = shift_freq & 0xff;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x69, data[0]);
+ if (ret)
+ return ret;
+ }
+
+ if (tnr_dmd->create_param.stationary_use) {
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_tune2_seq4,
+ ARRAY_SIZE(x_tune2_seq4));
+ if (ret)
+ return ret;
+ }
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_tune2_seq5,
+ ARRAY_SIZE(x_tune2_seq5));
+}
+
+static int x_tune3(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u8 en_fef_intmtnt_ctrl)
+{
+ u8 data[6] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_tune3_seq,
+ ARRAY_SIZE(x_tune3_seq));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ if (sys == CXD2880_DTV_SYS_DVBT2 && en_fef_intmtnt_ctrl)
+ memset(data, 0x01, sizeof(data));
+ else
+ memset(data, 0x00, sizeof(data));
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xef, data, 6);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x2d);
+ if (ret)
+ return ret;
+ if (sys == CXD2880_DTV_SYS_DVBT2 && en_fef_intmtnt_ctrl)
+ data[0] = 0x00;
+ else
+ data[0] = 0x01;
+
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xb1, data[0]);
+}
+
+static int x_tune4(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[2] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ data[0] = 0x14;
+ data[1] = 0x00;
+ ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x55, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ data[0] = 0x0b;
+ data[1] = 0xff;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x53, data, 2);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x57, 0x01);
+ if (ret)
+ return ret;
+ data[0] = 0x0b;
+ data[1] = 0xff;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x55, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ data[0] = 0x14;
+ data[1] = 0x00;
+ ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x53, data, 2);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x57, 0x02);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_tune4_seq,
+ ARRAY_SIZE(x_tune4_seq));
+ if (ret)
+ return ret;
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_DMD,
+ x_tune4_seq,
+ ARRAY_SIZE(x_tune4_seq));
+}
+
+static int x_sleep1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[3] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ x_sleep1_seq,
+ ARRAY_SIZE(x_sleep1_seq));
+ if (ret)
+ return ret;
+
+ data[0] = 0x00;
+ data[1] = 0x00;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x53, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ data[0] = 0x1f;
+ data[1] = 0xff;
+ data[2] = 0x03;
+ ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x55, data, 3);
+ if (ret)
+ return ret;
+ data[0] = 0x00;
+ data[1] = 0x00;
+ ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x53, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ data[0] = 0x1f;
+ data[1] = 0xff;
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x55, data, 2);
+}
+
+static int x_sleep2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data = 0;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_sleep2_seq1,
+ ARRAY_SIZE(x_sleep2_seq1));
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xb2, &data, 1);
+ if (ret)
+ return ret;
+
+ if ((data & 0x01) == 0x00)
+ return -EINVAL;
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ x_sleep2_seq2,
+ ARRAY_SIZE(x_sleep2_seq2));
+}
+
+static int x_sleep3(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_sleep3_seq,
+ ARRAY_SIZE(x_sleep3_seq));
+}
+
+static int x_sleep4(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_sleep4_seq,
+ ARRAY_SIZE(x_sleep4_seq));
+}
+
+static int spll_reset(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_tnrdmd_clockmode clockmode)
+{
+ u8 data[4] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ spll_reset_seq1,
+ ARRAY_SIZE(spll_reset_seq1));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ spll_reset_seq2,
+ ARRAY_SIZE(spll_reset_seq2));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ spll_reset_seq3,
+ ARRAY_SIZE(spll_reset_seq3));
+ if (ret)
+ return ret;
+
+ switch (clockmode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data[0] = 0x00;
+ break;
+
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data[0] = 0x01;
+ break;
+
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data[0] = 0x02;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x30, data[0]);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x22, 0x00);
+ if (ret)
+ return ret;
+
+ usleep_range(2000, 3000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x10, data, 1);
+ if (ret)
+ return ret;
+ if ((data[0] & 0x01) == 0x00)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ spll_reset_seq4,
+ ARRAY_SIZE(spll_reset_seq4));
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ spll_reset_seq5,
+ ARRAY_SIZE(spll_reset_seq5));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ memset(data, 0x00, sizeof(data));
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x26, data, 4);
+}
+
+static int t_power_x(struct cxd2880_tnrdmd *tnr_dmd, u8 on)
+{
+ u8 data[3] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ t_power_x_seq1,
+ ARRAY_SIZE(t_power_x_seq1));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ t_power_x_seq2,
+ ARRAY_SIZE(t_power_x_seq2));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ t_power_x_seq3,
+ ARRAY_SIZE(t_power_x_seq3));
+ if (ret)
+ return ret;
+
+ if (on) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x2b, 0x01);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x12, data, 1);
+ if (ret)
+ return ret;
+ if ((data[0] & 0x01) == 0)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ t_power_x_seq4,
+ ARRAY_SIZE(t_power_x_seq4));
+ if (ret)
+ return ret;
+ } else {
+ data[0] = 0x03;
+ data[1] = 0x00;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x2a, data, 2);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x13, data, 1);
+ if (ret)
+ return ret;
+ if ((data[0] & 0x01) == 0)
+ return -EINVAL;
+ }
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ t_power_x_seq5,
+ ARRAY_SIZE(t_power_x_seq5));
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x11, data, 1);
+ if (ret)
+ return ret;
+ if ((data[0] & 0x01) == 0)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ t_power_x_seq6,
+ ARRAY_SIZE(t_power_x_seq6));
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ t_power_x_seq7,
+ ARRAY_SIZE(t_power_x_seq7));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ memset(data, 0x00, sizeof(data));
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x27, data, 3);
+}
+
+struct cxd2880_tnrdmd_ts_clk_cfg {
+ u8 srl_clk_mode;
+ u8 srl_duty_mode;
+ u8 ts_clk_period;
+};
+
+static int set_ts_clk_mode_and_freq(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys)
+{
+ int ret;
+ u8 backwards_compatible = 0;
+ struct cxd2880_tnrdmd_ts_clk_cfg ts_clk_cfg;
+ u8 ts_rate_ctrl_off = 0;
+ u8 ts_in_off = 0;
+ u8 ts_clk_manaul_on = 0;
+ u8 data = 0;
+
+ static const struct cxd2880_tnrdmd_ts_clk_cfg srl_ts_clk_stgs[2][2] = {
+ {
+ {3, 1, 8,},
+ {0, 2, 16,}
+ }, {
+ {1, 1, 8,},
+ {2, 2, 16,}
+ }
+ };
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->is_ts_backwards_compatible_mode) {
+ backwards_compatible = 1;
+ ts_rate_ctrl_off = 1;
+ ts_in_off = 1;
+ } else {
+ backwards_compatible = 0;
+ ts_rate_ctrl_off = 0;
+ ts_in_off = 0;
+ }
+
+ if (tnr_dmd->ts_byte_clk_manual_setting) {
+ ts_clk_manaul_on = 1;
+ ts_rate_ctrl_off = 0;
+ }
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xd3, ts_rate_ctrl_off, 0x01);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xde, ts_in_off, 0x01);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xda, ts_clk_manaul_on, 0x01);
+ if (ret)
+ return ret;
+
+ ts_clk_cfg = srl_ts_clk_stgs[tnr_dmd->srl_ts_clk_mod_cnts]
+ [tnr_dmd->srl_ts_clk_frq];
+
+ if (tnr_dmd->ts_byte_clk_manual_setting)
+ ts_clk_cfg.ts_clk_period = tnr_dmd->ts_byte_clk_manual_setting;
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xc4, ts_clk_cfg.srl_clk_mode, 0x03);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xd1, ts_clk_cfg.srl_duty_mode, 0x03);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xd9,
+ ts_clk_cfg.ts_clk_period);
+ if (ret)
+ return ret;
+
+ data = backwards_compatible ? 0x00 : 0x01;
+
+ if (sys == CXD2880_DTV_SYS_DVBT) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_io_set_reg_bits(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x66, data, 0x01);
+ }
+
+ return ret;
+}
+
+static int pid_ftr_setting(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_tnrdmd_pid_ftr_cfg
+ *pid_ftr_cfg)
+{
+ int i;
+ int ret;
+ u8 data[65];
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ if (!pid_ftr_cfg)
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x50, 0x02);
+
+ data[0] = pid_ftr_cfg->is_negative ? 0x01 : 0x00;
+
+ for (i = 0; i < 32; i++) {
+ if (pid_ftr_cfg->pid_cfg[i].is_en) {
+ data[1 + (i * 2)] = (pid_ftr_cfg->pid_cfg[i].pid >> 8) | 0x20;
+ data[2 + (i * 2)] = pid_ftr_cfg->pid_cfg[i].pid & 0xff;
+ } else {
+ data[1 + (i * 2)] = 0x00;
+ data[2 + (i * 2)] = 0x00;
+ }
+ }
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x50, data, 65);
+}
+
+static int load_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ int ret;
+ u8 i;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ tnr_dmd->cfg_mem[i].tgt,
+ 0x00, tnr_dmd->cfg_mem[i].bank);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ tnr_dmd->cfg_mem[i].tgt,
+ tnr_dmd->cfg_mem[i].address,
+ tnr_dmd->cfg_mem[i].value,
+ tnr_dmd->cfg_mem[i].bit_mask);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int set_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_io_tgt tgt,
+ u8 bank, u8 address, u8 value, u8 bit_mask)
+{
+ u8 i;
+ u8 value_stored = 0;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) {
+ if (value_stored == 0 &&
+ tnr_dmd->cfg_mem[i].tgt == tgt &&
+ tnr_dmd->cfg_mem[i].bank == bank &&
+ tnr_dmd->cfg_mem[i].address == address) {
+ tnr_dmd->cfg_mem[i].value &= ~bit_mask;
+ tnr_dmd->cfg_mem[i].value |= (value & bit_mask);
+
+ tnr_dmd->cfg_mem[i].bit_mask |= bit_mask;
+
+ value_stored = 1;
+ }
+ }
+
+ if (value_stored)
+ return 0;
+
+ if (tnr_dmd->cfg_mem_last_entry < CXD2880_TNRDMD_MAX_CFG_MEM_COUNT) {
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].tgt = tgt;
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bank = bank;
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].address = address;
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].value = (value & bit_mask);
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bit_mask = bit_mask;
+ tnr_dmd->cfg_mem_last_entry++;
+ } else {
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_io *io,
+ struct cxd2880_tnrdmd_create_param
+ *create_param)
+{
+ if (!tnr_dmd || !io || !create_param)
+ return -EINVAL;
+
+ memset(tnr_dmd, 0, sizeof(struct cxd2880_tnrdmd));
+
+ tnr_dmd->io = io;
+ tnr_dmd->create_param = *create_param;
+
+ tnr_dmd->diver_mode = CXD2880_TNRDMD_DIVERMODE_SINGLE;
+ tnr_dmd->diver_sub = NULL;
+
+ tnr_dmd->srl_ts_clk_mod_cnts = 1;
+ tnr_dmd->en_fef_intmtnt_base = 1;
+ tnr_dmd->en_fef_intmtnt_lite = 1;
+ tnr_dmd->rf_lvl_cmpstn = NULL;
+ tnr_dmd->lna_thrs_tbl_air = NULL;
+ tnr_dmd->lna_thrs_tbl_cable = NULL;
+ atomic_set(&tnr_dmd->cancel, 0);
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd
+ *tnr_dmd_main,
+ struct cxd2880_io *io_main,
+ struct cxd2880_tnrdmd *tnr_dmd_sub,
+ struct cxd2880_io *io_sub,
+ struct
+ cxd2880_tnrdmd_diver_create_param
+ *create_param)
+{
+ struct cxd2880_tnrdmd_create_param *main_param, *sub_param;
+
+ if (!tnr_dmd_main || !io_main || !tnr_dmd_sub || !io_sub ||
+ !create_param)
+ return -EINVAL;
+
+ memset(tnr_dmd_main, 0, sizeof(struct cxd2880_tnrdmd));
+ memset(tnr_dmd_sub, 0, sizeof(struct cxd2880_tnrdmd));
+
+ main_param = &tnr_dmd_main->create_param;
+ sub_param = &tnr_dmd_sub->create_param;
+
+ tnr_dmd_main->io = io_main;
+ tnr_dmd_main->diver_mode = CXD2880_TNRDMD_DIVERMODE_MAIN;
+ tnr_dmd_main->diver_sub = tnr_dmd_sub;
+ tnr_dmd_main->create_param.en_internal_ldo =
+ create_param->en_internal_ldo;
+
+ main_param->ts_output_if = create_param->ts_output_if;
+ main_param->xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_MASTER;
+ main_param->xosc_cap = create_param->xosc_cap_main;
+ main_param->xosc_i = create_param->xosc_i_main;
+ main_param->is_cxd2881gg = create_param->is_cxd2881gg;
+ main_param->stationary_use = create_param->stationary_use;
+
+ tnr_dmd_sub->io = io_sub;
+ tnr_dmd_sub->diver_mode = CXD2880_TNRDMD_DIVERMODE_SUB;
+ tnr_dmd_sub->diver_sub = NULL;
+
+ sub_param->en_internal_ldo = create_param->en_internal_ldo;
+ sub_param->ts_output_if = create_param->ts_output_if;
+ sub_param->xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_SLAVE;
+ sub_param->xosc_cap = 0;
+ sub_param->xosc_i = create_param->xosc_i_sub;
+ sub_param->is_cxd2881gg = create_param->is_cxd2881gg;
+ sub_param->stationary_use = create_param->stationary_use;
+
+ tnr_dmd_main->srl_ts_clk_mod_cnts = 1;
+ tnr_dmd_main->en_fef_intmtnt_base = 1;
+ tnr_dmd_main->en_fef_intmtnt_lite = 1;
+ tnr_dmd_main->rf_lvl_cmpstn = NULL;
+ tnr_dmd_main->lna_thrs_tbl_air = NULL;
+ tnr_dmd_main->lna_thrs_tbl_cable = NULL;
+
+ tnr_dmd_sub->srl_ts_clk_mod_cnts = 1;
+ tnr_dmd_sub->en_fef_intmtnt_base = 1;
+ tnr_dmd_sub->en_fef_intmtnt_lite = 1;
+ tnr_dmd_sub->rf_lvl_cmpstn = NULL;
+ tnr_dmd_sub->lna_thrs_tbl_air = NULL;
+ tnr_dmd_sub->lna_thrs_tbl_cable = NULL;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ int ret;
+
+ if (!tnr_dmd || tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ tnr_dmd->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_UNKNOWN;
+ tnr_dmd->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN;
+ tnr_dmd->frequency_khz = 0;
+ tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN;
+ tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+ tnr_dmd->scan_mode = 0;
+ atomic_set(&tnr_dmd->cancel, 0);
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ tnr_dmd->diver_sub->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_UNKNOWN;
+ tnr_dmd->diver_sub->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN;
+ tnr_dmd->diver_sub->frequency_khz = 0;
+ tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN;
+ tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+ tnr_dmd->diver_sub->scan_mode = 0;
+ atomic_set(&tnr_dmd->diver_sub->cancel, 0);
+ }
+
+ ret = cxd2880_tnrdmd_chip_id(tnr_dmd, &tnr_dmd->chip_id);
+ if (ret)
+ return ret;
+
+ if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->chip_id))
+ return -ENOTTY;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ cxd2880_tnrdmd_chip_id(tnr_dmd->diver_sub,
+ &tnr_dmd->diver_sub->chip_id);
+ if (ret)
+ return ret;
+
+ if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->diver_sub->chip_id))
+ return -ENOTTY;
+ }
+
+ ret = p_init1(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = p_init1(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ usleep_range(1000, 2000);
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = p_init2(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ ret = p_init2(tnr_dmd);
+ if (ret)
+ return ret;
+
+ usleep_range(5000, 6000);
+
+ ret = p_init3(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = p_init3(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ ret = rf_init1(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret = rf_init1(tnr_dmd->diver_sub);
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 cpu_task_completed;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+ &cpu_task_completed);
+ if (ret)
+ return ret;
+
+ if (!cpu_task_completed)
+ return -EINVAL;
+
+ ret = rf_init2(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = rf_init2(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ ret = load_cfg_mem(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = load_cfg_mem(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP;
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *task_completed)
+{
+ u16 cpu_status = 0;
+ int ret;
+
+ if (!tnr_dmd || !task_completed)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd, &cpu_status);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ if (cpu_status == 0)
+ *task_completed = 1;
+ else
+ *task_completed = 0;
+
+ return 0;
+ }
+ if (cpu_status != 0) {
+ *task_completed = 0;
+ return 0;
+ }
+
+ ret = cxd2880_tnrdmd_mon_internal_cpu_status_sub(tnr_dmd, &cpu_status);
+ if (ret)
+ return ret;
+
+ if (cpu_status == 0)
+ *task_completed = 1;
+ else
+ *task_completed = 0;
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u32 frequency_khz,
+ enum cxd2880_dtv_bandwidth
+ bandwidth, u8 one_seg_opt,
+ u8 one_seg_opt_shft_dir)
+{
+ u8 data;
+ enum cxd2880_tnrdmd_clockmode new_clk_mode =
+ CXD2880_TNRDMD_CLOCKMODE_A;
+ int shift_frequency_khz;
+ u8 cpu_task_completed;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (frequency_khz < 4000)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_sleep(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00,
+ 0x00);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x2b,
+ &data,
+ 1);
+ if (ret)
+ return ret;
+
+ switch (sys) {
+ case CXD2880_DTV_SYS_DVBT:
+ if (data == 0x00) {
+ ret = t_power_x(tnr_dmd, 1);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = t_power_x(tnr_dmd->diver_sub, 1);
+ if (ret)
+ return ret;
+ }
+ }
+ break;
+
+ case CXD2880_DTV_SYS_DVBT2:
+ if (data == 0x01) {
+ ret = t_power_x(tnr_dmd, 0);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = t_power_x(tnr_dmd->diver_sub, 0);
+ if (ret)
+ return ret;
+ }
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = spll_reset(tnr_dmd, new_clk_mode);
+ if (ret)
+ return ret;
+
+ tnr_dmd->clk_mode = new_clk_mode;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = spll_reset(tnr_dmd->diver_sub, new_clk_mode);
+ if (ret)
+ return ret;
+
+ tnr_dmd->diver_sub->clk_mode = new_clk_mode;
+ }
+
+ ret = load_cfg_mem(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = load_cfg_mem(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ if (one_seg_opt) {
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ shift_frequency_khz = 350;
+ } else {
+ if (one_seg_opt_shft_dir)
+ shift_frequency_khz = 350;
+ else
+ shift_frequency_khz = -350;
+
+ if (tnr_dmd->create_param.xtal_share_type ==
+ CXD2880_TNRDMD_XTAL_SHARE_SLAVE)
+ shift_frequency_khz *= -1;
+ }
+ } else {
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ shift_frequency_khz = 150;
+ } else {
+ switch (tnr_dmd->create_param.xtal_share_type) {
+ case CXD2880_TNRDMD_XTAL_SHARE_NONE:
+ case CXD2880_TNRDMD_XTAL_SHARE_EXTREF:
+ default:
+ shift_frequency_khz = 0;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_MASTER:
+ shift_frequency_khz = 150;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_SLAVE:
+ shift_frequency_khz = -150;
+ break;
+ }
+ }
+ }
+
+ ret =
+ x_tune1(tnr_dmd, sys, frequency_khz, bandwidth,
+ tnr_dmd->is_cable_input, shift_frequency_khz);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ x_tune1(tnr_dmd->diver_sub, sys, frequency_khz,
+ bandwidth, tnr_dmd->is_cable_input,
+ -shift_frequency_khz);
+ if (ret)
+ return ret;
+ }
+
+ usleep_range(10000, 11000);
+
+ ret =
+ cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+ &cpu_task_completed);
+ if (ret)
+ return ret;
+
+ if (!cpu_task_completed)
+ return -EINVAL;
+
+ ret =
+ x_tune2(tnr_dmd, bandwidth, tnr_dmd->clk_mode,
+ shift_frequency_khz);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ x_tune2(tnr_dmd->diver_sub, bandwidth,
+ tnr_dmd->diver_sub->clk_mode,
+ -shift_frequency_khz);
+ if (ret)
+ return ret;
+ }
+
+ if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS) {
+ ret = set_ts_clk_mode_and_freq(tnr_dmd, sys);
+ } else {
+ struct cxd2880_tnrdmd_pid_ftr_cfg *pid_ftr_cfg;
+
+ if (tnr_dmd->pid_ftr_cfg_en)
+ pid_ftr_cfg = &tnr_dmd->pid_ftr_cfg;
+ else
+ pid_ftr_cfg = NULL;
+
+ ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
+ }
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u8 en_fef_intmtnt_ctrl)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = x_tune3(tnr_dmd, sys, en_fef_intmtnt_ctrl);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_tune3(tnr_dmd->diver_sub, sys, en_fef_intmtnt_ctrl);
+ if (ret)
+ return ret;
+ ret = x_tune4(tnr_dmd);
+ if (ret)
+ return ret;
+ }
+
+ return cxd2880_tnrdmd_set_ts_output(tnr_dmd, 1);
+}
+
+int cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state == CXD2880_TNRDMD_STATE_SLEEP)
+ return 0;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 0);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_sleep1(tnr_dmd);
+ if (ret)
+ return ret;
+ }
+
+ ret = x_sleep2(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_sleep2(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ switch (tnr_dmd->sys) {
+ case CXD2880_DTV_SYS_DVBT:
+ ret = cxd2880_tnrdmd_dvbt_sleep_setting(tnr_dmd);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_DTV_SYS_DVBT2:
+ ret = cxd2880_tnrdmd_dvbt2_sleep_setting(tnr_dmd);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = x_sleep3(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_sleep3(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ ret = x_sleep4(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_sleep4(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP;
+ tnr_dmd->frequency_khz = 0;
+ tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN;
+ tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP;
+ tnr_dmd->diver_sub->frequency_khz = 0;
+ tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN;
+ tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+ }
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_tnrdmd_cfg_id id,
+ int value)
+{
+ int ret = 0;
+ u8 data[2] = { 0 };
+ u8 need_sub_setting = 0;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ switch (id) {
+ case CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xc4,
+ value ? 0x00 : 0x10,
+ 0x10);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xc5,
+ value ? 0x00 : 0x02,
+ 0x02);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xc5,
+ value ? 0x00 : 0x04,
+ 0x04);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xcb,
+ value ? 0x00 : 0x01,
+ 0x01);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xc5,
+ value ? 0x01 : 0x00,
+ 0x01);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSCLK_CONT:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ tnr_dmd->srl_ts_clk_mod_cnts = value ? 0x01 : 0x00;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSCLK_MASK:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ if (value < 0 || value > 0x1f)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xc6, value,
+ 0x1f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSVALID_MASK:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ if (value < 0 || value > 0x1f)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xc8, value,
+ 0x1f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSERR_MASK:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ if (value < 0 || value > 0x1f)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xc9, value,
+ 0x1f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSERR_VALID_DIS:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x91,
+ value ? 0x01 : 0x00,
+ 0x01);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSPIN_CURRENT:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x51, value,
+ 0x3f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x50,
+ value ? 0x80 : 0x00,
+ 0x80);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSPIN_PULLUP:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x50, value,
+ 0x3f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSCLK_FREQ:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ if (value < 0 || value > 1)
+ return -EINVAL;
+
+ tnr_dmd->srl_ts_clk_frq =
+ (enum cxd2880_tnrdmd_serial_ts_clk)value;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ if (value < 0 || value > 0xff)
+ return -EINVAL;
+
+ tnr_dmd->ts_byte_clk_manual_setting = value;
+
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_PACKET_GAP:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ if (value < 0 || value > 7)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xd6, value,
+ 0x07);
+ if (ret)
+ return ret;
+
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ tnr_dmd->is_ts_backwards_compatible_mode = value ? 1 : 0;
+
+ break;
+
+ case CXD2880_TNRDMD_CFG_PWM_VALUE:
+ if (value < 0 || value > 0x1000)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x22,
+ value ? 0x01 : 0x00,
+ 0x01);
+ if (ret)
+ return ret;
+
+ data[0] = (value >> 8) & 0x1f;
+ data[1] = value & 0xff;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x23,
+ data[0], 0x1f);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x24,
+ data[1], 0xff);
+ if (ret)
+ return ret;
+
+ break;
+
+ case CXD2880_TNRDMD_CFG_INTERRUPT:
+ data[0] = (value >> 8) & 0xff;
+ data[1] = value & 0xff;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x48, data[0],
+ 0xff);
+ if (ret)
+ return ret;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x49, data[1],
+ 0xff);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL:
+ data[0] = value & 0x07;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x4a, data[0],
+ 0x07);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL:
+ data[0] = (value & 0x07) << 3;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x4a, data[0],
+ 0x38);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE:
+ if (value < CXD2880_TNRDMD_CLOCKMODE_UNKNOWN ||
+ value > CXD2880_TNRDMD_CLOCKMODE_C)
+ return -EINVAL;
+ tnr_dmd->fixed_clk_mode = (enum cxd2880_tnrdmd_clockmode)value;
+ break;
+
+ case CXD2880_TNRDMD_CFG_CABLE_INPUT:
+ tnr_dmd->is_cable_input = value ? 1 : 0;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE:
+ tnr_dmd->en_fef_intmtnt_base = value ? 1 : 0;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE:
+ tnr_dmd->en_fef_intmtnt_lite = value ? 1 : 0;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS:
+ data[0] = (value >> 8) & 0x07;
+ data[1] = value & 0xff;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x99, data[0],
+ 0x07);
+ if (ret)
+ return ret;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9a, data[1],
+ 0xff);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS:
+ data[0] = (value >> 8) & 0x07;
+ data[1] = value & 0xff;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9b, data[0],
+ 0x07);
+ if (ret)
+ return ret;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9c, data[1],
+ 0xff);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS:
+ data[0] = (value >> 8) & 0x07;
+ data[1] = value & 0xff;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9d, data[0],
+ 0x07);
+ if (ret)
+ return ret;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9e, data[1],
+ 0xff);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST:
+ tnr_dmd->blind_tune_dvbt2_first = value ? 1 : 0;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD:
+ if (value < 0 || value > 31)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x10, 0x60,
+ value & 0x1f, 0x1f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD:
+ if (value < 0 || value > 7)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x10, 0x6f,
+ value & 0x07, 0x07);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_BBER_MES:
+ if (value < 0 || value > 15)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x20, 0x72,
+ value & 0x0f, 0x0f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_LBER_MES:
+ if (value < 0 || value > 15)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x20, 0x6f,
+ value & 0x0f, 0x0f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT_PER_MES:
+ if (value < 0 || value > 15)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x10, 0x5c,
+ value & 0x0f, 0x0f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_PER_MES:
+ if (value < 0 || value > 15)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x24, 0xdc,
+ value & 0x0f, 0x0f);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (need_sub_setting &&
+ tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret = cxd2880_tnrdmd_set_cfg(tnr_dmd->diver_sub, id, value);
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id,
+ u8 en,
+ enum cxd2880_tnrdmd_gpio_mode mode,
+ u8 open_drain, u8 invert)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (id > 2)
+ return -EINVAL;
+
+ if (mode > CXD2880_TNRDMD_GPIO_MODE_EEW)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+ 0x00, 0x40 + id, mode,
+ 0x0f);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+ 0x00, 0x43,
+ open_drain ? (1 << id) : 0,
+ 1 << id);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+ 0x00, 0x44,
+ invert ? (1 << id) : 0,
+ 1 << id);
+ if (ret)
+ return ret;
+
+ return cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x45,
+ en ? 0 : (1 << id),
+ 1 << id);
+}
+
+int cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id,
+ u8 en,
+ enum cxd2880_tnrdmd_gpio_mode
+ mode, u8 open_drain, u8 invert)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_gpio_set_cfg(tnr_dmd->diver_sub, id, en, mode,
+ open_drain, invert);
+}
+
+int cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 *value)
+{
+ u8 data = 0;
+ int ret;
+
+ if (!tnr_dmd || !value)
+ return -EINVAL;
+
+ if (id > 2)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x20, &data, 1);
+ if (ret)
+ return ret;
+
+ *value = (data >> id) & 0x01;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 *value)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_gpio_read(tnr_dmd->diver_sub, id, value);
+}
+
+int cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 value)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (id > 2)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x46,
+ value ? (1 << id) : 0,
+ 1 << id);
+}
+
+int cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 value)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_gpio_write(tnr_dmd->diver_sub, id, value);
+}
+
+int cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd,
+ u16 *value)
+{
+ int ret;
+ u8 data[2] = { 0 };
+
+ if (!tnr_dmd || !value)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x15, data, 2);
+ if (ret)
+ return ret;
+
+ *value = (data[0] << 8) | data[1];
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd,
+ u16 value)
+{
+ int ret;
+ u8 data[2] = { 0 };
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ data[0] = (value >> 8) & 0xff;
+ data[1] = value & 0xff;
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x3c, data, 2);
+}
+
+int cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 clear_overflow_flag,
+ u8 clear_underflow_flag,
+ u8 clear_buf)
+{
+ int ret;
+ u8 data[2] = { 0 };
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ data[0] = clear_overflow_flag ? 0x02 : 0x00;
+ data[0] |= clear_underflow_flag ? 0x01 : 0x00;
+ data[1] = clear_buf ? 0x01 : 0x00;
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x9f, data, 2);
+}
+
+int cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_tnrdmd_chip_id *chip_id)
+{
+ int ret;
+ u8 data = 0;
+
+ if (!tnr_dmd || !chip_id)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xfd, &data, 1);
+ if (ret)
+ return ret;
+
+ *chip_id = (enum cxd2880_tnrdmd_chip_id)data;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_io_tgt tgt,
+ u8 bank, u8 address,
+ u8 value, u8 bit_mask)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io, tgt, 0x00, bank);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ tgt, address, value, bit_mask);
+ if (ret)
+ return ret;
+
+ return set_cfg_mem(tnr_dmd, tgt, bank, address, value, bit_mask);
+}
+
+int cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u8 scan_mode_end)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ tnr_dmd->scan_mode = scan_mode_end;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return cxd2880_tnrdmd_set_scan_mode(tnr_dmd->diver_sub, sys,
+ scan_mode_end);
+ else
+ return 0;
+}
+
+int cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_tnrdmd_pid_ftr_cfg
+ *pid_ftr_cfg)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS)
+ return -ENOTTY;
+
+ if (pid_ftr_cfg) {
+ tnr_dmd->pid_ftr_cfg = *pid_ftr_cfg;
+ tnr_dmd->pid_ftr_cfg_en = 1;
+ } else {
+ tnr_dmd->pid_ftr_cfg_en = 0;
+ }
+
+ if (tnr_dmd->state == CXD2880_TNRDMD_STATE_ACTIVE)
+ return pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
+ else
+ return 0;
+}
+
+int cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ int (*rf_lvl_cmpstn)
+ (struct cxd2880_tnrdmd *,
+ int *))
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ tnr_dmd->rf_lvl_cmpstn = rf_lvl_cmpstn;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ int (*rf_lvl_cmpstn)
+ (struct cxd2880_tnrdmd *,
+ int *))
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_set_rf_lvl_cmpstn(tnr_dmd->diver_sub,
+ rf_lvl_cmpstn);
+}
+
+int cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_tnrdmd_lna_thrs_tbl_air
+ *tbl_air,
+ struct cxd2880_tnrdmd_lna_thrs_tbl_cable
+ *tbl_cable)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ tnr_dmd->lna_thrs_tbl_air = tbl_air;
+ tnr_dmd->lna_thrs_tbl_cable = tbl_cable;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ struct
+ cxd2880_tnrdmd_lna_thrs_tbl_air
+ *tbl_air,
+ struct cxd2880_tnrdmd_lna_thrs_tbl_cable
+ *tbl_cable)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_set_lna_thrs(tnr_dmd->diver_sub,
+ tbl_air, tbl_cable);
+}
+
+int cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 en, u8 value)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ if (tnr_dmd->create_param.ts_output_if != CXD2880_TNRDMD_TSOUT_IF_TS)
+ return -ENOTTY;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ if (en) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x50, ((value & 0x1f) | 0x80));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x52, (value & 0x1f));
+ } else {
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ set_ts_pin_seq,
+ ARRAY_SIZE(set_ts_pin_seq));
+ if (ret)
+ return ret;
+
+ ret = load_cfg_mem(tnr_dmd);
+ }
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 en)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ switch (tnr_dmd->create_param.ts_output_if) {
+ case CXD2880_TNRDMD_TSOUT_IF_TS:
+ if (en) {
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ set_ts_output_seq1,
+ ARRAY_SIZE(set_ts_output_seq1));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ set_ts_output_seq2,
+ ARRAY_SIZE(set_ts_output_seq2));
+ if (ret)
+ return ret;
+ } else {
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ set_ts_output_seq3,
+ ARRAY_SIZE(set_ts_output_seq3));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ set_ts_output_seq4,
+ ARRAY_SIZE(set_ts_output_seq4));
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case CXD2880_TNRDMD_TSOUT_IF_SPI:
+ break;
+
+ case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ switch (tnr_dmd->create_param.ts_output_if) {
+ case CXD2880_TNRDMD_TSOUT_IF_SPI:
+ case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, &data, 1);
+ if (ret)
+ return ret;
+
+ break;
+ case CXD2880_TNRDMD_TSOUT_IF_TS:
+ default:
+ break;
+ }
+
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x01, 0x01);
+}
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..c6d6c8dd16a1
--- /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.5"
+
+#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2018-04-25"
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..c7e79da8c432
--- /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 <media/dvb_frontend.h>
+
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq1[] = {
+ {0x00, 0x00}, {0x31, 0x01},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq2[] = {
+ {0x00, 0x04}, {0x5c, 0xfb}, {0x00, 0x10}, {0xa4, 0x03},
+ {0x00, 0x14}, {0xb0, 0x00}, {0x00, 0x25},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq3[] = {
+ {0x00, 0x12}, {0x44, 0x00},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq4[] = {
+ {0x00, 0x11}, {0x87, 0xd2},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq5[] = {
+ {0x00, 0x00}, {0xfd, 0x01},
+};
+
+static const struct cxd2880_reg_value sleep_dmd_setting_seq1[] = {
+ {0x00, 0x04}, {0x5c, 0xd8}, {0x00, 0x10}, {0xa4, 0x00},
+};
+
+static const struct cxd2880_reg_value sleep_dmd_setting_seq2[] = {
+ {0x00, 0x11}, {0x87, 0x04},
+};
+
+static int x_tune_dvbt_demod_setting(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dtv_bandwidth
+ bandwidth,
+ enum cxd2880_tnrdmd_clockmode
+ clk_mode)
+{
+ static const u8 clk_mode_ckffrq_a[2] = { 0x52, 0x49 };
+ static const u8 clk_mode_ckffrq_b[2] = { 0x5d, 0x55 };
+ static const u8 clk_mode_ckffrq_c[2] = { 0x60, 0x00 };
+ static const u8 ratectl_margin[2] = { 0x01, 0xf0 };
+ static const u8 maxclkcnt_a[3] = { 0x73, 0xca, 0x49 };
+ static const u8 maxclkcnt_b[3] = { 0xc8, 0x13, 0xaa };
+ static const u8 maxclkcnt_c[3] = { 0xdc, 0x6c, 0x00 };
+
+ static const u8 bw8_nomi_ac[5] = { 0x15, 0x00, 0x00, 0x00, 0x00};
+ static const u8 bw8_nomi_b[5] = { 0x14, 0x6a, 0xaa, 0xaa, 0xaa};
+ static const u8 bw8_gtdofst_a[2] = { 0x01, 0x28 };
+ static const u8 bw8_gtdofst_b[2] = { 0x11, 0x44 };
+ static const u8 bw8_gtdofst_c[2] = { 0x15, 0x28 };
+ static const u8 bw8_mrc_a[5] = { 0x30, 0x00, 0x00, 0x90, 0x00 };
+ static const u8 bw8_mrc_b[5] = { 0x36, 0x71, 0x00, 0xa3, 0x55 };
+ static const u8 bw8_mrc_c[5] = { 0x38, 0x00, 0x00, 0xa8, 0x00 };
+ static const u8 bw8_notch[4] = { 0xb3, 0x00, 0x01, 0x02 };
+
+ static const u8 bw7_nomi_ac[5] = { 0x18, 0x00, 0x00, 0x00, 0x00};
+ static const u8 bw7_nomi_b[5] = { 0x17, 0x55, 0x55, 0x55, 0x55};
+ static const u8 bw7_gtdofst_a[2] = { 0x12, 0x4c };
+ static const u8 bw7_gtdofst_b[2] = { 0x1f, 0x15 };
+ static const u8 bw7_gtdofst_c[2] = { 0x1f, 0xf8 };
+ static const u8 bw7_mrc_a[5] = { 0x36, 0xdb, 0x00, 0xa4, 0x92 };
+ static const u8 bw7_mrc_b[5] = { 0x3e, 0x38, 0x00, 0xba, 0xaa };
+ static const u8 bw7_mrc_c[5] = { 0x40, 0x00, 0x00, 0xc0, 0x00 };
+ static const u8 bw7_notch[4] = { 0xb8, 0x00, 0x00, 0x03 };
+
+ static const u8 bw6_nomi_ac[5] = { 0x1c, 0x00, 0x00, 0x00, 0x00};
+ static const u8 bw6_nomi_b[5] = { 0x1b, 0x38, 0xe3, 0x8e, 0x38};
+ static const u8 bw6_gtdofst_a[2] = { 0x1f, 0xf8 };
+ static const u8 bw6_gtdofst_b[2] = { 0x24, 0x43 };
+ static const u8 bw6_gtdofst_c[2] = { 0x25, 0x4c };
+ static const u8 bw6_mrc_a[5] = { 0x40, 0x00, 0x00, 0xc0, 0x00 };
+ static const u8 bw6_mrc_b[5] = { 0x48, 0x97, 0x00, 0xd9, 0xc7 };
+ static const u8 bw6_mrc_c[5] = { 0x4a, 0xaa, 0x00, 0xdf, 0xff };
+ static const u8 bw6_notch[4] = { 0xbe, 0xab, 0x00, 0x03 };
+
+ static const u8 bw5_nomi_ac[5] = { 0x21, 0x99, 0x99, 0x99, 0x99};
+ static const u8 bw5_nomi_b[5] = { 0x20, 0xaa, 0xaa, 0xaa, 0xaa};
+ static const u8 bw5_gtdofst_a[2] = { 0x26, 0x5d };
+ static const u8 bw5_gtdofst_b[2] = { 0x2b, 0x84 };
+ static const u8 bw5_gtdofst_c[2] = { 0x2c, 0xc2 };
+ static const u8 bw5_mrc_a[5] = { 0x4c, 0xcc, 0x00, 0xe6, 0x66 };
+ static const u8 bw5_mrc_b[5] = { 0x57, 0x1c, 0x01, 0x05, 0x55 };
+ static const u8 bw5_mrc_c[5] = { 0x59, 0x99, 0x01, 0x0c, 0xcc };
+ static const u8 bw5_notch[4] = { 0xc8, 0x01, 0x00, 0x03 };
+ const u8 *data = NULL;
+ u8 sst_data;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ tune_dmd_setting_seq1,
+ ARRAY_SIZE(tune_dmd_setting_seq1));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x04);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = clk_mode_ckffrq_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = clk_mode_ckffrq_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = clk_mode_ckffrq_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x65, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x5d, 0x07);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+ u8 data[2] = { 0x01, 0x01 };
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xce, data, 2);
+ if (ret)
+ return ret;
+ }
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ tune_dmd_setting_seq2,
+ ARRAY_SIZE(tune_dmd_setting_seq2));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xf0, ratectl_margin, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN ||
+ tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) {
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ tune_dmd_setting_seq3,
+ ARRAY_SIZE(tune_dmd_setting_seq3));
+ if (ret)
+ return ret;
+ }
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) {
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ tune_dmd_setting_seq4,
+ ARRAY_SIZE(tune_dmd_setting_seq4));
+ if (ret)
+ return ret;
+ }
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x04);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = maxclkcnt_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = maxclkcnt_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = maxclkcnt_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x68, data, 3);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x04);
+ if (ret)
+ return ret;
+
+ switch (bandwidth) {
+ case CXD2880_DTV_BW_8_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw8_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw8_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x60, data, 5);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x00);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw8_gtdofst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw8_gtdofst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw8_gtdofst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x7d, data, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ sst_data = 0x35;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ sst_data = 0x34;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x71, sst_data);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw8_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw8_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw8_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, &data[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x51, &data[2], 3);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x72, &bw8_notch[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x6b, &bw8_notch[2], 2);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_DTV_BW_7_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw7_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw7_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x60, data, 5);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x02);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw7_gtdofst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw7_gtdofst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw7_gtdofst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x7d, data, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ sst_data = 0x2f;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ sst_data = 0x2e;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x71, sst_data);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw7_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw7_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw7_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, &data[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x51, &data[2], 3);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x72, &bw7_notch[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x6b, &bw7_notch[2], 2);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_DTV_BW_6_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw6_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw6_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x60, data, 5);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x04);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw6_gtdofst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw6_gtdofst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw6_gtdofst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x7d, data, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ sst_data = 0x29;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ sst_data = 0x2a;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x71, sst_data);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw6_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw6_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw6_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, &data[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x51, &data[2], 3);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x72, &bw6_notch[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x6b, &bw6_notch[2], 2);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_DTV_BW_5_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw5_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw5_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x60, data, 5);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x06);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw5_gtdofst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw5_gtdofst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw5_gtdofst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x7d, data, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ sst_data = 0x24;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ sst_data = 0x23;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x71, sst_data);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw5_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw5_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw5_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, &data[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x51, &data[2], 3);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x72, &bw5_notch[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x6b, &bw5_notch[2], 2);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ tune_dmd_setting_seq5,
+ ARRAY_SIZE(tune_dmd_setting_seq5));
+}
+
+static int x_sleep_dvbt_demod_setting(struct cxd2880_tnrdmd
+ *tnr_dmd)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ sleep_dmd_setting_seq1,
+ ARRAY_SIZE(sleep_dmd_setting_seq1));
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ sleep_dmd_setting_seq2,
+ ARRAY_SIZE(sleep_dmd_setting_seq2));
+
+ return ret;
+}
+
+static int dvbt_set_profile(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dvbt_profile profile)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x67,
+ (profile == CXD2880_DVBT_PROFILE_HP)
+ ? 0x00 : 0x01);
+}
+
+int cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt_tune_param
+ *tune_param)
+{
+ int ret;
+
+ if (!tnr_dmd || !tune_param)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT,
+ tune_param->center_freq_khz,
+ tune_param->bandwidth, 0, 0);
+ if (ret)
+ return ret;
+
+ ret =
+ x_tune_dvbt_demod_setting(tnr_dmd, tune_param->bandwidth,
+ tnr_dmd->clk_mode);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ x_tune_dvbt_demod_setting(tnr_dmd->diver_sub,
+ tune_param->bandwidth,
+ tnr_dmd->diver_sub->clk_mode);
+ if (ret)
+ return ret;
+ }
+
+ return dvbt_set_profile(tnr_dmd, tune_param->profile);
+}
+
+int cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt_tune_param
+ *tune_param)
+{
+ int ret;
+
+ if (!tnr_dmd || !tune_param)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_common_tune_setting2(tnr_dmd, CXD2880_DTV_SYS_DVBT,
+ 0);
+ if (ret)
+ return ret;
+
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE;
+ tnr_dmd->frequency_khz = tune_param->center_freq_khz;
+ tnr_dmd->sys = CXD2880_DTV_SYS_DVBT;
+ tnr_dmd->bandwidth = tune_param->bandwidth;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE;
+ tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz;
+ tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT;
+ tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth;
+ }
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = x_sleep_dvbt_demod_setting(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret = x_sleep_dvbt_demod_setting(tnr_dmd->diver_sub);
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock)
+{
+ int ret;
+
+ u8 sync_stat = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 unlock_detected_sub = 0;
+
+ if (!tnr_dmd || !lock)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+ &unlock_detected);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ if (sync_stat == 6)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ else if (unlock_detected)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return 0;
+ }
+
+ if (sync_stat == 6) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ return 0;
+ }
+
+ 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 0;
+}
+
+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 0;
+ }
+
+ if (ts_lock) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ return 0;
+ } else if (!unlock_detected) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+ return 0;
+ }
+
+ 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 0;
+}
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
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..a9ab983348c8
--- /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 <media/dvb_frontend.h>
+
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq1[] = {
+ {0x00, 0x00}, {0x31, 0x02},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq2[] = {
+ {0x00, 0x04}, {0x5d, 0x0b},
+};
+
+static int x_tune_dvbt2_demod_setting(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dtv_bandwidth
+ bandwidth,
+ enum cxd2880_tnrdmd_clockmode
+ clk_mode)
+{
+ static const u8 tsif_settings[2] = { 0x01, 0x01 };
+ static const u8 init_settings[14] = {
+ 0x07, 0x06, 0x01, 0xf0, 0x00, 0x00, 0x04, 0xb0, 0x00, 0x00,
+ 0x09, 0x9c, 0x0e, 0x4c
+ };
+ static const u8 clk_mode_settings_a1[9] = {
+ 0x52, 0x49, 0x2c, 0x51, 0x51, 0x3d, 0x15, 0x29, 0x0c
+ };
+
+ static const u8 clk_mode_settings_b1[9] = {
+ 0x5d, 0x55, 0x32, 0x5c, 0x5c, 0x45, 0x17, 0x2e, 0x0d
+ };
+
+ static const u8 clk_mode_settings_c1[9] = {
+ 0x60, 0x00, 0x34, 0x5e, 0x5e, 0x47, 0x18, 0x2f, 0x0e
+ };
+
+ static const u8 clk_mode_settings_a2[13] = {
+ 0x04, 0xe7, 0x94, 0x92, 0x09, 0xcf, 0x7e, 0xd0, 0x49,
+ 0xcd, 0xcd, 0x1f, 0x5b
+ };
+
+ static const u8 clk_mode_settings_b2[13] = {
+ 0x05, 0x90, 0x27, 0x55, 0x0b, 0x20, 0x8f, 0xd6, 0xea,
+ 0xc8, 0xc8, 0x23, 0x91
+ };
+
+ static const u8 clk_mode_settings_c2[13] = {
+ 0x05, 0xb8, 0xd8, 0x00, 0x0b, 0x72, 0x93, 0xf3, 0x00,
+ 0xcd, 0xcd, 0x24, 0x95
+ };
+
+ static const u8 clk_mode_settings_a3[5] = {
+ 0x0b, 0x6a, 0xc9, 0x03, 0x33
+ };
+ static const u8 clk_mode_settings_b3[5] = {
+ 0x01, 0x02, 0xe4, 0x03, 0x39
+ };
+ static const u8 clk_mode_settings_c3[5] = {
+ 0x01, 0x02, 0xeb, 0x03, 0x3b
+ };
+
+ static const u8 gtdofst[2] = { 0x3f, 0xff };
+
+ static const u8 bw8_gtdofst_a[2] = { 0x19, 0xd2 };
+ static const u8 bw8_nomi_ac[6] = { 0x15, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const u8 bw8_nomi_b[6] = { 0x14, 0x6a, 0xaa, 0xaa, 0xab, 0x00 };
+ static const u8 bw8_sst_a[2] = { 0x06, 0x2a };
+ static const u8 bw8_sst_b[2] = { 0x06, 0x29 };
+ static const u8 bw8_sst_c[2] = { 0x06, 0x28 };
+ static const u8 bw8_mrc_a[9] = {
+ 0x28, 0x00, 0x50, 0x00, 0x60, 0x00, 0x00, 0x90, 0x00
+ };
+ static const u8 bw8_mrc_b[9] = {
+ 0x2d, 0x5e, 0x5a, 0xbd, 0x6c, 0xe3, 0x00, 0xa3, 0x55
+ };
+ static const u8 bw8_mrc_c[9] = {
+ 0x2e, 0xaa, 0x5d, 0x55, 0x70, 0x00, 0x00, 0xa8, 0x00
+ };
+
+ static const u8 bw7_nomi_ac[6] = { 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const u8 bw7_nomi_b[6] = { 0x17, 0x55, 0x55, 0x55, 0x55, 0x00 };
+ static const u8 bw7_sst_a[2] = { 0x06, 0x23 };
+ static const u8 bw7_sst_b[2] = { 0x06, 0x22 };
+ static const u8 bw7_sst_c[2] = { 0x06, 0x21 };
+ static const u8 bw7_mrc_a[9] = {
+ 0x2d, 0xb6, 0x5b, 0x6d, 0x6d, 0xb6, 0x00, 0xa4, 0x92
+ };
+ static const u8 bw7_mrc_b[9] = {
+ 0x33, 0xda, 0x67, 0xb4, 0x7c, 0x71, 0x00, 0xba, 0xaa
+ };
+ static const u8 bw7_mrc_c[9] = {
+ 0x35, 0x55, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xc0, 0x00
+ };
+
+ static const u8 bw6_nomi_ac[6] = { 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const u8 bw6_nomi_b[6] = { 0x1b, 0x38, 0xe3, 0x8e, 0x39, 0x00 };
+ static const u8 bw6_sst_a[2] = { 0x06, 0x1c };
+ static const u8 bw6_sst_b[2] = { 0x06, 0x1b };
+ static const u8 bw6_sst_c[2] = { 0x06, 0x1a };
+ static const u8 bw6_mrc_a[9] = {
+ 0x35, 0x55, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xc0, 0x00
+ };
+ static const u8 bw6_mrc_b[9] = {
+ 0x3c, 0x7e, 0x78, 0xfc, 0x91, 0x2f, 0x00, 0xd9, 0xc7
+ };
+ static const u8 bw6_mrc_c[9] = {
+ 0x3e, 0x38, 0x7c, 0x71, 0x95, 0x55, 0x00, 0xdf, 0xff
+ };
+
+ static const u8 bw5_nomi_ac[6] = { 0x21, 0x99, 0x99, 0x99, 0x9a, 0x00 };
+ static const u8 bw5_nomi_b[6] = { 0x20, 0xaa, 0xaa, 0xaa, 0xab, 0x00 };
+ static const u8 bw5_sst_a[2] = { 0x06, 0x15 };
+ static const u8 bw5_sst_b[2] = { 0x06, 0x15 };
+ static const u8 bw5_sst_c[2] = { 0x06, 0x14 };
+ static const u8 bw5_mrc_a[9] = {
+ 0x40, 0x00, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xe6, 0x66
+ };
+ static const u8 bw5_mrc_b[9] = {
+ 0x48, 0x97, 0x78, 0xfc, 0x91, 0x2f, 0x01, 0x05, 0x55
+ };
+ static const u8 bw5_mrc_c[9] = {
+ 0x4a, 0xaa, 0x7c, 0x71, 0x95, 0x55, 0x01, 0x0c, 0xcc
+ };
+
+ static const u8 bw1_7_nomi_a[6] = {
+ 0x68, 0x0f, 0xa2, 0x32, 0xcf, 0x03
+ };
+ static const u8 bw1_7_nomi_c[6] = {
+ 0x68, 0x0f, 0xa2, 0x32, 0xcf, 0x03
+ };
+ static const u8 bw1_7_nomi_b[6] = {
+ 0x65, 0x2b, 0xa4, 0xcd, 0xd8, 0x03
+ };
+ static const u8 bw1_7_sst_a[2] = { 0x06, 0x0c };
+ static const u8 bw1_7_sst_b[2] = { 0x06, 0x0c };
+ static const u8 bw1_7_sst_c[2] = { 0x06, 0x0b };
+ static const u8 bw1_7_mrc_a[9] = {
+ 0x40, 0x00, 0x6a, 0xaa, 0x80, 0x00, 0x02, 0xc9, 0x8f
+ };
+ static const u8 bw1_7_mrc_b[9] = {
+ 0x48, 0x97, 0x78, 0xfc, 0x91, 0x2f, 0x03, 0x29, 0x5d
+ };
+ static const u8 bw1_7_mrc_c[9] = {
+ 0x4a, 0xaa, 0x7c, 0x71, 0x95, 0x55, 0x03, 0x40, 0x7d
+ };
+
+ const u8 *data = NULL;
+ const u8 *data2 = NULL;
+ const u8 *data3 = NULL;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ tune_dmd_setting_seq1,
+ ARRAY_SIZE(tune_dmd_setting_seq1));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ tune_dmd_setting_seq2,
+ ARRAY_SIZE(tune_dmd_setting_seq2));
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xce, tsif_settings, 2);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x20);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x8a, init_settings[0]);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x90, init_settings[1]);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x25);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xf0, &init_settings[2], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x2a);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xdc, init_settings[4]);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xde, init_settings[5]);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x2d);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x73, &init_settings[6], 4);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x8f, &init_settings[10], 4);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = clk_mode_settings_a1;
+ data2 = clk_mode_settings_a2;
+ data3 = clk_mode_settings_a3;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = clk_mode_settings_b1;
+ data2 = clk_mode_settings_b2;
+ data3 = clk_mode_settings_b3;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = clk_mode_settings_c1;
+ data2 = clk_mode_settings_c2;
+ data3 = clk_mode_settings_c3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x04);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1d, &data[0], 3);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x22, data[3]);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x24, data[4]);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x26, data[5]);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x29, &data[6], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x2d, data[8]);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x2e, &data2[0], 6);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x35, &data2[6], 7);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x3c, &data3[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x56, &data3[2], 3);
+ if (ret)
+ return ret;
+
+ switch (bandwidth) {
+ case CXD2880_DTV_BW_8_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw8_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw8_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, data, 6);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x00);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw8_gtdofst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = gtdofst;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x19, data, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw8_sst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw8_sst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw8_sst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1b, data, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw8_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw8_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw8_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, data, 9);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case CXD2880_DTV_BW_7_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw7_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw7_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, data, 6);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x02);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x19, gtdofst, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw7_sst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw7_sst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw7_sst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1b, data, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw7_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw7_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw7_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, data, 9);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case CXD2880_DTV_BW_6_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw6_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw6_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, data, 6);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x04);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x19, gtdofst, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw6_sst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw6_sst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw6_sst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1b, data, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw6_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw6_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw6_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, data, 9);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case CXD2880_DTV_BW_5_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw5_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw5_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, data, 6);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x06);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x19, gtdofst, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw5_sst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw5_sst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw5_sst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1b, data, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw5_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw5_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw5_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, data, 9);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case CXD2880_DTV_BW_1_7_MHZ:
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw1_7_nomi_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw1_7_nomi_c;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw1_7_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, data, 6);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x03);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x19, gtdofst, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw1_7_sst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw1_7_sst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw1_7_sst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1b, data, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw1_7_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw1_7_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw1_7_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, data, 9);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xfd, 0x01);
+}
+
+static int x_sleep_dvbt2_demod_setting(struct cxd2880_tnrdmd
+ *tnr_dmd)
+{
+ static const u8 difint_clip[] = {
+ 0, 1, 0, 2, 0, 4, 0, 8, 0, 16, 0, 32
+ };
+ int ret = 0;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x1d);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x47, difint_clip, 12);
+ }
+
+ return ret;
+}
+
+static int dvbt2_set_profile(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dvbt2_profile profile)
+{
+ u8 t2_mode_tune_mode = 0;
+ u8 seq_not2_dtime = 0;
+ u8 dtime1 = 0;
+ u8 dtime2 = 0;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ switch (tnr_dmd->clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ dtime1 = 0x27;
+ dtime2 = 0x0c;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ dtime1 = 0x2c;
+ dtime2 = 0x0d;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ dtime1 = 0x2e;
+ dtime2 = 0x0e;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (profile) {
+ case CXD2880_DVBT2_PROFILE_BASE:
+ t2_mode_tune_mode = 0x01;
+ seq_not2_dtime = dtime2;
+ break;
+
+ case CXD2880_DVBT2_PROFILE_LITE:
+ t2_mode_tune_mode = 0x05;
+ seq_not2_dtime = dtime1;
+ break;
+
+ case CXD2880_DVBT2_PROFILE_ANY:
+ t2_mode_tune_mode = 0x00;
+ seq_not2_dtime = dtime1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x2e);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, t2_mode_tune_mode);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x04);
+ if (ret)
+ return ret;
+
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x2c, seq_not2_dtime);
+}
+
+int cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_tune_param
+ *tune_param)
+{
+ int ret;
+
+ if (!tnr_dmd || !tune_param)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN &&
+ tune_param->profile == CXD2880_DVBT2_PROFILE_ANY)
+ return -ENOTTY;
+
+ ret =
+ cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT2,
+ tune_param->center_freq_khz,
+ tune_param->bandwidth, 0, 0);
+ if (ret)
+ return ret;
+
+ ret =
+ x_tune_dvbt2_demod_setting(tnr_dmd, tune_param->bandwidth,
+ tnr_dmd->clk_mode);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ x_tune_dvbt2_demod_setting(tnr_dmd->diver_sub,
+ tune_param->bandwidth,
+ tnr_dmd->diver_sub->clk_mode);
+ if (ret)
+ return ret;
+ }
+
+ ret = dvbt2_set_profile(tnr_dmd, tune_param->profile);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ dvbt2_set_profile(tnr_dmd->diver_sub, tune_param->profile);
+ if (ret)
+ return ret;
+ }
+
+ if (tune_param->data_plp_id == CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO)
+ ret = cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 1, 0);
+ else
+ ret =
+ cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 0,
+ (u8)(tune_param->data_plp_id));
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_tune_param
+ *tune_param)
+{
+ u8 en_fef_intmtnt_ctrl = 1;
+ int ret;
+
+ if (!tnr_dmd || !tune_param)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ switch (tune_param->profile) {
+ case CXD2880_DVBT2_PROFILE_BASE:
+ en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_base;
+ break;
+ case CXD2880_DVBT2_PROFILE_LITE:
+ en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_lite;
+ break;
+ case CXD2880_DVBT2_PROFILE_ANY:
+ if (tnr_dmd->en_fef_intmtnt_base &&
+ tnr_dmd->en_fef_intmtnt_lite)
+ en_fef_intmtnt_ctrl = 1;
+ else
+ en_fef_intmtnt_ctrl = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret =
+ cxd2880_tnrdmd_common_tune_setting2(tnr_dmd,
+ CXD2880_DTV_SYS_DVBT2,
+ en_fef_intmtnt_ctrl);
+ if (ret)
+ return ret;
+
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE;
+ tnr_dmd->frequency_khz = tune_param->center_freq_khz;
+ tnr_dmd->sys = CXD2880_DTV_SYS_DVBT2;
+ tnr_dmd->bandwidth = tune_param->bandwidth;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE;
+ tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz;
+ tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT2;
+ tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth;
+ }
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd
+ *tnr_dmd)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = x_sleep_dvbt2_demod_setting(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret = x_sleep_dvbt2_demod_setting(tnr_dmd->diver_sub);
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock)
+{
+ int ret;
+
+ u8 sync_stat = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 unlock_detected_sub = 0;
+
+ if (!tnr_dmd || !lock)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+ &unlock_detected);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ if (sync_stat == 6)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ else if (unlock_detected)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return 0;
+ }
+
+ if (sync_stat == 6) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ return 0;
+ }
+
+ 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 0;
+}
+
+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 0;
+ }
+
+ if (ts_lock) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ return 0;
+ } else if (!unlock_detected) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+ return 0;
+ }
+
+ 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 0;
+}
+
+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
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..4e173dd87ecf
--- /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 <linux/int_log.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
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..86d5a1e4022a
--- /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 <linux/int_log.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
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
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..a06d8368ca79
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
@@ -0,0 +1,1957 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_top.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
+
+#include <linux/spi/spi.h>
+
+#include <media/dvb_frontend.h>
+#include <linux/int_log.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;
+ }
+
+ ret = cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_TSPIN_CURRENT,
+ 0x00);
+ if (ret) {
+ mutex_unlock(priv->spi_mutex);
+ pr_err("cxd2880 set config failed %d\n", ret);
+ return ret;
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ pr_debug("OK.\n");
+
+ return ret;
+}
+
+static int cxd2880_sleep(struct dvb_frontend *fe)
+{
+ int ret;
+ struct cxd2880_priv *priv = NULL;
+
+ if (!fe) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_sleep(&priv->tnrdmd);
+ mutex_unlock(priv->spi_mutex);
+
+ pr_debug("tnrdmd_sleep ret %d\n", ret);
+
+ return ret;
+}
+
+static int cxd2880_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ int ret;
+ struct cxd2880_priv *priv = NULL;
+ struct dtv_frontend_properties *c = NULL;
+ int level = 0;
+
+ if (!fe || !strength) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT ||
+ c->delivery_system == SYS_DVBT2) {
+ ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &level);
+ } else {
+ pr_debug("invalid system\n");
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ level /= 125;
+ /*
+ * level should be between -105dBm and -30dBm.
+ * E.g. they should be between:
+ * -105000/125 = -840 and -30000/125 = -240
+ */
+ level = clamp(level, -840, -240);
+ /* scale value to 0x0000-0xffff */
+ *strength = ((level + 840) * 0xffff) / (-240 + 840);
+
+ if (ret)
+ pr_debug("ret = %d\n", ret);
+
+ return ret;
+}
+
+static int cxd2880_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ int ret;
+ int snrvalue = 0;
+ struct cxd2880_priv *priv = NULL;
+ struct dtv_frontend_properties *c = NULL;
+
+ if (!fe || !snr) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT) {
+ ret = cxd2880_tnrdmd_dvbt_mon_snr(&priv->tnrdmd,
+ &snrvalue);
+ } else if (c->delivery_system == SYS_DVBT2) {
+ ret = cxd2880_tnrdmd_dvbt2_mon_snr(&priv->tnrdmd,
+ &snrvalue);
+ } else {
+ pr_err("invalid system\n");
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ if (snrvalue < 0)
+ snrvalue = 0;
+ *snr = snrvalue;
+
+ if (ret)
+ pr_debug("ret = %d\n", ret);
+
+ return ret;
+}
+
+static int cxd2880_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ int ret;
+ struct cxd2880_priv *priv = NULL;
+ struct dtv_frontend_properties *c = NULL;
+
+ if (!fe || !ucblocks) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT) {
+ ret = cxd2880_tnrdmd_dvbt_mon_packet_error_number(&priv->tnrdmd,
+ ucblocks);
+ } else if (c->delivery_system == SYS_DVBT2) {
+ ret = cxd2880_tnrdmd_dvbt2_mon_packet_error_number(&priv->tnrdmd,
+ ucblocks);
+ } else {
+ pr_err("invalid system\n");
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ if (ret)
+ pr_debug("ret = %d\n", ret);
+
+ return ret;
+}
+
+static int cxd2880_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ *ber = 0;
+
+ return 0;
+}
+
+static int cxd2880_set_ber_per_period_t(struct dvb_frontend *fe)
+{
+ int ret;
+ struct cxd2880_priv *priv;
+ struct cxd2880_dvbt_tpsinfo info;
+ enum cxd2880_dtv_bandwidth bw;
+ u32 pre_ber_rate = 0;
+ u32 post_ber_rate = 0;
+ u32 ucblock_rate = 0;
+ u32 mes_exp = 0;
+ static const int cr_table[5] = {31500, 42000, 47250, 52500, 55125};
+ static const int denominator_tbl[4] = {125664, 129472, 137088, 152320};
+
+ if (!fe) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ bw = priv->dvbt_tune_param.bandwidth;
+
+ ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd,
+ &info);
+ if (ret) {
+ pr_err("tps monitor error ret = %d\n", ret);
+ info.hierarchy = CXD2880_DVBT_HIERARCHY_NON;
+ info.constellation = CXD2880_DVBT_CONSTELLATION_QPSK;
+ info.guard = CXD2880_DVBT_GUARD_1_4;
+ info.rate_hp = CXD2880_DVBT_CODERATE_1_2;
+ info.rate_lp = CXD2880_DVBT_CODERATE_1_2;
+ }
+
+ if (info.hierarchy == CXD2880_DVBT_HIERARCHY_NON) {
+ pre_ber_rate = 63000000 * bw * (info.constellation * 2 + 2) /
+ denominator_tbl[info.guard];
+
+ post_ber_rate = 1000 * cr_table[info.rate_hp] * bw *
+ (info.constellation * 2 + 2) /
+ denominator_tbl[info.guard];
+
+ ucblock_rate = 875 * cr_table[info.rate_hp] * bw *
+ (info.constellation * 2 + 2) /
+ denominator_tbl[info.guard];
+ } else {
+ u8 data = 0;
+ struct cxd2880_tnrdmd *tnrdmd = &priv->tnrdmd;
+
+ ret = tnrdmd->io->write_reg(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x10);
+ if (!ret) {
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x67, &data, 1);
+ if (ret)
+ data = 0x00;
+ } else {
+ data = 0x00;
+ }
+
+ if (data & 0x01) { /* Low priority */
+ pre_ber_rate =
+ 63000000 * bw * (info.constellation * 2 + 2) /
+ denominator_tbl[info.guard];
+
+ post_ber_rate = 1000 * cr_table[info.rate_lp] * bw *
+ (info.constellation * 2 + 2) /
+ denominator_tbl[info.guard];
+
+ ucblock_rate = (1000 * 7 / 8) * cr_table[info.rate_lp] *
+ bw * (info.constellation * 2 + 2) /
+ denominator_tbl[info.guard];
+ } else { /* High priority */
+ pre_ber_rate =
+ 63000000 * bw * 2 / denominator_tbl[info.guard];
+
+ post_ber_rate = 1000 * cr_table[info.rate_hp] * bw * 2 /
+ denominator_tbl[info.guard];
+
+ ucblock_rate = (1000 * 7 / 8) * cr_table[info.rate_hp] *
+ bw * 2 / denominator_tbl[info.guard];
+ }
+ }
+
+ mes_exp = pre_ber_rate < 8192 ? 8 : intlog2(pre_ber_rate) >> 24;
+ priv->pre_ber_interval =
+ ((1U << mes_exp) * 1000 + (pre_ber_rate / 2)) /
+ pre_ber_rate;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD,
+ mes_exp == 8 ? 0 : mes_exp - 12);
+
+ mes_exp = intlog2(post_ber_rate) >> 24;
+ priv->post_ber_interval =
+ ((1U << mes_exp) * 1000 + (post_ber_rate / 2)) /
+ post_ber_rate;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD,
+ mes_exp);
+
+ mes_exp = intlog2(ucblock_rate) >> 24;
+ priv->ucblock_interval =
+ ((1U << mes_exp) * 1000 + (ucblock_rate / 2)) /
+ ucblock_rate;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT_PER_MES,
+ mes_exp);
+
+ return 0;
+}
+
+static int cxd2880_set_ber_per_period_t2(struct dvb_frontend *fe)
+{
+ int ret;
+ struct cxd2880_priv *priv;
+ struct cxd2880_dvbt2_l1pre l1pre;
+ struct cxd2880_dvbt2_l1post l1post;
+ struct cxd2880_dvbt2_plp plp;
+ struct cxd2880_dvbt2_bbheader bbheader;
+ enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
+ u32 pre_ber_rate = 0;
+ u32 post_ber_rate = 0;
+ u32 ucblock_rate = 0;
+ u32 mes_exp = 0;
+ u32 term_a = 0;
+ u32 term_b = 0;
+ u32 denominator = 0;
+ static const u32 gi_tbl[7] = {32, 64, 128, 256, 8, 152, 76};
+ static const u8 n_tbl[6] = {8, 2, 4, 16, 1, 1};
+ static const u8 mode_tbl[6] = {2, 8, 4, 1, 16, 32};
+ static const u32 kbch_tbl[2][8] = {
+ {6952, 9472, 10552, 11632, 12352, 13072, 5152, 6232},
+ {32128, 38608, 42960, 48328, 51568, 53760, 0, 0}
+ };
+
+ if (!fe) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ bw = priv->dvbt2_tune_param.bandwidth;
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre);
+ if (ret) {
+ pr_info("l1 pre error\n");
+ goto error_ber_setting;
+ }
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_active_plp(&priv->tnrdmd,
+ CXD2880_DVBT2_PLP_DATA, &plp);
+ if (ret) {
+ pr_info("plp info error\n");
+ goto error_ber_setting;
+ }
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_l1_post(&priv->tnrdmd, &l1post);
+ if (ret) {
+ pr_info("l1 post error\n");
+ goto error_ber_setting;
+ }
+
+ term_a =
+ (mode_tbl[l1pre.fft_mode] * (1024 + gi_tbl[l1pre.gi])) *
+ (l1pre.num_symbols + n_tbl[l1pre.fft_mode]) + 2048;
+
+ if (l1pre.mixed && l1post.fef_intvl) {
+ term_b = (l1post.fef_length + (l1post.fef_intvl / 2)) /
+ l1post.fef_intvl;
+ } else {
+ term_b = 0;
+ }
+
+ switch (bw) {
+ case CXD2880_DTV_BW_1_7_MHZ:
+ denominator = ((term_a + term_b) * 71 + (131 / 2)) / 131;
+ break;
+ case CXD2880_DTV_BW_5_MHZ:
+ denominator = ((term_a + term_b) * 7 + 20) / 40;
+ break;
+ case CXD2880_DTV_BW_6_MHZ:
+ denominator = ((term_a + term_b) * 7 + 24) / 48;
+ break;
+ case CXD2880_DTV_BW_7_MHZ:
+ denominator = ((term_a + term_b) + 4) / 8;
+ break;
+ case CXD2880_DTV_BW_8_MHZ:
+ default:
+ denominator = ((term_a + term_b) * 7 + 32) / 64;
+ break;
+ }
+
+ if (plp.til_type && plp.til_len) {
+ pre_ber_rate =
+ (plp.num_blocks_max * 1000000 + (denominator / 2)) /
+ denominator;
+ pre_ber_rate = (pre_ber_rate + (plp.til_len / 2)) /
+ plp.til_len;
+ } else {
+ pre_ber_rate =
+ (plp.num_blocks_max * 1000000 + (denominator / 2)) /
+ denominator;
+ }
+
+ post_ber_rate = pre_ber_rate;
+
+ mes_exp = intlog2(pre_ber_rate) >> 24;
+ priv->pre_ber_interval =
+ ((1U << mes_exp) * 1000 + (pre_ber_rate / 2)) /
+ pre_ber_rate;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT2_LBER_MES,
+ mes_exp);
+
+ mes_exp = intlog2(post_ber_rate) >> 24;
+ priv->post_ber_interval =
+ ((1U << mes_exp) * 1000 + (post_ber_rate / 2)) /
+ post_ber_rate;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT2_BBER_MES,
+ mes_exp);
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_bbheader(&priv->tnrdmd,
+ CXD2880_DVBT2_PLP_DATA,
+ &bbheader);
+ if (ret) {
+ pr_info("bb header error\n");
+ goto error_ucblock_setting;
+ }
+
+ if (bbheader.plp_mode == CXD2880_DVBT2_PLP_MODE_NM) {
+ if (!bbheader.issy_indicator) {
+ ucblock_rate =
+ (pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] +
+ 752) / 1504;
+ } else {
+ ucblock_rate =
+ (pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] +
+ 764) / 1528;
+ }
+ } else if (bbheader.plp_mode == CXD2880_DVBT2_PLP_MODE_HEM) {
+ ucblock_rate =
+ (pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] + 748) /
+ 1496;
+ } else {
+ pr_info("plp mode is not Normal or HEM\n");
+ goto error_ucblock_setting;
+ }
+
+ mes_exp = intlog2(ucblock_rate) >> 24;
+ priv->ucblock_interval =
+ ((1U << mes_exp) * 1000 + (ucblock_rate / 2)) /
+ ucblock_rate;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT2_PER_MES,
+ mes_exp);
+
+ return 0;
+
+error_ber_setting:
+ priv->pre_ber_interval = 1000;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT2_LBER_MES, 0);
+
+ priv->post_ber_interval = 1000;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT2_BBER_MES, 0);
+
+error_ucblock_setting:
+ priv->ucblock_interval = 1000;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT2_PER_MES, 8);
+
+ return 0;
+}
+
+static int cxd2880_dvbt_tune(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt_tune_param
+ *tune_param)
+{
+ int ret;
+
+ if (!tnr_dmd || !tune_param)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ atomic_set(&tnr_dmd->cancel, 0);
+
+ if (tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ &&
+ tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ &&
+ tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ &&
+ tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ) {
+ return -ENOTTY;
+ }
+
+ ret = cxd2880_tnrdmd_dvbt_tune1(tnr_dmd, tune_param);
+ if (ret)
+ return ret;
+
+ usleep_range(CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000,
+ CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000 + 1000);
+
+ return cxd2880_tnrdmd_dvbt_tune2(tnr_dmd, tune_param);
+}
+
+static int cxd2880_dvbt2_tune(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_tune_param
+ *tune_param)
+{
+ int ret;
+
+ if (!tnr_dmd || !tune_param)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ atomic_set(&tnr_dmd->cancel, 0);
+
+ if (tune_param->bandwidth != CXD2880_DTV_BW_1_7_MHZ &&
+ tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ &&
+ tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ &&
+ tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ &&
+ tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ) {
+ return -ENOTTY;
+ }
+
+ if (tune_param->profile != CXD2880_DVBT2_PROFILE_BASE &&
+ tune_param->profile != CXD2880_DVBT2_PROFILE_LITE)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_dvbt2_tune1(tnr_dmd, tune_param);
+ if (ret)
+ return ret;
+
+ usleep_range(CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000,
+ CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000 + 1000);
+
+ return cxd2880_tnrdmd_dvbt2_tune2(tnr_dmd, tune_param);
+}
+
+static int cxd2880_set_frontend(struct dvb_frontend *fe)
+{
+ int ret;
+ struct dtv_frontend_properties *c;
+ struct cxd2880_priv *priv;
+ enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
+
+ if (!fe) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->pre_bit_error.stat[0].uvalue = 0;
+ c->pre_bit_error.len = 1;
+ c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->pre_bit_count.stat[0].uvalue = 0;
+ c->pre_bit_count.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_error.stat[0].uvalue = 0;
+ c->post_bit_error.len = 1;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].uvalue = 0;
+ c->post_bit_count.len = 1;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[0].uvalue = 0;
+ c->block_error.len = 1;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].uvalue = 0;
+ c->block_count.len = 1;
+
+ switch (c->bandwidth_hz) {
+ case 1712000:
+ bw = CXD2880_DTV_BW_1_7_MHZ;
+ break;
+ case 5000000:
+ bw = CXD2880_DTV_BW_5_MHZ;
+ break;
+ case 6000000:
+ bw = CXD2880_DTV_BW_6_MHZ;
+ break;
+ case 7000000:
+ bw = CXD2880_DTV_BW_7_MHZ;
+ break;
+ case 8000000:
+ bw = CXD2880_DTV_BW_8_MHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ priv->s = 0;
+
+ pr_info("sys:%d freq:%d bw:%d\n",
+ c->delivery_system, c->frequency, bw);
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT) {
+ priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT;
+ priv->dvbt_tune_param.center_freq_khz = c->frequency / 1000;
+ priv->dvbt_tune_param.bandwidth = bw;
+ priv->dvbt_tune_param.profile = CXD2880_DVBT_PROFILE_HP;
+ ret = cxd2880_dvbt_tune(&priv->tnrdmd,
+ &priv->dvbt_tune_param);
+ } else if (c->delivery_system == SYS_DVBT2) {
+ priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT2;
+ priv->dvbt2_tune_param.center_freq_khz = c->frequency / 1000;
+ priv->dvbt2_tune_param.bandwidth = bw;
+ priv->dvbt2_tune_param.data_plp_id = (u16)c->stream_id;
+ priv->dvbt2_tune_param.profile = CXD2880_DVBT2_PROFILE_BASE;
+ ret = cxd2880_dvbt2_tune(&priv->tnrdmd,
+ &priv->dvbt2_tune_param);
+ } else {
+ pr_err("invalid system\n");
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ pr_info("tune result %d\n", ret);
+
+ return ret;
+}
+
+static int cxd2880_get_stats(struct dvb_frontend *fe,
+ enum fe_status status)
+{
+ struct cxd2880_priv *priv = NULL;
+ struct dtv_frontend_properties *c = NULL;
+ u32 pre_bit_err = 0, pre_bit_count = 0;
+ u32 post_bit_err = 0, post_bit_count = 0;
+ u32 block_err = 0, block_count = 0;
+ int ret;
+
+ if (!fe) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ if (!(status & FE_HAS_LOCK) || !(status & FE_HAS_CARRIER)) {
+ 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) &&
+ (*status & FE_HAS_CARRIER)) {
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT) {
+ ret = cxd2880_set_ber_per_period_t(fe);
+ priv->s = *status;
+ } else if (c->delivery_system == SYS_DVBT2) {
+ ret = cxd2880_check_l1post_plp(fe);
+ if (!ret) {
+ ret = cxd2880_set_ber_per_period_t2(fe);
+ priv->s = *status;
+ }
+ } else {
+ pr_err("invalid system\n");
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+ }
+
+ cxd2880_get_stats(fe, *status);
+ return 0;
+}
+
+static int cxd2880_tune(struct dvb_frontend *fe,
+ bool retune,
+ unsigned int mode_flags,
+ unsigned int *delay,
+ enum fe_status *status)
+{
+ int ret;
+
+ if (!fe || !delay || !status) {
+ pr_err("invalid arg.");
+ return -EINVAL;
+ }
+
+ if (retune) {
+ ret = cxd2880_set_frontend(fe);
+ if (ret) {
+ pr_err("cxd2880_set_frontend failed %d\n", ret);
+ return ret;
+ }
+ }
+
+ *delay = HZ / 5;
+
+ return cxd2880_read_status(fe, status);
+}
+
+static int cxd2880_get_frontend_t(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ int ret;
+ struct cxd2880_priv *priv = NULL;
+ enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K;
+ enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32;
+ struct cxd2880_dvbt_tpsinfo tps;
+ enum cxd2880_tnrdmd_spectrum_sense sense;
+ u16 snr = 0;
+ int strength = 0;
+
+ if (!fe || !c) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd,
+ &mode, &guard);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ switch (mode) {
+ case CXD2880_DVBT_MODE_2K:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case CXD2880_DVBT_MODE_8K:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ default:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ pr_debug("transmission mode is invalid %d\n", mode);
+ break;
+ }
+ switch (guard) {
+ case CXD2880_DVBT_GUARD_1_32:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case CXD2880_DVBT_GUARD_1_16:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case CXD2880_DVBT_GUARD_1_8:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case CXD2880_DVBT_GUARD_1_4:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ default:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ pr_debug("guard interval is invalid %d\n",
+ guard);
+ break;
+ }
+ } else {
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ pr_debug("ModeGuard err %d\n", ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ switch (tps.hierarchy) {
+ case CXD2880_DVBT_HIERARCHY_NON:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case CXD2880_DVBT_HIERARCHY_1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case CXD2880_DVBT_HIERARCHY_2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case CXD2880_DVBT_HIERARCHY_4:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ default:
+ c->hierarchy = HIERARCHY_NONE;
+ pr_debug("TPSInfo hierarchy is invalid %d\n",
+ tps.hierarchy);
+ break;
+ }
+
+ switch (tps.rate_hp) {
+ case CXD2880_DVBT_CODERATE_1_2:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case CXD2880_DVBT_CODERATE_2_3:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case CXD2880_DVBT_CODERATE_3_4:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case CXD2880_DVBT_CODERATE_5_6:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case CXD2880_DVBT_CODERATE_7_8:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ default:
+ c->code_rate_HP = FEC_NONE;
+ pr_debug("TPSInfo rateHP is invalid %d\n",
+ tps.rate_hp);
+ break;
+ }
+ switch (tps.rate_lp) {
+ case CXD2880_DVBT_CODERATE_1_2:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case CXD2880_DVBT_CODERATE_2_3:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case CXD2880_DVBT_CODERATE_3_4:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case CXD2880_DVBT_CODERATE_5_6:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case CXD2880_DVBT_CODERATE_7_8:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ default:
+ c->code_rate_LP = FEC_NONE;
+ pr_debug("TPSInfo rateLP is invalid %d\n",
+ tps.rate_lp);
+ break;
+ }
+ switch (tps.constellation) {
+ case CXD2880_DVBT_CONSTELLATION_QPSK:
+ c->modulation = QPSK;
+ break;
+ case CXD2880_DVBT_CONSTELLATION_16QAM:
+ c->modulation = QAM_16;
+ break;
+ case CXD2880_DVBT_CONSTELLATION_64QAM:
+ c->modulation = QAM_64;
+ break;
+ default:
+ c->modulation = QPSK;
+ pr_debug("TPSInfo constellation is invalid %d\n",
+ tps.constellation);
+ break;
+ }
+ } else {
+ c->hierarchy = HIERARCHY_NONE;
+ c->code_rate_HP = FEC_NONE;
+ c->code_rate_LP = FEC_NONE;
+ c->modulation = QPSK;
+ pr_debug("TPS info err %d\n", ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ switch (sense) {
+ case CXD2880_TNRDMD_SPECTRUM_NORMAL:
+ c->inversion = INVERSION_OFF;
+ break;
+ case CXD2880_TNRDMD_SPECTRUM_INV:
+ c->inversion = INVERSION_ON;
+ break;
+ default:
+ c->inversion = INVERSION_OFF;
+ pr_debug("spectrum sense is invalid %d\n", sense);
+ break;
+ }
+ } else {
+ c->inversion = INVERSION_OFF;
+ pr_debug("spectrum_sense %d\n", ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->strength.stat[0].svalue = strength;
+ } else {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ pr_debug("mon_rf_lvl %d\n", ret);
+ }
+
+ ret = cxd2880_read_snr(fe, &snr);
+ if (!ret) {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = snr;
+ } else {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ pr_debug("read_snr %d\n", ret);
+ }
+
+ return 0;
+}
+
+static int cxd2880_get_frontend_t2(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ int ret;
+ struct cxd2880_priv *priv = NULL;
+ struct cxd2880_dvbt2_l1pre l1pre;
+ enum cxd2880_dvbt2_plp_code_rate coderate;
+ enum cxd2880_dvbt2_plp_constell qam;
+ enum cxd2880_tnrdmd_spectrum_sense sense;
+ u16 snr = 0;
+ int strength = 0;
+
+ if (!fe || !c) {
+ pr_err("invalid arg.\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ switch (l1pre.fft_mode) {
+ case CXD2880_DVBT2_M2K:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case CXD2880_DVBT2_M8K:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ case CXD2880_DVBT2_M4K:
+ c->transmission_mode = TRANSMISSION_MODE_4K;
+ break;
+ case CXD2880_DVBT2_M1K:
+ c->transmission_mode = TRANSMISSION_MODE_1K;
+ break;
+ case CXD2880_DVBT2_M16K:
+ c->transmission_mode = TRANSMISSION_MODE_16K;
+ break;
+ case CXD2880_DVBT2_M32K:
+ c->transmission_mode = TRANSMISSION_MODE_32K;
+ break;
+ default:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ pr_debug("L1Pre fft_mode is invalid %d\n",
+ l1pre.fft_mode);
+ break;
+ }
+ switch (l1pre.gi) {
+ case CXD2880_DVBT2_G1_32:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case CXD2880_DVBT2_G1_16:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case CXD2880_DVBT2_G1_8:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case CXD2880_DVBT2_G1_4:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ case CXD2880_DVBT2_G1_128:
+ c->guard_interval = GUARD_INTERVAL_1_128;
+ break;
+ case CXD2880_DVBT2_G19_128:
+ c->guard_interval = GUARD_INTERVAL_19_128;
+ break;
+ case CXD2880_DVBT2_G19_256:
+ c->guard_interval = GUARD_INTERVAL_19_256;
+ break;
+ default:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ pr_debug("L1Pre guard interval is invalid %d\n",
+ l1pre.gi);
+ break;
+ }
+ } else {
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ pr_debug("L1Pre err %d\n", ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt2_mon_code_rate(&priv->tnrdmd,
+ CXD2880_DVBT2_PLP_DATA,
+ &coderate);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ switch (coderate) {
+ case CXD2880_DVBT2_R1_2:
+ c->fec_inner = FEC_1_2;
+ break;
+ case CXD2880_DVBT2_R3_5:
+ c->fec_inner = FEC_3_5;
+ break;
+ case CXD2880_DVBT2_R2_3:
+ c->fec_inner = FEC_2_3;
+ break;
+ case CXD2880_DVBT2_R3_4:
+ c->fec_inner = FEC_3_4;
+ break;
+ case CXD2880_DVBT2_R4_5:
+ c->fec_inner = FEC_4_5;
+ break;
+ case CXD2880_DVBT2_R5_6:
+ c->fec_inner = FEC_5_6;
+ break;
+ default:
+ c->fec_inner = FEC_NONE;
+ pr_debug("CodeRate is invalid %d\n", coderate);
+ break;
+ }
+ } else {
+ c->fec_inner = FEC_NONE;
+ pr_debug("CodeRate %d\n", ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt2_mon_qam(&priv->tnrdmd,
+ CXD2880_DVBT2_PLP_DATA,
+ &qam);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ switch (qam) {
+ case CXD2880_DVBT2_QPSK:
+ c->modulation = QPSK;
+ break;
+ case CXD2880_DVBT2_QAM16:
+ c->modulation = QAM_16;
+ break;
+ case CXD2880_DVBT2_QAM64:
+ c->modulation = QAM_64;
+ break;
+ case CXD2880_DVBT2_QAM256:
+ c->modulation = QAM_256;
+ break;
+ default:
+ c->modulation = QPSK;
+ pr_debug("QAM is invalid %d\n", qam);
+ break;
+ }
+ } else {
+ c->modulation = QPSK;
+ pr_debug("QAM %d\n", ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(&priv->tnrdmd, &sense);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ switch (sense) {
+ case CXD2880_TNRDMD_SPECTRUM_NORMAL:
+ c->inversion = INVERSION_OFF;
+ break;
+ case CXD2880_TNRDMD_SPECTRUM_INV:
+ c->inversion = INVERSION_ON;
+ break;
+ default:
+ c->inversion = INVERSION_OFF;
+ pr_debug("spectrum sense is invalid %d\n", sense);
+ break;
+ }
+ } else {
+ c->inversion = INVERSION_OFF;
+ pr_debug("SpectrumSense %d\n", ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->strength.stat[0].svalue = strength;
+ } else {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ pr_debug("mon_rf_lvl %d\n", ret);
+ }
+
+ ret = cxd2880_read_snr(fe, &snr);
+ if (!ret) {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = snr;
+ } else {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ pr_debug("read_snr %d\n", ret);
+ }
+
+ return 0;
+}
+
+static int cxd2880_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *props)
+{
+ int ret;
+
+ if (!fe || !props) {
+ pr_err("invalid arg.");
+ return -EINVAL;
+ }
+
+ pr_debug("system=%d\n", fe->dtv_property_cache.delivery_system);
+ switch (fe->dtv_property_cache.delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2880_get_frontend_t(fe, props);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2880_get_frontend_t2(fe, props);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static enum dvbfe_algo cxd2880_get_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
+static struct dvb_frontend_ops cxd2880_dvbt_t2_ops = {
+ .info = {
+ .name = "Sony CXD2880",
+ .frequency_min_hz = 174 * MHz,
+ .frequency_max_hz = 862 * MHz,
+ .frequency_stepsize_hz = 1 * kHz,
+ .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_GPL(cxd2880_attach);
+
+MODULE_DESCRIPTION("Sony CXD2880 DVB-T2/T tuner + demod driver");
+MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/dvb-frontends/dib0070.c b/drivers/media/dvb-frontends/dib0070.c
new file mode 100644
index 000000000000..9a8e7cdd2a24
--- /dev/null
+++ b/drivers/media/dvb-frontends/dib0070.c
@@ -0,0 +1,769 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner.
+ *
+ * Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/)
+ *
+ * This code is more or less generated from another driver, please
+ * excuse some codingstyle oddities.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+
+#include <media/dvb_frontend.h>
+
+#include "dib0070.h"
+#include "dibx000_common.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
+
+#define DIB0070_P1D 0x00
+#define DIB0070_P1F 0x01
+#define DIB0070_P1G 0x03
+#define DIB0070S_P1A 0x02
+
+struct dib0070_state {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend *fe;
+ const struct dib0070_config *cfg;
+ u16 wbd_ff_offset;
+ u8 revision;
+
+ enum frontend_tune_state tune_state;
+ u32 current_rf;
+
+ /* for the captrim binary search */
+ s8 step;
+ u16 adc_diff;
+
+ s8 captrim;
+ s8 fcaptrim;
+ u16 lo4;
+
+ const struct dib0070_tuning *current_tune_table_index;
+ const struct dib0070_lna_match *lna_match;
+
+ u8 wbd_gain_current;
+ u16 wbd_offset_3_3[2];
+
+ /* for the I2C transfer */
+ struct i2c_msg msg[2];
+ u8 i2c_write_buffer[3];
+ u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
+};
+
+static u16 dib0070_read_reg(struct dib0070_state *state, u8 reg)
+{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock\n");
+ return 0;
+ }
+
+ state->i2c_write_buffer[0] = reg;
+
+ memset(state->msg, 0, 2 * sizeof(struct i2c_msg));
+ state->msg[0].addr = state->cfg->i2c_address;
+ state->msg[0].flags = 0;
+ state->msg[0].buf = state->i2c_write_buffer;
+ state->msg[0].len = 1;
+ state->msg[1].addr = state->cfg->i2c_address;
+ state->msg[1].flags = I2C_M_RD;
+ state->msg[1].buf = state->i2c_read_buffer;
+ state->msg[1].len = 2;
+
+ if (i2c_transfer(state->i2c, state->msg, 2) != 2) {
+ pr_warn("DiB0070 I2C read failed\n");
+ ret = 0;
+ } else
+ ret = (state->i2c_read_buffer[0] << 8)
+ | state->i2c_read_buffer[1];
+
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
+}
+
+static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val)
+{
+ int ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock\n");
+ return -EINVAL;
+ }
+ state->i2c_write_buffer[0] = reg;
+ state->i2c_write_buffer[1] = val >> 8;
+ state->i2c_write_buffer[2] = val & 0xff;
+
+ memset(state->msg, 0, sizeof(struct i2c_msg));
+ state->msg[0].addr = state->cfg->i2c_address;
+ state->msg[0].flags = 0;
+ state->msg[0].buf = state->i2c_write_buffer;
+ state->msg[0].len = 3;
+
+ if (i2c_transfer(state->i2c, state->msg, 1) != 1) {
+ pr_warn("DiB0070 I2C write failed\n");
+ ret = -EREMOTEIO;
+ } else
+ ret = 0;
+
+ mutex_unlock(&state->i2c_buffer_lock);
+ return ret;
+}
+
+#define HARD_RESET(state) do { \
+ state->cfg->sleep(state->fe, 0); \
+ if (state->cfg->reset) { \
+ state->cfg->reset(state->fe,1); msleep(10); \
+ state->cfg->reset(state->fe,0); msleep(10); \
+ } \
+} while (0)
+
+static int dib0070_set_bandwidth(struct dvb_frontend *fe)
+ {
+ struct dib0070_state *state = fe->tuner_priv;
+ u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff;
+
+ if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 7000)
+ tmp |= (0 << 14);
+ else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 6000)
+ tmp |= (1 << 14);
+ else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 5000)
+ tmp |= (2 << 14);
+ else
+ tmp |= (3 << 14);
+
+ dib0070_write_reg(state, 0x02, tmp);
+
+ /* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */
+ if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) {
+ u16 value = dib0070_read_reg(state, 0x17);
+
+ dib0070_write_reg(state, 0x17, value & 0xfffc);
+ tmp = dib0070_read_reg(state, 0x01) & 0x01ff;
+ dib0070_write_reg(state, 0x01, tmp | (60 << 9));
+
+ dib0070_write_reg(state, 0x17, value);
+ }
+ return 0;
+}
+
+static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state *tune_state)
+{
+ int8_t step_sign;
+ u16 adc;
+ int ret = 0;
+
+ if (*tune_state == CT_TUNER_STEP_0) {
+ dib0070_write_reg(state, 0x0f, 0xed10);
+ dib0070_write_reg(state, 0x17, 0x0034);
+
+ dib0070_write_reg(state, 0x18, 0x0032);
+ state->step = state->captrim = state->fcaptrim = 64;
+ state->adc_diff = 3000;
+ ret = 20;
+
+ *tune_state = CT_TUNER_STEP_1;
+ } else if (*tune_state == CT_TUNER_STEP_1) {
+ state->step /= 2;
+ dib0070_write_reg(state, 0x14, state->lo4 | state->captrim);
+ ret = 15;
+
+ *tune_state = CT_TUNER_STEP_2;
+ } else if (*tune_state == CT_TUNER_STEP_2) {
+
+ adc = dib0070_read_reg(state, 0x19);
+
+ dprintk("CAPTRIM=%d; ADC = %hd (ADC) & %dmV\n", state->captrim,
+ adc, (u32)adc * (u32)1800 / (u32)1024);
+
+ if (adc >= 400) {
+ adc -= 400;
+ step_sign = -1;
+ } else {
+ adc = 400 - adc;
+ step_sign = 1;
+ }
+
+ if (adc < state->adc_diff) {
+ dprintk("CAPTRIM=%d is closer to target (%hd/%hd)\n",
+ state->captrim, adc, state->adc_diff);
+ state->adc_diff = adc;
+ state->fcaptrim = state->captrim;
+ }
+ state->captrim += (step_sign * state->step);
+
+ if (state->step >= 1)
+ *tune_state = CT_TUNER_STEP_1;
+ else
+ *tune_state = CT_TUNER_STEP_3;
+
+ } else if (*tune_state == CT_TUNER_STEP_3) {
+ dib0070_write_reg(state, 0x14, state->lo4 | state->fcaptrim);
+ dib0070_write_reg(state, 0x18, 0x07ff);
+ *tune_state = CT_TUNER_STEP_4;
+ }
+
+ return ret;
+}
+
+static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt)
+{
+ struct dib0070_state *state = fe->tuner_priv;
+ u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0);
+
+ dprintk("CTRL_LO5: 0x%x\n", lo5);
+ return dib0070_write_reg(state, 0x15, lo5);
+}
+
+void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open)
+{
+ struct dib0070_state *state = fe->tuner_priv;
+
+ if (open) {
+ dib0070_write_reg(state, 0x1b, 0xff00);
+ dib0070_write_reg(state, 0x1a, 0x0000);
+ } else {
+ dib0070_write_reg(state, 0x1b, 0x4112);
+ if (state->cfg->vga_filter != 0) {
+ dib0070_write_reg(state, 0x1a, state->cfg->vga_filter);
+ dprintk("vga filter register is set to %x\n", state->cfg->vga_filter);
+ } else
+ dib0070_write_reg(state, 0x1a, 0x0009);
+ }
+}
+
+EXPORT_SYMBOL(dib0070_ctrl_agc_filter);
+struct dib0070_tuning {
+ u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
+ u8 switch_trim;
+ u8 vco_band;
+ u8 hfdiv;
+ u8 vco_multi;
+ u8 presc;
+ u8 wbdmux;
+ u16 tuner_enable;
+};
+
+struct dib0070_lna_match {
+ u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */
+ u8 lna_band;
+};
+
+static const struct dib0070_tuning dib0070s_tuning_table[] = {
+ { 570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800 }, /* UHF */
+ { 700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800 },
+ { 863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800 },
+ { 1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND */
+ { 1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 },
+ { 2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 },
+ { 0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000 }, /* SBAND */
+};
+
+static const struct dib0070_tuning dib0070_tuning_table[] = {
+ { 115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000 }, /* FM below 92MHz cannot be tuned */
+ { 179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000 }, /* VHF */
+ { 189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000 },
+ { 250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000 },
+ { 569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800 }, /* UHF */
+ { 699999, 2, 0, 1, 4, 2, 2, 0x4000 | 0x0800 },
+ { 863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800 },
+ { 0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND or everything higher than UHF */
+};
+
+static const struct dib0070_lna_match dib0070_lna_flip_chip[] = {
+ { 180000, 0 }, /* VHF */
+ { 188000, 1 },
+ { 196400, 2 },
+ { 250000, 3 },
+ { 550000, 0 }, /* UHF */
+ { 590000, 1 },
+ { 666000, 3 },
+ { 864000, 5 },
+ { 1500000, 0 }, /* LBAND or everything higher than UHF */
+ { 1600000, 1 },
+ { 2000000, 3 },
+ { 0xffffffff, 7 },
+};
+
+static const struct dib0070_lna_match dib0070_lna[] = {
+ { 180000, 0 }, /* VHF */
+ { 188000, 1 },
+ { 196400, 2 },
+ { 250000, 3 },
+ { 550000, 2 }, /* UHF */
+ { 650000, 3 },
+ { 750000, 5 },
+ { 850000, 6 },
+ { 864000, 7 },
+ { 1500000, 0 }, /* LBAND or everything higher than UHF */
+ { 1600000, 1 },
+ { 2000000, 3 },
+ { 0xffffffff, 7 },
+};
+
+#define LPF 100
+static int dib0070_tune_digital(struct dvb_frontend *fe)
+{
+ struct dib0070_state *state = fe->tuner_priv;
+
+ const struct dib0070_tuning *tune;
+ const struct dib0070_lna_match *lna_match;
+
+ enum frontend_tune_state *tune_state = &state->tune_state;
+ int ret = 10; /* 1ms is the default delay most of the time */
+
+ u8 band = (u8)BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency/1000);
+ u32 freq = fe->dtv_property_cache.frequency/1000 + (band == BAND_VHF ? state->cfg->freq_offset_khz_vhf : state->cfg->freq_offset_khz_uhf);
+
+#ifdef CONFIG_SYS_ISDBT
+ if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1)
+ if (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2)
+ && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))
+ || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
+ && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == (state->fe->dtv_property_cache.isdbt_sb_segment_count / 2)))
+ || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0)
+ && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))))
+ freq += 850;
+#endif
+ if (state->current_rf != freq) {
+
+ switch (state->revision) {
+ case DIB0070S_P1A:
+ tune = dib0070s_tuning_table;
+ lna_match = dib0070_lna;
+ break;
+ default:
+ tune = dib0070_tuning_table;
+ if (state->cfg->flip_chip)
+ lna_match = dib0070_lna_flip_chip;
+ else
+ lna_match = dib0070_lna;
+ break;
+ }
+ while (freq > tune->max_freq) /* find the right one */
+ tune++;
+ while (freq > lna_match->max_freq) /* find the right one */
+ lna_match++;
+
+ state->current_tune_table_index = tune;
+ state->lna_match = lna_match;
+ }
+
+ if (*tune_state == CT_TUNER_START) {
+ dprintk("Tuning for Band: %d (%d kHz)\n", band, freq);
+ if (state->current_rf != freq) {
+ u8 REFDIV;
+ u32 FBDiv, Rest, FREF, VCOF_kHz;
+ u8 Den;
+
+ state->current_rf = freq;
+ state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7);
+
+
+ dib0070_write_reg(state, 0x17, 0x30);
+
+
+ VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2;
+
+ switch (band) {
+ case BAND_VHF:
+ REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000);
+ break;
+ case BAND_FM:
+ REFDIV = (u8) ((state->cfg->clock_khz) / 1000);
+ break;
+ default:
+ REFDIV = (u8) (state->cfg->clock_khz / 10000);
+ break;
+ }
+ FREF = state->cfg->clock_khz / REFDIV;
+
+
+
+ switch (state->revision) {
+ case DIB0070S_P1A:
+ FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF);
+ Rest = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF;
+ break;
+
+ case DIB0070_P1G:
+ case DIB0070_P1F:
+ default:
+ FBDiv = (freq / (FREF / 2));
+ Rest = 2 * freq - FBDiv * FREF;
+ break;
+ }
+
+ if (Rest < LPF)
+ Rest = 0;
+ else if (Rest < 2 * LPF)
+ Rest = 2 * LPF;
+ else if (Rest > (FREF - LPF)) {
+ Rest = 0;
+ FBDiv += 1;
+ } else if (Rest > (FREF - 2 * LPF))
+ Rest = FREF - 2 * LPF;
+ Rest = (Rest * 6528) / (FREF / 10);
+
+ Den = 1;
+ if (Rest > 0) {
+ state->lo4 |= (1 << 14) | (1 << 12);
+ Den = 255;
+ }
+
+
+ dib0070_write_reg(state, 0x11, (u16)FBDiv);
+ dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV);
+ dib0070_write_reg(state, 0x13, (u16) Rest);
+
+ if (state->revision == DIB0070S_P1A) {
+
+ if (band == BAND_SBAND) {
+ dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);
+ dib0070_write_reg(state, 0x1d, 0xFFFF);
+ } else
+ dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1);
+ }
+
+ dib0070_write_reg(state, 0x20,
+ 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable);
+
+ dprintk("REFDIV: %u, FREF: %d\n", REFDIV, FREF);
+ dprintk("FBDIV: %d, Rest: %d\n", FBDiv, Rest);
+ dprintk("Num: %u, Den: %u, SD: %d\n", (u16)Rest, Den,
+ (state->lo4 >> 12) & 0x1);
+ dprintk("HFDIV code: %u\n",
+ state->current_tune_table_index->hfdiv);
+ dprintk("VCO = %u\n",
+ state->current_tune_table_index->vco_band);
+ dprintk("VCOF: ((%u*%d) << 1))\n",
+ state->current_tune_table_index->vco_multi,
+ freq);
+
+ *tune_state = CT_TUNER_STEP_0;
+ } else { /* we are already tuned to this frequency - the configuration is correct */
+ ret = 50; /* wakeup time */
+ *tune_state = CT_TUNER_STEP_5;
+ }
+ } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) {
+
+ ret = dib0070_captrim(state, tune_state);
+
+ } else if (*tune_state == CT_TUNER_STEP_4) {
+ const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;
+ if (tmp != NULL) {
+ while (freq/1000 > tmp->freq) /* find the right one */
+ tmp++;
+ dib0070_write_reg(state, 0x0f,
+ (0 << 15) | (1 << 14) | (3 << 12)
+ | (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7)
+ | (state->current_tune_table_index->wbdmux << 0));
+ state->wbd_gain_current = tmp->wbd_gain_val;
+ } else {
+ dib0070_write_reg(state, 0x0f,
+ (0 << 15) | (1 << 14) | (3 << 12)
+ | (6 << 9) | (0 << 8) | (1 << 7)
+ | (state->current_tune_table_index->wbdmux << 0));
+ state->wbd_gain_current = 6;
+ }
+
+ dib0070_write_reg(state, 0x06, 0x3fff);
+ dib0070_write_reg(state, 0x07,
+ (state->current_tune_table_index->switch_trim << 11) | (7 << 8) | (state->lna_match->lna_band << 3) | (3 << 0));
+ dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127));
+ dib0070_write_reg(state, 0x0d, 0x0d80);
+
+
+ dib0070_write_reg(state, 0x18, 0x07ff);
+ dib0070_write_reg(state, 0x17, 0x0033);
+
+
+ *tune_state = CT_TUNER_STEP_5;
+ } else if (*tune_state == CT_TUNER_STEP_5) {
+ dib0070_set_bandwidth(fe);
+ *tune_state = CT_TUNER_STOP;
+ } else {
+ ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */
+ }
+ return ret;
+}
+
+
+static int dib0070_tune(struct dvb_frontend *fe)
+{
+ struct dib0070_state *state = fe->tuner_priv;
+ uint32_t ret;
+
+ state->tune_state = CT_TUNER_START;
+
+ do {
+ ret = dib0070_tune_digital(fe);
+ if (ret != FE_CALLBACK_TIME_NEVER)
+ msleep(ret/10);
+ else
+ break;
+ } while (state->tune_state != CT_TUNER_STOP);
+
+ return 0;
+}
+
+static int dib0070_wakeup(struct dvb_frontend *fe)
+{
+ struct dib0070_state *state = fe->tuner_priv;
+ if (state->cfg->sleep)
+ state->cfg->sleep(fe, 0);
+ return 0;
+}
+
+static int dib0070_sleep(struct dvb_frontend *fe)
+{
+ struct dib0070_state *state = fe->tuner_priv;
+ if (state->cfg->sleep)
+ state->cfg->sleep(fe, 1);
+ return 0;
+}
+
+u8 dib0070_get_rf_output(struct dvb_frontend *fe)
+{
+ struct dib0070_state *state = fe->tuner_priv;
+ return (dib0070_read_reg(state, 0x07) >> 11) & 0x3;
+}
+EXPORT_SYMBOL(dib0070_get_rf_output);
+
+int dib0070_set_rf_output(struct dvb_frontend *fe, u8 no)
+{
+ struct dib0070_state *state = fe->tuner_priv;
+ u16 rxrf2 = dib0070_read_reg(state, 0x07) & 0xfe7ff;
+ if (no > 3)
+ no = 3;
+ if (no < 1)
+ no = 1;
+ return dib0070_write_reg(state, 0x07, rxrf2 | (no << 11));
+}
+EXPORT_SYMBOL(dib0070_set_rf_output);
+
+static const u16 dib0070_p1f_defaults[] =
+
+{
+ 7, 0x02,
+ 0x0008,
+ 0x0000,
+ 0x0000,
+ 0x0000,
+ 0x0000,
+ 0x0002,
+ 0x0100,
+
+ 3, 0x0d,
+ 0x0d80,
+ 0x0001,
+ 0x0000,
+
+ 4, 0x11,
+ 0x0000,
+ 0x0103,
+ 0x0000,
+ 0x0000,
+
+ 3, 0x16,
+ 0x0004 | 0x0040,
+ 0x0030,
+ 0x07ff,
+
+ 6, 0x1b,
+ 0x4112,
+ 0xff00,
+ 0xc07f,
+ 0x0000,
+ 0x0180,
+ 0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001,
+
+ 0,
+};
+
+static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain)
+{
+ u16 tuner_en = dib0070_read_reg(state, 0x20);
+ u16 offset;
+
+ dib0070_write_reg(state, 0x18, 0x07ff);
+ dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001);
+ dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0));
+ msleep(9);
+ offset = dib0070_read_reg(state, 0x19);
+ dib0070_write_reg(state, 0x20, tuner_en);
+ return offset;
+}
+
+static void dib0070_wbd_offset_calibration(struct dib0070_state *state)
+{
+ u8 gain;
+ for (gain = 6; gain < 8; gain++) {
+ state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2);
+ dprintk("Gain: %d, WBDOffset (3.3V) = %hd\n", gain, state->wbd_offset_3_3[gain-6]);
+ }
+}
+
+u16 dib0070_wbd_offset(struct dvb_frontend *fe)
+{
+ struct dib0070_state *state = fe->tuner_priv;
+ const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain;
+ u32 freq = fe->dtv_property_cache.frequency/1000;
+
+ if (tmp != NULL) {
+ while (freq/1000 > tmp->freq) /* find the right one */
+ tmp++;
+ state->wbd_gain_current = tmp->wbd_gain_val;
+ } else
+ state->wbd_gain_current = 6;
+
+ return state->wbd_offset_3_3[state->wbd_gain_current - 6];
+}
+EXPORT_SYMBOL(dib0070_wbd_offset);
+
+#define pgm_read_word(w) (*w)
+static int dib0070_reset(struct dvb_frontend *fe)
+{
+ struct dib0070_state *state = fe->tuner_priv;
+ u16 l, r, *n;
+
+ HARD_RESET(state);
+
+
+#ifndef FORCE_SBAND_TUNER
+ if ((dib0070_read_reg(state, 0x22) >> 9) & 0x1)
+ state->revision = (dib0070_read_reg(state, 0x1f) >> 8) & 0xff;
+ else
+#else
+#warning forcing SBAND
+#endif
+ state->revision = DIB0070S_P1A;
+
+ /* P1F or not */
+ dprintk("Revision: %x\n", state->revision);
+
+ if (state->revision == DIB0070_P1D) {
+ dprintk("Error: this driver is not to be used meant for P1D or earlier\n");
+ return -EINVAL;
+ }
+
+ n = (u16 *) dib0070_p1f_defaults;
+ l = pgm_read_word(n++);
+ while (l) {
+ r = pgm_read_word(n++);
+ do {
+ dib0070_write_reg(state, (u8)r, pgm_read_word(n++));
+ r++;
+ } while (--l);
+ l = pgm_read_word(n++);
+ }
+
+ if (state->cfg->force_crystal_mode != 0)
+ r = state->cfg->force_crystal_mode;
+ else if (state->cfg->clock_khz >= 24000)
+ r = 1;
+ else
+ r = 2;
+
+
+ r |= state->cfg->osc_buffer_state << 3;
+
+ dib0070_write_reg(state, 0x10, r);
+ dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 5));
+
+ if (state->cfg->invert_iq) {
+ r = dib0070_read_reg(state, 0x02) & 0xffdf;
+ dib0070_write_reg(state, 0x02, r | (1 << 5));
+ }
+
+ if (state->revision == DIB0070S_P1A)
+ dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0);
+ else
+ dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump,
+ state->cfg->enable_third_order_filter);
+
+ dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8);
+
+ dib0070_wbd_offset_calibration(state);
+
+ return 0;
+}
+
+static int dib0070_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct dib0070_state *state = fe->tuner_priv;
+
+ *frequency = 1000 * state->current_rf;
+ return 0;
+}
+
+static void dib0070_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+}
+
+static const struct dvb_tuner_ops dib0070_ops = {
+ .info = {
+ .name = "DiBcom DiB0070",
+ .frequency_min_hz = 45 * MHz,
+ .frequency_max_hz = 860 * MHz,
+ .frequency_step_hz = 1 * kHz,
+ },
+ .release = dib0070_release,
+
+ .init = dib0070_wakeup,
+ .sleep = dib0070_sleep,
+ .set_params = dib0070_tune,
+
+ .get_frequency = dib0070_get_frequency,
+// .get_bandwidth = dib0070_get_bandwidth
+};
+
+struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg)
+{
+ struct dib0070_state *state = kzalloc(sizeof(struct dib0070_state), GFP_KERNEL);
+ if (state == NULL)
+ return NULL;
+
+ state->cfg = cfg;
+ state->i2c = i2c;
+ state->fe = fe;
+ mutex_init(&state->i2c_buffer_lock);
+ fe->tuner_priv = state;
+
+ if (dib0070_reset(fe) != 0)
+ goto free_mem;
+
+ pr_info("DiB0070: successfully identified\n");
+ memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = state;
+ return fe;
+
+free_mem:
+ kfree(state);
+ fe->tuner_priv = NULL;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(dib0070_attach);
+
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
+MODULE_DESCRIPTION("Driver for the DiBcom 0070 base-band RF Tuner");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib0070.h b/drivers/media/dvb-frontends/dib0070.h
index 45c31fae3967..ae5c44e9b1e6 100644
--- a/drivers/media/dvb/frontends/dib0070.h
+++ b/drivers/media/dvb-frontends/dib0070.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner.
*
* Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
*/
#ifndef DIB0070_H
#define DIB0070_H
@@ -48,7 +45,7 @@ struct dib0070_config {
u8 vga_filter;
};
-#if defined(CONFIG_DVB_TUNER_DIB0070) || (defined(CONFIG_DVB_TUNER_DIB0070_MODULE) && defined(MODULE))
+#if IS_REACHABLE(CONFIG_DVB_TUNER_DIB0070)
extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg);
extern u16 dib0070_wbd_offset(struct dvb_frontend *);
extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, u8 open);
diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c
index d9fe60b4be48..6cbbb351d545 100644
--- a/drivers/media/dvb/frontends/dib0090.c
+++ b/drivers/media/dvb-frontends/dib0090.c
@@ -1,35 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Linux-DVB Driver for DiBcom's DiB0090 base-band RF Tuner.
*
* Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/)
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
* This code is more or less generated from another driver, please
* excuse some codingstyle oddities.
- *
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "dib0090.h"
#include "dibx000_common.h"
@@ -38,12 +24,10 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
-#define dprintk(args...) do { \
- if (debug) { \
- printk(KERN_DEBUG "DiB0090: "); \
- printk(args); \
- printk("\n"); \
- } \
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
} while (0)
#define CONFIG_SYS_DVBT
@@ -94,7 +78,7 @@ MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
#define SOC_8090_P1G_11R1 0x86
#define SOC_8090_P1G_21R1 0x8e
-/* else use thos ones to check */
+/* else use those ones to check */
#define P1A_B 0x0
#define P1C 0x1
#define P1D_E_F 0x3
@@ -218,7 +202,7 @@ static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg)
u16 ret;
if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
- dprintk("could not acquire lock");
+ dprintk("could not acquire lock\n");
return 0;
}
@@ -235,7 +219,7 @@ static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg)
state->msg[1].len = 2;
if (i2c_transfer(state->i2c, state->msg, 2) != 2) {
- printk(KERN_WARNING "DiB0090 I2C read failed\n");
+ pr_warn("DiB0090 I2C read failed\n");
ret = 0;
} else
ret = (state->i2c_read_buffer[0] << 8)
@@ -250,7 +234,7 @@ static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val)
int ret;
if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
- dprintk("could not acquire lock");
+ dprintk("could not acquire lock\n");
return -EINVAL;
}
@@ -265,7 +249,7 @@ static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val)
state->msg[0].len = 3;
if (i2c_transfer(state->i2c, state->msg, 1) != 1) {
- printk(KERN_WARNING "DiB0090 I2C write failed\n");
+ pr_warn("DiB0090 I2C write failed\n");
ret = -EREMOTEIO;
} else
ret = 0;
@@ -279,7 +263,7 @@ static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg)
u16 ret;
if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
- dprintk("could not acquire lock");
+ dprintk("could not acquire lock\n");
return 0;
}
@@ -291,7 +275,7 @@ static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg)
state->msg.buf = state->i2c_read_buffer;
state->msg.len = 2;
if (i2c_transfer(state->i2c, &state->msg, 1) != 1) {
- printk(KERN_WARNING "DiB0090 I2C read failed\n");
+ pr_warn("DiB0090 I2C read failed\n");
ret = 0;
} else
ret = (state->i2c_read_buffer[0] << 8)
@@ -306,7 +290,7 @@ static int dib0090_fw_write_reg(struct dib0090_fw_state *state, u8 reg, u16 val)
int ret;
if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
- dprintk("could not acquire lock");
+ dprintk("could not acquire lock\n");
return -EINVAL;
}
@@ -319,7 +303,7 @@ static int dib0090_fw_write_reg(struct dib0090_fw_state *state, u8 reg, u16 val)
state->msg.buf = state->i2c_write_buffer;
state->msg.len = 2;
if (i2c_transfer(state->i2c, &state->msg, 1) != 1) {
- printk(KERN_WARNING "DiB0090 I2C write failed\n");
+ pr_warn("DiB0090 I2C write failed\n");
ret = -EREMOTEIO;
} else
ret = 0;
@@ -351,7 +335,7 @@ static int dib0090_identify(struct dvb_frontend *fe)
identity->p1g = 0;
identity->in_soc = 0;
- dprintk("Tuner identification (Version = 0x%04x)", v);
+ dprintk("Tuner identification (Version = 0x%04x)\n", v);
/* without PLL lock info */
v &= ~KROSUS_PLL_LOCKED;
@@ -366,19 +350,19 @@ static int dib0090_identify(struct dvb_frontend *fe)
identity->in_soc = 1;
switch (identity->version) {
case SOC_8090_P1G_11R1:
- dprintk("SOC 8090 P1-G11R1 Has been detected");
+ dprintk("SOC 8090 P1-G11R1 Has been detected\n");
identity->p1g = 1;
break;
case SOC_8090_P1G_21R1:
- dprintk("SOC 8090 P1-G21R1 Has been detected");
+ dprintk("SOC 8090 P1-G21R1 Has been detected\n");
identity->p1g = 1;
break;
case SOC_7090_P1G_11R1:
- dprintk("SOC 7090 P1-G11R1 Has been detected");
+ dprintk("SOC 7090 P1-G11R1 Has been detected\n");
identity->p1g = 1;
break;
case SOC_7090_P1G_21R1:
- dprintk("SOC 7090 P1-G21R1 Has been detected");
+ dprintk("SOC 7090 P1-G21R1 Has been detected\n");
identity->p1g = 1;
break;
default:
@@ -387,16 +371,16 @@ static int dib0090_identify(struct dvb_frontend *fe)
} else {
switch ((identity->version >> 5) & 0x7) {
case MP001:
- dprintk("MP001 : 9090/8096");
+ dprintk("MP001 : 9090/8096\n");
break;
case MP005:
- dprintk("MP005 : Single Sband");
+ dprintk("MP005 : Single Sband\n");
break;
case MP008:
- dprintk("MP008 : diversity VHF-UHF-LBAND");
+ dprintk("MP008 : diversity VHF-UHF-LBAND\n");
break;
case MP009:
- dprintk("MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND");
+ dprintk("MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND\n");
break;
default:
goto identification_error;
@@ -404,21 +388,21 @@ static int dib0090_identify(struct dvb_frontend *fe)
switch (identity->version & 0x1f) {
case P1G_21R2:
- dprintk("P1G_21R2 detected");
+ dprintk("P1G_21R2 detected\n");
identity->p1g = 1;
break;
case P1G:
- dprintk("P1G detected");
+ dprintk("P1G detected\n");
identity->p1g = 1;
break;
case P1D_E_F:
- dprintk("P1D/E/F detected");
+ dprintk("P1D/E/F detected\n");
break;
case P1C:
- dprintk("P1C detected");
+ dprintk("P1C detected\n");
break;
case P1A_B:
- dprintk("P1-A/B detected: driver is deactivated - not available");
+ dprintk("P1-A/B detected: driver is deactivated - not available\n");
goto identification_error;
break;
default:
@@ -441,7 +425,7 @@ static int dib0090_fw_identify(struct dvb_frontend *fe)
identity->p1g = 0;
identity->in_soc = 0;
- dprintk("FE: Tuner identification (Version = 0x%04x)", v);
+ dprintk("FE: Tuner identification (Version = 0x%04x)\n", v);
/* without PLL lock info */
v &= ~KROSUS_PLL_LOCKED;
@@ -456,19 +440,19 @@ static int dib0090_fw_identify(struct dvb_frontend *fe)
identity->in_soc = 1;
switch (identity->version) {
case SOC_8090_P1G_11R1:
- dprintk("SOC 8090 P1-G11R1 Has been detected");
+ dprintk("SOC 8090 P1-G11R1 Has been detected\n");
identity->p1g = 1;
break;
case SOC_8090_P1G_21R1:
- dprintk("SOC 8090 P1-G21R1 Has been detected");
+ dprintk("SOC 8090 P1-G21R1 Has been detected\n");
identity->p1g = 1;
break;
case SOC_7090_P1G_11R1:
- dprintk("SOC 7090 P1-G11R1 Has been detected");
+ dprintk("SOC 7090 P1-G11R1 Has been detected\n");
identity->p1g = 1;
break;
case SOC_7090_P1G_21R1:
- dprintk("SOC 7090 P1-G21R1 Has been detected");
+ dprintk("SOC 7090 P1-G21R1 Has been detected\n");
identity->p1g = 1;
break;
default:
@@ -477,16 +461,16 @@ static int dib0090_fw_identify(struct dvb_frontend *fe)
} else {
switch ((identity->version >> 5) & 0x7) {
case MP001:
- dprintk("MP001 : 9090/8096");
+ dprintk("MP001 : 9090/8096\n");
break;
case MP005:
- dprintk("MP005 : Single Sband");
+ dprintk("MP005 : Single Sband\n");
break;
case MP008:
- dprintk("MP008 : diversity VHF-UHF-LBAND");
+ dprintk("MP008 : diversity VHF-UHF-LBAND\n");
break;
case MP009:
- dprintk("MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND");
+ dprintk("MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND\n");
break;
default:
goto identification_error;
@@ -494,21 +478,21 @@ static int dib0090_fw_identify(struct dvb_frontend *fe)
switch (identity->version & 0x1f) {
case P1G_21R2:
- dprintk("P1G_21R2 detected");
+ dprintk("P1G_21R2 detected\n");
identity->p1g = 1;
break;
case P1G:
- dprintk("P1G detected");
+ dprintk("P1G detected\n");
identity->p1g = 1;
break;
case P1D_E_F:
- dprintk("P1D/E/F detected");
+ dprintk("P1D/E/F detected\n");
break;
case P1C:
- dprintk("P1C detected");
+ dprintk("P1C detected\n");
break;
case P1A_B:
- dprintk("P1-A/B detected: driver is deactivated - not available");
+ dprintk("P1-A/B detected: driver is deactivated - not available\n");
goto identification_error;
break;
default:
@@ -528,20 +512,19 @@ static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_
u16 PllCfg, i, v;
HARD_RESET(state);
-
dib0090_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL);
- dib0090_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */
+ if (cfg->in_soc)
+ return;
- if (!cfg->in_soc) {
- /* adcClkOutRatio=8->7, release reset */
- dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0);
- if (cfg->clkoutdrive != 0)
- dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8)
- | (cfg->clkoutdrive << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0));
- else
- dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8)
- | (7 << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0));
- }
+ dib0090_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */
+ /* adcClkOutRatio=8->7, release reset */
+ dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0);
+ if (cfg->clkoutdrive != 0)
+ dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8)
+ | (cfg->clkoutdrive << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0));
+ else
+ dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8)
+ | (7 << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0));
/* Read Pll current config * */
PllCfg = dib0090_read_reg(state, 0x21);
@@ -575,7 +558,7 @@ static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_
} while (--i);
if (i == 0) {
- dprintk("Pll: Unable to lock Pll");
+ dprintk("Pll: Unable to lock Pll\n");
return;
}
@@ -597,7 +580,7 @@ static int dib0090_fw_reset_digital(struct dvb_frontend *fe, const struct dib009
u16 v;
int i;
- dprintk("fw reset digital");
+ dprintk("fw reset digital\n");
HARD_RESET(state);
dib0090_fw_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL);
@@ -646,7 +629,7 @@ static int dib0090_fw_reset_digital(struct dvb_frontend *fe, const struct dib009
} while (--i);
if (i == 0) {
- dprintk("Pll: Unable to lock Pll");
+ dprintk("Pll: Unable to lock Pll\n");
return -EIO;
}
@@ -694,193 +677,181 @@ void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast)
EXPORT_SYMBOL(dib0090_dcc_freq);
static const u16 bb_ramp_pwm_normal_socs[] = {
- 550, /* max BB gain in 10th of dB */
- (1 << 9) | 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */
+ 550, /* max BB gain in 10th of dB */
+ (1<<9) | 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */
440,
- (4 << 9) | 0, /* BB_RAMP3 = 26dB */
- (0 << 9) | 208, /* BB_RAMP4 */
- (4 << 9) | 208, /* BB_RAMP5 = 29dB */
- (0 << 9) | 440, /* BB_RAMP6 */
+ (4 << 9) | 0, /* BB_RAMP3 = 26dB */
+ (0 << 9) | 208, /* BB_RAMP4 */
+ (4 << 9) | 208, /* BB_RAMP5 = 29dB */
+ (0 << 9) | 440, /* BB_RAMP6 */
};
-static const u16 rf_ramp_pwm_cband_7090[] = {
- 280, /* max RF gain in 10th of dB */
- 18, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
- 504, /* ramp_max = maximum X used on the ramp */
- (29 << 10) | 364, /* RF_RAMP5, LNA 1 = 8dB */
- (0 << 10) | 504, /* RF_RAMP6, LNA 1 */
- (60 << 10) | 228, /* RF_RAMP7, LNA 2 = 7.7dB */
- (0 << 10) | 364, /* RF_RAMP8, LNA 2 */
- (34 << 10) | 109, /* GAIN_4_1, LNA 3 = 6.8dB */
- (0 << 10) | 228, /* GAIN_4_2, LNA 3 */
- (37 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */
- (0 << 10) | 109, /* RF_RAMP4, LNA 4 */
+static const u16 rf_ramp_pwm_cband_7090p[] = {
+ 280, /* max RF gain in 10th of dB */
+ 18, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
+ 504, /* ramp_max = maximum X used on the ramp */
+ (29 << 10) | 364, /* RF_RAMP5, LNA 1 = 8dB */
+ (0 << 10) | 504, /* RF_RAMP6, LNA 1 */
+ (60 << 10) | 228, /* RF_RAMP7, LNA 2 = 7.7dB */
+ (0 << 10) | 364, /* RF_RAMP8, LNA 2 */
+ (34 << 10) | 109, /* GAIN_4_1, LNA 3 = 6.8dB */
+ (0 << 10) | 228, /* GAIN_4_2, LNA 3 */
+ (37 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */
+ (0 << 10) | 109, /* RF_RAMP4, LNA 4 */
};
-static const uint16_t rf_ramp_pwm_cband_7090e_sensitivity[] = {
- 186,
- 40,
- 746,
- (10 << 10) | 345,
- (0 << 10) | 746,
- (0 << 10) | 0,
- (0 << 10) | 0,
- (28 << 10) | 200,
- (0 << 10) | 345,
- (20 << 10) | 0,
- (0 << 10) | 200,
+static const u16 rf_ramp_pwm_cband_7090e_sensitivity[] = {
+ 186, /* max RF gain in 10th of dB */
+ 40, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
+ 746, /* ramp_max = maximum X used on the ramp */
+ (10 << 10) | 345, /* RF_RAMP5, LNA 1 = 10dB */
+ (0 << 10) | 746, /* RF_RAMP6, LNA 1 */
+ (0 << 10) | 0, /* RF_RAMP7, LNA 2 = 0 dB */
+ (0 << 10) | 0, /* RF_RAMP8, LNA 2 */
+ (28 << 10) | 200, /* GAIN_4_1, LNA 3 = 6.8dB */ /* 3.61 dB */
+ (0 << 10) | 345, /* GAIN_4_2, LNA 3 */
+ (20 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ /* 4.96 dB */
+ (0 << 10) | 200, /* RF_RAMP4, LNA 4 */
};
-static const uint16_t rf_ramp_pwm_cband_7090e_aci[] = {
- 86,
- 40,
- 345,
- (0 << 10) | 0,
- (0 << 10) | 0,
- (0 << 10) | 0,
- (0 << 10) | 0,
- (28 << 10) | 200,
- (0 << 10) | 345,
- (20 << 10) | 0,
- (0 << 10) | 200,
+static const u16 rf_ramp_pwm_cband_7090e_aci[] = {
+ 86, /* max RF gain in 10th of dB */
+ 40, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
+ 345, /* ramp_max = maximum X used on the ramp */
+ (0 << 10) | 0, /* RF_RAMP5, LNA 1 = 8dB */ /* 7.47 dB */
+ (0 << 10) | 0, /* RF_RAMP6, LNA 1 */
+ (0 << 10) | 0, /* RF_RAMP7, LNA 2 = 0 dB */
+ (0 << 10) | 0, /* RF_RAMP8, LNA 2 */
+ (28 << 10) | 200, /* GAIN_4_1, LNA 3 = 6.8dB */ /* 3.61 dB */
+ (0 << 10) | 345, /* GAIN_4_2, LNA 3 */
+ (20 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ /* 4.96 dB */
+ (0 << 10) | 200, /* RF_RAMP4, LNA 4 */
};
static const u16 rf_ramp_pwm_cband_8090[] = {
- 345, /* max RF gain in 10th of dB */
- 29, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
- 1000, /* ramp_max = maximum X used on the ramp */
- (35 << 10) | 772, /* RF_RAMP3, LNA 1 = 8dB */
- (0 << 10) | 1000, /* RF_RAMP4, LNA 1 */
- (58 << 10) | 496, /* RF_RAMP5, LNA 2 = 9.5dB */
- (0 << 10) | 772, /* RF_RAMP6, LNA 2 */
- (27 << 10) | 200, /* RF_RAMP7, LNA 3 = 10.5dB */
- (0 << 10) | 496, /* RF_RAMP8, LNA 3 */
- (40 << 10) | 0, /* GAIN_4_1, LNA 4 = 7dB */
- (0 << 10) | 200, /* GAIN_4_2, LNA 4 */
+ 345, /* max RF gain in 10th of dB */
+ 29, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
+ 1000, /* ramp_max = maximum X used on the ramp */
+ (35 << 10) | 772, /* RF_RAMP3, LNA 1 = 8dB */
+ (0 << 10) | 1000, /* RF_RAMP4, LNA 1 */
+ (58 << 10) | 496, /* RF_RAMP5, LNA 2 = 9.5dB */
+ (0 << 10) | 772, /* RF_RAMP6, LNA 2 */
+ (27 << 10) | 200, /* RF_RAMP7, LNA 3 = 10.5dB */
+ (0 << 10) | 496, /* RF_RAMP8, LNA 3 */
+ (40 << 10) | 0, /* GAIN_4_1, LNA 4 = 7dB */
+ (0 << 10) | 200, /* GAIN_4_2, LNA 4 */
};
static const u16 rf_ramp_pwm_uhf_7090[] = {
- 407, /* max RF gain in 10th of dB */
- 13, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
- 529, /* ramp_max = maximum X used on the ramp */
- (23 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */
- (0 << 10) | 176, /* RF_RAMP4, LNA 1 */
- (63 << 10) | 400, /* RF_RAMP5, LNA 2 = 8dB */
- (0 << 10) | 529, /* RF_RAMP6, LNA 2 */
- (48 << 10) | 316, /* RF_RAMP7, LNA 3 = 6.8dB */
- (0 << 10) | 400, /* RF_RAMP8, LNA 3 */
- (29 << 10) | 176, /* GAIN_4_1, LNA 4 = 11.5dB */
- (0 << 10) | 316, /* GAIN_4_2, LNA 4 */
+ 407, /* max RF gain in 10th of dB */
+ 13, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
+ 529, /* ramp_max = maximum X used on the ramp */
+ (23 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */
+ (0 << 10) | 176, /* RF_RAMP4, LNA 1 */
+ (63 << 10) | 400, /* RF_RAMP5, LNA 2 = 8dB */
+ (0 << 10) | 529, /* RF_RAMP6, LNA 2 */
+ (48 << 10) | 316, /* RF_RAMP7, LNA 3 = 6.8dB */
+ (0 << 10) | 400, /* RF_RAMP8, LNA 3 */
+ (29 << 10) | 176, /* GAIN_4_1, LNA 4 = 11.5dB */
+ (0 << 10) | 316, /* GAIN_4_2, LNA 4 */
};
static const u16 rf_ramp_pwm_uhf_8090[] = {
- 388, /* max RF gain in 10th of dB */
- 26, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
- 1008, /* ramp_max = maximum X used on the ramp */
- (11 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */
- (0 << 10) | 369, /* RF_RAMP4, LNA 1 */
- (41 << 10) | 809, /* RF_RAMP5, LNA 2 = 8dB */
- (0 << 10) | 1008, /* RF_RAMP6, LNA 2 */
- (27 << 10) | 659, /* RF_RAMP7, LNA 3 = 6dB */
- (0 << 10) | 809, /* RF_RAMP8, LNA 3 */
- (14 << 10) | 369, /* GAIN_4_1, LNA 4 = 11.5dB */
- (0 << 10) | 659, /* GAIN_4_2, LNA 4 */
-};
-
-static const u16 rf_ramp_pwm_cband[] = {
- 0, /* max RF gain in 10th of dB */
- 0, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */
- 0, /* ramp_max = maximum X used on the ramp */
- (0 << 10) | 0, /* 0x2c, LNA 1 = 0dB */
- (0 << 10) | 0, /* 0x2d, LNA 1 */
- (0 << 10) | 0, /* 0x2e, LNA 2 = 0dB */
- (0 << 10) | 0, /* 0x2f, LNA 2 */
- (0 << 10) | 0, /* 0x30, LNA 3 = 0dB */
- (0 << 10) | 0, /* 0x31, LNA 3 */
- (0 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */
- (0 << 10) | 0, /* GAIN_4_2, LNA 4 */
-};
-
-static const u16 rf_ramp_vhf[] = {
- 412, /* max RF gain in 10th of dB */
- 132, 307, 127, /* LNA1, 13.2dB */
- 105, 412, 255, /* LNA2, 10.5dB */
- 50, 50, 127, /* LNA3, 5dB */
- 125, 175, 127, /* LNA4, 12.5dB */
- 0, 0, 127, /* CBAND, 0dB */
+ 388, /* max RF gain in 10th of dB */
+ 26, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
+ 1008, /* ramp_max = maximum X used on the ramp */
+ (11 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */
+ (0 << 10) | 369, /* RF_RAMP4, LNA 1 */
+ (41 << 10) | 809, /* RF_RAMP5, LNA 2 = 8dB */
+ (0 << 10) | 1008, /* RF_RAMP6, LNA 2 */
+ (27 << 10) | 659, /* RF_RAMP7, LNA 3 = 6dB */
+ (0 << 10) | 809, /* RF_RAMP8, LNA 3 */
+ (14 << 10) | 369, /* GAIN_4_1, LNA 4 = 11.5dB */
+ (0 << 10) | 659, /* GAIN_4_2, LNA 4 */
};
-static const u16 rf_ramp_uhf[] = {
- 412, /* max RF gain in 10th of dB */
- 132, 307, 127, /* LNA1 : total gain = 13.2dB, point on the ramp where this amp is full gain, value to write to get full gain */
- 105, 412, 255, /* LNA2 : 10.5 dB */
- 50, 50, 127, /* LNA3 : 5.0 dB */
- 125, 175, 127, /* LNA4 : 12.5 dB */
- 0, 0, 127, /* CBAND : 0.0 dB */
+/* GENERAL PWM ramp definition for all other Krosus */
+static const u16 bb_ramp_pwm_normal[] = {
+ 500, /* max BB gain in 10th of dB */
+ 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */
+ 400,
+ (2 << 9) | 0, /* BB_RAMP3 = 21dB */
+ (0 << 9) | 168, /* BB_RAMP4 */
+ (2 << 9) | 168, /* BB_RAMP5 = 29dB */
+ (0 << 9) | 400, /* BB_RAMP6 */
};
-static const u16 rf_ramp_cband_broadmatching[] = /* for p1G only */
-{
- 314, /* Calibrated at 200MHz order has been changed g4-g3-g2-g1 */
- 84, 314, 127, /* LNA1 */
- 80, 230, 255, /* LNA2 */
- 80, 150, 127, /* LNA3 It was measured 12dB, do not lock if 120 */
- 70, 70, 127, /* LNA4 */
- 0, 0, 127, /* CBAND */
+#if 0
+/* Currently unused */
+static const u16 bb_ramp_pwm_boost[] = {
+ 550, /* max BB gain in 10th of dB */
+ 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */
+ 440,
+ (2 << 9) | 0, /* BB_RAMP3 = 26dB */
+ (0 << 9) | 208, /* BB_RAMP4 */
+ (2 << 9) | 208, /* BB_RAMP5 = 29dB */
+ (0 << 9) | 440, /* BB_RAMP6 */
};
+#endif
-static const u16 rf_ramp_cband[] = {
- 332, /* max RF gain in 10th of dB */
- 132, 252, 127, /* LNA1, dB */
- 80, 332, 255, /* LNA2, dB */
- 0, 0, 127, /* LNA3, dB */
- 0, 0, 127, /* LNA4, dB */
- 120, 120, 127, /* LT1 CBAND */
+static const u16 rf_ramp_pwm_cband[] = {
+ 314, /* max RF gain in 10th of dB */
+ 33, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
+ 1023, /* ramp_max = maximum X used on the ramp */
+ (8 << 10) | 743, /* RF_RAMP3, LNA 1 = 0dB */
+ (0 << 10) | 1023, /* RF_RAMP4, LNA 1 */
+ (15 << 10) | 469, /* RF_RAMP5, LNA 2 = 0dB */
+ (0 << 10) | 742, /* RF_RAMP6, LNA 2 */
+ (9 << 10) | 234, /* RF_RAMP7, LNA 3 = 0dB */
+ (0 << 10) | 468, /* RF_RAMP8, LNA 3 */
+ (9 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */
+ (0 << 10) | 233, /* GAIN_4_2, LNA 4 */
};
static const u16 rf_ramp_pwm_vhf[] = {
- 404, /* max RF gain in 10th of dB */
- 25, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */
- 1011, /* ramp_max = maximum X used on the ramp */
- (6 << 10) | 417, /* 0x2c, LNA 1 = 13.2dB */
- (0 << 10) | 756, /* 0x2d, LNA 1 */
- (16 << 10) | 756, /* 0x2e, LNA 2 = 10.5dB */
- (0 << 10) | 1011, /* 0x2f, LNA 2 */
- (16 << 10) | 290, /* 0x30, LNA 3 = 5dB */
- (0 << 10) | 417, /* 0x31, LNA 3 */
- (7 << 10) | 0, /* GAIN_4_1, LNA 4 = 12.5dB */
- (0 << 10) | 290, /* GAIN_4_2, LNA 4 */
+ 398, /* max RF gain in 10th of dB */
+ 24, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
+ 954, /* ramp_max = maximum X used on the ramp */
+ (7 << 10) | 0, /* RF_RAMP3, LNA 1 = 13.2dB */
+ (0 << 10) | 290, /* RF_RAMP4, LNA 1 */
+ (16 << 10) | 699, /* RF_RAMP5, LNA 2 = 10.5dB */
+ (0 << 10) | 954, /* RF_RAMP6, LNA 2 */
+ (17 << 10) | 580, /* RF_RAMP7, LNA 3 = 5dB */
+ (0 << 10) | 699, /* RF_RAMP8, LNA 3 */
+ (7 << 10) | 290, /* GAIN_4_1, LNA 4 = 12.5dB */
+ (0 << 10) | 580, /* GAIN_4_2, LNA 4 */
};
static const u16 rf_ramp_pwm_uhf[] = {
- 404, /* max RF gain in 10th of dB */
- 25, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */
- 1011, /* ramp_max = maximum X used on the ramp */
- (6 << 10) | 417, /* 0x2c, LNA 1 = 13.2dB */
- (0 << 10) | 756, /* 0x2d, LNA 1 */
- (16 << 10) | 756, /* 0x2e, LNA 2 = 10.5dB */
- (0 << 10) | 1011, /* 0x2f, LNA 2 */
- (16 << 10) | 0, /* 0x30, LNA 3 = 5dB */
- (0 << 10) | 127, /* 0x31, LNA 3 */
- (7 << 10) | 127, /* GAIN_4_1, LNA 4 = 12.5dB */
- (0 << 10) | 417, /* GAIN_4_2, LNA 4 */
-};
-
-static const u16 bb_ramp_boost[] = {
- 550, /* max BB gain in 10th of dB */
- 260, 260, 26, /* BB1, 26dB */
- 290, 550, 29, /* BB2, 29dB */
+ 398, /* max RF gain in 10th of dB */
+ 24, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
+ 954, /* ramp_max = maximum X used on the ramp */
+ (7 << 10) | 0, /* RF_RAMP3, LNA 1 = 13.2dB */
+ (0 << 10) | 290, /* RF_RAMP4, LNA 1 */
+ (16 << 10) | 699, /* RF_RAMP5, LNA 2 = 10.5dB */
+ (0 << 10) | 954, /* RF_RAMP6, LNA 2 */
+ (17 << 10) | 580, /* RF_RAMP7, LNA 3 = 5dB */
+ (0 << 10) | 699, /* RF_RAMP8, LNA 3 */
+ (7 << 10) | 290, /* GAIN_4_1, LNA 4 = 12.5dB */
+ (0 << 10) | 580, /* GAIN_4_2, LNA 4 */
};
-static const u16 bb_ramp_pwm_normal[] = {
- 500, /* max RF gain in 10th of dB */
- 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x34 */
- 400,
- (2 << 9) | 0, /* 0x35 = 21dB */
- (0 << 9) | 168, /* 0x36 */
- (2 << 9) | 168, /* 0x37 = 29dB */
- (0 << 9) | 400, /* 0x38 */
+#if 0
+/* Currently unused */
+static const u16 rf_ramp_pwm_sband[] = {
+ 253, /* max RF gain in 10th of dB */
+ 38, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
+ 961,
+ (4 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.1dB */
+ (0 << 10) | 508, /* RF_RAMP4, LNA 1 */
+ (9 << 10) | 508, /* RF_RAMP5, LNA 2 = 11.2dB */
+ (0 << 10) | 961, /* RF_RAMP6, LNA 2 */
+ (0 << 10) | 0, /* RF_RAMP7, LNA 3 = 0dB */
+ (0 << 10) | 0, /* RF_RAMP8, LNA 3 */
+ (0 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */
+ (0 << 10) | 0, /* GAIN_4_2, LNA 4 */
};
+#endif
struct slope {
s16 range;
@@ -935,7 +906,7 @@ static void dib0090_wbd_target(struct dib0090_state *state, u32 rf)
#endif
state->wbd_target = dib0090_wbd_to_db(state, state->wbd_offset + offset);
- dprintk("wbd-target: %d dB", (u32) state->wbd_target);
+ dprintk("wbd-target: %d dB\n", (u32) state->wbd_target);
}
static const int gain_reg_addr[4] = {
@@ -1032,7 +1003,7 @@ static void dib0090_gain_apply(struct dib0090_state *state, s16 gain_delta, s16
gain_reg[3] |= ((bb % 10) * 100) / 125;
#ifdef DEBUG_AGC
- dprintk("GA CALC: DB: %3d(rf) + %3d(bb) = %3d gain_reg[0]=%04x gain_reg[1]=%04x gain_reg[2]=%04x gain_reg[0]=%04x", rf, bb, rf + bb,
+ dprintk("GA CALC: DB: %3d(rf) + %3d(bb) = %3d gain_reg[0]=%04x gain_reg[1]=%04x gain_reg[2]=%04x gain_reg[0]=%04x\n", rf, bb, rf + bb,
gain_reg[0], gain_reg[1], gain_reg[2], gain_reg[3]);
#endif
@@ -1063,7 +1034,7 @@ static void dib0090_set_rframp_pwm(struct dib0090_state *state, const u16 * cfg)
dib0090_write_reg(state, 0x2a, 0xffff);
- dprintk("total RF gain: %ddB, step: %d", (u32) cfg[0], dib0090_read_reg(state, 0x2a));
+ dprintk("total RF gain: %ddB, step: %d\n", (u32) cfg[0], dib0090_read_reg(state, 0x2a));
dib0090_write_regs(state, 0x2c, cfg + 3, 6);
dib0090_write_regs(state, 0x3e, cfg + 9, 2);
@@ -1082,77 +1053,82 @@ static void dib0090_set_bbramp_pwm(struct dib0090_state *state, const u16 * cfg)
dib0090_set_boost(state, cfg[0] > 500); /* we want the boost if the gain is higher that 50dB */
dib0090_write_reg(state, 0x33, 0xffff);
- dprintk("total BB gain: %ddB, step: %d", (u32) cfg[0], dib0090_read_reg(state, 0x33));
+ dprintk("total BB gain: %ddB, step: %d\n", (u32) cfg[0], dib0090_read_reg(state, 0x33));
dib0090_write_regs(state, 0x35, cfg + 3, 4);
}
void dib0090_pwm_gain_reset(struct dvb_frontend *fe)
{
struct dib0090_state *state = fe->tuner_priv;
- /* reset the AGC */
+ const u16 *bb_ramp = bb_ramp_pwm_normal; /* default baseband config */
+ const u16 *rf_ramp = NULL;
+ u8 en_pwm_rf_mux = 1;
+ /* reset the AGC */
if (state->config->use_pwm_agc) {
-#ifdef CONFIG_BAND_SBAND
- if (state->current_band == BAND_SBAND) {
- dib0090_set_rframp_pwm(state, rf_ramp_pwm_sband);
- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_boost);
- } else
-#endif
-#ifdef CONFIG_BAND_CBAND
if (state->current_band == BAND_CBAND) {
if (state->identity.in_soc) {
- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs);
+ bb_ramp = bb_ramp_pwm_normal_socs;
if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1)
- dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_8090);
- else if (state->identity.version == SOC_7090_P1G_11R1
- || state->identity.version == SOC_7090_P1G_21R1) {
+ rf_ramp = rf_ramp_pwm_cband_8090;
+ else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) {
if (state->config->is_dib7090e) {
if (state->rf_ramp == NULL)
- dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_7090e_sensitivity);
+ rf_ramp = rf_ramp_pwm_cband_7090e_sensitivity;
else
- dib0090_set_rframp_pwm(state, state->rf_ramp);
+ rf_ramp = state->rf_ramp;
} else
- dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_7090);
+ rf_ramp = rf_ramp_pwm_cband_7090p;
}
- } else {
- dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband);
- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
- }
+ } else
+ rf_ramp = rf_ramp_pwm_cband;
} else
-#endif
-#ifdef CONFIG_BAND_VHF
- if (state->current_band == BAND_VHF) {
- if (state->identity.in_soc) {
- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs);
- } else {
- dib0090_set_rframp_pwm(state, rf_ramp_pwm_vhf);
- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
+
+ if (state->current_band == BAND_VHF) {
+ if (state->identity.in_soc) {
+ bb_ramp = bb_ramp_pwm_normal_socs;
+ /* rf_ramp = &rf_ramp_pwm_vhf_socs; */ /* TODO */
+ } else
+ rf_ramp = rf_ramp_pwm_vhf;
+ } else if (state->current_band == BAND_UHF) {
+ if (state->identity.in_soc) {
+ bb_ramp = bb_ramp_pwm_normal_socs;
+ if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1)
+ rf_ramp = rf_ramp_pwm_uhf_8090;
+ else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1)
+ rf_ramp = rf_ramp_pwm_uhf_7090;
+ } else
+ rf_ramp = rf_ramp_pwm_uhf;
}
+ if (rf_ramp)
+ dib0090_set_rframp_pwm(state, rf_ramp);
+ dib0090_set_bbramp_pwm(state, bb_ramp);
+
+ /* activate the ramp generator using PWM control */
+ if (state->rf_ramp)
+ dprintk("ramp RF gain = %d BAND = %s version = %d\n",
+ state->rf_ramp[0],
+ (state->current_band == BAND_CBAND) ? "CBAND" : "NOT CBAND",
+ state->identity.version & 0x1f);
+
+ if (rf_ramp && ((state->rf_ramp && state->rf_ramp[0] == 0) ||
+ (state->current_band == BAND_CBAND &&
+ (state->identity.version & 0x1f) <= P1D_E_F))) {
+ dprintk("DE-Engage mux for direct gain reg control\n");
+ en_pwm_rf_mux = 0;
} else
-#endif
- {
- if (state->identity.in_soc) {
- if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1)
- dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf_8090);
- else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1)
- dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf_7090);
- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs);
- } else {
- dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf);
- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal);
- }
- }
+ dprintk("Engage mux for PWM control\n");
- if (state->rf_ramp[0] != 0)
- dib0090_write_reg(state, 0x32, (3 << 11));
- else
- dib0090_write_reg(state, 0x32, (0 << 11));
+ dib0090_write_reg(state, 0x32, (en_pwm_rf_mux << 12) | (en_pwm_rf_mux << 11));
- dib0090_write_reg(state, 0x04, 0x03);
- dib0090_write_reg(state, 0x39, (1 << 10));
+ /* Set fast servo cutoff to start AGC; 0 = 1KHz ; 1 = 50Hz ; 2 = 150Hz ; 3 = 50KHz ; 4 = servo fast*/
+ if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1)
+ dib0090_write_reg(state, 0x04, 3);
+ else
+ dib0090_write_reg(state, 0x04, 1);
+ dib0090_write_reg(state, 0x39, (1 << 10)); /* 0 gain by default */
}
}
-
EXPORT_SYMBOL(dib0090_pwm_gain_reset);
void dib0090_set_dc_servo(struct dvb_frontend *fe, u8 DC_servo_cutoff)
@@ -1193,22 +1169,22 @@ int dib0090_gain_control(struct dvb_frontend *fe)
#endif
#ifdef CONFIG_BAND_VHF
if (state->current_band == BAND_VHF && !state->identity.p1g) {
- dib0090_set_rframp(state, rf_ramp_vhf);
- dib0090_set_bbramp(state, bb_ramp_boost);
+ dib0090_set_rframp(state, rf_ramp_pwm_vhf);
+ dib0090_set_bbramp(state, bb_ramp_pwm_normal);
} else
#endif
#ifdef CONFIG_BAND_CBAND
if (state->current_band == BAND_CBAND && !state->identity.p1g) {
- dib0090_set_rframp(state, rf_ramp_cband);
- dib0090_set_bbramp(state, bb_ramp_boost);
+ dib0090_set_rframp(state, rf_ramp_pwm_cband);
+ dib0090_set_bbramp(state, bb_ramp_pwm_normal);
} else
#endif
if ((state->current_band == BAND_CBAND || state->current_band == BAND_VHF) && state->identity.p1g) {
- dib0090_set_rframp(state, rf_ramp_cband_broadmatching);
- dib0090_set_bbramp(state, bb_ramp_boost);
+ dib0090_set_rframp(state, rf_ramp_pwm_cband_7090p);
+ dib0090_set_bbramp(state, bb_ramp_pwm_normal_socs);
} else {
- dib0090_set_rframp(state, rf_ramp_uhf);
- dib0090_set_bbramp(state, bb_ramp_boost);
+ dib0090_set_rframp(state, rf_ramp_pwm_uhf);
+ dib0090_set_bbramp(state, bb_ramp_pwm_normal);
}
dib0090_write_reg(state, 0x32, 0);
@@ -1297,7 +1273,7 @@ int dib0090_gain_control(struct dvb_frontend *fe)
#endif
if (*tune_state == CT_AGC_STEP_1) { /* quickly go to the correct range of the ADC power */
- if (ABS(adc_error) < 50 || state->agc_step++ > 5) {
+ if (abs(adc_error) < 50 || state->agc_step++ > 5) {
#ifdef CONFIG_STANDARD_DAB
if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB) {
@@ -1360,7 +1336,7 @@ u16 dib0090_get_wbd_target(struct dvb_frontend *fe)
while (f_MHz > wbd->max_freq)
wbd++;
- dprintk("using wbd-table-entry with max freq %d", wbd->max_freq);
+ dprintk("using wbd-table-entry with max freq %d\n", wbd->max_freq);
if (current_temp < 0)
current_temp = 0;
@@ -1381,8 +1357,8 @@ u16 dib0090_get_wbd_target(struct dvb_frontend *fe)
wbd_tcold += ((wbd_thot - wbd_tcold) * current_temp) >> 7;
state->wbd_target = dib0090_wbd_to_db(state, state->wbd_offset + wbd_tcold);
- dprintk("wbd-target: %d dB", (u32) state->wbd_target);
- dprintk("wbd offset applied is %d", wbd_tcold);
+ dprintk("wbd-target: %d dB\n", (u32) state->wbd_target);
+ dprintk("wbd offset applied is %d\n", wbd_tcold);
return state->wbd_offset + wbd_tcold;
}
@@ -1423,14 +1399,14 @@ int dib0090_update_rframp_7090(struct dvb_frontend *fe, u8 cfg_sensitivity)
if ((!state->identity.p1g) || (!state->identity.in_soc)
|| ((state->identity.version != SOC_7090_P1G_21R1)
&& (state->identity.version != SOC_7090_P1G_11R1))) {
- dprintk("%s() function can only be used for dib7090P", __func__);
+ dprintk("%s() function can only be used for dib7090P\n", __func__);
return -ENODEV;
}
if (cfg_sensitivity)
- state->rf_ramp = (const u16 *)&rf_ramp_pwm_cband_7090e_sensitivity;
+ state->rf_ramp = rf_ramp_pwm_cband_7090e_sensitivity;
else
- state->rf_ramp = (const u16 *)&rf_ramp_pwm_cband_7090e_aci;
+ state->rf_ramp = rf_ramp_pwm_cband_7090e_aci;
dib0090_pwm_gain_reset(fe);
return 0;
@@ -1553,14 +1529,16 @@ static void dib0090_set_EFUSE(struct dib0090_state *state)
if ((c >= CAP_VALUE_MAX) || (c <= CAP_VALUE_MIN))
c = 32;
+ else
+ c += 14;
if ((h >= HR_MAX) || (h <= HR_MIN))
h = 34;
if ((n >= POLY_MAX) || (n <= POLY_MIN))
n = 3;
- dib0090_write_reg(state, 0x13, (h << 10)) ;
- e2 = (n<<11) | ((h>>2)<<6) | (c);
- dib0090_write_reg(state, 0x2, e2) ; /* Load the BB_2 */
+ dib0090_write_reg(state, 0x13, (h << 10));
+ e2 = (n << 11) | ((h >> 2)<<6) | c;
+ dib0090_write_reg(state, 0x2, e2); /* Load the BB_2 */
}
}
@@ -1596,7 +1574,7 @@ static int dib0090_reset(struct dvb_frontend *fe)
if (((state->identity.version & 0x1f) >= P1D_E_F) || (state->identity.in_soc))
dib0090_set_EFUSE(state);
- /* Congigure in function of the crystal */
+ /* Configure in function of the crystal */
if (state->config->force_crystal_mode != 0)
dib0090_write_reg(state, 0x14,
state->config->force_crystal_mode & 3);
@@ -1604,7 +1582,7 @@ static int dib0090_reset(struct dvb_frontend *fe)
dib0090_write_reg(state, 0x14, 1);
else
dib0090_write_reg(state, 0x14, 2);
- dprintk("Pll lock : %d", (dib0090_read_reg(state, 0x1a) >> 11) & 0x1);
+ dprintk("Pll lock : %d\n", (dib0090_read_reg(state, 0x1a) >> 11) & 0x1);
state->calibrate = DC_CAL | WBD_CAL | TEMP_CAL; /* enable iq-offset-calibration and wbd-calibration when tuning next time */
@@ -1714,12 +1692,11 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front
if (state->identity.p1g)
state->dc = dc_p1g_table;
- *tune_state = CT_TUNER_STEP_0;
-
- /* fall through */
+ fallthrough;
case CT_TUNER_STEP_0:
- dprintk("Sart/continue DC calibration for %s path", (state->dc->i == 1) ? "I" : "Q");
+ dprintk("Start/continue DC calibration for %s path\n",
+ (state->dc->i == 1) ? "I" : "Q");
dib0090_write_reg(state, 0x01, state->dc->bb1);
dib0090_write_reg(state, 0x07, state->bb7 | (state->dc->i << 7));
@@ -1741,13 +1718,13 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front
break;
case CT_TUNER_STEP_5: /* found an offset */
- dprintk("adc_diff = %d, current step= %d", (u32) state->adc_diff, state->step);
+ dprintk("adc_diff = %d, current step= %d\n", (u32) state->adc_diff, state->step);
if (state->step == 0 && state->adc_diff < 0) {
state->min_adc_diff = -1023;
- dprintk("Change of sign of the minimum adc diff");
+ dprintk("Change of sign of the minimum adc diff\n");
}
- dprintk("adc_diff = %d, min_adc_diff = %d current_step = %d", state->adc_diff, state->min_adc_diff, state->step);
+ dprintk("adc_diff = %d, min_adc_diff = %d current_step = %d\n", state->adc_diff, state->min_adc_diff, state->step);
/* first turn for this frequency */
if (state->step == 0) {
@@ -1765,13 +1742,14 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front
*tune_state = CT_TUNER_STEP_1;
} else {
/* the minimum was what we have seen in the step before */
- if (ABS(state->adc_diff) > ABS(state->min_adc_diff)) {
- dprintk("Since adc_diff N = %d > adc_diff step N-1 = %d, Come back one step", state->adc_diff, state->min_adc_diff);
+ if (abs(state->adc_diff) > abs(state->min_adc_diff)) {
+ dprintk("Since adc_diff N = %d > adc_diff step N-1 = %d, Come back one step\n", state->adc_diff, state->min_adc_diff);
state->step--;
}
dib0090_set_trim(state);
- dprintk("BB Offset Cal, BBreg=%hd,Offset=%hd,Value Set=%hd", state->dc->addr, state->adc_diff, state->step);
+ dprintk("BB Offset Cal, BBreg=%u,Offset=%d,Value Set=%d\n",
+ state->dc->addr, state->adc_diff, state->step);
state->dc++;
if (state->dc->addr == 0) /* done */
@@ -1787,6 +1765,8 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front
dib0090_write_reg(state, 0x1f, 0x7);
*tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */
state->calibrate &= ~DC_CAL;
+ break;
+
default:
break;
}
@@ -1827,7 +1807,7 @@ static int dib0090_wbd_calibration(struct dib0090_state *state, enum frontend_tu
case CT_TUNER_STEP_0:
state->wbd_offset = dib0090_get_slow_adc_val(state);
- dprintk("WBD calibration offset = %d", state->wbd_offset);
+ dprintk("WBD calibration offset = %d\n", state->wbd_offset);
*tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */
state->calibrate &= ~WBD_CAL;
break;
@@ -2063,7 +2043,7 @@ int dib0090_update_tuning_table_7090(struct dvb_frontend *fe,
struct dib0090_state *state = fe->tuner_priv;
const struct dib0090_tuning *tune =
dib0090_tuning_table_cband_7090e_sensitivity;
- const struct dib0090_tuning dib0090_tuning_table_cband_7090e_aci[] = {
+ static const struct dib0090_tuning dib0090_tuning_table_cband_7090e_aci[] = {
{ 300000, 0 , 3, 0x8165, 0x2c0, 0x2d12, 0xb84e, EN_CAB },
{ 650000, 0 , 4, 0x815B, 0x280, 0x2d12, 0xb84e, EN_CAB },
{ 860000, 0 , 5, 0x84EF, 0x280, 0x2d12, 0xb84e, EN_CAB },
@@ -2072,7 +2052,7 @@ int dib0090_update_tuning_table_7090(struct dvb_frontend *fe,
if ((!state->identity.p1g) || (!state->identity.in_soc)
|| ((state->identity.version != SOC_7090_P1G_21R1)
&& (state->identity.version != SOC_7090_P1G_11R1))) {
- dprintk("%s() function can only be used for dib7090", __func__);
+ dprintk("%s() function can only be used for dib7090\n", __func__);
return -ENODEV;
}
@@ -2106,7 +2086,8 @@ static int dib0090_captrim_search(struct dib0090_state *state, enum frontend_tun
force_soft_search = 1;
if (*tune_state == CT_TUNER_START) {
- dprintk("Start Captrim search : %s", (force_soft_search == 1) ? "FORCE SOFT SEARCH" : "AUTO");
+ dprintk("Start Captrim search : %s\n",
+ (force_soft_search == 1) ? "FORCE SOFT SEARCH" : "AUTO");
dib0090_write_reg(state, 0x10, 0x2B1);
dib0090_write_reg(state, 0x1e, 0x0032);
@@ -2148,13 +2129,13 @@ static int dib0090_captrim_search(struct dib0090_state *state, enum frontend_tun
dib0090_read_reg(state, 0x40);
state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7F;
- dprintk("***Final Captrim= 0x%x", state->fcaptrim);
+ dprintk("***Final Captrim= 0x%x\n", state->fcaptrim);
*tune_state = CT_TUNER_STEP_3;
} else {
/* MERGE for all krosus before P1G */
adc = dib0090_get_slow_adc_val(state);
- dprintk("CAPTRIM=%d; ADC = %d (ADC) & %dmV", (u32) state->captrim, (u32) adc, (u32) (adc) * (u32) 1800 / (u32) 1024);
+ dprintk("CAPTRIM=%d; ADC = %d (ADC) & %dmV\n", (u32) state->captrim, (u32) adc, (u32) (adc) * (u32) 1800 / (u32) 1024);
if (state->rest == 0 || state->identity.in_soc) { /* Just for 8090P SOCS where auto captrim HW bug : TO CHECK IN ACI for SOCS !!! if 400 for 8090p SOC => tune issue !!! */
adc_target = 200;
@@ -2170,7 +2151,7 @@ static int dib0090_captrim_search(struct dib0090_state *state, enum frontend_tun
}
if (adc < state->adc_diff) {
- dprintk("CAPTRIM=%d is closer to target (%d/%d)", (u32) state->captrim, (u32) adc, (u32) state->adc_diff);
+ dprintk("CAPTRIM=%d is closer to target (%d/%d)\n", (u32) state->captrim, (u32) adc, (u32) state->adc_diff);
state->adc_diff = adc;
state->fcaptrim = state->captrim;
}
@@ -2224,7 +2205,7 @@ static int dib0090_get_temperature(struct dib0090_state *state, enum frontend_tu
val = dib0090_get_slow_adc_val(state);
state->temperature = ((s16) ((val - state->adc_diff) * 180) >> 8) + 55;
- dprintk("temperature: %d C", state->temperature - 30);
+ dprintk("temperature: %d C\n", state->temperature - 30);
*tune_state = CT_TUNER_STEP_2;
break;
@@ -2445,14 +2426,7 @@ static int dib0090_tune(struct dvb_frontend *fe)
Den = 1;
if (Rest > 0) {
- if (state->config->analog_output)
- lo6 |= (1 << 2) | 2;
- else {
- if (state->identity.in_soc)
- lo6 |= (1 << 2) | 2;
- else
- lo6 |= (1 << 2) | 2;
- }
+ lo6 |= (1 << 2) | 2;
Den = 255;
}
dib0090_write_reg(state, 0x15, (u16) FBDiv);
@@ -2476,7 +2450,7 @@ static int dib0090_tune(struct dvb_frontend *fe)
state->current_standard = state->fe->dtv_property_cache.delivery_system;
ret = 20;
- state->calibrate = CAPTRIM_CAL; /* captrim serach now */
+ state->calibrate = CAPTRIM_CAL; /* captrim search now */
}
else if (*tune_state == CT_TUNER_STEP_0) { /* Warning : because of captrim cal, if you change this step, change it also in _cal.c file because it is the step following captrim cal state machine */
@@ -2486,13 +2460,13 @@ static int dib0090_tune(struct dvb_frontend *fe)
wbd++;
dib0090_write_reg(state, 0x1e, 0x07ff);
- dprintk("Final Captrim: %d", (u32) state->fcaptrim);
- dprintk("HFDIV code: %d", (u32) pll->hfdiv_code);
- dprintk("VCO = %d", (u32) pll->vco_band);
- dprintk("VCOF in kHz: %d ((%d*%d) << 1))", (u32) ((pll->hfdiv * state->rf_request) * 2), (u32) pll->hfdiv, (u32) state->rf_request);
- dprintk("REFDIV: %d, FREF: %d", (u32) 1, (u32) state->config->io.clock_khz);
- dprintk("FBDIV: %d, Rest: %d", (u32) dib0090_read_reg(state, 0x15), (u32) dib0090_read_reg(state, 0x17));
- dprintk("Num: %d, Den: %d, SD: %d", (u32) dib0090_read_reg(state, 0x17), (u32) (dib0090_read_reg(state, 0x16) >> 8),
+ dprintk("Final Captrim: %d\n", (u32) state->fcaptrim);
+ dprintk("HFDIV code: %d\n", (u32) pll->hfdiv_code);
+ dprintk("VCO = %d\n", (u32) pll->vco_band);
+ dprintk("VCOF in kHz: %d ((%d*%d) << 1))\n", (u32) ((pll->hfdiv * state->rf_request) * 2), (u32) pll->hfdiv, (u32) state->rf_request);
+ dprintk("REFDIV: %d, FREF: %d\n", (u32) 1, (u32) state->config->io.clock_khz);
+ dprintk("FBDIV: %d, Rest: %d\n", (u32) dib0090_read_reg(state, 0x15), (u32) dib0090_read_reg(state, 0x17));
+ dprintk("Num: %d, Den: %d, SD: %d\n", (u32) dib0090_read_reg(state, 0x17), (u32) (dib0090_read_reg(state, 0x16) >> 8),
(u32) dib0090_read_reg(state, 0x1c) & 0x3);
#define WBD 0x781 /* 1 1 1 1 0000 0 0 1 */
@@ -2506,7 +2480,7 @@ static int dib0090_tune(struct dvb_frontend *fe)
dib0090_write_reg(state, 0x10, state->wbdmux);
if ((tune->tuner_enable == EN_CAB) && state->identity.p1g) {
- dprintk("P1G : The cable band is selected and lna_tune = %d", tune->lna_tune);
+ dprintk("P1G : The cable band is selected and lna_tune = %d\n", tune->lna_tune);
dib0090_write_reg(state, 0x09, tune->lna_bias);
dib0090_write_reg(state, 0x0b, 0xb800 | (tune->lna_tune << 6) | (tune->switch_trim));
} else
@@ -2532,11 +2506,10 @@ static int dib0090_tune(struct dvb_frontend *fe)
return ret;
}
-static int dib0090_release(struct dvb_frontend *fe)
+static void dib0090_release(struct dvb_frontend *fe)
{
kfree(fe->tuner_priv);
fe->tuner_priv = NULL;
- return 0;
}
enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe)
@@ -2575,10 +2548,19 @@ static int dib0090_set_params(struct dvb_frontend *fe)
do {
ret = dib0090_tune(fe);
- if (ret != FE_CALLBACK_TIME_NEVER)
- msleep(ret / 10);
- else
+ if (ret == FE_CALLBACK_TIME_NEVER)
break;
+
+ /*
+ * Despite dib0090_tune returns time at a 0.1 ms range,
+ * the actual sleep time depends on CONFIG_HZ. The worse case
+ * is when CONFIG_HZ=100. In such case, the minimum granularity
+ * is 10ms. On some real field tests, the tuner sometimes don't
+ * lock when this timer is lower than 10ms. So, enforce a 10ms
+ * granularity and use usleep_range() instead of msleep().
+ */
+ ret = 10 * (ret + 99)/100;
+ usleep_range(ret * 1000, (ret + 1) * 1000);
} while (state->tune_state != CT_TUNER_STOP);
return 0;
@@ -2587,9 +2569,9 @@ static int dib0090_set_params(struct dvb_frontend *fe)
static const struct dvb_tuner_ops dib0090_ops = {
.info = {
.name = "DiBcom DiB0090",
- .frequency_min = 45000000,
- .frequency_max = 860000000,
- .frequency_step = 1000,
+ .frequency_min_hz = 45 * MHz,
+ .frequency_max_hz = 860 * MHz,
+ .frequency_step_hz = 1 * kHz,
},
.release = dib0090_release,
@@ -2602,9 +2584,9 @@ static const struct dvb_tuner_ops dib0090_ops = {
static const struct dvb_tuner_ops dib0090_fw_ops = {
.info = {
.name = "DiBcom DiB0090",
- .frequency_min = 45000000,
- .frequency_max = 860000000,
- .frequency_step = 1000,
+ .frequency_min_hz = 45 * MHz,
+ .frequency_max_hz = 860 * MHz,
+ .frequency_step_hz = 1 * kHz,
},
.release = dib0090_release,
@@ -2642,7 +2624,7 @@ struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapte
if (dib0090_reset(fe) != 0)
goto free_mem;
- printk(KERN_INFO "DiB0090: successfully identified\n");
+ pr_info("DiB0090: successfully identified\n");
memcpy(&fe->ops.tuner_ops, &dib0090_ops, sizeof(struct dvb_tuner_ops));
return fe;
@@ -2652,7 +2634,7 @@ struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapte
return NULL;
}
-EXPORT_SYMBOL(dib0090_register);
+EXPORT_SYMBOL_GPL(dib0090_register);
struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config)
{
@@ -2669,7 +2651,7 @@ struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_ada
if (dib0090_fw_reset_digital(fe, st->config) != 0)
goto free_mem;
- dprintk("DiB0090 FW: successfully identified");
+ dprintk("DiB0090 FW: successfully identified\n");
memcpy(&fe->ops.tuner_ops, &dib0090_fw_ops, sizeof(struct dvb_tuner_ops));
return fe;
@@ -2678,9 +2660,9 @@ free_mem:
fe->tuner_priv = NULL;
return NULL;
}
-EXPORT_SYMBOL(dib0090_fw_register);
+EXPORT_SYMBOL_GPL(dib0090_fw_register);
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
-MODULE_AUTHOR("Olivier Grenie <olivier.grenie@dibcom.fr>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
+MODULE_AUTHOR("Olivier Grenie <olivier.grenie@parrot.com>");
MODULE_DESCRIPTION("Driver for the DiBcom 0090 base-band RF Tuner");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib0090.h b/drivers/media/dvb-frontends/dib0090.h
index 781dc49de45b..e5cb311694d9 100644
--- a/drivers/media/dvb/frontends/dib0090.h
+++ b/drivers/media/dvb-frontends/dib0090.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Linux-DVB Driver for DiBcom's DiB0090 base-band RF Tuner.
*
* Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
*/
#ifndef DIB0090_H
#define DIB0090_H
@@ -75,7 +72,7 @@ struct dib0090_config {
u8 force_crystal_mode;
};
-#if defined(CONFIG_DVB_TUNER_DIB0090) || (defined(CONFIG_DVB_TUNER_DIB0090_MODULE) && defined(MODULE))
+#if IS_REACHABLE(CONFIG_DVB_TUNER_DIB0090)
extern struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config);
extern struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config);
extern void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast);
diff --git a/drivers/media/dvb-frontends/dib3000.h b/drivers/media/dvb-frontends/dib3000.h
new file mode 100644
index 000000000000..3f129efa21de
--- /dev/null
+++ b/drivers/media/dvb-frontends/dib3000.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * public header file of the frontend drivers for mobile DVB-T demodulators
+ * DiBcom 3000M-B and DiBcom 3000P/M-C (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
+ *
+ * based on GPL code from DibCom, which has
+ *
+ * Copyright (C) 2004 Amaury Demol for DiBcom
+ *
+ * Acknowledgements
+ *
+ * Amaury Demol from DiBcom for providing specs and driver
+ * sources, on which this driver (and the dvb-dibusb) are based.
+ *
+ * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information
+ */
+
+#ifndef DIB3000_H
+#define DIB3000_H
+
+#include <linux/dvb/frontend.h>
+
+struct dib3000_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+};
+
+struct dib_fe_xfer_ops
+{
+ /* pid and transfer handling is done in the demodulator */
+ int (*pid_parse)(struct dvb_frontend *fe, int onoff);
+ int (*fifo_ctrl)(struct dvb_frontend *fe, int onoff);
+ int (*pid_ctrl)(struct dvb_frontend *fe, int index, int pid, int onoff);
+ int (*tuner_pass_ctrl)(struct dvb_frontend *fe, int onoff, u8 pll_ctrl);
+};
+
+#if IS_REACHABLE(CONFIG_DVB_DIB3000MB)
+extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
+ struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops);
+#else
+static inline struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
+ struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_DIB3000MB
+
+#endif // DIB3000_H
diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb-frontends/dib3000mb.c
index af91e0c92339..63bc7b74bc8b 100644
--- a/drivers/media/dvb/frontends/dib3000mb.c
+++ b/drivers/media/dvb-frontends/dib3000mb.c
@@ -1,26 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Frontend driver for mobile DVB-T demodulator DiBcom 3000M-B
* DiBcom (http://www.dibcom.fr/)
*
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* based on GPL code from DibCom, which has
*
- * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
+ * Copyright (C) 2004 Amaury Demol for DiBcom
*
* Acknowledgements
*
- * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ * Amaury Demol from DiBcom for providing specs and driver
* sources, on which this driver (and the dvb-dibusb) are based.
*
- * see Documentation/dvb/README.dvb-usb for more information
- *
+ * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -28,7 +26,7 @@
#include <linux/string.h>
#include <linux/slab.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "dib3000.h"
#include "dib3000mb_priv.h"
@@ -36,24 +34,24 @@
/* Version information */
#define DRIVER_VERSION "0.1"
#define DRIVER_DESC "DiBcom 3000M-B DVB-T demodulator"
-#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
+#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@posteo.de"
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-able)).");
-#define deb_info(args...) dprintk(0x01,args)
-#define deb_i2c(args...) dprintk(0x02,args)
-#define deb_srch(args...) dprintk(0x04,args)
-#define deb_info(args...) dprintk(0x01,args)
-#define deb_xfer(args...) dprintk(0x02,args)
-#define deb_setf(args...) dprintk(0x04,args)
-#define deb_getf(args...) dprintk(0x08,args)
+#define deb_info(args...) dprintk(0x01, args)
+#define deb_i2c(args...) dprintk(0x02, args)
+#define deb_srch(args...) dprintk(0x04, args)
+#define deb_info(args...) dprintk(0x01, args)
+#define deb_xfer(args...) dprintk(0x02, args)
+#define deb_setf(args...) dprintk(0x04, args)
+#define deb_getf(args...) dprintk(0x08, args)
static int dib3000_read_reg(struct dib3000_state *state, u16 reg)
{
u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff };
- u8 rb[2];
+ u8 rb[2] = {};
struct i2c_msg msg[] = {
{ .addr = state->config.demod_address, .flags = 0, .buf = wb, .len = 2 },
{ .addr = state->config.demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 },
@@ -112,116 +110,110 @@ static u16 dib3000_seq[2][2][2] = /* fft,gua, inv */
}
};
-static int dib3000mb_get_frontend(struct dvb_frontend* fe);
+static int dib3000mb_get_frontend(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *c);
static int dib3000mb_set_frontend(struct dvb_frontend *fe, int tuner)
{
struct dib3000_state* state = fe->demodulator_priv;
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- fe_code_rate_t fe_cr = FEC_NONE;
+ enum fe_code_rate fe_cr = FEC_NONE;
int search_state, seq;
if (tuner && fe->ops.tuner_ops.set_params) {
fe->ops.tuner_ops.set_params(fe);
if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
- deb_setf("bandwidth: ");
switch (c->bandwidth_hz) {
case 8000000:
- deb_setf("8 MHz\n");
wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[2]);
wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_8mhz);
break;
case 7000000:
- deb_setf("7 MHz\n");
wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[1]);
wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_7mhz);
break;
case 6000000:
- deb_setf("6 MHz\n");
wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[0]);
wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_6mhz);
break;
case 0:
return -EOPNOTSUPP;
default:
- err("unknown bandwidth value.");
+ pr_err("unknown bandwidth value.\n");
return -EINVAL;
}
+ deb_setf("bandwidth: %d MHZ\n", c->bandwidth_hz / 1000000);
}
wr(DIB3000MB_REG_LOCK1_MASK, DIB3000MB_LOCK1_SEARCH_4);
- deb_setf("transmission mode: ");
switch (c->transmission_mode) {
case TRANSMISSION_MODE_2K:
- deb_setf("2k\n");
+ deb_setf("transmission mode: 2k\n");
wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_2K);
break;
case TRANSMISSION_MODE_8K:
- deb_setf("8k\n");
+ deb_setf("transmission mode: 8k\n");
wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_8K);
break;
case TRANSMISSION_MODE_AUTO:
- deb_setf("auto\n");
+ deb_setf("transmission mode: auto\n");
break;
default:
return -EINVAL;
}
- deb_setf("guard: ");
switch (c->guard_interval) {
case GUARD_INTERVAL_1_32:
- deb_setf("1_32\n");
+ deb_setf("guard 1_32\n");
wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_32);
break;
case GUARD_INTERVAL_1_16:
- deb_setf("1_16\n");
+ deb_setf("guard 1_16\n");
wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_16);
break;
case GUARD_INTERVAL_1_8:
- deb_setf("1_8\n");
+ deb_setf("guard 1_8\n");
wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_8);
break;
case GUARD_INTERVAL_1_4:
- deb_setf("1_4\n");
+ deb_setf("guard 1_4\n");
wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_4);
break;
case GUARD_INTERVAL_AUTO:
- deb_setf("auto\n");
+ deb_setf("guard auto\n");
break;
default:
return -EINVAL;
}
- deb_setf("inversion: ");
switch (c->inversion) {
case INVERSION_OFF:
- deb_setf("off\n");
+ deb_setf("inversion off\n");
wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_OFF);
break;
case INVERSION_AUTO:
- deb_setf("auto ");
+ deb_setf("inversion auto\n");
break;
case INVERSION_ON:
- deb_setf("on\n");
+ deb_setf("inversion on\n");
wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_ON);
break;
default:
return -EINVAL;
}
- deb_setf("modulation: ");
switch (c->modulation) {
case QPSK:
- deb_setf("qpsk\n");
+ deb_setf("modulation: qpsk\n");
wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_QPSK);
break;
case QAM_16:
- deb_setf("qam16\n");
+ deb_setf("modulation: qam16\n");
wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_16QAM);
break;
case QAM_64:
- deb_setf("qam64\n");
+ deb_setf("modulation: qam64\n");
wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_64QAM);
break;
case QAM_AUTO:
@@ -229,69 +221,64 @@ static int dib3000mb_set_frontend(struct dvb_frontend *fe, int tuner)
default:
return -EINVAL;
}
- deb_setf("hierarchy: ");
switch (c->hierarchy) {
case HIERARCHY_NONE:
- deb_setf("none ");
- /* fall through */
+ deb_setf("hierarchy: none\n");
+ fallthrough;
case HIERARCHY_1:
- deb_setf("alpha=1\n");
+ deb_setf("hierarchy: alpha=1\n");
wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_1);
break;
case HIERARCHY_2:
- deb_setf("alpha=2\n");
+ deb_setf("hierarchy: alpha=2\n");
wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_2);
break;
case HIERARCHY_4:
- deb_setf("alpha=4\n");
+ deb_setf("hierarchy: alpha=4\n");
wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_4);
break;
case HIERARCHY_AUTO:
- deb_setf("alpha=auto\n");
+ deb_setf("hierarchy: alpha=auto\n");
break;
default:
return -EINVAL;
}
- deb_setf("hierarchy: ");
if (c->hierarchy == HIERARCHY_NONE) {
- deb_setf("none\n");
wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_OFF);
wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_HP);
fe_cr = c->code_rate_HP;
} else if (c->hierarchy != HIERARCHY_AUTO) {
- deb_setf("on\n");
wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_ON);
wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_LP);
fe_cr = c->code_rate_LP;
}
- deb_setf("fec: ");
switch (fe_cr) {
case FEC_1_2:
- deb_setf("1_2\n");
+ deb_setf("fec: 1_2\n");
wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_1_2);
break;
case FEC_2_3:
- deb_setf("2_3\n");
+ deb_setf("fec: 2_3\n");
wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_2_3);
break;
case FEC_3_4:
- deb_setf("3_4\n");
+ deb_setf("fec: 3_4\n");
wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_3_4);
break;
case FEC_5_6:
- deb_setf("5_6\n");
+ deb_setf("fec: 5_6\n");
wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_5_6);
break;
case FEC_7_8:
- deb_setf("7_8\n");
+ deb_setf("fec: 7_8\n");
wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_7_8);
break;
case FEC_NONE:
- deb_setf("none ");
+ deb_setf("fec: none\n");
break;
case FEC_AUTO:
- deb_setf("auto\n");
+ deb_setf("fec: auto\n");
break;
default:
return -EINVAL;
@@ -356,10 +343,11 @@ static int dib3000mb_set_frontend(struct dvb_frontend *fe, int tuner)
rd(DIB3000MB_REG_LOCK2_VALUE))) < 0 && as_count++ < 100)
msleep(1);
- deb_setf("search_state after autosearch %d after %d checks\n",search_state,as_count);
+ deb_setf("search_state after autosearch %d after %d checks\n",
+ search_state, as_count);
if (search_state == 1) {
- if (dib3000mb_get_frontend(fe) == 0) {
+ if (dib3000mb_get_frontend(fe, c) == 0) {
deb_setf("reading tuning data from frontend succeeded.\n");
return dib3000mb_set_frontend(fe, 0);
}
@@ -450,11 +438,11 @@ static int dib3000mb_fe_init(struct dvb_frontend* fe, int mobile_mode)
return 0;
}
-static int dib3000mb_get_frontend(struct dvb_frontend* fe)
+static int dib3000mb_get_frontend(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *c)
{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct dib3000_state* state = fe->demodulator_priv;
- fe_code_rate_t *cr;
+ enum fe_code_rate *cr;
u16 tps_val;
int inv_test1,inv_test2;
u32 dds_val, threshold = 0x800000;
@@ -463,7 +451,7 @@ static int dib3000mb_get_frontend(struct dvb_frontend* fe)
return 0;
dds_val = ((rd(DIB3000MB_REG_DDS_VALUE_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_VALUE_LSB);
- deb_getf("DDS_VAL: %x %x %x",dds_val, rd(DIB3000MB_REG_DDS_VALUE_MSB), rd(DIB3000MB_REG_DDS_VALUE_LSB));
+ deb_getf("DDS_VAL: %x %x %x\n", dds_val, rd(DIB3000MB_REG_DDS_VALUE_MSB), rd(DIB3000MB_REG_DDS_VALUE_LSB));
if (dds_val < threshold)
inv_test1 = 0;
else if (dds_val == threshold)
@@ -472,7 +460,7 @@ static int dib3000mb_get_frontend(struct dvb_frontend* fe)
inv_test1 = 2;
dds_val = ((rd(DIB3000MB_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_FREQ_LSB);
- deb_getf("DDS_FREQ: %x %x %x",dds_val, rd(DIB3000MB_REG_DDS_FREQ_MSB), rd(DIB3000MB_REG_DDS_FREQ_LSB));
+ deb_getf("DDS_FREQ: %x %x %x\n", dds_val, rd(DIB3000MB_REG_DDS_FREQ_MSB), rd(DIB3000MB_REG_DDS_FREQ_LSB));
if (dds_val < threshold)
inv_test2 = 0;
else if (dds_val == threshold)
@@ -489,19 +477,19 @@ static int dib3000mb_get_frontend(struct dvb_frontend* fe)
switch ((tps_val = rd(DIB3000MB_REG_TPS_QAM))) {
case DIB3000_CONSTELLATION_QPSK:
- deb_getf("QPSK ");
+ deb_getf("QPSK\n");
c->modulation = QPSK;
break;
case DIB3000_CONSTELLATION_16QAM:
- deb_getf("QAM16 ");
+ deb_getf("QAM16\n");
c->modulation = QAM_16;
break;
case DIB3000_CONSTELLATION_64QAM:
- deb_getf("QAM64 ");
+ deb_getf("QAM64\n");
c->modulation = QAM_64;
break;
default:
- err("Unexpected constellation returned by TPS (%d)", tps_val);
+ pr_err("Unexpected constellation returned by TPS (%d)\n", tps_val);
break;
}
deb_getf("TPS: %d\n", tps_val);
@@ -512,23 +500,23 @@ static int dib3000mb_get_frontend(struct dvb_frontend* fe)
c->code_rate_HP = FEC_NONE;
switch ((tps_val = rd(DIB3000MB_REG_TPS_VIT_ALPHA))) {
case DIB3000_ALPHA_0:
- deb_getf("HIERARCHY_NONE ");
+ deb_getf("HIERARCHY_NONE\n");
c->hierarchy = HIERARCHY_NONE;
break;
case DIB3000_ALPHA_1:
- deb_getf("HIERARCHY_1 ");
+ deb_getf("HIERARCHY_1\n");
c->hierarchy = HIERARCHY_1;
break;
case DIB3000_ALPHA_2:
- deb_getf("HIERARCHY_2 ");
+ deb_getf("HIERARCHY_2\n");
c->hierarchy = HIERARCHY_2;
break;
case DIB3000_ALPHA_4:
- deb_getf("HIERARCHY_4 ");
+ deb_getf("HIERARCHY_4\n");
c->hierarchy = HIERARCHY_4;
break;
default:
- err("Unexpected ALPHA value returned by TPS (%d)", tps_val);
+ pr_err("Unexpected ALPHA value returned by TPS (%d)\n", tps_val);
break;
}
deb_getf("TPS: %d\n", tps_val);
@@ -545,65 +533,65 @@ static int dib3000mb_get_frontend(struct dvb_frontend* fe)
switch (tps_val) {
case DIB3000_FEC_1_2:
- deb_getf("FEC_1_2 ");
+ deb_getf("FEC_1_2\n");
*cr = FEC_1_2;
break;
case DIB3000_FEC_2_3:
- deb_getf("FEC_2_3 ");
+ deb_getf("FEC_2_3\n");
*cr = FEC_2_3;
break;
case DIB3000_FEC_3_4:
- deb_getf("FEC_3_4 ");
+ deb_getf("FEC_3_4\n");
*cr = FEC_3_4;
break;
case DIB3000_FEC_5_6:
- deb_getf("FEC_5_6 ");
+ deb_getf("FEC_5_6\n");
*cr = FEC_4_5;
break;
case DIB3000_FEC_7_8:
- deb_getf("FEC_7_8 ");
+ deb_getf("FEC_7_8\n");
*cr = FEC_7_8;
break;
default:
- err("Unexpected FEC returned by TPS (%d)", tps_val);
+ pr_err("Unexpected FEC returned by TPS (%d)\n", tps_val);
break;
}
deb_getf("TPS: %d\n",tps_val);
switch ((tps_val = rd(DIB3000MB_REG_TPS_GUARD_TIME))) {
case DIB3000_GUARD_TIME_1_32:
- deb_getf("GUARD_INTERVAL_1_32 ");
+ deb_getf("GUARD_INTERVAL_1_32\n");
c->guard_interval = GUARD_INTERVAL_1_32;
break;
case DIB3000_GUARD_TIME_1_16:
- deb_getf("GUARD_INTERVAL_1_16 ");
+ deb_getf("GUARD_INTERVAL_1_16\n");
c->guard_interval = GUARD_INTERVAL_1_16;
break;
case DIB3000_GUARD_TIME_1_8:
- deb_getf("GUARD_INTERVAL_1_8 ");
+ deb_getf("GUARD_INTERVAL_1_8\n");
c->guard_interval = GUARD_INTERVAL_1_8;
break;
case DIB3000_GUARD_TIME_1_4:
- deb_getf("GUARD_INTERVAL_1_4 ");
+ deb_getf("GUARD_INTERVAL_1_4\n");
c->guard_interval = GUARD_INTERVAL_1_4;
break;
default:
- err("Unexpected Guard Time returned by TPS (%d)", tps_val);
+ pr_err("Unexpected Guard Time returned by TPS (%d)\n", tps_val);
break;
}
deb_getf("TPS: %d\n", tps_val);
switch ((tps_val = rd(DIB3000MB_REG_TPS_FFT))) {
case DIB3000_TRANSMISSION_MODE_2K:
- deb_getf("TRANSMISSION_MODE_2K ");
+ deb_getf("TRANSMISSION_MODE_2K\n");
c->transmission_mode = TRANSMISSION_MODE_2K;
break;
case DIB3000_TRANSMISSION_MODE_8K:
- deb_getf("TRANSMISSION_MODE_8K ");
+ deb_getf("TRANSMISSION_MODE_8K\n");
c->transmission_mode = TRANSMISSION_MODE_8K;
break;
default:
- err("unexpected transmission mode return by TPS (%d)", tps_val);
+ pr_err("unexpected transmission mode return by TPS (%d)\n", tps_val);
break;
}
deb_getf("TPS: %d\n", tps_val);
@@ -611,7 +599,8 @@ static int dib3000mb_get_frontend(struct dvb_frontend* fe)
return 0;
}
-static int dib3000mb_read_status(struct dvb_frontend* fe, fe_status_t *stat)
+static int dib3000mb_read_status(struct dvb_frontend *fe,
+ enum fe_status *stat)
{
struct dib3000_state* state = fe->demodulator_priv;
@@ -651,7 +640,7 @@ static int dib3000mb_read_ber(struct dvb_frontend* fe, u32 *ber)
return 0;
}
-/* see dib3000-watch dvb-apps for exact calcuations of signal_strength and snr */
+/* see dib3000-watch dvb-apps for exact calculations of signal_strength and snr */
static int dib3000mb_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
{
struct dib3000_state* state = fe->demodulator_priv;
@@ -749,7 +738,7 @@ static int dib3000mb_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_
return 0;
}
-static struct dvb_frontend_ops dib3000mb_ops;
+static const struct dvb_frontend_ops dib3000mb_ops;
struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops)
@@ -789,13 +778,13 @@ error:
return NULL;
}
-static struct dvb_frontend_ops dib3000mb_ops = {
+static const struct dvb_frontend_ops dib3000mb_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = "DiBcom 3000M-B DVB-T",
- .frequency_min = 44250000,
- .frequency_max = 867250000,
- .frequency_stepsize = 62500,
+ .frequency_min_hz = 44250 * kHz,
+ .frequency_max_hz = 867250 * kHz,
+ .frequency_stepsize_hz = 62500,
.caps = FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
@@ -826,4 +815,4 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(dib3000mb_attach);
+EXPORT_SYMBOL_GPL(dib3000mb_attach);
diff --git a/drivers/media/dvb/frontends/dib3000mb_priv.h b/drivers/media/dvb-frontends/dib3000mb_priv.h
index 9dc235aa44b7..2503966fb82f 100644
--- a/drivers/media/dvb/frontends/dib3000mb_priv.h
+++ b/drivers/media/dvb-frontends/dib3000mb_priv.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* dib3000mb_priv.h
*
- * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
+ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* for more information see dib3000mb.c .
*/
@@ -13,20 +10,15 @@
#ifndef __DIB3000MB_PRIV_H_INCLUDED__
#define __DIB3000MB_PRIV_H_INCLUDED__
-/* info and err, taken from usb.h, if there is anything available like by default. */
-#define err(format, arg...) printk(KERN_ERR "dib3000: " format "\n" , ## arg)
-#define info(format, arg...) printk(KERN_INFO "dib3000: " format "\n" , ## arg)
-#define warn(format, arg...) printk(KERN_WARNING "dib3000: " format "\n" , ## arg)
-
/* handy shortcuts */
#define rd(reg) dib3000_read_reg(state,reg)
#define wr(reg,val) if (dib3000_write_reg(state,reg,val)) \
- { err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; }
+ { pr_err("while sending 0x%04x to 0x%04x.", val, reg); return -EREMOTEIO; }
#define wr_foreach(a,v) { int i; \
if (sizeof(a) != sizeof(v)) \
- err("sizeof: %zu %zu is different",sizeof(a),sizeof(v));\
+ pr_err("sizeof: %zu %zu is different", sizeof(a), sizeof(v));\
for (i=0; i < sizeof(a)/sizeof(u16); i++) \
wr(a[i],v[i]); \
}
@@ -37,8 +29,11 @@
/* debug */
-#define dprintk(level,args...) \
- do { if ((debug & level)) { printk(args); } } while (0)
+#define dprintk(level, fmt, arg...) do { \
+ if (debug & level) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
/* mask for enabling a specific pid for the pid_filter */
#define DIB3000_ACTIVATE_PID_FILTERING (0x2000)
diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb-frontends/dib3000mc.c
index ffad181a9692..c2fca8289aba 100644
--- a/drivers/media/dvb/frontends/dib3000mc.c
+++ b/drivers/media/dvb-frontends/dib3000mc.c
@@ -1,21 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for DiBcom DiB3000MC/P-demodulator.
*
* Copyright (C) 2004-7 DiBcom (http://www.dibcom.fr/)
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* This code is partially based on the previous dib3000mc.c .
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "dib3000mc.h"
@@ -27,7 +26,11 @@ static int buggy_sfn_workaround;
module_param(buggy_sfn_workaround, int, 0644);
MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (default: 0)");
-#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB3000MC/P:"); printk(args); printk("\n"); } } while (0)
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
struct dib3000mc_state {
struct dvb_frontend demod;
@@ -49,29 +52,57 @@ struct dib3000mc_state {
static u16 dib3000mc_read_word(struct dib3000mc_state *state, u16 reg)
{
- u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff };
- u8 rb[2];
struct i2c_msg msg[2] = {
- { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 },
- { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+ { .addr = state->i2c_addr >> 1, .flags = 0, .len = 2 },
+ { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .len = 2 },
};
+ u16 word;
+ u8 *b;
+
+ b = kmalloc(4, GFP_KERNEL);
+ if (!b)
+ return 0;
+
+ b[0] = (reg >> 8) | 0x80;
+ b[1] = reg;
+ b[2] = 0;
+ b[3] = 0;
+
+ msg[0].buf = b;
+ msg[1].buf = b + 2;
if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
dprintk("i2c read error on %d\n",reg);
- return (rb[0] << 8) | rb[1];
+ word = (b[2] << 8) | b[3];
+ kfree(b);
+
+ return word;
}
static int dib3000mc_write_word(struct dib3000mc_state *state, u16 reg, u16 val)
{
- u8 b[4] = {
- (reg >> 8) & 0xff, reg & 0xff,
- (val >> 8) & 0xff, val & 0xff,
- };
struct i2c_msg msg = {
- .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4
+ .addr = state->i2c_addr >> 1, .flags = 0, .len = 4
};
- return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+ int rc;
+ u8 *b;
+
+ b = kmalloc(4, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ b[0] = reg >> 8;
+ b[1] = reg;
+ b[2] = val >> 8;
+ b[3] = val;
+
+ msg.buf = b;
+
+ rc = i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+ kfree(b);
+
+ return rc;
}
static int dib3000mc_identify(struct dib3000mc_state *state)
@@ -131,7 +162,7 @@ static int dib3000mc_set_timing(struct dib3000mc_state *state, s16 nfft, u32 bw,
static int dib3000mc_setup_pwm_state(struct dib3000mc_state *state)
{
u16 reg_51, reg_52 = state->cfg->agc->setup & 0xfefb;
- if (state->cfg->pwm3_inversion) {
+ if (state->cfg->pwm3_inversion) {
reg_51 = (2 << 14) | (0 << 10) | (7 << 6) | (2 << 2) | (2 << 0);
reg_52 |= (1 << 2);
} else {
@@ -141,12 +172,12 @@ static int dib3000mc_setup_pwm_state(struct dib3000mc_state *state)
dib3000mc_write_word(state, 51, reg_51);
dib3000mc_write_word(state, 52, reg_52);
- if (state->cfg->use_pwm3)
+ if (state->cfg->use_pwm3)
dib3000mc_write_word(state, 245, (1 << 3) | (1 << 0));
else
dib3000mc_write_word(state, 245, 0);
- dib3000mc_write_word(state, 1040, 0x3);
+ dib3000mc_write_word(state, 1040, 0x3);
return 0;
}
@@ -417,7 +448,7 @@ static int dib3000mc_sleep(struct dvb_frontend *demod)
dib3000mc_write_word(state, 1032, 0xFFFF);
dib3000mc_write_word(state, 1033, 0xFFF0);
- return 0;
+ return 0;
}
static void dib3000mc_set_adp_cfg(struct dib3000mc_state *state, s16 qam)
@@ -447,10 +478,14 @@ static void dib3000mc_set_channel_cfg(struct dib3000mc_state *state,
dib3000mc_set_bandwidth(state, bw);
dib3000mc_set_timing(state, ch->transmission_mode, bw, 0);
-// if (boost)
-// dib3000mc_write_word(state, 100, (11 << 6) + 6);
-// else
+#if 1
+ dib3000mc_write_word(state, 100, (16 << 6) + 9);
+#else
+ if (boost)
+ dib3000mc_write_word(state, 100, (11 << 6) + 6);
+ else
dib3000mc_write_word(state, 100, (16 << 6) + 9);
+#endif
dib3000mc_write_word(state, 1027, 0x0800);
dib3000mc_write_word(state, 1027, 0x0000);
@@ -632,9 +667,9 @@ struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod,
EXPORT_SYMBOL(dib3000mc_get_tuner_i2c_master);
-static int dib3000mc_get_frontend(struct dvb_frontend* fe)
+static int dib3000mc_get_frontend(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *fep)
{
- struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
struct dib3000mc_state *state = fe->demodulator_priv;
u16 tps = dib3000mc_read_word(state,458);
@@ -722,7 +757,7 @@ static int dib3000mc_set_frontend(struct dvb_frontend *fe)
if (found == 0 || found == 1)
return 0; // no channel found
- dib3000mc_get_frontend(fe);
+ dib3000mc_get_frontend(fe, fep);
}
ret = dib3000mc_tune(fe);
@@ -732,7 +767,7 @@ static int dib3000mc_set_frontend(struct dvb_frontend *fe)
return ret;
}
-static int dib3000mc_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+static int dib3000mc_read_status(struct dvb_frontend *fe, enum fe_status *stat)
{
struct dib3000mc_state *state = fe->demodulator_priv;
u16 lock = dib3000mc_read_word(state, 509);
@@ -824,7 +859,7 @@ int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defa
int k;
u8 new_addr;
- static u8 DIB3000MC_I2C_ADDRESS[] = {20,22,24,26};
+ static const u8 DIB3000MC_I2C_ADDRESS[] = { 20, 22, 24, 26 };
dmcst = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL);
if (dmcst == NULL)
@@ -869,7 +904,7 @@ int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defa
}
EXPORT_SYMBOL(dib3000mc_i2c_enumeration);
-static struct dvb_frontend_ops dib3000mc_ops;
+static const struct dvb_frontend_ops dib3000mc_ops;
struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg)
{
@@ -900,15 +935,15 @@ error:
kfree(st);
return NULL;
}
-EXPORT_SYMBOL(dib3000mc_attach);
+EXPORT_SYMBOL_GPL(dib3000mc_attach);
-static struct dvb_frontend_ops dib3000mc_ops = {
+static const struct dvb_frontend_ops dib3000mc_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = "DiBcom 3000MC/P",
- .frequency_min = 44250000,
- .frequency_max = 867250000,
- .frequency_stepsize = 62500,
+ .frequency_min_hz = 44250 * kHz,
+ .frequency_max_hz = 867250 * kHz,
+ .frequency_stepsize_hz = 62500,
.caps = FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
@@ -935,6 +970,6 @@ static struct dvb_frontend_ops dib3000mc_ops = {
.read_ucblocks = dib3000mc_read_unc_blocks,
};
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for the DiBcom 3000MC/P COFDM demodulator");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib3000mc.h b/drivers/media/dvb-frontends/dib3000mc.h
index d75ffad2d752..5ed3d9147f8b 100644
--- a/drivers/media/dvb/frontends/dib3000mc.h
+++ b/drivers/media/dvb-frontends/dib3000mc.h
@@ -1,14 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Driver for DiBcom DiB3000MC/P-demodulator.
*
* Copyright (C) 2004-6 DiBcom (http://www.dibcom.fr/)
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher\@desy.de)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de)
*
* This code is partially based on the previous dib3000mc.c .
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
*/
#ifndef DIB3000MC_H
#define DIB3000MC_H
@@ -39,8 +36,7 @@ struct dib3000mc_config {
#define DEFAULT_DIB3000MC_I2C_ADDRESS 16
#define DEFAULT_DIB3000P_I2C_ADDRESS 24
-#if defined(CONFIG_DVB_DIB3000MC) || (defined(CONFIG_DVB_DIB3000MC_MODULE) && \
- defined(MODULE))
+#if IS_REACHABLE(CONFIG_DVB_DIB3000MC)
extern struct dvb_frontend *dib3000mc_attach(struct i2c_adapter *i2c_adap,
u8 i2c_addr,
struct dib3000mc_config *cfg);
diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb-frontends/dib7000m.c
index 148bf79236fb..fdb22f32e3a1 100644
--- a/drivers/media/dvb/frontends/dib7000m.c
+++ b/drivers/media/dvb-frontends/dib7000m.c
@@ -1,19 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Linux-DVB Driver for DiBcom's DiB7000M and
* first generation DiB7000P-demodulator-family.
*
* Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "dib7000m.h"
@@ -21,7 +21,11 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
-#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000M: "); printk(args); printk("\n"); } } while (0)
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
struct dib7000m_state {
struct dvb_frontend demod;
@@ -74,7 +78,7 @@ static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg)
u16 ret;
if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
- dprintk("could not acquire lock");
+ dprintk("could not acquire lock\n");
return 0;
}
@@ -92,7 +96,7 @@ static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg)
state->msg[1].len = 2;
if (i2c_transfer(state->i2c_adap, state->msg, 2) != 2)
- dprintk("i2c read error on %d",reg);
+ dprintk("i2c read error on %d\n", reg);
ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
mutex_unlock(&state->i2c_buffer_lock);
@@ -105,7 +109,7 @@ static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val)
int ret;
if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
- dprintk("could not acquire lock");
+ dprintk("could not acquire lock\n");
return -EINVAL;
}
@@ -154,7 +158,7 @@ static int dib7000m_set_output_mode(struct dib7000m_state *state, int mode)
fifo_threshold = 1792;
smo_mode = (dib7000m_read_word(state, 294 + state->reg_offs) & 0x0010) | (1 << 1);
- dprintk( "setting output mode for demod %p to %d", &state->demod, mode);
+ dprintk("setting output mode for demod %p to %d\n", &state->demod, mode);
switch (mode) {
case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock
@@ -181,7 +185,7 @@ static int dib7000m_set_output_mode(struct dib7000m_state *state, int mode)
outreg = 0;
break;
default:
- dprintk( "Unhandled output_mode passed to be set for demod %p",&state->demod);
+ dprintk("Unhandled output_mode passed to be set for demod %p\n", &state->demod);
break;
}
@@ -302,7 +306,7 @@ static int dib7000m_set_adc_state(struct dib7000m_state *state, enum dibx000_adc
break;
}
-// dprintk( "913: %x, 914: %x", reg_913, reg_914);
+// dprintk("913: %x, 914: %x\n", reg_913, reg_914);
ret |= dib7000m_write_word(state, 913, reg_913);
ret |= dib7000m_write_word(state, 914, reg_914);
@@ -320,10 +324,10 @@ static int dib7000m_set_bandwidth(struct dib7000m_state *state, u32 bw)
state->current_bandwidth = bw;
if (state->timf == 0) {
- dprintk( "using default timf");
+ dprintk("using default timf\n");
timf = state->timf_default;
} else {
- dprintk( "using updated timf");
+ dprintk("using updated timf\n");
timf = state->timf;
}
@@ -340,7 +344,7 @@ static int dib7000m_set_diversity_in(struct dvb_frontend *demod, int onoff)
struct dib7000m_state *state = demod->demodulator_priv;
if (state->div_force_off) {
- dprintk( "diversity combination deactivated - forced by COFDM parameters");
+ dprintk("diversity combination deactivated - forced by COFDM parameters\n");
onoff = 0;
}
state->div_state = (u8)onoff;
@@ -362,7 +366,7 @@ static int dib7000m_sad_calib(struct dib7000m_state *state)
{
/* internal */
-// dib7000m_write_word(state, 928, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth
+// dib7000m_write_word(state, 928, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writing in set_bandwidth
dib7000m_write_word(state, 929, (0 << 1) | (0 << 0));
dib7000m_write_word(state, 930, 776); // 0.625*3.3 / 4096
@@ -580,10 +584,10 @@ static int dib7000m_demod_reset(struct dib7000m_state *state)
dib7000mc_reset_pll(state);
if (dib7000m_reset_gpio(state) != 0)
- dprintk( "GPIO reset was not successful.");
+ dprintk("GPIO reset was not successful.\n");
if (dib7000m_set_output_mode(state, OUTMODE_HIGH_Z) != 0)
- dprintk( "OUTPUT_MODE could not be reset.");
+ dprintk("OUTPUT_MODE could not be reset.\n");
/* unforce divstr regardless whether i2c enumeration was done or not */
dib7000m_write_word(state, 1794, dib7000m_read_word(state, 1794) & ~(1 << 1) );
@@ -650,7 +654,7 @@ static int dib7000m_agc_soft_split(struct dib7000m_state *state)
(agc - state->current_agc->split.min_thres) /
(state->current_agc->split.max_thres - state->current_agc->split.min_thres);
- dprintk( "AGC split_offset: %d",split_offset);
+ dprintk("AGC split_offset: %d\n", split_offset);
// P_agc_force_split and P_agc_split_offset
return dib7000m_write_word(state, 103, (dib7000m_read_word(state, 103) & 0xff00) | split_offset);
@@ -687,7 +691,7 @@ static int dib7000m_set_agc_config(struct dib7000m_state *state, u8 band)
}
if (agc == NULL) {
- dprintk( "no valid AGC configuration found for band 0x%02x",band);
+ dprintk("no valid AGC configuration found for band 0x%02x\n", band);
return -EINVAL;
}
@@ -703,7 +707,7 @@ static int dib7000m_set_agc_config(struct dib7000m_state *state, u8 band)
dib7000m_write_word(state, 98, (agc->alpha_mant << 5) | agc->alpha_exp);
dib7000m_write_word(state, 99, (agc->beta_mant << 6) | agc->beta_exp);
- dprintk( "WBD: ref: %d, sel: %d, active: %d, alpha: %d",
+ dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d\n",
state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel);
/* AGC continued */
@@ -724,7 +728,7 @@ static int dib7000m_set_agc_config(struct dib7000m_state *state, u8 band)
if (state->revision > 0x4000) { // settings for the MC
dib7000m_write_word(state, 71, agc->agc1_pt3);
-// dprintk( "929: %x %d %d",
+// dprintk("929: %x %d %d\n",
// (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2), agc->wbd_inv, agc->wbd_sel);
dib7000m_write_word(state, 929, (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2));
} else {
@@ -742,7 +746,7 @@ static void dib7000m_update_timf(struct dib7000m_state *state)
state->timf = timf * 160 / (state->current_bandwidth / 50);
dib7000m_write_word(state, 23, (u16) (timf >> 16));
dib7000m_write_word(state, 24, (u16) (timf & 0xffff));
- dprintk( "updated timf_frequency: %d (default: %d)",state->timf, state->timf_default);
+ dprintk("updated timf_frequency: %d (default: %d)\n", state->timf, state->timf_default);
}
static int dib7000m_agc_startup(struct dvb_frontend *demod)
@@ -804,7 +808,7 @@ static int dib7000m_agc_startup(struct dvb_frontend *demod)
dib7000m_restart_agc(state);
- dprintk( "SPLIT %p: %hd", demod, agc_split);
+ dprintk("SPLIT %p: %u\n", demod, agc_split);
(*agc_state)++;
ret = 5;
@@ -921,7 +925,7 @@ static void dib7000m_set_channel(struct dib7000m_state *state, struct dtv_fronte
}
state->div_sync_wait = (value * 3) / 2 + 32; // add 50% SFN margin + compensate for one DVSY-fifo TODO
- /* deactive the possibility of diversity reception if extended interleave - not for 7000MC */
+ /* deactivate the possibility of diversity reception if extended interleave - not for 7000MC */
/* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */
if (1 == 1 || state->revision > 0x4000)
state->div_force_off = 0;
@@ -1013,12 +1017,12 @@ static int dib7000m_autosearch_irq(struct dib7000m_state *state, u16 reg)
u16 irq_pending = dib7000m_read_word(state, reg);
if (irq_pending & 0x1) { // failed
- dprintk( "autosearch failed");
+ dprintk("autosearch failed\n");
return 1;
}
if (irq_pending & 0x2) { // succeeded
- dprintk( "autosearch succeeded");
+ dprintk("autosearch succeeded\n");
return 2;
}
return 0; // still pending
@@ -1041,10 +1045,7 @@ static int dib7000m_tune(struct dvb_frontend *demod)
u16 value;
// we are already tuned - just resuming from suspend
- if (ch != NULL)
- dib7000m_set_channel(state, ch, 0);
- else
- return -EINVAL;
+ dib7000m_set_channel(state, ch, 0);
// restart demod
ret |= dib7000m_write_word(state, 898, 0x4000);
@@ -1105,7 +1106,7 @@ static int dib7000m_wakeup(struct dvb_frontend *demod)
dib7000m_set_power_mode(state, DIB7000M_POWER_ALL);
if (dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0)
- dprintk( "could not start Slow ADC");
+ dprintk("could not start Slow ADC\n");
return 0;
}
@@ -1124,7 +1125,7 @@ static int dib7000m_identify(struct dib7000m_state *state)
u16 value;
if ((value = dib7000m_read_word(state, 896)) != 0x01b3) {
- dprintk( "wrong Vendor ID (0x%x)",value);
+ dprintk("wrong Vendor ID (0x%x)\n", value);
return -EREMOTEIO;
}
@@ -1133,30 +1134,30 @@ static int dib7000m_identify(struct dib7000m_state *state)
state->revision != 0x4001 &&
state->revision != 0x4002 &&
state->revision != 0x4003) {
- dprintk( "wrong Device ID (0x%x)",value);
+ dprintk("wrong Device ID (0x%x)\n", value);
return -EREMOTEIO;
}
/* protect this driver to be used with 7000PC */
if (state->revision == 0x4000 && dib7000m_read_word(state, 769) == 0x4000) {
- dprintk( "this driver does not work with DiB7000PC");
+ dprintk("this driver does not work with DiB7000PC\n");
return -EREMOTEIO;
}
switch (state->revision) {
- case 0x4000: dprintk( "found DiB7000MA/PA/MB/PB"); break;
- case 0x4001: state->reg_offs = 1; dprintk( "found DiB7000HC"); break;
- case 0x4002: state->reg_offs = 1; dprintk( "found DiB7000MC"); break;
- case 0x4003: state->reg_offs = 1; dprintk( "found DiB9000"); break;
+ case 0x4000: dprintk("found DiB7000MA/PA/MB/PB\n"); break;
+ case 0x4001: state->reg_offs = 1; dprintk("found DiB7000HC\n"); break;
+ case 0x4002: state->reg_offs = 1; dprintk("found DiB7000MC\n"); break;
+ case 0x4003: state->reg_offs = 1; dprintk("found DiB9000\n"); break;
}
return 0;
}
-static int dib7000m_get_frontend(struct dvb_frontend* fe)
+static int dib7000m_get_frontend(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *fep)
{
- struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
struct dib7000m_state *state = fe->demodulator_priv;
u16 tps = dib7000m_read_word(state,480);
@@ -1245,11 +1246,11 @@ static int dib7000m_set_frontend(struct dvb_frontend *fe)
found = dib7000m_autosearch_is_irq(fe);
} while (found == 0 && i--);
- dprintk("autosearch returns: %d",found);
+ dprintk("autosearch returns: %d\n", found);
if (found == 0 || found == 1)
return 0; // no channel found
- dib7000m_get_frontend(fe);
+ dib7000m_get_frontend(fe, fep);
}
ret = dib7000m_tune(fe);
@@ -1259,7 +1260,7 @@ static int dib7000m_set_frontend(struct dvb_frontend *fe)
return ret;
}
-static int dib7000m_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+static int dib7000m_read_status(struct dvb_frontend *fe, enum fe_status *stat)
{
struct dib7000m_state *state = fe->demodulator_priv;
u16 lock = dib7000m_read_word(state, 535);
@@ -1333,7 +1334,7 @@ int dib7000m_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
struct dib7000m_state *state = fe->demodulator_priv;
u16 val = dib7000m_read_word(state, 294 + state->reg_offs) & 0xffef;
val |= (onoff & 0x1) << 4;
- dprintk("PID filter enabled %d", onoff);
+ dprintk("PID filter enabled %d\n", onoff);
return dib7000m_write_word(state, 294 + state->reg_offs, val);
}
EXPORT_SYMBOL(dib7000m_pid_filter_ctrl);
@@ -1341,7 +1342,7 @@ EXPORT_SYMBOL(dib7000m_pid_filter_ctrl);
int dib7000m_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
{
struct dib7000m_state *state = fe->demodulator_priv;
- dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff);
+ dprintk("PID filter: index %x, PID %d, OnOff %d\n", id, pid, onoff);
return dib7000m_write_word(state, 300 + state->reg_offs + id,
onoff ? (1 << 13) | pid : 0);
}
@@ -1365,7 +1366,7 @@ int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods,
if (dib7000m_identify(&st) != 0) {
st.i2c_addr = default_addr;
if (dib7000m_identify(&st) != 0) {
- dprintk("DiB7000M #%d: not identified", k);
+ dprintk("DiB7000M #%d: not identified\n", k);
return -EIO;
}
}
@@ -1378,7 +1379,7 @@ int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods,
/* set new i2c address and force divstart */
dib7000m_write_word(&st, 1794, (new_addr << 2) | 0x2);
- dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr);
+ dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr);
}
for (k = 0; k < no_of_demods; k++) {
@@ -1397,7 +1398,7 @@ int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods,
EXPORT_SYMBOL(dib7000m_i2c_enumeration);
#endif
-static struct dvb_frontend_ops dib7000m_ops;
+static const struct dvb_frontend_ops dib7000m_ops;
struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg)
{
struct dvb_frontend *demod;
@@ -1433,15 +1434,15 @@ error:
kfree(st);
return NULL;
}
-EXPORT_SYMBOL(dib7000m_attach);
+EXPORT_SYMBOL_GPL(dib7000m_attach);
-static struct dvb_frontend_ops dib7000m_ops = {
+static const struct dvb_frontend_ops dib7000m_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = "DiBcom 7000MA/MB/PA/PB/MC",
- .frequency_min = 44250000,
- .frequency_max = 867250000,
- .frequency_stepsize = 62500,
+ .frequency_min_hz = 44250 * kHz,
+ .frequency_max_hz = 867250 * kHz,
+ .frequency_stepsize_hz = 62500,
.caps = FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
@@ -1468,6 +1469,6 @@ static struct dvb_frontend_ops dib7000m_ops = {
.read_ucblocks = dib7000m_read_unc_blocks,
};
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for the DiBcom 7000MA/MB/PA/PB/MC COFDM demodulator");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib7000m.h b/drivers/media/dvb-frontends/dib7000m.h
index 81fcf2241c64..df7ecb4314cd 100644
--- a/drivers/media/dvb/frontends/dib7000m.h
+++ b/drivers/media/dvb-frontends/dib7000m.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef DIB7000M_H
#define DIB7000M_H
@@ -38,8 +39,7 @@ struct dib7000m_config {
#define DEFAULT_DIB7000M_I2C_ADDRESS 18
-#if defined(CONFIG_DVB_DIB7000M) || (defined(CONFIG_DVB_DIB7000M_MODULE) && \
- defined(MODULE))
+#if IS_REACHABLE(CONFIG_DVB_DIB7000M)
extern struct dvb_frontend *dib7000m_attach(struct i2c_adapter *i2c_adap,
u8 i2c_addr,
struct dib7000m_config *cfg);
diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c
index 3e1eefada0e8..7d3a994b7cc4 100644
--- a/drivers/media/dvb/frontends/dib7000p.c
+++ b/drivers/media/dvb-frontends/dib7000p.c
@@ -1,19 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Linux-DVB Driver for DiBcom's second generation DiB7000P (PC).
*
* Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
+#include <asm/div64.h>
-#include "dvb_math.h"
-#include "dvb_frontend.h"
+#include <linux/int_log.h>
+#include <media/dvb_frontend.h>
#include "dib7000p.h"
@@ -25,12 +26,11 @@ static int buggy_sfn_workaround;
module_param(buggy_sfn_workaround, int, 0644);
MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (default: 0)");
-#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000P: "); printk(args); printk("\n"); } } while (0)
-
-struct i2c_device {
- struct i2c_adapter *i2c_adap;
- u8 i2c_addr;
-};
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
struct dib7000p_state {
struct dvb_frontend demod;
@@ -72,6 +72,12 @@ struct dib7000p_state {
struct mutex i2c_buffer_lock;
u8 input_mode_mpeg;
+
+ /* for DVBv5 stats */
+ s64 old_ucb;
+ unsigned long per_jiffies_stats;
+ unsigned long ber_jiffies_stats;
+ unsigned long get_stats_time;
};
enum dib7000p_power_mode {
@@ -80,7 +86,7 @@ enum dib7000p_power_mode {
DIB7000P_POWER_INTERFACE_ONLY,
};
-/* dib7090 specific fonctions */
+/* dib7090 specific functions */
static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode);
static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff);
static void dib7090_setDibTxMux(struct dib7000p_state *state, int mode);
@@ -91,7 +97,7 @@ static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg)
u16 ret;
if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
- dprintk("could not acquire lock");
+ dprintk("could not acquire lock\n");
return 0;
}
@@ -109,7 +115,7 @@ static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg)
state->msg[1].len = 2;
if (i2c_transfer(state->i2c_adap, state->msg, 2) != 2)
- dprintk("i2c read error on %d", reg);
+ dprintk("i2c read error on %d\n", reg);
ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
mutex_unlock(&state->i2c_buffer_lock);
@@ -121,7 +127,7 @@ static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val)
int ret;
if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
- dprintk("could not acquire lock");
+ dprintk("could not acquire lock\n");
return -EINVAL;
}
@@ -167,7 +173,7 @@ static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode)
fifo_threshold = 1792;
smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1);
- dprintk("setting output mode for demod %p to %d", &state->demod, mode);
+ dprintk("setting output mode for demod %p to %d\n", &state->demod, mode);
switch (mode) {
case OUTMODE_MPEG2_PAR_GATED_CLK:
@@ -197,7 +203,7 @@ static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode)
outreg = 0;
break;
default:
- dprintk("Unhandled output_mode passed to be set for demod %p", &state->demod);
+ dprintk("Unhandled output_mode passed to be set for demod %p\n", &state->demod);
break;
}
@@ -217,7 +223,7 @@ static int dib7000p_set_diversity_in(struct dvb_frontend *demod, int onoff)
struct dib7000p_state *state = demod->demodulator_priv;
if (state->div_force_off) {
- dprintk("diversity combination deactivated - forced by COFDM parameters");
+ dprintk("diversity combination deactivated - forced by COFDM parameters\n");
onoff = 0;
dib7000p_write_word(state, 207, 0);
} else
@@ -265,10 +271,10 @@ static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_p
if (state->version != SOC7090)
reg_1280 &= ~((1 << 11));
reg_1280 &= ~(1 << 6);
- /* fall through wanted to enable the interfaces */
-
+ fallthrough;
+ case DIB7000P_POWER_INTERFACE_ONLY:
/* just leave power on the control-interfaces: GPIO and (I2C or SDIO) */
- case DIB7000P_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C */
+ /* TODO power up either SDIO or I2C */
if (state->version == SOC7090)
reg_1280 &= ~((1 << 7) | (1 << 5));
else
@@ -305,7 +311,7 @@ static void dib7000p_set_adc_state(struct dib7000p_state *state, enum dibx000_ad
dib7000p_write_word(state, 1925, reg | (1 << 4) | (1 << 2)); /* en_slowAdc = 1 & reset_sladc = 1 */
- reg = dib7000p_read_word(state, 1925); /* read acces to make it works... strange ... */
+ reg = dib7000p_read_word(state, 1925); /* read access to make it works... strange ... */
msleep(200);
dib7000p_write_word(state, 1925, reg & ~(1 << 4)); /* en_slowAdc = 1 & reset_sladc = 0 */
@@ -367,10 +373,10 @@ static int dib7000p_set_bandwidth(struct dib7000p_state *state, u32 bw)
state->current_bandwidth = bw;
if (state->timf == 0) {
- dprintk("using default timf");
+ dprintk("using default timf\n");
timf = state->cfg.bw->timf;
} else {
- dprintk("using updated timf");
+ dprintk("using updated timf\n");
timf = state->timf;
}
@@ -401,7 +407,7 @@ static int dib7000p_sad_calib(struct dib7000p_state *state)
return 0;
}
-int dib7000p_set_wbd_ref(struct dvb_frontend *demod, u16 value)
+static int dib7000p_set_wbd_ref(struct dvb_frontend *demod, u16 value)
{
struct dib7000p_state *state = demod->demodulator_priv;
if (value > 4095)
@@ -409,9 +415,8 @@ int dib7000p_set_wbd_ref(struct dvb_frontend *demod, u16 value)
state->wbd_ref = value;
return dib7000p_write_word(state, 105, (dib7000p_read_word(state, 105) & 0xf000) | value);
}
-EXPORT_SYMBOL(dib7000p_set_wbd_ref);
-int dib7000p_get_agc_values(struct dvb_frontend *fe,
+static int dib7000p_get_agc_values(struct dvb_frontend *fe,
u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd)
{
struct dib7000p_state *state = fe->demodulator_priv;
@@ -427,7 +432,12 @@ int dib7000p_get_agc_values(struct dvb_frontend *fe,
return 0;
}
-EXPORT_SYMBOL(dib7000p_get_agc_values);
+
+static int dib7000p_set_agc1_min(struct dvb_frontend *fe, u16 v)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ return dib7000p_write_word(state, 108, v);
+}
static void dib7000p_reset_pll(struct dib7000p_state *state)
{
@@ -471,7 +481,7 @@ static u32 dib7000p_get_internal_freq(struct dib7000p_state *state)
return internal;
}
-int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw)
+static int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw)
{
struct dib7000p_state *state = fe->demodulator_priv;
u16 reg_1857, reg_1856 = dib7000p_read_word(state, 1856);
@@ -482,8 +492,8 @@ int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config
prediv = reg_1856 & 0x3f;
loopdiv = (reg_1856 >> 6) & 0x3f;
- if ((bw != NULL) && (bw->pll_prediv != prediv || bw->pll_ratio != loopdiv)) {
- dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)", prediv, bw->pll_prediv, loopdiv, bw->pll_ratio);
+ if (loopdiv && bw && (bw->pll_prediv != prediv || bw->pll_ratio != loopdiv)) {
+ dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)\n", prediv, bw->pll_prediv, loopdiv, bw->pll_ratio);
reg_1856 &= 0xf000;
reg_1857 = dib7000p_read_word(state, 1857);
dib7000p_write_word(state, 1857, reg_1857 & ~(1 << 15));
@@ -500,18 +510,17 @@ int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config
dib7000p_write_word(state, 1857, reg_1857 | (1 << 15));
while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1)
- dprintk("Waiting for PLL to lock");
+ dprintk("Waiting for PLL to lock\n");
return 0;
}
return -EIO;
}
-EXPORT_SYMBOL(dib7000p_update_pll);
static int dib7000p_reset_gpio(struct dib7000p_state *st)
{
/* reset the GPIOs */
- dprintk("gpio dir: %x: val: %x, pwm_pos: %x", st->gpio_dir, st->gpio_val, st->cfg.gpio_pwm_pos);
+ dprintk("gpio dir: %x: val: %x, pwm_pos: %x\n", st->gpio_dir, st->gpio_val, st->cfg.gpio_pwm_pos);
dib7000p_write_word(st, 1029, st->gpio_dir);
dib7000p_write_word(st, 1030, st->gpio_val);
@@ -539,12 +548,11 @@ static int dib7000p_cfg_gpio(struct dib7000p_state *st, u8 num, u8 dir, u8 val)
return 0;
}
-int dib7000p_set_gpio(struct dvb_frontend *demod, u8 num, u8 dir, u8 val)
+static int dib7000p_set_gpio(struct dvb_frontend *demod, u8 num, u8 dir, u8 val)
{
struct dib7000p_state *state = demod->demodulator_priv;
return dib7000p_cfg_gpio(state, num, dir, val);
}
-EXPORT_SYMBOL(dib7000p_set_gpio);
static u16 dib7000p_defaults[] = {
// auto search configuration
@@ -629,6 +637,8 @@ static u16 dib7000p_defaults[] = {
0,
};
+static void dib7000p_reset_stats(struct dvb_frontend *fe);
+
static int dib7000p_demod_reset(struct dib7000p_state *state)
{
dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
@@ -658,7 +668,7 @@ static int dib7000p_demod_reset(struct dib7000p_state *state)
dib7000p_reset_pll(state);
if (dib7000p_reset_gpio(state) != 0)
- dprintk("GPIO reset was not successful.");
+ dprintk("GPIO reset was not successful.\n");
if (state->version == SOC7090) {
dib7000p_write_word(state, 899, 0);
@@ -670,7 +680,7 @@ static int dib7000p_demod_reset(struct dib7000p_state *state)
dib7000p_write_word(state, 273, (0<<6) | 30);
}
if (dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) != 0)
- dprintk("OUTPUT_MODE could not be reset.");
+ dprintk("OUTPUT_MODE could not be reset.\n");
dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON);
dib7000p_sad_calib(state);
@@ -748,7 +758,7 @@ static int dib7000p_set_agc_config(struct dib7000p_state *state, u8 band)
}
if (agc == NULL) {
- dprintk("no valid AGC configuration found for band 0x%02x", band);
+ dprintk("no valid AGC configuration found for band 0x%02x\n", band);
return -EINVAL;
}
@@ -765,7 +775,7 @@ static int dib7000p_set_agc_config(struct dib7000p_state *state, u8 band)
dib7000p_write_word(state, 102, (agc->beta_mant << 6) | agc->beta_exp);
/* AGC continued */
- dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d",
+ dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d\n",
state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel);
if (state->wbd_ref != 0)
@@ -787,15 +797,21 @@ static int dib7000p_set_agc_config(struct dib7000p_state *state, u8 band)
return 0;
}
-static void dib7000p_set_dds(struct dib7000p_state *state, s32 offset_khz)
+static int dib7000p_set_dds(struct dib7000p_state *state, s32 offset_khz)
{
u32 internal = dib7000p_get_internal_freq(state);
- s32 unit_khz_dds_val = 67108864 / (internal); /* 2**26 / Fsampling is the unit 1KHz offset */
- u32 abs_offset_khz = ABS(offset_khz);
+ s32 unit_khz_dds_val;
+ u32 abs_offset_khz = abs(offset_khz);
u32 dds = state->cfg.bw->ifreq & 0x1ffffff;
u8 invert = !!(state->cfg.bw->ifreq & (1 << 25));
+ if (internal == 0) {
+ pr_warn("DIB7000P: dib7000p_get_internal_freq returned 0\n");
+ return -1;
+ }
+ /* 2**26 / Fsampling is the unit 1KHz offset */
+ unit_khz_dds_val = 67108864 / (internal);
- dprintk("setting a frequency offset of %dkHz internal freq = %d invert = %d", offset_khz, internal, invert);
+ dprintk("setting a frequency offset of %dkHz internal freq = %d invert = %d\n", offset_khz, internal, invert);
if (offset_khz < 0)
unit_khz_dds_val *= -1;
@@ -810,6 +826,7 @@ static void dib7000p_set_dds(struct dib7000p_state *state, s32 offset_khz)
dib7000p_write_word(state, 21, (u16) (((dds >> 16) & 0x1ff) | (0 << 10) | (invert << 9)));
dib7000p_write_word(state, 22, (u16) (dds & 0xffff));
}
+ return 0;
}
static int dib7000p_agc_startup(struct dvb_frontend *demod)
@@ -821,6 +838,7 @@ static int dib7000p_agc_startup(struct dvb_frontend *demod)
u8 agc_split;
u16 reg;
u32 upd_demod_gain_period = 0x1000;
+ s32 frequency_offset = 0;
switch (state->agc_state) {
case 0:
@@ -841,7 +859,16 @@ static int dib7000p_agc_startup(struct dvb_frontend *demod)
if (dib7000p_set_agc_config(state, BAND_OF_FREQUENCY(ch->frequency / 1000)) != 0)
return -1;
- dib7000p_set_dds(state, 0);
+ if (demod->ops.tuner_ops.get_frequency) {
+ u32 frequency_tuner;
+
+ demod->ops.tuner_ops.get_frequency(demod, &frequency_tuner);
+ frequency_offset = (s32)frequency_tuner / 1000 - ch->frequency / 1000;
+ }
+
+ if (dib7000p_set_dds(state, frequency_offset) < 0)
+ return -1;
+
ret = 7;
(*agc_state)++;
break;
@@ -883,7 +910,7 @@ static int dib7000p_agc_startup(struct dvb_frontend *demod)
dib7000p_restart_agc(state);
- dprintk("SPLIT %p: %hd", demod, agc_split);
+ dprintk("SPLIT %p: %u\n", demod, agc_split);
(*agc_state)++;
ret = 5;
@@ -915,11 +942,11 @@ static void dib7000p_update_timf(struct dib7000p_state *state)
state->timf = timf * 160 / (state->current_bandwidth / 50);
dib7000p_write_word(state, 23, (u16) (timf >> 16));
dib7000p_write_word(state, 24, (u16) (timf & 0xffff));
- dprintk("updated timf_frequency: %d (default: %d)", state->timf, state->cfg.bw->timf);
+ dprintk("updated timf_frequency: %d (default: %d)\n", state->timf, state->cfg.bw->timf);
}
-u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf)
+static u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf)
{
struct dib7000p_state *state = fe->demodulator_priv;
switch (op) {
@@ -935,7 +962,6 @@ u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf)
dib7000p_set_bandwidth(state, state->current_bandwidth);
return state->timf;
}
-EXPORT_SYMBOL(dib7000p_ctrl_timf);
static void dib7000p_set_channel(struct dib7000p_state *state,
struct dtv_frontend_properties *ch, u8 seq)
@@ -1067,7 +1093,7 @@ static void dib7000p_set_channel(struct dib7000p_state *state,
else
state->div_sync_wait = (value * 3) / 2 + state->cfg.diversity_delay;
- /* deactive the possibility of diversity reception if extended interleaver */
+ /* deactivate the possibility of diversity reception if extended interleaver */
state->div_force_off = !1 && ch->transmission_mode != TRANSMISSION_MODE_8K;
dib7000p_set_diversity_in(&state->demod, state->div_state);
@@ -1157,8 +1183,8 @@ static int dib7000p_autosearch_is_irq(struct dvb_frontend *demod)
static void dib7000p_spur_protect(struct dib7000p_state *state, u32 rf_khz, u32 bw)
{
- static s16 notch[] = { 16143, 14402, 12238, 9713, 6902, 3888, 759, -2392 };
- static u8 sine[] = { 0, 2, 3, 5, 6, 8, 9, 11, 13, 14, 16, 17, 19, 20, 22,
+ static const s16 notch[] = { 16143, 14402, 12238, 9713, 6902, 3888, 759, -2392 };
+ static const u8 sine[] = { 0, 2, 3, 5, 6, 8, 9, 11, 13, 14, 16, 17, 19, 20, 22,
24, 25, 27, 28, 30, 31, 33, 34, 36, 38, 39, 41, 42, 44, 45, 47, 48, 50, 51,
53, 55, 56, 58, 59, 61, 62, 64, 65, 67, 68, 70, 71, 73, 74, 76, 77, 79, 80,
82, 83, 85, 86, 88, 89, 91, 92, 94, 95, 97, 98, 99, 101, 102, 104, 105,
@@ -1184,7 +1210,7 @@ static void dib7000p_spur_protect(struct dib7000p_state *state, u32 rf_khz, u32
int bw_khz = bw;
u32 pha;
- dprintk("relative position of the Spur: %dk (RF: %dk, XTAL: %dk)", f_rel, rf_khz, xtal);
+ dprintk("relative position of the Spur: %dk (RF: %dk, XTAL: %dk)\n", f_rel, rf_khz, xtal);
if (f_rel < -bw_khz / 2 || f_rel > bw_khz / 2)
return;
@@ -1234,7 +1260,7 @@ static void dib7000p_spur_protect(struct dib7000p_state *state, u32 rf_khz, u32
coef_im[k] = (1 << 24) - 1;
coef_im[k] /= (1 << 15);
- dprintk("PALF COEF: %d re: %d im: %d", k, coef_re[k], coef_im[k]);
+ dprintk("PALF COEF: %d re: %d im: %d\n", k, coef_re[k], coef_im[k]);
dib7000p_write_word(state, 143, (0 << 14) | (k << 10) | (coef_re[k] & 0x3ff));
dib7000p_write_word(state, 144, coef_im[k] & 0x3ff);
@@ -1262,7 +1288,7 @@ static int dib7000p_tune(struct dvb_frontend *demod)
/* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */
tmp = (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3);
if (state->sfn_workaround_active) {
- dprintk("SFN workaround is active");
+ dprintk("SFN workaround is active\n");
tmp |= (1 << 9);
dib7000p_write_word(state, 166, 0x4000);
} else {
@@ -1345,6 +1371,9 @@ static int dib7000p_tune(struct dvb_frontend *demod)
dib7000p_spur_protect(state, ch->frequency / 1000, BANDWIDTH_TO_KHZ(ch->bandwidth_hz));
dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz));
+
+ dib7000p_reset_stats(demod);
+
return 0;
}
@@ -1369,24 +1398,24 @@ static int dib7000p_sleep(struct dvb_frontend *demod)
static int dib7000p_identify(struct dib7000p_state *st)
{
u16 value;
- dprintk("checking demod on I2C address: %d (%x)", st->i2c_addr, st->i2c_addr);
+ dprintk("checking demod on I2C address: %d (%x)\n", st->i2c_addr, st->i2c_addr);
if ((value = dib7000p_read_word(st, 768)) != 0x01b3) {
- dprintk("wrong Vendor ID (read=0x%x)", value);
+ dprintk("wrong Vendor ID (read=0x%x)\n", value);
return -EREMOTEIO;
}
if ((value = dib7000p_read_word(st, 769)) != 0x4000) {
- dprintk("wrong Device ID (%x)", value);
+ dprintk("wrong Device ID (%x)\n", value);
return -EREMOTEIO;
}
return 0;
}
-static int dib7000p_get_frontend(struct dvb_frontend *fe)
+static int dib7000p_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *fep)
{
- struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
struct dib7000p_state *state = fe->demodulator_priv;
u16 tps = dib7000p_read_word(state, 463);
@@ -1515,11 +1544,11 @@ static int dib7000p_set_frontend(struct dvb_frontend *fe)
found = dib7000p_autosearch_is_irq(fe);
} while (found == 0 && i--);
- dprintk("autosearch returns: %d", found);
+ dprintk("autosearch returns: %d\n", found);
if (found == 0 || found == 1)
return 0;
- dib7000p_get_frontend(fe);
+ dib7000p_get_frontend(fe, fep);
}
ret = dib7000p_tune(fe);
@@ -1537,7 +1566,9 @@ static int dib7000p_set_frontend(struct dvb_frontend *fe)
return ret;
}
-static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t * stat)
+static int dib7000p_get_stats(struct dvb_frontend *fe, enum fe_status stat);
+
+static int dib7000p_read_status(struct dvb_frontend *fe, enum fe_status *stat)
{
struct dib7000p_state *state = fe->demodulator_priv;
u16 lock = dib7000p_read_word(state, 509);
@@ -1555,6 +1586,8 @@ static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t * stat)
if ((lock & 0x0038) == 0x38)
*stat |= FE_HAS_LOCK;
+ dib7000p_get_stats(fe, *stat);
+
return 0;
}
@@ -1580,7 +1613,7 @@ static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 * strength
return 0;
}
-static int dib7000p_read_snr(struct dvb_frontend *fe, u16 * snr)
+static u32 dib7000p_get_snr(struct dvb_frontend *fe)
{
struct dib7000p_state *state = fe->demodulator_priv;
u16 val;
@@ -1610,10 +1643,353 @@ static int dib7000p_read_snr(struct dvb_frontend *fe, u16 * snr)
else
result -= intlog10(2) * 10 * noise_exp - 100;
+ return result;
+}
+
+static int dib7000p_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ u32 result;
+
+ result = dib7000p_get_snr(fe);
+
*snr = result / ((1 << 24) / 10);
return 0;
}
+static void dib7000p_reset_stats(struct dvb_frontend *demod)
+{
+ struct dib7000p_state *state = demod->demodulator_priv;
+ struct dtv_frontend_properties *c = &demod->dtv_property_cache;
+ u32 ucb;
+
+ memset(&c->strength, 0, sizeof(c->strength));
+ memset(&c->cnr, 0, sizeof(c->cnr));
+ memset(&c->post_bit_error, 0, sizeof(c->post_bit_error));
+ memset(&c->post_bit_count, 0, sizeof(c->post_bit_count));
+ memset(&c->block_error, 0, sizeof(c->block_error));
+
+ c->strength.len = 1;
+ c->cnr.len = 1;
+ c->block_error.len = 1;
+ c->block_count.len = 1;
+ c->post_bit_error.len = 1;
+ c->post_bit_count.len = 1;
+
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->strength.stat[0].uvalue = 0;
+
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ dib7000p_read_unc_blocks(demod, &ucb);
+
+ state->old_ucb = ucb;
+ state->ber_jiffies_stats = 0;
+ state->per_jiffies_stats = 0;
+}
+
+struct linear_segments {
+ unsigned x;
+ signed y;
+};
+
+/*
+ * Table to estimate signal strength in dBm.
+ * This table should be empirically determinated by measuring the signal
+ * strength generated by a RF generator directly connected into
+ * a device.
+ * This table was determinated by measuring the signal strength generated
+ * by a DTA-2111 RF generator directly connected into a dib7000p device
+ * (a Hauppauge Nova-TD stick), using a good quality 3 meters length
+ * RC6 cable and good RC6 connectors, connected directly to antenna 1.
+ * As the minimum output power of DTA-2111 is -31dBm, a 16 dBm attenuator
+ * were used, for the lower power values.
+ * The real value can actually be on other devices, or even at the
+ * second antena input, depending on several factors, like if LNA
+ * is enabled or not, if diversity is enabled, type of connectors, etc.
+ * Yet, it is better to use this measure in dB than a random non-linear
+ * percentage value, especially for antenna adjustments.
+ * On my tests, the precision of the measure using this table is about
+ * 0.5 dB, with sounds reasonable enough to adjust antennas.
+ */
+#define DB_OFFSET 131000
+
+static struct linear_segments strength_to_db_table[] = {
+ { 63630, DB_OFFSET - 20500},
+ { 62273, DB_OFFSET - 21000},
+ { 60162, DB_OFFSET - 22000},
+ { 58730, DB_OFFSET - 23000},
+ { 58294, DB_OFFSET - 24000},
+ { 57778, DB_OFFSET - 25000},
+ { 57320, DB_OFFSET - 26000},
+ { 56779, DB_OFFSET - 27000},
+ { 56293, DB_OFFSET - 28000},
+ { 55724, DB_OFFSET - 29000},
+ { 55145, DB_OFFSET - 30000},
+ { 54680, DB_OFFSET - 31000},
+ { 54293, DB_OFFSET - 32000},
+ { 53813, DB_OFFSET - 33000},
+ { 53427, DB_OFFSET - 34000},
+ { 52981, DB_OFFSET - 35000},
+
+ { 52636, DB_OFFSET - 36000},
+ { 52014, DB_OFFSET - 37000},
+ { 51674, DB_OFFSET - 38000},
+ { 50692, DB_OFFSET - 39000},
+ { 49824, DB_OFFSET - 40000},
+ { 49052, DB_OFFSET - 41000},
+ { 48436, DB_OFFSET - 42000},
+ { 47836, DB_OFFSET - 43000},
+ { 47368, DB_OFFSET - 44000},
+ { 46468, DB_OFFSET - 45000},
+ { 45597, DB_OFFSET - 46000},
+ { 44586, DB_OFFSET - 47000},
+ { 43667, DB_OFFSET - 48000},
+ { 42673, DB_OFFSET - 49000},
+ { 41816, DB_OFFSET - 50000},
+ { 40876, DB_OFFSET - 51000},
+ { 0, 0},
+};
+
+static u32 interpolate_value(u32 value, struct linear_segments *segments,
+ unsigned len)
+{
+ u64 tmp64;
+ u32 dx;
+ s32 dy;
+ int i, ret;
+
+ if (value >= segments[0].x)
+ return segments[0].y;
+ if (value < segments[len-1].x)
+ return segments[len-1].y;
+
+ for (i = 1; i < len - 1; i++) {
+ /* If value is identical, no need to interpolate */
+ if (value == segments[i].x)
+ return segments[i].y;
+ if (value > segments[i].x)
+ break;
+ }
+
+ /* Linear interpolation between the two (x,y) points */
+ dy = segments[i - 1].y - segments[i].y;
+ dx = segments[i - 1].x - segments[i].x;
+
+ tmp64 = value - segments[i].x;
+ tmp64 *= dy;
+ do_div(tmp64, dx);
+ ret = segments[i].y + tmp64;
+
+ return ret;
+}
+
+/* FIXME: may require changes - this one was borrowed from dib8000 */
+static u32 dib7000p_get_time_us(struct dvb_frontend *demod)
+{
+ struct dtv_frontend_properties *c = &demod->dtv_property_cache;
+ u64 time_us, tmp64;
+ u32 tmp, denom;
+ int guard, rate_num, rate_denum = 1, bits_per_symbol;
+ int interleaving = 0, fft_div;
+
+ switch (c->guard_interval) {
+ case GUARD_INTERVAL_1_4:
+ guard = 4;
+ break;
+ case GUARD_INTERVAL_1_8:
+ guard = 8;
+ break;
+ case GUARD_INTERVAL_1_16:
+ guard = 16;
+ break;
+ default:
+ case GUARD_INTERVAL_1_32:
+ guard = 32;
+ break;
+ }
+
+ switch (c->transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ fft_div = 4;
+ break;
+ case TRANSMISSION_MODE_4K:
+ fft_div = 2;
+ break;
+ default:
+ case TRANSMISSION_MODE_8K:
+ fft_div = 1;
+ break;
+ }
+
+ switch (c->modulation) {
+ case DQPSK:
+ case QPSK:
+ bits_per_symbol = 2;
+ break;
+ case QAM_16:
+ bits_per_symbol = 4;
+ break;
+ default:
+ case QAM_64:
+ bits_per_symbol = 6;
+ break;
+ }
+
+ switch ((c->hierarchy == 0 || 1 == 1) ? c->code_rate_HP : c->code_rate_LP) {
+ case FEC_1_2:
+ rate_num = 1;
+ rate_denum = 2;
+ break;
+ case FEC_2_3:
+ rate_num = 2;
+ rate_denum = 3;
+ break;
+ case FEC_3_4:
+ rate_num = 3;
+ rate_denum = 4;
+ break;
+ case FEC_5_6:
+ rate_num = 5;
+ rate_denum = 6;
+ break;
+ default:
+ case FEC_7_8:
+ rate_num = 7;
+ rate_denum = 8;
+ break;
+ }
+
+ denom = bits_per_symbol * rate_num * fft_div * 384;
+
+ /*
+ * FIXME: check if the math makes sense. If so, fill the
+ * interleaving var.
+ */
+
+ /* If calculus gets wrong, wait for 1s for the next stats */
+ if (!denom)
+ return 0;
+
+ /* Estimate the period for the total bit rate */
+ time_us = rate_denum * (1008 * 1562500L);
+ tmp64 = time_us;
+ do_div(tmp64, guard);
+ time_us = time_us + tmp64;
+ time_us += denom / 2;
+ do_div(time_us, denom);
+
+ tmp = 1008 * 96 * interleaving;
+ time_us += tmp + tmp / guard;
+
+ return time_us;
+}
+
+static int dib7000p_get_stats(struct dvb_frontend *demod, enum fe_status stat)
+{
+ struct dib7000p_state *state = demod->demodulator_priv;
+ struct dtv_frontend_properties *c = &demod->dtv_property_cache;
+ int show_per_stats = 0;
+ u32 time_us = 0, val, snr;
+ u64 blocks, ucb;
+ s32 db;
+ u16 strength;
+
+ /* Get Signal strength */
+ dib7000p_read_signal_strength(demod, &strength);
+ val = strength;
+ db = interpolate_value(val,
+ strength_to_db_table,
+ ARRAY_SIZE(strength_to_db_table)) - DB_OFFSET;
+ c->strength.stat[0].svalue = db;
+
+ /* UCB/BER/CNR measures require lock */
+ if (!(stat & FE_HAS_LOCK)) {
+ c->cnr.len = 1;
+ c->block_count.len = 1;
+ c->block_error.len = 1;
+ c->post_bit_error.len = 1;
+ c->post_bit_count.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return 0;
+ }
+
+ /* Check if time for stats was elapsed */
+ if (time_after(jiffies, state->per_jiffies_stats)) {
+ state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000);
+
+ /* Get SNR */
+ snr = dib7000p_get_snr(demod);
+ if (snr)
+ snr = (1000L * snr) >> 24;
+ else
+ snr = 0;
+ c->cnr.stat[0].svalue = snr;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+
+ /* Get UCB measures */
+ dib7000p_read_unc_blocks(demod, &val);
+ ucb = val - state->old_ucb;
+ if (val < state->old_ucb)
+ ucb += 0x100000000LL;
+
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue = ucb;
+
+ /* Estimate the number of packets based on bitrate */
+ if (!time_us)
+ time_us = dib7000p_get_time_us(demod);
+
+ if (time_us) {
+ blocks = 1250000ULL * 1000000ULL;
+ do_div(blocks, time_us * 8 * 204);
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue += blocks;
+ }
+
+ show_per_stats = 1;
+ }
+
+ /* Get post-BER measures */
+ if (time_after(jiffies, state->ber_jiffies_stats)) {
+ time_us = dib7000p_get_time_us(demod);
+ state->ber_jiffies_stats = jiffies + msecs_to_jiffies((time_us + 500) / 1000);
+
+ dprintk("Next all layers stats available in %u us.\n", time_us);
+
+ dib7000p_read_ber(demod, &val);
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += val;
+
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue += 100000000;
+ }
+
+ /* Get PER measures */
+ if (show_per_stats) {
+ dib7000p_read_unc_blocks(demod, &val);
+
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue += val;
+
+ time_us = dib7000p_get_time_us(demod);
+ if (time_us) {
+ blocks = 1250000ULL * 1000000ULL;
+ do_div(blocks, time_us * 8 * 204);
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue += blocks;
+ }
+ }
+ return 0;
+}
+
static int dib7000p_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune)
{
tune->min_delay_ms = 1000;
@@ -1628,7 +2004,7 @@ static void dib7000p_release(struct dvb_frontend *demod)
kfree(st);
}
-int dib7000pc_detection(struct i2c_adapter *i2c_adap)
+static int dib7000pc_detection(struct i2c_adapter *i2c_adap)
{
u8 *tx, *rx;
struct i2c_msg msg[2] = {
@@ -1637,10 +2013,10 @@ int dib7000pc_detection(struct i2c_adapter *i2c_adap)
};
int ret = 0;
- tx = kzalloc(2*sizeof(u8), GFP_KERNEL);
+ tx = kzalloc(2, GFP_KERNEL);
if (!tx)
return -ENOMEM;
- rx = kzalloc(2*sizeof(u8), GFP_KERNEL);
+ rx = kzalloc(2, GFP_KERNEL);
if (!rx) {
ret = -ENOMEM;
goto rx_memory_error;
@@ -1654,53 +2030,52 @@ int dib7000pc_detection(struct i2c_adapter *i2c_adap)
if (i2c_transfer(i2c_adap, msg, 2) == 2)
if (rx[0] == 0x01 && rx[1] == 0xb3) {
- dprintk("-D- DiB7000PC detected");
- return 1;
+ dprintk("-D- DiB7000PC detected\n");
+ ret = 1;
+ goto out;
}
msg[0].addr = msg[1].addr = 0x40;
if (i2c_transfer(i2c_adap, msg, 2) == 2)
if (rx[0] == 0x01 && rx[1] == 0xb3) {
- dprintk("-D- DiB7000PC detected");
- return 1;
+ dprintk("-D- DiB7000PC detected\n");
+ ret = 1;
+ goto out;
}
- dprintk("-D- DiB7000PC not detected");
+ dprintk("-D- DiB7000PC not detected\n");
+out:
kfree(rx);
rx_memory_error:
kfree(tx);
return ret;
}
-EXPORT_SYMBOL(dib7000pc_detection);
-struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating)
+static struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating)
{
struct dib7000p_state *st = demod->demodulator_priv;
return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating);
}
-EXPORT_SYMBOL(dib7000p_get_i2c_master);
-int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
+static int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
{
struct dib7000p_state *state = fe->demodulator_priv;
u16 val = dib7000p_read_word(state, 235) & 0xffef;
val |= (onoff & 0x1) << 4;
- dprintk("PID filter enabled %d", onoff);
+ dprintk("PID filter enabled %d\n", onoff);
return dib7000p_write_word(state, 235, val);
}
-EXPORT_SYMBOL(dib7000p_pid_filter_ctrl);
-int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
+static int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
{
struct dib7000p_state *state = fe->demodulator_priv;
- dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff);
+ dprintk("PID filter: index %x, PID %d, OnOff %d\n", id, pid, onoff);
return dib7000p_write_word(state, 241 + id, onoff ? (1 << 13) | pid : 0);
}
-EXPORT_SYMBOL(dib7000p_pid_filter);
-int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[])
+static int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[])
{
struct dib7000p_state *dpst;
int k = 0;
@@ -1739,7 +2114,7 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau
/* set new i2c address and force divstart */
dib7000p_write_word(dpst, 1285, (new_addr << 2) | 0x2);
- dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr);
+ dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr);
}
for (k = 0; k < no_of_demods; k++) {
@@ -1759,7 +2134,6 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau
kfree(dpst);
return 0;
}
-EXPORT_SYMBOL(dib7000p_i2c_enumeration);
static const s32 lut_1000ln_mant[] = {
6908, 6956, 7003, 7047, 7090, 7131, 7170, 7208, 7244, 7279, 7313, 7346, 7377, 7408, 7438, 7467, 7495, 7523, 7549, 7575, 7600
@@ -1776,21 +2150,21 @@ static s32 dib7000p_get_adc_power(struct dvb_frontend *fe)
buf[0] = dib7000p_read_word(state, 0x184);
buf[1] = dib7000p_read_word(state, 0x185);
pow_i = (buf[0] << 16) | buf[1];
- dprintk("raw pow_i = %d", pow_i);
+ dprintk("raw pow_i = %d\n", pow_i);
tmp_val = pow_i;
while (tmp_val >>= 1)
exp++;
mant = (pow_i * 1000 / (1 << exp));
- dprintk(" mant = %d exp = %d", mant / 1000, exp);
+ dprintk(" mant = %d exp = %d\n", mant / 1000, exp);
ix = (u8) ((mant - 1000) / 100); /* index of the LUT */
- dprintk(" ix = %d", ix);
+ dprintk(" ix = %d\n", ix);
pow_i = (lut_1000ln_mant[ix] + 693 * (exp - 20) - 6908);
pow_i = (pow_i << 8) / 1000;
- dprintk(" pow_i = %d", pow_i);
+ dprintk(" pow_i = %d\n", pow_i);
return pow_i;
}
@@ -1819,13 +2193,15 @@ static int w7090p_tuner_write_serpar(struct i2c_adapter *i2c_adap, struct i2c_ms
struct dib7000p_state *state = i2c_get_adapdata(i2c_adap);
u8 n_overflow = 1;
u16 i = 1000;
+ if (msg[0].len < 3)
+ return -EOPNOTSUPP;
u16 serpar_num = msg[0].buf[0];
while (n_overflow == 1 && i) {
n_overflow = (dib7000p_read_word(state, 1984) >> 1) & 0x1;
i--;
if (i == 0)
- dprintk("Tuner ITF: write busy (overflow)");
+ dprintk("Tuner ITF: write busy (overflow)\n");
}
dib7000p_write_word(state, 1985, (1 << 6) | (serpar_num & 0x3f));
dib7000p_write_word(state, 1986, (msg[0].buf[1] << 8) | msg[0].buf[2]);
@@ -1838,6 +2214,8 @@ static int w7090p_tuner_read_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg
struct dib7000p_state *state = i2c_get_adapdata(i2c_adap);
u8 n_overflow = 1, n_empty = 1;
u16 i = 1000;
+ if (msg[0].len < 1 || msg[1].len < 2)
+ return -EOPNOTSUPP;
u16 serpar_num = msg[0].buf[0];
u16 read_word;
@@ -1845,7 +2223,7 @@ static int w7090p_tuner_read_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg
n_overflow = (dib7000p_read_word(state, 1984) >> 1) & 0x1;
i--;
if (i == 0)
- dprintk("TunerITF: read busy (overflow)");
+ dprintk("TunerITF: read busy (overflow)\n");
}
dib7000p_write_word(state, 1985, (0 << 6) | (serpar_num & 0x3f));
@@ -1854,7 +2232,7 @@ static int w7090p_tuner_read_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg
n_empty = dib7000p_read_word(state, 1984) & 0x1;
i--;
if (i == 0)
- dprintk("TunerITF: read busy (empty)");
+ dprintk("TunerITF: read busy (empty)\n");
}
read_word = dib7000p_read_word(state, 1987);
msg[1].buf[0] = (read_word >> 8) & 0xff;
@@ -1882,8 +2260,12 @@ static int dib7090p_rw_on_apb(struct i2c_adapter *i2c_adap,
u16 word;
if (num == 1) { /* write */
+ if (msg[0].len < 3)
+ return -EOPNOTSUPP;
dib7000p_write_word(state, apb_address, ((msg[0].buf[1] << 8) | (msg[0].buf[2])));
} else {
+ if (msg[1].len < 2)
+ return -EOPNOTSUPP;
word = dib7000p_read_word(state, apb_address);
msg[1].buf[0] = (word >> 8) & 0xff;
msg[1].buf[1] = (word) & 0xff;
@@ -1999,7 +2381,7 @@ static int dib7090_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[]
}
}
- if (apb_address != 0) /* R/W acces via APB */
+ if (apb_address != 0) /* R/W access via APB */
return dib7090p_rw_on_apb(i2c_adap, msg, num, apb_address);
else /* R/W access via SERPAR */
return w7090p_tuner_rw_serpar(i2c_adap, msg, num);
@@ -2012,17 +2394,16 @@ static u32 dib7000p_i2c_func(struct i2c_adapter *adapter)
return I2C_FUNC_I2C;
}
-static struct i2c_algorithm dib7090_tuner_xfer_algo = {
+static const struct i2c_algorithm dib7090_tuner_xfer_algo = {
.master_xfer = dib7090_tuner_xfer,
.functionality = dib7000p_i2c_func,
};
-struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe)
+static struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe)
{
struct dib7000p_state *st = fe->demodulator_priv;
return &st->dib7090_tuner_adap;
}
-EXPORT_SYMBOL(dib7090_get_i2c_tuner);
static int dib7090_host_bus_drive(struct dib7000p_state *state, u8 drive)
{
@@ -2076,7 +2457,7 @@ static u32 dib7090_calcSyncFreq(u32 P_Kin, u32 P_Kout, u32 insertExtSynchro, u32
static int dib7090_cfg_DibTx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout, u32 insertExtSynchro, u32 synchroMode, u32 syncWord, u32 syncSize)
{
- dprintk("Configure DibStream Tx");
+ dprintk("Configure DibStream Tx\n");
dib7000p_write_word(state, 1615, 1);
dib7000p_write_word(state, 1603, P_Kin);
@@ -2096,7 +2477,7 @@ static int dib7090_cfg_DibRx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout
{
u32 syncFreq;
- dprintk("Configure DibStream Rx");
+ dprintk("Configure DibStream Rx\n");
if ((P_Kin != 0) && (P_Kout != 0)) {
syncFreq = dib7090_calcSyncFreq(P_Kin, P_Kout, insertExtSynchro, syncSize);
dib7000p_write_word(state, 1542, syncFreq);
@@ -2133,7 +2514,7 @@ static void dib7090_enMpegMux(struct dib7000p_state *state, int onoff)
static void dib7090_configMpegMux(struct dib7000p_state *state,
u16 pulseWidth, u16 enSerialMode, u16 enSerialClkDiv2)
{
- dprintk("Enable Mpeg mux");
+ dprintk("Enable Mpeg mux\n");
dib7090_enMpegMux(state, 0);
@@ -2154,17 +2535,17 @@ static void dib7090_setDibTxMux(struct dib7000p_state *state, int mode)
switch (mode) {
case MPEG_ON_DIBTX:
- dprintk("SET MPEG ON DIBSTREAM TX");
+ dprintk("SET MPEG ON DIBSTREAM TX\n");
dib7090_cfg_DibTx(state, 8, 5, 0, 0, 0, 0);
reg_1288 |= (1<<9);
break;
case DIV_ON_DIBTX:
- dprintk("SET DIV_OUT ON DIBSTREAM TX");
+ dprintk("SET DIV_OUT ON DIBSTREAM TX\n");
dib7090_cfg_DibTx(state, 5, 5, 0, 0, 0, 0);
reg_1288 |= (1<<8);
break;
case ADC_ON_DIBTX:
- dprintk("SET ADC_OUT ON DIBSTREAM TX");
+ dprintk("SET ADC_OUT ON DIBSTREAM TX\n");
dib7090_cfg_DibTx(state, 20, 5, 10, 0, 0, 0);
reg_1288 |= (1<<7);
break;
@@ -2180,17 +2561,17 @@ static void dib7090_setHostBusMux(struct dib7000p_state *state, int mode)
switch (mode) {
case DEMOUT_ON_HOSTBUS:
- dprintk("SET DEM OUT OLD INTERF ON HOST BUS");
+ dprintk("SET DEM OUT OLD INTERF ON HOST BUS\n");
dib7090_enMpegMux(state, 0);
reg_1288 |= (1<<6);
break;
case DIBTX_ON_HOSTBUS:
- dprintk("SET DIBSTREAM TX ON HOST BUS");
+ dprintk("SET DIBSTREAM TX ON HOST BUS\n");
dib7090_enMpegMux(state, 0);
reg_1288 |= (1<<5);
break;
case MPEG_ON_HOSTBUS:
- dprintk("SET MPEG MUX ON HOST BUS");
+ dprintk("SET MPEG MUX ON HOST BUS\n");
reg_1288 |= (1<<4);
break;
default:
@@ -2199,14 +2580,14 @@ static void dib7090_setHostBusMux(struct dib7000p_state *state, int mode)
dib7000p_write_word(state, 1288, reg_1288);
}
-int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff)
+static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff)
{
struct dib7000p_state *state = fe->demodulator_priv;
u16 reg_1287;
switch (onoff) {
case 0: /* only use the internal way - not the diversity input */
- dprintk("%s mode OFF : by default Enable Mpeg INPUT", __func__);
+ dprintk("%s mode OFF : by default Enable Mpeg INPUT\n", __func__);
dib7090_cfg_DibRx(state, 8, 5, 0, 0, 0, 8, 0);
/* Do not divide the serial clock of MPEG MUX */
@@ -2222,7 +2603,7 @@ int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff)
break;
case 1: /* both ways */
case 2: /* only the diversity input */
- dprintk("%s ON : Enable diversity INPUT", __func__);
+ dprintk("%s ON : Enable diversity INPUT\n", __func__);
dib7090_cfg_DibRx(state, 5, 5, 0, 0, 0, 0, 0);
state->input_mode_mpeg = 0;
break;
@@ -2253,11 +2634,11 @@ static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode)
case OUTMODE_MPEG2_SERIAL:
if (prefer_mpeg_mux_use) {
- dprintk("setting output mode TS_SERIAL using Mpeg Mux");
+ dprintk("setting output mode TS_SERIAL using Mpeg Mux\n");
dib7090_configMpegMux(state, 3, 1, 1);
dib7090_setHostBusMux(state, MPEG_ON_HOSTBUS);
} else {/* Use Smooth block */
- dprintk("setting output mode TS_SERIAL using Smooth bloc");
+ dprintk("setting output mode TS_SERIAL using Smooth block\n");
dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS);
outreg |= (2<<6) | (0 << 1);
}
@@ -2265,24 +2646,24 @@ static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode)
case OUTMODE_MPEG2_PAR_GATED_CLK:
if (prefer_mpeg_mux_use) {
- dprintk("setting output mode TS_PARALLEL_GATED using Mpeg Mux");
+ dprintk("setting output mode TS_PARALLEL_GATED using Mpeg Mux\n");
dib7090_configMpegMux(state, 2, 0, 0);
dib7090_setHostBusMux(state, MPEG_ON_HOSTBUS);
} else { /* Use Smooth block */
- dprintk("setting output mode TS_PARALLEL_GATED using Smooth block");
+ dprintk("setting output mode TS_PARALLEL_GATED using Smooth block\n");
dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS);
outreg |= (0<<6);
}
break;
case OUTMODE_MPEG2_PAR_CONT_CLK: /* Using Smooth block only */
- dprintk("setting output mode TS_PARALLEL_CONT using Smooth block");
+ dprintk("setting output mode TS_PARALLEL_CONT using Smooth block\n");
dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS);
outreg |= (1<<6);
break;
- case OUTMODE_MPEG2_FIFO: /* Using Smooth block because not supported by new Mpeg Mux bloc */
- dprintk("setting output mode TS_FIFO using Smooth block");
+ case OUTMODE_MPEG2_FIFO: /* Using Smooth block because not supported by new Mpeg Mux block */
+ dprintk("setting output mode TS_FIFO using Smooth block\n");
dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS);
outreg |= (5<<6);
smo_mode |= (3 << 1);
@@ -2290,13 +2671,13 @@ static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode)
break;
case OUTMODE_DIVERSITY:
- dprintk("setting output mode MODE_DIVERSITY");
+ dprintk("setting output mode MODE_DIVERSITY\n");
dib7090_setDibTxMux(state, DIV_ON_DIBTX);
dib7090_setHostBusMux(state, DIBTX_ON_HOSTBUS);
break;
case OUTMODE_ANALOG_ADC:
- dprintk("setting output mode MODE_ANALOG_ADC");
+ dprintk("setting output mode MODE_ANALOG_ADC\n");
dib7090_setDibTxMux(state, ADC_ON_DIBTX);
dib7090_setHostBusMux(state, DIBTX_ON_HOSTBUS);
break;
@@ -2314,12 +2695,12 @@ static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode)
return ret;
}
-int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff)
+static int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff)
{
struct dib7000p_state *state = fe->demodulator_priv;
u16 en_cur_state;
- dprintk("sleep dib7090: %d", onoff);
+ dprintk("sleep dib7090: %d\n", onoff);
en_cur_state = dib7000p_read_word(state, 1922);
@@ -2337,15 +2718,13 @@ int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff)
return 0;
}
-EXPORT_SYMBOL(dib7090_tuner_sleep);
-int dib7090_get_adc_power(struct dvb_frontend *fe)
+static int dib7090_get_adc_power(struct dvb_frontend *fe)
{
return dib7000p_get_adc_power(fe);
}
-EXPORT_SYMBOL(dib7090_get_adc_power);
-int dib7090_slave_reset(struct dvb_frontend *fe)
+static int dib7090_slave_reset(struct dvb_frontend *fe)
{
struct dib7000p_state *state = fe->demodulator_priv;
u16 reg;
@@ -2356,10 +2735,9 @@ int dib7090_slave_reset(struct dvb_frontend *fe)
dib7000p_write_word(state, 1032, 0xffff);
return 0;
}
-EXPORT_SYMBOL(dib7090_slave_reset);
-static struct dvb_frontend_ops dib7000p_ops;
-struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg)
+static const struct dvb_frontend_ops dib7000p_ops;
+static struct dvb_frontend *dib7000p_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg)
{
struct dvb_frontend *demod;
struct dib7000p_state *st;
@@ -2399,7 +2777,8 @@ struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr);
/* init 7090 tuner adapter */
- strncpy(st->dib7090_tuner_adap.name, "DiB7090 tuner interface", sizeof(st->dib7090_tuner_adap.name));
+ strscpy(st->dib7090_tuner_adap.name, "DiB7090 tuner interface",
+ sizeof(st->dib7090_tuner_adap.name));
st->dib7090_tuner_adap.algo = &dib7090_tuner_xfer_algo;
st->dib7090_tuner_adap.algo_data = NULL;
st->dib7090_tuner_adap.dev.parent = st->i2c_adap->dev.parent;
@@ -2408,6 +2787,8 @@ struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
dib7000p_demod_reset(st);
+ dib7000p_reset_stats(demod);
+
if (st->version == SOC7090) {
dib7090_set_output_mode(demod, st->cfg.output_mode);
dib7090_set_diversity_in(demod, 0);
@@ -2419,15 +2800,40 @@ error:
kfree(st);
return NULL;
}
-EXPORT_SYMBOL(dib7000p_attach);
-static struct dvb_frontend_ops dib7000p_ops = {
+void *dib7000p_attach(struct dib7000p_ops *ops)
+{
+ if (!ops)
+ return NULL;
+
+ ops->slave_reset = dib7090_slave_reset;
+ ops->get_adc_power = dib7090_get_adc_power;
+ ops->dib7000pc_detection = dib7000pc_detection;
+ ops->get_i2c_tuner = dib7090_get_i2c_tuner;
+ ops->tuner_sleep = dib7090_tuner_sleep;
+ ops->init = dib7000p_init;
+ ops->set_agc1_min = dib7000p_set_agc1_min;
+ ops->set_gpio = dib7000p_set_gpio;
+ ops->i2c_enumeration = dib7000p_i2c_enumeration;
+ ops->pid_filter = dib7000p_pid_filter;
+ ops->pid_filter_ctrl = dib7000p_pid_filter_ctrl;
+ ops->get_i2c_master = dib7000p_get_i2c_master;
+ ops->update_pll = dib7000p_update_pll;
+ ops->ctrl_timf = dib7000p_ctrl_timf;
+ ops->get_agc_values = dib7000p_get_agc_values;
+ ops->set_wbd_ref = dib7000p_set_wbd_ref;
+
+ return ops;
+}
+EXPORT_SYMBOL_GPL(dib7000p_attach);
+
+static const struct dvb_frontend_ops dib7000p_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = "DiBcom 7000PC",
- .frequency_min = 44250000,
- .frequency_max = 867250000,
- .frequency_stepsize = 62500,
+ .frequency_min_hz = 44250 * kHz,
+ .frequency_max_hz = 867250 * kHz,
+ .frequency_stepsize_hz = 62500,
.caps = FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
@@ -2451,7 +2857,7 @@ static struct dvb_frontend_ops dib7000p_ops = {
.read_ucblocks = dib7000p_read_unc_blocks,
};
-MODULE_AUTHOR("Olivier Grenie <ogrenie@dibcom.fr>");
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_AUTHOR("Olivier Grenie <olivie.grenie@parrot.com>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Driver for the DiBcom 7000PC COFDM demodulator");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/dib7000p.h b/drivers/media/dvb-frontends/dib7000p.h
new file mode 100644
index 000000000000..2e10b5ccce67
--- /dev/null
+++ b/drivers/media/dvb-frontends/dib7000p.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef DIB7000P_H
+#define DIB7000P_H
+
+#include "dibx000_common.h"
+
+struct dib7000p_config {
+ u8 output_mpeg2_in_188_bytes;
+ u8 hostbus_diversity;
+ u8 tuner_is_baseband;
+ int (*update_lna) (struct dvb_frontend *, u16 agc_global);
+
+ u8 agc_config_count;
+ struct dibx000_agc_config *agc;
+ struct dibx000_bandwidth_config *bw;
+
+#define DIB7000P_GPIO_DEFAULT_DIRECTIONS 0xffff
+ u16 gpio_dir;
+#define DIB7000P_GPIO_DEFAULT_VALUES 0x0000
+ u16 gpio_val;
+#define DIB7000P_GPIO_PWM_POS0(v) ((v & 0xf) << 12)
+#define DIB7000P_GPIO_PWM_POS1(v) ((v & 0xf) << 8 )
+#define DIB7000P_GPIO_PWM_POS2(v) ((v & 0xf) << 4 )
+#define DIB7000P_GPIO_PWM_POS3(v) (v & 0xf)
+#define DIB7000P_GPIO_DEFAULT_PWM_POS 0xffff
+ u16 gpio_pwm_pos;
+
+ u16 pwm_freq_div;
+
+ u8 quartz_direct;
+
+ u8 spur_protect;
+
+ int (*agc_control) (struct dvb_frontend *, u8 before);
+
+ u8 output_mode;
+ u8 disable_sample_and_hold:1;
+
+ u8 enable_current_mirror:1;
+ u16 diversity_delay;
+
+ u8 default_i2c_addr;
+ u8 enMpegOutput:1;
+};
+
+#define DEFAULT_DIB7000P_I2C_ADDRESS 18
+
+struct dib7000p_ops {
+ int (*set_wbd_ref)(struct dvb_frontend *demod, u16 value);
+ int (*get_agc_values)(struct dvb_frontend *fe,
+ u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd);
+ int (*set_agc1_min)(struct dvb_frontend *fe, u16 v);
+ int (*update_pll)(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw);
+ int (*set_gpio)(struct dvb_frontend *demod, u8 num, u8 dir, u8 val);
+ u32 (*ctrl_timf)(struct dvb_frontend *fe, u8 op, u32 timf);
+ int (*dib7000pc_detection)(struct i2c_adapter *i2c_adap);
+ struct i2c_adapter *(*get_i2c_master)(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating);
+ int (*pid_filter_ctrl)(struct dvb_frontend *fe, u8 onoff);
+ int (*pid_filter)(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff);
+ int (*i2c_enumeration)(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]);
+ struct i2c_adapter *(*get_i2c_tuner)(struct dvb_frontend *fe);
+ int (*tuner_sleep)(struct dvb_frontend *fe, int onoff);
+ int (*get_adc_power)(struct dvb_frontend *fe);
+ int (*slave_reset)(struct dvb_frontend *fe);
+ struct dvb_frontend *(*init)(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg);
+};
+
+#if IS_REACHABLE(CONFIG_DVB_DIB7000P)
+void *dib7000p_attach(struct dib7000p_ops *ops);
+#else
+static inline void *dib7000p_attach(struct dib7000p_ops *ops)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c
new file mode 100644
index 000000000000..d90f1b0b2051
--- /dev/null
+++ b/drivers/media/dvb-frontends/dib8000.c
@@ -0,0 +1,4538 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Linux-DVB Driver for DiBcom's DiB8000 chip (ISDB-T).
+ *
+ * Copyright (C) 2009 DiBcom (http://www.dibcom.fr/)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <asm/div64.h>
+
+#include <linux/int_log.h>
+
+#include <media/dvb_frontend.h>
+
+#include "dib8000.h"
+
+#define LAYER_ALL -1
+#define LAYER_A 1
+#define LAYER_B 2
+#define LAYER_C 3
+
+#define MAX_NUMBER_OF_FRONTENDS 6
+/* #define DIB8000_AGC_FREEZE */
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
+
+struct i2c_device {
+ struct i2c_adapter *adap;
+ u8 addr;
+ u8 *i2c_write_buffer;
+ u8 *i2c_read_buffer;
+ struct mutex *i2c_buffer_lock;
+};
+
+enum param_loop_step {
+ LOOP_TUNE_1,
+ LOOP_TUNE_2
+};
+
+enum dib8000_autosearch_step {
+ AS_START = 0,
+ AS_SEARCHING_FFT,
+ AS_SEARCHING_GUARD,
+ AS_DONE = 100,
+};
+
+enum timeout_mode {
+ SYMBOL_DEPENDENT_OFF = 0,
+ SYMBOL_DEPENDENT_ON,
+};
+
+struct dib8000_state {
+ struct dib8000_config cfg;
+
+ struct i2c_device i2c;
+
+ struct dibx000_i2c_master i2c_master;
+
+ u16 wbd_ref;
+
+ u8 current_band;
+ u32 current_bandwidth;
+ struct dibx000_agc_config *current_agc;
+ u32 timf;
+ u32 timf_default;
+
+ u8 div_force_off:1;
+ u8 div_state:1;
+ u16 div_sync_wait;
+
+ u8 agc_state;
+ u8 differential_constellation;
+ u8 diversity_onoff;
+
+ s16 ber_monitored_layer;
+ u16 gpio_dir;
+ u16 gpio_val;
+
+ u16 revision;
+ u8 isdbt_cfg_loaded;
+ enum frontend_tune_state tune_state;
+ s32 status;
+
+ struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS];
+
+ /* for the I2C transfer */
+ struct i2c_msg msg[2];
+ u8 i2c_write_buffer[4];
+ u8 i2c_read_buffer[2];
+ struct mutex i2c_buffer_lock;
+ u8 input_mode_mpeg;
+
+ u16 tuner_enable;
+ struct i2c_adapter dib8096p_tuner_adap;
+ u16 current_demod_bw;
+
+ u16 seg_mask;
+ u16 seg_diff_mask;
+ u16 mode;
+ u8 layer_b_nb_seg;
+ u8 layer_c_nb_seg;
+
+ u8 channel_parameters_set;
+ u16 autosearch_state;
+ u16 found_nfft;
+ u16 found_guard;
+ u8 subchannel;
+ u8 symbol_duration;
+ unsigned long timeout;
+ u8 longest_intlv_layer;
+ u16 output_mode;
+
+ /* for DVBv5 stats */
+ s64 init_ucb;
+ unsigned long per_jiffies_stats;
+ unsigned long ber_jiffies_stats;
+ unsigned long ber_jiffies_stats_layer[3];
+
+#ifdef DIB8000_AGC_FREEZE
+ u16 agc1_max;
+ u16 agc1_min;
+ u16 agc2_max;
+ u16 agc2_min;
+#endif
+};
+
+enum dib8000_power_mode {
+ DIB8000_POWER_ALL = 0,
+ DIB8000_POWER_INTERFACE_ONLY,
+};
+
+static u16 dib8000_i2c_read16(struct i2c_device *i2c, u16 reg)
+{
+ u16 ret;
+ struct i2c_msg msg[2] = {
+ {.addr = i2c->addr >> 1, .flags = 0, .len = 2},
+ {.addr = i2c->addr >> 1, .flags = I2C_M_RD, .len = 2},
+ };
+
+ if (mutex_lock_interruptible(i2c->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock\n");
+ return 0;
+ }
+
+ msg[0].buf = i2c->i2c_write_buffer;
+ msg[0].buf[0] = reg >> 8;
+ msg[0].buf[1] = reg & 0xff;
+ msg[1].buf = i2c->i2c_read_buffer;
+
+ if (i2c_transfer(i2c->adap, msg, 2) != 2)
+ dprintk("i2c read error on %d\n", reg);
+
+ ret = (msg[1].buf[0] << 8) | msg[1].buf[1];
+ mutex_unlock(i2c->i2c_buffer_lock);
+ return ret;
+}
+
+static u16 __dib8000_read_word(struct dib8000_state *state, u16 reg)
+{
+ u16 ret;
+
+ state->i2c_write_buffer[0] = reg >> 8;
+ state->i2c_write_buffer[1] = reg & 0xff;
+
+ memset(state->msg, 0, 2 * sizeof(struct i2c_msg));
+ state->msg[0].addr = state->i2c.addr >> 1;
+ state->msg[0].flags = 0;
+ state->msg[0].buf = state->i2c_write_buffer;
+ state->msg[0].len = 2;
+ state->msg[1].addr = state->i2c.addr >> 1;
+ state->msg[1].flags = I2C_M_RD;
+ state->msg[1].buf = state->i2c_read_buffer;
+ state->msg[1].len = 2;
+
+ if (i2c_transfer(state->i2c.adap, state->msg, 2) != 2)
+ dprintk("i2c read error on %d\n", reg);
+
+ ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+
+ return ret;
+}
+
+static u16 dib8000_read_word(struct dib8000_state *state, u16 reg)
+{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock\n");
+ return 0;
+ }
+
+ ret = __dib8000_read_word(state, reg);
+
+ mutex_unlock(&state->i2c_buffer_lock);
+
+ return ret;
+}
+
+static u32 dib8000_read32(struct dib8000_state *state, u16 reg)
+{
+ u16 rw[2];
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock\n");
+ return 0;
+ }
+
+ rw[0] = __dib8000_read_word(state, reg + 0);
+ rw[1] = __dib8000_read_word(state, reg + 1);
+
+ mutex_unlock(&state->i2c_buffer_lock);
+
+ return ((rw[0] << 16) | (rw[1]));
+}
+
+static int dib8000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val)
+{
+ struct i2c_msg msg = {.addr = i2c->addr >> 1, .flags = 0, .len = 4};
+ int ret = 0;
+
+ if (mutex_lock_interruptible(i2c->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock\n");
+ return -EINVAL;
+ }
+
+ msg.buf = i2c->i2c_write_buffer;
+ msg.buf[0] = (reg >> 8) & 0xff;
+ msg.buf[1] = reg & 0xff;
+ msg.buf[2] = (val >> 8) & 0xff;
+ msg.buf[3] = val & 0xff;
+
+ ret = i2c_transfer(i2c->adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+ mutex_unlock(i2c->i2c_buffer_lock);
+
+ return ret;
+}
+
+static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val)
+{
+ int ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock\n");
+ return -EINVAL;
+ }
+
+ state->i2c_write_buffer[0] = (reg >> 8) & 0xff;
+ state->i2c_write_buffer[1] = reg & 0xff;
+ state->i2c_write_buffer[2] = (val >> 8) & 0xff;
+ state->i2c_write_buffer[3] = val & 0xff;
+
+ memset(&state->msg[0], 0, sizeof(struct i2c_msg));
+ state->msg[0].addr = state->i2c.addr >> 1;
+ state->msg[0].flags = 0;
+ state->msg[0].buf = state->i2c_write_buffer;
+ state->msg[0].len = 4;
+
+ ret = (i2c_transfer(state->i2c.adap, state->msg, 1) != 1 ?
+ -EREMOTEIO : 0);
+ mutex_unlock(&state->i2c_buffer_lock);
+
+ return ret;
+}
+
+static const s16 coeff_2k_sb_1seg_dqpsk[8] = {
+ (769 << 5) | 0x0a, (745 << 5) | 0x03, (595 << 5) | 0x0d, (769 << 5) | 0x0a, (920 << 5) | 0x09, (784 << 5) | 0x02, (519 << 5) | 0x0c,
+ (920 << 5) | 0x09
+};
+
+static const s16 coeff_2k_sb_1seg[8] = {
+ (692 << 5) | 0x0b, (683 << 5) | 0x01, (519 << 5) | 0x09, (692 << 5) | 0x0b, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f
+};
+
+static const s16 coeff_2k_sb_3seg_0dqpsk_1dqpsk[8] = {
+ (832 << 5) | 0x10, (912 << 5) | 0x05, (900 << 5) | 0x12, (832 << 5) | 0x10, (-931 << 5) | 0x0f, (912 << 5) | 0x04, (807 << 5) | 0x11,
+ (-931 << 5) | 0x0f
+};
+
+static const s16 coeff_2k_sb_3seg_0dqpsk[8] = {
+ (622 << 5) | 0x0c, (941 << 5) | 0x04, (796 << 5) | 0x10, (622 << 5) | 0x0c, (982 << 5) | 0x0c, (519 << 5) | 0x02, (572 << 5) | 0x0e,
+ (982 << 5) | 0x0c
+};
+
+static const s16 coeff_2k_sb_3seg_1dqpsk[8] = {
+ (699 << 5) | 0x14, (607 << 5) | 0x04, (944 << 5) | 0x13, (699 << 5) | 0x14, (-720 << 5) | 0x0d, (640 << 5) | 0x03, (866 << 5) | 0x12,
+ (-720 << 5) | 0x0d
+};
+
+static const s16 coeff_2k_sb_3seg[8] = {
+ (664 << 5) | 0x0c, (925 << 5) | 0x03, (937 << 5) | 0x10, (664 << 5) | 0x0c, (-610 << 5) | 0x0a, (697 << 5) | 0x01, (836 << 5) | 0x0e,
+ (-610 << 5) | 0x0a
+};
+
+static const s16 coeff_4k_sb_1seg_dqpsk[8] = {
+ (-955 << 5) | 0x0e, (687 << 5) | 0x04, (818 << 5) | 0x10, (-955 << 5) | 0x0e, (-922 << 5) | 0x0d, (750 << 5) | 0x03, (665 << 5) | 0x0f,
+ (-922 << 5) | 0x0d
+};
+
+static const s16 coeff_4k_sb_1seg[8] = {
+ (638 << 5) | 0x0d, (683 << 5) | 0x02, (638 << 5) | 0x0d, (638 << 5) | 0x0d, (-655 << 5) | 0x0a, (517 << 5) | 0x00, (698 << 5) | 0x0d,
+ (-655 << 5) | 0x0a
+};
+
+static const s16 coeff_4k_sb_3seg_0dqpsk_1dqpsk[8] = {
+ (-707 << 5) | 0x14, (910 << 5) | 0x06, (889 << 5) | 0x16, (-707 << 5) | 0x14, (-958 << 5) | 0x13, (993 << 5) | 0x05, (523 << 5) | 0x14,
+ (-958 << 5) | 0x13
+};
+
+static const s16 coeff_4k_sb_3seg_0dqpsk[8] = {
+ (-723 << 5) | 0x13, (910 << 5) | 0x05, (777 << 5) | 0x14, (-723 << 5) | 0x13, (-568 << 5) | 0x0f, (547 << 5) | 0x03, (696 << 5) | 0x12,
+ (-568 << 5) | 0x0f
+};
+
+static const s16 coeff_4k_sb_3seg_1dqpsk[8] = {
+ (-940 << 5) | 0x15, (607 << 5) | 0x05, (915 << 5) | 0x16, (-940 << 5) | 0x15, (-848 << 5) | 0x13, (683 << 5) | 0x04, (543 << 5) | 0x14,
+ (-848 << 5) | 0x13
+};
+
+static const s16 coeff_4k_sb_3seg[8] = {
+ (612 << 5) | 0x12, (910 << 5) | 0x04, (864 << 5) | 0x14, (612 << 5) | 0x12, (-869 << 5) | 0x13, (683 << 5) | 0x02, (869 << 5) | 0x12,
+ (-869 << 5) | 0x13
+};
+
+static const s16 coeff_8k_sb_1seg_dqpsk[8] = {
+ (-835 << 5) | 0x12, (684 << 5) | 0x05, (735 << 5) | 0x14, (-835 << 5) | 0x12, (-598 << 5) | 0x10, (781 << 5) | 0x04, (739 << 5) | 0x13,
+ (-598 << 5) | 0x10
+};
+
+static const s16 coeff_8k_sb_1seg[8] = {
+ (673 << 5) | 0x0f, (683 << 5) | 0x03, (808 << 5) | 0x12, (673 << 5) | 0x0f, (585 << 5) | 0x0f, (512 << 5) | 0x01, (780 << 5) | 0x0f,
+ (585 << 5) | 0x0f
+};
+
+static const s16 coeff_8k_sb_3seg_0dqpsk_1dqpsk[8] = {
+ (863 << 5) | 0x17, (930 << 5) | 0x07, (878 << 5) | 0x19, (863 << 5) | 0x17, (0 << 5) | 0x14, (521 << 5) | 0x05, (980 << 5) | 0x18,
+ (0 << 5) | 0x14
+};
+
+static const s16 coeff_8k_sb_3seg_0dqpsk[8] = {
+ (-924 << 5) | 0x17, (910 << 5) | 0x06, (774 << 5) | 0x17, (-924 << 5) | 0x17, (-877 << 5) | 0x15, (565 << 5) | 0x04, (553 << 5) | 0x15,
+ (-877 << 5) | 0x15
+};
+
+static const s16 coeff_8k_sb_3seg_1dqpsk[8] = {
+ (-921 << 5) | 0x19, (607 << 5) | 0x06, (881 << 5) | 0x19, (-921 << 5) | 0x19, (-921 << 5) | 0x14, (713 << 5) | 0x05, (1018 << 5) | 0x18,
+ (-921 << 5) | 0x14
+};
+
+static const s16 coeff_8k_sb_3seg[8] = {
+ (514 << 5) | 0x14, (910 << 5) | 0x05, (861 << 5) | 0x17, (514 << 5) | 0x14, (690 << 5) | 0x14, (683 << 5) | 0x03, (662 << 5) | 0x15,
+ (690 << 5) | 0x14
+};
+
+static const s16 ana_fe_coeff_3seg[24] = {
+ 81, 80, 78, 74, 68, 61, 54, 45, 37, 28, 19, 11, 4, 1022, 1017, 1013, 1010, 1008, 1008, 1008, 1008, 1010, 1014, 1017
+};
+
+static const s16 ana_fe_coeff_1seg[24] = {
+ 249, 226, 164, 82, 5, 981, 970, 988, 1018, 20, 31, 26, 8, 1012, 1000, 1018, 1012, 8, 15, 14, 9, 3, 1017, 1003
+};
+
+static const s16 ana_fe_coeff_13seg[24] = {
+ 396, 305, 105, -51, -77, -12, 41, 31, -11, -30, -11, 14, 15, -2, -13, -7, 5, 8, 1, -6, -7, -3, 0, 1
+};
+
+static u16 fft_to_mode(struct dib8000_state *state)
+{
+ u16 mode;
+ switch (state->fe[0]->dtv_property_cache.transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ mode = 1;
+ break;
+ case TRANSMISSION_MODE_4K:
+ mode = 2;
+ break;
+ default:
+ case TRANSMISSION_MODE_AUTO:
+ case TRANSMISSION_MODE_8K:
+ mode = 3;
+ break;
+ }
+ return mode;
+}
+
+static void dib8000_set_acquisition_mode(struct dib8000_state *state)
+{
+ u16 nud = dib8000_read_word(state, 298);
+ nud |= (1 << 3) | (1 << 0);
+ dprintk("acquisition mode activated\n");
+ dib8000_write_word(state, 298, nud);
+}
+static int dib8000_set_output_mode(struct dvb_frontend *fe, int mode)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u16 outreg, fifo_threshold, smo_mode, sram = 0x0205; /* by default SDRAM deintlv is enabled */
+
+ state->output_mode = mode;
+ outreg = 0;
+ fifo_threshold = 1792;
+ smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1);
+
+ dprintk("-I- Setting output mode for demod %p to %d\n",
+ &state->fe[0], mode);
+
+ switch (mode) {
+ case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock
+ outreg = (1 << 10); /* 0x0400 */
+ break;
+ case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock
+ outreg = (1 << 10) | (1 << 6); /* 0x0440 */
+ break;
+ case OUTMODE_MPEG2_SERIAL: // STBs with serial input
+ outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */
+ break;
+ case OUTMODE_DIVERSITY:
+ if (state->cfg.hostbus_diversity) {
+ outreg = (1 << 10) | (4 << 6); /* 0x0500 */
+ sram &= 0xfdff;
+ } else
+ sram |= 0x0c00;
+ break;
+ case OUTMODE_MPEG2_FIFO: // e.g. USB feeding
+ smo_mode |= (3 << 1);
+ fifo_threshold = 512;
+ outreg = (1 << 10) | (5 << 6);
+ break;
+ case OUTMODE_HIGH_Z: // disable
+ outreg = 0;
+ break;
+
+ case OUTMODE_ANALOG_ADC:
+ outreg = (1 << 10) | (3 << 6);
+ dib8000_set_acquisition_mode(state);
+ break;
+
+ default:
+ dprintk("Unhandled output_mode passed to be set for demod %p\n",
+ &state->fe[0]);
+ return -EINVAL;
+ }
+
+ if (state->cfg.output_mpeg2_in_188_bytes)
+ smo_mode |= (1 << 5);
+
+ dib8000_write_word(state, 299, smo_mode);
+ dib8000_write_word(state, 300, fifo_threshold); /* synchronous fread */
+ dib8000_write_word(state, 1286, outreg);
+ dib8000_write_word(state, 1291, sram);
+
+ return 0;
+}
+
+static int dib8000_set_diversity_in(struct dvb_frontend *fe, int onoff)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u16 tmp, sync_wait = dib8000_read_word(state, 273) & 0xfff0;
+
+ dprintk("set diversity input to %i\n", onoff);
+ if (!state->differential_constellation) {
+ dib8000_write_word(state, 272, 1 << 9); //dvsy_off_lmod4 = 1
+ dib8000_write_word(state, 273, sync_wait | (1 << 2) | 2); // sync_enable = 1; comb_mode = 2
+ } else {
+ dib8000_write_word(state, 272, 0); //dvsy_off_lmod4 = 0
+ dib8000_write_word(state, 273, sync_wait); // sync_enable = 0; comb_mode = 0
+ }
+ state->diversity_onoff = onoff;
+
+ switch (onoff) {
+ case 0: /* only use the internal way - not the diversity input */
+ dib8000_write_word(state, 270, 1);
+ dib8000_write_word(state, 271, 0);
+ break;
+ case 1: /* both ways */
+ dib8000_write_word(state, 270, 6);
+ dib8000_write_word(state, 271, 6);
+ break;
+ case 2: /* only the diversity input */
+ dib8000_write_word(state, 270, 0);
+ dib8000_write_word(state, 271, 1);
+ break;
+ }
+
+ if (state->revision == 0x8002) {
+ tmp = dib8000_read_word(state, 903);
+ dib8000_write_word(state, 903, tmp & ~(1 << 3));
+ msleep(30);
+ dib8000_write_word(state, 903, tmp | (1 << 3));
+ }
+ return 0;
+}
+
+static void dib8000_set_power_mode(struct dib8000_state *state, enum dib8000_power_mode mode)
+{
+ /* by default everything is going to be powered off */
+ u16 reg_774 = 0x3fff, reg_775 = 0xffff, reg_776 = 0xffff,
+ reg_900 = (dib8000_read_word(state, 900) & 0xfffc) | 0x3,
+ reg_1280;
+
+ if (state->revision != 0x8090)
+ reg_1280 = (dib8000_read_word(state, 1280) & 0x00ff) | 0xff00;
+ else
+ reg_1280 = (dib8000_read_word(state, 1280) & 0x707f) | 0x8f80;
+
+ /* now, depending on the requested mode, we power on */
+ switch (mode) {
+ /* power up everything in the demod */
+ case DIB8000_POWER_ALL:
+ reg_774 = 0x0000;
+ reg_775 = 0x0000;
+ reg_776 = 0x0000;
+ reg_900 &= 0xfffc;
+ if (state->revision != 0x8090)
+ reg_1280 &= 0x00ff;
+ else
+ reg_1280 &= 0x707f;
+ break;
+ case DIB8000_POWER_INTERFACE_ONLY:
+ if (state->revision != 0x8090)
+ reg_1280 &= 0x00ff;
+ else
+ reg_1280 &= 0xfa7b;
+ break;
+ }
+
+ dprintk("powermode : 774 : %x ; 775 : %x; 776 : %x ; 900 : %x; 1280 : %x\n", reg_774, reg_775, reg_776, reg_900, reg_1280);
+ dib8000_write_word(state, 774, reg_774);
+ dib8000_write_word(state, 775, reg_775);
+ dib8000_write_word(state, 776, reg_776);
+ dib8000_write_word(state, 900, reg_900);
+ dib8000_write_word(state, 1280, reg_1280);
+}
+
+static int dib8000_set_adc_state(struct dib8000_state *state, enum dibx000_adc_states no)
+{
+ int ret = 0;
+ u16 reg, reg_907 = dib8000_read_word(state, 907);
+ u16 reg_908 = dib8000_read_word(state, 908);
+
+ switch (no) {
+ case DIBX000_SLOW_ADC_ON:
+ if (state->revision != 0x8090) {
+ reg_908 |= (1 << 1) | (1 << 0);
+ ret |= dib8000_write_word(state, 908, reg_908);
+ reg_908 &= ~(1 << 1);
+ } else {
+ reg = dib8000_read_word(state, 1925);
+ /* en_slowAdc = 1 & reset_sladc = 1 */
+ dib8000_write_word(state, 1925, reg |
+ (1<<4) | (1<<2));
+
+ /* read access to make it works... strange ... */
+ reg = dib8000_read_word(state, 1925);
+ msleep(20);
+ /* en_slowAdc = 1 & reset_sladc = 0 */
+ dib8000_write_word(state, 1925, reg & ~(1<<4));
+
+ reg = dib8000_read_word(state, 921) & ~((0x3 << 14)
+ | (0x3 << 12));
+ /* ref = Vin1 => Vbg ; sel = Vin0 or Vin3 ;
+ (Vin2 = Vcm) */
+ dib8000_write_word(state, 921, reg | (1 << 14)
+ | (3 << 12));
+ }
+ break;
+
+ case DIBX000_SLOW_ADC_OFF:
+ if (state->revision == 0x8090) {
+ reg = dib8000_read_word(state, 1925);
+ /* reset_sladc = 1 en_slowAdc = 0 */
+ dib8000_write_word(state, 1925,
+ (reg & ~(1<<2)) | (1<<4));
+ }
+ reg_908 |= (1 << 1) | (1 << 0);
+ break;
+
+ case DIBX000_ADC_ON:
+ reg_907 &= 0x0fff;
+ reg_908 &= 0x0003;
+ break;
+
+ case DIBX000_ADC_OFF: // leave the VBG voltage on
+ reg_907 = (1 << 13) | (1 << 12);
+ reg_908 = (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 1);
+ break;
+
+ case DIBX000_VBG_ENABLE:
+ reg_907 &= ~(1 << 15);
+ break;
+
+ case DIBX000_VBG_DISABLE:
+ reg_907 |= (1 << 15);
+ break;
+
+ default:
+ break;
+ }
+
+ ret |= dib8000_write_word(state, 907, reg_907);
+ ret |= dib8000_write_word(state, 908, reg_908);
+
+ return ret;
+}
+
+static int dib8000_set_bandwidth(struct dvb_frontend *fe, u32 bw)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u32 timf;
+
+ if (bw == 0)
+ bw = 6000;
+
+ if (state->timf == 0) {
+ dprintk("using default timf\n");
+ timf = state->timf_default;
+ } else {
+ dprintk("using updated timf\n");
+ timf = state->timf;
+ }
+
+ dib8000_write_word(state, 29, (u16) ((timf >> 16) & 0xffff));
+ dib8000_write_word(state, 30, (u16) ((timf) & 0xffff));
+
+ return 0;
+}
+
+static int dib8000_sad_calib(struct dib8000_state *state)
+{
+ u8 sad_sel = 3;
+
+ if (state->revision == 0x8090) {
+ dib8000_write_word(state, 922, (sad_sel << 2));
+ dib8000_write_word(state, 923, 2048);
+
+ dib8000_write_word(state, 922, (sad_sel << 2) | 0x1);
+ dib8000_write_word(state, 922, (sad_sel << 2));
+ } else {
+ /* internal */
+ dib8000_write_word(state, 923, (0 << 1) | (0 << 0));
+ dib8000_write_word(state, 924, 776);
+
+ /* do the calibration */
+ dib8000_write_word(state, 923, (1 << 0));
+ dib8000_write_word(state, 923, (0 << 0));
+ }
+
+ msleep(1);
+ return 0;
+}
+
+static int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ if (value > 4095)
+ value = 4095;
+ state->wbd_ref = value;
+ return dib8000_write_word(state, 106, value);
+}
+
+static void dib8000_reset_pll_common(struct dib8000_state *state, const struct dibx000_bandwidth_config *bw)
+{
+ dprintk("ifreq: %d %x, inversion: %d\n", bw->ifreq, bw->ifreq, bw->ifreq >> 25);
+ if (state->revision != 0x8090) {
+ dib8000_write_word(state, 23,
+ (u16) (((bw->internal * 1000) >> 16) & 0xffff));
+ dib8000_write_word(state, 24,
+ (u16) ((bw->internal * 1000) & 0xffff));
+ } else {
+ dib8000_write_word(state, 23, (u16) (((bw->internal / 2 * 1000) >> 16) & 0xffff));
+ dib8000_write_word(state, 24,
+ (u16) ((bw->internal / 2 * 1000) & 0xffff));
+ }
+ dib8000_write_word(state, 27, (u16) ((bw->ifreq >> 16) & 0x01ff));
+ dib8000_write_word(state, 28, (u16) (bw->ifreq & 0xffff));
+ dib8000_write_word(state, 26, (u16) ((bw->ifreq >> 25) & 0x0003));
+
+ if (state->revision != 0x8090)
+ dib8000_write_word(state, 922, bw->sad_cfg);
+}
+
+static void dib8000_reset_pll(struct dib8000_state *state)
+{
+ const struct dibx000_bandwidth_config *pll = state->cfg.pll;
+ u16 clk_cfg1, reg;
+
+ if (state->revision != 0x8090) {
+ dib8000_write_word(state, 901,
+ (pll->pll_prediv << 8) | (pll->pll_ratio << 0));
+
+ clk_cfg1 = (1 << 10) | (0 << 9) | (pll->IO_CLK_en_core << 8) |
+ (pll->bypclk_div << 5) | (pll->enable_refdiv << 4) |
+ (1 << 3) | (pll->pll_range << 1) |
+ (pll->pll_reset << 0);
+
+ dib8000_write_word(state, 902, clk_cfg1);
+ clk_cfg1 = (clk_cfg1 & 0xfff7) | (pll->pll_bypass << 3);
+ dib8000_write_word(state, 902, clk_cfg1);
+
+ dprintk("clk_cfg1: 0x%04x\n", clk_cfg1);
+
+ /* smpl_cfg: P_refclksel=2, P_ensmplsel=1 nodivsmpl=1 */
+ if (state->cfg.pll->ADClkSrc == 0)
+ dib8000_write_word(state, 904,
+ (0 << 15) | (0 << 12) | (0 << 10) |
+ (pll->modulo << 8) |
+ (pll->ADClkSrc << 7) | (0 << 1));
+ else if (state->cfg.refclksel != 0)
+ dib8000_write_word(state, 904, (0 << 15) | (1 << 12) |
+ ((state->cfg.refclksel & 0x3) << 10) |
+ (pll->modulo << 8) |
+ (pll->ADClkSrc << 7) | (0 << 1));
+ else
+ dib8000_write_word(state, 904, (0 << 15) | (1 << 12) |
+ (3 << 10) | (pll->modulo << 8) |
+ (pll->ADClkSrc << 7) | (0 << 1));
+ } else {
+ dib8000_write_word(state, 1856, (!pll->pll_reset<<13) |
+ (pll->pll_range<<12) | (pll->pll_ratio<<6) |
+ (pll->pll_prediv));
+
+ reg = dib8000_read_word(state, 1857);
+ dib8000_write_word(state, 1857, reg|(!pll->pll_bypass<<15));
+
+ reg = dib8000_read_word(state, 1858); /* Force clk out pll /2 */
+ dib8000_write_word(state, 1858, reg | 1);
+
+ dib8000_write_word(state, 904, (pll->modulo << 8));
+ }
+
+ dib8000_reset_pll_common(state, pll);
+}
+
+static int dib8000_update_pll(struct dvb_frontend *fe,
+ struct dibx000_bandwidth_config *pll, u32 bw, u8 ratio)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u16 reg_1857, reg_1856 = dib8000_read_word(state, 1856);
+ u8 loopdiv, prediv, oldprediv = state->cfg.pll->pll_prediv ;
+ u32 internal, xtal;
+
+ /* get back old values */
+ prediv = reg_1856 & 0x3f;
+ loopdiv = (reg_1856 >> 6) & 0x3f;
+
+ if ((pll == NULL) || (pll->pll_prediv == prediv &&
+ pll->pll_ratio == loopdiv))
+ return -EINVAL;
+
+ dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)\n", prediv, pll->pll_prediv, loopdiv, pll->pll_ratio);
+ if (state->revision == 0x8090) {
+ reg_1856 &= 0xf000;
+ reg_1857 = dib8000_read_word(state, 1857);
+ /* disable PLL */
+ dib8000_write_word(state, 1857, reg_1857 & ~(1 << 15));
+
+ dib8000_write_word(state, 1856, reg_1856 |
+ ((pll->pll_ratio & 0x3f) << 6) |
+ (pll->pll_prediv & 0x3f));
+
+ /* write new system clk into P_sec_len */
+ internal = dib8000_read32(state, 23) / 1000;
+ dprintk("Old Internal = %d\n", internal);
+ xtal = 2 * (internal / loopdiv) * prediv;
+ internal = 1000 * (xtal/pll->pll_prediv) * pll->pll_ratio;
+ dprintk("Xtal = %d , New Fmem = %d New Fdemod = %d, New Fsampling = %d\n", xtal, internal/1000, internal/2000, internal/8000);
+ dprintk("New Internal = %d\n", internal);
+
+ dib8000_write_word(state, 23,
+ (u16) (((internal / 2) >> 16) & 0xffff));
+ dib8000_write_word(state, 24, (u16) ((internal / 2) & 0xffff));
+ /* enable PLL */
+ dib8000_write_word(state, 1857, reg_1857 | (1 << 15));
+
+ while (((dib8000_read_word(state, 1856)>>15)&0x1) != 1)
+ dprintk("Waiting for PLL to lock\n");
+
+ /* verify */
+ reg_1856 = dib8000_read_word(state, 1856);
+ dprintk("PLL Updated with prediv = %d and loopdiv = %d\n",
+ reg_1856&0x3f, (reg_1856>>6)&0x3f);
+ } else {
+ if (bw != state->current_demod_bw) {
+ /** Bandwidth change => force PLL update **/
+ dprintk("PLL: Bandwidth Change %d MHz -> %d MHz (prediv: %d->%d)\n", state->current_demod_bw / 1000, bw / 1000, oldprediv, state->cfg.pll->pll_prediv);
+
+ if (state->cfg.pll->pll_prediv != oldprediv) {
+ /** Full PLL change only if prediv is changed **/
+
+ /** full update => bypass and reconfigure **/
+ dprintk("PLL: New Setting for %d MHz Bandwidth (prediv: %d, ratio: %d)\n", bw/1000, state->cfg.pll->pll_prediv, state->cfg.pll->pll_ratio);
+ dib8000_write_word(state, 902, dib8000_read_word(state, 902) | (1<<3)); /* bypass PLL */
+ dib8000_reset_pll(state);
+ dib8000_write_word(state, 898, 0x0004); /* sad */
+ } else
+ ratio = state->cfg.pll->pll_ratio;
+
+ state->current_demod_bw = bw;
+ }
+
+ if (ratio != 0) {
+ /** ratio update => only change ratio **/
+ dprintk("PLL: Update ratio (prediv: %d, ratio: %d)\n", state->cfg.pll->pll_prediv, ratio);
+ dib8000_write_word(state, 901, (state->cfg.pll->pll_prediv << 8) | (ratio << 0)); /* only the PLL ratio is updated. */
+ }
+ }
+
+ return 0;
+}
+
+static int dib8000_reset_gpio(struct dib8000_state *st)
+{
+ /* reset the GPIOs */
+ dib8000_write_word(st, 1029, st->cfg.gpio_dir);
+ dib8000_write_word(st, 1030, st->cfg.gpio_val);
+
+ /* TODO 782 is P_gpio_od */
+
+ dib8000_write_word(st, 1032, st->cfg.gpio_pwm_pos);
+
+ dib8000_write_word(st, 1037, st->cfg.pwm_freq_div);
+ return 0;
+}
+
+static int dib8000_cfg_gpio(struct dib8000_state *st, u8 num, u8 dir, u8 val)
+{
+ st->cfg.gpio_dir = dib8000_read_word(st, 1029);
+ st->cfg.gpio_dir &= ~(1 << num); /* reset the direction bit */
+ st->cfg.gpio_dir |= (dir & 0x1) << num; /* set the new direction */
+ dib8000_write_word(st, 1029, st->cfg.gpio_dir);
+
+ st->cfg.gpio_val = dib8000_read_word(st, 1030);
+ st->cfg.gpio_val &= ~(1 << num); /* reset the direction bit */
+ st->cfg.gpio_val |= (val & 0x01) << num; /* set the new value */
+ dib8000_write_word(st, 1030, st->cfg.gpio_val);
+
+ dprintk("gpio dir: %x: gpio val: %x\n", st->cfg.gpio_dir, st->cfg.gpio_val);
+
+ return 0;
+}
+
+static int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ return dib8000_cfg_gpio(state, num, dir, val);
+}
+
+static const u16 dib8000_defaults[] = {
+ /* auto search configuration - lock0 by default waiting
+ * for cpil_lock; lock1 cpil_lock; lock2 tmcc_sync_lock */
+ 3, 7,
+ 0x0004,
+ 0x0400,
+ 0x0814,
+
+ 12, 11,
+ 0x001b,
+ 0x7740,
+ 0x005b,
+ 0x8d80,
+ 0x01c9,
+ 0xc380,
+ 0x0000,
+ 0x0080,
+ 0x0000,
+ 0x0090,
+ 0x0001,
+ 0xd4c0,
+
+ /*1, 32,
+ 0x6680 // P_corm_thres Lock algorithms configuration */
+
+ 11, 80, /* set ADC level to -16 */
+ (1 << 13) - 825 - 117,
+ (1 << 13) - 837 - 117,
+ (1 << 13) - 811 - 117,
+ (1 << 13) - 766 - 117,
+ (1 << 13) - 737 - 117,
+ (1 << 13) - 693 - 117,
+ (1 << 13) - 648 - 117,
+ (1 << 13) - 619 - 117,
+ (1 << 13) - 575 - 117,
+ (1 << 13) - 531 - 117,
+ (1 << 13) - 501 - 117,
+
+ 4, 108,
+ 0,
+ 0,
+ 0,
+ 0,
+
+ 1, 175,
+ 0x0410,
+ 1, 179,
+ 8192, // P_fft_nb_to_cut
+
+ 6, 181,
+ 0x2800, // P_coff_corthres_ ( 2k 4k 8k ) 0x2800
+ 0x2800,
+ 0x2800,
+ 0x2800, // P_coff_cpilthres_ ( 2k 4k 8k ) 0x2800
+ 0x2800,
+ 0x2800,
+
+ 2, 193,
+ 0x0666, // P_pha3_thres
+ 0x0000, // P_cti_use_cpe, P_cti_use_prog
+
+ 2, 205,
+ 0x200f, // P_cspu_regul, P_cspu_win_cut
+ 0x000f, // P_des_shift_work
+
+ 5, 215,
+ 0x023d, // P_adp_regul_cnt
+ 0x00a4, // P_adp_noise_cnt
+ 0x00a4, // P_adp_regul_ext
+ 0x7ff0, // P_adp_noise_ext
+ 0x3ccc, // P_adp_fil
+
+ 1, 230,
+ 0x0000, // P_2d_byp_ti_num
+
+ 1, 263,
+ 0x800, //P_equal_thres_wgn
+
+ 1, 268,
+ (2 << 9) | 39, // P_equal_ctrl_synchro, P_equal_speedmode
+
+ 1, 270,
+ 0x0001, // P_div_lock0_wait
+ 1, 285,
+ 0x0020, //p_fec_
+ 1, 299,
+ 0x0062, /* P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard */
+
+ 1, 338,
+ (1 << 12) | // P_ctrl_corm_thres4pre_freq_inh=1
+ (1 << 10) |
+ (0 << 9) | /* P_ctrl_pre_freq_inh=0 */
+ (3 << 5) | /* P_ctrl_pre_freq_step=3 */
+ (1 << 0), /* P_pre_freq_win_len=1 */
+
+ 0,
+};
+
+static u16 dib8000_identify(struct i2c_device *client)
+{
+ u16 value;
+
+ //because of glitches sometimes
+ value = dib8000_i2c_read16(client, 896);
+
+ if ((value = dib8000_i2c_read16(client, 896)) != 0x01b3) {
+ dprintk("wrong Vendor ID (read=0x%x)\n", value);
+ return 0;
+ }
+
+ value = dib8000_i2c_read16(client, 897);
+ if (value != 0x8000 && value != 0x8001 &&
+ value != 0x8002 && value != 0x8090) {
+ dprintk("wrong Device ID (%x)\n", value);
+ return 0;
+ }
+
+ switch (value) {
+ case 0x8000:
+ dprintk("found DiB8000A\n");
+ break;
+ case 0x8001:
+ dprintk("found DiB8000B\n");
+ break;
+ case 0x8002:
+ dprintk("found DiB8000C\n");
+ break;
+ case 0x8090:
+ dprintk("found DiB8096P\n");
+ break;
+ }
+ return value;
+}
+
+static int dib8000_read_unc_blocks(struct dvb_frontend *fe, u32 *unc);
+
+static void dib8000_reset_stats(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ u32 ucb;
+
+ memset(&c->strength, 0, sizeof(c->strength));
+ memset(&c->cnr, 0, sizeof(c->cnr));
+ memset(&c->post_bit_error, 0, sizeof(c->post_bit_error));
+ memset(&c->post_bit_count, 0, sizeof(c->post_bit_count));
+ memset(&c->block_error, 0, sizeof(c->block_error));
+
+ c->strength.len = 1;
+ c->cnr.len = 1;
+ c->block_error.len = 1;
+ c->block_count.len = 1;
+ c->post_bit_error.len = 1;
+ c->post_bit_count.len = 1;
+
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->strength.stat[0].uvalue = 0;
+
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ dib8000_read_unc_blocks(fe, &ucb);
+
+ state->init_ucb = -ucb;
+ state->ber_jiffies_stats = 0;
+ state->per_jiffies_stats = 0;
+ memset(&state->ber_jiffies_stats_layer, 0,
+ sizeof(state->ber_jiffies_stats_layer));
+}
+
+static int dib8000_reset(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+
+ if ((state->revision = dib8000_identify(&state->i2c)) == 0)
+ return -EINVAL;
+
+ /* sram lead in, rdy */
+ if (state->revision != 0x8090)
+ dib8000_write_word(state, 1287, 0x0003);
+
+ if (state->revision == 0x8000)
+ dprintk("error : dib8000 MA not supported\n");
+
+ dibx000_reset_i2c_master(&state->i2c_master);
+
+ dib8000_set_power_mode(state, DIB8000_POWER_ALL);
+
+ /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */
+ dib8000_set_adc_state(state, DIBX000_ADC_OFF);
+
+ /* restart all parts */
+ dib8000_write_word(state, 770, 0xffff);
+ dib8000_write_word(state, 771, 0xffff);
+ dib8000_write_word(state, 772, 0xfffc);
+ dib8000_write_word(state, 898, 0x000c); /* restart sad */
+ if (state->revision == 0x8090)
+ dib8000_write_word(state, 1280, 0x0045);
+ else
+ dib8000_write_word(state, 1280, 0x004d);
+ dib8000_write_word(state, 1281, 0x000c);
+
+ dib8000_write_word(state, 770, 0x0000);
+ dib8000_write_word(state, 771, 0x0000);
+ dib8000_write_word(state, 772, 0x0000);
+ dib8000_write_word(state, 898, 0x0004); // sad
+ dib8000_write_word(state, 1280, 0x0000);
+ dib8000_write_word(state, 1281, 0x0000);
+
+ /* drives */
+ if (state->revision != 0x8090) {
+ if (state->cfg.drives)
+ dib8000_write_word(state, 906, state->cfg.drives);
+ else {
+ dprintk("using standard PAD-drive-settings, please adjust settings in config-struct to be optimal.\n");
+ /* min drive SDRAM - not optimal - adjust */
+ dib8000_write_word(state, 906, 0x2d98);
+ }
+ }
+
+ dib8000_reset_pll(state);
+ if (state->revision != 0x8090)
+ dib8000_write_word(state, 898, 0x0004);
+
+ if (dib8000_reset_gpio(state) != 0)
+ dprintk("GPIO reset was not successful.\n");
+
+ if ((state->revision != 0x8090) &&
+ (dib8000_set_output_mode(fe, OUTMODE_HIGH_Z) != 0))
+ dprintk("OUTPUT_MODE could not be reset.\n");
+
+ state->current_agc = NULL;
+
+ // P_iqc_alpha_pha, P_iqc_alpha_amp, P_iqc_dcc_alpha, ...
+ /* P_iqc_ca2 = 0; P_iqc_impnc_on = 0; P_iqc_mode = 0; */
+ if (state->cfg.pll->ifreq == 0)
+ dib8000_write_word(state, 40, 0x0755); /* P_iqc_corr_inh = 0 enable IQcorr block */
+ else
+ dib8000_write_word(state, 40, 0x1f55); /* P_iqc_corr_inh = 1 disable IQcorr block */
+
+ {
+ u16 l = 0, r;
+ const u16 *n;
+ n = dib8000_defaults;
+ l = *n++;
+ while (l) {
+ r = *n++;
+ do {
+ dib8000_write_word(state, r, *n++);
+ r++;
+ } while (--l);
+ l = *n++;
+ }
+ }
+
+ state->isdbt_cfg_loaded = 0;
+
+ //div_cfg override for special configs
+ if ((state->revision != 8090) && (state->cfg.div_cfg != 0))
+ dib8000_write_word(state, 903, state->cfg.div_cfg);
+
+ /* unforce divstr regardless whether i2c enumeration was done or not */
+ dib8000_write_word(state, 1285, dib8000_read_word(state, 1285) & ~(1 << 1));
+
+ dib8000_set_bandwidth(fe, 6000);
+
+ dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON);
+ dib8000_sad_calib(state);
+ if (state->revision != 0x8090)
+ dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF);
+
+ /* ber_rs_len = 3 */
+ dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & ~0x60) | (3 << 5));
+
+ dib8000_set_power_mode(state, DIB8000_POWER_INTERFACE_ONLY);
+
+ dib8000_reset_stats(fe);
+
+ return 0;
+}
+
+static void dib8000_restart_agc(struct dib8000_state *state)
+{
+ // P_restart_iqc & P_restart_agc
+ dib8000_write_word(state, 770, 0x0a00);
+ dib8000_write_word(state, 770, 0x0000);
+}
+
+static int dib8000_update_lna(struct dib8000_state *state)
+{
+ u16 dyn_gain;
+
+ if (state->cfg.update_lna) {
+ // read dyn_gain here (because it is demod-dependent and not tuner)
+ dyn_gain = dib8000_read_word(state, 390);
+
+ if (state->cfg.update_lna(state->fe[0], dyn_gain)) {
+ dib8000_restart_agc(state);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int dib8000_set_agc_config(struct dib8000_state *state, u8 band)
+{
+ struct dibx000_agc_config *agc = NULL;
+ int i;
+ u16 reg;
+
+ if (state->current_band == band && state->current_agc != NULL)
+ return 0;
+ state->current_band = band;
+
+ for (i = 0; i < state->cfg.agc_config_count; i++)
+ if (state->cfg.agc[i].band_caps & band) {
+ agc = &state->cfg.agc[i];
+ break;
+ }
+
+ if (agc == NULL) {
+ dprintk("no valid AGC configuration found for band 0x%02x\n", band);
+ return -EINVAL;
+ }
+
+ state->current_agc = agc;
+
+ /* AGC */
+ dib8000_write_word(state, 76, agc->setup);
+ dib8000_write_word(state, 77, agc->inv_gain);
+ dib8000_write_word(state, 78, agc->time_stabiliz);
+ dib8000_write_word(state, 101, (agc->alpha_level << 12) | agc->thlock);
+
+ // Demod AGC loop configuration
+ dib8000_write_word(state, 102, (agc->alpha_mant << 5) | agc->alpha_exp);
+ dib8000_write_word(state, 103, (agc->beta_mant << 6) | agc->beta_exp);
+
+ dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d\n",
+ state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel);
+
+ /* AGC continued */
+ if (state->wbd_ref != 0)
+ dib8000_write_word(state, 106, state->wbd_ref);
+ else // use default
+ dib8000_write_word(state, 106, agc->wbd_ref);
+
+ if (state->revision == 0x8090) {
+ reg = dib8000_read_word(state, 922) & (0x3 << 2);
+ dib8000_write_word(state, 922, reg | (agc->wbd_sel << 2));
+ }
+
+ dib8000_write_word(state, 107, (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8));
+ dib8000_write_word(state, 108, agc->agc1_max);
+ dib8000_write_word(state, 109, agc->agc1_min);
+ dib8000_write_word(state, 110, agc->agc2_max);
+ dib8000_write_word(state, 111, agc->agc2_min);
+ dib8000_write_word(state, 112, (agc->agc1_pt1 << 8) | agc->agc1_pt2);
+ dib8000_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2);
+ dib8000_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
+ dib8000_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2);
+
+ dib8000_write_word(state, 75, agc->agc1_pt3);
+ if (state->revision != 0x8090)
+ dib8000_write_word(state, 923,
+ (dib8000_read_word(state, 923) & 0xffe3) |
+ (agc->wbd_inv << 4) | (agc->wbd_sel << 2));
+
+ return 0;
+}
+
+static void dib8000_pwm_agc_reset(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ dib8000_set_adc_state(state, DIBX000_ADC_ON);
+ dib8000_set_agc_config(state, (unsigned char)(BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000)));
+}
+
+static int dib8000_agc_soft_split(struct dib8000_state *state)
+{
+ u16 agc, split_offset;
+
+ if (!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0)
+ return 0;
+
+ // n_agc_global
+ agc = dib8000_read_word(state, 390);
+
+ if (agc > state->current_agc->split.min_thres)
+ split_offset = state->current_agc->split.min;
+ else if (agc < state->current_agc->split.max_thres)
+ split_offset = state->current_agc->split.max;
+ else
+ split_offset = state->current_agc->split.max *
+ (agc - state->current_agc->split.min_thres) /
+ (state->current_agc->split.max_thres - state->current_agc->split.min_thres);
+
+ dprintk("AGC split_offset: %d\n", split_offset);
+
+ // P_agc_force_split and P_agc_split_offset
+ dib8000_write_word(state, 107, (dib8000_read_word(state, 107) & 0xff00) | split_offset);
+ return 5000;
+}
+
+static int dib8000_agc_startup(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ enum frontend_tune_state *tune_state = &state->tune_state;
+ int ret = 0;
+ u16 reg;
+ u32 upd_demod_gain_period = 0x8000;
+
+ switch (*tune_state) {
+ case CT_AGC_START:
+ // set power-up level: interf+analog+AGC
+
+ if (state->revision != 0x8090)
+ dib8000_set_adc_state(state, DIBX000_ADC_ON);
+ else {
+ dib8000_set_power_mode(state, DIB8000_POWER_ALL);
+
+ reg = dib8000_read_word(state, 1947)&0xff00;
+ dib8000_write_word(state, 1946,
+ upd_demod_gain_period & 0xFFFF);
+ /* bit 14 = enDemodGain */
+ dib8000_write_word(state, 1947, reg | (1<<14) |
+ ((upd_demod_gain_period >> 16) & 0xFF));
+
+ /* enable adc i & q */
+ reg = dib8000_read_word(state, 1920);
+ dib8000_write_word(state, 1920, (reg | 0x3) &
+ (~(1 << 7)));
+ }
+
+ if (dib8000_set_agc_config(state, (unsigned char)(BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000))) != 0) {
+ *tune_state = CT_AGC_STOP;
+ state->status = FE_STATUS_TUNE_FAILED;
+ break;
+ }
+
+ ret = 70;
+ *tune_state = CT_AGC_STEP_0;
+ break;
+
+ case CT_AGC_STEP_0:
+ //AGC initialization
+ if (state->cfg.agc_control)
+ state->cfg.agc_control(fe, 1);
+
+ dib8000_restart_agc(state);
+
+ // wait AGC rough lock time
+ ret = 50;
+ *tune_state = CT_AGC_STEP_1;
+ break;
+
+ case CT_AGC_STEP_1:
+ // wait AGC accurate lock time
+ ret = 70;
+
+ if (dib8000_update_lna(state))
+ // wait only AGC rough lock time
+ ret = 50;
+ else
+ *tune_state = CT_AGC_STEP_2;
+ break;
+
+ case CT_AGC_STEP_2:
+ dib8000_agc_soft_split(state);
+
+ if (state->cfg.agc_control)
+ state->cfg.agc_control(fe, 0);
+
+ *tune_state = CT_AGC_STOP;
+ break;
+ default:
+ ret = dib8000_agc_soft_split(state);
+ break;
+ }
+ return ret;
+
+}
+
+static void dib8096p_host_bus_drive(struct dib8000_state *state, u8 drive)
+{
+ u16 reg;
+
+ drive &= 0x7;
+
+ /* drive host bus 2, 3, 4 */
+ reg = dib8000_read_word(state, 1798) &
+ ~(0x7 | (0x7 << 6) | (0x7 << 12));
+ reg |= (drive<<12) | (drive<<6) | drive;
+ dib8000_write_word(state, 1798, reg);
+
+ /* drive host bus 5,6 */
+ reg = dib8000_read_word(state, 1799) & ~((0x7 << 2) | (0x7 << 8));
+ reg |= (drive<<8) | (drive<<2);
+ dib8000_write_word(state, 1799, reg);
+
+ /* drive host bus 7, 8, 9 */
+ reg = dib8000_read_word(state, 1800) &
+ ~(0x7 | (0x7 << 6) | (0x7 << 12));
+ reg |= (drive<<12) | (drive<<6) | drive;
+ dib8000_write_word(state, 1800, reg);
+
+ /* drive host bus 10, 11 */
+ reg = dib8000_read_word(state, 1801) & ~((0x7 << 2) | (0x7 << 8));
+ reg |= (drive<<8) | (drive<<2);
+ dib8000_write_word(state, 1801, reg);
+
+ /* drive host bus 12, 13, 14 */
+ reg = dib8000_read_word(state, 1802) &
+ ~(0x7 | (0x7 << 6) | (0x7 << 12));
+ reg |= (drive<<12) | (drive<<6) | drive;
+ dib8000_write_word(state, 1802, reg);
+}
+
+static u32 dib8096p_calcSyncFreq(u32 P_Kin, u32 P_Kout,
+ u32 insertExtSynchro, u32 syncSize)
+{
+ u32 quantif = 3;
+ u32 nom = (insertExtSynchro * P_Kin+syncSize);
+ u32 denom = P_Kout;
+ u32 syncFreq = ((nom << quantif) / denom);
+
+ if ((syncFreq & ((1 << quantif) - 1)) != 0)
+ syncFreq = (syncFreq >> quantif) + 1;
+ else
+ syncFreq = (syncFreq >> quantif);
+
+ if (syncFreq != 0)
+ syncFreq = syncFreq - 1;
+
+ return syncFreq;
+}
+
+static void dib8096p_cfg_DibTx(struct dib8000_state *state, u32 P_Kin,
+ u32 P_Kout, u32 insertExtSynchro, u32 synchroMode,
+ u32 syncWord, u32 syncSize)
+{
+ dprintk("Configure DibStream Tx\n");
+
+ dib8000_write_word(state, 1615, 1);
+ dib8000_write_word(state, 1603, P_Kin);
+ dib8000_write_word(state, 1605, P_Kout);
+ dib8000_write_word(state, 1606, insertExtSynchro);
+ dib8000_write_word(state, 1608, synchroMode);
+ dib8000_write_word(state, 1609, (syncWord >> 16) & 0xffff);
+ dib8000_write_word(state, 1610, syncWord & 0xffff);
+ dib8000_write_word(state, 1612, syncSize);
+ dib8000_write_word(state, 1615, 0);
+}
+
+static void dib8096p_cfg_DibRx(struct dib8000_state *state, u32 P_Kin,
+ u32 P_Kout, u32 synchroMode, u32 insertExtSynchro,
+ u32 syncWord, u32 syncSize, u32 dataOutRate)
+{
+ u32 syncFreq;
+
+ dprintk("Configure DibStream Rx synchroMode = %d\n", synchroMode);
+
+ if ((P_Kin != 0) && (P_Kout != 0)) {
+ syncFreq = dib8096p_calcSyncFreq(P_Kin, P_Kout,
+ insertExtSynchro, syncSize);
+ dib8000_write_word(state, 1542, syncFreq);
+ }
+
+ dib8000_write_word(state, 1554, 1);
+ dib8000_write_word(state, 1536, P_Kin);
+ dib8000_write_word(state, 1537, P_Kout);
+ dib8000_write_word(state, 1539, synchroMode);
+ dib8000_write_word(state, 1540, (syncWord >> 16) & 0xffff);
+ dib8000_write_word(state, 1541, syncWord & 0xffff);
+ dib8000_write_word(state, 1543, syncSize);
+ dib8000_write_word(state, 1544, dataOutRate);
+ dib8000_write_word(state, 1554, 0);
+}
+
+static void dib8096p_enMpegMux(struct dib8000_state *state, int onoff)
+{
+ u16 reg_1287;
+
+ reg_1287 = dib8000_read_word(state, 1287);
+
+ switch (onoff) {
+ case 1:
+ reg_1287 &= ~(1 << 8);
+ break;
+ case 0:
+ reg_1287 |= (1 << 8);
+ break;
+ }
+
+ dib8000_write_word(state, 1287, reg_1287);
+}
+
+static void dib8096p_configMpegMux(struct dib8000_state *state,
+ u16 pulseWidth, u16 enSerialMode, u16 enSerialClkDiv2)
+{
+ u16 reg_1287;
+
+ dprintk("Enable Mpeg mux\n");
+
+ dib8096p_enMpegMux(state, 0);
+
+ /* If the input mode is MPEG do not divide the serial clock */
+ if ((enSerialMode == 1) && (state->input_mode_mpeg == 1))
+ enSerialClkDiv2 = 0;
+
+ reg_1287 = ((pulseWidth & 0x1f) << 3) |
+ ((enSerialMode & 0x1) << 2) | (enSerialClkDiv2 & 0x1);
+ dib8000_write_word(state, 1287, reg_1287);
+
+ dib8096p_enMpegMux(state, 1);
+}
+
+static void dib8096p_setDibTxMux(struct dib8000_state *state, int mode)
+{
+ u16 reg_1288 = dib8000_read_word(state, 1288) & ~(0x7 << 7);
+
+ switch (mode) {
+ case MPEG_ON_DIBTX:
+ dprintk("SET MPEG ON DIBSTREAM TX\n");
+ dib8096p_cfg_DibTx(state, 8, 5, 0, 0, 0, 0);
+ reg_1288 |= (1 << 9); break;
+ case DIV_ON_DIBTX:
+ dprintk("SET DIV_OUT ON DIBSTREAM TX\n");
+ dib8096p_cfg_DibTx(state, 5, 5, 0, 0, 0, 0);
+ reg_1288 |= (1 << 8); break;
+ case ADC_ON_DIBTX:
+ dprintk("SET ADC_OUT ON DIBSTREAM TX\n");
+ dib8096p_cfg_DibTx(state, 20, 5, 10, 0, 0, 0);
+ reg_1288 |= (1 << 7); break;
+ default:
+ break;
+ }
+ dib8000_write_word(state, 1288, reg_1288);
+}
+
+static void dib8096p_setHostBusMux(struct dib8000_state *state, int mode)
+{
+ u16 reg_1288 = dib8000_read_word(state, 1288) & ~(0x7 << 4);
+
+ switch (mode) {
+ case DEMOUT_ON_HOSTBUS:
+ dprintk("SET DEM OUT OLD INTERF ON HOST BUS\n");
+ dib8096p_enMpegMux(state, 0);
+ reg_1288 |= (1 << 6);
+ break;
+ case DIBTX_ON_HOSTBUS:
+ dprintk("SET DIBSTREAM TX ON HOST BUS\n");
+ dib8096p_enMpegMux(state, 0);
+ reg_1288 |= (1 << 5);
+ break;
+ case MPEG_ON_HOSTBUS:
+ dprintk("SET MPEG MUX ON HOST BUS\n");
+ reg_1288 |= (1 << 4);
+ break;
+ default:
+ break;
+ }
+ dib8000_write_word(state, 1288, reg_1288);
+}
+
+static int dib8096p_set_diversity_in(struct dvb_frontend *fe, int onoff)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u16 reg_1287;
+
+ switch (onoff) {
+ case 0: /* only use the internal way - not the diversity input */
+ dprintk("%s mode OFF : by default Enable Mpeg INPUT\n",
+ __func__);
+ /* outputRate = 8 */
+ dib8096p_cfg_DibRx(state, 8, 5, 0, 0, 0, 8, 0);
+
+ /* Do not divide the serial clock of MPEG MUX in
+ SERIAL MODE in case input mode MPEG is used */
+ reg_1287 = dib8000_read_word(state, 1287);
+ /* enSerialClkDiv2 == 1 ? */
+ if ((reg_1287 & 0x1) == 1) {
+ /* force enSerialClkDiv2 = 0 */
+ reg_1287 &= ~0x1;
+ dib8000_write_word(state, 1287, reg_1287);
+ }
+ state->input_mode_mpeg = 1;
+ break;
+ case 1: /* both ways */
+ case 2: /* only the diversity input */
+ dprintk("%s ON : Enable diversity INPUT\n", __func__);
+ dib8096p_cfg_DibRx(state, 5, 5, 0, 0, 0, 0, 0);
+ state->input_mode_mpeg = 0;
+ break;
+ }
+
+ dib8000_set_diversity_in(state->fe[0], onoff);
+ return 0;
+}
+
+static int dib8096p_set_output_mode(struct dvb_frontend *fe, int mode)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u16 outreg, smo_mode, fifo_threshold;
+ u8 prefer_mpeg_mux_use = 1;
+ int ret = 0;
+
+ state->output_mode = mode;
+ dib8096p_host_bus_drive(state, 1);
+
+ fifo_threshold = 1792;
+ smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1);
+ outreg = dib8000_read_word(state, 1286) &
+ ~((1 << 10) | (0x7 << 6) | (1 << 1));
+
+ switch (mode) {
+ case OUTMODE_HIGH_Z:
+ outreg = 0;
+ break;
+
+ case OUTMODE_MPEG2_SERIAL:
+ if (prefer_mpeg_mux_use) {
+ dprintk("dib8096P setting output mode TS_SERIAL using Mpeg Mux\n");
+ dib8096p_configMpegMux(state, 3, 1, 1);
+ dib8096p_setHostBusMux(state, MPEG_ON_HOSTBUS);
+ } else {/* Use Smooth block */
+ dprintk("dib8096P setting output mode TS_SERIAL using Smooth block\n");
+ dib8096p_setHostBusMux(state,
+ DEMOUT_ON_HOSTBUS);
+ outreg |= (2 << 6) | (0 << 1);
+ }
+ break;
+
+ case OUTMODE_MPEG2_PAR_GATED_CLK:
+ if (prefer_mpeg_mux_use) {
+ dprintk("dib8096P setting output mode TS_PARALLEL_GATED using Mpeg Mux\n");
+ dib8096p_configMpegMux(state, 2, 0, 0);
+ dib8096p_setHostBusMux(state, MPEG_ON_HOSTBUS);
+ } else { /* Use Smooth block */
+ dprintk("dib8096P setting output mode TS_PARALLEL_GATED using Smooth block\n");
+ dib8096p_setHostBusMux(state,
+ DEMOUT_ON_HOSTBUS);
+ outreg |= (0 << 6);
+ }
+ break;
+
+ case OUTMODE_MPEG2_PAR_CONT_CLK: /* Using Smooth block only */
+ dprintk("dib8096P setting output mode TS_PARALLEL_CONT using Smooth block\n");
+ dib8096p_setHostBusMux(state, DEMOUT_ON_HOSTBUS);
+ outreg |= (1 << 6);
+ break;
+
+ case OUTMODE_MPEG2_FIFO:
+ /* Using Smooth block because not supported
+ * by new Mpeg Mux block
+ */
+ dprintk("dib8096P setting output mode TS_FIFO using Smooth block\n");
+ dib8096p_setHostBusMux(state, DEMOUT_ON_HOSTBUS);
+ outreg |= (5 << 6);
+ smo_mode |= (3 << 1);
+ fifo_threshold = 512;
+ break;
+
+ case OUTMODE_DIVERSITY:
+ dprintk("dib8096P setting output mode MODE_DIVERSITY\n");
+ dib8096p_setDibTxMux(state, DIV_ON_DIBTX);
+ dib8096p_setHostBusMux(state, DIBTX_ON_HOSTBUS);
+ break;
+
+ case OUTMODE_ANALOG_ADC:
+ dprintk("dib8096P setting output mode MODE_ANALOG_ADC\n");
+ dib8096p_setDibTxMux(state, ADC_ON_DIBTX);
+ dib8096p_setHostBusMux(state, DIBTX_ON_HOSTBUS);
+ break;
+ }
+
+ if (mode != OUTMODE_HIGH_Z)
+ outreg |= (1<<10);
+
+ dprintk("output_mpeg2_in_188_bytes = %d\n",
+ state->cfg.output_mpeg2_in_188_bytes);
+ if (state->cfg.output_mpeg2_in_188_bytes)
+ smo_mode |= (1 << 5);
+
+ ret |= dib8000_write_word(state, 299, smo_mode);
+ /* synchronous fread */
+ ret |= dib8000_write_word(state, 299 + 1, fifo_threshold);
+ ret |= dib8000_write_word(state, 1286, outreg);
+
+ return ret;
+}
+
+static int map_addr_to_serpar_number(struct i2c_msg *msg)
+{
+ if (msg->buf[0] <= 15)
+ msg->buf[0] -= 1;
+ else if (msg->buf[0] == 17)
+ msg->buf[0] = 15;
+ else if (msg->buf[0] == 16)
+ msg->buf[0] = 17;
+ else if (msg->buf[0] == 19)
+ msg->buf[0] = 16;
+ else if (msg->buf[0] >= 21 && msg->buf[0] <= 25)
+ msg->buf[0] -= 3;
+ else if (msg->buf[0] == 28)
+ msg->buf[0] = 23;
+ else if (msg->buf[0] == 99)
+ msg->buf[0] = 99;
+ else
+ return -EINVAL;
+ return 0;
+}
+
+static int dib8096p_tuner_write_serpar(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msg[], int num)
+{
+ struct dib8000_state *state = i2c_get_adapdata(i2c_adap);
+ u8 n_overflow = 1;
+ u16 i = 1000;
+ u16 serpar_num = msg[0].buf[0];
+
+ while (n_overflow == 1 && i) {
+ n_overflow = (dib8000_read_word(state, 1984) >> 1) & 0x1;
+ i--;
+ if (i == 0)
+ dprintk("Tuner ITF: write busy (overflow)\n");
+ }
+ dib8000_write_word(state, 1985, (1 << 6) | (serpar_num & 0x3f));
+ dib8000_write_word(state, 1986, (msg[0].buf[1] << 8) | msg[0].buf[2]);
+
+ return num;
+}
+
+static int dib8096p_tuner_read_serpar(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msg[], int num)
+{
+ struct dib8000_state *state = i2c_get_adapdata(i2c_adap);
+ u8 n_overflow = 1, n_empty = 1;
+ u16 i = 1000;
+ u16 serpar_num = msg[0].buf[0];
+ u16 read_word;
+
+ while (n_overflow == 1 && i) {
+ n_overflow = (dib8000_read_word(state, 1984) >> 1) & 0x1;
+ i--;
+ if (i == 0)
+ dprintk("TunerITF: read busy (overflow)\n");
+ }
+ dib8000_write_word(state, 1985, (0<<6) | (serpar_num&0x3f));
+
+ i = 1000;
+ while (n_empty == 1 && i) {
+ n_empty = dib8000_read_word(state, 1984)&0x1;
+ i--;
+ if (i == 0)
+ dprintk("TunerITF: read busy (empty)\n");
+ }
+
+ read_word = dib8000_read_word(state, 1987);
+ msg[1].buf[0] = (read_word >> 8) & 0xff;
+ msg[1].buf[1] = (read_word) & 0xff;
+
+ return num;
+}
+
+static int dib8096p_tuner_rw_serpar(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msg[], int num)
+{
+ if (map_addr_to_serpar_number(&msg[0]) == 0) {
+ if (num == 1) /* write */
+ return dib8096p_tuner_write_serpar(i2c_adap, msg, 1);
+ else /* read */
+ return dib8096p_tuner_read_serpar(i2c_adap, msg, 2);
+ }
+ return num;
+}
+
+static int dib8096p_rw_on_apb(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msg[], int num, u16 apb_address)
+{
+ struct dib8000_state *state = i2c_get_adapdata(i2c_adap);
+ u16 word;
+
+ if (num == 1) { /* write */
+ dib8000_write_word(state, apb_address,
+ ((msg[0].buf[1] << 8) | (msg[0].buf[2])));
+ } else {
+ word = dib8000_read_word(state, apb_address);
+ msg[1].buf[0] = (word >> 8) & 0xff;
+ msg[1].buf[1] = (word) & 0xff;
+ }
+ return num;
+}
+
+static int dib8096p_tuner_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msg[], int num)
+{
+ struct dib8000_state *state = i2c_get_adapdata(i2c_adap);
+ u16 apb_address = 0, word;
+ int i = 0;
+
+ switch (msg[0].buf[0]) {
+ case 0x12:
+ apb_address = 1920;
+ break;
+ case 0x14:
+ apb_address = 1921;
+ break;
+ case 0x24:
+ apb_address = 1922;
+ break;
+ case 0x1a:
+ apb_address = 1923;
+ break;
+ case 0x22:
+ apb_address = 1924;
+ break;
+ case 0x33:
+ apb_address = 1926;
+ break;
+ case 0x34:
+ apb_address = 1927;
+ break;
+ case 0x35:
+ apb_address = 1928;
+ break;
+ case 0x36:
+ apb_address = 1929;
+ break;
+ case 0x37:
+ apb_address = 1930;
+ break;
+ case 0x38:
+ apb_address = 1931;
+ break;
+ case 0x39:
+ apb_address = 1932;
+ break;
+ case 0x2a:
+ apb_address = 1935;
+ break;
+ case 0x2b:
+ apb_address = 1936;
+ break;
+ case 0x2c:
+ apb_address = 1937;
+ break;
+ case 0x2d:
+ apb_address = 1938;
+ break;
+ case 0x2e:
+ apb_address = 1939;
+ break;
+ case 0x2f:
+ apb_address = 1940;
+ break;
+ case 0x30:
+ apb_address = 1941;
+ break;
+ case 0x31:
+ apb_address = 1942;
+ break;
+ case 0x32:
+ apb_address = 1943;
+ break;
+ case 0x3e:
+ apb_address = 1944;
+ break;
+ case 0x3f:
+ apb_address = 1945;
+ break;
+ case 0x40:
+ apb_address = 1948;
+ break;
+ case 0x25:
+ apb_address = 936;
+ break;
+ case 0x26:
+ apb_address = 937;
+ break;
+ case 0x27:
+ apb_address = 938;
+ break;
+ case 0x28:
+ apb_address = 939;
+ break;
+ case 0x1d:
+ /* get sad sel request */
+ i = ((dib8000_read_word(state, 921) >> 12)&0x3);
+ word = dib8000_read_word(state, 924+i);
+ msg[1].buf[0] = (word >> 8) & 0xff;
+ msg[1].buf[1] = (word) & 0xff;
+ return num;
+ case 0x1f:
+ if (num == 1) { /* write */
+ word = (u16) ((msg[0].buf[1] << 8) |
+ msg[0].buf[2]);
+ /* in the VGAMODE Sel are located on bit 0/1 */
+ word &= 0x3;
+ word = (dib8000_read_word(state, 921) &
+ ~(3<<12)) | (word<<12);
+ /* Set the proper input */
+ dib8000_write_word(state, 921, word);
+ return num;
+ }
+ }
+
+ if (apb_address != 0) /* R/W access via APB */
+ return dib8096p_rw_on_apb(i2c_adap, msg, num, apb_address);
+ else /* R/W access via SERPAR */
+ return dib8096p_tuner_rw_serpar(i2c_adap, msg, num);
+
+ return 0;
+}
+
+static u32 dib8096p_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static const struct i2c_algorithm dib8096p_tuner_xfer_algo = {
+ .master_xfer = dib8096p_tuner_xfer,
+ .functionality = dib8096p_i2c_func,
+};
+
+static struct i2c_adapter *dib8096p_get_i2c_tuner(struct dvb_frontend *fe)
+{
+ struct dib8000_state *st = fe->demodulator_priv;
+ return &st->dib8096p_tuner_adap;
+}
+
+static int dib8096p_tuner_sleep(struct dvb_frontend *fe, int onoff)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u16 en_cur_state;
+
+ dprintk("sleep dib8096p: %d\n", onoff);
+
+ en_cur_state = dib8000_read_word(state, 1922);
+
+ /* LNAs and MIX are ON and therefore it is a valid configuration */
+ if (en_cur_state > 0xff)
+ state->tuner_enable = en_cur_state ;
+
+ if (onoff)
+ en_cur_state &= 0x00ff;
+ else {
+ if (state->tuner_enable != 0)
+ en_cur_state = state->tuner_enable;
+ }
+
+ dib8000_write_word(state, 1922, en_cur_state);
+
+ return 0;
+}
+
+static const s32 lut_1000ln_mant[] =
+{
+ 908, 7003, 7090, 7170, 7244, 7313, 7377, 7438, 7495, 7549, 7600
+};
+
+static s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u32 ix = 0, tmp_val = 0, exp = 0, mant = 0;
+ s32 val;
+
+ val = dib8000_read32(state, 384);
+ if (mode) {
+ tmp_val = val;
+ while (tmp_val >>= 1)
+ exp++;
+ mant = (val * 1000 / (1<<exp));
+ ix = (u8)((mant-1000)/100); /* index of the LUT */
+ val = (lut_1000ln_mant[ix] + 693*(exp-20) - 6908);
+ val = (val*256)/1000;
+ }
+ return val;
+}
+
+static int dib8090p_get_dc_power(struct dvb_frontend *fe, u8 IQ)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ int val = 0;
+
+ switch (IQ) {
+ case 1:
+ val = dib8000_read_word(state, 403);
+ break;
+ case 0:
+ val = dib8000_read_word(state, 404);
+ break;
+ }
+ if (val & 0x200)
+ val -= 1024;
+
+ return val;
+}
+
+static void dib8000_update_timf(struct dib8000_state *state)
+{
+ u32 timf = state->timf = dib8000_read32(state, 435);
+
+ dib8000_write_word(state, 29, (u16) (timf >> 16));
+ dib8000_write_word(state, 30, (u16) (timf & 0xffff));
+ dprintk("Updated timing frequency: %d (default: %d)\n", state->timf, state->timf_default);
+}
+
+static u32 dib8000_ctrl_timf(struct dvb_frontend *fe, uint8_t op, uint32_t timf)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+
+ switch (op) {
+ case DEMOD_TIMF_SET:
+ state->timf = timf;
+ break;
+ case DEMOD_TIMF_UPDATE:
+ dib8000_update_timf(state);
+ break;
+ case DEMOD_TIMF_GET:
+ break;
+ }
+ dib8000_set_bandwidth(state->fe[0], 6000);
+
+ return state->timf;
+}
+
+static const u16 adc_target_16dB[11] = {
+ 7250, 7238, 7264, 7309, 7338, 7382, 7427, 7456, 7500, 7544, 7574
+};
+
+static const u8 permu_seg[] = { 6, 5, 7, 4, 8, 3, 9, 2, 10, 1, 11, 0, 12 };
+
+static u16 dib8000_set_layer(struct dib8000_state *state, u8 layer_index, u16 max_constellation)
+{
+ u8 cr, constellation, time_intlv;
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+
+ switch (c->layer[layer_index].modulation) {
+ case DQPSK:
+ constellation = 0;
+ break;
+ case QPSK:
+ constellation = 1;
+ break;
+ case QAM_16:
+ constellation = 2;
+ break;
+ case QAM_64:
+ default:
+ constellation = 3;
+ break;
+ }
+
+ switch (c->layer[layer_index].fec) {
+ case FEC_1_2:
+ cr = 1;
+ break;
+ case FEC_2_3:
+ cr = 2;
+ break;
+ case FEC_3_4:
+ cr = 3;
+ break;
+ case FEC_5_6:
+ cr = 5;
+ break;
+ case FEC_7_8:
+ default:
+ cr = 7;
+ break;
+ }
+
+ time_intlv = fls(c->layer[layer_index].interleaving);
+ if (time_intlv > 3 && !(time_intlv == 4 && c->isdbt_sb_mode == 1))
+ time_intlv = 0;
+
+ dib8000_write_word(state, 2 + layer_index, (constellation << 10) | ((c->layer[layer_index].segment_count & 0xf) << 6) | (cr << 3) | time_intlv);
+ if (c->layer[layer_index].segment_count > 0) {
+ switch (max_constellation) {
+ case DQPSK:
+ case QPSK:
+ if (c->layer[layer_index].modulation == QAM_16 || c->layer[layer_index].modulation == QAM_64)
+ max_constellation = c->layer[layer_index].modulation;
+ break;
+ case QAM_16:
+ if (c->layer[layer_index].modulation == QAM_64)
+ max_constellation = c->layer[layer_index].modulation;
+ break;
+ }
+ }
+
+ return max_constellation;
+}
+
+static const u16 adp_Q64[4] = {0x0148, 0xfff0, 0x00a4, 0xfff8}; /* P_adp_regul_cnt 0.04, P_adp_noise_cnt -0.002, P_adp_regul_ext 0.02, P_adp_noise_ext -0.001 */
+static const u16 adp_Q16[4] = {0x023d, 0xffdf, 0x00a4, 0xfff0}; /* P_adp_regul_cnt 0.07, P_adp_noise_cnt -0.004, P_adp_regul_ext 0.02, P_adp_noise_ext -0.002 */
+static const u16 adp_Qdefault[4] = {0x099a, 0xffae, 0x0333, 0xfff8}; /* P_adp_regul_cnt 0.3, P_adp_noise_cnt -0.01, P_adp_regul_ext 0.1, P_adp_noise_ext -0.002 */
+static u16 dib8000_adp_fine_tune(struct dib8000_state *state, u16 max_constellation)
+{
+ u16 i, ana_gain = 0;
+ const u16 *adp;
+
+ /* channel estimation fine configuration */
+ switch (max_constellation) {
+ case QAM_64:
+ ana_gain = 0x7;
+ adp = &adp_Q64[0];
+ break;
+ case QAM_16:
+ ana_gain = 0x7;
+ adp = &adp_Q16[0];
+ break;
+ default:
+ ana_gain = 0;
+ adp = &adp_Qdefault[0];
+ break;
+ }
+
+ for (i = 0; i < 4; i++)
+ dib8000_write_word(state, 215 + i, adp[i]);
+
+ return ana_gain;
+}
+
+static void dib8000_update_ana_gain(struct dib8000_state *state, u16 ana_gain)
+{
+ u16 i;
+
+ dib8000_write_word(state, 116, ana_gain);
+
+ /* update ADC target depending on ana_gain */
+ if (ana_gain) { /* set -16dB ADC target for ana_gain=-1 */
+ for (i = 0; i < 10; i++)
+ dib8000_write_word(state, 80 + i, adc_target_16dB[i]);
+ } else { /* set -22dB ADC target for ana_gain=0 */
+ for (i = 0; i < 10; i++)
+ dib8000_write_word(state, 80 + i, adc_target_16dB[i] - 355);
+ }
+}
+
+static void dib8000_load_ana_fe_coefs(struct dib8000_state *state, const s16 *ana_fe)
+{
+ u16 mode = 0;
+
+ if (state->isdbt_cfg_loaded == 0)
+ for (mode = 0; mode < 24; mode++)
+ dib8000_write_word(state, 117 + mode, ana_fe[mode]);
+}
+
+static const u16 lut_prbs_2k[13] = {
+ 0x423, 0x009, 0x5C7,
+ 0x7A6, 0x3D8, 0x527,
+ 0x7FF, 0x79B, 0x3D6,
+ 0x3A2, 0x53B, 0x2F4,
+ 0x213
+};
+
+static const u16 lut_prbs_4k[13] = {
+ 0x208, 0x0C3, 0x7B9,
+ 0x423, 0x5C7, 0x3D8,
+ 0x7FF, 0x3D6, 0x53B,
+ 0x213, 0x029, 0x0D0,
+ 0x48E
+};
+
+static const u16 lut_prbs_8k[13] = {
+ 0x740, 0x069, 0x7DD,
+ 0x208, 0x7B9, 0x5C7,
+ 0x7FF, 0x53B, 0x029,
+ 0x48E, 0x4C4, 0x367,
+ 0x684
+};
+
+static u16 dib8000_get_init_prbs(struct dib8000_state *state, u16 subchannel)
+{
+ int sub_channel_prbs_group = 0;
+ int prbs_group;
+
+ sub_channel_prbs_group = subchannel / 3;
+ if (sub_channel_prbs_group >= ARRAY_SIZE(lut_prbs_2k))
+ return 0;
+
+ switch (state->fe[0]->dtv_property_cache.transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ prbs_group = lut_prbs_2k[sub_channel_prbs_group];
+ break;
+ case TRANSMISSION_MODE_4K:
+ prbs_group = lut_prbs_4k[sub_channel_prbs_group];
+ break;
+ default:
+ case TRANSMISSION_MODE_8K:
+ prbs_group = lut_prbs_8k[sub_channel_prbs_group];
+ }
+
+ dprintk("sub_channel_prbs_group = %d , subchannel =%d prbs = 0x%04x\n",
+ sub_channel_prbs_group, subchannel, prbs_group);
+
+ return prbs_group;
+}
+
+static void dib8000_set_13seg_channel(struct dib8000_state *state)
+{
+ u16 i;
+ u16 coff_pow = 0x2800;
+
+ state->seg_mask = 0x1fff; /* All 13 segments enabled */
+
+ /* ---- COFF ---- Carloff, the most robust --- */
+ if (state->isdbt_cfg_loaded == 0) { /* if not Sound Broadcasting mode : put default values for 13 segments */
+ dib8000_write_word(state, 180, (16 << 6) | 9);
+ dib8000_write_word(state, 187, (4 << 12) | (8 << 5) | 0x2);
+ coff_pow = 0x2800;
+ for (i = 0; i < 6; i++)
+ dib8000_write_word(state, 181+i, coff_pow);
+
+ /* P_ctrl_corm_thres4pre_freq_inh=1, P_ctrl_pre_freq_mode_sat=1 */
+ /* P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 3, P_pre_freq_win_len=1 */
+ dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (3 << 5) | 1);
+
+ /* P_ctrl_pre_freq_win_len=8, P_ctrl_pre_freq_thres_lockin=6 */
+ dib8000_write_word(state, 340, (8 << 6) | (6 << 0));
+ /* P_ctrl_pre_freq_thres_lockout=4, P_small_use_tmcc/ac/cp=1 */
+ dib8000_write_word(state, 341, (4 << 3) | (1 << 2) | (1 << 1) | (1 << 0));
+
+ dib8000_write_word(state, 228, 0); /* default value */
+ dib8000_write_word(state, 265, 31); /* default value */
+ dib8000_write_word(state, 205, 0x200f); /* init value */
+ }
+
+ /*
+ * make the cpil_coff_lock more robust but slower p_coff_winlen
+ * 6bits; p_coff_thres_lock 6bits (for coff lock if needed)
+ */
+
+ if (state->cfg.pll->ifreq == 0)
+ dib8000_write_word(state, 266, ~state->seg_mask | state->seg_diff_mask | 0x40); /* P_equal_noise_seg_inh */
+
+ dib8000_load_ana_fe_coefs(state, ana_fe_coeff_13seg);
+}
+
+static void dib8000_set_subchannel_prbs(struct dib8000_state *state, u16 init_prbs)
+{
+ u16 reg_1;
+
+ reg_1 = dib8000_read_word(state, 1);
+ dib8000_write_word(state, 1, (init_prbs << 2) | (reg_1 & 0x3)); /* ADDR 1 */
+}
+
+static void dib8000_small_fine_tune(struct dib8000_state *state)
+{
+ u16 i;
+ const s16 *ncoeff;
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+
+ dib8000_write_word(state, 352, state->seg_diff_mask);
+ dib8000_write_word(state, 353, state->seg_mask);
+
+ /* P_small_coef_ext_enable=ISDB-Tsb, P_small_narrow_band=ISDB-Tsb, P_small_last_seg=13, P_small_offset_num_car=5 */
+ dib8000_write_word(state, 351, (c->isdbt_sb_mode << 9) | (c->isdbt_sb_mode << 8) | (13 << 4) | 5);
+
+ if (c->isdbt_sb_mode) {
+ /* ---- SMALL ---- */
+ switch (c->transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ if (c->isdbt_partial_reception == 0) { /* 1-seg */
+ if (c->layer[0].modulation == DQPSK) /* DQPSK */
+ ncoeff = coeff_2k_sb_1seg_dqpsk;
+ else /* QPSK or QAM */
+ ncoeff = coeff_2k_sb_1seg;
+ } else { /* 3-segments */
+ if (c->layer[0].modulation == DQPSK) { /* DQPSK on central segment */
+ if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */
+ ncoeff = coeff_2k_sb_3seg_0dqpsk_1dqpsk;
+ else /* QPSK or QAM on external segments */
+ ncoeff = coeff_2k_sb_3seg_0dqpsk;
+ } else { /* QPSK or QAM on central segment */
+ if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */
+ ncoeff = coeff_2k_sb_3seg_1dqpsk;
+ else /* QPSK or QAM on external segments */
+ ncoeff = coeff_2k_sb_3seg;
+ }
+ }
+ break;
+ case TRANSMISSION_MODE_4K:
+ if (c->isdbt_partial_reception == 0) { /* 1-seg */
+ if (c->layer[0].modulation == DQPSK) /* DQPSK */
+ ncoeff = coeff_4k_sb_1seg_dqpsk;
+ else /* QPSK or QAM */
+ ncoeff = coeff_4k_sb_1seg;
+ } else { /* 3-segments */
+ if (c->layer[0].modulation == DQPSK) { /* DQPSK on central segment */
+ if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */
+ ncoeff = coeff_4k_sb_3seg_0dqpsk_1dqpsk;
+ else /* QPSK or QAM on external segments */
+ ncoeff = coeff_4k_sb_3seg_0dqpsk;
+ } else { /* QPSK or QAM on central segment */
+ if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */
+ ncoeff = coeff_4k_sb_3seg_1dqpsk;
+ else /* QPSK or QAM on external segments */
+ ncoeff = coeff_4k_sb_3seg;
+ }
+ }
+ break;
+ case TRANSMISSION_MODE_AUTO:
+ case TRANSMISSION_MODE_8K:
+ default:
+ if (c->isdbt_partial_reception == 0) { /* 1-seg */
+ if (c->layer[0].modulation == DQPSK) /* DQPSK */
+ ncoeff = coeff_8k_sb_1seg_dqpsk;
+ else /* QPSK or QAM */
+ ncoeff = coeff_8k_sb_1seg;
+ } else { /* 3-segments */
+ if (c->layer[0].modulation == DQPSK) { /* DQPSK on central segment */
+ if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */
+ ncoeff = coeff_8k_sb_3seg_0dqpsk_1dqpsk;
+ else /* QPSK or QAM on external segments */
+ ncoeff = coeff_8k_sb_3seg_0dqpsk;
+ } else { /* QPSK or QAM on central segment */
+ if (c->layer[1].modulation == DQPSK) /* DQPSK on external segments */
+ ncoeff = coeff_8k_sb_3seg_1dqpsk;
+ else /* QPSK or QAM on external segments */
+ ncoeff = coeff_8k_sb_3seg;
+ }
+ }
+ break;
+ }
+
+ for (i = 0; i < 8; i++)
+ dib8000_write_word(state, 343 + i, ncoeff[i]);
+ }
+}
+
+static const u16 coff_thres_1seg[3] = {300, 150, 80};
+static const u16 coff_thres_3seg[3] = {350, 300, 250};
+static void dib8000_set_sb_channel(struct dib8000_state *state)
+{
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ const u16 *coff;
+ u16 i;
+
+ if (c->transmission_mode == TRANSMISSION_MODE_2K || c->transmission_mode == TRANSMISSION_MODE_4K) {
+ dib8000_write_word(state, 219, dib8000_read_word(state, 219) | 0x1); /* adp_pass =1 */
+ dib8000_write_word(state, 190, dib8000_read_word(state, 190) | (0x1 << 14)); /* pha3_force_pha_shift = 1 */
+ } else {
+ dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); /* adp_pass =0 */
+ dib8000_write_word(state, 190, dib8000_read_word(state, 190) & 0xbfff); /* pha3_force_pha_shift = 0 */
+ }
+
+ if (c->isdbt_partial_reception == 1) /* 3-segments */
+ state->seg_mask = 0x00E0;
+ else /* 1-segment */
+ state->seg_mask = 0x0040;
+
+ dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200);
+
+ /* ---- COFF ---- Carloff, the most robust --- */
+ /* P_coff_cpil_alpha=4, P_coff_inh=0, P_coff_cpil_winlen=64, P_coff_narrow_band=1, P_coff_square_val=1, P_coff_one_seg=~partial_rcpt, P_coff_use_tmcc=1, P_coff_use_ac=1 */
+ dib8000_write_word(state, 187, (4 << 12) | (0 << 11) | (63 << 5) | (0x3 << 3) | ((~c->isdbt_partial_reception & 1) << 2) | 0x3);
+
+ dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); /* P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 */
+ dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0));/* P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 */
+
+ /* Sound Broadcasting mode 1 seg */
+ if (c->isdbt_partial_reception == 0) {
+ /* P_coff_winlen=63, P_coff_thres_lock=15, P_coff_one_seg_width = (P_mode == 3) , P_coff_one_seg_sym = (P_mode-1) */
+ if (state->mode == 3)
+ dib8000_write_word(state, 180, 0x1fcf | ((state->mode - 1) << 14));
+ else
+ dib8000_write_word(state, 180, 0x0fcf | ((state->mode - 1) << 14));
+
+ /* P_ctrl_corm_thres4pre_freq_inh=1,P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 5, P_pre_freq_win_len=4 */
+ dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (5 << 5) | 4);
+ coff = &coff_thres_1seg[0];
+ } else { /* Sound Broadcasting mode 3 seg */
+ dib8000_write_word(state, 180, 0x1fcf | (1 << 14));
+ /* P_ctrl_corm_thres4pre_freq_inh = 1, P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 4, P_pre_freq_win_len=4 */
+ dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (4 << 5) | 4);
+ coff = &coff_thres_3seg[0];
+ }
+
+ dib8000_write_word(state, 228, 1); /* P_2d_mode_byp=1 */
+ dib8000_write_word(state, 205, dib8000_read_word(state, 205) & 0xfff0); /* P_cspu_win_cut = 0 */
+
+ if (c->isdbt_partial_reception == 0 && c->transmission_mode == TRANSMISSION_MODE_2K)
+ dib8000_write_word(state, 265, 15); /* P_equal_noise_sel = 15 */
+
+ /* Write COFF thres */
+ for (i = 0 ; i < 3; i++) {
+ dib8000_write_word(state, 181+i, coff[i]);
+ dib8000_write_word(state, 184+i, coff[i]);
+ }
+
+ /*
+ * make the cpil_coff_lock more robust but slower p_coff_winlen
+ * 6bits; p_coff_thres_lock 6bits (for coff lock if needed)
+ */
+
+ dib8000_write_word(state, 266, ~state->seg_mask | state->seg_diff_mask); /* P_equal_noise_seg_inh */
+
+ if (c->isdbt_partial_reception == 0)
+ dib8000_write_word(state, 178, 64); /* P_fft_powrange = 64 */
+ else
+ dib8000_write_word(state, 178, 32); /* P_fft_powrange = 32 */
+}
+
+static void dib8000_set_isdbt_common_channel(struct dib8000_state *state, u8 seq, u8 autosearching)
+{
+ u16 p_cfr_left_edge = 0, p_cfr_right_edge = 0;
+ u16 tmcc_pow = 0, ana_gain = 0, tmp = 0, i = 0, nbseg_diff = 0 ;
+ u16 max_constellation = DQPSK;
+ int init_prbs;
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+
+ if (autosearching)
+ c->isdbt_partial_reception = 1;
+
+ /* P_mode */
+ dib8000_write_word(state, 10, (seq << 4));
+
+ /* init mode */
+ state->mode = fft_to_mode(state);
+
+ /* set guard */
+ tmp = dib8000_read_word(state, 1);
+ dib8000_write_word(state, 1, (tmp&0xfffc) | (c->guard_interval & 0x3));
+
+ dib8000_write_word(state, 274, (dib8000_read_word(state, 274) & 0xffcf) | ((c->isdbt_partial_reception & 1) << 5) | ((c->isdbt_sb_mode & 1) << 4));
+
+ /* signal optimization parameter */
+ if (c->isdbt_partial_reception) {
+ state->seg_diff_mask = (c->layer[0].modulation == DQPSK) << permu_seg[0];
+ for (i = 1; i < 3; i++)
+ nbseg_diff += (c->layer[i].modulation == DQPSK) * c->layer[i].segment_count;
+ for (i = 0; i < nbseg_diff; i++)
+ state->seg_diff_mask |= 1 << permu_seg[i+1];
+ } else {
+ for (i = 0; i < 3; i++)
+ nbseg_diff += (c->layer[i].modulation == DQPSK) * c->layer[i].segment_count;
+ for (i = 0; i < nbseg_diff; i++)
+ state->seg_diff_mask |= 1 << permu_seg[i];
+ }
+
+ if (state->seg_diff_mask)
+ dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200);
+ else
+ dib8000_write_word(state, 268, (2 << 9) | 39); /*init value */
+
+ for (i = 0; i < 3; i++)
+ max_constellation = dib8000_set_layer(state, i, max_constellation);
+ if (autosearching == 0) {
+ state->layer_b_nb_seg = c->layer[1].segment_count;
+ state->layer_c_nb_seg = c->layer[2].segment_count;
+ }
+
+ /* WRITE: Mode & Diff mask */
+ dib8000_write_word(state, 0, (state->mode << 13) | state->seg_diff_mask);
+
+ state->differential_constellation = (state->seg_diff_mask != 0);
+
+ /* channel estimation fine configuration */
+ ana_gain = dib8000_adp_fine_tune(state, max_constellation);
+
+ /* update ana_gain depending on max constellation */
+ dib8000_update_ana_gain(state, ana_gain);
+
+ /* ---- ANA_FE ---- */
+ if (c->isdbt_partial_reception) /* 3-segments */
+ dib8000_load_ana_fe_coefs(state, ana_fe_coeff_3seg);
+ else
+ dib8000_load_ana_fe_coefs(state, ana_fe_coeff_1seg); /* 1-segment */
+
+ /* TSB or ISDBT ? apply it now */
+ if (c->isdbt_sb_mode) {
+ dib8000_set_sb_channel(state);
+ init_prbs = dib8000_get_init_prbs(state,
+ c->isdbt_sb_subchannel);
+ } else {
+ dib8000_set_13seg_channel(state);
+ init_prbs = 0xfff;
+ }
+
+ /* SMALL */
+ dib8000_small_fine_tune(state);
+
+ dib8000_set_subchannel_prbs(state, init_prbs);
+
+ /* ---- CHAN_BLK ---- */
+ for (i = 0; i < 13; i++) {
+ if ((((~state->seg_diff_mask) >> i) & 1) == 1) {
+ p_cfr_left_edge += (1 << i) * ((i == 0) || ((((state->seg_mask & (~state->seg_diff_mask)) >> (i - 1)) & 1) == 0));
+ p_cfr_right_edge += (1 << i) * ((i == 12) || ((((state->seg_mask & (~state->seg_diff_mask)) >> (i + 1)) & 1) == 0));
+ }
+ }
+ dib8000_write_word(state, 222, p_cfr_left_edge); /* p_cfr_left_edge */
+ dib8000_write_word(state, 223, p_cfr_right_edge); /* p_cfr_right_edge */
+ /* "P_cspu_left_edge" & "P_cspu_right_edge" not used => do not care */
+
+ dib8000_write_word(state, 189, ~state->seg_mask | state->seg_diff_mask); /* P_lmod4_seg_inh */
+ dib8000_write_word(state, 192, ~state->seg_mask | state->seg_diff_mask); /* P_pha3_seg_inh */
+ dib8000_write_word(state, 225, ~state->seg_mask | state->seg_diff_mask); /* P_tac_seg_inh */
+
+ if (!autosearching)
+ dib8000_write_word(state, 288, (~state->seg_mask | state->seg_diff_mask) & 0x1fff); /* P_tmcc_seg_eq_inh */
+ else
+ dib8000_write_word(state, 288, 0x1fff); /*disable equalisation of the tmcc when autosearch to be able to find the DQPSK channels. */
+
+ dib8000_write_word(state, 211, state->seg_mask & (~state->seg_diff_mask)); /* P_des_seg_enabled */
+ dib8000_write_word(state, 287, ~state->seg_mask | 0x1000); /* P_tmcc_seg_inh */
+
+ dib8000_write_word(state, 178, 32); /* P_fft_powrange = 32 */
+
+ /* ---- TMCC ---- */
+ for (i = 0; i < 3; i++)
+ tmcc_pow += (((c->layer[i].modulation == DQPSK) * 4 + 1) * c->layer[i].segment_count) ;
+
+ /* Quantif of "P_tmcc_dec_thres_?k" is (0, 5+mode, 9); */
+ /* Threshold is set at 1/4 of max power. */
+ tmcc_pow *= (1 << (9-2));
+ dib8000_write_word(state, 290, tmcc_pow); /* P_tmcc_dec_thres_2k */
+ dib8000_write_word(state, 291, tmcc_pow); /* P_tmcc_dec_thres_4k */
+ dib8000_write_word(state, 292, tmcc_pow); /* P_tmcc_dec_thres_8k */
+ /*dib8000_write_word(state, 287, (1 << 13) | 0x1000 ); */
+
+ /* ---- PHA3 ---- */
+ if (state->isdbt_cfg_loaded == 0)
+ dib8000_write_word(state, 250, 3285); /* p_2d_hspeed_thr0 */
+
+ state->isdbt_cfg_loaded = 0;
+}
+
+static u32 dib8000_wait_lock(struct dib8000_state *state, u32 internal,
+ u32 wait0_ms, u32 wait1_ms, u32 wait2_ms)
+{
+ u32 value = 0; /* P_search_end0 wait time */
+ u16 reg = 11; /* P_search_end0 start addr */
+
+ for (reg = 11; reg < 16; reg += 2) {
+ if (reg == 11) {
+ if (state->revision == 0x8090)
+ value = internal * wait1_ms;
+ else
+ value = internal * wait0_ms;
+ } else if (reg == 13)
+ value = internal * wait1_ms;
+ else if (reg == 15)
+ value = internal * wait2_ms;
+ dib8000_write_word(state, reg, (u16)((value >> 16) & 0xffff));
+ dib8000_write_word(state, (reg + 1), (u16)(value & 0xffff));
+ }
+ return value;
+}
+
+static int dib8000_autosearch_start(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ u8 slist = 0;
+ u32 value, internal = state->cfg.pll->internal;
+
+ if (state->revision == 0x8090)
+ internal = dib8000_read32(state, 23) / 1000;
+
+ if ((state->revision >= 0x8002) &&
+ (state->autosearch_state == AS_SEARCHING_FFT)) {
+ dib8000_write_word(state, 37, 0x0065); /* P_ctrl_pha_off_max default values */
+ dib8000_write_word(state, 116, 0x0000); /* P_ana_gain to 0 */
+
+ dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x1fff) | (0 << 13) | (1 << 15)); /* P_mode = 0, P_restart_search=1 */
+ dib8000_write_word(state, 1, (dib8000_read_word(state, 1) & 0xfffc) | 0); /* P_guard = 0 */
+ dib8000_write_word(state, 6, 0); /* P_lock0_mask = 0 */
+ dib8000_write_word(state, 7, 0); /* P_lock1_mask = 0 */
+ dib8000_write_word(state, 8, 0); /* P_lock2_mask = 0 */
+ dib8000_write_word(state, 10, (dib8000_read_word(state, 10) & 0x200) | (16 << 4) | (0 << 0)); /* P_search_list=16, P_search_maxtrial=0 */
+
+ if (state->revision == 0x8090)
+ value = dib8000_wait_lock(state, internal, 10, 10, 10); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */
+ else
+ value = dib8000_wait_lock(state, internal, 20, 20, 20); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */
+
+ dib8000_write_word(state, 17, 0);
+ dib8000_write_word(state, 18, 200); /* P_search_rstst = 200 */
+ dib8000_write_word(state, 19, 0);
+ dib8000_write_word(state, 20, 400); /* P_search_rstend = 400 */
+ dib8000_write_word(state, 21, (value >> 16) & 0xffff); /* P_search_checkst */
+ dib8000_write_word(state, 22, value & 0xffff);
+
+ if (state->revision == 0x8090)
+ dib8000_write_word(state, 32, (dib8000_read_word(state, 32) & 0xf0ff) | (0 << 8)); /* P_corm_alpha = 0 */
+ else
+ dib8000_write_word(state, 32, (dib8000_read_word(state, 32) & 0xf0ff) | (9 << 8)); /* P_corm_alpha = 3 */
+ dib8000_write_word(state, 355, 2); /* P_search_param_max = 2 */
+
+ /* P_search_param_select = (1 | 1<<4 | 1 << 8) */
+ dib8000_write_word(state, 356, 0);
+ dib8000_write_word(state, 357, 0x111);
+
+ dib8000_write_word(state, 770, (dib8000_read_word(state, 770) & 0xdfff) | (1 << 13)); /* P_restart_ccg = 1 */
+ dib8000_write_word(state, 770, (dib8000_read_word(state, 770) & 0xdfff) | (0 << 13)); /* P_restart_ccg = 0 */
+ dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x7ff) | (0 << 15) | (1 << 13)); /* P_restart_search = 0; */
+ } else if ((state->revision >= 0x8002) &&
+ (state->autosearch_state == AS_SEARCHING_GUARD)) {
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ c->inversion = 0;
+ c->layer[0].modulation = QAM_64;
+ c->layer[0].fec = FEC_2_3;
+ c->layer[0].interleaving = 0;
+ c->layer[0].segment_count = 13;
+
+ slist = 16;
+ c->transmission_mode = state->found_nfft;
+
+ dib8000_set_isdbt_common_channel(state, slist, 1);
+
+ /* set lock_mask values */
+ dib8000_write_word(state, 6, 0x4);
+ if (state->revision == 0x8090)
+ dib8000_write_word(state, 7, ((1 << 12) | (1 << 11) | (1 << 10)));/* tmcc_dec_lock, tmcc_sync_lock, tmcc_data_lock, tmcc_bch_uncor */
+ else
+ dib8000_write_word(state, 7, 0x8);
+ dib8000_write_word(state, 8, 0x1000);
+
+ /* set lock_mask wait time values */
+ if (state->revision == 0x8090)
+ dib8000_wait_lock(state, internal, 50, 100, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */
+ else
+ dib8000_wait_lock(state, internal, 50, 200, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */
+
+ dib8000_write_word(state, 355, 3); /* P_search_param_max = 3 */
+
+ /* P_search_param_select = 0xf; look for the 4 different guard intervals */
+ dib8000_write_word(state, 356, 0);
+ dib8000_write_word(state, 357, 0xf);
+
+ value = dib8000_read_word(state, 0);
+ dib8000_write_word(state, 0, (u16)((1 << 15) | value));
+ dib8000_read_word(state, 1284); /* reset the INT. n_irq_pending */
+ dib8000_write_word(state, 0, (u16)value);
+ } else {
+ c->inversion = 0;
+ c->layer[0].modulation = QAM_64;
+ c->layer[0].fec = FEC_2_3;
+ c->layer[0].interleaving = 0;
+ c->layer[0].segment_count = 13;
+ if (!c->isdbt_sb_mode)
+ c->layer[0].segment_count = 13;
+
+ /* choose the right list, in sb, always do everything */
+ if (c->isdbt_sb_mode) {
+ slist = 7;
+ dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13));
+ } else {
+ if (c->guard_interval == GUARD_INTERVAL_AUTO) {
+ if (c->transmission_mode == TRANSMISSION_MODE_AUTO) {
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ slist = 7;
+ dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); /* P_mode = 1 to have autosearch start ok with mode2 */
+ } else {
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ slist = 3;
+ }
+ } else {
+ if (c->transmission_mode == TRANSMISSION_MODE_AUTO) {
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ slist = 2;
+ dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); /* P_mode = 1 */
+ } else
+ slist = 0;
+ }
+ }
+ dprintk("Using list for autosearch : %d\n", slist);
+
+ dib8000_set_isdbt_common_channel(state, slist, 1);
+
+ /* set lock_mask values */
+ dib8000_write_word(state, 6, 0x4);
+ if (state->revision == 0x8090)
+ dib8000_write_word(state, 7, (1 << 12) | (1 << 11) | (1 << 10));
+ else
+ dib8000_write_word(state, 7, 0x8);
+ dib8000_write_word(state, 8, 0x1000);
+
+ /* set lock_mask wait time values */
+ if (state->revision == 0x8090)
+ dib8000_wait_lock(state, internal, 50, 200, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */
+ else
+ dib8000_wait_lock(state, internal, 50, 100, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */
+
+ value = dib8000_read_word(state, 0);
+ dib8000_write_word(state, 0, (u16)((1 << 15) | value));
+ dib8000_read_word(state, 1284); /* reset the INT. n_irq_pending */
+ dib8000_write_word(state, 0, (u16)value);
+ }
+ return 0;
+}
+
+static int dib8000_autosearch_irq(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u16 irq_pending = dib8000_read_word(state, 1284);
+
+ if ((state->revision >= 0x8002) &&
+ (state->autosearch_state == AS_SEARCHING_FFT)) {
+ if (irq_pending & 0x1) {
+ dprintk("dib8000_autosearch_irq: max correlation result available\n");
+ return 3;
+ }
+ } else {
+ if (irq_pending & 0x1) { /* failed */
+ dprintk("dib8000_autosearch_irq failed\n");
+ return 1;
+ }
+
+ if (irq_pending & 0x2) { /* succeeded */
+ dprintk("dib8000_autosearch_irq succeeded\n");
+ return 2;
+ }
+ }
+
+ return 0; // still pending
+}
+
+static void dib8000_viterbi_state(struct dib8000_state *state, u8 onoff)
+{
+ u16 tmp;
+
+ tmp = dib8000_read_word(state, 771);
+ if (onoff) /* start P_restart_chd : channel_decoder */
+ dib8000_write_word(state, 771, tmp & 0xfffd);
+ else /* stop P_restart_chd : channel_decoder */
+ dib8000_write_word(state, 771, tmp | (1<<1));
+}
+
+static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz)
+{
+ s16 unit_khz_dds_val;
+ u32 abs_offset_khz = abs(offset_khz);
+ u32 dds = state->cfg.pll->ifreq & 0x1ffffff;
+ u8 invert = !!(state->cfg.pll->ifreq & (1 << 25));
+ u8 ratio;
+
+ if (state->revision == 0x8090) {
+ u32 internal = dib8000_read32(state, 23) / 1000;
+
+ ratio = 4;
+
+ unit_khz_dds_val = (1<<26) / (internal ?: 1);
+ if (offset_khz < 0)
+ dds = (1 << 26) - (abs_offset_khz * unit_khz_dds_val);
+ else
+ dds = (abs_offset_khz * unit_khz_dds_val);
+
+ if (invert)
+ dds = (1<<26) - dds;
+ } else {
+ ratio = 2;
+ unit_khz_dds_val = (u16) (67108864 / state->cfg.pll->internal);
+
+ if (offset_khz < 0)
+ unit_khz_dds_val *= -1;
+
+ /* IF tuner */
+ if (invert)
+ dds -= abs_offset_khz * unit_khz_dds_val;
+ else
+ dds += abs_offset_khz * unit_khz_dds_val;
+ }
+
+ dprintk("setting a DDS frequency offset of %c%dkHz\n", invert ? '-' : ' ', dds / unit_khz_dds_val);
+
+ if (abs_offset_khz <= (state->cfg.pll->internal / ratio)) {
+ /* Max dds offset is the half of the demod freq */
+ dib8000_write_word(state, 26, invert);
+ dib8000_write_word(state, 27, (u16)(dds >> 16) & 0x1ff);
+ dib8000_write_word(state, 28, (u16)(dds & 0xffff));
+ }
+}
+
+static void dib8000_set_frequency_offset(struct dib8000_state *state)
+{
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ int i;
+ u32 current_rf;
+ int total_dds_offset_khz;
+
+ if (state->fe[0]->ops.tuner_ops.get_frequency)
+ state->fe[0]->ops.tuner_ops.get_frequency(state->fe[0], &current_rf);
+ else
+ current_rf = c->frequency;
+ current_rf /= 1000;
+ total_dds_offset_khz = (int)current_rf - (int)c->frequency / 1000;
+
+ if (c->isdbt_sb_mode) {
+ state->subchannel = c->isdbt_sb_subchannel;
+
+ i = dib8000_read_word(state, 26) & 1; /* P_dds_invspec */
+ dib8000_write_word(state, 26, c->inversion ^ i);
+
+ if (state->cfg.pll->ifreq == 0) { /* low if tuner */
+ if ((c->inversion ^ i) == 0)
+ dib8000_write_word(state, 26, dib8000_read_word(state, 26) | 1);
+ } else {
+ if ((c->inversion ^ i) == 0)
+ total_dds_offset_khz *= -1;
+ }
+ }
+
+ dprintk("%dkhz tuner offset (frequency = %dHz & current_rf = %dHz) total_dds_offset_hz = %d\n", c->frequency - current_rf, c->frequency, current_rf, total_dds_offset_khz);
+
+ /* apply dds offset now */
+ dib8000_set_dds(state, total_dds_offset_khz);
+}
+
+static u16 LUT_isdbt_symbol_duration[4] = { 26, 101, 63 };
+
+static u32 dib8000_get_symbol_duration(struct dib8000_state *state)
+{
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ u16 i;
+
+ switch (c->transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ i = 0;
+ break;
+ case TRANSMISSION_MODE_4K:
+ i = 2;
+ break;
+ default:
+ case TRANSMISSION_MODE_AUTO:
+ case TRANSMISSION_MODE_8K:
+ i = 1;
+ break;
+ }
+
+ return (LUT_isdbt_symbol_duration[i] / (c->bandwidth_hz / 1000)) + 1;
+}
+
+static void dib8000_set_isdbt_loop_params(struct dib8000_state *state, enum param_loop_step loop_step)
+{
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ u16 reg_32 = 0, reg_37 = 0;
+
+ switch (loop_step) {
+ case LOOP_TUNE_1:
+ if (c->isdbt_sb_mode) {
+ if (c->isdbt_partial_reception == 0) {
+ reg_32 = ((11 - state->mode) << 12) | (6 << 8) | 0x40; /* P_timf_alpha = (11-P_mode), P_corm_alpha=6, P_corm_thres=0x40 */
+ reg_37 = (3 << 5) | (0 << 4) | (10 - state->mode); /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (10-P_mode) */
+ } else { /* Sound Broadcasting mode 3 seg */
+ reg_32 = ((10 - state->mode) << 12) | (6 << 8) | 0x60; /* P_timf_alpha = (10-P_mode), P_corm_alpha=6, P_corm_thres=0x60 */
+ reg_37 = (3 << 5) | (0 << 4) | (9 - state->mode); /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (9-P_mode) */
+ }
+ } else { /* 13-seg start conf offset loop parameters */
+ reg_32 = ((9 - state->mode) << 12) | (6 << 8) | 0x80; /* P_timf_alpha = (9-P_mode, P_corm_alpha=6, P_corm_thres=0x80 */
+ reg_37 = (3 << 5) | (0 << 4) | (8 - state->mode); /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = 9 */
+ }
+ break;
+ case LOOP_TUNE_2:
+ if (c->isdbt_sb_mode) {
+ if (c->isdbt_partial_reception == 0) { /* Sound Broadcasting mode 1 seg */
+ reg_32 = ((13-state->mode) << 12) | (6 << 8) | 0x40; /* P_timf_alpha = (13-P_mode) , P_corm_alpha=6, P_corm_thres=0x40*/
+ reg_37 = (12-state->mode) | ((5 + state->mode) << 5);
+ } else { /* Sound Broadcasting mode 3 seg */
+ reg_32 = ((12-state->mode) << 12) | (6 << 8) | 0x60; /* P_timf_alpha = (12-P_mode) , P_corm_alpha=6, P_corm_thres=0x60 */
+ reg_37 = (11-state->mode) | ((5 + state->mode) << 5);
+ }
+ } else { /* 13 seg */
+ reg_32 = ((11-state->mode) << 12) | (6 << 8) | 0x80; /* P_timf_alpha = 8 , P_corm_alpha=6, P_corm_thres=0x80 */
+ reg_37 = ((5+state->mode) << 5) | (10 - state->mode);
+ }
+ break;
+ }
+ dib8000_write_word(state, 32, reg_32);
+ dib8000_write_word(state, 37, reg_37);
+}
+
+static void dib8000_demod_restart(struct dib8000_state *state)
+{
+ dib8000_write_word(state, 770, 0x4000);
+ dib8000_write_word(state, 770, 0x0000);
+ return;
+}
+
+static void dib8000_set_sync_wait(struct dib8000_state *state)
+{
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ u16 sync_wait = 64;
+
+ /* P_dvsy_sync_wait - reuse mode */
+ switch (c->transmission_mode) {
+ case TRANSMISSION_MODE_8K:
+ sync_wait = 256;
+ break;
+ case TRANSMISSION_MODE_4K:
+ sync_wait = 128;
+ break;
+ default:
+ case TRANSMISSION_MODE_2K:
+ sync_wait = 64;
+ break;
+ }
+
+ if (state->cfg.diversity_delay == 0)
+ sync_wait = (sync_wait * (1 << (c->guard_interval)) * 3) / 2 + 48; /* add 50% SFN margin + compensate for one DVSY-fifo */
+ else
+ sync_wait = (sync_wait * (1 << (c->guard_interval)) * 3) / 2 + state->cfg.diversity_delay; /* add 50% SFN margin + compensate for DVSY-fifo */
+
+ dib8000_write_word(state, 273, (dib8000_read_word(state, 273) & 0x000f) | (sync_wait << 4));
+}
+
+static unsigned long dib8000_get_timeout(struct dib8000_state *state, u32 delay, enum timeout_mode mode)
+{
+ if (mode == SYMBOL_DEPENDENT_ON)
+ delay *= state->symbol_duration;
+
+ return jiffies + usecs_to_jiffies(delay * 100);
+}
+
+static s32 dib8000_get_status(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ return state->status;
+}
+
+static enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ return state->tune_state;
+}
+
+static int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+
+ state->tune_state = tune_state;
+ return 0;
+}
+
+static int dib8000_tune_restart_from_demod(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+
+ state->status = FE_STATUS_TUNE_PENDING;
+ state->tune_state = CT_DEMOD_START;
+ return 0;
+}
+
+static u16 dib8000_read_lock(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+
+ if (state->revision == 0x8090)
+ return dib8000_read_word(state, 570);
+ return dib8000_read_word(state, 568);
+}
+
+static int dib8090p_init_sdram(struct dib8000_state *state)
+{
+ u16 reg = 0;
+ dprintk("init sdram\n");
+
+ reg = dib8000_read_word(state, 274) & 0xfff0;
+ dib8000_write_word(state, 274, reg | 0x7); /* P_dintlv_delay_ram = 7 because of MobileSdram */
+
+ dib8000_write_word(state, 1803, (7 << 2));
+
+ reg = dib8000_read_word(state, 1280);
+ dib8000_write_word(state, 1280, reg | (1 << 2)); /* force restart P_restart_sdram */
+ dib8000_write_word(state, 1280, reg); /* release restart P_restart_sdram */
+
+ return 0;
+}
+
+/**
+ * is_manual_mode - Check if TMCC should be used for parameters settings
+ * @c: struct dvb_frontend_properties
+ *
+ * By default, TMCC table should be used for parameter settings on most
+ * usercases. However, sometimes it is desirable to lock the demod to
+ * use the manual parameters.
+ *
+ * On manual mode, the current dib8000_tune state machine is very restrict:
+ * It requires that both per-layer and per-transponder parameters to be
+ * properly specified, otherwise the device won't lock.
+ *
+ * Check if all those conditions are properly satisfied before allowing
+ * the device to use the manual frequency lock mode.
+ */
+static int is_manual_mode(struct dtv_frontend_properties *c)
+{
+ int i, n_segs = 0;
+
+ /* Use auto mode on DVB-T compat mode */
+ if (c->delivery_system != SYS_ISDBT)
+ return 0;
+
+ /*
+ * Transmission mode is only detected on auto mode, currently
+ */
+ if (c->transmission_mode == TRANSMISSION_MODE_AUTO) {
+ dprintk("transmission mode auto\n");
+ return 0;
+ }
+
+ /*
+ * Guard interval is only detected on auto mode, currently
+ */
+ if (c->guard_interval == GUARD_INTERVAL_AUTO) {
+ dprintk("guard interval auto\n");
+ return 0;
+ }
+
+ /*
+ * If no layer is enabled, assume auto mode, as at least one
+ * layer should be enabled
+ */
+ if (!c->isdbt_layer_enabled) {
+ dprintk("no layer modulation specified\n");
+ return 0;
+ }
+
+ /*
+ * Check if the per-layer parameters aren't auto and
+ * disable a layer if segment count is 0 or invalid.
+ */
+ for (i = 0; i < 3; i++) {
+ if (!(c->isdbt_layer_enabled & 1 << i))
+ continue;
+
+ if ((c->layer[i].segment_count > 13) ||
+ (c->layer[i].segment_count == 0)) {
+ c->isdbt_layer_enabled &= ~(1 << i);
+ continue;
+ }
+
+ n_segs += c->layer[i].segment_count;
+
+ if ((c->layer[i].modulation == QAM_AUTO) ||
+ (c->layer[i].fec == FEC_AUTO)) {
+ dprintk("layer %c has either modulation or FEC auto\n",
+ 'A' + i);
+ return 0;
+ }
+ }
+
+ /*
+ * Userspace specified a wrong number of segments.
+ * fallback to auto mode.
+ */
+ if (n_segs == 0 || n_segs > 13) {
+ dprintk("number of segments is invalid\n");
+ return 0;
+ }
+
+ /* Everything looks ok for manual mode */
+ return 1;
+}
+
+static int dib8000_tune(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ enum frontend_tune_state *tune_state = &state->tune_state;
+
+ u16 locks, deeper_interleaver = 0, i;
+ int ret = 1; /* 1 symbol duration (in 100us unit) delay most of the time */
+
+ unsigned long *timeout = &state->timeout;
+ unsigned long now = jiffies;
+ u16 init_prbs;
+#ifdef DIB8000_AGC_FREEZE
+ u16 agc1, agc2;
+#endif
+
+ u32 corm[4] = {0, 0, 0, 0};
+ u8 find_index, max_value;
+
+#if 0
+ if (*tune_state < CT_DEMOD_STOP)
+ dprintk("IN: context status = %d, TUNE_STATE %d autosearch step = %u jiffies = %lu\n",
+ state->channel_parameters_set, *tune_state, state->autosearch_state, now);
+#endif
+
+ switch (*tune_state) {
+ case CT_DEMOD_START: /* 30 */
+ dib8000_reset_stats(fe);
+
+ if (state->revision == 0x8090)
+ dib8090p_init_sdram(state);
+ state->status = FE_STATUS_TUNE_PENDING;
+ state->channel_parameters_set = is_manual_mode(c);
+
+ dprintk("Tuning channel on %s search mode\n",
+ state->channel_parameters_set ? "manual" : "auto");
+
+ dib8000_viterbi_state(state, 0); /* force chan dec in restart */
+
+ /* Layer monitor */
+ dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60);
+
+ dib8000_set_frequency_offset(state);
+ dib8000_set_bandwidth(fe, c->bandwidth_hz / 1000);
+
+ if (state->channel_parameters_set == 0) { /* The channel struct is unknown, search it ! */
+#ifdef DIB8000_AGC_FREEZE
+ if (state->revision != 0x8090) {
+ state->agc1_max = dib8000_read_word(state, 108);
+ state->agc1_min = dib8000_read_word(state, 109);
+ state->agc2_max = dib8000_read_word(state, 110);
+ state->agc2_min = dib8000_read_word(state, 111);
+ agc1 = dib8000_read_word(state, 388);
+ agc2 = dib8000_read_word(state, 389);
+ dib8000_write_word(state, 108, agc1);
+ dib8000_write_word(state, 109, agc1);
+ dib8000_write_word(state, 110, agc2);
+ dib8000_write_word(state, 111, agc2);
+ }
+#endif
+ state->autosearch_state = AS_SEARCHING_FFT;
+ state->found_nfft = TRANSMISSION_MODE_AUTO;
+ state->found_guard = GUARD_INTERVAL_AUTO;
+ *tune_state = CT_DEMOD_SEARCH_NEXT;
+ } else { /* we already know the channel struct so TUNE only ! */
+ state->autosearch_state = AS_DONE;
+ *tune_state = CT_DEMOD_STEP_3;
+ }
+ state->symbol_duration = dib8000_get_symbol_duration(state);
+ break;
+
+ case CT_DEMOD_SEARCH_NEXT: /* 51 */
+ dib8000_autosearch_start(fe);
+ if (state->revision == 0x8090)
+ ret = 50;
+ else
+ ret = 15;
+ *tune_state = CT_DEMOD_STEP_1;
+ break;
+
+ case CT_DEMOD_STEP_1: /* 31 */
+ switch (dib8000_autosearch_irq(fe)) {
+ case 1: /* fail */
+ state->status = FE_STATUS_TUNE_FAILED;
+ state->autosearch_state = AS_DONE;
+ *tune_state = CT_DEMOD_STOP; /* else we are done here */
+ break;
+ case 2: /* Success */
+ state->status = FE_STATUS_FFT_SUCCESS; /* signal to the upper layer, that there was a channel found and the parameters can be read */
+ *tune_state = CT_DEMOD_STEP_3;
+ if (state->autosearch_state == AS_SEARCHING_GUARD)
+ *tune_state = CT_DEMOD_STEP_2;
+ else
+ state->autosearch_state = AS_DONE;
+ break;
+ case 3: /* Autosearch FFT max correlation endded */
+ *tune_state = CT_DEMOD_STEP_2;
+ break;
+ }
+ break;
+
+ case CT_DEMOD_STEP_2:
+ switch (state->autosearch_state) {
+ case AS_SEARCHING_FFT:
+ /* searching for the correct FFT */
+ if (state->revision == 0x8090) {
+ corm[2] = (dib8000_read_word(state, 596) << 16) | (dib8000_read_word(state, 597));
+ corm[1] = (dib8000_read_word(state, 598) << 16) | (dib8000_read_word(state, 599));
+ corm[0] = (dib8000_read_word(state, 600) << 16) | (dib8000_read_word(state, 601));
+ } else {
+ corm[2] = (dib8000_read_word(state, 594) << 16) | (dib8000_read_word(state, 595));
+ corm[1] = (dib8000_read_word(state, 596) << 16) | (dib8000_read_word(state, 597));
+ corm[0] = (dib8000_read_word(state, 598) << 16) | (dib8000_read_word(state, 599));
+ }
+ /* dprintk("corm fft: %u %u %u\n", corm[0], corm[1], corm[2]); */
+
+ max_value = 0;
+ for (find_index = 1 ; find_index < 3 ; find_index++) {
+ if (corm[max_value] < corm[find_index])
+ max_value = find_index ;
+ }
+
+ switch (max_value) {
+ case 0:
+ state->found_nfft = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ state->found_nfft = TRANSMISSION_MODE_4K;
+ break;
+ case 2:
+ default:
+ state->found_nfft = TRANSMISSION_MODE_8K;
+ break;
+ }
+ /* dprintk("Autosearch FFT has found Mode %d\n", max_value + 1); */
+
+ *tune_state = CT_DEMOD_SEARCH_NEXT;
+ state->autosearch_state = AS_SEARCHING_GUARD;
+ if (state->revision == 0x8090)
+ ret = 50;
+ else
+ ret = 10;
+ break;
+ case AS_SEARCHING_GUARD:
+ /* searching for the correct guard interval */
+ if (state->revision == 0x8090)
+ state->found_guard = dib8000_read_word(state, 572) & 0x3;
+ else
+ state->found_guard = dib8000_read_word(state, 570) & 0x3;
+ /* dprintk("guard interval found=%i\n", state->found_guard); */
+
+ *tune_state = CT_DEMOD_STEP_3;
+ break;
+ default:
+ /* the demod should never be in this state */
+ state->status = FE_STATUS_TUNE_FAILED;
+ state->autosearch_state = AS_DONE;
+ *tune_state = CT_DEMOD_STOP; /* else we are done here */
+ break;
+ }
+ break;
+
+ case CT_DEMOD_STEP_3: /* 33 */
+ dib8000_set_isdbt_loop_params(state, LOOP_TUNE_1);
+ dib8000_set_isdbt_common_channel(state, 0, 0);/* setting the known channel parameters here */
+ *tune_state = CT_DEMOD_STEP_4;
+ break;
+
+ case CT_DEMOD_STEP_4: /* (34) */
+ dib8000_demod_restart(state);
+
+ dib8000_set_sync_wait(state);
+ dib8000_set_diversity_in(state->fe[0], state->diversity_onoff);
+
+ locks = (dib8000_read_word(state, 180) >> 6) & 0x3f; /* P_coff_winlen ? */
+ /* coff should lock over P_coff_winlen ofdm symbols : give 3 times this length to lock */
+ *timeout = dib8000_get_timeout(state, 2 * locks, SYMBOL_DEPENDENT_ON);
+ *tune_state = CT_DEMOD_STEP_5;
+ break;
+
+ case CT_DEMOD_STEP_5: /* (35) */
+ locks = dib8000_read_lock(fe);
+ if (locks & (0x3 << 11)) { /* coff-lock and off_cpil_lock achieved */
+ dib8000_update_timf(state); /* we achieved a coff_cpil_lock - it's time to update the timf */
+ if (!state->differential_constellation) {
+ /* 2 times lmod4_win_len + 10 symbols (pipe delay after coff + nb to compute a 1st correlation) */
+ *timeout = dib8000_get_timeout(state, (20 * ((dib8000_read_word(state, 188)>>5)&0x1f)), SYMBOL_DEPENDENT_ON);
+ *tune_state = CT_DEMOD_STEP_7;
+ } else {
+ *tune_state = CT_DEMOD_STEP_8;
+ }
+ } else if (time_after(now, *timeout)) {
+ *tune_state = CT_DEMOD_STEP_6; /* goto check for diversity input connection */
+ }
+ break;
+
+ case CT_DEMOD_STEP_6: /* (36) if there is an input (diversity) */
+ if ((state->fe[1] != NULL) && (state->output_mode != OUTMODE_DIVERSITY)) {
+ /* if there is a diversity fe in input and this fe is has not already failed : wait here until this fe has succeeded or failed */
+ if (dib8000_get_status(state->fe[1]) <= FE_STATUS_STD_SUCCESS) /* Something is locked on the input fe */
+ *tune_state = CT_DEMOD_STEP_8; /* go for mpeg */
+ else if (dib8000_get_status(state->fe[1]) >= FE_STATUS_TUNE_TIME_TOO_SHORT) { /* fe in input failed also, break the current one */
+ *tune_state = CT_DEMOD_STOP; /* else we are done here ; step 8 will close the loops and exit */
+ dib8000_viterbi_state(state, 1); /* start viterbi chandec */
+ dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2);
+ state->status = FE_STATUS_TUNE_FAILED;
+ }
+ } else {
+ dib8000_viterbi_state(state, 1); /* start viterbi chandec */
+ dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2);
+ *tune_state = CT_DEMOD_STOP; /* else we are done here ; step 8 will close the loops and exit */
+ state->status = FE_STATUS_TUNE_FAILED;
+ }
+ break;
+
+ case CT_DEMOD_STEP_7: /* 37 */
+ locks = dib8000_read_lock(fe);
+ if (locks & (1<<10)) { /* lmod4_lock */
+ ret = 14; /* wait for 14 symbols */
+ *tune_state = CT_DEMOD_STEP_8;
+ } else if (time_after(now, *timeout))
+ *tune_state = CT_DEMOD_STEP_6; /* goto check for diversity input connection */
+ break;
+
+ case CT_DEMOD_STEP_8: /* 38 */
+ dib8000_viterbi_state(state, 1); /* start viterbi chandec */
+ dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2);
+
+ /* mpeg will never lock on this condition because init_prbs is not set : search for it !*/
+ if (c->isdbt_sb_mode
+ && c->isdbt_sb_subchannel < 14
+ && !state->differential_constellation) {
+ state->subchannel = 0;
+ *tune_state = CT_DEMOD_STEP_11;
+ } else {
+ *tune_state = CT_DEMOD_STEP_9;
+ state->status = FE_STATUS_LOCKED;
+ }
+ break;
+
+ case CT_DEMOD_STEP_9: /* 39 */
+ if ((state->revision == 0x8090) || ((dib8000_read_word(state, 1291) >> 9) & 0x1)) { /* fe capable of deinterleaving : esram */
+ /* defines timeout for mpeg lock depending on interleaver length of longest layer */
+ for (i = 0; i < 3; i++) {
+ if (c->layer[i].interleaving >= deeper_interleaver) {
+ dprintk("layer%i: time interleaver = %d\n", i, c->layer[i].interleaving);
+ if (c->layer[i].segment_count > 0) { /* valid layer */
+ deeper_interleaver = c->layer[0].interleaving;
+ state->longest_intlv_layer = i;
+ }
+ }
+ }
+
+ if (deeper_interleaver == 0)
+ locks = 2; /* locks is the tmp local variable name */
+ else if (deeper_interleaver == 3)
+ locks = 8;
+ else
+ locks = 2 * deeper_interleaver;
+
+ if (state->diversity_onoff != 0) /* because of diversity sync */
+ locks *= 2;
+
+ *timeout = now + msecs_to_jiffies(200 * locks); /* give the mpeg lock 800ms if sram is present */
+ dprintk("Deeper interleaver mode = %d on layer %d : timeout mult factor = %d => will use timeout = %ld\n",
+ deeper_interleaver, state->longest_intlv_layer, locks, *timeout);
+
+ *tune_state = CT_DEMOD_STEP_10;
+ } else
+ *tune_state = CT_DEMOD_STOP;
+ break;
+
+ case CT_DEMOD_STEP_10: /* 40 */
+ locks = dib8000_read_lock(fe);
+ if (locks&(1<<(7-state->longest_intlv_layer))) { /* mpeg lock : check the longest one */
+ dprintk("ISDB-T layer locks: Layer A %s, Layer B %s, Layer C %s\n",
+ c->layer[0].segment_count ? (locks >> 7) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled",
+ c->layer[1].segment_count ? (locks >> 6) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled",
+ c->layer[2].segment_count ? (locks >> 5) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled");
+ if (c->isdbt_sb_mode
+ && c->isdbt_sb_subchannel < 14
+ && !state->differential_constellation)
+ /* signal to the upper layer, that there was a channel found and the parameters can be read */
+ state->status = FE_STATUS_DEMOD_SUCCESS;
+ else
+ state->status = FE_STATUS_DATA_LOCKED;
+ *tune_state = CT_DEMOD_STOP;
+ } else if (time_after(now, *timeout)) {
+ if (c->isdbt_sb_mode
+ && c->isdbt_sb_subchannel < 14
+ && !state->differential_constellation) { /* continue to try init prbs autosearch */
+ state->subchannel += 3;
+ *tune_state = CT_DEMOD_STEP_11;
+ } else { /* we are done mpeg of the longest interleaver xas not locking but let's try if an other layer has locked in the same time */
+ if (locks & (0x7 << 5)) {
+ dprintk("Not all ISDB-T layers locked in %d ms: Layer A %s, Layer B %s, Layer C %s\n",
+ jiffies_to_msecs(now - *timeout),
+ c->layer[0].segment_count ? (locks >> 7) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled",
+ c->layer[1].segment_count ? (locks >> 6) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled",
+ c->layer[2].segment_count ? (locks >> 5) & 0x1 ? "locked" : "NOT LOCKED" : "not enabled");
+
+ state->status = FE_STATUS_DATA_LOCKED;
+ } else
+ state->status = FE_STATUS_TUNE_FAILED;
+ *tune_state = CT_DEMOD_STOP;
+ }
+ }
+ break;
+
+ case CT_DEMOD_STEP_11: /* 41 : init prbs autosearch */
+ init_prbs = dib8000_get_init_prbs(state, state->subchannel);
+
+ if (init_prbs) {
+ dib8000_set_subchannel_prbs(state, init_prbs);
+ *tune_state = CT_DEMOD_STEP_9;
+ } else {
+ *tune_state = CT_DEMOD_STOP;
+ state->status = FE_STATUS_TUNE_FAILED;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* tuning is finished - cleanup the demod */
+ switch (*tune_state) {
+ case CT_DEMOD_STOP: /* (42) */
+#ifdef DIB8000_AGC_FREEZE
+ if ((state->revision != 0x8090) && (state->agc1_max != 0)) {
+ dib8000_write_word(state, 108, state->agc1_max);
+ dib8000_write_word(state, 109, state->agc1_min);
+ dib8000_write_word(state, 110, state->agc2_max);
+ dib8000_write_word(state, 111, state->agc2_min);
+ state->agc1_max = 0;
+ state->agc1_min = 0;
+ state->agc2_max = 0;
+ state->agc2_min = 0;
+ }
+#endif
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ if ((ret > 0) && (*tune_state > CT_DEMOD_STEP_3))
+ return ret * state->symbol_duration;
+ if ((ret > 0) && (ret < state->symbol_duration))
+ return state->symbol_duration; /* at least one symbol */
+ return ret;
+}
+
+static int dib8000_wakeup(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u8 index_frontend;
+ int ret;
+
+ dib8000_set_power_mode(state, DIB8000_POWER_ALL);
+ dib8000_set_adc_state(state, DIBX000_ADC_ON);
+ if (dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0)
+ dprintk("could not start Slow ADC\n");
+
+ if (state->revision == 0x8090)
+ dib8000_sad_calib(state);
+
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ ret = state->fe[index_frontend]->ops.init(state->fe[index_frontend]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dib8000_sleep(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u8 index_frontend;
+ int ret;
+
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (state->revision != 0x8090)
+ dib8000_set_output_mode(fe, OUTMODE_HIGH_Z);
+ dib8000_set_power_mode(state, DIB8000_POWER_INTERFACE_ONLY);
+ return dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF) | dib8000_set_adc_state(state, DIBX000_ADC_OFF);
+}
+
+static int dib8000_read_status(struct dvb_frontend *fe, enum fe_status *stat);
+
+static int dib8000_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u16 i, val = 0;
+ enum fe_status stat = 0;
+ u8 index_frontend, sub_index_frontend;
+
+ c->bandwidth_hz = 6000000;
+
+ /*
+ * If called to early, get_frontend makes dib8000_tune to either
+ * not lock or not sync. This causes dvbv5-scan/dvbv5-zap to fail.
+ * So, let's just return if frontend 0 has not locked.
+ */
+ dib8000_read_status(fe, &stat);
+ if (!(stat & FE_HAS_SYNC))
+ return 0;
+
+ dprintk("dib8000_get_frontend: TMCC lock\n");
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat);
+ if (stat&FE_HAS_SYNC) {
+ dprintk("TMCC lock on the slave%i\n", index_frontend);
+ /* synchronize the cache with the other frontends */
+ state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend], c);
+ for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); sub_index_frontend++) {
+ if (sub_index_frontend != index_frontend) {
+ state->fe[sub_index_frontend]->dtv_property_cache.isdbt_sb_mode = state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode;
+ state->fe[sub_index_frontend]->dtv_property_cache.inversion = state->fe[index_frontend]->dtv_property_cache.inversion;
+ state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode = state->fe[index_frontend]->dtv_property_cache.transmission_mode;
+ state->fe[sub_index_frontend]->dtv_property_cache.guard_interval = state->fe[index_frontend]->dtv_property_cache.guard_interval;
+ state->fe[sub_index_frontend]->dtv_property_cache.isdbt_partial_reception = state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception;
+ for (i = 0; i < 3; i++) {
+ state->fe[sub_index_frontend]->dtv_property_cache.layer[i].segment_count = state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count;
+ state->fe[sub_index_frontend]->dtv_property_cache.layer[i].interleaving = state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving;
+ state->fe[sub_index_frontend]->dtv_property_cache.layer[i].fec = state->fe[index_frontend]->dtv_property_cache.layer[i].fec;
+ state->fe[sub_index_frontend]->dtv_property_cache.layer[i].modulation = state->fe[index_frontend]->dtv_property_cache.layer[i].modulation;
+ }
+ }
+ }
+ return 0;
+ }
+ }
+
+ c->isdbt_sb_mode = dib8000_read_word(state, 508) & 0x1;
+
+ if (state->revision == 0x8090)
+ val = dib8000_read_word(state, 572);
+ else
+ val = dib8000_read_word(state, 570);
+ c->inversion = (val & 0x40) >> 6;
+ switch ((val & 0x30) >> 4) {
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ dprintk("dib8000_get_frontend: transmission mode 2K\n");
+ break;
+ case 2:
+ c->transmission_mode = TRANSMISSION_MODE_4K;
+ dprintk("dib8000_get_frontend: transmission mode 4K\n");
+ break;
+ case 3:
+ default:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ dprintk("dib8000_get_frontend: transmission mode 8K\n");
+ break;
+ }
+
+ switch (val & 0x3) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ dprintk("dib8000_get_frontend: Guard Interval = 1/32\n");
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ dprintk("dib8000_get_frontend: Guard Interval = 1/16\n");
+ break;
+ case 2:
+ dprintk("dib8000_get_frontend: Guard Interval = 1/8\n");
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ dprintk("dib8000_get_frontend: Guard Interval = 1/4\n");
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ val = dib8000_read_word(state, 505);
+ c->isdbt_partial_reception = val & 1;
+ dprintk("dib8000_get_frontend: partial_reception = %d\n", c->isdbt_partial_reception);
+
+ for (i = 0; i < 3; i++) {
+ int show;
+
+ val = dib8000_read_word(state, 493 + i) & 0x0f;
+ c->layer[i].segment_count = val;
+
+ if (val == 0 || val > 13)
+ show = 0;
+ else
+ show = 1;
+
+ if (show)
+ dprintk("dib8000_get_frontend: Layer %d segments = %d\n",
+ i, c->layer[i].segment_count);
+
+ val = dib8000_read_word(state, 499 + i) & 0x3;
+ /* Interleaving can be 0, 1, 2 or 4 */
+ if (val == 3)
+ val = 4;
+ c->layer[i].interleaving = val;
+ if (show)
+ dprintk("dib8000_get_frontend: Layer %d time_intlv = %d\n",
+ i, c->layer[i].interleaving);
+
+ val = dib8000_read_word(state, 481 + i);
+ switch (val & 0x7) {
+ case 1:
+ c->layer[i].fec = FEC_1_2;
+ if (show)
+ dprintk("dib8000_get_frontend: Layer %d Code Rate = 1/2\n", i);
+ break;
+ case 2:
+ c->layer[i].fec = FEC_2_3;
+ if (show)
+ dprintk("dib8000_get_frontend: Layer %d Code Rate = 2/3\n", i);
+ break;
+ case 3:
+ c->layer[i].fec = FEC_3_4;
+ if (show)
+ dprintk("dib8000_get_frontend: Layer %d Code Rate = 3/4\n", i);
+ break;
+ case 5:
+ c->layer[i].fec = FEC_5_6;
+ if (show)
+ dprintk("dib8000_get_frontend: Layer %d Code Rate = 5/6\n", i);
+ break;
+ default:
+ c->layer[i].fec = FEC_7_8;
+ if (show)
+ dprintk("dib8000_get_frontend: Layer %d Code Rate = 7/8\n", i);
+ break;
+ }
+
+ val = dib8000_read_word(state, 487 + i);
+ switch (val & 0x3) {
+ case 0:
+ c->layer[i].modulation = DQPSK;
+ if (show)
+ dprintk("dib8000_get_frontend: Layer %d DQPSK\n", i);
+ break;
+ case 1:
+ c->layer[i].modulation = QPSK;
+ if (show)
+ dprintk("dib8000_get_frontend: Layer %d QPSK\n", i);
+ break;
+ case 2:
+ c->layer[i].modulation = QAM_16;
+ if (show)
+ dprintk("dib8000_get_frontend: Layer %d QAM16\n", i);
+ break;
+ case 3:
+ default:
+ c->layer[i].modulation = QAM_64;
+ if (show)
+ dprintk("dib8000_get_frontend: Layer %d QAM64\n", i);
+ break;
+ }
+ }
+
+ /* synchronize the cache with the other frontends */
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode = c->isdbt_sb_mode;
+ state->fe[index_frontend]->dtv_property_cache.inversion = c->inversion;
+ state->fe[index_frontend]->dtv_property_cache.transmission_mode = c->transmission_mode;
+ state->fe[index_frontend]->dtv_property_cache.guard_interval = c->guard_interval;
+ state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception = c->isdbt_partial_reception;
+ for (i = 0; i < 3; i++) {
+ state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count = c->layer[i].segment_count;
+ state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving = c->layer[i].interleaving;
+ state->fe[index_frontend]->dtv_property_cache.layer[i].fec = c->layer[i].fec;
+ state->fe[index_frontend]->dtv_property_cache.layer[i].modulation = c->layer[i].modulation;
+ }
+ }
+ return 0;
+}
+
+static int dib8000_set_frontend(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ int l, i, active, time, time_slave = 0;
+ u8 exit_condition, index_frontend;
+ unsigned long delay, callback_time;
+
+ if (c->frequency == 0) {
+ dprintk("dib8000: must at least specify frequency\n");
+ return 0;
+ }
+
+ if (c->bandwidth_hz == 0) {
+ dprintk("dib8000: no bandwidth specified, set to default\n");
+ c->bandwidth_hz = 6000000;
+ }
+
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ /* synchronization of the cache */
+ state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_ISDBT;
+ memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties));
+
+ /* set output mode and diversity input */
+ if (state->revision != 0x8090) {
+ dib8000_set_diversity_in(state->fe[index_frontend], 1);
+ if (index_frontend != 0)
+ dib8000_set_output_mode(state->fe[index_frontend],
+ OUTMODE_DIVERSITY);
+ else
+ dib8000_set_output_mode(state->fe[0], OUTMODE_HIGH_Z);
+ } else {
+ dib8096p_set_diversity_in(state->fe[index_frontend], 1);
+ if (index_frontend != 0)
+ dib8096p_set_output_mode(state->fe[index_frontend],
+ OUTMODE_DIVERSITY);
+ else
+ dib8096p_set_output_mode(state->fe[0], OUTMODE_HIGH_Z);
+ }
+
+ /* tune the tuner */
+ if (state->fe[index_frontend]->ops.tuner_ops.set_params)
+ state->fe[index_frontend]->ops.tuner_ops.set_params(state->fe[index_frontend]);
+
+ dib8000_set_tune_state(state->fe[index_frontend], CT_AGC_START);
+ }
+
+ /* turn off the diversity of the last chip */
+ if (state->revision != 0x8090)
+ dib8000_set_diversity_in(state->fe[index_frontend - 1], 0);
+ else
+ dib8096p_set_diversity_in(state->fe[index_frontend - 1], 0);
+
+ /* start up the AGC */
+ do {
+ time = dib8000_agc_startup(state->fe[0]);
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ time_slave = dib8000_agc_startup(state->fe[index_frontend]);
+ if (time == 0)
+ time = time_slave;
+ else if ((time_slave != 0) && (time_slave > time))
+ time = time_slave;
+ }
+ if (time == 0)
+ break;
+
+ /*
+ * Despite dib8000_agc_startup returns time at a 0.1 ms range,
+ * the actual sleep time depends on CONFIG_HZ. The worse case
+ * is when CONFIG_HZ=100. In such case, the minimum granularity
+ * is 10ms. On some real field tests, the tuner sometimes don't
+ * lock when this timer is lower than 10ms. So, enforce a 10ms
+ * granularity.
+ */
+ time = 10 * (time + 99)/100;
+ usleep_range(time * 1000, (time + 1) * 1000);
+ exit_condition = 1;
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ if (dib8000_get_tune_state(state->fe[index_frontend]) != CT_AGC_STOP) {
+ exit_condition = 0;
+ break;
+ }
+ }
+ } while (exit_condition == 0);
+
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
+ dib8000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START);
+
+ active = 1;
+ do {
+ callback_time = 0;
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ delay = dib8000_tune(state->fe[index_frontend]);
+ if (delay != 0) {
+ delay = jiffies + usecs_to_jiffies(100 * delay);
+ if (!callback_time || delay < callback_time)
+ callback_time = delay;
+ }
+
+ /* we are in autosearch */
+ if (state->channel_parameters_set == 0) { /* searching */
+ if ((dib8000_get_status(state->fe[index_frontend]) == FE_STATUS_DEMOD_SUCCESS) || (dib8000_get_status(state->fe[index_frontend]) == FE_STATUS_FFT_SUCCESS)) {
+ dprintk("autosearch succeeded on fe%i\n", index_frontend);
+ dib8000_get_frontend(state->fe[index_frontend], c); /* we read the channel parameters from the frontend which was successful */
+ state->channel_parameters_set = 1;
+
+ for (l = 0; (l < MAX_NUMBER_OF_FRONTENDS) && (state->fe[l] != NULL); l++) {
+ if (l != index_frontend) { /* and for all frontend except the successful one */
+ dprintk("Restarting frontend %d\n", l);
+ dib8000_tune_restart_from_demod(state->fe[l]);
+
+ state->fe[l]->dtv_property_cache.isdbt_sb_mode = state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode;
+ state->fe[l]->dtv_property_cache.inversion = state->fe[index_frontend]->dtv_property_cache.inversion;
+ state->fe[l]->dtv_property_cache.transmission_mode = state->fe[index_frontend]->dtv_property_cache.transmission_mode;
+ state->fe[l]->dtv_property_cache.guard_interval = state->fe[index_frontend]->dtv_property_cache.guard_interval;
+ state->fe[l]->dtv_property_cache.isdbt_partial_reception = state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception;
+ for (i = 0; i < 3; i++) {
+ state->fe[l]->dtv_property_cache.layer[i].segment_count = state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count;
+ state->fe[l]->dtv_property_cache.layer[i].interleaving = state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving;
+ state->fe[l]->dtv_property_cache.layer[i].fec = state->fe[index_frontend]->dtv_property_cache.layer[i].fec;
+ state->fe[l]->dtv_property_cache.layer[i].modulation = state->fe[index_frontend]->dtv_property_cache.layer[i].modulation;
+ }
+
+ }
+ }
+ }
+ }
+ }
+ /* tuning is done when the master frontend is done (failed or success) */
+ if (dib8000_get_status(state->fe[0]) == FE_STATUS_TUNE_FAILED ||
+ dib8000_get_status(state->fe[0]) == FE_STATUS_LOCKED ||
+ dib8000_get_status(state->fe[0]) == FE_STATUS_DATA_LOCKED) {
+ active = 0;
+ /* we need to wait for all frontends to be finished */
+ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ if (dib8000_get_tune_state(state->fe[index_frontend]) != CT_DEMOD_STOP)
+ active = 1;
+ }
+ if (active == 0)
+ dprintk("tuning done with status %d\n", dib8000_get_status(state->fe[0]));
+ }
+
+ if ((active == 1) && (callback_time == 0)) {
+ dprintk("strange callback time something went wrong\n");
+ active = 0;
+ }
+
+ while ((active == 1) && (time_before(jiffies, callback_time)))
+ msleep(100);
+ } while (active);
+
+ /* set output mode */
+ if (state->revision != 0x8090)
+ dib8000_set_output_mode(state->fe[0], state->cfg.output_mode);
+ else {
+ dib8096p_set_output_mode(state->fe[0], state->cfg.output_mode);
+ if (state->cfg.enMpegOutput == 0) {
+ dib8096p_setDibTxMux(state, MPEG_ON_DIBTX);
+ dib8096p_setHostBusMux(state, DIBTX_ON_HOSTBUS);
+ }
+ }
+
+ return 0;
+}
+
+static int dib8000_get_stats(struct dvb_frontend *fe, enum fe_status stat);
+
+static int dib8000_read_status(struct dvb_frontend *fe, enum fe_status *stat)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u16 lock_slave = 0, lock;
+ u8 index_frontend;
+
+ lock = dib8000_read_lock(fe);
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
+ lock_slave |= dib8000_read_lock(state->fe[index_frontend]);
+
+ *stat = 0;
+
+ if (((lock >> 13) & 1) || ((lock_slave >> 13) & 1))
+ *stat |= FE_HAS_SIGNAL;
+
+ if (((lock >> 8) & 1) || ((lock_slave >> 8) & 1)) /* Equal */
+ *stat |= FE_HAS_CARRIER;
+
+ if ((((lock >> 1) & 0xf) == 0xf) || (((lock_slave >> 1) & 0xf) == 0xf)) /* TMCC_SYNC */
+ *stat |= FE_HAS_SYNC;
+
+ if ((((lock >> 12) & 1) || ((lock_slave >> 12) & 1)) && ((lock >> 5) & 7)) /* FEC MPEG */
+ *stat |= FE_HAS_LOCK;
+
+ if (((lock >> 12) & 1) || ((lock_slave >> 12) & 1)) {
+ lock = dib8000_read_word(state, 554); /* Viterbi Layer A */
+ if (lock & 0x01)
+ *stat |= FE_HAS_VITERBI;
+
+ lock = dib8000_read_word(state, 555); /* Viterbi Layer B */
+ if (lock & 0x01)
+ *stat |= FE_HAS_VITERBI;
+
+ lock = dib8000_read_word(state, 556); /* Viterbi Layer C */
+ if (lock & 0x01)
+ *stat |= FE_HAS_VITERBI;
+ }
+ dib8000_get_stats(fe, *stat);
+
+ return 0;
+}
+
+static int dib8000_read_ber(struct dvb_frontend *fe, u32 * ber)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+
+ /* 13 segments */
+ if (state->revision == 0x8090)
+ *ber = (dib8000_read_word(state, 562) << 16) |
+ dib8000_read_word(state, 563);
+ else
+ *ber = (dib8000_read_word(state, 560) << 16) |
+ dib8000_read_word(state, 561);
+ return 0;
+}
+
+static int dib8000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+
+ /* packet error on 13 seg */
+ if (state->revision == 0x8090)
+ *unc = dib8000_read_word(state, 567);
+ else
+ *unc = dib8000_read_word(state, 565);
+ return 0;
+}
+
+static int dib8000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u8 index_frontend;
+ u16 val;
+
+ *strength = 0;
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
+ state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val);
+ if (val > 65535 - *strength)
+ *strength = 65535;
+ else
+ *strength += val;
+ }
+
+ val = 65535 - dib8000_read_word(state, 390);
+ if (val > 65535 - *strength)
+ *strength = 65535;
+ else
+ *strength += val;
+ return 0;
+}
+
+static u32 dib8000_get_snr(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u32 n, s, exp;
+ u16 val;
+
+ if (state->revision != 0x8090)
+ val = dib8000_read_word(state, 542);
+ else
+ val = dib8000_read_word(state, 544);
+ n = (val >> 6) & 0xff;
+ exp = (val & 0x3f);
+ if ((exp & 0x20) != 0)
+ exp -= 0x40;
+ n <<= exp+16;
+
+ if (state->revision != 0x8090)
+ val = dib8000_read_word(state, 543);
+ else
+ val = dib8000_read_word(state, 545);
+ s = (val >> 6) & 0xff;
+ exp = (val & 0x3f);
+ if ((exp & 0x20) != 0)
+ exp -= 0x40;
+ s <<= exp+16;
+
+ if (n > 0) {
+ u32 t = (s/n) << 16;
+ return t + ((s << 16) - n*t) / n;
+ }
+ return 0xffffffff;
+}
+
+static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u8 index_frontend;
+ u32 snr_master;
+
+ snr_master = dib8000_get_snr(fe);
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
+ snr_master += dib8000_get_snr(state->fe[index_frontend]);
+
+ if ((snr_master >> 16) != 0) {
+ snr_master = 10*intlog10(snr_master>>16);
+ *snr = snr_master / ((1 << 24) / 10);
+ }
+ else
+ *snr = 0;
+
+ return 0;
+}
+
+struct per_layer_regs {
+ u16 lock, ber, per;
+};
+
+static const struct per_layer_regs per_layer_regs[] = {
+ { 554, 560, 562 },
+ { 555, 576, 578 },
+ { 556, 581, 583 },
+};
+
+struct linear_segments {
+ unsigned x;
+ signed y;
+};
+
+/*
+ * Table to estimate signal strength in dBm.
+ * This table was empirically determinated by measuring the signal
+ * strength generated by a DTA-2111 RF generator directly connected into
+ * a dib8076 device (a PixelView PV-D231U stick), using a good quality
+ * 3 meters RC6 cable and good RC6 connectors.
+ * The real value can actually be different on other devices, depending
+ * on several factors, like if LNA is enabled or not, if diversity is
+ * enabled, type of connectors, etc.
+ * Yet, it is better to use this measure in dB than a random non-linear
+ * percentage value, especially for antenna adjustments.
+ * On my tests, the precision of the measure using this table is about
+ * 0.5 dB, with sounds reasonable enough.
+ */
+static struct linear_segments strength_to_db_table[] = {
+ { 55953, 108500 }, /* -22.5 dBm */
+ { 55394, 108000 },
+ { 53834, 107000 },
+ { 52863, 106000 },
+ { 52239, 105000 },
+ { 52012, 104000 },
+ { 51803, 103000 },
+ { 51566, 102000 },
+ { 51356, 101000 },
+ { 51112, 100000 },
+ { 50869, 99000 },
+ { 50600, 98000 },
+ { 50363, 97000 },
+ { 50117, 96000 }, /* -35 dBm */
+ { 49889, 95000 },
+ { 49680, 94000 },
+ { 49493, 93000 },
+ { 49302, 92000 },
+ { 48929, 91000 },
+ { 48416, 90000 },
+ { 48035, 89000 },
+ { 47593, 88000 },
+ { 47282, 87000 },
+ { 46953, 86000 },
+ { 46698, 85000 },
+ { 45617, 84000 },
+ { 44773, 83000 },
+ { 43845, 82000 },
+ { 43020, 81000 },
+ { 42010, 80000 }, /* -51 dBm */
+ { 0, 0 },
+};
+
+static u32 interpolate_value(u32 value, struct linear_segments *segments,
+ unsigned len)
+{
+ u64 tmp64;
+ u32 dx;
+ s32 dy;
+ int i, ret;
+
+ if (value >= segments[0].x)
+ return segments[0].y;
+ if (value < segments[len-1].x)
+ return segments[len-1].y;
+
+ for (i = 1; i < len - 1; i++) {
+ /* If value is identical, no need to interpolate */
+ if (value == segments[i].x)
+ return segments[i].y;
+ if (value > segments[i].x)
+ break;
+ }
+
+ /* Linear interpolation between the two (x,y) points */
+ dy = segments[i - 1].y - segments[i].y;
+ dx = segments[i - 1].x - segments[i].x;
+
+ tmp64 = value - segments[i].x;
+ tmp64 *= dy;
+ do_div(tmp64, dx);
+ ret = segments[i].y + tmp64;
+
+ return ret;
+}
+
+static u32 dib8000_get_time_us(struct dvb_frontend *fe, int layer)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ int ini_layer, end_layer, i;
+ u64 time_us, tmp64;
+ u32 tmp, denom;
+ int guard, rate_num, rate_denum = 1, bits_per_symbol, nsegs;
+ int interleaving = 0, fft_div;
+
+ if (layer >= 0) {
+ ini_layer = layer;
+ end_layer = layer + 1;
+ } else {
+ ini_layer = 0;
+ end_layer = 3;
+ }
+
+ switch (c->guard_interval) {
+ case GUARD_INTERVAL_1_4:
+ guard = 4;
+ break;
+ case GUARD_INTERVAL_1_8:
+ guard = 8;
+ break;
+ case GUARD_INTERVAL_1_16:
+ guard = 16;
+ break;
+ default:
+ case GUARD_INTERVAL_1_32:
+ guard = 32;
+ break;
+ }
+
+ switch (c->transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ fft_div = 4;
+ break;
+ case TRANSMISSION_MODE_4K:
+ fft_div = 2;
+ break;
+ default:
+ case TRANSMISSION_MODE_8K:
+ fft_div = 1;
+ break;
+ }
+
+ denom = 0;
+ for (i = ini_layer; i < end_layer; i++) {
+ nsegs = c->layer[i].segment_count;
+ if (nsegs == 0 || nsegs > 13)
+ continue;
+
+ switch (c->layer[i].modulation) {
+ case DQPSK:
+ case QPSK:
+ bits_per_symbol = 2;
+ break;
+ case QAM_16:
+ bits_per_symbol = 4;
+ break;
+ default:
+ case QAM_64:
+ bits_per_symbol = 6;
+ break;
+ }
+
+ switch (c->layer[i].fec) {
+ case FEC_1_2:
+ rate_num = 1;
+ rate_denum = 2;
+ break;
+ case FEC_2_3:
+ rate_num = 2;
+ rate_denum = 3;
+ break;
+ case FEC_3_4:
+ rate_num = 3;
+ rate_denum = 4;
+ break;
+ case FEC_5_6:
+ rate_num = 5;
+ rate_denum = 6;
+ break;
+ default:
+ case FEC_7_8:
+ rate_num = 7;
+ rate_denum = 8;
+ break;
+ }
+
+ interleaving = c->layer[i].interleaving;
+
+ denom += bits_per_symbol * rate_num * fft_div * nsegs * 384;
+ }
+
+ /* If all goes wrong, wait for 1s for the next stats */
+ if (!denom)
+ return 0;
+
+ /* Estimate the period for the total bit rate */
+ time_us = rate_denum * (1008 * 1562500L);
+ tmp64 = time_us;
+ do_div(tmp64, guard);
+ time_us = time_us + tmp64;
+ time_us += denom / 2;
+ do_div(time_us, denom);
+
+ tmp = 1008 * 96 * interleaving;
+ time_us += tmp + tmp / guard;
+
+ return time_us;
+}
+
+static int dib8000_get_stats(struct dvb_frontend *fe, enum fe_status stat)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ int i;
+ int show_per_stats = 0;
+ u32 time_us = 0, snr, val;
+ u64 blocks;
+ s32 db;
+ u16 strength;
+
+ /* Get Signal strength */
+ dib8000_read_signal_strength(fe, &strength);
+ val = strength;
+ db = interpolate_value(val,
+ strength_to_db_table,
+ ARRAY_SIZE(strength_to_db_table)) - 131000;
+ c->strength.stat[0].svalue = db;
+
+ /* UCB/BER/CNR measures require lock */
+ if (!(stat & FE_HAS_LOCK)) {
+ c->cnr.len = 1;
+ c->block_count.len = 1;
+ c->block_error.len = 1;
+ c->post_bit_error.len = 1;
+ c->post_bit_count.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return 0;
+ }
+
+ /* Check if time for stats was elapsed */
+ if (time_after(jiffies, state->per_jiffies_stats)) {
+ state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000);
+
+ /* Get SNR */
+ snr = dib8000_get_snr(fe);
+ for (i = 1; i < MAX_NUMBER_OF_FRONTENDS; i++) {
+ if (state->fe[i])
+ snr += dib8000_get_snr(state->fe[i]);
+ }
+ snr = snr >> 16;
+
+ if (snr) {
+ snr = 10 * intlog10(snr);
+ snr = (1000L * snr) >> 24;
+ } else {
+ snr = 0;
+ }
+ c->cnr.stat[0].svalue = snr;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+
+ /* Get UCB measures */
+ dib8000_read_unc_blocks(fe, &val);
+ if (val < state->init_ucb)
+ state->init_ucb += 0x100000000LL;
+
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue = val + state->init_ucb;
+
+ /* Estimate the number of packets based on bitrate */
+ if (!time_us)
+ time_us = dib8000_get_time_us(fe, -1);
+
+ if (time_us) {
+ blocks = 1250000ULL * 1000000ULL;
+ do_div(blocks, time_us * 8 * 204);
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue += blocks;
+ }
+
+ show_per_stats = 1;
+ }
+
+ /* Get post-BER measures */
+ if (time_after(jiffies, state->ber_jiffies_stats)) {
+ time_us = dib8000_get_time_us(fe, -1);
+ state->ber_jiffies_stats = jiffies + msecs_to_jiffies((time_us + 500) / 1000);
+
+ dprintk("Next all layers stats available in %u us.\n", time_us);
+
+ dib8000_read_ber(fe, &val);
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += val;
+
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue += 100000000;
+ }
+
+ if (state->revision < 0x8002)
+ return 0;
+
+ c->block_error.len = 4;
+ c->post_bit_error.len = 4;
+ c->post_bit_count.len = 4;
+
+ for (i = 0; i < 3; i++) {
+ unsigned nsegs = c->layer[i].segment_count;
+
+ if (nsegs == 0 || nsegs > 13)
+ continue;
+
+ time_us = 0;
+
+ if (time_after(jiffies, state->ber_jiffies_stats_layer[i])) {
+ time_us = dib8000_get_time_us(fe, i);
+
+ state->ber_jiffies_stats_layer[i] = jiffies + msecs_to_jiffies((time_us + 500) / 1000);
+ dprintk("Next layer %c stats will be available in %u us\n",
+ 'A' + i, time_us);
+
+ val = dib8000_read_word(state, per_layer_regs[i].ber);
+ c->post_bit_error.stat[1 + i].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[1 + i].uvalue += val;
+
+ c->post_bit_count.stat[1 + i].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[1 + i].uvalue += 100000000;
+ }
+
+ if (show_per_stats) {
+ val = dib8000_read_word(state, per_layer_regs[i].per);
+
+ c->block_error.stat[1 + i].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[1 + i].uvalue += val;
+
+ if (!time_us)
+ time_us = dib8000_get_time_us(fe, i);
+ if (time_us) {
+ blocks = 1250000ULL * 1000000ULL;
+ do_div(blocks, time_us * 8 * 204);
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue += blocks;
+ }
+ }
+ }
+ return 0;
+}
+
+static int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ u8 index_frontend = 1;
+
+ while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL))
+ index_frontend++;
+ if (index_frontend < MAX_NUMBER_OF_FRONTENDS) {
+ dprintk("set slave fe %p to index %i\n", fe_slave, index_frontend);
+ state->fe[index_frontend] = fe_slave;
+ return 0;
+ }
+
+ dprintk("too many slave frontend\n");
+ return -ENOMEM;
+}
+
+static struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+
+ if (slave_index >= MAX_NUMBER_OF_FRONTENDS)
+ return NULL;
+ return state->fe[slave_index];
+}
+
+static int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods,
+ u8 default_addr, u8 first_addr, u8 is_dib8096p)
+{
+ int k = 0, ret = 0;
+ u8 new_addr = 0;
+ struct i2c_device client = {.adap = host };
+
+ client.i2c_write_buffer = kzalloc(4, GFP_KERNEL);
+ if (!client.i2c_write_buffer) {
+ dprintk("%s: not enough memory\n", __func__);
+ return -ENOMEM;
+ }
+ client.i2c_read_buffer = kzalloc(4, GFP_KERNEL);
+ if (!client.i2c_read_buffer) {
+ dprintk("%s: not enough memory\n", __func__);
+ ret = -ENOMEM;
+ goto error_memory_read;
+ }
+ client.i2c_buffer_lock = kzalloc(sizeof(struct mutex), GFP_KERNEL);
+ if (!client.i2c_buffer_lock) {
+ dprintk("%s: not enough memory\n", __func__);
+ ret = -ENOMEM;
+ goto error_memory_lock;
+ }
+ mutex_init(client.i2c_buffer_lock);
+
+ for (k = no_of_demods - 1; k >= 0; k--) {
+ /* designated i2c address */
+ new_addr = first_addr + (k << 1);
+
+ client.addr = new_addr;
+ if (!is_dib8096p)
+ dib8000_i2c_write16(&client, 1287, 0x0003); /* sram lead in, rdy */
+ if (dib8000_identify(&client) == 0) {
+ /* sram lead in, rdy */
+ if (!is_dib8096p)
+ dib8000_i2c_write16(&client, 1287, 0x0003);
+ client.addr = default_addr;
+ if (dib8000_identify(&client) == 0) {
+ dprintk("#%d: not identified\n", k);
+ ret = -EINVAL;
+ goto error;
+ }
+ }
+
+ /* start diversity to pull_down div_str - just for i2c-enumeration */
+ dib8000_i2c_write16(&client, 1286, (1 << 10) | (4 << 6));
+
+ /* set new i2c address and force divstart */
+ dib8000_i2c_write16(&client, 1285, (new_addr << 2) | 0x2);
+ client.addr = new_addr;
+ dib8000_identify(&client);
+
+ dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr);
+ }
+
+ for (k = 0; k < no_of_demods; k++) {
+ new_addr = first_addr | (k << 1);
+ client.addr = new_addr;
+
+ // unforce divstr
+ dib8000_i2c_write16(&client, 1285, new_addr << 2);
+
+ /* deactivate div - it was just for i2c-enumeration */
+ dib8000_i2c_write16(&client, 1286, 0);
+ }
+
+error:
+ kfree(client.i2c_buffer_lock);
+error_memory_lock:
+ kfree(client.i2c_read_buffer);
+error_memory_read:
+ kfree(client.i2c_write_buffer);
+
+ return ret;
+}
+
+static int dib8000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ tune->step_size = 0;
+ tune->max_drift = 0;
+ return 0;
+}
+
+static void dib8000_release(struct dvb_frontend *fe)
+{
+ struct dib8000_state *st = fe->demodulator_priv;
+ u8 index_frontend;
+
+ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++)
+ dvb_frontend_detach(st->fe[index_frontend]);
+
+ dibx000_exit_i2c_master(&st->i2c_master);
+ i2c_del_adapter(&st->dib8096p_tuner_adap);
+ kfree(st->fe[0]);
+ kfree(st);
+}
+
+static struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating)
+{
+ struct dib8000_state *st = fe->demodulator_priv;
+ return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating);
+}
+
+static int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
+{
+ struct dib8000_state *st = fe->demodulator_priv;
+ u16 val = dib8000_read_word(st, 299) & 0xffef;
+ val |= (onoff & 0x1) << 4;
+
+ dprintk("pid filter enabled %d\n", onoff);
+ return dib8000_write_word(st, 299, val);
+}
+
+static int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
+{
+ struct dib8000_state *st = fe->demodulator_priv;
+ dprintk("Index %x, PID %d, OnOff %d\n", id, pid, onoff);
+ return dib8000_write_word(st, 305 + id, onoff ? (1 << 13) | pid : 0);
+}
+
+static const struct dvb_frontend_ops dib8000_ops = {
+ .delsys = { SYS_ISDBT },
+ .info = {
+ .name = "DiBcom 8000 ISDB-T",
+ .frequency_min_hz = 44250 * kHz,
+ .frequency_max_hz = 867250 * kHz,
+ .frequency_stepsize_hz = 62500,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = dib8000_release,
+
+ .init = dib8000_wakeup,
+ .sleep = dib8000_sleep,
+
+ .set_frontend = dib8000_set_frontend,
+ .get_tune_settings = dib8000_fe_get_tune_settings,
+ .get_frontend = dib8000_get_frontend,
+
+ .read_status = dib8000_read_status,
+ .read_ber = dib8000_read_ber,
+ .read_signal_strength = dib8000_read_signal_strength,
+ .read_snr = dib8000_read_snr,
+ .read_ucblocks = dib8000_read_unc_blocks,
+};
+
+static struct dvb_frontend *dib8000_init(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg)
+{
+ struct dvb_frontend *fe;
+ struct dib8000_state *state;
+
+ dprintk("dib8000_init\n");
+
+ state = kzalloc(sizeof(struct dib8000_state), GFP_KERNEL);
+ if (state == NULL)
+ return NULL;
+ fe = kzalloc(sizeof(struct dvb_frontend), GFP_KERNEL);
+ if (fe == NULL)
+ goto error;
+
+ memcpy(&state->cfg, cfg, sizeof(struct dib8000_config));
+ state->i2c.adap = i2c_adap;
+ state->i2c.addr = i2c_addr;
+ state->i2c.i2c_write_buffer = state->i2c_write_buffer;
+ state->i2c.i2c_read_buffer = state->i2c_read_buffer;
+ mutex_init(&state->i2c_buffer_lock);
+ state->i2c.i2c_buffer_lock = &state->i2c_buffer_lock;
+ state->gpio_val = cfg->gpio_val;
+ state->gpio_dir = cfg->gpio_dir;
+
+ /* Ensure the output mode remains at the previous default if it's
+ * not specifically set by the caller.
+ */
+ if ((state->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (state->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK))
+ state->cfg.output_mode = OUTMODE_MPEG2_FIFO;
+
+ state->fe[0] = fe;
+ fe->demodulator_priv = state;
+ memcpy(&state->fe[0]->ops, &dib8000_ops, sizeof(struct dvb_frontend_ops));
+
+ state->timf_default = cfg->pll->timf;
+
+ if (dib8000_identify(&state->i2c) == 0) {
+ kfree(fe);
+ goto error;
+ }
+
+ dibx000_init_i2c_master(&state->i2c_master, DIB8000, state->i2c.adap, state->i2c.addr);
+
+ /* init 8096p tuner adapter */
+ strscpy(state->dib8096p_tuner_adap.name, "DiB8096P tuner interface",
+ sizeof(state->dib8096p_tuner_adap.name));
+ state->dib8096p_tuner_adap.algo = &dib8096p_tuner_xfer_algo;
+ state->dib8096p_tuner_adap.algo_data = NULL;
+ state->dib8096p_tuner_adap.dev.parent = state->i2c.adap->dev.parent;
+ i2c_set_adapdata(&state->dib8096p_tuner_adap, state);
+ i2c_add_adapter(&state->dib8096p_tuner_adap);
+
+ dib8000_reset(fe);
+
+ dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & ~0x60) | (3 << 5)); /* ber_rs_len = 3 */
+ state->current_demod_bw = 6000;
+
+ return fe;
+
+error:
+ kfree(state);
+ return NULL;
+}
+
+void *dib8000_attach(struct dib8000_ops *ops)
+{
+ if (!ops)
+ return NULL;
+
+ ops->pwm_agc_reset = dib8000_pwm_agc_reset;
+ ops->get_dc_power = dib8090p_get_dc_power;
+ ops->set_gpio = dib8000_set_gpio;
+ ops->get_slave_frontend = dib8000_get_slave_frontend;
+ ops->set_tune_state = dib8000_set_tune_state;
+ ops->pid_filter_ctrl = dib8000_pid_filter_ctrl;
+ ops->get_adc_power = dib8000_get_adc_power;
+ ops->update_pll = dib8000_update_pll;
+ ops->tuner_sleep = dib8096p_tuner_sleep;
+ ops->get_tune_state = dib8000_get_tune_state;
+ ops->get_i2c_tuner = dib8096p_get_i2c_tuner;
+ ops->set_slave_frontend = dib8000_set_slave_frontend;
+ ops->pid_filter = dib8000_pid_filter;
+ ops->ctrl_timf = dib8000_ctrl_timf;
+ ops->init = dib8000_init;
+ ops->get_i2c_master = dib8000_get_i2c_master;
+ ops->i2c_enumeration = dib8000_i2c_enumeration;
+ ops->set_wbd_ref = dib8000_set_wbd_ref;
+
+ return ops;
+}
+EXPORT_SYMBOL_GPL(dib8000_attach);
+
+MODULE_AUTHOR("Olivier Grenie <Olivier.Grenie@parrot.com, Patrick Boettcher <patrick.boettcher@posteo.de>");
+MODULE_DESCRIPTION("Driver for the DiBcom 8000 ISDB-T demodulator");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/dib8000.h b/drivers/media/dvb-frontends/dib8000.h
new file mode 100644
index 000000000000..b920fe769021
--- /dev/null
+++ b/drivers/media/dvb-frontends/dib8000.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef DIB8000_H
+#define DIB8000_H
+
+#include "dibx000_common.h"
+
+struct dib8000_config {
+ u8 output_mpeg2_in_188_bytes;
+ u8 hostbus_diversity;
+ u8 tuner_is_baseband;
+ int (*update_lna) (struct dvb_frontend *, u16 agc_global);
+
+ u8 agc_config_count;
+ struct dibx000_agc_config *agc;
+ struct dibx000_bandwidth_config *pll;
+
+#define DIB8000_GPIO_DEFAULT_DIRECTIONS 0xffff
+ u16 gpio_dir;
+#define DIB8000_GPIO_DEFAULT_VALUES 0x0000
+ u16 gpio_val;
+#define DIB8000_GPIO_PWM_POS0(v) ((v & 0xf) << 12)
+#define DIB8000_GPIO_PWM_POS1(v) ((v & 0xf) << 8 )
+#define DIB8000_GPIO_PWM_POS2(v) ((v & 0xf) << 4 )
+#define DIB8000_GPIO_PWM_POS3(v) (v & 0xf)
+#define DIB8000_GPIO_DEFAULT_PWM_POS 0xffff
+ u16 gpio_pwm_pos;
+ u16 pwm_freq_div;
+
+ void (*agc_control) (struct dvb_frontend *, u8 before);
+
+ u16 drives;
+ u16 diversity_delay;
+ u8 div_cfg;
+ u8 output_mode;
+ u8 refclksel;
+ u8 enMpegOutput:1;
+
+ struct dibx000_bandwidth_config *plltable;
+};
+
+#define DEFAULT_DIB8000_I2C_ADDRESS 18
+
+struct dib8000_ops {
+ int (*set_wbd_ref)(struct dvb_frontend *fe, u16 value);
+ int (*update_pll)(struct dvb_frontend *fe,
+ struct dibx000_bandwidth_config *pll, u32 bw, u8 ratio);
+ int (*set_gpio)(struct dvb_frontend *fe, u8 num, u8 dir, u8 val);
+ void (*pwm_agc_reset)(struct dvb_frontend *fe);
+ struct i2c_adapter *(*get_i2c_tuner)(struct dvb_frontend *fe);
+ int (*tuner_sleep)(struct dvb_frontend *fe, int onoff);
+ s32 (*get_adc_power)(struct dvb_frontend *fe, u8 mode);
+ int (*get_dc_power)(struct dvb_frontend *fe, u8 IQ);
+ u32 (*ctrl_timf)(struct dvb_frontend *fe, uint8_t op, uint32_t timf);
+ enum frontend_tune_state (*get_tune_state)(struct dvb_frontend *fe);
+ int (*set_tune_state)(struct dvb_frontend *fe, enum frontend_tune_state tune_state);
+ int (*set_slave_frontend)(struct dvb_frontend *fe, struct dvb_frontend *fe_slave);
+ struct dvb_frontend *(*get_slave_frontend)(struct dvb_frontend *fe, int slave_index);
+ int (*i2c_enumeration)(struct i2c_adapter *host, int no_of_demods,
+ u8 default_addr, u8 first_addr, u8 is_dib8096p);
+ struct i2c_adapter *(*get_i2c_master)(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating);
+ int (*pid_filter_ctrl)(struct dvb_frontend *fe, u8 onoff);
+ int (*pid_filter)(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff);
+ struct dvb_frontend *(*init)(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg);
+};
+
+#if IS_REACHABLE(CONFIG_DVB_DIB8000)
+void *dib8000_attach(struct dib8000_ops *ops);
+#else
+static inline void *dib8000_attach(struct dib8000_ops *ops)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb-frontends/dib9000.c
index 6201c59a78dd..83cf6eadd49c 100644
--- a/drivers/media/dvb/frontends/dib9000.c
+++ b/drivers/media/dvb-frontends/dib9000.c
@@ -1,18 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Linux-DVB Driver for DiBcom's DiB9000 and demodulator-family.
*
* Copyright (C) 2005-10 DiBcom (http://www.dibcom.fr/)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
-#include "dvb_math.h"
-#include "dvb_frontend.h"
+#include <linux/int_log.h>
+#include <media/dvb_frontend.h>
#include "dib9000.h"
#include "dibx000_common.h"
@@ -21,7 +21,12 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
-#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB9000: "); printk(args); printk("\n"); } } while (0)
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
+
#define MAX_NUMBER_OF_FRONTENDS 6
struct i2c_device {
@@ -225,7 +230,7 @@ static u16 to_fw_output_mode(u16 mode)
}
}
-static u16 dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 * b, u32 len, u16 attribute)
+static int dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 *b, u32 len, u16 attribute)
{
u32 chunk_size = 126;
u32 l;
@@ -253,12 +258,12 @@ static u16 dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 * b, u32
state->i2c_write_buffer[0] |= (1 << 4);
do {
- l = len < chunk_size ? len : chunk_size;
+ l = min(len, chunk_size);
state->msg[1].len = l;
state->msg[1].buf = b;
ret = i2c_transfer(state->i2c.i2c_adap, state->msg, 2) != 2 ? -EREMOTEIO : 0;
if (ret != 0) {
- dprintk("i2c read error on %d", reg);
+ dprintk("i2c read error on %d\n", reg);
return -EREMOTEIO;
}
@@ -285,7 +290,7 @@ static u16 dib9000_i2c_read16(struct i2c_device *i2c, u16 reg)
i2c->i2c_write_buffer[1] = reg & 0xff;
if (i2c_transfer(i2c->i2c_adap, msg, 2) != 2) {
- dprintk("read register %x error", reg);
+ dprintk("read register %x error\n", reg);
return 0;
}
@@ -309,7 +314,7 @@ static inline u16 dib9000_read_word_attr(struct dib9000_state *state, u16 reg, u
#define dib9000_read16_noinc_attr(state, reg, b, len, attribute) dib9000_read16_attr(state, reg, b, len, (attribute) | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)
-static u16 dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 * buf, u32 len, u16 attribute)
+static int dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 *buf, u32 len, u16 attribute)
{
u32 chunk_size = 126;
u32 l;
@@ -337,7 +342,7 @@ static u16 dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 *
state->i2c_write_buffer[0] |= (1 << 4);
do {
- l = len < chunk_size ? len : chunk_size;
+ l = min(len, chunk_size);
state->msg[0].len = l + 2;
memcpy(&state->i2c_write_buffer[2], buf, l);
@@ -440,7 +445,7 @@ static int dib9000_risc_mem_read(struct dib9000_state *state, u8 cmd, u8 * b, u1
return -EIO;
if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return -EINTR;
}
dib9000_risc_mem_setup(state, cmd | 0x80);
@@ -456,7 +461,7 @@ static int dib9000_risc_mem_write(struct dib9000_state *state, u8 cmd, const u8
return -EIO;
if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return -EINTR;
}
dib9000_risc_mem_setup(state, cmd);
@@ -479,13 +484,13 @@ static int dib9000_firmware_download(struct dib9000_state *state, u8 risc_id, u1
dib9000_write_word(state, 1025 + offs, 0);
dib9000_write_word(state, 1031 + offs, key);
- dprintk("going to download %dB of microcode", len);
+ dprintk("going to download %dB of microcode\n", len);
if (dib9000_write16_noinc(state, 1026 + offs, (u8 *) code, (u16) len) != 0) {
- dprintk("error while downloading microcode for RISC %c", 'A' + risc_id);
+ dprintk("error while downloading microcode for RISC %c\n", 'A' + risc_id);
return -EIO;
}
- dprintk("Microcode for RISC %c loaded", 'A' + risc_id);
+ dprintk("Microcode for RISC %c loaded\n", 'A' + risc_id);
return 0;
}
@@ -511,10 +516,10 @@ static int dib9000_mbx_host_init(struct dib9000_state *state, u8 risc_id)
} while ((reset_reg & 0x8000) && --tries);
if (reset_reg & 0x8000) {
- dprintk("MBX: init ERROR, no response from RISC %c", 'A' + risc_id);
+ dprintk("MBX: init ERROR, no response from RISC %c\n", 'A' + risc_id);
return -EIO;
}
- dprintk("MBX: initialized");
+ dprintk("MBX: initialized\n");
return 0;
}
@@ -531,30 +536,27 @@ static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data,
return -EINVAL;
if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return -EINTR;
}
tmp = MAX_MAILBOX_TRY;
do {
size = dib9000_read_word_attr(state, 1043, attr) & 0xff;
if ((size + len + 1) > MBX_MAX_WORDS && --tmp) {
- dprintk("MBX: RISC mbx full, retrying");
+ dprintk("MBX: RISC mbx full, retrying\n");
msleep(100);
} else
break;
} while (1);
- /*dprintk( "MBX: size: %d", size); */
+ /*dprintk( "MBX: size: %d\n", size); */
if (tmp == 0) {
ret = -EINVAL;
goto out;
}
#ifdef DUMP_MSG
- dprintk("--> %02x %d ", id, len + 1);
- for (i = 0; i < len; i++)
- dprintk("%04x ", data[i]);
- dprintk("\n");
+ dprintk("--> %02x %d %*ph\n", id, len + 1, len, data);
#endif
/* byte-order conversion - works on big (where it is not necessary) or little endian */
@@ -596,7 +598,7 @@ static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id,
return 0;
if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return 0;
}
if (risc_id == 1)
@@ -622,13 +624,13 @@ static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id,
}
#ifdef DUMP_MSG
- dprintk("<-- ");
+ dprintk("<--\n");
for (i = 0; i < size + 1; i++)
- dprintk("%04x ", d[i]);
+ dprintk("%04x\n", d[i]);
dprintk("\n");
#endif
} else {
- dprintk("MBX: message is too big for message cache (%d), flushing message", size);
+ dprintk("MBX: message is too big for message cache (%d), flushing message\n", size);
size--; /* Initial word already read */
while (size--)
dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, 2, attr);
@@ -649,9 +651,11 @@ static int dib9000_risc_debug_buf(struct dib9000_state *state, u16 * data, u8 si
b[2 * (size - 2) - 1] = '\0'; /* Bullet proof the buffer */
if (*b == '~') {
b++;
- dprintk(b);
+ dprintk("%s\n", b);
} else
- dprintk("RISC%d: %d.%04d %s", state->fe_id, ts / 10000, ts % 10000, *b ? b : "<emtpy>");
+ dprintk("RISC%d: %d.%04d %s\n",
+ state->fe_id,
+ ts / 10000, ts % 10000, *b ? b : "<empty>");
return 1;
}
@@ -666,7 +670,7 @@ static int dib9000_mbx_fetch_to_cache(struct dib9000_state *state, u16 attr)
if (*block == 0) {
size = dib9000_mbx_read(state, block, 1, attr);
-/* dprintk( "MBX: fetched %04x message to cache", *block); */
+/* dprintk( "MBX: fetched %04x message to cache\n", *block); */
switch (*block >> 8) {
case IN_MSG_DEBUG_BUF:
@@ -686,7 +690,7 @@ static int dib9000_mbx_fetch_to_cache(struct dib9000_state *state, u16 attr)
return 1;
}
}
- dprintk("MBX: no free cache-slot found for new message...");
+ dprintk("MBX: no free cache-slot found for new message...\n");
return -1;
}
@@ -706,7 +710,7 @@ static int dib9000_mbx_process(struct dib9000_state *state, u16 attr)
return -1;
if (mutex_lock_interruptible(&state->platform.risc.mbx_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return -1;
}
@@ -715,7 +719,7 @@ static int dib9000_mbx_process(struct dib9000_state *state, u16 attr)
dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */
/* if (tmp) */
-/* dprintk( "cleared IRQ: %x", tmp); */
+/* dprintk( "cleared IRQ: %x\n", tmp); */
mutex_unlock(&state->platform.risc.mbx_lock);
return ret;
@@ -750,7 +754,7 @@ static int dib9000_mbx_get_message_attr(struct dib9000_state *state, u16 id, u16
} while (--timeout);
if (timeout == 0) {
- dprintk("waiting for message %d timed out", id);
+ dprintk("waiting for message %d timed out\n", id);
return -1;
}
@@ -770,7 +774,7 @@ static int dib9000_risc_check_version(struct dib9000_state *state)
return -EIO;
fw_version = (r[0] << 8) | r[1];
- dprintk("RISC: ver: %d.%02d (IC: %d)", fw_version >> 10, fw_version & 0x3ff, (r[2] << 8) | r[3]);
+ dprintk("RISC: ver: %d.%02d (IC: %d)\n", fw_version >> 10, fw_version & 0x3ff, (r[2] << 8) | r[3]);
if ((fw_version >> 10) != 7)
return -EINVAL;
@@ -850,40 +854,40 @@ static u16 dib9000_identify(struct i2c_device *client)
value = dib9000_i2c_read16(client, 896);
if (value != 0x01b3) {
- dprintk("wrong Vendor ID (0x%x)", value);
+ dprintk("wrong Vendor ID (0x%x)\n", value);
return 0;
}
value = dib9000_i2c_read16(client, 897);
if (value != 0x4000 && value != 0x4001 && value != 0x4002 && value != 0x4003 && value != 0x4004 && value != 0x4005) {
- dprintk("wrong Device ID (0x%x)", value);
+ dprintk("wrong Device ID (0x%x)\n", value);
return 0;
}
/* protect this driver to be used with 7000PC */
if (value == 0x4000 && dib9000_i2c_read16(client, 769) == 0x4000) {
- dprintk("this driver does not work with DiB7000PC");
+ dprintk("this driver does not work with DiB7000PC\n");
return 0;
}
switch (value) {
case 0x4000:
- dprintk("found DiB7000MA/PA/MB/PB");
+ dprintk("found DiB7000MA/PA/MB/PB\n");
break;
case 0x4001:
- dprintk("found DiB7000HC");
+ dprintk("found DiB7000HC\n");
break;
case 0x4002:
- dprintk("found DiB7000MC");
+ dprintk("found DiB7000MC\n");
break;
case 0x4003:
- dprintk("found DiB9000A");
+ dprintk("found DiB9000A\n");
break;
case 0x4004:
- dprintk("found DiB9000H");
+ dprintk("found DiB9000H\n");
break;
case 0x4005:
- dprintk("found DiB9000M");
+ dprintk("found DiB9000M\n");
break;
}
@@ -1013,7 +1017,7 @@ static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address
if (address >= 1024 || !state->platform.risc.fw_is_running)
return -EINVAL;
- /* dprintk( "APB access thru rd fw %d %x", address, attribute); */
+ /* dprintk( "APB access through rd fw %d %x\n", address, attribute); */
mb[0] = (u16) address;
mb[1] = len / 2;
@@ -1040,13 +1044,18 @@ static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 addres
if (address >= 1024 || !state->platform.risc.fw_is_running)
return -EINVAL;
- /* dprintk( "APB access thru wr fw %d %x", address, attribute); */
+ if (len > 18)
+ return -EINVAL;
- mb[0] = (unsigned short)address;
- for (i = 0; i < len && i < 20; i += 2)
- mb[1 + (i / 2)] = (b[i] << 8 | b[i + 1]);
+ /* dprintk( "APB access through wr fw %d %x\n", address, attribute); */
- dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_W, mb, 1 + len / 2, attribute);
+ mb[0] = (u16)address;
+ for (i = 0; i + 1 < len; i += 2)
+ mb[1 + i / 2] = b[i] << 8 | b[i + 1];
+ if (len & 1)
+ mb[1 + len / 2] = b[len - 1] << 8;
+
+ dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_W, mb, (3 + len) / 2, attribute);
return dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute) == 1 ? 0 : -EINVAL;
}
@@ -1186,7 +1195,7 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe)
int ret = 0;
if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return -EINTR;
}
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
@@ -1529,7 +1538,7 @@ static int dib9000_fw_set_output_mode(struct dvb_frontend *fe, int mode)
struct dib9000_state *state = fe->demodulator_priv;
u16 outreg, smo_mode;
- dprintk("setting output mode for demod %p to %d", fe, mode);
+ dprintk("setting output mode for demod %p to %d\n", fe, mode);
switch (mode) {
case OUTMODE_MPEG2_PAR_GATED_CLK:
@@ -1551,7 +1560,7 @@ static int dib9000_fw_set_output_mode(struct dvb_frontend *fe, int mode)
outreg = 0;
break;
default:
- dprintk("Unhandled output_mode passed to be set for demod %p", &state->fe[0]);
+ dprintk("Unhandled output_mode passed to be set for demod %p\n", &state->fe[0]);
return -EINVAL;
}
@@ -1585,7 +1594,7 @@ static int dib9000_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[]
len = 16;
if (dib9000_read_word(state, 790) != 0)
- dprintk("TunerITF: read busy");
+ dprintk("TunerITF: read busy\n");
dib9000_write_word(state, 784, (u16) (msg[index_msg].addr));
dib9000_write_word(state, 787, (len / 2) - 1);
@@ -1596,7 +1605,7 @@ static int dib9000_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[]
i--;
if (i == 0)
- dprintk("TunerITF: read failed");
+ dprintk("TunerITF: read failed\n");
for (i = 0; i < len; i += 2) {
t = dib9000_read_word(state, 785);
@@ -1604,13 +1613,13 @@ static int dib9000_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[]
msg[index_msg].buf[i + 1] = (t) & 0xff;
}
if (dib9000_read_word(state, 790) != 0)
- dprintk("TunerITF: read more data than expected");
+ dprintk("TunerITF: read more data than expected\n");
} else {
i = 1000;
while (dib9000_read_word(state, 789) && i)
i--;
if (i == 0)
- dprintk("TunerITF: write busy");
+ dprintk("TunerITF: write busy\n");
len = msg[index_msg].len;
if (len > 16)
@@ -1626,7 +1635,7 @@ static int dib9000_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[]
while (dib9000_read_word(state, 791) > 0 && i)
i--;
if (i == 0)
- dprintk("TunerITF: write failed");
+ dprintk("TunerITF: write failed\n");
}
}
return num;
@@ -1671,7 +1680,7 @@ static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2
}
if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return 0;
}
@@ -1702,12 +1711,12 @@ static u32 dib9000_i2c_func(struct i2c_adapter *adapter)
return I2C_FUNC_I2C;
}
-static struct i2c_algorithm dib9000_tuner_algo = {
+static const struct i2c_algorithm dib9000_tuner_algo = {
.master_xfer = dib9000_tuner_xfer,
.functionality = dib9000_i2c_func,
};
-static struct i2c_algorithm dib9000_component_bus_algo = {
+static const struct i2c_algorithm dib9000_component_bus_algo = {
.master_xfer = dib9000_fw_component_bus_xfer,
.functionality = dib9000_i2c_func,
};
@@ -1754,7 +1763,7 @@ static int dib9000_cfg_gpio(struct dib9000_state *st, u8 num, u8 dir, u8 val)
st->gpio_val |= (val & 0x01) << num; /* set the new value */
dib9000_write_word(st, 774, st->gpio_val);
- dprintk("gpio dir: %04x: gpio val: %04x", st->gpio_dir, st->gpio_val);
+ dprintk("gpio dir: %04x: gpio val: %04x\n", st->gpio_dir, st->gpio_val);
return 0;
}
@@ -1774,7 +1783,7 @@ int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
if ((state->pid_ctrl_index != -2) && (state->pid_ctrl_index < 9)) {
/* postpone the pid filtering cmd */
- dprintk("pid filter cmd postpone");
+ dprintk("pid filter cmd postpone\n");
state->pid_ctrl_index++;
state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER_CTRL;
state->pid_ctrl[state->pid_ctrl_index].onoff = onoff;
@@ -1782,14 +1791,14 @@ int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
}
if (mutex_lock_interruptible(&state->demod_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return -EINTR;
}
val = dib9000_read_word(state, 294 + 1) & 0xffef;
val |= (onoff & 0x1) << 4;
- dprintk("PID filter enabled %d", onoff);
+ dprintk("PID filter enabled %d\n", onoff);
ret = dib9000_write_word(state, 294 + 1, val);
mutex_unlock(&state->demod_lock);
return ret;
@@ -1804,7 +1813,7 @@ int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
if (state->pid_ctrl_index != -2) {
/* postpone the pid filtering cmd */
- dprintk("pid filter postpone");
+ dprintk("pid filter postpone\n");
if (state->pid_ctrl_index < 9) {
state->pid_ctrl_index++;
state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER;
@@ -1812,15 +1821,15 @@ int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
state->pid_ctrl[state->pid_ctrl_index].pid = pid;
state->pid_ctrl[state->pid_ctrl_index].onoff = onoff;
} else
- dprintk("can not add any more pid ctrl cmd");
+ dprintk("can not add any more pid ctrl cmd\n");
return 0;
}
if (mutex_lock_interruptible(&state->demod_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return -EINTR;
}
- dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff);
+ dprintk("Index %x, PID %d, OnOff %d\n", id, pid, onoff);
ret = dib9000_write_word(state, 300 + 1 + id,
onoff ? (1 << 13) | pid : 0);
mutex_unlock(&state->demod_lock);
@@ -1863,7 +1872,7 @@ static int dib9000_sleep(struct dvb_frontend *fe)
int ret = 0;
if (mutex_lock_interruptible(&state->demod_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return -EINTR;
}
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
@@ -1884,16 +1893,17 @@ static int dib9000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_fron
return 0;
}
-static int dib9000_get_frontend(struct dvb_frontend *fe)
+static int dib9000_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct dib9000_state *state = fe->demodulator_priv;
u8 index_frontend, sub_index_frontend;
- fe_status_t stat;
+ enum fe_status stat;
int ret = 0;
if (state->get_frontend_internal == 0) {
if (mutex_lock_interruptible(&state->demod_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return -EINTR;
}
}
@@ -1901,10 +1911,10 @@ static int dib9000_get_frontend(struct dvb_frontend *fe)
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat);
if (stat & FE_HAS_SYNC) {
- dprintk("TPS lock on the slave%i", index_frontend);
+ dprintk("TPS lock on the slave%i\n", index_frontend);
/* synchronize the cache with the other frontends */
- state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend]);
+ state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend], c);
for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL);
sub_index_frontend++) {
if (sub_index_frontend != index_frontend) {
@@ -1938,14 +1948,14 @@ static int dib9000_get_frontend(struct dvb_frontend *fe)
/* synchronize the cache with the other frontends */
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
- state->fe[index_frontend]->dtv_property_cache.inversion = fe->dtv_property_cache.inversion;
- state->fe[index_frontend]->dtv_property_cache.transmission_mode = fe->dtv_property_cache.transmission_mode;
- state->fe[index_frontend]->dtv_property_cache.guard_interval = fe->dtv_property_cache.guard_interval;
- state->fe[index_frontend]->dtv_property_cache.modulation = fe->dtv_property_cache.modulation;
- state->fe[index_frontend]->dtv_property_cache.hierarchy = fe->dtv_property_cache.hierarchy;
- state->fe[index_frontend]->dtv_property_cache.code_rate_HP = fe->dtv_property_cache.code_rate_HP;
- state->fe[index_frontend]->dtv_property_cache.code_rate_LP = fe->dtv_property_cache.code_rate_LP;
- state->fe[index_frontend]->dtv_property_cache.rolloff = fe->dtv_property_cache.rolloff;
+ state->fe[index_frontend]->dtv_property_cache.inversion = c->inversion;
+ state->fe[index_frontend]->dtv_property_cache.transmission_mode = c->transmission_mode;
+ state->fe[index_frontend]->dtv_property_cache.guard_interval = c->guard_interval;
+ state->fe[index_frontend]->dtv_property_cache.modulation = c->modulation;
+ state->fe[index_frontend]->dtv_property_cache.hierarchy = c->hierarchy;
+ state->fe[index_frontend]->dtv_property_cache.code_rate_HP = c->code_rate_HP;
+ state->fe[index_frontend]->dtv_property_cache.code_rate_LP = c->code_rate_LP;
+ state->fe[index_frontend]->dtv_property_cache.rolloff = c->rolloff;
}
ret = 0;
@@ -1989,18 +1999,18 @@ static int dib9000_set_frontend(struct dvb_frontend *fe)
/* check that the correct parameters are set */
if (state->fe[0]->dtv_property_cache.frequency == 0) {
- dprintk("dib9000: must specify frequency ");
+ dprintk("dib9000: must specify frequency\n");
return 0;
}
if (state->fe[0]->dtv_property_cache.bandwidth_hz == 0) {
- dprintk("dib9000: must specify bandwidth ");
+ dprintk("dib9000: must specify bandwidth\n");
return 0;
}
state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */
if (mutex_lock_interruptible(&state->demod_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return 0;
}
@@ -2067,18 +2077,18 @@ static int dib9000_set_frontend(struct dvb_frontend *fe)
/* check the tune result */
if (exit_condition == 1) { /* tune failed */
- dprintk("tune failed");
+ dprintk("tune failed\n");
mutex_unlock(&state->demod_lock);
/* tune failed; put all the pid filtering cmd to junk */
state->pid_ctrl_index = -1;
return 0;
}
- dprintk("tune success on frontend%i", index_frontend_success);
+ dprintk("tune success on frontend%i\n", index_frontend_success);
/* synchronize all the channel cache */
state->get_frontend_internal = 1;
- dib9000_get_frontend(state->fe[0]);
+ dib9000_get_frontend(state->fe[0], &state->fe[0]->dtv_property_cache);
state->get_frontend_internal = 0;
/* retune the other frontends with the found channel */
@@ -2156,14 +2166,14 @@ static u16 dib9000_read_lock(struct dvb_frontend *fe)
return dib9000_read_word(state, 535);
}
-static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
+static int dib9000_read_status(struct dvb_frontend *fe, enum fe_status *stat)
{
struct dib9000_state *state = fe->demodulator_priv;
u8 index_frontend;
u16 lock = 0, lock_slave = 0;
if (mutex_lock_interruptible(&state->demod_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return -EINTR;
}
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
@@ -2196,11 +2206,11 @@ static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber)
int ret = 0;
if (mutex_lock_interruptible(&state->demod_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return -EINTR;
}
if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
ret = -EINTR;
goto error;
}
@@ -2231,7 +2241,7 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
int ret = 0;
if (mutex_lock_interruptible(&state->demod_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return -EINTR;
}
*strength = 0;
@@ -2244,7 +2254,7 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
}
if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
ret = -EINTR;
goto error;
}
@@ -2275,7 +2285,7 @@ static u32 dib9000_get_snr(struct dvb_frontend *fe)
u16 val;
if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return 0;
}
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
@@ -2314,7 +2324,7 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr)
u32 snr_master;
if (mutex_lock_interruptible(&state->demod_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return -EINTR;
}
snr_master = dib9000_get_snr(fe);
@@ -2339,11 +2349,11 @@ static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc)
int ret = 0;
if (mutex_lock_interruptible(&state->demod_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
return -EINTR;
}
if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
- dprintk("could not get the lock");
+ dprintk("could not get the lock\n");
ret = -EINTR;
goto error;
}
@@ -2368,14 +2378,14 @@ int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defaul
u8 new_addr = 0;
struct i2c_device client = {.i2c_adap = i2c };
- client.i2c_write_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL);
+ client.i2c_write_buffer = kzalloc(4, GFP_KERNEL);
if (!client.i2c_write_buffer) {
- dprintk("%s: not enough memory", __func__);
+ dprintk("%s: not enough memory\n", __func__);
return -ENOMEM;
}
- client.i2c_read_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL);
+ client.i2c_read_buffer = kzalloc(4, GFP_KERNEL);
if (!client.i2c_read_buffer) {
- dprintk("%s: not enough memory", __func__);
+ dprintk("%s: not enough memory\n", __func__);
ret = -ENOMEM;
goto error_memory;
}
@@ -2402,7 +2412,7 @@ int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defaul
if (dib9000_identify(&client) == 0) {
client.i2c_addr = default_addr;
if (dib9000_identify(&client) == 0) {
- dprintk("DiB9000 #%d: not identified", k);
+ dprintk("DiB9000 #%d: not identified\n", k);
ret = -EIO;
goto error;
}
@@ -2411,7 +2421,7 @@ int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defaul
dib9000_i2c_write16(&client, 1795, (1 << 10) | (4 << 6));
dib9000_i2c_write16(&client, 1794, (new_addr << 2) | 2);
- dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr);
+ dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr);
}
for (k = 0; k < no_of_demods; k++) {
@@ -2439,34 +2449,16 @@ int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_
while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL))
index_frontend++;
if (index_frontend < MAX_NUMBER_OF_FRONTENDS) {
- dprintk("set slave fe %p to index %i", fe_slave, index_frontend);
+ dprintk("set slave fe %p to index %i\n", fe_slave, index_frontend);
state->fe[index_frontend] = fe_slave;
return 0;
}
- dprintk("too many slave frontend");
+ dprintk("too many slave frontend\n");
return -ENOMEM;
}
EXPORT_SYMBOL(dib9000_set_slave_frontend);
-int dib9000_remove_slave_frontend(struct dvb_frontend *fe)
-{
- struct dib9000_state *state = fe->demodulator_priv;
- u8 index_frontend = 1;
-
- while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL))
- index_frontend++;
- if (index_frontend != 1) {
- dprintk("remove slave fe %p (index %i)", state->fe[index_frontend - 1], index_frontend - 1);
- state->fe[index_frontend] = NULL;
- return 0;
- }
-
- dprintk("no frontend to be removed");
- return -ENODEV;
-}
-EXPORT_SYMBOL(dib9000_remove_slave_frontend);
-
struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index)
{
struct dib9000_state *state = fe->demodulator_priv;
@@ -2477,7 +2469,7 @@ struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int sla
}
EXPORT_SYMBOL(dib9000_get_slave_frontend);
-static struct dvb_frontend_ops dib9000_ops;
+static const struct dvb_frontend_ops dib9000_ops;
struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg)
{
struct dvb_frontend *fe;
@@ -2526,7 +2518,8 @@ struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, c
dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c.i2c_adap, st->i2c.i2c_addr);
st->tuner_adap.dev.parent = i2c_adap->dev.parent;
- strncpy(st->tuner_adap.name, "DIB9000_FW TUNER ACCESS", sizeof(st->tuner_adap.name));
+ strscpy(st->tuner_adap.name, "DIB9000_FW TUNER ACCESS",
+ sizeof(st->tuner_adap.name));
st->tuner_adap.algo = &dib9000_tuner_algo;
st->tuner_adap.algo_data = NULL;
i2c_set_adapdata(&st->tuner_adap, st);
@@ -2534,7 +2527,8 @@ struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, c
goto error;
st->component_bus.dev.parent = i2c_adap->dev.parent;
- strncpy(st->component_bus.name, "DIB9000_FW COMPONENT BUS ACCESS", sizeof(st->component_bus.name));
+ strscpy(st->component_bus.name, "DIB9000_FW COMPONENT BUS ACCESS",
+ sizeof(st->component_bus.name));
st->component_bus.algo = &dib9000_component_bus_algo;
st->component_bus.algo_data = NULL;
st->component_bus_speed = 340;
@@ -2552,15 +2546,15 @@ error:
kfree(st);
return NULL;
}
-EXPORT_SYMBOL(dib9000_attach);
+EXPORT_SYMBOL_GPL(dib9000_attach);
-static struct dvb_frontend_ops dib9000_ops = {
+static const struct dvb_frontend_ops dib9000_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = "DiBcom 9000",
- .frequency_min = 44250000,
- .frequency_max = 867250000,
- .frequency_stepsize = 62500,
+ .frequency_min_hz = 44250 * kHz,
+ .frequency_max_hz = 867250 * kHz,
+ .frequency_stepsize_hz = 62500,
.caps = FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
@@ -2584,7 +2578,7 @@ static struct dvb_frontend_ops dib9000_ops = {
.read_ucblocks = dib9000_read_unc_blocks,
};
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
-MODULE_AUTHOR("Olivier Grenie <ogrenie@dibcom.fr>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
+MODULE_AUTHOR("Olivier Grenie <olivier.grenie@parrot.com>");
MODULE_DESCRIPTION("Driver for the DiBcom 9000 COFDM demodulator");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib9000.h b/drivers/media/dvb-frontends/dib9000.h
index b5781a48034c..bb03362ac7a3 100644
--- a/drivers/media/dvb/frontends/dib9000.h
+++ b/drivers/media/dvb-frontends/dib9000.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef DIB9000_H
#define DIB9000_H
@@ -27,7 +28,7 @@ struct dib9000_config {
#define DEFAULT_DIB9000_I2C_ADDRESS 18
-#if defined(CONFIG_DVB_DIB9000) || (defined(CONFIG_DVB_DIB9000_MODULE) && defined(MODULE))
+#if IS_REACHABLE(CONFIG_DVB_DIB9000)
extern struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg);
extern int dib9000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr);
extern struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe);
@@ -37,7 +38,6 @@ extern int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff);
extern int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff);
extern int dib9000_firmware_post_pll_init(struct dvb_frontend *fe);
extern int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave);
-extern int dib9000_remove_slave_frontend(struct dvb_frontend *fe);
extern struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index);
extern struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe);
extern int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c);
@@ -97,12 +97,6 @@ static inline int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb
return -ENODEV;
}
-int dib9000_remove_slave_frontend(struct dvb_frontend *fe)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return -ENODEV;
-}
-
static inline struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb-frontends/dibx000_common.c
index 43be7238311e..bd5c5d7223aa 100644
--- a/drivers/media/dvb/frontends/dibx000_common.c
+++ b/drivers/media/dvb-frontends/dibx000_common.c
@@ -1,3 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/module.h>
@@ -8,14 +11,18 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
-#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiBX000: "); printk(args); printk("\n"); } } while (0)
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val)
{
int ret;
if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
- dprintk("could not acquire lock");
+ dprintk("could not acquire lock\n");
return -EINVAL;
}
@@ -41,7 +48,7 @@ static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg)
u16 ret;
if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
- dprintk("could not acquire lock");
+ dprintk("could not acquire lock\n");
return 0;
}
@@ -59,7 +66,7 @@ static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg)
mst->msg[1].len = 2;
if (i2c_transfer(mst->i2c_adap, mst->msg, 2) != 2)
- dprintk("i2c read error on %d", reg);
+ dprintk("i2c read error on %d\n", reg);
ret = (mst->i2c_read_buffer[0] << 8) | mst->i2c_read_buffer[1];
mutex_unlock(&mst->i2c_buffer_lock);
@@ -192,7 +199,7 @@ static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst,
enum dibx000_i2c_interface intf)
{
if (mst->device_rev > DIB3000MC && mst->selected_interface != intf) {
- dprintk("selecting interface: %d", intf);
+ dprintk("selecting interface: %d\n", intf);
mst->selected_interface = intf;
return dibx000_write_word(mst, mst->base_reg + 4, intf);
}
@@ -243,12 +250,12 @@ static int dibx000_i2c_master_xfer_gpio34(struct i2c_adapter *i2c_adap, struct i
return num;
}
-static struct i2c_algorithm dibx000_i2c_master_gpio12_xfer_algo = {
+static const struct i2c_algorithm dibx000_i2c_master_gpio12_xfer_algo = {
.master_xfer = dibx000_i2c_master_xfer_gpio12,
.functionality = dibx000_i2c_func,
};
-static struct i2c_algorithm dibx000_i2c_master_gpio34_xfer_algo = {
+static const struct i2c_algorithm dibx000_i2c_master_gpio34_xfer_algo = {
.master_xfer = dibx000_i2c_master_xfer_gpio34,
.functionality = dibx000_i2c_func,
};
@@ -282,15 +289,15 @@ static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap,
int ret;
if (num > 32) {
- dprintk("%s: too much I2C message to be transmitted (%i).\
- Maximum is 32", __func__, num);
+ dprintk("%s: too much I2C message to be transmitted (%i). Maximum is 32",
+ __func__, num);
return -ENOMEM;
}
dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_6_7);
if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
- dprintk("could not acquire lock");
+ dprintk("could not acquire lock\n");
return -EINVAL;
}
@@ -317,7 +324,7 @@ static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap,
return ret;
}
-static struct i2c_algorithm dibx000_i2c_gated_gpio67_algo = {
+static const struct i2c_algorithm dibx000_i2c_gated_gpio67_algo = {
.master_xfer = dibx000_i2c_gated_gpio67_xfer,
.functionality = dibx000_i2c_func,
};
@@ -329,15 +336,15 @@ static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap,
int ret;
if (num > 32) {
- dprintk("%s: too much I2C message to be transmitted (%i).\
- Maximum is 32", __func__, num);
+ dprintk("%s: too much I2C message to be transmitted (%i). Maximum is 32",
+ __func__, num);
return -ENOMEM;
}
dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER);
if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
- dprintk("could not acquire lock");
+ dprintk("could not acquire lock\n");
return -EINVAL;
}
memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num));
@@ -362,7 +369,7 @@ static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap,
return ret;
}
-static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = {
+static const struct i2c_algorithm dibx000_i2c_gated_tuner_algo = {
.master_xfer = dibx000_i2c_gated_tuner_xfer,
.functionality = dibx000_i2c_func,
};
@@ -391,7 +398,7 @@ struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst,
i2c = &mst->master_i2c_adap_gpio67;
break;
default:
- printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n");
+ pr_err("incorrect I2C interface selected\n");
break;
}
@@ -415,10 +422,10 @@ void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst)
EXPORT_SYMBOL(dibx000_reset_i2c_master);
static int i2c_adapter_init(struct i2c_adapter *i2c_adap,
- struct i2c_algorithm *algo, const char *name,
+ const struct i2c_algorithm *algo, const char *name,
struct dibx000_i2c_master *mst)
{
- strncpy(i2c_adap->name, name, sizeof(i2c_adap->name));
+ strscpy(i2c_adap->name, name, sizeof(i2c_adap->name));
i2c_adap->algo = algo;
i2c_adap->algo_data = NULL;
i2c_set_adapdata(i2c_adap, mst);
@@ -434,7 +441,7 @@ int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev,
mutex_init(&mst->i2c_buffer_lock);
if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) {
- dprintk("could not acquire lock");
+ dprintk("could not acquire lock\n");
return -EINVAL;
}
memset(mst->msg, 0, sizeof(struct i2c_msg));
@@ -456,29 +463,25 @@ int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev,
if (i2c_adapter_init
(&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo,
"DiBX000 tuner I2C bus", mst) != 0)
- printk(KERN_ERR
- "DiBX000: could not initialize the tuner i2c_adapter\n");
+ pr_err("could not initialize the tuner i2c_adapter\n");
mst->master_i2c_adap_gpio12.dev.parent = mst->i2c_adap->dev.parent;
if (i2c_adapter_init
(&mst->master_i2c_adap_gpio12, &dibx000_i2c_master_gpio12_xfer_algo,
"DiBX000 master GPIO12 I2C bus", mst) != 0)
- printk(KERN_ERR
- "DiBX000: could not initialize the master i2c_adapter\n");
+ pr_err("could not initialize the master i2c_adapter\n");
mst->master_i2c_adap_gpio34.dev.parent = mst->i2c_adap->dev.parent;
if (i2c_adapter_init
(&mst->master_i2c_adap_gpio34, &dibx000_i2c_master_gpio34_xfer_algo,
"DiBX000 master GPIO34 I2C bus", mst) != 0)
- printk(KERN_ERR
- "DiBX000: could not initialize the master i2c_adapter\n");
+ pr_err("could not initialize the master i2c_adapter\n");
mst->master_i2c_adap_gpio67.dev.parent = mst->i2c_adap->dev.parent;
if (i2c_adapter_init
(&mst->master_i2c_adap_gpio67, &dibx000_i2c_gated_gpio67_algo,
"DiBX000 master GPIO67 I2C bus", mst) != 0)
- printk(KERN_ERR
- "DiBX000: could not initialize the master i2c_adapter\n");
+ pr_err("could not initialize the master i2c_adapter\n");
/* initialize the i2c-master by closing the gate */
dibx000_i2c_gate_ctrl(mst, mst->i2c_write_buffer, 0, 0);
@@ -500,16 +503,6 @@ void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst)
}
EXPORT_SYMBOL(dibx000_exit_i2c_master);
-
-u32 systime(void)
-{
- struct timespec t;
-
- t = current_kernel_time();
- return (t.tv_sec * 10000) + (t.tv_nsec / 100000);
-}
-EXPORT_SYMBOL(systime);
-
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
MODULE_DESCRIPTION("Common function the DiBcom demodulator family");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb-frontends/dibx000_common.h
index 5f484881d7b1..12b58f5c677d 100644
--- a/drivers/media/dvb/frontends/dibx000_common.h
+++ b/drivers/media/dvb-frontends/dibx000_common.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef DIBX000_COMMON_H
#define DIBX000_COMMON_H
@@ -47,8 +48,6 @@ extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst);
extern void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst);
extern int dibx000_i2c_set_speed(struct i2c_adapter *i2c_adap, u16 speed);
-extern u32 systime(void);
-
#define BAND_LBAND 0x01
#define BAND_UHF 0x02
#define BAND_VHF 0x04
@@ -193,7 +192,8 @@ enum frontend_tune_state {
CT_DEMOD_STEP_8,
CT_DEMOD_STEP_9,
CT_DEMOD_STEP_10,
- CT_DEMOD_SEARCH_NEXT = 41,
+ CT_DEMOD_STEP_11,
+ CT_DEMOD_SEARCH_NEXT = 51,
CT_DEMOD_STEP_LOCKED,
CT_DEMOD_STOP,
@@ -223,8 +223,6 @@ struct dvb_frontend_parametersContext {
#define FE_CALLBACK_TIME_NEVER 0xffffffff
-#define ABS(x) ((x < 0) ? (-x) : (x))
-
#define DATA_BUS_ACCESS_MODE_8BIT 0x01
#define DATA_BUS_ACCESS_MODE_16BIT 0x02
#define DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT 0x10
diff --git a/drivers/media/dvb-frontends/drx39xyj/Kconfig b/drivers/media/dvb-frontends/drx39xyj/Kconfig
new file mode 100644
index 000000000000..e29c8351788d
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config DVB_DRX39XYJ
+ tristate "Micronas DRX-J demodulator"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
diff --git a/drivers/media/dvb-frontends/drx39xyj/Makefile b/drivers/media/dvb-frontends/drx39xyj/Makefile
new file mode 100644
index 000000000000..e97ab6130a4a
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+drx39xyj-objs := drxj.o
+
+obj-$(CONFIG_DVB_DRX39XYJ) += drx39xyj.o
+
+ccflags-y += -I$(srctree)/drivers/media/tuners/
diff --git a/drivers/media/dvb-frontends/drx39xyj/drx39xxj.h b/drivers/media/dvb-frontends/drx39xyj/drx39xxj.h
new file mode 100644
index 000000000000..4d83ce5e859b
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/drx39xxj.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Micronas DRX39xx family (drx3933j)
+ *
+ * Written by Devin Heitmueller <devin.heitmueller@kernellabs.com>
+ */
+
+#ifndef DRX39XXJ_H
+#define DRX39XXJ_H
+
+#include <linux/dvb/frontend.h>
+#include <media/dvb_frontend.h>
+#include "drx_driver.h"
+
+struct drx39xxj_state {
+ struct i2c_adapter *i2c;
+ struct drx_demod_instance *demod;
+ struct dvb_frontend frontend;
+ unsigned int i2c_gate_open:1;
+ const struct firmware *fw;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_DRX39XYJ)
+struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c) {
+ return NULL;
+};
+#endif
+
+#endif /* DVB_DUMMY_FE_H */
diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h b/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h
new file mode 100644
index 000000000000..9df34c10d22b
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h
@@ -0,0 +1,254 @@
+/*
+ Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of Trident Microsystems nor Hauppauge Computer Works
+ nor the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*******************************************************************************
+* FILENAME: $Id: drx_dap_fasi.h,v 1.5 2009/07/07 14:21:40 justin Exp $
+*
+* DESCRIPTION:
+* Part of DRX driver.
+* Data access protocol: Fast Access Sequential Interface (fasi)
+* Fast access, because of short addressing format (16 instead of 32 bits addr)
+* Sequential, because of I2C.
+*
+* USAGE:
+* Include.
+*
+* NOTES:
+*
+*
+*******************************************************************************/
+
+/*-------- compilation control switches --------------------------------------*/
+
+#ifndef __DRX_DAP_FASI_H__
+#define __DRX_DAP_FASI_H__
+
+/*-------- Required includes -------------------------------------------------*/
+
+#include "drx_driver.h"
+
+/*-------- Defines, configuring the API --------------------------------------*/
+
+/********************************************
+* Allowed address formats
+********************************************/
+
+/*
+* Comments about short/long addressing format:
+*
+* The DAP FASI offers long address format (4 bytes) and short address format
+* (2 bytes). The DAP can operate in 3 modes:
+* (1) only short
+* (2) only long
+* (3) both long and short but short preferred and long only when necessary
+*
+* These modes must be selected compile time via compile switches.
+* Compile switch settings for the different modes:
+* (1) DRXDAPFASI_LONG_ADDR_ALLOWED=0, DRXDAPFASI_SHORT_ADDR_ALLOWED=1
+* (2) DRXDAPFASI_LONG_ADDR_ALLOWED=1, DRXDAPFASI_SHORT_ADDR_ALLOWED=0
+* (3) DRXDAPFASI_LONG_ADDR_ALLOWED=1, DRXDAPFASI_SHORT_ADDR_ALLOWED=1
+*
+* The default setting will be (3) both long and short.
+* The default setting will need no compile switches.
+* The default setting must be overridden if compile switches are already
+* defined.
+*
+*/
+
+/* set default */
+#if !defined(DRXDAPFASI_LONG_ADDR_ALLOWED)
+#define DRXDAPFASI_LONG_ADDR_ALLOWED 1
+#endif
+
+/* set default */
+#if !defined(DRXDAPFASI_SHORT_ADDR_ALLOWED)
+#define DRXDAPFASI_SHORT_ADDR_ALLOWED 1
+#endif
+
+/* check */
+#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 0) && \
+ (DRXDAPFASI_SHORT_ADDR_ALLOWED == 0))
+#error At least one of short- or long-addressing format must be allowed.
+*; /* illegal statement to force compiler error */
+#endif
+
+/********************************************
+* Single/master multi master setting
+********************************************/
+/*
+* Comments about SINGLE MASTER/MULTI MASTER modes:
+*
+* Consider the two sides:1) the master and 2)the slave.
+*
+* Master:
+* Single/multimaster operation set via DRXDAP_SINGLE_MASTER compile switch
+* + single master mode means no use of repeated starts
+* + multi master mode means use of repeated starts
+* Default is single master.
+* Default can be overridden by setting the compile switch DRXDAP_SINGLE_MASTER.
+*
+* Slave:
+* Single/multi master selected via the flags in the FASI protocol.
+* + single master means remember memory address between i2c packets
+* + multimaster means flush memory address between i2c packets
+* Default is single master, DAP FASI changes multi-master setting silently
+* into single master setting. This cannot be overridden.
+*
+*/
+/* set default */
+#ifndef DRXDAP_SINGLE_MASTER
+#define DRXDAP_SINGLE_MASTER 0
+#endif
+
+/********************************************
+* Chunk/mode checking
+********************************************/
+/*
+* Comments about DRXDAP_MAX_WCHUNKSIZE in single or multi master mode and
+* in combination with short and long addressing format. All text below
+* assumes long addressing format. The table also includes information
+* for short ADDRessing format.
+*
+* In single master mode, data can be written by sending the register address
+* first, then two or four bytes of data in the next packet.
+* Because the device address plus a register address equals five bytes,
+* the minimum chunk size must be five.
+* If ten-bit I2C device addresses are used, the minimum chunk size must be six,
+* because the I2C device address will then occupy two bytes when writing.
+*
+* Data in single master mode is transferred as follows:
+* <S> <devW> a0 a1 a2 a3 <P>
+* <S> <devW> d0 d1 [d2 d3] <P>
+* ..
+* or
+* ..
+* <S> <devW> a0 a1 a2 a3 <P>
+* <S> <devR> --- <P>
+*
+* In multi-master mode, the data must immediately follow the address (an I2C
+* stop resets the internal address), and hence the minimum chunk size is
+* 1 <I2C address> + 4 (register address) + 2 (data to send) = 7 bytes (8 if
+* 10-bit I2C device addresses are used).
+*
+* The 7-bit or 10-bit i2c address parameters is a runtime parameter.
+* The other parameters can be limited via compile time switches.
+*
+*-------------------------------------------------------------------------------
+*
+* Minimum chunk size table (in bytes):
+*
+* +----------------+----------------+
+* | 7b i2c addr | 10b i2c addr |
+* +----------------+----------------+
+* | single | multi | single | multi |
+* ------+--------+-------+--------+-------+
+* short | 3 | 5 | 4 | 6 |
+* long | 5 | 7 | 6 | 8 |
+* ------+--------+-------+--------+-------+
+*
+*/
+
+/* set default */
+#if !defined(DRXDAP_MAX_WCHUNKSIZE)
+#define DRXDAP_MAX_WCHUNKSIZE 254
+#endif
+
+/* check */
+#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 0) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1))
+#if DRXDAP_SINGLE_MASTER
+#define DRXDAP_MAX_WCHUNKSIZE_MIN 3
+#else
+#define DRXDAP_MAX_WCHUNKSIZE_MIN 5
+#endif
+#else
+#if DRXDAP_SINGLE_MASTER
+#define DRXDAP_MAX_WCHUNKSIZE_MIN 5
+#else
+#define DRXDAP_MAX_WCHUNKSIZE_MIN 7
+#endif
+#endif
+
+#if DRXDAP_MAX_WCHUNKSIZE < DRXDAP_MAX_WCHUNKSIZE_MIN
+#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 0) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1))
+#if DRXDAP_SINGLE_MASTER
+#error DRXDAP_MAX_WCHUNKSIZE must be at least 3 in single master mode
+*; /* illegal statement to force compiler error */
+#else
+#error DRXDAP_MAX_WCHUNKSIZE must be at least 5 in multi master mode
+*; /* illegal statement to force compiler error */
+#endif
+#else
+#if DRXDAP_SINGLE_MASTER
+#error DRXDAP_MAX_WCHUNKSIZE must be at least 5 in single master mode
+*; /* illegal statement to force compiler error */
+#else
+#error DRXDAP_MAX_WCHUNKSIZE must be at least 7 in multi master mode
+*; /* illegal statement to force compiler error */
+#endif
+#endif
+#endif
+
+/* set default */
+#if !defined(DRXDAP_MAX_RCHUNKSIZE)
+#define DRXDAP_MAX_RCHUNKSIZE 254
+#endif
+
+/* check */
+#if DRXDAP_MAX_RCHUNKSIZE < 2
+#error DRXDAP_MAX_RCHUNKSIZE must be at least 2
+*; /* illegal statement to force compiler error */
+#endif
+
+/* check */
+#if DRXDAP_MAX_RCHUNKSIZE & 1
+#error DRXDAP_MAX_RCHUNKSIZE must be even
+*; /* illegal statement to force compiler error */
+#endif
+
+/*-------- Public API functions ----------------------------------------------*/
+
+#define DRXDAP_FASI_RMW 0x10000000
+#define DRXDAP_FASI_BROADCAST 0x20000000
+#define DRXDAP_FASI_CLEARCRC 0x80000000
+#define DRXDAP_FASI_SINGLE_MASTER 0xC0000000
+#define DRXDAP_FASI_MULTI_MASTER 0x40000000
+#define DRXDAP_FASI_SMM_SWITCH 0x40000000 /* single/multi master switch */
+#define DRXDAP_FASI_MODEFLAGS 0xC0000000
+#define DRXDAP_FASI_FLAGS 0xF0000000
+
+#define DRXDAP_FASI_ADDR2BLOCK(addr) (((addr)>>22)&0x3F)
+#define DRXDAP_FASI_ADDR2BANK(addr) (((addr)>>16)&0x3F)
+#define DRXDAP_FASI_ADDR2OFFSET(addr) ((addr)&0x7FFF)
+
+#define DRXDAP_FASI_SHORT_FORMAT(addr) (((addr) & 0xFC30FF80) == 0)
+#define DRXDAP_FASI_LONG_FORMAT(addr) (((addr) & 0xFC30FF80) != 0)
+#define DRXDAP_FASI_OFFSET_TOO_LARGE(addr) (((addr) & 0x00008000) != 0)
+
+#endif /* __DRX_DAP_FASI_H__ */
diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_driver.h b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h
new file mode 100644
index 000000000000..2c2fd4bf79cc
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h
@@ -0,0 +1,2326 @@
+/*
+ Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of Trident Microsystems nor Hauppauge Computer Works
+ nor the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef __DRXDRIVER_H__
+#define __DRXDRIVER_H__
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+
+/*
+ * This structure contains the I2C address, the device ID and a user_data pointer.
+ * The user_data pointer can be used for application specific purposes.
+ */
+struct i2c_device_addr {
+ u16 i2c_addr; /* The I2C address of the device. */
+ u16 i2c_dev_id; /* The device identifier. */
+ void *user_data; /* User data pointer */
+};
+
+/*
+* \def IS_I2C_10BIT( addr )
+* \brief Determine if I2C address 'addr' is a 10 bits address or not.
+* \param addr The I2C address.
+* \return int.
+* \retval 0 if address is not a 10 bits I2C address.
+* \retval 1 if address is a 10 bits I2C address.
+*/
+#define IS_I2C_10BIT(addr) \
+ (((addr) & 0xF8) == 0xF0)
+
+/*------------------------------------------------------------------------------
+Exported FUNCTIONS
+------------------------------------------------------------------------------*/
+
+/*
+* \fn drxbsp_i2c_init()
+* \brief Initialize I2C communication module.
+* \return int Return status.
+* \retval 0 Initialization successful.
+* \retval -EIO Initialization failed.
+*/
+int drxbsp_i2c_init(void);
+
+/*
+* \fn drxbsp_i2c_term()
+* \brief Terminate I2C communication module.
+* \return int Return status.
+* \retval 0 Termination successful.
+* \retval -EIO Termination failed.
+*/
+int drxbsp_i2c_term(void);
+
+/*
+* \fn int drxbsp_i2c_write_read( struct i2c_device_addr *w_dev_addr,
+* u16 w_count,
+* u8 * wData,
+* struct i2c_device_addr *r_dev_addr,
+* u16 r_count,
+* u8 * r_data)
+* \brief Read and/or write count bytes from I2C bus, store them in data[].
+* \param w_dev_addr The device i2c address and the device ID to write to
+* \param w_count The number of bytes to write
+* \param wData The array to write the data to
+* \param r_dev_addr The device i2c address and the device ID to read from
+* \param r_count The number of bytes to read
+* \param r_data The array to read the data from
+* \return int Return status.
+* \retval 0 Success.
+* \retval -EIO Failure.
+* \retval -EINVAL Parameter 'wcount' is not zero but parameter
+* 'wdata' contains NULL.
+* Idem for 'rcount' and 'rdata'.
+* Both w_dev_addr and r_dev_addr are NULL.
+*
+* This function must implement an atomic write and/or read action on the I2C bus
+* No other process may use the I2C bus when this function is executing.
+* The critical section of this function runs from and including the I2C
+* write, up to and including the I2C read action.
+*
+* The device ID can be useful if several devices share an I2C address.
+* It can be used to control a "switch" on the I2C bus to the correct device.
+*/
+int drxbsp_i2c_write_read(struct i2c_device_addr *w_dev_addr,
+ u16 w_count,
+ u8 *wData,
+ struct i2c_device_addr *r_dev_addr,
+ u16 r_count, u8 *r_data);
+
+/*
+* \fn drxbsp_i2c_error_text()
+* \brief Returns a human readable error.
+* Counter part of numerical drx_i2c_error_g.
+*
+* \return char* Pointer to human readable error text.
+*/
+char *drxbsp_i2c_error_text(void);
+
+/*
+* \var drx_i2c_error_g;
+* \brief I2C specific error codes, platform dependent.
+*/
+extern int drx_i2c_error_g;
+
+#define TUNER_MODE_SUB0 0x0001 /* for sub-mode (e.g. RF-AGC setting) */
+#define TUNER_MODE_SUB1 0x0002 /* for sub-mode (e.g. RF-AGC setting) */
+#define TUNER_MODE_SUB2 0x0004 /* for sub-mode (e.g. RF-AGC setting) */
+#define TUNER_MODE_SUB3 0x0008 /* for sub-mode (e.g. RF-AGC setting) */
+#define TUNER_MODE_SUB4 0x0010 /* for sub-mode (e.g. RF-AGC setting) */
+#define TUNER_MODE_SUB5 0x0020 /* for sub-mode (e.g. RF-AGC setting) */
+#define TUNER_MODE_SUB6 0x0040 /* for sub-mode (e.g. RF-AGC setting) */
+#define TUNER_MODE_SUB7 0x0080 /* for sub-mode (e.g. RF-AGC setting) */
+
+#define TUNER_MODE_DIGITAL 0x0100 /* for digital channel (e.g. DVB-T) */
+#define TUNER_MODE_ANALOG 0x0200 /* for analog channel (e.g. PAL) */
+#define TUNER_MODE_SWITCH 0x0400 /* during channel switch & scanning */
+#define TUNER_MODE_LOCK 0x0800 /* after tuner has locked */
+#define TUNER_MODE_6MHZ 0x1000 /* for 6MHz bandwidth channels */
+#define TUNER_MODE_7MHZ 0x2000 /* for 7MHz bandwidth channels */
+#define TUNER_MODE_8MHZ 0x4000 /* for 8MHz bandwidth channels */
+
+#define TUNER_MODE_SUB_MAX 8
+#define TUNER_MODE_SUBALL (TUNER_MODE_SUB0 | TUNER_MODE_SUB1 | \
+ TUNER_MODE_SUB2 | TUNER_MODE_SUB3 | \
+ TUNER_MODE_SUB4 | TUNER_MODE_SUB5 | \
+ TUNER_MODE_SUB6 | TUNER_MODE_SUB7)
+
+
+enum tuner_lock_status {
+ TUNER_LOCKED,
+ TUNER_NOT_LOCKED
+};
+
+struct tuner_common {
+ char *name; /* Tuner brand & type name */
+ s32 min_freq_rf; /* Lowest RF input frequency, in kHz */
+ s32 max_freq_rf; /* Highest RF input frequency, in kHz */
+
+ u8 sub_mode; /* Index to sub-mode in use */
+ char ***sub_mode_descriptions; /* Pointer to description of sub-modes */
+ u8 sub_modes; /* Number of available sub-modes */
+
+ /* The following fields will be either 0, NULL or false and do not need
+ initialisation */
+ void *self_check; /* gives proof of initialization */
+ bool programmed; /* only valid if self_check is OK */
+ s32 r_ffrequency; /* only valid if programmed */
+ s32 i_ffrequency; /* only valid if programmed */
+
+ void *my_user_data; /* pointer to associated demod instance */
+ u16 my_capabilities; /* value for storing application flags */
+};
+
+struct tuner_instance;
+
+typedef int(*tuner_open_func_t) (struct tuner_instance *tuner);
+typedef int(*tuner_close_func_t) (struct tuner_instance *tuner);
+
+typedef int(*tuner_set_frequency_func_t) (struct tuner_instance *tuner,
+ u32 mode,
+ s32
+ frequency);
+
+typedef int(*tuner_get_frequency_func_t) (struct tuner_instance *tuner,
+ u32 mode,
+ s32 *
+ r_ffrequency,
+ s32 *
+ i_ffrequency);
+
+typedef int(*tuner_lock_status_func_t) (struct tuner_instance *tuner,
+ enum tuner_lock_status *
+ lock_stat);
+
+typedef int(*tune_ri2c_write_read_func_t) (struct tuner_instance *tuner,
+ struct i2c_device_addr *
+ w_dev_addr, u16 w_count,
+ u8 *wData,
+ struct i2c_device_addr *
+ r_dev_addr, u16 r_count,
+ u8 *r_data);
+
+struct tuner_ops {
+ tuner_open_func_t open_func;
+ tuner_close_func_t close_func;
+ tuner_set_frequency_func_t set_frequency_func;
+ tuner_get_frequency_func_t get_frequency_func;
+ tuner_lock_status_func_t lock_status_func;
+ tune_ri2c_write_read_func_t i2c_write_read_func;
+
+};
+
+struct tuner_instance {
+ struct i2c_device_addr my_i2c_dev_addr;
+ struct tuner_common *my_common_attr;
+ void *my_ext_attr;
+ struct tuner_ops *my_funct;
+};
+
+int drxbsp_tuner_set_frequency(struct tuner_instance *tuner,
+ u32 mode,
+ s32 frequency);
+
+int drxbsp_tuner_get_frequency(struct tuner_instance *tuner,
+ u32 mode,
+ s32 *r_ffrequency,
+ s32 *i_ffrequency);
+
+int drxbsp_tuner_default_i2c_write_read(struct tuner_instance *tuner,
+ struct i2c_device_addr *w_dev_addr,
+ u16 w_count,
+ u8 *wData,
+ struct i2c_device_addr *r_dev_addr,
+ u16 r_count, u8 *r_data);
+
+/*************
+*
+* This section configures the DRX Data Access Protocols (DAPs).
+*
+**************/
+
+/*
+* \def DRXDAP_SINGLE_MASTER
+* \brief Enable I2C single or I2C multimaster mode on host.
+*
+* Set to 1 to enable single master mode
+* Set to 0 to enable multi master mode
+*
+* The actual DAP implementation may be restricted to only one of the modes.
+* A compiler warning or error will be generated if the DAP implementation
+* overrides or cannot handle the mode defined below.
+*/
+#ifndef DRXDAP_SINGLE_MASTER
+#define DRXDAP_SINGLE_MASTER 1
+#endif
+
+/*
+* \def DRXDAP_MAX_WCHUNKSIZE
+* \brief Defines maximum chunksize of an i2c write action by host.
+*
+* This indicates the maximum size of data the I2C device driver is able to
+* write at a time. This includes I2C device address and register addressing.
+*
+* This maximum size may be restricted by the actual DAP implementation.
+* A compiler warning or error will be generated if the DAP implementation
+* overrides or cannot handle the chunksize defined below.
+*
+* Beware that the DAP uses DRXDAP_MAX_WCHUNKSIZE to create a temporary data
+* buffer. Do not undefine or choose too large, unless your system is able to
+* handle a stack buffer of that size.
+*
+*/
+#ifndef DRXDAP_MAX_WCHUNKSIZE
+#define DRXDAP_MAX_WCHUNKSIZE 60
+#endif
+
+/*
+* \def DRXDAP_MAX_RCHUNKSIZE
+* \brief Defines maximum chunksize of an i2c read action by host.
+*
+* This indicates the maximum size of data the I2C device driver is able to read
+* at a time. Minimum value is 2. Also, the read chunk size must be even.
+*
+* This maximum size may be restricted by the actual DAP implementation.
+* A compiler warning or error will be generated if the DAP implementation
+* overrides or cannot handle the chunksize defined below.
+*/
+#ifndef DRXDAP_MAX_RCHUNKSIZE
+#define DRXDAP_MAX_RCHUNKSIZE 60
+#endif
+
+/*************
+*
+* This section describes drxdriver defines.
+*
+**************/
+
+/*
+* \def DRX_UNKNOWN
+* \brief Generic UNKNOWN value for DRX enumerated types.
+*
+* Used to indicate that the parameter value is unknown or not yet initialized.
+*/
+#ifndef DRX_UNKNOWN
+#define DRX_UNKNOWN (254)
+#endif
+
+/*
+* \def DRX_AUTO
+* \brief Generic AUTO value for DRX enumerated types.
+*
+* Used to instruct the driver to automatically determine the value of the
+* parameter.
+*/
+#ifndef DRX_AUTO
+#define DRX_AUTO (255)
+#endif
+
+/*************
+*
+* This section describes flag definitions for the device capbilities.
+*
+**************/
+
+/*
+* \brief LNA capability flag
+*
+* Device has a Low Noise Amplifier
+*
+*/
+#define DRX_CAPABILITY_HAS_LNA (1UL << 0)
+/*
+* \brief OOB-RX capability flag
+*
+* Device has OOB-RX
+*
+*/
+#define DRX_CAPABILITY_HAS_OOBRX (1UL << 1)
+/*
+* \brief ATV capability flag
+*
+* Device has ATV
+*
+*/
+#define DRX_CAPABILITY_HAS_ATV (1UL << 2)
+/*
+* \brief DVB-T capability flag
+*
+* Device has DVB-T
+*
+*/
+#define DRX_CAPABILITY_HAS_DVBT (1UL << 3)
+/*
+* \brief ITU-B capability flag
+*
+* Device has ITU-B
+*
+*/
+#define DRX_CAPABILITY_HAS_ITUB (1UL << 4)
+/*
+* \brief Audio capability flag
+*
+* Device has Audio
+*
+*/
+#define DRX_CAPABILITY_HAS_AUD (1UL << 5)
+/*
+* \brief SAW switch capability flag
+*
+* Device has SAW switch
+*
+*/
+#define DRX_CAPABILITY_HAS_SAWSW (1UL << 6)
+/*
+* \brief GPIO1 capability flag
+*
+* Device has GPIO1
+*
+*/
+#define DRX_CAPABILITY_HAS_GPIO1 (1UL << 7)
+/*
+* \brief GPIO2 capability flag
+*
+* Device has GPIO2
+*
+*/
+#define DRX_CAPABILITY_HAS_GPIO2 (1UL << 8)
+/*
+* \brief IRQN capability flag
+*
+* Device has IRQN
+*
+*/
+#define DRX_CAPABILITY_HAS_IRQN (1UL << 9)
+/*
+* \brief 8VSB capability flag
+*
+* Device has 8VSB
+*
+*/
+#define DRX_CAPABILITY_HAS_8VSB (1UL << 10)
+/*
+* \brief SMA-TX capability flag
+*
+* Device has SMATX
+*
+*/
+#define DRX_CAPABILITY_HAS_SMATX (1UL << 11)
+/*
+* \brief SMA-RX capability flag
+*
+* Device has SMARX
+*
+*/
+#define DRX_CAPABILITY_HAS_SMARX (1UL << 12)
+/*
+* \brief ITU-A/C capability flag
+*
+* Device has ITU-A/C
+*
+*/
+#define DRX_CAPABILITY_HAS_ITUAC (1UL << 13)
+
+/*-------------------------------------------------------------------------
+MACROS
+-------------------------------------------------------------------------*/
+/* Macros to stringify the version number */
+#define DRX_VERSIONSTRING(MAJOR, MINOR, PATCH) \
+ DRX_VERSIONSTRING_HELP(MAJOR)"." \
+ DRX_VERSIONSTRING_HELP(MINOR)"." \
+ DRX_VERSIONSTRING_HELP(PATCH)
+#define DRX_VERSIONSTRING_HELP(NUM) #NUM
+
+/*
+* \brief Macro to create byte array elements from 16 bit integers.
+* This macro is used to create byte arrays for block writes.
+* Block writes speed up I2C traffic between host and demod.
+* The macro takes care of the required byte order in a 16 bits word.
+* x->lowbyte(x), highbyte(x)
+*/
+#define DRX_16TO8(x) ((u8) (((u16)x) & 0xFF)), \
+ ((u8)((((u16)x)>>8)&0xFF))
+
+/*
+* \brief Macro to convert 16 bit register value to a s32
+*/
+#define DRX_U16TODRXFREQ(x) ((x & 0x8000) ? \
+ ((s32) \
+ (((u32) x) | 0xFFFF0000)) : \
+ ((s32) x))
+
+/*-------------------------------------------------------------------------
+ENUM
+-------------------------------------------------------------------------*/
+
+/*
+* \enum enum drx_standard
+* \brief Modulation standards.
+*/
+enum drx_standard {
+ DRX_STANDARD_DVBT = 0, /*< Terrestrial DVB-T. */
+ DRX_STANDARD_8VSB, /*< Terrestrial 8VSB. */
+ DRX_STANDARD_NTSC, /*< Terrestrial\Cable analog NTSC. */
+ DRX_STANDARD_PAL_SECAM_BG,
+ /*< Terrestrial analog PAL/SECAM B/G */
+ DRX_STANDARD_PAL_SECAM_DK,
+ /*< Terrestrial analog PAL/SECAM D/K */
+ DRX_STANDARD_PAL_SECAM_I,
+ /*< Terrestrial analog PAL/SECAM I */
+ DRX_STANDARD_PAL_SECAM_L,
+ /*< Terrestrial analog PAL/SECAM L
+ with negative modulation */
+ DRX_STANDARD_PAL_SECAM_LP,
+ /*< Terrestrial analog PAL/SECAM L
+ with positive modulation */
+ DRX_STANDARD_ITU_A, /*< Cable ITU ANNEX A. */
+ DRX_STANDARD_ITU_B, /*< Cable ITU ANNEX B. */
+ DRX_STANDARD_ITU_C, /*< Cable ITU ANNEX C. */
+ DRX_STANDARD_ITU_D, /*< Cable ITU ANNEX D. */
+ DRX_STANDARD_FM, /*< Terrestrial\Cable FM radio */
+ DRX_STANDARD_DTMB, /*< Terrestrial DTMB standard (China)*/
+ DRX_STANDARD_UNKNOWN = DRX_UNKNOWN,
+ /*< Standard unknown. */
+ DRX_STANDARD_AUTO = DRX_AUTO
+ /*< Autodetect standard. */
+};
+
+/*
+* \enum enum drx_standard
+* \brief Modulation sub-standards.
+*/
+enum drx_substandard {
+ DRX_SUBSTANDARD_MAIN = 0, /*< Main subvariant of standard */
+ DRX_SUBSTANDARD_ATV_BG_SCANDINAVIA,
+ DRX_SUBSTANDARD_ATV_DK_POLAND,
+ DRX_SUBSTANDARD_ATV_DK_CHINA,
+ DRX_SUBSTANDARD_UNKNOWN = DRX_UNKNOWN,
+ /*< Sub-standard unknown. */
+ DRX_SUBSTANDARD_AUTO = DRX_AUTO
+ /*< Auto (default) sub-standard */
+};
+
+/*
+* \enum enum drx_bandwidth
+* \brief Channel bandwidth or channel spacing.
+*/
+enum drx_bandwidth {
+ DRX_BANDWIDTH_8MHZ = 0, /*< Bandwidth 8 MHz. */
+ DRX_BANDWIDTH_7MHZ, /*< Bandwidth 7 MHz. */
+ DRX_BANDWIDTH_6MHZ, /*< Bandwidth 6 MHz. */
+ DRX_BANDWIDTH_UNKNOWN = DRX_UNKNOWN,
+ /*< Bandwidth unknown. */
+ DRX_BANDWIDTH_AUTO = DRX_AUTO
+ /*< Auto Set Bandwidth */
+};
+
+/*
+* \enum enum drx_mirror
+* \brief Indicate if channel spectrum is mirrored or not.
+*/
+enum drx_mirror {
+ DRX_MIRROR_NO = 0, /*< Spectrum is not mirrored. */
+ DRX_MIRROR_YES, /*< Spectrum is mirrored. */
+ DRX_MIRROR_UNKNOWN = DRX_UNKNOWN,
+ /*< Unknown if spectrum is mirrored. */
+ DRX_MIRROR_AUTO = DRX_AUTO
+ /*< Autodetect if spectrum is mirrored. */
+};
+
+/*
+* \enum enum drx_modulation
+* \brief Constellation type of the channel.
+*/
+enum drx_modulation {
+ DRX_CONSTELLATION_BPSK = 0, /*< Modulation is BPSK. */
+ DRX_CONSTELLATION_QPSK, /*< Constellation is QPSK. */
+ DRX_CONSTELLATION_PSK8, /*< Constellation is PSK8. */
+ DRX_CONSTELLATION_QAM16, /*< Constellation is QAM16. */
+ DRX_CONSTELLATION_QAM32, /*< Constellation is QAM32. */
+ DRX_CONSTELLATION_QAM64, /*< Constellation is QAM64. */
+ DRX_CONSTELLATION_QAM128, /*< Constellation is QAM128. */
+ DRX_CONSTELLATION_QAM256, /*< Constellation is QAM256. */
+ DRX_CONSTELLATION_QAM512, /*< Constellation is QAM512. */
+ DRX_CONSTELLATION_QAM1024, /*< Constellation is QAM1024. */
+ DRX_CONSTELLATION_QPSK_NR, /*< Constellation is QPSK_NR */
+ DRX_CONSTELLATION_UNKNOWN = DRX_UNKNOWN,
+ /*< Constellation unknown. */
+ DRX_CONSTELLATION_AUTO = DRX_AUTO
+ /*< Autodetect constellation. */
+};
+
+/*
+* \enum enum drx_hierarchy
+* \brief Hierarchy of the channel.
+*/
+enum drx_hierarchy {
+ DRX_HIERARCHY_NONE = 0, /*< None hierarchical channel. */
+ DRX_HIERARCHY_ALPHA1, /*< Hierarchical channel, alpha=1. */
+ DRX_HIERARCHY_ALPHA2, /*< Hierarchical channel, alpha=2. */
+ DRX_HIERARCHY_ALPHA4, /*< Hierarchical channel, alpha=4. */
+ DRX_HIERARCHY_UNKNOWN = DRX_UNKNOWN,
+ /*< Hierarchy unknown. */
+ DRX_HIERARCHY_AUTO = DRX_AUTO
+ /*< Autodetect hierarchy. */
+};
+
+/*
+* \enum enum drx_priority
+* \brief Channel priority in case of hierarchical transmission.
+*/
+enum drx_priority {
+ DRX_PRIORITY_LOW = 0, /*< Low priority channel. */
+ DRX_PRIORITY_HIGH, /*< High priority channel. */
+ DRX_PRIORITY_UNKNOWN = DRX_UNKNOWN
+ /*< Priority unknown. */
+};
+
+/*
+* \enum enum drx_coderate
+* \brief Channel priority in case of hierarchical transmission.
+*/
+enum drx_coderate {
+ DRX_CODERATE_1DIV2 = 0, /*< Code rate 1/2nd. */
+ DRX_CODERATE_2DIV3, /*< Code rate 2/3nd. */
+ DRX_CODERATE_3DIV4, /*< Code rate 3/4nd. */
+ DRX_CODERATE_5DIV6, /*< Code rate 5/6nd. */
+ DRX_CODERATE_7DIV8, /*< Code rate 7/8nd. */
+ DRX_CODERATE_UNKNOWN = DRX_UNKNOWN,
+ /*< Code rate unknown. */
+ DRX_CODERATE_AUTO = DRX_AUTO
+ /*< Autodetect code rate. */
+};
+
+/*
+* \enum enum drx_guard
+* \brief Guard interval of a channel.
+*/
+enum drx_guard {
+ DRX_GUARD_1DIV32 = 0, /*< Guard interval 1/32nd. */
+ DRX_GUARD_1DIV16, /*< Guard interval 1/16th. */
+ DRX_GUARD_1DIV8, /*< Guard interval 1/8th. */
+ DRX_GUARD_1DIV4, /*< Guard interval 1/4th. */
+ DRX_GUARD_UNKNOWN = DRX_UNKNOWN,
+ /*< Guard interval unknown. */
+ DRX_GUARD_AUTO = DRX_AUTO
+ /*< Autodetect guard interval. */
+};
+
+/*
+* \enum enum drx_fft_mode
+* \brief FFT mode.
+*/
+enum drx_fft_mode {
+ DRX_FFTMODE_2K = 0, /*< 2K FFT mode. */
+ DRX_FFTMODE_4K, /*< 4K FFT mode. */
+ DRX_FFTMODE_8K, /*< 8K FFT mode. */
+ DRX_FFTMODE_UNKNOWN = DRX_UNKNOWN,
+ /*< FFT mode unknown. */
+ DRX_FFTMODE_AUTO = DRX_AUTO
+ /*< Autodetect FFT mode. */
+};
+
+/*
+* \enum enum drx_classification
+* \brief Channel classification.
+*/
+enum drx_classification {
+ DRX_CLASSIFICATION_GAUSS = 0, /*< Gaussion noise. */
+ DRX_CLASSIFICATION_HVY_GAUSS, /*< Heavy Gaussion noise. */
+ DRX_CLASSIFICATION_COCHANNEL, /*< Co-channel. */
+ DRX_CLASSIFICATION_STATIC, /*< Static echo. */
+ DRX_CLASSIFICATION_MOVING, /*< Moving echo. */
+ DRX_CLASSIFICATION_ZERODB, /*< Zero dB echo. */
+ DRX_CLASSIFICATION_UNKNOWN = DRX_UNKNOWN,
+ /*< Unknown classification */
+ DRX_CLASSIFICATION_AUTO = DRX_AUTO
+ /*< Autodetect classification. */
+};
+
+/*
+* /enum enum drx_interleave_mode
+* /brief Interleave modes
+*/
+enum drx_interleave_mode {
+ DRX_INTERLEAVEMODE_I128_J1 = 0,
+ DRX_INTERLEAVEMODE_I128_J1_V2,
+ DRX_INTERLEAVEMODE_I128_J2,
+ DRX_INTERLEAVEMODE_I64_J2,
+ DRX_INTERLEAVEMODE_I128_J3,
+ DRX_INTERLEAVEMODE_I32_J4,
+ DRX_INTERLEAVEMODE_I128_J4,
+ DRX_INTERLEAVEMODE_I16_J8,
+ DRX_INTERLEAVEMODE_I128_J5,
+ DRX_INTERLEAVEMODE_I8_J16,
+ DRX_INTERLEAVEMODE_I128_J6,
+ DRX_INTERLEAVEMODE_RESERVED_11,
+ DRX_INTERLEAVEMODE_I128_J7,
+ DRX_INTERLEAVEMODE_RESERVED_13,
+ DRX_INTERLEAVEMODE_I128_J8,
+ DRX_INTERLEAVEMODE_RESERVED_15,
+ DRX_INTERLEAVEMODE_I12_J17,
+ DRX_INTERLEAVEMODE_I5_J4,
+ DRX_INTERLEAVEMODE_B52_M240,
+ DRX_INTERLEAVEMODE_B52_M720,
+ DRX_INTERLEAVEMODE_B52_M48,
+ DRX_INTERLEAVEMODE_B52_M0,
+ DRX_INTERLEAVEMODE_UNKNOWN = DRX_UNKNOWN,
+ /*< Unknown interleave mode */
+ DRX_INTERLEAVEMODE_AUTO = DRX_AUTO
+ /*< Autodetect interleave mode */
+};
+
+/*
+* \enum enum drx_carrier_mode
+* \brief Channel Carrier Mode.
+*/
+enum drx_carrier_mode {
+ DRX_CARRIER_MULTI = 0, /*< Multi carrier mode */
+ DRX_CARRIER_SINGLE, /*< Single carrier mode */
+ DRX_CARRIER_UNKNOWN = DRX_UNKNOWN,
+ /*< Carrier mode unknown. */
+ DRX_CARRIER_AUTO = DRX_AUTO /*< Autodetect carrier mode */
+};
+
+/*
+* \enum enum drx_frame_mode
+* \brief Channel Frame Mode.
+*/
+enum drx_frame_mode {
+ DRX_FRAMEMODE_420 = 0, /*< 420 with variable PN */
+ DRX_FRAMEMODE_595, /*< 595 */
+ DRX_FRAMEMODE_945, /*< 945 with variable PN */
+ DRX_FRAMEMODE_420_FIXED_PN,
+ /*< 420 with fixed PN */
+ DRX_FRAMEMODE_945_FIXED_PN,
+ /*< 945 with fixed PN */
+ DRX_FRAMEMODE_UNKNOWN = DRX_UNKNOWN,
+ /*< Frame mode unknown. */
+ DRX_FRAMEMODE_AUTO = DRX_AUTO
+ /*< Autodetect frame mode */
+};
+
+/*
+* \enum enum drx_tps_frame
+* \brief Frame number in current super-frame.
+*/
+enum drx_tps_frame {
+ DRX_TPS_FRAME1 = 0, /*< TPS frame 1. */
+ DRX_TPS_FRAME2, /*< TPS frame 2. */
+ DRX_TPS_FRAME3, /*< TPS frame 3. */
+ DRX_TPS_FRAME4, /*< TPS frame 4. */
+ DRX_TPS_FRAME_UNKNOWN = DRX_UNKNOWN
+ /*< TPS frame unknown. */
+};
+
+/*
+* \enum enum drx_ldpc
+* \brief TPS LDPC .
+*/
+enum drx_ldpc {
+ DRX_LDPC_0_4 = 0, /*< LDPC 0.4 */
+ DRX_LDPC_0_6, /*< LDPC 0.6 */
+ DRX_LDPC_0_8, /*< LDPC 0.8 */
+ DRX_LDPC_UNKNOWN = DRX_UNKNOWN,
+ /*< LDPC unknown. */
+ DRX_LDPC_AUTO = DRX_AUTO /*< Autodetect LDPC */
+};
+
+/*
+* \enum enum drx_pilot_mode
+* \brief Pilot modes in DTMB.
+*/
+enum drx_pilot_mode {
+ DRX_PILOT_ON = 0, /*< Pilot On */
+ DRX_PILOT_OFF, /*< Pilot Off */
+ DRX_PILOT_UNKNOWN = DRX_UNKNOWN,
+ /*< Pilot unknown. */
+ DRX_PILOT_AUTO = DRX_AUTO /*< Autodetect Pilot */
+};
+
+/*
+ * enum drxu_code_action - indicate if firmware has to be uploaded or verified.
+ * @UCODE_UPLOAD: Upload the microcode image to device
+ * @UCODE_VERIFY: Compare microcode image with code on device
+ */
+enum drxu_code_action {
+ UCODE_UPLOAD,
+ UCODE_VERIFY
+};
+
+/*
+* \enum enum drx_lock_status * \brief Used to reflect current lock status of demodulator.
+*
+* The generic lock states have device dependent semantics.
+
+ DRX_NEVER_LOCK = 0,
+ **< Device will never lock on this signal *
+ DRX_NOT_LOCKED,
+ **< Device has no lock at all *
+ DRX_LOCK_STATE_1,
+ **< Generic lock state *
+ DRX_LOCK_STATE_2,
+ **< Generic lock state *
+ DRX_LOCK_STATE_3,
+ **< Generic lock state *
+ DRX_LOCK_STATE_4,
+ **< Generic lock state *
+ DRX_LOCK_STATE_5,
+ **< Generic lock state *
+ DRX_LOCK_STATE_6,
+ **< Generic lock state *
+ DRX_LOCK_STATE_7,
+ **< Generic lock state *
+ DRX_LOCK_STATE_8,
+ **< Generic lock state *
+ DRX_LOCK_STATE_9,
+ **< Generic lock state *
+ DRX_LOCKED **< Device is in lock *
+*/
+
+enum drx_lock_status {
+ DRX_NEVER_LOCK = 0,
+ DRX_NOT_LOCKED,
+ DRX_LOCK_STATE_1,
+ DRX_LOCK_STATE_2,
+ DRX_LOCK_STATE_3,
+ DRX_LOCK_STATE_4,
+ DRX_LOCK_STATE_5,
+ DRX_LOCK_STATE_6,
+ DRX_LOCK_STATE_7,
+ DRX_LOCK_STATE_8,
+ DRX_LOCK_STATE_9,
+ DRX_LOCKED
+};
+
+/*
+* \enum enum drx_uio* \brief Used to address a User IO (UIO).
+*/
+enum drx_uio {
+ DRX_UIO1,
+ DRX_UIO2,
+ DRX_UIO3,
+ DRX_UIO4,
+ DRX_UIO5,
+ DRX_UIO6,
+ DRX_UIO7,
+ DRX_UIO8,
+ DRX_UIO9,
+ DRX_UIO10,
+ DRX_UIO11,
+ DRX_UIO12,
+ DRX_UIO13,
+ DRX_UIO14,
+ DRX_UIO15,
+ DRX_UIO16,
+ DRX_UIO17,
+ DRX_UIO18,
+ DRX_UIO19,
+ DRX_UIO20,
+ DRX_UIO21,
+ DRX_UIO22,
+ DRX_UIO23,
+ DRX_UIO24,
+ DRX_UIO25,
+ DRX_UIO26,
+ DRX_UIO27,
+ DRX_UIO28,
+ DRX_UIO29,
+ DRX_UIO30,
+ DRX_UIO31,
+ DRX_UIO32,
+ DRX_UIO_MAX = DRX_UIO32
+};
+
+/*
+* \enum enum drxuio_mode * \brief Used to configure the modus oprandi of a UIO.
+*
+* DRX_UIO_MODE_FIRMWARE is an old uio mode.
+* It is replaced by the modes DRX_UIO_MODE_FIRMWARE0 .. DRX_UIO_MODE_FIRMWARE9.
+* To be backward compatible DRX_UIO_MODE_FIRMWARE is equivalent to
+* DRX_UIO_MODE_FIRMWARE0.
+*/
+enum drxuio_mode {
+ DRX_UIO_MODE_DISABLE = 0x01,
+ /*< not used, pin is configured as input */
+ DRX_UIO_MODE_READWRITE = 0x02,
+ /*< used for read/write by application */
+ DRX_UIO_MODE_FIRMWARE = 0x04,
+ /*< controlled by firmware, function 0 */
+ DRX_UIO_MODE_FIRMWARE0 = DRX_UIO_MODE_FIRMWARE,
+ /*< same as above */
+ DRX_UIO_MODE_FIRMWARE1 = 0x08,
+ /*< controlled by firmware, function 1 */
+ DRX_UIO_MODE_FIRMWARE2 = 0x10,
+ /*< controlled by firmware, function 2 */
+ DRX_UIO_MODE_FIRMWARE3 = 0x20,
+ /*< controlled by firmware, function 3 */
+ DRX_UIO_MODE_FIRMWARE4 = 0x40,
+ /*< controlled by firmware, function 4 */
+ DRX_UIO_MODE_FIRMWARE5 = 0x80
+ /*< controlled by firmware, function 5 */
+};
+
+/*
+* \enum enum drxoob_downstream_standard * \brief Used to select OOB standard.
+*
+* Based on ANSI 55-1 and 55-2
+*/
+enum drxoob_downstream_standard {
+ DRX_OOB_MODE_A = 0,
+ /*< ANSI 55-1 */
+ DRX_OOB_MODE_B_GRADE_A,
+ /*< ANSI 55-2 A */
+ DRX_OOB_MODE_B_GRADE_B
+ /*< ANSI 55-2 B */
+};
+
+/*-------------------------------------------------------------------------
+STRUCTS
+-------------------------------------------------------------------------*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== CTRL CFG related data structures ========================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+#ifndef DRX_CFG_BASE
+#define DRX_CFG_BASE 0
+#endif
+
+#define DRX_CFG_MPEG_OUTPUT (DRX_CFG_BASE + 0) /* MPEG TS output */
+#define DRX_CFG_PKTERR (DRX_CFG_BASE + 1) /* Packet Error */
+#define DRX_CFG_SYMCLK_OFFS (DRX_CFG_BASE + 2) /* Symbol Clk Offset */
+#define DRX_CFG_SMA (DRX_CFG_BASE + 3) /* Smart Antenna */
+#define DRX_CFG_PINSAFE (DRX_CFG_BASE + 4) /* Pin safe mode */
+#define DRX_CFG_SUBSTANDARD (DRX_CFG_BASE + 5) /* substandard */
+#define DRX_CFG_AUD_VOLUME (DRX_CFG_BASE + 6) /* volume */
+#define DRX_CFG_AUD_RDS (DRX_CFG_BASE + 7) /* rds */
+#define DRX_CFG_AUD_AUTOSOUND (DRX_CFG_BASE + 8) /* ASS & ASC */
+#define DRX_CFG_AUD_ASS_THRES (DRX_CFG_BASE + 9) /* ASS Thresholds */
+#define DRX_CFG_AUD_DEVIATION (DRX_CFG_BASE + 10) /* Deviation */
+#define DRX_CFG_AUD_PRESCALE (DRX_CFG_BASE + 11) /* Prescale */
+#define DRX_CFG_AUD_MIXER (DRX_CFG_BASE + 12) /* Mixer */
+#define DRX_CFG_AUD_AVSYNC (DRX_CFG_BASE + 13) /* AVSync */
+#define DRX_CFG_AUD_CARRIER (DRX_CFG_BASE + 14) /* Audio carriers */
+#define DRX_CFG_I2S_OUTPUT (DRX_CFG_BASE + 15) /* I2S output */
+#define DRX_CFG_ATV_STANDARD (DRX_CFG_BASE + 16) /* ATV standard */
+#define DRX_CFG_SQI_SPEED (DRX_CFG_BASE + 17) /* SQI speed */
+#define DRX_CTRL_CFG_MAX (DRX_CFG_BASE + 18) /* never to be used */
+
+#define DRX_CFG_PINS_SAFE_MODE DRX_CFG_PINSAFE
+/*============================================================================*/
+/*============================================================================*/
+/*== CTRL related data structures ============================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+/*
+ * struct drxu_code_info Parameters for microcode upload and verfiy.
+ *
+ * @mc_file: microcode file name
+ *
+ * Used by DRX_CTRL_LOAD_UCODE and DRX_CTRL_VERIFY_UCODE
+ */
+struct drxu_code_info {
+ char *mc_file;
+};
+
+/*
+* \struct drx_mc_version_rec_t
+* \brief Microcode version record
+* Version numbers are stored in BCD format, as usual:
+* o major number = bits 31-20 (first three nibbles of MSW)
+* o minor number = bits 19-16 (fourth nibble of MSW)
+* o patch number = bits 15-0 (remaining nibbles in LSW)
+*
+* The device type indicates for which the device is meant. It is based on the
+* JTAG ID, using everything except the bond ID and the metal fix.
+*
+* Special values:
+* - mc_dev_type == 0 => any device allowed
+* - mc_base_version == 0.0.0 => full microcode (mc_version is the version)
+* - mc_base_version != 0.0.0 => patch microcode, the base microcode version
+* (mc_version is the version)
+*/
+#define AUX_VER_RECORD 0x8000
+
+struct drx_mc_version_rec {
+ u16 aux_type; /* type of aux data - 0x8000 for version record */
+ u32 mc_dev_type; /* device type, based on JTAG ID */
+ u32 mc_version; /* version of microcode */
+ u32 mc_base_version; /* in case of patch: the original microcode version */
+};
+
+/*========================================*/
+
+/*
+* \struct drx_filter_info_t
+* \brief Parameters for loading filter coefficients
+*
+* Used by DRX_CTRL_LOAD_FILTER
+*/
+struct drx_filter_info {
+ u8 *data_re;
+ /*< pointer to coefficients for RE */
+ u8 *data_im;
+ /*< pointer to coefficients for IM */
+ u16 size_re;
+ /*< size of coefficients for RE */
+ u16 size_im;
+ /*< size of coefficients for IM */
+};
+
+/*========================================*/
+
+/*
+* \struct struct drx_channel * \brief The set of parameters describing a single channel.
+*
+* Used by DRX_CTRL_SET_CHANNEL and DRX_CTRL_GET_CHANNEL.
+* Only certain fields need to be used for a specific standard.
+*
+*/
+struct drx_channel {
+ s32 frequency;
+ /*< frequency in kHz */
+ enum drx_bandwidth bandwidth;
+ /*< bandwidth */
+ enum drx_mirror mirror; /*< mirrored or not on RF */
+ enum drx_modulation constellation;
+ /*< constellation */
+ enum drx_hierarchy hierarchy;
+ /*< hierarchy */
+ enum drx_priority priority; /*< priority */
+ enum drx_coderate coderate; /*< coderate */
+ enum drx_guard guard; /*< guard interval */
+ enum drx_fft_mode fftmode; /*< fftmode */
+ enum drx_classification classification;
+ /*< classification */
+ u32 symbolrate;
+ /*< symbolrate in symbols/sec */
+ enum drx_interleave_mode interleavemode;
+ /*< interleaveMode QAM */
+ enum drx_ldpc ldpc; /*< ldpc */
+ enum drx_carrier_mode carrier; /*< carrier */
+ enum drx_frame_mode framemode;
+ /*< frame mode */
+ enum drx_pilot_mode pilot; /*< pilot mode */
+};
+
+/*========================================*/
+
+enum drx_cfg_sqi_speed {
+ DRX_SQI_SPEED_FAST = 0,
+ DRX_SQI_SPEED_MEDIUM,
+ DRX_SQI_SPEED_SLOW,
+ DRX_SQI_SPEED_UNKNOWN = DRX_UNKNOWN
+};
+
+/*========================================*/
+
+/*
+* \struct struct drx_complex * A complex number.
+*
+* Used by DRX_CTRL_CONSTEL.
+*/
+struct drx_complex {
+ s16 im;
+ /*< Imaginary part. */
+ s16 re;
+ /*< Real part. */
+};
+
+/*========================================*/
+
+/*
+* \struct struct drx_frequency_plan * Array element of a frequency plan.
+*
+* Used by DRX_CTRL_SCAN_INIT.
+*/
+struct drx_frequency_plan {
+ s32 first;
+ /*< First centre frequency in this band */
+ s32 last;
+ /*< Last centre frequency in this band */
+ s32 step;
+ /*< Stepping frequency in this band */
+ enum drx_bandwidth bandwidth;
+ /*< Bandwidth within this frequency band */
+ u16 ch_number;
+ /*< First channel number in this band, or first
+ index in ch_names */
+ char **ch_names;
+ /*< Optional list of channel names in this
+ band */
+};
+
+/*========================================*/
+
+/*
+* \struct struct drx_scan_param * Parameters for channel scan.
+*
+* Used by DRX_CTRL_SCAN_INIT.
+*/
+struct drx_scan_param {
+ struct drx_frequency_plan *frequency_plan;
+ /*< Frequency plan (array)*/
+ u16 frequency_plan_size; /*< Number of bands */
+ u32 num_tries; /*< Max channels tried */
+ s32 skip; /*< Minimum frequency step to take
+ after a channel is found */
+ void *ext_params; /*< Standard specific params */
+};
+
+/*========================================*/
+
+/*
+* \brief Scan commands.
+* Used by scanning algorithms.
+*/
+enum drx_scan_command {
+ DRX_SCAN_COMMAND_INIT = 0,/*< Initialize scanning */
+ DRX_SCAN_COMMAND_NEXT, /*< Next scan */
+ DRX_SCAN_COMMAND_STOP /*< Stop scanning */
+};
+
+/*========================================*/
+
+/*
+* \brief Inner scan function prototype.
+*/
+typedef int(*drx_scan_func_t) (void *scan_context,
+ enum drx_scan_command scan_command,
+ struct drx_channel *scan_channel,
+ bool *get_next_channel);
+
+/*========================================*/
+
+/*
+* \struct struct drxtps_info * TPS information, DVB-T specific.
+*
+* Used by DRX_CTRL_TPS_INFO.
+*/
+ struct drxtps_info {
+ enum drx_fft_mode fftmode; /*< Fft mode */
+ enum drx_guard guard; /*< Guard interval */
+ enum drx_modulation constellation;
+ /*< Constellation */
+ enum drx_hierarchy hierarchy;
+ /*< Hierarchy */
+ enum drx_coderate high_coderate;
+ /*< High code rate */
+ enum drx_coderate low_coderate;
+ /*< Low cod rate */
+ enum drx_tps_frame frame; /*< Tps frame */
+ u8 length; /*< Length */
+ u16 cell_id; /*< Cell id */
+ };
+
+/*========================================*/
+
+/*
+* \brief Power mode of device.
+*
+* Used by DRX_CTRL_SET_POWER_MODE.
+*/
+ enum drx_power_mode {
+ DRX_POWER_UP = 0,
+ /*< Generic , Power Up Mode */
+ DRX_POWER_MODE_1,
+ /*< Device specific , Power Up Mode */
+ DRX_POWER_MODE_2,
+ /*< Device specific , Power Up Mode */
+ DRX_POWER_MODE_3,
+ /*< Device specific , Power Up Mode */
+ DRX_POWER_MODE_4,
+ /*< Device specific , Power Up Mode */
+ DRX_POWER_MODE_5,
+ /*< Device specific , Power Up Mode */
+ DRX_POWER_MODE_6,
+ /*< Device specific , Power Up Mode */
+ DRX_POWER_MODE_7,
+ /*< Device specific , Power Up Mode */
+ DRX_POWER_MODE_8,
+ /*< Device specific , Power Up Mode */
+
+ DRX_POWER_MODE_9,
+ /*< Device specific , Power Down Mode */
+ DRX_POWER_MODE_10,
+ /*< Device specific , Power Down Mode */
+ DRX_POWER_MODE_11,
+ /*< Device specific , Power Down Mode */
+ DRX_POWER_MODE_12,
+ /*< Device specific , Power Down Mode */
+ DRX_POWER_MODE_13,
+ /*< Device specific , Power Down Mode */
+ DRX_POWER_MODE_14,
+ /*< Device specific , Power Down Mode */
+ DRX_POWER_MODE_15,
+ /*< Device specific , Power Down Mode */
+ DRX_POWER_MODE_16,
+ /*< Device specific , Power Down Mode */
+ DRX_POWER_DOWN = 255
+ /*< Generic , Power Down Mode */
+ };
+
+/*========================================*/
+
+/*
+* \enum enum drx_module * \brief Software module identification.
+*
+* Used by DRX_CTRL_VERSION.
+*/
+ enum drx_module {
+ DRX_MODULE_DEVICE,
+ DRX_MODULE_MICROCODE,
+ DRX_MODULE_DRIVERCORE,
+ DRX_MODULE_DEVICEDRIVER,
+ DRX_MODULE_DAP,
+ DRX_MODULE_BSP_I2C,
+ DRX_MODULE_BSP_TUNER,
+ DRX_MODULE_BSP_HOST,
+ DRX_MODULE_UNKNOWN
+ };
+
+/*
+* \enum struct drx_version * \brief Version information of one software module.
+*
+* Used by DRX_CTRL_VERSION.
+*/
+ struct drx_version {
+ enum drx_module module_type;
+ /*< Type identifier of the module */
+ char *module_name;
+ /*< Name or description of module */
+ u16 v_major; /*< Major version number */
+ u16 v_minor; /*< Minor version number */
+ u16 v_patch; /*< Patch version number */
+ char *v_string; /*< Version as text string */
+ };
+
+/*
+* \enum struct drx_version_list * \brief List element of NULL terminated, linked list for version information.
+*
+* Used by DRX_CTRL_VERSION.
+*/
+struct drx_version_list {
+ struct drx_version *version;/*< Version information */
+ struct drx_version_list *next;
+ /*< Next list element */
+};
+
+/*========================================*/
+
+/*
+* \brief Parameters needed to confiugure a UIO.
+*
+* Used by DRX_CTRL_UIO_CFG.
+*/
+ struct drxuio_cfg {
+ enum drx_uio uio;
+ /*< UIO identifier */
+ enum drxuio_mode mode;
+ /*< UIO operational mode */
+ };
+
+/*========================================*/
+
+/*
+* \brief Parameters needed to read from or write to a UIO.
+*
+* Used by DRX_CTRL_UIO_READ and DRX_CTRL_UIO_WRITE.
+*/
+ struct drxuio_data {
+ enum drx_uio uio;
+ /*< UIO identifier */
+ bool value;
+ /*< UIO value (true=1, false=0) */
+ };
+
+/*========================================*/
+
+/*
+* \brief Parameters needed to configure OOB.
+*
+* Used by DRX_CTRL_SET_OOB.
+*/
+ struct drxoob {
+ s32 frequency; /*< Frequency in kHz */
+ enum drxoob_downstream_standard standard;
+ /*< OOB standard */
+ bool spectrum_inverted; /*< If true, then spectrum
+ is inverted */
+ };
+
+/*========================================*/
+
+/*
+* \brief Metrics from OOB.
+*
+* Used by DRX_CTRL_GET_OOB.
+*/
+ struct drxoob_status {
+ s32 frequency; /*< Frequency in Khz */
+ enum drx_lock_status lock; /*< Lock status */
+ u32 mer; /*< MER */
+ s32 symbol_rate_offset; /*< Symbolrate offset in ppm */
+ };
+
+/*========================================*/
+
+/*
+* \brief Device dependent configuration data.
+*
+* Used by DRX_CTRL_SET_CFG and DRX_CTRL_GET_CFG.
+* A sort of nested drx_ctrl() functionality for device specific controls.
+*/
+ struct drx_cfg {
+ u32 cfg_type;
+ /*< Function identifier */
+ void *cfg_data;
+ /*< Function data */
+ };
+
+/*========================================*/
+
+/*
+* /struct DRXMpegStartWidth_t
+* MStart width [nr MCLK cycles] for serial MPEG output.
+*/
+
+ enum drxmpeg_str_width {
+ DRX_MPEG_STR_WIDTH_1,
+ DRX_MPEG_STR_WIDTH_8
+ };
+
+/* CTRL CFG MPEG output */
+/*
+* \struct struct drx_cfg_mpeg_output * \brief Configuration parameters for MPEG output control.
+*
+* Used by DRX_CFG_MPEG_OUTPUT, in combination with DRX_CTRL_SET_CFG and
+* DRX_CTRL_GET_CFG.
+*/
+
+ struct drx_cfg_mpeg_output {
+ bool enable_mpeg_output;/*< If true, enable MPEG output */
+ bool insert_rs_byte; /*< If true, insert RS byte */
+ bool enable_parallel; /*< If true, parallel out otherwise
+ serial */
+ bool invert_data; /*< If true, invert DATA signals */
+ bool invert_err; /*< If true, invert ERR signal */
+ bool invert_str; /*< If true, invert STR signals */
+ bool invert_val; /*< If true, invert VAL signals */
+ bool invert_clk; /*< If true, invert CLK signals */
+ bool static_clk; /*< If true, static MPEG clockrate
+ will be used, otherwise clockrate
+ will adapt to the bitrate of the
+ TS */
+ u32 bitrate; /*< Maximum bitrate in b/s in case
+ static clockrate is selected */
+ enum drxmpeg_str_width width_str;
+ /*< MPEG start width */
+ };
+
+
+/*========================================*/
+
+/*
+* \struct struct drxi2c_data * \brief Data for I2C via 2nd or 3rd or etc I2C port.
+*
+* Used by DRX_CTRL_I2C_READWRITE.
+* If port_nr is equal to primairy port_nr BSPI2C will be used.
+*
+*/
+ struct drxi2c_data {
+ u16 port_nr; /*< I2C port number */
+ struct i2c_device_addr *w_dev_addr;
+ /*< Write device address */
+ u16 w_count; /*< Size of write data in bytes */
+ u8 *wData; /*< Pointer to write data */
+ struct i2c_device_addr *r_dev_addr;
+ /*< Read device address */
+ u16 r_count; /*< Size of data to read in bytes */
+ u8 *r_data; /*< Pointer to read buffer */
+ };
+
+/*========================================*/
+
+/*
+* \enum enum drx_aud_standard * \brief Audio standard identifier.
+*
+* Used by DRX_CTRL_SET_AUD.
+*/
+ enum drx_aud_standard {
+ DRX_AUD_STANDARD_BTSC, /*< set BTSC standard (USA) */
+ DRX_AUD_STANDARD_A2, /*< set A2-Korea FM Stereo */
+ DRX_AUD_STANDARD_EIAJ, /*< set to Japanese FM Stereo */
+ DRX_AUD_STANDARD_FM_STEREO,/*< set to FM-Stereo Radio */
+ DRX_AUD_STANDARD_M_MONO, /*< for 4.5 MHz mono detected */
+ DRX_AUD_STANDARD_D_K_MONO, /*< for 6.5 MHz mono detected */
+ DRX_AUD_STANDARD_BG_FM, /*< set BG_FM standard */
+ DRX_AUD_STANDARD_D_K1, /*< set D_K1 standard */
+ DRX_AUD_STANDARD_D_K2, /*< set D_K2 standard */
+ DRX_AUD_STANDARD_D_K3, /*< set D_K3 standard */
+ DRX_AUD_STANDARD_BG_NICAM_FM,
+ /*< set BG_NICAM_FM standard */
+ DRX_AUD_STANDARD_L_NICAM_AM,
+ /*< set L_NICAM_AM standard */
+ DRX_AUD_STANDARD_I_NICAM_FM,
+ /*< set I_NICAM_FM standard */
+ DRX_AUD_STANDARD_D_K_NICAM_FM,
+ /*< set D_K_NICAM_FM standard */
+ DRX_AUD_STANDARD_NOT_READY,/*< used to detect audio standard */
+ DRX_AUD_STANDARD_AUTO = DRX_AUTO,
+ /*< Automatic Standard Detection */
+ DRX_AUD_STANDARD_UNKNOWN = DRX_UNKNOWN
+ /*< used as auto and for readback */
+ };
+
+/* CTRL_AUD_GET_STATUS - struct drx_aud_status */
+/*
+* \enum enum drx_aud_nicam_status * \brief Status of NICAM carrier.
+*/
+ enum drx_aud_nicam_status {
+ DRX_AUD_NICAM_DETECTED = 0,
+ /*< NICAM carrier detected */
+ DRX_AUD_NICAM_NOT_DETECTED,
+ /*< NICAM carrier not detected */
+ DRX_AUD_NICAM_BAD /*< NICAM carrier bad quality */
+ };
+
+/*
+* \struct struct drx_aud_status * \brief Audio status characteristics.
+*/
+ struct drx_aud_status {
+ bool stereo; /*< stereo detection */
+ bool carrier_a; /*< carrier A detected */
+ bool carrier_b; /*< carrier B detected */
+ bool sap; /*< sap / bilingual detection */
+ bool rds; /*< RDS data array present */
+ enum drx_aud_nicam_status nicam_status;
+ /*< status of NICAM carrier */
+ s8 fm_ident; /*< FM Identification value */
+ };
+
+/* CTRL_AUD_READ_RDS - DRXRDSdata_t */
+
+/*
+* \struct DRXRDSdata_t
+* \brief Raw RDS data array.
+*/
+ struct drx_cfg_aud_rds {
+ bool valid; /*< RDS data validation */
+ u16 data[18]; /*< data from one RDS data array */
+ };
+
+/* DRX_CFG_AUD_VOLUME - struct drx_cfg_aud_volume - set/get */
+/*
+* \enum DRXAudAVCDecayTime_t
+* \brief Automatic volume control configuration.
+*/
+ enum drx_aud_avc_mode {
+ DRX_AUD_AVC_OFF, /*< Automatic volume control off */
+ DRX_AUD_AVC_DECAYTIME_8S, /*< level volume in 8 seconds */
+ DRX_AUD_AVC_DECAYTIME_4S, /*< level volume in 4 seconds */
+ DRX_AUD_AVC_DECAYTIME_2S, /*< level volume in 2 seconds */
+ DRX_AUD_AVC_DECAYTIME_20MS/*< level volume in 20 millisec */
+ };
+
+/*
+* /enum DRXAudMaxAVCGain_t
+* /brief Automatic volume control max gain in audio baseband.
+*/
+ enum drx_aud_avc_max_gain {
+ DRX_AUD_AVC_MAX_GAIN_0DB, /*< maximum AVC gain 0 dB */
+ DRX_AUD_AVC_MAX_GAIN_6DB, /*< maximum AVC gain 6 dB */
+ DRX_AUD_AVC_MAX_GAIN_12DB /*< maximum AVC gain 12 dB */
+ };
+
+/*
+* /enum DRXAudMaxAVCAtten_t
+* /brief Automatic volume control max attenuation in audio baseband.
+*/
+ enum drx_aud_avc_max_atten {
+ DRX_AUD_AVC_MAX_ATTEN_12DB,
+ /*< maximum AVC attenuation 12 dB */
+ DRX_AUD_AVC_MAX_ATTEN_18DB,
+ /*< maximum AVC attenuation 18 dB */
+ DRX_AUD_AVC_MAX_ATTEN_24DB/*< maximum AVC attenuation 24 dB */
+ };
+/*
+* \struct struct drx_cfg_aud_volume * \brief Audio volume configuration.
+*/
+ struct drx_cfg_aud_volume {
+ bool mute; /*< mute overrides volume setting */
+ s16 volume; /*< volume, range -114 to 12 dB */
+ enum drx_aud_avc_mode avc_mode; /*< AVC auto volume control mode */
+ u16 avc_ref_level; /*< AVC reference level */
+ enum drx_aud_avc_max_gain avc_max_gain;
+ /*< AVC max gain selection */
+ enum drx_aud_avc_max_atten avc_max_atten;
+ /*< AVC max attenuation selection */
+ s16 strength_left; /*< quasi-peak, left speaker */
+ s16 strength_right; /*< quasi-peak, right speaker */
+ };
+
+/* DRX_CFG_I2S_OUTPUT - struct drx_cfg_i2s_output - set/get */
+/*
+* \enum enum drxi2s_mode * \brief I2S output mode.
+*/
+ enum drxi2s_mode {
+ DRX_I2S_MODE_MASTER, /*< I2S is in master mode */
+ DRX_I2S_MODE_SLAVE /*< I2S is in slave mode */
+ };
+
+/*
+* \enum enum drxi2s_word_length * \brief Width of I2S data.
+*/
+ enum drxi2s_word_length {
+ DRX_I2S_WORDLENGTH_32 = 0,/*< I2S data is 32 bit wide */
+ DRX_I2S_WORDLENGTH_16 = 1 /*< I2S data is 16 bit wide */
+ };
+
+/*
+* \enum enum drxi2s_format * \brief Data wordstrobe alignment for I2S.
+*/
+ enum drxi2s_format {
+ DRX_I2S_FORMAT_WS_WITH_DATA,
+ /*< I2S data and wordstrobe are aligned */
+ DRX_I2S_FORMAT_WS_ADVANCED
+ /*< I2S data one cycle after wordstrobe */
+ };
+
+/*
+* \enum enum drxi2s_polarity * \brief Polarity of I2S data.
+*/
+ enum drxi2s_polarity {
+ DRX_I2S_POLARITY_RIGHT,/*< wordstrobe - right high, left low */
+ DRX_I2S_POLARITY_LEFT /*< wordstrobe - right low, left high */
+ };
+
+/*
+* \struct struct drx_cfg_i2s_output * \brief I2S output configuration.
+*/
+ struct drx_cfg_i2s_output {
+ bool output_enable; /*< I2S output enable */
+ u32 frequency; /*< range from 8000-48000 Hz */
+ enum drxi2s_mode mode; /*< I2S mode, master or slave */
+ enum drxi2s_word_length word_length;
+ /*< I2S wordlength, 16 or 32 bits */
+ enum drxi2s_polarity polarity;/*< I2S wordstrobe polarity */
+ enum drxi2s_format format; /*< I2S wordstrobe delay to data */
+ };
+
+/* ------------------------------expert interface-----------------------------*/
+/*
+* /enum enum drx_aud_fm_deemphasis * setting for FM-Deemphasis in audio demodulator.
+*
+*/
+ enum drx_aud_fm_deemphasis {
+ DRX_AUD_FM_DEEMPH_50US,
+ DRX_AUD_FM_DEEMPH_75US,
+ DRX_AUD_FM_DEEMPH_OFF
+ };
+
+/*
+* /enum DRXAudDeviation_t
+* setting for deviation mode in audio demodulator.
+*
+*/
+ enum drx_cfg_aud_deviation {
+ DRX_AUD_DEVIATION_NORMAL,
+ DRX_AUD_DEVIATION_HIGH
+ };
+
+/*
+* /enum enum drx_no_carrier_option * setting for carrier, mute/noise.
+*
+*/
+ enum drx_no_carrier_option {
+ DRX_NO_CARRIER_MUTE,
+ DRX_NO_CARRIER_NOISE
+ };
+
+/*
+* \enum DRXAudAutoSound_t
+* \brief Automatic Sound
+*/
+ enum drx_cfg_aud_auto_sound {
+ DRX_AUD_AUTO_SOUND_OFF = 0,
+ DRX_AUD_AUTO_SOUND_SELECT_ON_CHANGE_ON,
+ DRX_AUD_AUTO_SOUND_SELECT_ON_CHANGE_OFF
+ };
+
+/*
+* \enum DRXAudASSThres_t
+* \brief Automatic Sound Select Thresholds
+*/
+ struct drx_cfg_aud_ass_thres {
+ u16 a2; /* A2 Threshold for ASS configuration */
+ u16 btsc; /* BTSC Threshold for ASS configuration */
+ u16 nicam; /* Nicam Threshold for ASS configuration */
+ };
+
+/*
+* \struct struct drx_aud_carrier * \brief Carrier detection related parameters
+*/
+ struct drx_aud_carrier {
+ u16 thres; /* carrier detetcion threshold for primary carrier (A) */
+ enum drx_no_carrier_option opt; /* Mute or noise at no carrier detection (A) */
+ s32 shift; /* DC level of incoming signal (A) */
+ s32 dco; /* frequency adjustment (A) */
+ };
+
+/*
+* \struct struct drx_cfg_aud_carriers * \brief combining carrier A & B to one struct
+*/
+ struct drx_cfg_aud_carriers {
+ struct drx_aud_carrier a;
+ struct drx_aud_carrier b;
+ };
+
+/*
+* /enum enum drx_aud_i2s_src * Selection of audio source
+*/
+ enum drx_aud_i2s_src {
+ DRX_AUD_SRC_MONO,
+ DRX_AUD_SRC_STEREO_OR_AB,
+ DRX_AUD_SRC_STEREO_OR_A,
+ DRX_AUD_SRC_STEREO_OR_B};
+
+/*
+* \enum enum drx_aud_i2s_matrix * \brief Used for selecting I2S output.
+*/
+ enum drx_aud_i2s_matrix {
+ DRX_AUD_I2S_MATRIX_A_MONO,
+ /*< A sound only, stereo or mono */
+ DRX_AUD_I2S_MATRIX_B_MONO,
+ /*< B sound only, stereo or mono */
+ DRX_AUD_I2S_MATRIX_STEREO,
+ /*< A+B sound, transparent */
+ DRX_AUD_I2S_MATRIX_MONO /*< A+B mixed to mono sum, (L+R)/2 */};
+
+/*
+* /enum enum drx_aud_fm_matrix * setting for FM-Matrix in audio demodulator.
+*
+*/
+ enum drx_aud_fm_matrix {
+ DRX_AUD_FM_MATRIX_NO_MATRIX,
+ DRX_AUD_FM_MATRIX_GERMAN,
+ DRX_AUD_FM_MATRIX_KOREAN,
+ DRX_AUD_FM_MATRIX_SOUND_A,
+ DRX_AUD_FM_MATRIX_SOUND_B};
+
+/*
+* \struct DRXAudMatrices_t
+* \brief Mixer settings
+*/
+struct drx_cfg_aud_mixer {
+ enum drx_aud_i2s_src source_i2s;
+ enum drx_aud_i2s_matrix matrix_i2s;
+ enum drx_aud_fm_matrix matrix_fm;
+};
+
+/*
+* \enum DRXI2SVidSync_t
+* \brief Audio/video synchronization, interacts with I2S mode.
+* AUTO_1 and AUTO_2 are for automatic video standard detection with preference
+* for NTSC or Monochrome, because the frequencies are too close (59.94 & 60 Hz)
+*/
+ enum drx_cfg_aud_av_sync {
+ DRX_AUD_AVSYNC_OFF,/*< audio/video synchronization is off */
+ DRX_AUD_AVSYNC_NTSC,
+ /*< it is an NTSC system */
+ DRX_AUD_AVSYNC_MONOCHROME,
+ /*< it is a MONOCHROME system */
+ DRX_AUD_AVSYNC_PAL_SECAM
+ /*< it is a PAL/SECAM system */};
+
+/*
+* \struct struct drx_cfg_aud_prescale * \brief Prescalers
+*/
+struct drx_cfg_aud_prescale {
+ u16 fm_deviation;
+ s16 nicam_gain;
+};
+
+/*
+* \struct struct drx_aud_beep * \brief Beep
+*/
+struct drx_aud_beep {
+ s16 volume; /* dB */
+ u16 frequency; /* Hz */
+ bool mute;
+};
+
+/*
+* \enum enum drx_aud_btsc_detect * \brief BTSC detetcion mode
+*/
+ enum drx_aud_btsc_detect {
+ DRX_BTSC_STEREO,
+ DRX_BTSC_MONO_AND_SAP};
+
+/*
+* \struct struct drx_aud_data * \brief Audio data structure
+*/
+struct drx_aud_data {
+ /* audio storage */
+ bool audio_is_active;
+ enum drx_aud_standard audio_standard;
+ struct drx_cfg_i2s_output i2sdata;
+ struct drx_cfg_aud_volume volume;
+ enum drx_cfg_aud_auto_sound auto_sound;
+ struct drx_cfg_aud_ass_thres ass_thresholds;
+ struct drx_cfg_aud_carriers carriers;
+ struct drx_cfg_aud_mixer mixer;
+ enum drx_cfg_aud_deviation deviation;
+ enum drx_cfg_aud_av_sync av_sync;
+ struct drx_cfg_aud_prescale prescale;
+ enum drx_aud_fm_deemphasis deemph;
+ enum drx_aud_btsc_detect btsc_detect;
+ /* rds */
+ u16 rds_data_counter;
+ bool rds_data_present;
+};
+
+/*
+* \enum enum drx_qam_lock_range * \brief QAM lock range mode
+*/
+ enum drx_qam_lock_range {
+ DRX_QAM_LOCKRANGE_NORMAL,
+ DRX_QAM_LOCKRANGE_EXTENDED};
+
+/*============================================================================*/
+/*============================================================================*/
+/*== Data access structures ==================================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+/* Address on device */
+ typedef u32 dr_xaddr_t, *pdr_xaddr_t;
+
+/* Protocol specific flags */
+ typedef u32 dr_xflags_t, *pdr_xflags_t;
+
+/* Write block of data to device */
+ typedef int(*drx_write_block_func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u16 datasize, /* size of data in bytes */
+ u8 *data, /* data to send */
+ u32 flags);
+
+/* Read block of data from device */
+ typedef int(*drx_read_block_func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u16 datasize, /* size of data in bytes */
+ u8 *data, /* receive buffer */
+ u32 flags);
+
+/* Write 8-bits value to device */
+ typedef int(*drx_write_reg8func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u8 data, /* data to send */
+ u32 flags);
+
+/* Read 8-bits value to device */
+ typedef int(*drx_read_reg8func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u8 *data, /* receive buffer */
+ u32 flags);
+
+/* Read modify write 8-bits value to device */
+ typedef int(*drx_read_modify_write_reg8func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 waddr, /* write address of register */
+ u32 raddr, /* read address of register */
+ u8 wdata, /* data to write */
+ u8 *rdata); /* data to read */
+
+/* Write 16-bits value to device */
+ typedef int(*drx_write_reg16func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u16 data, /* data to send */
+ u32 flags);
+
+/* Read 16-bits value to device */
+ typedef int(*drx_read_reg16func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u16 *data, /* receive buffer */
+ u32 flags);
+
+/* Read modify write 16-bits value to device */
+ typedef int(*drx_read_modify_write_reg16func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 waddr, /* write address of register */
+ u32 raddr, /* read address of register */
+ u16 wdata, /* data to write */
+ u16 *rdata); /* data to read */
+
+/* Write 32-bits value to device */
+ typedef int(*drx_write_reg32func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u32 data, /* data to send */
+ u32 flags);
+
+/* Read 32-bits value to device */
+ typedef int(*drx_read_reg32func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 addr, /* address of register/memory */
+ u32 *data, /* receive buffer */
+ u32 flags);
+
+/* Read modify write 32-bits value to device */
+ typedef int(*drx_read_modify_write_reg32func_t) (struct i2c_device_addr *dev_addr, /* address of I2C device */
+ u32 waddr, /* write address of register */
+ u32 raddr, /* read address of register */
+ u32 wdata, /* data to write */
+ u32 *rdata); /* data to read */
+
+/*
+* \struct struct drx_access_func * \brief Interface to an access protocol.
+*/
+struct drx_access_func {
+ drx_write_block_func_t write_block_func;
+ drx_read_block_func_t read_block_func;
+ drx_write_reg8func_t write_reg8func;
+ drx_read_reg8func_t read_reg8func;
+ drx_read_modify_write_reg8func_t read_modify_write_reg8func;
+ drx_write_reg16func_t write_reg16func;
+ drx_read_reg16func_t read_reg16func;
+ drx_read_modify_write_reg16func_t read_modify_write_reg16func;
+ drx_write_reg32func_t write_reg32func;
+ drx_read_reg32func_t read_reg32func;
+ drx_read_modify_write_reg32func_t read_modify_write_reg32func;
+};
+
+/* Register address and data for register dump function */
+struct drx_reg_dump {
+ u32 address;
+ u32 data;
+};
+
+/*============================================================================*/
+/*============================================================================*/
+/*== Demod instance data structures ==========================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+/*
+* \struct struct drx_common_attr * \brief Set of common attributes, shared by all DRX devices.
+*/
+ struct drx_common_attr {
+ /* Microcode (firmware) attributes */
+ char *microcode_file; /*< microcode filename */
+ bool verify_microcode;
+ /*< Use microcode verify or not. */
+ struct drx_mc_version_rec mcversion;
+ /*< Version record of microcode from file */
+
+ /* Clocks and tuner attributes */
+ s32 intermediate_freq;
+ /*< IF,if tuner instance not used. (kHz)*/
+ s32 sys_clock_freq;
+ /*< Systemclock frequency. (kHz) */
+ s32 osc_clock_freq;
+ /*< Oscillator clock frequency. (kHz) */
+ s16 osc_clock_deviation;
+ /*< Oscillator clock deviation. (ppm) */
+ bool mirror_freq_spect;
+ /*< Mirror IF frequency spectrum or not.*/
+
+ /* Initial MPEG output attributes */
+ struct drx_cfg_mpeg_output mpeg_cfg;
+ /*< MPEG configuration */
+
+ bool is_opened; /*< if true instance is already opened. */
+
+ /* Channel scan */
+ struct drx_scan_param *scan_param;
+ /*< scan parameters */
+ u16 scan_freq_plan_index;
+ /*< next index in freq plan */
+ s32 scan_next_frequency;
+ /*< next freq to scan */
+ bool scan_ready; /*< scan ready flag */
+ u32 scan_max_channels;/*< number of channels in freqplan */
+ u32 scan_channels_scanned;
+ /*< number of channels scanned */
+ /* Channel scan - inner loop: demod related */
+ drx_scan_func_t scan_function;
+ /*< function to check channel */
+ /* Channel scan - inner loop: SYSObj related */
+ void *scan_context; /*< Context Pointer of SYSObj */
+ /* Channel scan - parameters for default DTV scan function in core driver */
+ u16 scan_demod_lock_timeout;
+ /*< millisecs to wait for lock */
+ enum drx_lock_status scan_desired_lock;
+ /*< lock requirement for channel found */
+ /* scan_active can be used by SetChannel to decide how to program the tuner,
+ fast or slow (but stable). Usually fast during scan. */
+ bool scan_active; /*< true when scan routines are active */
+
+ /* Power management */
+ enum drx_power_mode current_power_mode;
+ /*< current power management mode */
+
+ /* Tuner */
+ u8 tuner_port_nr; /*< nr of I2C port to which tuner is */
+ s32 tuner_min_freq_rf;
+ /*< minimum RF input frequency, in kHz */
+ s32 tuner_max_freq_rf;
+ /*< maximum RF input frequency, in kHz */
+ bool tuner_rf_agc_pol; /*< if true invert RF AGC polarity */
+ bool tuner_if_agc_pol; /*< if true invert IF AGC polarity */
+ bool tuner_slow_mode; /*< if true invert IF AGC polarity */
+
+ struct drx_channel current_channel;
+ /*< current channel parameters */
+ enum drx_standard current_standard;
+ /*< current standard selection */
+ enum drx_standard prev_standard;
+ /*< previous standard selection */
+ enum drx_standard di_cache_standard;
+ /*< standard in DI cache if available */
+ bool use_bootloader; /*< use bootloader in open */
+ u32 capabilities; /*< capabilities flags */
+ u32 product_id; /*< product ID inc. metal fix number */};
+
+/*
+* Generic functions for DRX devices.
+*/
+
+struct drx_demod_instance;
+
+/*
+* \struct struct drx_demod_instance * \brief Top structure of demodulator instance.
+*/
+struct drx_demod_instance {
+ /*< data access protocol functions */
+ struct i2c_device_addr *my_i2c_dev_addr;
+ /*< i2c address and device identifier */
+ struct drx_common_attr *my_common_attr;
+ /*< common DRX attributes */
+ void *my_ext_attr; /*< device specific attributes */
+ /* generic demodulator data */
+
+ struct i2c_adapter *i2c;
+};
+
+/*-------------------------------------------------------------------------
+MACROS
+Conversion from enum values to human readable form.
+-------------------------------------------------------------------------*/
+
+/* standard */
+
+#define DRX_STR_STANDARD(x) ( \
+ (x == DRX_STANDARD_DVBT) ? "DVB-T" : \
+ (x == DRX_STANDARD_8VSB) ? "8VSB" : \
+ (x == DRX_STANDARD_NTSC) ? "NTSC" : \
+ (x == DRX_STANDARD_PAL_SECAM_BG) ? "PAL/SECAM B/G" : \
+ (x == DRX_STANDARD_PAL_SECAM_DK) ? "PAL/SECAM D/K" : \
+ (x == DRX_STANDARD_PAL_SECAM_I) ? "PAL/SECAM I" : \
+ (x == DRX_STANDARD_PAL_SECAM_L) ? "PAL/SECAM L" : \
+ (x == DRX_STANDARD_PAL_SECAM_LP) ? "PAL/SECAM LP" : \
+ (x == DRX_STANDARD_ITU_A) ? "ITU-A" : \
+ (x == DRX_STANDARD_ITU_B) ? "ITU-B" : \
+ (x == DRX_STANDARD_ITU_C) ? "ITU-C" : \
+ (x == DRX_STANDARD_ITU_D) ? "ITU-D" : \
+ (x == DRX_STANDARD_FM) ? "FM" : \
+ (x == DRX_STANDARD_DTMB) ? "DTMB" : \
+ (x == DRX_STANDARD_AUTO) ? "Auto" : \
+ (x == DRX_STANDARD_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+
+/* channel */
+
+#define DRX_STR_BANDWIDTH(x) ( \
+ (x == DRX_BANDWIDTH_8MHZ) ? "8 MHz" : \
+ (x == DRX_BANDWIDTH_7MHZ) ? "7 MHz" : \
+ (x == DRX_BANDWIDTH_6MHZ) ? "6 MHz" : \
+ (x == DRX_BANDWIDTH_AUTO) ? "Auto" : \
+ (x == DRX_BANDWIDTH_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_FFTMODE(x) ( \
+ (x == DRX_FFTMODE_2K) ? "2k" : \
+ (x == DRX_FFTMODE_4K) ? "4k" : \
+ (x == DRX_FFTMODE_8K) ? "8k" : \
+ (x == DRX_FFTMODE_AUTO) ? "Auto" : \
+ (x == DRX_FFTMODE_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_GUARD(x) ( \
+ (x == DRX_GUARD_1DIV32) ? "1/32nd" : \
+ (x == DRX_GUARD_1DIV16) ? "1/16th" : \
+ (x == DRX_GUARD_1DIV8) ? "1/8th" : \
+ (x == DRX_GUARD_1DIV4) ? "1/4th" : \
+ (x == DRX_GUARD_AUTO) ? "Auto" : \
+ (x == DRX_GUARD_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_CONSTELLATION(x) ( \
+ (x == DRX_CONSTELLATION_BPSK) ? "BPSK" : \
+ (x == DRX_CONSTELLATION_QPSK) ? "QPSK" : \
+ (x == DRX_CONSTELLATION_PSK8) ? "PSK8" : \
+ (x == DRX_CONSTELLATION_QAM16) ? "QAM16" : \
+ (x == DRX_CONSTELLATION_QAM32) ? "QAM32" : \
+ (x == DRX_CONSTELLATION_QAM64) ? "QAM64" : \
+ (x == DRX_CONSTELLATION_QAM128) ? "QAM128" : \
+ (x == DRX_CONSTELLATION_QAM256) ? "QAM256" : \
+ (x == DRX_CONSTELLATION_QAM512) ? "QAM512" : \
+ (x == DRX_CONSTELLATION_QAM1024) ? "QAM1024" : \
+ (x == DRX_CONSTELLATION_QPSK_NR) ? "QPSK_NR" : \
+ (x == DRX_CONSTELLATION_AUTO) ? "Auto" : \
+ (x == DRX_CONSTELLATION_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_CODERATE(x) ( \
+ (x == DRX_CODERATE_1DIV2) ? "1/2nd" : \
+ (x == DRX_CODERATE_2DIV3) ? "2/3rd" : \
+ (x == DRX_CODERATE_3DIV4) ? "3/4th" : \
+ (x == DRX_CODERATE_5DIV6) ? "5/6th" : \
+ (x == DRX_CODERATE_7DIV8) ? "7/8th" : \
+ (x == DRX_CODERATE_AUTO) ? "Auto" : \
+ (x == DRX_CODERATE_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_HIERARCHY(x) ( \
+ (x == DRX_HIERARCHY_NONE) ? "None" : \
+ (x == DRX_HIERARCHY_ALPHA1) ? "Alpha=1" : \
+ (x == DRX_HIERARCHY_ALPHA2) ? "Alpha=2" : \
+ (x == DRX_HIERARCHY_ALPHA4) ? "Alpha=4" : \
+ (x == DRX_HIERARCHY_AUTO) ? "Auto" : \
+ (x == DRX_HIERARCHY_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_PRIORITY(x) ( \
+ (x == DRX_PRIORITY_LOW) ? "Low" : \
+ (x == DRX_PRIORITY_HIGH) ? "High" : \
+ (x == DRX_PRIORITY_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_MIRROR(x) ( \
+ (x == DRX_MIRROR_NO) ? "Normal" : \
+ (x == DRX_MIRROR_YES) ? "Mirrored" : \
+ (x == DRX_MIRROR_AUTO) ? "Auto" : \
+ (x == DRX_MIRROR_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_CLASSIFICATION(x) ( \
+ (x == DRX_CLASSIFICATION_GAUSS) ? "Gaussion" : \
+ (x == DRX_CLASSIFICATION_HVY_GAUSS) ? "Heavy Gaussion" : \
+ (x == DRX_CLASSIFICATION_COCHANNEL) ? "Co-channel" : \
+ (x == DRX_CLASSIFICATION_STATIC) ? "Static echo" : \
+ (x == DRX_CLASSIFICATION_MOVING) ? "Moving echo" : \
+ (x == DRX_CLASSIFICATION_ZERODB) ? "Zero dB echo" : \
+ (x == DRX_CLASSIFICATION_UNKNOWN) ? "Unknown" : \
+ (x == DRX_CLASSIFICATION_AUTO) ? "Auto" : \
+ "(Invalid)")
+
+#define DRX_STR_INTERLEAVEMODE(x) ( \
+ (x == DRX_INTERLEAVEMODE_I128_J1) ? "I128_J1" : \
+ (x == DRX_INTERLEAVEMODE_I128_J1_V2) ? "I128_J1_V2" : \
+ (x == DRX_INTERLEAVEMODE_I128_J2) ? "I128_J2" : \
+ (x == DRX_INTERLEAVEMODE_I64_J2) ? "I64_J2" : \
+ (x == DRX_INTERLEAVEMODE_I128_J3) ? "I128_J3" : \
+ (x == DRX_INTERLEAVEMODE_I32_J4) ? "I32_J4" : \
+ (x == DRX_INTERLEAVEMODE_I128_J4) ? "I128_J4" : \
+ (x == DRX_INTERLEAVEMODE_I16_J8) ? "I16_J8" : \
+ (x == DRX_INTERLEAVEMODE_I128_J5) ? "I128_J5" : \
+ (x == DRX_INTERLEAVEMODE_I8_J16) ? "I8_J16" : \
+ (x == DRX_INTERLEAVEMODE_I128_J6) ? "I128_J6" : \
+ (x == DRX_INTERLEAVEMODE_RESERVED_11) ? "Reserved 11" : \
+ (x == DRX_INTERLEAVEMODE_I128_J7) ? "I128_J7" : \
+ (x == DRX_INTERLEAVEMODE_RESERVED_13) ? "Reserved 13" : \
+ (x == DRX_INTERLEAVEMODE_I128_J8) ? "I128_J8" : \
+ (x == DRX_INTERLEAVEMODE_RESERVED_15) ? "Reserved 15" : \
+ (x == DRX_INTERLEAVEMODE_I12_J17) ? "I12_J17" : \
+ (x == DRX_INTERLEAVEMODE_I5_J4) ? "I5_J4" : \
+ (x == DRX_INTERLEAVEMODE_B52_M240) ? "B52_M240" : \
+ (x == DRX_INTERLEAVEMODE_B52_M720) ? "B52_M720" : \
+ (x == DRX_INTERLEAVEMODE_B52_M48) ? "B52_M48" : \
+ (x == DRX_INTERLEAVEMODE_B52_M0) ? "B52_M0" : \
+ (x == DRX_INTERLEAVEMODE_UNKNOWN) ? "Unknown" : \
+ (x == DRX_INTERLEAVEMODE_AUTO) ? "Auto" : \
+ "(Invalid)")
+
+#define DRX_STR_LDPC(x) ( \
+ (x == DRX_LDPC_0_4) ? "0.4" : \
+ (x == DRX_LDPC_0_6) ? "0.6" : \
+ (x == DRX_LDPC_0_8) ? "0.8" : \
+ (x == DRX_LDPC_AUTO) ? "Auto" : \
+ (x == DRX_LDPC_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+
+#define DRX_STR_CARRIER(x) ( \
+ (x == DRX_CARRIER_MULTI) ? "Multi" : \
+ (x == DRX_CARRIER_SINGLE) ? "Single" : \
+ (x == DRX_CARRIER_AUTO) ? "Auto" : \
+ (x == DRX_CARRIER_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+
+#define DRX_STR_FRAMEMODE(x) ( \
+ (x == DRX_FRAMEMODE_420) ? "420" : \
+ (x == DRX_FRAMEMODE_595) ? "595" : \
+ (x == DRX_FRAMEMODE_945) ? "945" : \
+ (x == DRX_FRAMEMODE_420_FIXED_PN) ? "420 with fixed PN" : \
+ (x == DRX_FRAMEMODE_945_FIXED_PN) ? "945 with fixed PN" : \
+ (x == DRX_FRAMEMODE_AUTO) ? "Auto" : \
+ (x == DRX_FRAMEMODE_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+
+#define DRX_STR_PILOT(x) ( \
+ (x == DRX_PILOT_ON) ? "On" : \
+ (x == DRX_PILOT_OFF) ? "Off" : \
+ (x == DRX_PILOT_AUTO) ? "Auto" : \
+ (x == DRX_PILOT_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+/* TPS */
+
+#define DRX_STR_TPS_FRAME(x) ( \
+ (x == DRX_TPS_FRAME1) ? "Frame1" : \
+ (x == DRX_TPS_FRAME2) ? "Frame2" : \
+ (x == DRX_TPS_FRAME3) ? "Frame3" : \
+ (x == DRX_TPS_FRAME4) ? "Frame4" : \
+ (x == DRX_TPS_FRAME_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+
+/* lock status */
+
+#define DRX_STR_LOCKSTATUS(x) ( \
+ (x == DRX_NEVER_LOCK) ? "Never" : \
+ (x == DRX_NOT_LOCKED) ? "No" : \
+ (x == DRX_LOCKED) ? "Locked" : \
+ (x == DRX_LOCK_STATE_1) ? "Lock state 1" : \
+ (x == DRX_LOCK_STATE_2) ? "Lock state 2" : \
+ (x == DRX_LOCK_STATE_3) ? "Lock state 3" : \
+ (x == DRX_LOCK_STATE_4) ? "Lock state 4" : \
+ (x == DRX_LOCK_STATE_5) ? "Lock state 5" : \
+ (x == DRX_LOCK_STATE_6) ? "Lock state 6" : \
+ (x == DRX_LOCK_STATE_7) ? "Lock state 7" : \
+ (x == DRX_LOCK_STATE_8) ? "Lock state 8" : \
+ (x == DRX_LOCK_STATE_9) ? "Lock state 9" : \
+ "(Invalid)")
+
+/* version information , modules */
+#define DRX_STR_MODULE(x) ( \
+ (x == DRX_MODULE_DEVICE) ? "Device" : \
+ (x == DRX_MODULE_MICROCODE) ? "Microcode" : \
+ (x == DRX_MODULE_DRIVERCORE) ? "CoreDriver" : \
+ (x == DRX_MODULE_DEVICEDRIVER) ? "DeviceDriver" : \
+ (x == DRX_MODULE_BSP_I2C) ? "BSP I2C" : \
+ (x == DRX_MODULE_BSP_TUNER) ? "BSP Tuner" : \
+ (x == DRX_MODULE_BSP_HOST) ? "BSP Host" : \
+ (x == DRX_MODULE_DAP) ? "Data Access Protocol" : \
+ (x == DRX_MODULE_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+
+#define DRX_STR_POWER_MODE(x) ( \
+ (x == DRX_POWER_UP) ? "DRX_POWER_UP " : \
+ (x == DRX_POWER_MODE_1) ? "DRX_POWER_MODE_1" : \
+ (x == DRX_POWER_MODE_2) ? "DRX_POWER_MODE_2" : \
+ (x == DRX_POWER_MODE_3) ? "DRX_POWER_MODE_3" : \
+ (x == DRX_POWER_MODE_4) ? "DRX_POWER_MODE_4" : \
+ (x == DRX_POWER_MODE_5) ? "DRX_POWER_MODE_5" : \
+ (x == DRX_POWER_MODE_6) ? "DRX_POWER_MODE_6" : \
+ (x == DRX_POWER_MODE_7) ? "DRX_POWER_MODE_7" : \
+ (x == DRX_POWER_MODE_8) ? "DRX_POWER_MODE_8" : \
+ (x == DRX_POWER_MODE_9) ? "DRX_POWER_MODE_9" : \
+ (x == DRX_POWER_MODE_10) ? "DRX_POWER_MODE_10" : \
+ (x == DRX_POWER_MODE_11) ? "DRX_POWER_MODE_11" : \
+ (x == DRX_POWER_MODE_12) ? "DRX_POWER_MODE_12" : \
+ (x == DRX_POWER_MODE_13) ? "DRX_POWER_MODE_13" : \
+ (x == DRX_POWER_MODE_14) ? "DRX_POWER_MODE_14" : \
+ (x == DRX_POWER_MODE_15) ? "DRX_POWER_MODE_15" : \
+ (x == DRX_POWER_MODE_16) ? "DRX_POWER_MODE_16" : \
+ (x == DRX_POWER_DOWN) ? "DRX_POWER_DOWN " : \
+ "(Invalid)")
+
+#define DRX_STR_OOB_STANDARD(x) ( \
+ (x == DRX_OOB_MODE_A) ? "ANSI 55-1 " : \
+ (x == DRX_OOB_MODE_B_GRADE_A) ? "ANSI 55-2 A" : \
+ (x == DRX_OOB_MODE_B_GRADE_B) ? "ANSI 55-2 B" : \
+ "(Invalid)")
+
+#define DRX_STR_AUD_STANDARD(x) ( \
+ (x == DRX_AUD_STANDARD_BTSC) ? "BTSC" : \
+ (x == DRX_AUD_STANDARD_A2) ? "A2" : \
+ (x == DRX_AUD_STANDARD_EIAJ) ? "EIAJ" : \
+ (x == DRX_AUD_STANDARD_FM_STEREO) ? "FM Stereo" : \
+ (x == DRX_AUD_STANDARD_AUTO) ? "Auto" : \
+ (x == DRX_AUD_STANDARD_M_MONO) ? "M-Standard Mono" : \
+ (x == DRX_AUD_STANDARD_D_K_MONO) ? "D/K Mono FM" : \
+ (x == DRX_AUD_STANDARD_BG_FM) ? "B/G-Dual Carrier FM (A2)" : \
+ (x == DRX_AUD_STANDARD_D_K1) ? "D/K1-Dual Carrier FM" : \
+ (x == DRX_AUD_STANDARD_D_K2) ? "D/K2-Dual Carrier FM" : \
+ (x == DRX_AUD_STANDARD_D_K3) ? "D/K3-Dual Carrier FM" : \
+ (x == DRX_AUD_STANDARD_BG_NICAM_FM) ? "B/G-NICAM-FM" : \
+ (x == DRX_AUD_STANDARD_L_NICAM_AM) ? "L-NICAM-AM" : \
+ (x == DRX_AUD_STANDARD_I_NICAM_FM) ? "I-NICAM-FM" : \
+ (x == DRX_AUD_STANDARD_D_K_NICAM_FM) ? "D/K-NICAM-FM" : \
+ (x == DRX_AUD_STANDARD_UNKNOWN) ? "Unknown" : \
+ "(Invalid)")
+#define DRX_STR_AUD_STEREO(x) ( \
+ (x == true) ? "Stereo" : \
+ (x == false) ? "Mono" : \
+ "(Invalid)")
+
+#define DRX_STR_AUD_SAP(x) ( \
+ (x == true) ? "Present" : \
+ (x == false) ? "Not present" : \
+ "(Invalid)")
+
+#define DRX_STR_AUD_CARRIER(x) ( \
+ (x == true) ? "Present" : \
+ (x == false) ? "Not present" : \
+ "(Invalid)")
+
+#define DRX_STR_AUD_RDS(x) ( \
+ (x == true) ? "Available" : \
+ (x == false) ? "Not Available" : \
+ "(Invalid)")
+
+#define DRX_STR_AUD_NICAM_STATUS(x) ( \
+ (x == DRX_AUD_NICAM_DETECTED) ? "Detected" : \
+ (x == DRX_AUD_NICAM_NOT_DETECTED) ? "Not detected" : \
+ (x == DRX_AUD_NICAM_BAD) ? "Bad" : \
+ "(Invalid)")
+
+#define DRX_STR_RDS_VALID(x) ( \
+ (x == true) ? "Valid" : \
+ (x == false) ? "Not Valid" : \
+ "(Invalid)")
+
+/*-------------------------------------------------------------------------
+Access macros
+-------------------------------------------------------------------------*/
+
+/*
+* \brief Create a compilable reference to the microcode attribute
+* \param d pointer to demod instance
+*
+* Used as main reference to an attribute field.
+* Used by both macro implementation and function implementation.
+* These macros are defined to avoid duplication of code in macro and function
+* definitions that handle access of demod common or extended attributes.
+*
+*/
+
+#define DRX_ATTR_MCRECORD(d) ((d)->my_common_attr->mcversion)
+#define DRX_ATTR_MIRRORFREQSPECT(d) ((d)->my_common_attr->mirror_freq_spect)
+#define DRX_ATTR_CURRENTPOWERMODE(d)((d)->my_common_attr->current_power_mode)
+#define DRX_ATTR_ISOPENED(d) ((d)->my_common_attr->is_opened)
+#define DRX_ATTR_USEBOOTLOADER(d) ((d)->my_common_attr->use_bootloader)
+#define DRX_ATTR_CURRENTSTANDARD(d) ((d)->my_common_attr->current_standard)
+#define DRX_ATTR_PREVSTANDARD(d) ((d)->my_common_attr->prev_standard)
+#define DRX_ATTR_CACHESTANDARD(d) ((d)->my_common_attr->di_cache_standard)
+#define DRX_ATTR_CURRENTCHANNEL(d) ((d)->my_common_attr->current_channel)
+#define DRX_ATTR_MICROCODE(d) ((d)->my_common_attr->microcode)
+#define DRX_ATTR_VERIFYMICROCODE(d) ((d)->my_common_attr->verify_microcode)
+#define DRX_ATTR_CAPABILITIES(d) ((d)->my_common_attr->capabilities)
+#define DRX_ATTR_PRODUCTID(d) ((d)->my_common_attr->product_id)
+#define DRX_ATTR_INTERMEDIATEFREQ(d) ((d)->my_common_attr->intermediate_freq)
+#define DRX_ATTR_SYSCLOCKFREQ(d) ((d)->my_common_attr->sys_clock_freq)
+#define DRX_ATTR_TUNERRFAGCPOL(d) ((d)->my_common_attr->tuner_rf_agc_pol)
+#define DRX_ATTR_TUNERIFAGCPOL(d) ((d)->my_common_attr->tuner_if_agc_pol)
+#define DRX_ATTR_TUNERSLOWMODE(d) ((d)->my_common_attr->tuner_slow_mode)
+#define DRX_ATTR_TUNERSPORTNR(d) ((d)->my_common_attr->tuner_port_nr)
+#define DRX_ATTR_I2CADDR(d) ((d)->my_i2c_dev_addr->i2c_addr)
+#define DRX_ATTR_I2CDEVID(d) ((d)->my_i2c_dev_addr->i2c_dev_id)
+#define DRX_ISMCVERTYPE(x) ((x) == AUX_VER_RECORD)
+
+/*************************/
+
+/* Macros with device-specific handling are converted to CFG functions */
+
+#define DRX_ACCESSMACRO_SET(demod, value, cfg_name, data_type) \
+ do { \
+ struct drx_cfg config; \
+ data_type cfg_data; \
+ config.cfg_type = cfg_name; \
+ config.cfg_data = &cfg_data; \
+ cfg_data = value; \
+ drx_ctrl(demod, DRX_CTRL_SET_CFG, &config); \
+ } while (0)
+
+#define DRX_ACCESSMACRO_GET(demod, value, cfg_name, data_type, error_value) \
+ do { \
+ int cfg_status; \
+ struct drx_cfg config; \
+ data_type cfg_data; \
+ config.cfg_type = cfg_name; \
+ config.cfg_data = &cfg_data; \
+ cfg_status = drx_ctrl(demod, DRX_CTRL_GET_CFG, &config); \
+ if (cfg_status == 0) { \
+ value = cfg_data; \
+ } else { \
+ value = (data_type)error_value; \
+ } \
+ } while (0)
+
+/* Configuration functions for usage by Access (XS) Macros */
+
+#ifndef DRX_XS_CFG_BASE
+#define DRX_XS_CFG_BASE (500)
+#endif
+
+#define DRX_XS_CFG_PRESET (DRX_XS_CFG_BASE + 0)
+#define DRX_XS_CFG_AUD_BTSC_DETECT (DRX_XS_CFG_BASE + 1)
+#define DRX_XS_CFG_QAM_LOCKRANGE (DRX_XS_CFG_BASE + 2)
+
+/* Access Macros with device-specific handling */
+
+#define DRX_SET_PRESET(d, x) \
+ DRX_ACCESSMACRO_SET((d), (x), DRX_XS_CFG_PRESET, char*)
+#define DRX_GET_PRESET(d, x) \
+ DRX_ACCESSMACRO_GET((d), (x), DRX_XS_CFG_PRESET, char*, "ERROR")
+
+#define DRX_SET_AUD_BTSC_DETECT(d, x) DRX_ACCESSMACRO_SET((d), (x), \
+ DRX_XS_CFG_AUD_BTSC_DETECT, enum drx_aud_btsc_detect)
+#define DRX_GET_AUD_BTSC_DETECT(d, x) DRX_ACCESSMACRO_GET((d), (x), \
+ DRX_XS_CFG_AUD_BTSC_DETECT, enum drx_aud_btsc_detect, DRX_UNKNOWN)
+
+#define DRX_SET_QAM_LOCKRANGE(d, x) DRX_ACCESSMACRO_SET((d), (x), \
+ DRX_XS_CFG_QAM_LOCKRANGE, enum drx_qam_lock_range)
+#define DRX_GET_QAM_LOCKRANGE(d, x) DRX_ACCESSMACRO_GET((d), (x), \
+ DRX_XS_CFG_QAM_LOCKRANGE, enum drx_qam_lock_range, DRX_UNKNOWN)
+
+/*
+* \brief Macro to check if std is an ATV standard
+* \retval true std is an ATV standard
+* \retval false std is an ATV standard
+*/
+#define DRX_ISATVSTD(std) (((std) == DRX_STANDARD_PAL_SECAM_BG) || \
+ ((std) == DRX_STANDARD_PAL_SECAM_DK) || \
+ ((std) == DRX_STANDARD_PAL_SECAM_I) || \
+ ((std) == DRX_STANDARD_PAL_SECAM_L) || \
+ ((std) == DRX_STANDARD_PAL_SECAM_LP) || \
+ ((std) == DRX_STANDARD_NTSC) || \
+ ((std) == DRX_STANDARD_FM))
+
+/*
+* \brief Macro to check if std is an QAM standard
+* \retval true std is an QAM standards
+* \retval false std is an QAM standards
+*/
+#define DRX_ISQAMSTD(std) (((std) == DRX_STANDARD_ITU_A) || \
+ ((std) == DRX_STANDARD_ITU_B) || \
+ ((std) == DRX_STANDARD_ITU_C) || \
+ ((std) == DRX_STANDARD_ITU_D))
+
+/*
+* \brief Macro to check if std is VSB standard
+* \retval true std is VSB standard
+* \retval false std is not VSB standard
+*/
+#define DRX_ISVSBSTD(std) ((std) == DRX_STANDARD_8VSB)
+
+/*
+* \brief Macro to check if std is DVBT standard
+* \retval true std is DVBT standard
+* \retval false std is not DVBT standard
+*/
+#define DRX_ISDVBTSTD(std) ((std) == DRX_STANDARD_DVBT)
+
+/*-------------------------------------------------------------------------
+THE END
+-------------------------------------------------------------------------*/
+#endif /* __DRXDRIVER_H__ */
diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_driver_version.h b/drivers/media/dvb-frontends/drx39xyj/drx_driver_version.h
new file mode 100644
index 000000000000..ff05a4ffb190
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/drx_driver_version.h
@@ -0,0 +1,72 @@
+/*
+ *******************************************************************************
+ * WARNING - THIS FILE HAS BEEN GENERATED - DO NOT CHANGE
+ *
+ * Filename: drx_driver_version.h
+ * Generated on: Mon Jan 18 12:09:23 2010
+ * Generated by: IDF:x 1.3.0
+ * Generated from: ../../../device/drxj/version
+ * Output start: [entry point]
+ *
+ * filename last modified re-use
+ *
+ Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of Trident Microsystems nor Hauppauge Computer Works
+ nor the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* -----------------------------------------------------
+ * version.idf Mon Jan 18 11:56:10 2010 -
+ *
+ */
+
+#ifndef __DRX_DRIVER_VERSION__H__
+#define __DRX_DRIVER_VERSION__H__ INCLUDED
+
+#ifdef _REGISTERTABLE_
+#include <registertable.h>
+ extern register_table_t drx_driver_version[];
+ extern register_table_info_t drx_driver_version_info[];
+#endif /* _REGISTERTABLE_ */
+
+/*
+ *==============================================================================
+ * VERSION
+ * version@/var/cvs/projects/drxj.cvsroot/hostcode/drxdriver/device/drxj
+ *==============================================================================
+ */
+
+#define VERSION__A 0x0
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 0
+#define VERSION_PATCH 56
+
+#endif /* __DRX_DRIVER_VERSION__H__ */
+/*
+ * End of file (drx_driver_version.h)
+ *******************************************************************************
+ */
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c
new file mode 100644
index 000000000000..428b31e60874
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c
@@ -0,0 +1,12391 @@
+/*
+ Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of Trident Microsystems nor Hauppauge Computer Works
+ nor the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+ DRXJ specific implementation of DRX driver
+ authors: Dragan Savic, Milos Nikolic, Mihajlo Katona, Tao Ding, Paul Janssen
+
+ The Linux DVB Driver for Micronas DRX39xx family (drx3933j) was
+ written by Devin Heitmueller <devin.heitmueller@kernellabs.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*-----------------------------------------------------------------------------
+INCLUDE FILES
+----------------------------------------------------------------------------*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#include <media/dvb_frontend.h>
+#include "drx39xxj.h"
+
+#include "drxj.h"
+#include "drxj_map.h"
+
+/*============================================================================*/
+/*=== DEFINES ================================================================*/
+/*============================================================================*/
+
+#define DRX39XX_MAIN_FIRMWARE "dvb-fe-drxj-mc-1.0.8.fw"
+
+/*
+* \brief Maximum u32 value.
+*/
+#ifndef MAX_U32
+#define MAX_U32 ((u32) (0xFFFFFFFFL))
+#endif
+
+/* Customer configurable hardware settings, etc */
+#ifndef MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH
+#define MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH 0x02
+#endif
+
+#ifndef MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH
+#define MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH 0x02
+#endif
+
+#ifndef MPEG_OUTPUT_CLK_DRIVE_STRENGTH
+#define MPEG_OUTPUT_CLK_DRIVE_STRENGTH 0x06
+#endif
+
+#ifndef OOB_CRX_DRIVE_STRENGTH
+#define OOB_CRX_DRIVE_STRENGTH 0x02
+#endif
+
+#ifndef OOB_DRX_DRIVE_STRENGTH
+#define OOB_DRX_DRIVE_STRENGTH 0x02
+#endif
+/*** START DJCOMBO patches to DRXJ registermap constants *********************/
+/*** registermap 200706071303 from drxj **************************************/
+#define ATV_TOP_CR_AMP_TH_FM 0x0
+#define ATV_TOP_CR_AMP_TH_L 0xA
+#define ATV_TOP_CR_AMP_TH_LP 0xA
+#define ATV_TOP_CR_AMP_TH_BG 0x8
+#define ATV_TOP_CR_AMP_TH_DK 0x8
+#define ATV_TOP_CR_AMP_TH_I 0x8
+#define ATV_TOP_CR_CONT_CR_D_MN 0x18
+#define ATV_TOP_CR_CONT_CR_D_FM 0x0
+#define ATV_TOP_CR_CONT_CR_D_L 0x20
+#define ATV_TOP_CR_CONT_CR_D_LP 0x20
+#define ATV_TOP_CR_CONT_CR_D_BG 0x18
+#define ATV_TOP_CR_CONT_CR_D_DK 0x18
+#define ATV_TOP_CR_CONT_CR_D_I 0x18
+#define ATV_TOP_CR_CONT_CR_I_MN 0x80
+#define ATV_TOP_CR_CONT_CR_I_FM 0x0
+#define ATV_TOP_CR_CONT_CR_I_L 0x80
+#define ATV_TOP_CR_CONT_CR_I_LP 0x80
+#define ATV_TOP_CR_CONT_CR_I_BG 0x80
+#define ATV_TOP_CR_CONT_CR_I_DK 0x80
+#define ATV_TOP_CR_CONT_CR_I_I 0x80
+#define ATV_TOP_CR_CONT_CR_P_MN 0x4
+#define ATV_TOP_CR_CONT_CR_P_FM 0x0
+#define ATV_TOP_CR_CONT_CR_P_L 0x4
+#define ATV_TOP_CR_CONT_CR_P_LP 0x4
+#define ATV_TOP_CR_CONT_CR_P_BG 0x4
+#define ATV_TOP_CR_CONT_CR_P_DK 0x4
+#define ATV_TOP_CR_CONT_CR_P_I 0x4
+#define ATV_TOP_CR_OVM_TH_MN 0xA0
+#define ATV_TOP_CR_OVM_TH_FM 0x0
+#define ATV_TOP_CR_OVM_TH_L 0xA0
+#define ATV_TOP_CR_OVM_TH_LP 0xA0
+#define ATV_TOP_CR_OVM_TH_BG 0xA0
+#define ATV_TOP_CR_OVM_TH_DK 0xA0
+#define ATV_TOP_CR_OVM_TH_I 0xA0
+#define ATV_TOP_EQU0_EQU_C0_FM 0x0
+#define ATV_TOP_EQU0_EQU_C0_L 0x3
+#define ATV_TOP_EQU0_EQU_C0_LP 0x3
+#define ATV_TOP_EQU0_EQU_C0_BG 0x7
+#define ATV_TOP_EQU0_EQU_C0_DK 0x0
+#define ATV_TOP_EQU0_EQU_C0_I 0x3
+#define ATV_TOP_EQU1_EQU_C1_FM 0x0
+#define ATV_TOP_EQU1_EQU_C1_L 0x1F6
+#define ATV_TOP_EQU1_EQU_C1_LP 0x1F6
+#define ATV_TOP_EQU1_EQU_C1_BG 0x197
+#define ATV_TOP_EQU1_EQU_C1_DK 0x198
+#define ATV_TOP_EQU1_EQU_C1_I 0x1F6
+#define ATV_TOP_EQU2_EQU_C2_FM 0x0
+#define ATV_TOP_EQU2_EQU_C2_L 0x28
+#define ATV_TOP_EQU2_EQU_C2_LP 0x28
+#define ATV_TOP_EQU2_EQU_C2_BG 0xC5
+#define ATV_TOP_EQU2_EQU_C2_DK 0xB0
+#define ATV_TOP_EQU2_EQU_C2_I 0x28
+#define ATV_TOP_EQU3_EQU_C3_FM 0x0
+#define ATV_TOP_EQU3_EQU_C3_L 0x192
+#define ATV_TOP_EQU3_EQU_C3_LP 0x192
+#define ATV_TOP_EQU3_EQU_C3_BG 0x12E
+#define ATV_TOP_EQU3_EQU_C3_DK 0x18E
+#define ATV_TOP_EQU3_EQU_C3_I 0x192
+#define ATV_TOP_STD_MODE_MN 0x0
+#define ATV_TOP_STD_MODE_FM 0x1
+#define ATV_TOP_STD_MODE_L 0x0
+#define ATV_TOP_STD_MODE_LP 0x0
+#define ATV_TOP_STD_MODE_BG 0x0
+#define ATV_TOP_STD_MODE_DK 0x0
+#define ATV_TOP_STD_MODE_I 0x0
+#define ATV_TOP_STD_VID_POL_MN 0x0
+#define ATV_TOP_STD_VID_POL_FM 0x0
+#define ATV_TOP_STD_VID_POL_L 0x2
+#define ATV_TOP_STD_VID_POL_LP 0x2
+#define ATV_TOP_STD_VID_POL_BG 0x0
+#define ATV_TOP_STD_VID_POL_DK 0x0
+#define ATV_TOP_STD_VID_POL_I 0x0
+#define ATV_TOP_VID_AMP_MN 0x380
+#define ATV_TOP_VID_AMP_FM 0x0
+#define ATV_TOP_VID_AMP_L 0xF50
+#define ATV_TOP_VID_AMP_LP 0xF50
+#define ATV_TOP_VID_AMP_BG 0x380
+#define ATV_TOP_VID_AMP_DK 0x394
+#define ATV_TOP_VID_AMP_I 0x3D8
+#define IQM_CF_OUT_ENA_OFDM__M 0x4
+#define IQM_FS_ADJ_SEL_B_QAM 0x1
+#define IQM_FS_ADJ_SEL_B_OFF 0x0
+#define IQM_FS_ADJ_SEL_B_VSB 0x2
+#define IQM_RC_ADJ_SEL_B_OFF 0x0
+#define IQM_RC_ADJ_SEL_B_QAM 0x1
+#define IQM_RC_ADJ_SEL_B_VSB 0x2
+/*** END DJCOMBO patches to DRXJ registermap *********************************/
+
+#include "drx_driver_version.h"
+
+/* #define DRX_DEBUG */
+#ifdef DRX_DEBUG
+#include <stdio.h>
+#endif
+
+/*-----------------------------------------------------------------------------
+ENUMS
+----------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+DEFINES
+----------------------------------------------------------------------------*/
+#ifndef DRXJ_WAKE_UP_KEY
+#define DRXJ_WAKE_UP_KEY (demod->my_i2c_dev_addr->i2c_addr)
+#endif
+
+/*
+* \def DRXJ_DEF_I2C_ADDR
+* \brief Default I2C address of a demodulator instance.
+*/
+#define DRXJ_DEF_I2C_ADDR (0x52)
+
+/*
+* \def DRXJ_DEF_DEMOD_DEV_ID
+* \brief Default device identifier of a demodultor instance.
+*/
+#define DRXJ_DEF_DEMOD_DEV_ID (1)
+
+/*
+* \def DRXJ_SCAN_TIMEOUT
+* \brief Timeout value for waiting on demod lock during channel scan (millisec).
+*/
+#define DRXJ_SCAN_TIMEOUT 1000
+
+/*
+* \def HI_I2C_DELAY
+* \brief HI timing delay for I2C timing (in nano seconds)
+*
+* Used to compute HI_CFG_DIV
+*/
+#define HI_I2C_DELAY 42
+
+/*
+* \def HI_I2C_BRIDGE_DELAY
+* \brief HI timing delay for I2C timing (in nano seconds)
+*
+* Used to compute HI_CFG_BDL
+*/
+#define HI_I2C_BRIDGE_DELAY 750
+
+/*
+* \brief Time Window for MER and SER Measurement in Units of Segment duration.
+*/
+#define VSB_TOP_MEASUREMENT_PERIOD 64
+#define SYMBOLS_PER_SEGMENT 832
+
+/*
+* \brief bit rate and segment rate constants used for SER and BER.
+*/
+/* values taken from the QAM microcode */
+#define DRXJ_QAM_SL_SIG_POWER_QAM_UNKNOWN 0
+#define DRXJ_QAM_SL_SIG_POWER_QPSK 32768
+#define DRXJ_QAM_SL_SIG_POWER_QAM8 24576
+#define DRXJ_QAM_SL_SIG_POWER_QAM16 40960
+#define DRXJ_QAM_SL_SIG_POWER_QAM32 20480
+#define DRXJ_QAM_SL_SIG_POWER_QAM64 43008
+#define DRXJ_QAM_SL_SIG_POWER_QAM128 20992
+#define DRXJ_QAM_SL_SIG_POWER_QAM256 43520
+/*
+* \brief Min supported symbolrates.
+*/
+#ifndef DRXJ_QAM_SYMBOLRATE_MIN
+#define DRXJ_QAM_SYMBOLRATE_MIN (520000)
+#endif
+
+/*
+* \brief Max supported symbolrates.
+*/
+#ifndef DRXJ_QAM_SYMBOLRATE_MAX
+#define DRXJ_QAM_SYMBOLRATE_MAX (7233000)
+#endif
+
+/*
+* \def DRXJ_QAM_MAX_WAITTIME
+* \brief Maximal wait time for QAM auto constellation in ms
+*/
+#ifndef DRXJ_QAM_MAX_WAITTIME
+#define DRXJ_QAM_MAX_WAITTIME 900
+#endif
+
+#ifndef DRXJ_QAM_FEC_LOCK_WAITTIME
+#define DRXJ_QAM_FEC_LOCK_WAITTIME 150
+#endif
+
+#ifndef DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME
+#define DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME 200
+#endif
+
+/*
+* \def SCU status and results
+* \brief SCU
+*/
+#define DRX_SCU_READY 0
+#define DRXJ_MAX_WAITTIME 100 /* ms */
+#define FEC_RS_MEASUREMENT_PERIOD 12894 /* 1 sec */
+#define FEC_RS_MEASUREMENT_PRESCALE 1 /* n sec */
+
+/*
+* \def DRX_AUD_MAX_DEVIATION
+* \brief Needed for calculation of prescale feature in AUD
+*/
+#ifndef DRXJ_AUD_MAX_FM_DEVIATION
+#define DRXJ_AUD_MAX_FM_DEVIATION 100 /* kHz */
+#endif
+
+/*
+* \brief Needed for calculation of NICAM prescale feature in AUD
+*/
+#ifndef DRXJ_AUD_MAX_NICAM_PRESCALE
+#define DRXJ_AUD_MAX_NICAM_PRESCALE (9) /* dB */
+#endif
+
+/*
+* \brief Needed for calculation of NICAM prescale feature in AUD
+*/
+#ifndef DRXJ_AUD_MAX_WAITTIME
+#define DRXJ_AUD_MAX_WAITTIME 250 /* ms */
+#endif
+
+/* ATV config changed flags */
+#define DRXJ_ATV_CHANGED_COEF (0x00000001UL)
+#define DRXJ_ATV_CHANGED_PEAK_FLT (0x00000008UL)
+#define DRXJ_ATV_CHANGED_NOISE_FLT (0x00000010UL)
+#define DRXJ_ATV_CHANGED_OUTPUT (0x00000020UL)
+#define DRXJ_ATV_CHANGED_SIF_ATT (0x00000040UL)
+
+/* UIO define */
+#define DRX_UIO_MODE_FIRMWARE_SMA DRX_UIO_MODE_FIRMWARE0
+#define DRX_UIO_MODE_FIRMWARE_SAW DRX_UIO_MODE_FIRMWARE1
+
+/*
+ * MICROCODE RELATED DEFINES
+ */
+
+/* Magic word for checking correct Endianness of microcode data */
+#define DRX_UCODE_MAGIC_WORD ((((u16)'H')<<8)+((u16)'L'))
+
+/* CRC flag in ucode header, flags field. */
+#define DRX_UCODE_CRC_FLAG (0x0001)
+
+/*
+ * Maximum size of buffer used to verify the microcode.
+ * Must be an even number
+ */
+#define DRX_UCODE_MAX_BUF_SIZE (DRXDAP_MAX_RCHUNKSIZE)
+
+#if DRX_UCODE_MAX_BUF_SIZE & 1
+#error DRX_UCODE_MAX_BUF_SIZE must be an even number
+#endif
+
+/*
+ * Power mode macros
+ */
+
+#define DRX_ISPOWERDOWNMODE(mode) ((mode == DRX_POWER_MODE_9) || \
+ (mode == DRX_POWER_MODE_10) || \
+ (mode == DRX_POWER_MODE_11) || \
+ (mode == DRX_POWER_MODE_12) || \
+ (mode == DRX_POWER_MODE_13) || \
+ (mode == DRX_POWER_MODE_14) || \
+ (mode == DRX_POWER_MODE_15) || \
+ (mode == DRX_POWER_MODE_16) || \
+ (mode == DRX_POWER_DOWN))
+
+/* Pin safe mode macro */
+#define DRXJ_PIN_SAFE_MODE 0x0000
+/*============================================================================*/
+/*=== GLOBAL VARIABLEs =======================================================*/
+/*============================================================================*/
+/*
+*/
+
+/*
+* \brief Temporary register definitions.
+* (register definitions that are not yet available in register master)
+*/
+
+/*****************************************************************************/
+/* Audio block 0x103 is write only. To avoid shadowing in driver accessing */
+/* RAM addresses directly. This must be READ ONLY to avoid problems. */
+/* Writing to the interface addresses are more than only writing the RAM */
+/* locations */
+/*****************************************************************************/
+/*
+* \brief RAM location of MODUS registers
+*/
+#define AUD_DEM_RAM_MODUS_HI__A 0x10204A3
+#define AUD_DEM_RAM_MODUS_HI__M 0xF000
+
+#define AUD_DEM_RAM_MODUS_LO__A 0x10204A4
+#define AUD_DEM_RAM_MODUS_LO__M 0x0FFF
+
+/*
+* \brief RAM location of I2S config registers
+*/
+#define AUD_DEM_RAM_I2S_CONFIG1__A 0x10204B1
+#define AUD_DEM_RAM_I2S_CONFIG2__A 0x10204B2
+
+/*
+* \brief RAM location of DCO config registers
+*/
+#define AUD_DEM_RAM_DCO_B_HI__A 0x1020461
+#define AUD_DEM_RAM_DCO_B_LO__A 0x1020462
+#define AUD_DEM_RAM_DCO_A_HI__A 0x1020463
+#define AUD_DEM_RAM_DCO_A_LO__A 0x1020464
+
+/*
+* \brief RAM location of Threshold registers
+*/
+#define AUD_DEM_RAM_NICAM_THRSHLD__A 0x102045A
+#define AUD_DEM_RAM_A2_THRSHLD__A 0x10204BB
+#define AUD_DEM_RAM_BTSC_THRSHLD__A 0x10204A6
+
+/*
+* \brief RAM location of Carrier Threshold registers
+*/
+#define AUD_DEM_RAM_CM_A_THRSHLD__A 0x10204AF
+#define AUD_DEM_RAM_CM_B_THRSHLD__A 0x10204B0
+
+/*
+* \brief FM Matrix register fix
+*/
+#ifdef AUD_DEM_WR_FM_MATRIX__A
+#undef AUD_DEM_WR_FM_MATRIX__A
+#endif
+#define AUD_DEM_WR_FM_MATRIX__A 0x105006F
+
+/*============================================================================*/
+/*
+* \brief Defines required for audio
+*/
+#define AUD_VOLUME_ZERO_DB 115
+#define AUD_VOLUME_DB_MIN -60
+#define AUD_VOLUME_DB_MAX 12
+#define AUD_CARRIER_STRENGTH_QP_0DB 0x4000
+#define AUD_CARRIER_STRENGTH_QP_0DB_LOG10T100 421
+#define AUD_MAX_AVC_REF_LEVEL 15
+#define AUD_I2S_FREQUENCY_MAX 48000UL
+#define AUD_I2S_FREQUENCY_MIN 12000UL
+#define AUD_RDS_ARRAY_SIZE 18
+
+/*
+* \brief Needed for calculation of prescale feature in AUD
+*/
+#ifndef DRX_AUD_MAX_FM_DEVIATION
+#define DRX_AUD_MAX_FM_DEVIATION (100) /* kHz */
+#endif
+
+/*
+* \brief Needed for calculation of NICAM prescale feature in AUD
+*/
+#ifndef DRX_AUD_MAX_NICAM_PRESCALE
+#define DRX_AUD_MAX_NICAM_PRESCALE (9) /* dB */
+#endif
+
+/*============================================================================*/
+/* Values for I2S Master/Slave pin configurations */
+#define SIO_PDR_I2S_CL_CFG_MODE__MASTER 0x0004
+#define SIO_PDR_I2S_CL_CFG_DRIVE__MASTER 0x0008
+#define SIO_PDR_I2S_CL_CFG_MODE__SLAVE 0x0004
+#define SIO_PDR_I2S_CL_CFG_DRIVE__SLAVE 0x0000
+
+#define SIO_PDR_I2S_DA_CFG_MODE__MASTER 0x0003
+#define SIO_PDR_I2S_DA_CFG_DRIVE__MASTER 0x0008
+#define SIO_PDR_I2S_DA_CFG_MODE__SLAVE 0x0003
+#define SIO_PDR_I2S_DA_CFG_DRIVE__SLAVE 0x0008
+
+#define SIO_PDR_I2S_WS_CFG_MODE__MASTER 0x0004
+#define SIO_PDR_I2S_WS_CFG_DRIVE__MASTER 0x0008
+#define SIO_PDR_I2S_WS_CFG_MODE__SLAVE 0x0004
+#define SIO_PDR_I2S_WS_CFG_DRIVE__SLAVE 0x0000
+
+/*============================================================================*/
+/*=== REGISTER ACCESS MACROS =================================================*/
+/*============================================================================*/
+
+/*
+* This macro is used to create byte arrays for block writes.
+* Block writes speed up I2C traffic between host and demod.
+* The macro takes care of the required byte order in a 16 bits word.
+* x -> lowbyte(x), highbyte(x)
+*/
+#define DRXJ_16TO8(x) ((u8) (((u16)x) & 0xFF)), \
+ ((u8)((((u16)x)>>8)&0xFF))
+/*
+* This macro is used to convert byte array to 16 bit register value for block read.
+* Block read speed up I2C traffic between host and demod.
+* The macro takes care of the required byte order in a 16 bits word.
+*/
+#define DRXJ_8TO16(x) ((u16) (x[0] | (x[1] << 8)))
+
+/*============================================================================*/
+/*=== MISC DEFINES ===========================================================*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*=== HI COMMAND RELATED DEFINES =============================================*/
+/*============================================================================*/
+
+/*
+* \brief General maximum number of retries for ucode command interfaces
+*/
+#define DRXJ_MAX_RETRIES (100)
+
+/*============================================================================*/
+/*=== STANDARD RELATED MACROS ================================================*/
+/*============================================================================*/
+
+#define DRXJ_ISATVSTD(std) ((std == DRX_STANDARD_PAL_SECAM_BG) || \
+ (std == DRX_STANDARD_PAL_SECAM_DK) || \
+ (std == DRX_STANDARD_PAL_SECAM_I) || \
+ (std == DRX_STANDARD_PAL_SECAM_L) || \
+ (std == DRX_STANDARD_PAL_SECAM_LP) || \
+ (std == DRX_STANDARD_NTSC) || \
+ (std == DRX_STANDARD_FM))
+
+#define DRXJ_ISQAMSTD(std) ((std == DRX_STANDARD_ITU_A) || \
+ (std == DRX_STANDARD_ITU_B) || \
+ (std == DRX_STANDARD_ITU_C) || \
+ (std == DRX_STANDARD_ITU_D))
+
+/*-----------------------------------------------------------------------------
+GLOBAL VARIABLES
+----------------------------------------------------------------------------*/
+/*
+ * DRXJ DAP structures
+ */
+
+static int drxdap_fasi_read_block(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 datasize,
+ u8 *data, u32 flags);
+
+
+static int drxj_dap_read_modify_write_reg16(struct i2c_device_addr *dev_addr,
+ u32 waddr,
+ u32 raddr,
+ u16 wdata, u16 *rdata);
+
+static int drxj_dap_read_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 *data, u32 flags);
+
+static int drxdap_fasi_read_reg32(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u32 *data, u32 flags);
+
+static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 datasize,
+ u8 *data, u32 flags);
+
+static int drxj_dap_write_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 data, u32 flags);
+
+static int drxdap_fasi_write_reg32(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u32 data, u32 flags);
+
+static struct drxj_data drxj_data_g = {
+ false, /* has_lna : true if LNA (aka PGA) present */
+ false, /* has_oob : true if OOB supported */
+ false, /* has_ntsc: true if NTSC supported */
+ false, /* has_btsc: true if BTSC supported */
+ false, /* has_smatx: true if SMA_TX pin is available */
+ false, /* has_smarx: true if SMA_RX pin is available */
+ false, /* has_gpio : true if GPIO pin is available */
+ false, /* has_irqn : true if IRQN pin is available */
+ 0, /* mfx A1/A2/A... */
+
+ /* tuner settings */
+ false, /* tuner mirrors RF signal */
+ /* standard/channel settings */
+ DRX_STANDARD_UNKNOWN, /* current standard */
+ DRX_CONSTELLATION_AUTO, /* constellation */
+ 0, /* frequency in KHz */
+ DRX_BANDWIDTH_UNKNOWN, /* curr_bandwidth */
+ DRX_MIRROR_NO, /* mirror */
+
+ /* signal quality information: */
+ /* default values taken from the QAM Programming guide */
+ /* fec_bits_desired should not be less than 4000000 */
+ 4000000, /* fec_bits_desired */
+ 5, /* fec_vd_plen */
+ 4, /* qam_vd_prescale */
+ 0xFFFF, /* qamVDPeriod */
+ 204 * 8, /* fec_rs_plen annex A */
+ 1, /* fec_rs_prescale */
+ FEC_RS_MEASUREMENT_PERIOD, /* fec_rs_period */
+ true, /* reset_pkt_err_acc */
+ 0, /* pkt_err_acc_start */
+
+ /* HI configuration */
+ 0, /* hi_cfg_timing_div */
+ 0, /* hi_cfg_bridge_delay */
+ 0, /* hi_cfg_wake_up_key */
+ 0, /* hi_cfg_ctrl */
+ 0, /* HICfgTimeout */
+ /* UIO configuration */
+ DRX_UIO_MODE_DISABLE, /* uio_sma_rx_mode */
+ DRX_UIO_MODE_DISABLE, /* uio_sma_tx_mode */
+ DRX_UIO_MODE_DISABLE, /* uioASELMode */
+ DRX_UIO_MODE_DISABLE, /* uio_irqn_mode */
+ /* FS setting */
+ 0UL, /* iqm_fs_rate_ofs */
+ false, /* pos_image */
+ /* RC setting */
+ 0UL, /* iqm_rc_rate_ofs */
+ /* AUD information */
+/* false, * flagSetAUDdone */
+/* false, * detectedRDS */
+/* true, * flagASDRequest */
+/* false, * flagHDevClear */
+/* false, * flagHDevSet */
+/* (u16) 0xFFF, * rdsLastCount */
+
+ /* ATV configuration */
+ 0UL, /* flags cfg changes */
+ /* shadow of ATV_TOP_EQU0__A */
+ {-5,
+ ATV_TOP_EQU0_EQU_C0_FM,
+ ATV_TOP_EQU0_EQU_C0_L,
+ ATV_TOP_EQU0_EQU_C0_LP,
+ ATV_TOP_EQU0_EQU_C0_BG,
+ ATV_TOP_EQU0_EQU_C0_DK,
+ ATV_TOP_EQU0_EQU_C0_I},
+ /* shadow of ATV_TOP_EQU1__A */
+ {-50,
+ ATV_TOP_EQU1_EQU_C1_FM,
+ ATV_TOP_EQU1_EQU_C1_L,
+ ATV_TOP_EQU1_EQU_C1_LP,
+ ATV_TOP_EQU1_EQU_C1_BG,
+ ATV_TOP_EQU1_EQU_C1_DK,
+ ATV_TOP_EQU1_EQU_C1_I},
+ /* shadow of ATV_TOP_EQU2__A */
+ {210,
+ ATV_TOP_EQU2_EQU_C2_FM,
+ ATV_TOP_EQU2_EQU_C2_L,
+ ATV_TOP_EQU2_EQU_C2_LP,
+ ATV_TOP_EQU2_EQU_C2_BG,
+ ATV_TOP_EQU2_EQU_C2_DK,
+ ATV_TOP_EQU2_EQU_C2_I},
+ /* shadow of ATV_TOP_EQU3__A */
+ {-160,
+ ATV_TOP_EQU3_EQU_C3_FM,
+ ATV_TOP_EQU3_EQU_C3_L,
+ ATV_TOP_EQU3_EQU_C3_LP,
+ ATV_TOP_EQU3_EQU_C3_BG,
+ ATV_TOP_EQU3_EQU_C3_DK,
+ ATV_TOP_EQU3_EQU_C3_I},
+ false, /* flag: true=bypass */
+ ATV_TOP_VID_PEAK__PRE, /* shadow of ATV_TOP_VID_PEAK__A */
+ ATV_TOP_NOISE_TH__PRE, /* shadow of ATV_TOP_NOISE_TH__A */
+ true, /* flag CVBS output enable */
+ false, /* flag SIF output enable */
+ DRXJ_SIF_ATTENUATION_0DB, /* current SIF att setting */
+ { /* qam_rf_agc_cfg */
+ DRX_STANDARD_ITU_B, /* standard */
+ DRX_AGC_CTRL_AUTO, /* ctrl_mode */
+ 0, /* output_level */
+ 0, /* min_output_level */
+ 0xFFFF, /* max_output_level */
+ 0x0000, /* speed */
+ 0x0000, /* top */
+ 0x0000 /* c.o.c. */
+ },
+ { /* qam_if_agc_cfg */
+ DRX_STANDARD_ITU_B, /* standard */
+ DRX_AGC_CTRL_AUTO, /* ctrl_mode */
+ 0, /* output_level */
+ 0, /* min_output_level */
+ 0xFFFF, /* max_output_level */
+ 0x0000, /* speed */
+ 0x0000, /* top (don't care) */
+ 0x0000 /* c.o.c. (don't care) */
+ },
+ { /* vsb_rf_agc_cfg */
+ DRX_STANDARD_8VSB, /* standard */
+ DRX_AGC_CTRL_AUTO, /* ctrl_mode */
+ 0, /* output_level */
+ 0, /* min_output_level */
+ 0xFFFF, /* max_output_level */
+ 0x0000, /* speed */
+ 0x0000, /* top (don't care) */
+ 0x0000 /* c.o.c. (don't care) */
+ },
+ { /* vsb_if_agc_cfg */
+ DRX_STANDARD_8VSB, /* standard */
+ DRX_AGC_CTRL_AUTO, /* ctrl_mode */
+ 0, /* output_level */
+ 0, /* min_output_level */
+ 0xFFFF, /* max_output_level */
+ 0x0000, /* speed */
+ 0x0000, /* top (don't care) */
+ 0x0000 /* c.o.c. (don't care) */
+ },
+ 0, /* qam_pga_cfg */
+ 0, /* vsb_pga_cfg */
+ { /* qam_pre_saw_cfg */
+ DRX_STANDARD_ITU_B, /* standard */
+ 0, /* reference */
+ false /* use_pre_saw */
+ },
+ { /* vsb_pre_saw_cfg */
+ DRX_STANDARD_8VSB, /* standard */
+ 0, /* reference */
+ false /* use_pre_saw */
+ },
+
+ /* Version information */
+#ifndef _CH_
+ {
+ "01234567890", /* human readable version microcode */
+ "01234567890" /* human readable version device specific code */
+ },
+ {
+ { /* struct drx_version for microcode */
+ DRX_MODULE_UNKNOWN,
+ (char *)(NULL),
+ 0,
+ 0,
+ 0,
+ (char *)(NULL)
+ },
+ { /* struct drx_version for device specific code */
+ DRX_MODULE_UNKNOWN,
+ (char *)(NULL),
+ 0,
+ 0,
+ 0,
+ (char *)(NULL)
+ }
+ },
+ {
+ { /* struct drx_version_list for microcode */
+ (struct drx_version *) (NULL),
+ (struct drx_version_list *) (NULL)
+ },
+ { /* struct drx_version_list for device specific code */
+ (struct drx_version *) (NULL),
+ (struct drx_version_list *) (NULL)
+ }
+ },
+#endif
+ false, /* smart_ant_inverted */
+ /* Tracking filter setting for OOB */
+ {
+ 12000,
+ 9300,
+ 6600,
+ 5280,
+ 3700,
+ 3000,
+ 2000,
+ 0},
+ false, /* oob_power_on */
+ 0, /* mpeg_ts_static_bitrate */
+ false, /* disable_te_ihandling */
+ false, /* bit_reverse_mpeg_outout */
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_AUTO, /* mpeg_output_clock_rate */
+ DRXJ_MPEG_START_WIDTH_1CLKCYC, /* mpeg_start_width */
+
+ /* Pre SAW & Agc configuration for ATV */
+ {
+ DRX_STANDARD_NTSC, /* standard */
+ 7, /* reference */
+ true /* use_pre_saw */
+ },
+ { /* ATV RF-AGC */
+ DRX_STANDARD_NTSC, /* standard */
+ DRX_AGC_CTRL_AUTO, /* ctrl_mode */
+ 0, /* output_level */
+ 0, /* min_output_level (d.c.) */
+ 0, /* max_output_level (d.c.) */
+ 3, /* speed */
+ 9500, /* top */
+ 4000 /* cut-off current */
+ },
+ { /* ATV IF-AGC */
+ DRX_STANDARD_NTSC, /* standard */
+ DRX_AGC_CTRL_AUTO, /* ctrl_mode */
+ 0, /* output_level */
+ 0, /* min_output_level (d.c.) */
+ 0, /* max_output_level (d.c.) */
+ 3, /* speed */
+ 2400, /* top */
+ 0 /* c.o.c. (d.c.) */
+ },
+ 140, /* ATV PGA config */
+ 0, /* curr_symbol_rate */
+
+ false, /* pdr_safe_mode */
+ SIO_PDR_GPIO_CFG__PRE, /* pdr_safe_restore_val_gpio */
+ SIO_PDR_VSYNC_CFG__PRE, /* pdr_safe_restore_val_v_sync */
+ SIO_PDR_SMA_RX_CFG__PRE, /* pdr_safe_restore_val_sma_rx */
+ SIO_PDR_SMA_TX_CFG__PRE, /* pdr_safe_restore_val_sma_tx */
+
+ 4, /* oob_pre_saw */
+ DRXJ_OOB_LO_POW_MINUS10DB, /* oob_lo_pow */
+ {
+ false /* aud_data, only first member */
+ },
+};
+
+/*
+* \var drxj_default_addr_g
+* \brief Default I2C address and device identifier.
+*/
+static struct i2c_device_addr drxj_default_addr_g = {
+ DRXJ_DEF_I2C_ADDR, /* i2c address */
+ DRXJ_DEF_DEMOD_DEV_ID /* device id */
+};
+
+/*
+* \var drxj_default_comm_attr_g
+* \brief Default common attributes of a drxj demodulator instance.
+*/
+static struct drx_common_attr drxj_default_comm_attr_g = {
+ NULL, /* ucode file */
+ true, /* ucode verify switch */
+ {0}, /* version record */
+
+ 44000, /* IF in kHz in case no tuner instance is used */
+ (151875 - 0), /* system clock frequency in kHz */
+ 0, /* oscillator frequency kHz */
+ 0, /* oscillator deviation in ppm, signed */
+ false, /* If true mirror frequency spectrum */
+ {
+ /* MPEG output configuration */
+ true, /* If true, enable MPEG output */
+ false, /* If true, insert RS byte */
+ false, /* If true, parallel out otherwise serial */
+ false, /* If true, invert DATA signals */
+ false, /* If true, invert ERR signal */
+ false, /* If true, invert STR signals */
+ false, /* If true, invert VAL signals */
+ false, /* If true, invert CLK signals */
+ true, /* If true, static MPEG clockrate will
+ be used, otherwise clockrate will
+ adapt to the bitrate of the TS */
+ 19392658UL, /* Maximum bitrate in b/s in case
+ static clockrate is selected */
+ DRX_MPEG_STR_WIDTH_1 /* MPEG Start width in clock cycles */
+ },
+ /* Initilisations below can be omitted, they require no user input and
+ are initially 0, NULL or false. The compiler will initialize them to these
+ values when omitted. */
+ false, /* is_opened */
+
+ /* SCAN */
+ NULL, /* no scan params yet */
+ 0, /* current scan index */
+ 0, /* next scan frequency */
+ false, /* scan ready flag */
+ 0, /* max channels to scan */
+ 0, /* nr of channels scanned */
+ NULL, /* default scan function */
+ NULL, /* default context pointer */
+ 0, /* millisec to wait for demod lock */
+ DRXJ_DEMOD_LOCK, /* desired lock */
+ false,
+
+ /* Power management */
+ DRX_POWER_UP,
+
+ /* Tuner */
+ 1, /* nr of I2C port to which tuner is */
+ 0L, /* minimum RF input frequency, in kHz */
+ 0L, /* maximum RF input frequency, in kHz */
+ false, /* Rf Agc Polarity */
+ false, /* If Agc Polarity */
+ false, /* tuner slow mode */
+
+ { /* current channel (all 0) */
+ 0UL /* channel.frequency */
+ },
+ DRX_STANDARD_UNKNOWN, /* current standard */
+ DRX_STANDARD_UNKNOWN, /* previous standard */
+ DRX_STANDARD_UNKNOWN, /* di_cache_standard */
+ false, /* use_bootloader */
+ 0UL, /* capabilities */
+ 0 /* mfx */
+};
+
+/*
+* \var drxj_default_demod_g
+* \brief Default drxj demodulator instance.
+*/
+static struct drx_demod_instance drxj_default_demod_g = {
+ &drxj_default_addr_g, /* i2c address & device id */
+ &drxj_default_comm_attr_g, /* demod common attributes */
+ &drxj_data_g /* demod device specific attributes */
+};
+
+/*
+* \brief Default audio data structure for DRK demodulator instance.
+*
+* This structure is DRXK specific.
+*
+*/
+static struct drx_aud_data drxj_default_aud_data_g = {
+ false, /* audio_is_active */
+ DRX_AUD_STANDARD_AUTO, /* audio_standard */
+
+ /* i2sdata */
+ {
+ false, /* output_enable */
+ 48000, /* frequency */
+ DRX_I2S_MODE_MASTER, /* mode */
+ DRX_I2S_WORDLENGTH_32, /* word_length */
+ DRX_I2S_POLARITY_RIGHT, /* polarity */
+ DRX_I2S_FORMAT_WS_WITH_DATA /* format */
+ },
+ /* volume */
+ {
+ true, /* mute; */
+ 0, /* volume */
+ DRX_AUD_AVC_OFF, /* avc_mode */
+ 0, /* avc_ref_level */
+ DRX_AUD_AVC_MAX_GAIN_12DB, /* avc_max_gain */
+ DRX_AUD_AVC_MAX_ATTEN_24DB, /* avc_max_atten */
+ 0, /* strength_left */
+ 0 /* strength_right */
+ },
+ DRX_AUD_AUTO_SOUND_SELECT_ON_CHANGE_ON, /* auto_sound */
+ /* ass_thresholds */
+ {
+ 440, /* A2 */
+ 12, /* BTSC */
+ 700, /* NICAM */
+ },
+ /* carrier */
+ {
+ /* a */
+ {
+ 42, /* thres */
+ DRX_NO_CARRIER_NOISE, /* opt */
+ 0, /* shift */
+ 0 /* dco */
+ },
+ /* b */
+ {
+ 42, /* thres */
+ DRX_NO_CARRIER_MUTE, /* opt */
+ 0, /* shift */
+ 0 /* dco */
+ },
+
+ },
+ /* mixer */
+ {
+ DRX_AUD_SRC_STEREO_OR_A, /* source_i2s */
+ DRX_AUD_I2S_MATRIX_STEREO, /* matrix_i2s */
+ DRX_AUD_FM_MATRIX_SOUND_A /* matrix_fm */
+ },
+ DRX_AUD_DEVIATION_NORMAL, /* deviation */
+ DRX_AUD_AVSYNC_OFF, /* av_sync */
+
+ /* prescale */
+ {
+ DRX_AUD_MAX_FM_DEVIATION, /* fm_deviation */
+ DRX_AUD_MAX_NICAM_PRESCALE /* nicam_gain */
+ },
+ DRX_AUD_FM_DEEMPH_75US, /* deemph */
+ DRX_BTSC_STEREO, /* btsc_detect */
+ 0, /* rds_data_counter */
+ false /* rds_data_present */
+};
+
+/*-----------------------------------------------------------------------------
+STRUCTURES
+----------------------------------------------------------------------------*/
+/* HI command */
+struct drxj_hi_cmd {
+ u16 cmd;
+ u16 param1;
+ u16 param2;
+ u16 param3;
+ u16 param4;
+ u16 param5;
+ u16 param6;
+};
+
+/*============================================================================*/
+/*=== MICROCODE RELATED STRUCTURES ===========================================*/
+/*============================================================================*/
+
+/*
+ * struct drxu_code_block_hdr - Structure of the microcode block headers
+ *
+ * @addr: Destination address of the data in this block
+ * @size: Size of the block data following this header counted in
+ * 16 bits words
+ * @CRC: CRC value of the data block, only valid if CRC flag is
+ * set.
+ */
+struct drxu_code_block_hdr {
+ u32 addr;
+ u16 size;
+ u16 flags;
+ u16 CRC;
+};
+
+/*-----------------------------------------------------------------------------
+FUNCTIONS
+----------------------------------------------------------------------------*/
+/* Some prototypes */
+static int
+hi_command(struct i2c_device_addr *dev_addr,
+ const struct drxj_hi_cmd *cmd, u16 *result);
+
+static int
+ctrl_lock_status(struct drx_demod_instance *demod, enum drx_lock_status *lock_stat);
+
+static int
+ctrl_power_mode(struct drx_demod_instance *demod, enum drx_power_mode *mode);
+
+static int power_down_aud(struct drx_demod_instance *demod);
+
+static int
+ctrl_set_cfg_pre_saw(struct drx_demod_instance *demod, struct drxj_cfg_pre_saw *pre_saw);
+
+static int
+ctrl_set_cfg_afe_gain(struct drx_demod_instance *demod, struct drxj_cfg_afe_gain *afe_gain);
+
+/*============================================================================*/
+/*============================================================================*/
+/*== HELPER FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+
+
+/*============================================================================*/
+
+/*
+* \fn u32 frac28(u32 N, u32 D)
+* \brief Compute: (1<<28)*N/D
+* \param N 32 bits
+* \param D 32 bits
+* \return (1<<28)*N/D
+* This function is used to avoid floating-point calculations as they may
+* not be present on the target platform.
+
+* frac28 performs an unsigned 28/28 bits division to 32-bit fixed point
+* fraction used for setting the Frequency Shifter registers.
+* N and D can hold numbers up to width: 28-bits.
+* The 4 bits integer part and the 28 bits fractional part are calculated.
+
+* Usage condition: ((1<<28)*n)/d < ((1<<32)-1) => (n/d) < 15.999
+
+* N: 0...(1<<28)-1 = 268435454
+* D: 0...(1<<28)-1
+* Q: 0...(1<<32)-1
+*/
+static u32 frac28(u32 N, u32 D)
+{
+ int i = 0;
+ u32 Q1 = 0;
+ u32 R0 = 0;
+
+ R0 = (N % D) << 4; /* 32-28 == 4 shifts possible at max */
+ Q1 = N / D; /* integer part, only the 4 least significant bits
+ will be visible in the result */
+
+ /* division using radix 16, 7 nibbles in the result */
+ for (i = 0; i < 7; i++) {
+ Q1 = (Q1 << 4) | R0 / D;
+ R0 = (R0 % D) << 4;
+ }
+ /* rounding */
+ if ((R0 >> 3) >= D)
+ Q1++;
+
+ return Q1;
+}
+
+/*
+* \fn u32 log1_times100( u32 x)
+* \brief Compute: 100*log10(x)
+* \param x 32 bits
+* \return 100*log10(x)
+*
+* 100*log10(x)
+* = 100*(log2(x)/log2(10)))
+* = (100*(2^15)*log2(x))/((2^15)*log2(10))
+* = ((200*(2^15)*log2(x))/((2^15)*log2(10)))/2
+* = ((200*(2^15)*(log2(x/y)+log2(y)))/((2^15)*log2(10)))/2
+* = ((200*(2^15)*log2(x/y))+(200*(2^15)*log2(y)))/((2^15)*log2(10)))/2
+*
+* where y = 2^k and 1<= (x/y) < 2
+*/
+
+static u32 log1_times100(u32 x)
+{
+ static const u8 scale = 15;
+ static const u8 index_width = 5;
+ /*
+ log2lut[n] = (1<<scale) * 200 * log2( 1.0 + ( (1.0/(1<<INDEXWIDTH)) * n ))
+ 0 <= n < ((1<<INDEXWIDTH)+1)
+ */
+
+ static const u32 log2lut[] = {
+ 0, /* 0.000000 */
+ 290941, /* 290941.300628 */
+ 573196, /* 573196.476418 */
+ 847269, /* 847269.179851 */
+ 1113620, /* 1113620.489452 */
+ 1372674, /* 1372673.576986 */
+ 1624818, /* 1624817.752104 */
+ 1870412, /* 1870411.981536 */
+ 2109788, /* 2109787.962654 */
+ 2343253, /* 2343252.817465 */
+ 2571091, /* 2571091.461923 */
+ 2793569, /* 2793568.696416 */
+ 3010931, /* 3010931.055901 */
+ 3223408, /* 3223408.452106 */
+ 3431216, /* 3431215.635215 */
+ 3634553, /* 3634553.498355 */
+ 3833610, /* 3833610.244726 */
+ 4028562, /* 4028562.434393 */
+ 4219576, /* 4219575.925308 */
+ 4406807, /* 4406806.721144 */
+ 4590402, /* 4590401.736809 */
+ 4770499, /* 4770499.491025 */
+ 4947231, /* 4947230.734179 */
+ 5120719, /* 5120719.018555 */
+ 5291081, /* 5291081.217197 */
+ 5458428, /* 5458427.996830 */
+ 5622864, /* 5622864.249668 */
+ 5784489, /* 5784489.488298 */
+ 5943398, /* 5943398.207380 */
+ 6099680, /* 6099680.215452 */
+ 6253421, /* 6253420.939751 */
+ 6404702, /* 6404701.706649 */
+ 6553600, /* 6553600.000000 */
+ };
+
+ u8 i = 0;
+ u32 y = 0;
+ u32 d = 0;
+ u32 k = 0;
+ u32 r = 0;
+
+ if (x == 0)
+ return 0;
+
+ /* Scale x (normalize) */
+ /* computing y in log(x/y) = log(x) - log(y) */
+ if ((x & (((u32) (-1)) << (scale + 1))) == 0) {
+ for (k = scale; k > 0; k--) {
+ if (x & (((u32) 1) << scale))
+ break;
+ x <<= 1;
+ }
+ } else {
+ for (k = scale; k < 31; k++) {
+ if ((x & (((u32) (-1)) << (scale + 1))) == 0)
+ break;
+ x >>= 1;
+ }
+ }
+ /*
+ Now x has binary point between bit[scale] and bit[scale-1]
+ and 1.0 <= x < 2.0 */
+
+ /* correction for division: log(x) = log(x/y)+log(y) */
+ y = k * ((((u32) 1) << scale) * 200);
+
+ /* remove integer part */
+ x &= ((((u32) 1) << scale) - 1);
+ /* get index */
+ i = (u8) (x >> (scale - index_width));
+ /* compute delta (x-a) */
+ d = x & ((((u32) 1) << (scale - index_width)) - 1);
+ /* compute log, multiplication ( d* (.. )) must be within range ! */
+ y += log2lut[i] +
+ ((d * (log2lut[i + 1] - log2lut[i])) >> (scale - index_width));
+ /* Conver to log10() */
+ y /= 108853; /* (log2(10) << scale) */
+ r = (y >> 1);
+ /* rounding */
+ if (y & ((u32)1))
+ r++;
+
+ return r;
+
+}
+
+/*
+* \fn u32 frac_times1e6( u16 N, u32 D)
+* \brief Compute: (N/D) * 1000000.
+* \param N nominator 16-bits.
+* \param D denominator 32-bits.
+* \return u32
+* \retval ((N/D) * 1000000), 32 bits
+*
+* No check on D=0!
+*/
+static u32 frac_times1e6(u32 N, u32 D)
+{
+ u32 remainder = 0;
+ u32 frac = 0;
+
+ /*
+ frac = (N * 1000000) / D
+ To let it fit in a 32 bits computation:
+ frac = (N * (1000000 >> 4)) / (D >> 4)
+ This would result in a problem in case D < 16 (div by 0).
+ So we do it more elaborate as shown below.
+ */
+ frac = (((u32) N) * (1000000 >> 4)) / D;
+ frac <<= 4;
+ remainder = (((u32) N) * (1000000 >> 4)) % D;
+ remainder <<= 4;
+ frac += remainder / D;
+ remainder = remainder % D;
+ if ((remainder * 2) > D)
+ frac++;
+
+ return frac;
+}
+
+/*============================================================================*/
+
+
+/*
+* \brief Values for NICAM prescaler gain. Computed from dB to integer
+* and rounded. For calc used formula: 16*10^(prescaleGain[dB]/20).
+*
+*/
+#if 0
+/* Currently, unused as we lack support for analog TV */
+static const u16 nicam_presc_table_val[43] = {
+ 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 8, 9, 10, 11, 13, 14, 16,
+ 18, 20, 23, 25, 28, 32, 36, 40, 45,
+ 51, 57, 64, 71, 80, 90, 101, 113, 127
+};
+#endif
+
+/*============================================================================*/
+/*== END HELPER FUNCTIONS ==*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== DRXJ DAP FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+
+/*
+ This layer takes care of some device specific register access protocols:
+ -conversion to short address format
+ -access to audio block
+ This layer is placed between the drx_dap_fasi and the rest of the drxj
+ specific implementation. This layer can use address map knowledge whereas
+ dap_fasi may not use memory map knowledge.
+
+ * For audio currently only 16 bits read and write register access is
+ supported. More is not needed. RMW and 32 or 8 bit access on audio
+ registers will have undefined behaviour. Flags (RMW, CRC reset, broadcast
+ single/multi master) will be ignored.
+
+ TODO: check ignoring single/multimaster is ok for AUD access ?
+*/
+
+#define DRXJ_ISAUDWRITE(addr) ((((addr) >> 16) & 1) == 1)
+#define DRXJ_DAP_AUDTRIF_TIMEOUT 80 /* millisec */
+/*============================================================================*/
+
+/*
+* \fn bool is_handled_by_aud_tr_if( u32 addr )
+* \brief Check if this address is handled by the audio token ring interface.
+* \param addr
+* \return bool
+* \retval true Yes, handled by audio token ring interface
+* \retval false No, not handled by audio token ring interface
+*
+*/
+static
+bool is_handled_by_aud_tr_if(u32 addr)
+{
+ bool retval = false;
+
+ if ((DRXDAP_FASI_ADDR2BLOCK(addr) == 4) &&
+ (DRXDAP_FASI_ADDR2BANK(addr) > 1) &&
+ (DRXDAP_FASI_ADDR2BANK(addr) < 6)) {
+ retval = true;
+ }
+
+ return retval;
+}
+
+/*============================================================================*/
+
+int drxbsp_i2c_write_read(struct i2c_device_addr *w_dev_addr,
+ u16 w_count,
+ u8 *wData,
+ struct i2c_device_addr *r_dev_addr,
+ u16 r_count, u8 *r_data)
+{
+ struct drx39xxj_state *state;
+ struct i2c_msg msg[2];
+ unsigned int num_msgs;
+
+ if (w_dev_addr == NULL) {
+ /* Read only */
+ state = r_dev_addr->user_data;
+ msg[0].addr = r_dev_addr->i2c_addr >> 1;
+ msg[0].flags = I2C_M_RD;
+ msg[0].buf = r_data;
+ msg[0].len = r_count;
+ num_msgs = 1;
+ } else if (r_dev_addr == NULL) {
+ /* Write only */
+ state = w_dev_addr->user_data;
+ msg[0].addr = w_dev_addr->i2c_addr >> 1;
+ msg[0].flags = 0;
+ msg[0].buf = wData;
+ msg[0].len = w_count;
+ num_msgs = 1;
+ } else {
+ /* Both write and read */
+ state = w_dev_addr->user_data;
+ msg[0].addr = w_dev_addr->i2c_addr >> 1;
+ msg[0].flags = 0;
+ msg[0].buf = wData;
+ msg[0].len = w_count;
+ msg[1].addr = r_dev_addr->i2c_addr >> 1;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = r_data;
+ msg[1].len = r_count;
+ num_msgs = 2;
+ }
+
+ if (state->i2c == NULL) {
+ pr_err("i2c was zero, aborting\n");
+ return 0;
+ }
+ if (i2c_transfer(state->i2c, msg, num_msgs) != num_msgs) {
+ pr_warn("drx3933: I2C write/read failed\n");
+ return -EREMOTEIO;
+ }
+
+#ifdef DJH_DEBUG
+ if (w_dev_addr == NULL || r_dev_addr == NULL)
+ return 0;
+
+ state = w_dev_addr->user_data;
+
+ if (state->i2c == NULL)
+ return 0;
+
+ msg[0].addr = w_dev_addr->i2c_addr;
+ msg[0].flags = 0;
+ msg[0].buf = wData;
+ msg[0].len = w_count;
+ msg[1].addr = r_dev_addr->i2c_addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = r_data;
+ msg[1].len = r_count;
+ num_msgs = 2;
+
+ pr_debug("drx3933 i2c operation addr=%x i2c=%p, wc=%x rc=%x\n",
+ w_dev_addr->i2c_addr, state->i2c, w_count, r_count);
+
+ if (i2c_transfer(state->i2c, msg, 2) != 2) {
+ pr_warn("drx3933: I2C write/read failed\n");
+ return -EREMOTEIO;
+ }
+#endif
+ return 0;
+}
+
+/*============================================================================*/
+
+/*****************************
+*
+* int drxdap_fasi_read_block (
+* struct i2c_device_addr *dev_addr, -- address of I2C device
+* u32 addr, -- address of chip register/memory
+* u16 datasize, -- number of bytes to read
+* u8 *data, -- data to receive
+* u32 flags) -- special device flags
+*
+* Read block data from chip address. Because the chip is word oriented,
+* the number of bytes to read must be even.
+*
+* Make sure that the buffer to receive the data is large enough.
+*
+* Although this function expects an even number of bytes, it is still byte
+* oriented, and the data read back is NOT translated to the endianness of
+* the target platform.
+*
+* Output:
+* - 0 if reading was successful
+* in that case: data read is in *data.
+* - -EIO if anything went wrong
+*
+******************************/
+
+static int drxdap_fasi_read_block(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 datasize,
+ u8 *data, u32 flags)
+{
+ u8 buf[4];
+ u16 bufx;
+ int rc;
+ u16 overhead_size = 0;
+
+ /* Check parameters ******************************************************* */
+ if (dev_addr == NULL)
+ return -EINVAL;
+
+ overhead_size = (IS_I2C_10BIT(dev_addr->i2c_addr) ? 2 : 1) +
+ (DRXDAP_FASI_LONG_FORMAT(addr) ? 4 : 2);
+
+ if ((DRXDAP_FASI_OFFSET_TOO_LARGE(addr)) ||
+ ((!(DRXDAPFASI_LONG_ADDR_ALLOWED)) &&
+ DRXDAP_FASI_LONG_FORMAT(addr)) ||
+ (overhead_size > (DRXDAP_MAX_WCHUNKSIZE)) ||
+ ((datasize != 0) && (data == NULL)) || ((datasize & 1) == 1)) {
+ return -EINVAL;
+ }
+
+ /* ReadModifyWrite & mode flag bits are not allowed */
+ flags &= (~DRXDAP_FASI_RMW & ~DRXDAP_FASI_MODEFLAGS);
+#if DRXDAP_SINGLE_MASTER
+ flags |= DRXDAP_FASI_SINGLE_MASTER;
+#endif
+
+ /* Read block from I2C **************************************************** */
+ do {
+ u16 todo = min(datasize, DRXDAP_MAX_RCHUNKSIZE);
+
+ bufx = 0;
+
+ addr &= ~DRXDAP_FASI_FLAGS;
+ addr |= flags;
+
+#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 1) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1))
+ /* short format address preferred but long format otherwise */
+ if (DRXDAP_FASI_LONG_FORMAT(addr)) {
+#endif
+#if (DRXDAPFASI_LONG_ADDR_ALLOWED == 1)
+ buf[bufx++] = (u8) (((addr << 1) & 0xFF) | 0x01);
+ buf[bufx++] = (u8) ((addr >> 16) & 0xFF);
+ buf[bufx++] = (u8) ((addr >> 24) & 0xFF);
+ buf[bufx++] = (u8) ((addr >> 7) & 0xFF);
+#endif
+#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 1) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1))
+ } else {
+#endif
+#if (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1)
+ buf[bufx++] = (u8) ((addr << 1) & 0xFF);
+ buf[bufx++] =
+ (u8) (((addr >> 16) & 0x0F) |
+ ((addr >> 18) & 0xF0));
+#endif
+#if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 1) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1))
+ }
+#endif
+
+#if DRXDAP_SINGLE_MASTER
+ /*
+ * In single master mode, split the read and write actions.
+ * No special action is needed for write chunks here.
+ */
+ rc = drxbsp_i2c_write_read(dev_addr, bufx, buf,
+ NULL, 0, NULL);
+ if (rc == 0)
+ rc = drxbsp_i2c_write_read(NULL, 0, NULL, dev_addr, todo, data);
+#else
+ /* In multi master mode, do everything in one RW action */
+ rc = drxbsp_i2c_write_read(dev_addr, bufx, buf, dev_addr, todo,
+ data);
+#endif
+ data += todo;
+ addr += (todo >> 1);
+ datasize -= todo;
+ } while (datasize && rc == 0);
+
+ return rc;
+}
+
+
+/*****************************
+*
+* int drxdap_fasi_read_reg16 (
+* struct i2c_device_addr *dev_addr, -- address of I2C device
+* u32 addr, -- address of chip register/memory
+* u16 *data, -- data to receive
+* u32 flags) -- special device flags
+*
+* Read one 16-bit register or memory location. The data received back is
+* converted back to the target platform's endianness.
+*
+* Output:
+* - 0 if reading was successful
+* in that case: read data is at *data
+* - -EIO if anything went wrong
+*
+******************************/
+
+static int drxdap_fasi_read_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 *data, u32 flags)
+{
+ u8 buf[sizeof(*data)];
+ int rc;
+
+ if (!data)
+ return -EINVAL;
+
+ rc = drxdap_fasi_read_block(dev_addr, addr, sizeof(*data), buf, flags);
+ *data = buf[0] + (((u16) buf[1]) << 8);
+ return rc;
+}
+
+/*****************************
+*
+* int drxdap_fasi_read_reg32 (
+* struct i2c_device_addr *dev_addr, -- address of I2C device
+* u32 addr, -- address of chip register/memory
+* u32 *data, -- data to receive
+* u32 flags) -- special device flags
+*
+* Read one 32-bit register or memory location. The data received back is
+* converted back to the target platform's endianness.
+*
+* Output:
+* - 0 if reading was successful
+* in that case: read data is at *data
+* - -EIO if anything went wrong
+*
+******************************/
+
+static int drxdap_fasi_read_reg32(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u32 *data, u32 flags)
+{
+ u8 buf[sizeof(*data)];
+ int rc;
+
+ if (!data)
+ return -EINVAL;
+
+ rc = drxdap_fasi_read_block(dev_addr, addr, sizeof(*data), buf, flags);
+ *data = (((u32) buf[0]) << 0) +
+ (((u32) buf[1]) << 8) +
+ (((u32) buf[2]) << 16) + (((u32) buf[3]) << 24);
+ return rc;
+}
+
+/*****************************
+*
+* int drxdap_fasi_write_block (
+* struct i2c_device_addr *dev_addr, -- address of I2C device
+* u32 addr, -- address of chip register/memory
+* u16 datasize, -- number of bytes to read
+* u8 *data, -- data to receive
+* u32 flags) -- special device flags
+*
+* Write block data to chip address. Because the chip is word oriented,
+* the number of bytes to write must be even.
+*
+* Although this function expects an even number of bytes, it is still byte
+* oriented, and the data being written is NOT translated from the endianness of
+* the target platform.
+*
+* Output:
+* - 0 if writing was successful
+* - -EIO if anything went wrong
+*
+******************************/
+
+static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 datasize,
+ u8 *data, u32 flags)
+{
+ u8 buf[DRXDAP_MAX_WCHUNKSIZE];
+ int st = -EIO;
+ int first_err = 0;
+ u16 overhead_size = 0;
+ u16 block_size = 0;
+
+ /* Check parameters ******************************************************* */
+ if (dev_addr == NULL)
+ return -EINVAL;
+
+ overhead_size = (IS_I2C_10BIT(dev_addr->i2c_addr) ? 2 : 1) +
+ (DRXDAP_FASI_LONG_FORMAT(addr) ? 4 : 2);
+
+ if ((DRXDAP_FASI_OFFSET_TOO_LARGE(addr)) ||
+ ((!(DRXDAPFASI_LONG_ADDR_ALLOWED)) &&
+ DRXDAP_FASI_LONG_FORMAT(addr)) ||
+ (overhead_size > (DRXDAP_MAX_WCHUNKSIZE)) ||
+ ((datasize != 0) && (data == NULL)) || ((datasize & 1) == 1))
+ return -EINVAL;
+
+ flags &= DRXDAP_FASI_FLAGS;
+ flags &= ~DRXDAP_FASI_MODEFLAGS;
+#if DRXDAP_SINGLE_MASTER
+ flags |= DRXDAP_FASI_SINGLE_MASTER;
+#endif
+
+ /* Write block to I2C ***************************************************** */
+ block_size = ((DRXDAP_MAX_WCHUNKSIZE) - overhead_size) & ~1;
+ do {
+ u16 todo = 0;
+ u16 bufx = 0;
+
+ /* Buffer device address */
+ addr &= ~DRXDAP_FASI_FLAGS;
+ addr |= flags;
+#if (((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1) && ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1))
+ /* short format address preferred but long format otherwise */
+ if (DRXDAP_FASI_LONG_FORMAT(addr)) {
+#endif
+#if ((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1)
+ buf[bufx++] = (u8) (((addr << 1) & 0xFF) | 0x01);
+ buf[bufx++] = (u8) ((addr >> 16) & 0xFF);
+ buf[bufx++] = (u8) ((addr >> 24) & 0xFF);
+ buf[bufx++] = (u8) ((addr >> 7) & 0xFF);
+#endif
+#if (((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1) && ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1))
+ } else {
+#endif
+#if ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1)
+ buf[bufx++] = (u8) ((addr << 1) & 0xFF);
+ buf[bufx++] =
+ (u8) (((addr >> 16) & 0x0F) |
+ ((addr >> 18) & 0xF0));
+#endif
+#if (((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1) && ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1))
+ }
+#endif
+
+ /*
+ In single master mode block_size can be 0. In such a case this I2C
+ sequense will be visible: (1) write address {i2c addr,
+ 4 bytes chip address} (2) write data {i2c addr, 4 bytes data }
+ (3) write address (4) write data etc...
+ Address must be rewritten because HI is reset after data transport and
+ expects an address.
+ */
+ todo = min(block_size, datasize);
+ if (todo == 0) {
+ u16 overhead_size_i2c_addr = 0;
+ u16 data_block_size = 0;
+
+ overhead_size_i2c_addr =
+ (IS_I2C_10BIT(dev_addr->i2c_addr) ? 2 : 1);
+ data_block_size =
+ (DRXDAP_MAX_WCHUNKSIZE - overhead_size_i2c_addr) & ~1;
+
+ /* write device address */
+ st = drxbsp_i2c_write_read(dev_addr,
+ (u16) (bufx),
+ buf,
+ (struct i2c_device_addr *)(NULL),
+ 0, (u8 *)(NULL));
+
+ if ((st != 0) && (first_err == 0)) {
+ /* at the end, return the first error encountered */
+ first_err = st;
+ }
+ bufx = 0;
+ todo = min(data_block_size, datasize);
+ }
+ memcpy(&buf[bufx], data, todo);
+ /* write (address if can do and) data */
+ st = drxbsp_i2c_write_read(dev_addr,
+ (u16) (bufx + todo),
+ buf,
+ (struct i2c_device_addr *)(NULL),
+ 0, (u8 *)(NULL));
+
+ if ((st != 0) && (first_err == 0)) {
+ /* at the end, return the first error encountered */
+ first_err = st;
+ }
+ datasize -= todo;
+ data += todo;
+ addr += (todo >> 1);
+ } while (datasize);
+
+ return first_err;
+}
+
+/*****************************
+*
+* int drxdap_fasi_write_reg16 (
+* struct i2c_device_addr *dev_addr, -- address of I2C device
+* u32 addr, -- address of chip register/memory
+* u16 data, -- data to send
+* u32 flags) -- special device flags
+*
+* Write one 16-bit register or memory location. The data being written is
+* converted from the target platform's endianness to little endian.
+*
+* Output:
+* - 0 if writing was successful
+* - -EIO if anything went wrong
+*
+******************************/
+
+static int drxdap_fasi_write_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 data, u32 flags)
+{
+ u8 buf[sizeof(data)];
+
+ buf[0] = (u8) ((data >> 0) & 0xFF);
+ buf[1] = (u8) ((data >> 8) & 0xFF);
+
+ return drxdap_fasi_write_block(dev_addr, addr, sizeof(data), buf, flags);
+}
+
+/*****************************
+*
+* int drxdap_fasi_read_modify_write_reg16 (
+* struct i2c_device_addr *dev_addr, -- address of I2C device
+* u32 waddr, -- address of chip register/memory
+* u32 raddr, -- chip address to read back from
+* u16 wdata, -- data to send
+* u16 *rdata) -- data to receive back
+*
+* Write 16-bit data, then read back the original contents of that location.
+* Requires long addressing format to be allowed.
+*
+* Before sending data, the data is converted to little endian. The
+* data received back is converted back to the target platform's endianness.
+*
+* WARNING: This function is only guaranteed to work if there is one
+* master on the I2C bus.
+*
+* Output:
+* - 0 if reading was successful
+* in that case: read back data is at *rdata
+* - -EIO if anything went wrong
+*
+******************************/
+
+static int drxdap_fasi_read_modify_write_reg16(struct i2c_device_addr *dev_addr,
+ u32 waddr,
+ u32 raddr,
+ u16 wdata, u16 *rdata)
+{
+ int rc = -EIO;
+
+#if (DRXDAPFASI_LONG_ADDR_ALLOWED == 1)
+ if (rdata == NULL)
+ return -EINVAL;
+
+ rc = drxdap_fasi_write_reg16(dev_addr, waddr, wdata, DRXDAP_FASI_RMW);
+ if (rc == 0)
+ rc = drxdap_fasi_read_reg16(dev_addr, raddr, rdata, 0);
+#endif
+
+ return rc;
+}
+
+/*****************************
+*
+* int drxdap_fasi_write_reg32 (
+* struct i2c_device_addr *dev_addr, -- address of I2C device
+* u32 addr, -- address of chip register/memory
+* u32 data, -- data to send
+* u32 flags) -- special device flags
+*
+* Write one 32-bit register or memory location. The data being written is
+* converted from the target platform's endianness to little endian.
+*
+* Output:
+* - 0 if writing was successful
+* - -EIO if anything went wrong
+*
+******************************/
+
+static int drxdap_fasi_write_reg32(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u32 data, u32 flags)
+{
+ u8 buf[sizeof(data)];
+
+ buf[0] = (u8) ((data >> 0) & 0xFF);
+ buf[1] = (u8) ((data >> 8) & 0xFF);
+ buf[2] = (u8) ((data >> 16) & 0xFF);
+ buf[3] = (u8) ((data >> 24) & 0xFF);
+
+ return drxdap_fasi_write_block(dev_addr, addr, sizeof(data), buf, flags);
+}
+
+/*============================================================================*/
+
+/*
+* \fn int drxj_dap_rm_write_reg16short
+* \brief Read modify write 16 bits audio register using short format only.
+* \param dev_addr
+* \param waddr Address to write to
+* \param raddr Address to read from (usually SIO_HI_RA_RAM_S0_RMWBUF__A)
+* \param wdata Data to write
+* \param rdata Buffer for data to read
+* \return int
+* \retval 0 Success
+* \retval -EIO Timeout, I2C error, illegal bank
+*
+* 16 bits register read modify write access using short addressing format only.
+* Requires knowledge of the registermap, thus device dependent.
+* Using DAP FASI directly to avoid endless recursion of RMWs to audio registers.
+*
+*/
+
+/* TODO correct define should be #if ( DRXDAPFASI_SHORT_ADDR_ALLOWED==1 )
+ See comments drxj_dap_read_modify_write_reg16 */
+#if (DRXDAPFASI_LONG_ADDR_ALLOWED == 0)
+static int drxj_dap_rm_write_reg16short(struct i2c_device_addr *dev_addr,
+ u32 waddr,
+ u32 raddr,
+ u16 wdata, u16 *rdata)
+{
+ int rc;
+
+ if (rdata == NULL)
+ return -EINVAL;
+
+ /* Set RMW flag */
+ rc = drxdap_fasi_write_reg16(dev_addr,
+ SIO_HI_RA_RAM_S0_FLG_ACC__A,
+ SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__M,
+ 0x0000);
+ if (rc == 0) {
+ /* Write new data: triggers RMW */
+ rc = drxdap_fasi_write_reg16(dev_addr, waddr, wdata,
+ 0x0000);
+ }
+ if (rc == 0) {
+ /* Read old data */
+ rc = drxdap_fasi_read_reg16(dev_addr, raddr, rdata,
+ 0x0000);
+ }
+ if (rc == 0) {
+ /* Reset RMW flag */
+ rc = drxdap_fasi_write_reg16(dev_addr,
+ SIO_HI_RA_RAM_S0_FLG_ACC__A,
+ 0, 0x0000);
+ }
+
+ return rc;
+}
+#endif
+
+/*============================================================================*/
+
+static int drxj_dap_read_modify_write_reg16(struct i2c_device_addr *dev_addr,
+ u32 waddr,
+ u32 raddr,
+ u16 wdata, u16 *rdata)
+{
+ /* TODO: correct short/long addressing format decision,
+ now long format has higher prio then short because short also
+ needs virt bnks (not impl yet) for certain audio registers */
+#if (DRXDAPFASI_LONG_ADDR_ALLOWED == 1)
+ return drxdap_fasi_read_modify_write_reg16(dev_addr,
+ waddr,
+ raddr, wdata, rdata);
+#else
+ return drxj_dap_rm_write_reg16short(dev_addr, waddr, raddr, wdata, rdata);
+#endif
+}
+
+
+/*============================================================================*/
+
+/*
+* \fn int drxj_dap_read_aud_reg16
+* \brief Read 16 bits audio register
+* \param dev_addr
+* \param addr
+* \param data
+* \return int
+* \retval 0 Success
+* \retval -EIO Timeout, I2C error, illegal bank
+*
+* 16 bits register read access via audio token ring interface.
+*
+*/
+static int drxj_dap_read_aud_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr, u16 *data)
+{
+ u32 start_timer = 0;
+ u32 current_timer = 0;
+ u32 delta_timer = 0;
+ u16 tr_status = 0;
+ int stat = -EIO;
+
+ /* No read possible for bank 3, return with error */
+ if (DRXDAP_FASI_ADDR2BANK(addr) == 3) {
+ stat = -EINVAL;
+ } else {
+ const u32 write_bit = ((dr_xaddr_t) 1) << 16;
+
+ /* Force reset write bit */
+ addr &= (~write_bit);
+
+ /* Set up read */
+ start_timer = jiffies_to_msecs(jiffies);
+ do {
+ /* RMW to aud TR IF until request is granted or timeout */
+ stat = drxj_dap_read_modify_write_reg16(dev_addr,
+ addr,
+ SIO_HI_RA_RAM_S0_RMWBUF__A,
+ 0x0000, &tr_status);
+
+ if (stat != 0)
+ break;
+
+ current_timer = jiffies_to_msecs(jiffies);
+ delta_timer = current_timer - start_timer;
+ if (delta_timer > DRXJ_DAP_AUDTRIF_TIMEOUT) {
+ stat = -EIO;
+ break;
+ }
+
+ } while (((tr_status & AUD_TOP_TR_CTR_FIFO_LOCK__M) ==
+ AUD_TOP_TR_CTR_FIFO_LOCK_LOCKED) ||
+ ((tr_status & AUD_TOP_TR_CTR_FIFO_FULL__M) ==
+ AUD_TOP_TR_CTR_FIFO_FULL_FULL));
+ } /* if ( DRXDAP_FASI_ADDR2BANK(addr)!=3 ) */
+
+ /* Wait for read ready status or timeout */
+ if (stat == 0) {
+ start_timer = jiffies_to_msecs(jiffies);
+
+ while ((tr_status & AUD_TOP_TR_CTR_FIFO_RD_RDY__M) !=
+ AUD_TOP_TR_CTR_FIFO_RD_RDY_READY) {
+ stat = drxj_dap_read_reg16(dev_addr,
+ AUD_TOP_TR_CTR__A,
+ &tr_status, 0x0000);
+ if (stat != 0)
+ break;
+
+ current_timer = jiffies_to_msecs(jiffies);
+ delta_timer = current_timer - start_timer;
+ if (delta_timer > DRXJ_DAP_AUDTRIF_TIMEOUT) {
+ stat = -EIO;
+ break;
+ }
+ } /* while ( ... ) */
+ }
+
+ /* Read value */
+ if (stat == 0)
+ stat = drxj_dap_read_modify_write_reg16(dev_addr,
+ AUD_TOP_TR_RD_REG__A,
+ SIO_HI_RA_RAM_S0_RMWBUF__A,
+ 0x0000, data);
+ return stat;
+}
+
+/*============================================================================*/
+
+static int drxj_dap_read_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 *data, u32 flags)
+{
+ int stat = -EIO;
+
+ /* Check param */
+ if ((dev_addr == NULL) || (data == NULL))
+ return -EINVAL;
+
+ if (is_handled_by_aud_tr_if(addr))
+ stat = drxj_dap_read_aud_reg16(dev_addr, addr, data);
+ else
+ stat = drxdap_fasi_read_reg16(dev_addr, addr, data, flags);
+
+ return stat;
+}
+/*============================================================================*/
+
+/*
+* \fn int drxj_dap_write_aud_reg16
+* \brief Write 16 bits audio register
+* \param dev_addr
+* \param addr
+* \param data
+* \return int
+* \retval 0 Success
+* \retval -EIO Timeout, I2C error, illegal bank
+*
+* 16 bits register write access via audio token ring interface.
+*
+*/
+static int drxj_dap_write_aud_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr, u16 data)
+{
+ int stat = -EIO;
+
+ /* No write possible for bank 2, return with error */
+ if (DRXDAP_FASI_ADDR2BANK(addr) == 2) {
+ stat = -EINVAL;
+ } else {
+ u32 start_timer = 0;
+ u32 current_timer = 0;
+ u32 delta_timer = 0;
+ u16 tr_status = 0;
+ const u32 write_bit = ((dr_xaddr_t) 1) << 16;
+
+ /* Force write bit */
+ addr |= write_bit;
+ start_timer = jiffies_to_msecs(jiffies);
+ do {
+ /* RMW to aud TR IF until request is granted or timeout */
+ stat = drxj_dap_read_modify_write_reg16(dev_addr,
+ addr,
+ SIO_HI_RA_RAM_S0_RMWBUF__A,
+ data, &tr_status);
+ if (stat != 0)
+ break;
+
+ current_timer = jiffies_to_msecs(jiffies);
+ delta_timer = current_timer - start_timer;
+ if (delta_timer > DRXJ_DAP_AUDTRIF_TIMEOUT) {
+ stat = -EIO;
+ break;
+ }
+
+ } while (((tr_status & AUD_TOP_TR_CTR_FIFO_LOCK__M) ==
+ AUD_TOP_TR_CTR_FIFO_LOCK_LOCKED) ||
+ ((tr_status & AUD_TOP_TR_CTR_FIFO_FULL__M) ==
+ AUD_TOP_TR_CTR_FIFO_FULL_FULL));
+
+ } /* if ( DRXDAP_FASI_ADDR2BANK(addr)!=2 ) */
+
+ return stat;
+}
+
+/*============================================================================*/
+
+static int drxj_dap_write_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 data, u32 flags)
+{
+ int stat = -EIO;
+
+ /* Check param */
+ if (dev_addr == NULL)
+ return -EINVAL;
+
+ if (is_handled_by_aud_tr_if(addr))
+ stat = drxj_dap_write_aud_reg16(dev_addr, addr, data);
+ else
+ stat = drxdap_fasi_write_reg16(dev_addr,
+ addr, data, flags);
+
+ return stat;
+}
+
+/*============================================================================*/
+
+/* Free data ram in SIO HI */
+#define SIO_HI_RA_RAM_USR_BEGIN__A 0x420040
+#define SIO_HI_RA_RAM_USR_END__A 0x420060
+
+#define DRXJ_HI_ATOMIC_BUF_START (SIO_HI_RA_RAM_USR_BEGIN__A)
+#define DRXJ_HI_ATOMIC_BUF_END (SIO_HI_RA_RAM_USR_BEGIN__A + 7)
+#define DRXJ_HI_ATOMIC_READ SIO_HI_RA_RAM_PAR_3_ACP_RW_READ
+#define DRXJ_HI_ATOMIC_WRITE SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE
+
+/*
+* \fn int drxj_dap_atomic_read_write_block()
+* \brief Basic access routine for atomic read or write access
+* \param dev_addr pointer to i2c dev address
+* \param addr destination/source address
+* \param datasize size of data buffer in bytes
+* \param data pointer to data buffer
+* \return int
+* \retval 0 Success
+* \retval -EIO Timeout, I2C error, illegal bank
+*
+*/
+static
+int drxj_dap_atomic_read_write_block(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 datasize,
+ u8 *data, bool read_flag)
+{
+ struct drxj_hi_cmd hi_cmd;
+ int rc;
+ u16 word;
+ u16 dummy = 0;
+ u16 i = 0;
+
+ /* Parameter check */
+ if (!data || !dev_addr || ((datasize % 2)) || ((datasize / 2) > 8))
+ return -EINVAL;
+
+ /* Set up HI parameters to read or write n bytes */
+ hi_cmd.cmd = SIO_HI_RA_RAM_CMD_ATOMIC_COPY;
+ hi_cmd.param1 =
+ (u16) ((DRXDAP_FASI_ADDR2BLOCK(DRXJ_HI_ATOMIC_BUF_START) << 6) +
+ DRXDAP_FASI_ADDR2BANK(DRXJ_HI_ATOMIC_BUF_START));
+ hi_cmd.param2 =
+ (u16) DRXDAP_FASI_ADDR2OFFSET(DRXJ_HI_ATOMIC_BUF_START);
+ hi_cmd.param3 = (u16) ((datasize / 2) - 1);
+ if (!read_flag)
+ hi_cmd.param3 |= DRXJ_HI_ATOMIC_WRITE;
+ else
+ hi_cmd.param3 |= DRXJ_HI_ATOMIC_READ;
+ hi_cmd.param4 = (u16) ((DRXDAP_FASI_ADDR2BLOCK(addr) << 6) +
+ DRXDAP_FASI_ADDR2BANK(addr));
+ hi_cmd.param5 = (u16) DRXDAP_FASI_ADDR2OFFSET(addr);
+
+ if (!read_flag) {
+ /* write data to buffer */
+ for (i = 0; i < (datasize / 2); i++) {
+
+ word = ((u16) data[2 * i]);
+ word += (((u16) data[(2 * i) + 1]) << 8);
+ drxj_dap_write_reg16(dev_addr,
+ (DRXJ_HI_ATOMIC_BUF_START + i),
+ word, 0);
+ }
+ }
+
+ rc = hi_command(dev_addr, &hi_cmd, &dummy);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (read_flag) {
+ /* read data from buffer */
+ for (i = 0; i < (datasize / 2); i++) {
+ rc = drxj_dap_read_reg16(dev_addr,
+ (DRXJ_HI_ATOMIC_BUF_START + i),
+ &word, 0);
+ if (rc) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data[2 * i] = (u8) (word & 0xFF);
+ data[(2 * i) + 1] = (u8) (word >> 8);
+ }
+ }
+
+ return 0;
+
+rw_error:
+ return rc;
+
+}
+
+/*============================================================================*/
+
+/*
+* \fn int drxj_dap_atomic_read_reg32()
+* \brief Atomic read of 32 bits words
+*/
+static
+int drxj_dap_atomic_read_reg32(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u32 *data, u32 flags)
+{
+ u8 buf[sizeof(*data)] = { 0 };
+ int rc;
+ u32 word = 0;
+
+ if (!data)
+ return -EINVAL;
+
+ rc = drxj_dap_atomic_read_write_block(dev_addr, addr,
+ sizeof(*data), buf, true);
+
+ if (rc < 0)
+ return 0;
+
+ word = (u32) buf[3];
+ word <<= 8;
+ word |= (u32) buf[2];
+ word <<= 8;
+ word |= (u32) buf[1];
+ word <<= 8;
+ word |= (u32) buf[0];
+
+ *data = word;
+
+ return rc;
+}
+
+/*============================================================================*/
+
+/*============================================================================*/
+/*== END DRXJ DAP FUNCTIONS ==*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== HOST INTERFACE FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+
+/*
+* \fn int hi_cfg_command()
+* \brief Configure HI with settings stored in the demod structure.
+* \param demod Demodulator.
+* \return int.
+*
+* This routine was created because to much orthogonal settings have
+* been put into one HI API function (configure). Especially the I2C bridge
+* enable/disable should not need re-configuration of the HI.
+*
+*/
+static int hi_cfg_command(const struct drx_demod_instance *demod)
+{
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ struct drxj_hi_cmd hi_cmd;
+ u16 result = 0;
+ int rc;
+
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ hi_cmd.cmd = SIO_HI_RA_RAM_CMD_CONFIG;
+ hi_cmd.param1 = SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY;
+ hi_cmd.param2 = ext_attr->hi_cfg_timing_div;
+ hi_cmd.param3 = ext_attr->hi_cfg_bridge_delay;
+ hi_cmd.param4 = ext_attr->hi_cfg_wake_up_key;
+ hi_cmd.param5 = ext_attr->hi_cfg_ctrl;
+ hi_cmd.param6 = ext_attr->hi_cfg_transmit;
+
+ rc = hi_command(demod->my_i2c_dev_addr, &hi_cmd, &result);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Reset power down flag (set one call only) */
+ ext_attr->hi_cfg_ctrl &= (~(SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ));
+
+ return 0;
+
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int hi_command()
+* \brief Configure HI with settings stored in the demod structure.
+* \param dev_addr I2C address.
+* \param cmd HI command.
+* \param result HI command result.
+* \return int.
+*
+* Sends command to HI
+*
+*/
+static int
+hi_command(struct i2c_device_addr *dev_addr, const struct drxj_hi_cmd *cmd, u16 *result)
+{
+ u16 wait_cmd = 0;
+ u16 nr_retries = 0;
+ bool powerdown_cmd = false;
+ int rc;
+
+ /* Write parameters */
+ switch (cmd->cmd) {
+
+ case SIO_HI_RA_RAM_CMD_CONFIG:
+ case SIO_HI_RA_RAM_CMD_ATOMIC_COPY:
+ rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_6__A, cmd->param6, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_5__A, cmd->param5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_4__A, cmd->param4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_3__A, cmd->param3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ fallthrough;
+ case SIO_HI_RA_RAM_CMD_BRDCTRL:
+ rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_2__A, cmd->param2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_1__A, cmd->param1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ fallthrough;
+ case SIO_HI_RA_RAM_CMD_NULL:
+ /* No parameters */
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Write command */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_CMD__A, cmd->cmd, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if ((cmd->cmd) == SIO_HI_RA_RAM_CMD_RESET)
+ msleep(1);
+
+ /* Detect power down to omit reading result */
+ powerdown_cmd = (bool) ((cmd->cmd == SIO_HI_RA_RAM_CMD_CONFIG) &&
+ (((cmd->
+ param5) & SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M)
+ == SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ));
+ if (!powerdown_cmd) {
+ /* Wait until command rdy */
+ do {
+ nr_retries++;
+ if (nr_retries > DRXJ_MAX_RETRIES) {
+ rc = -ETIMEDOUT;
+ pr_err("timeout\n");
+ goto rw_error;
+ }
+
+ rc = drxj_dap_read_reg16(dev_addr, SIO_HI_RA_RAM_CMD__A, &wait_cmd, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } while (wait_cmd != 0);
+
+ /* Read result */
+ rc = drxj_dap_read_reg16(dev_addr, SIO_HI_RA_RAM_RES__A, result, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ }
+ /* if ( powerdown_cmd == true ) */
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int init_hi( const struct drx_demod_instance *demod )
+* \brief Initialise and configurate HI.
+* \param demod pointer to demod data.
+* \return int Return status.
+* \retval 0 Success.
+* \retval -EIO Failure.
+*
+* Needs to know Psys (System Clock period) and Posc (Osc Clock period)
+* Need to store configuration in driver because of the way I2C
+* bridging is controlled.
+*
+*/
+static int init_hi(const struct drx_demod_instance *demod)
+{
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL);
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
+ int rc;
+
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+ dev_addr = demod->my_i2c_dev_addr;
+
+ /* PATCH for bug 5003, HI ucode v3.1.0 */
+ rc = drxj_dap_write_reg16(dev_addr, 0x4301D7, 0x801, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Timing div, 250ns/Psys */
+ /* Timing div, = ( delay (nano seconds) * sysclk (kHz) )/ 1000 */
+ ext_attr->hi_cfg_timing_div =
+ (u16) ((common_attr->sys_clock_freq / 1000) * HI_I2C_DELAY) / 1000;
+ /* Clipping */
+ if ((ext_attr->hi_cfg_timing_div) > SIO_HI_RA_RAM_PAR_2_CFG_DIV__M)
+ ext_attr->hi_cfg_timing_div = SIO_HI_RA_RAM_PAR_2_CFG_DIV__M;
+ /* Bridge delay, uses oscilator clock */
+ /* Delay = ( delay (nano seconds) * oscclk (kHz) )/ 1000 */
+ /* SDA brdige delay */
+ ext_attr->hi_cfg_bridge_delay =
+ (u16) ((common_attr->osc_clock_freq / 1000) * HI_I2C_BRIDGE_DELAY) /
+ 1000;
+ /* Clipping */
+ if ((ext_attr->hi_cfg_bridge_delay) > SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M)
+ ext_attr->hi_cfg_bridge_delay = SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M;
+ /* SCL bridge delay, same as SDA for now */
+ ext_attr->hi_cfg_bridge_delay += ((ext_attr->hi_cfg_bridge_delay) <<
+ SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B);
+ /* Wakeup key, setting the read flag (as suggest in the documentation) does
+ not always result into a working solution (barebones worked VI2C failed).
+ Not setting the bit works in all cases . */
+ ext_attr->hi_cfg_wake_up_key = DRXJ_WAKE_UP_KEY;
+ /* port/bridge/power down ctrl */
+ ext_attr->hi_cfg_ctrl = (SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE);
+ /* transit mode time out delay and watch dog divider */
+ ext_attr->hi_cfg_transmit = SIO_HI_RA_RAM_PAR_6__PRE;
+
+ rc = hi_cfg_command(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+/*== END HOST INTERFACE FUNCTIONS ==*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== AUXILIARY FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+
+/*
+* \fn int get_device_capabilities()
+* \brief Get and store device capabilities.
+* \param demod Pointer to demodulator instance.
+* \return int.
+* \return 0 Success
+* \retval -EIO Failure
+*
+* Depending on pulldowns on MDx pins the following internals are set:
+* * common_attr->osc_clock_freq
+* * ext_attr->has_lna
+* * ext_attr->has_ntsc
+* * ext_attr->has_btsc
+* * ext_attr->has_oob
+*
+*/
+static int get_device_capabilities(struct drx_demod_instance *demod)
+{
+ struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL);
+ struct drxj_data *ext_attr = (struct drxj_data *) NULL;
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
+ u16 sio_pdr_ohw_cfg = 0;
+ u32 sio_top_jtagid_lo = 0;
+ u16 bid = 0;
+ int rc;
+
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ dev_addr = demod->my_i2c_dev_addr;
+
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_read_reg16(dev_addr, SIO_PDR_OHW_CFG__A, &sio_pdr_ohw_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ switch ((sio_pdr_ohw_cfg & SIO_PDR_OHW_CFG_FREF_SEL__M)) {
+ case 0:
+ /* ignore (bypass ?) */
+ break;
+ case 1:
+ /* 27 MHz */
+ common_attr->osc_clock_freq = 27000;
+ break;
+ case 2:
+ /* 20.25 MHz */
+ common_attr->osc_clock_freq = 20250;
+ break;
+ case 3:
+ /* 4 MHz */
+ common_attr->osc_clock_freq = 4000;
+ break;
+ default:
+ return -EIO;
+ }
+
+ /*
+ Determine device capabilities
+ Based on pinning v47
+ */
+ rc = drxdap_fasi_read_reg32(dev_addr, SIO_TOP_JTAGID_LO__A, &sio_top_jtagid_lo, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->mfx = (u8) ((sio_top_jtagid_lo >> 29) & 0xF);
+
+ switch ((sio_top_jtagid_lo >> 12) & 0xFF) {
+ case 0x31:
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_read_reg16(dev_addr, SIO_PDR_UIO_IN_HI__A, &bid, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ bid = (bid >> 10) & 0xf;
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ ext_attr->has_lna = true;
+ ext_attr->has_ntsc = false;
+ ext_attr->has_btsc = false;
+ ext_attr->has_oob = false;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = false;
+ ext_attr->has_gpio = false;
+ ext_attr->has_irqn = false;
+ break;
+ case 0x33:
+ ext_attr->has_lna = false;
+ ext_attr->has_ntsc = false;
+ ext_attr->has_btsc = false;
+ ext_attr->has_oob = false;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = false;
+ ext_attr->has_gpio = false;
+ ext_attr->has_irqn = false;
+ break;
+ case 0x45:
+ ext_attr->has_lna = true;
+ ext_attr->has_ntsc = true;
+ ext_attr->has_btsc = false;
+ ext_attr->has_oob = false;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = false;
+ break;
+ case 0x46:
+ ext_attr->has_lna = false;
+ ext_attr->has_ntsc = true;
+ ext_attr->has_btsc = false;
+ ext_attr->has_oob = false;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = false;
+ break;
+ case 0x41:
+ ext_attr->has_lna = true;
+ ext_attr->has_ntsc = true;
+ ext_attr->has_btsc = true;
+ ext_attr->has_oob = false;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = false;
+ break;
+ case 0x43:
+ ext_attr->has_lna = false;
+ ext_attr->has_ntsc = true;
+ ext_attr->has_btsc = true;
+ ext_attr->has_oob = false;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = false;
+ break;
+ case 0x32:
+ ext_attr->has_lna = true;
+ ext_attr->has_ntsc = false;
+ ext_attr->has_btsc = false;
+ ext_attr->has_oob = true;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = true;
+ break;
+ case 0x34:
+ ext_attr->has_lna = false;
+ ext_attr->has_ntsc = true;
+ ext_attr->has_btsc = true;
+ ext_attr->has_oob = true;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = true;
+ break;
+ case 0x42:
+ ext_attr->has_lna = true;
+ ext_attr->has_ntsc = true;
+ ext_attr->has_btsc = true;
+ ext_attr->has_oob = true;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = true;
+ break;
+ case 0x44:
+ ext_attr->has_lna = false;
+ ext_attr->has_ntsc = true;
+ ext_attr->has_btsc = true;
+ ext_attr->has_oob = true;
+ ext_attr->has_smatx = true;
+ ext_attr->has_smarx = true;
+ ext_attr->has_gpio = true;
+ ext_attr->has_irqn = true;
+ break;
+ default:
+ /* Unknown device variant */
+ return -EIO;
+ break;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int power_up_device()
+* \brief Power up device.
+* \param demod Pointer to demodulator instance.
+* \return int.
+* \return 0 Success
+* \retval -EIO Failure, I2C or max retries reached
+*
+*/
+
+#ifndef DRXJ_MAX_RETRIES_POWERUP
+#define DRXJ_MAX_RETRIES_POWERUP 10
+#endif
+
+static int power_up_device(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
+ u8 data = 0;
+ u16 retry_count = 0;
+ struct i2c_device_addr wake_up_addr;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ wake_up_addr.i2c_addr = DRXJ_WAKE_UP_KEY;
+ wake_up_addr.i2c_dev_id = dev_addr->i2c_dev_id;
+ wake_up_addr.user_data = dev_addr->user_data;
+ /*
+ * I2C access may fail in this case: no ack
+ * dummy write must be used to wake uop device, dummy read must be used to
+ * reset HI state machine (avoiding actual writes)
+ */
+ do {
+ data = 0;
+ drxbsp_i2c_write_read(&wake_up_addr, 1, &data,
+ (struct i2c_device_addr *)(NULL), 0,
+ (u8 *)(NULL));
+ msleep(10);
+ retry_count++;
+ } while ((drxbsp_i2c_write_read
+ ((struct i2c_device_addr *) (NULL), 0, (u8 *)(NULL), dev_addr, 1,
+ &data)
+ != 0) && (retry_count < DRXJ_MAX_RETRIES_POWERUP));
+
+ /* Need some recovery time .... */
+ msleep(10);
+
+ if (retry_count == DRXJ_MAX_RETRIES_POWERUP)
+ return -EIO;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/* MPEG Output Configuration Functions - begin */
+/*----------------------------------------------------------------------------*/
+/*
+* \fn int ctrl_set_cfg_mpeg_output()
+* \brief Set MPEG output configuration of the device.
+* \param devmod Pointer to demodulator instance.
+* \param cfg_data Pointer to mpeg output configuaration.
+* \return int.
+*
+* Configure MPEG output parameters.
+*
+*/
+static int
+ctrl_set_cfg_mpeg_output(struct drx_demod_instance *demod, struct drx_cfg_mpeg_output *cfg_data)
+{
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL);
+ int rc;
+ u16 fec_oc_reg_mode = 0;
+ u16 fec_oc_reg_ipr_mode = 0;
+ u16 fec_oc_reg_ipr_invert = 0;
+ u32 max_bit_rate = 0;
+ u32 rcn_rate = 0;
+ u32 nr_bits = 0;
+ u16 sio_pdr_md_cfg = 0;
+ /* data mask for the output data byte */
+ u16 invert_data_mask =
+ FEC_OC_IPR_INVERT_MD7__M | FEC_OC_IPR_INVERT_MD6__M |
+ FEC_OC_IPR_INVERT_MD5__M | FEC_OC_IPR_INVERT_MD4__M |
+ FEC_OC_IPR_INVERT_MD3__M | FEC_OC_IPR_INVERT_MD2__M |
+ FEC_OC_IPR_INVERT_MD1__M | FEC_OC_IPR_INVERT_MD0__M;
+
+ /* check arguments */
+ if ((demod == NULL) || (cfg_data == NULL))
+ return -EINVAL;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+
+ if (cfg_data->enable_mpeg_output == true) {
+ /* quick and dirty patch to set MPEG in case current std is not
+ producing MPEG */
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_8VSB:
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ break;
+ default:
+ return 0;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_OCR_INVERT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_8VSB:
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_USAGE__A, 7, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* 2048 bytes fifo ram */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_TMD_CTL_UPD_RATE__A, 10, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_TMD_INT_UPD_RATE__A, 10, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_AVR_PARM_A__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_AVR_PARM_B__A, 7, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_RCN_GAIN__A, 10, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Low Water Mark for synchronization */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_LWM__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* High Water Mark for synchronization */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_HWM__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_C:
+ switch (ext_attr->constellation) {
+ case DRX_CONSTELLATION_QAM256:
+ nr_bits = 8;
+ break;
+ case DRX_CONSTELLATION_QAM128:
+ nr_bits = 7;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ nr_bits = 6;
+ break;
+ case DRX_CONSTELLATION_QAM32:
+ nr_bits = 5;
+ break;
+ case DRX_CONSTELLATION_QAM16:
+ nr_bits = 4;
+ break;
+ default:
+ return -EIO;
+ } /* ext_attr->constellation */
+ /* max_bit_rate = symbol_rate * nr_bits * coef */
+ /* coef = 188/204 */
+ max_bit_rate =
+ (ext_attr->curr_symbol_rate / 8) * nr_bits * 188;
+ fallthrough; /* as b/c Annex A/C need following settings */
+ case DRX_STANDARD_ITU_B:
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_USAGE__A, FEC_OC_FCT_USAGE__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_TMD_CTL_UPD_RATE__A, FEC_OC_TMD_CTL_UPD_RATE__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_TMD_INT_UPD_RATE__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_AVR_PARM_A__A, FEC_OC_AVR_PARM_A__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_AVR_PARM_B__A, FEC_OC_AVR_PARM_B__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (cfg_data->static_clk == true) {
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_RCN_GAIN__A, 0xD, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else {
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_RCN_GAIN__A, FEC_OC_RCN_GAIN__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_LWM__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_HWM__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ break;
+ } /* switch (standard) */
+
+ /* Check insertion of the Reed-Solomon parity bytes */
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_MODE__A, &fec_oc_reg_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_IPR_MODE__A, &fec_oc_reg_ipr_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (cfg_data->insert_rs_byte == true) {
+ /* enable parity symbol forward */
+ fec_oc_reg_mode |= FEC_OC_MODE_PARITY__M;
+ /* MVAL disable during parity bytes */
+ fec_oc_reg_ipr_mode |= FEC_OC_IPR_MODE_MVAL_DIS_PAR__M;
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_8VSB:
+ rcn_rate = 0x004854D3;
+ break;
+ case DRX_STANDARD_ITU_B:
+ fec_oc_reg_mode |= FEC_OC_MODE_TRANSPARENT__M;
+ switch (ext_attr->constellation) {
+ case DRX_CONSTELLATION_QAM256:
+ rcn_rate = 0x008945E7;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ rcn_rate = 0x005F64D4;
+ break;
+ default:
+ return -EIO;
+ }
+ break;
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_C:
+ /* insert_rs_byte = true -> coef = 188/188 -> 1, RS bits are in MPEG output */
+ rcn_rate =
+ (frac28
+ (max_bit_rate,
+ (u32) (common_attr->sys_clock_freq / 8))) /
+ 188;
+ break;
+ default:
+ return -EIO;
+ } /* ext_attr->standard */
+ } else { /* insert_rs_byte == false */
+
+ /* disable parity symbol forward */
+ fec_oc_reg_mode &= (~FEC_OC_MODE_PARITY__M);
+ /* MVAL enable during parity bytes */
+ fec_oc_reg_ipr_mode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M);
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_8VSB:
+ rcn_rate = 0x0041605C;
+ break;
+ case DRX_STANDARD_ITU_B:
+ fec_oc_reg_mode &= (~FEC_OC_MODE_TRANSPARENT__M);
+ switch (ext_attr->constellation) {
+ case DRX_CONSTELLATION_QAM256:
+ rcn_rate = 0x0082D6A0;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ rcn_rate = 0x005AEC1A;
+ break;
+ default:
+ return -EIO;
+ }
+ break;
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_C:
+ /* insert_rs_byte = false -> coef = 188/204, RS bits not in MPEG output */
+ rcn_rate =
+ (frac28
+ (max_bit_rate,
+ (u32) (common_attr->sys_clock_freq / 8))) /
+ 204;
+ break;
+ default:
+ return -EIO;
+ } /* ext_attr->standard */
+ }
+
+ if (cfg_data->enable_parallel == true) { /* MPEG data output is parallel -> clear ipr_mode[0] */
+ fec_oc_reg_ipr_mode &= (~(FEC_OC_IPR_MODE_SERIAL__M));
+ } else { /* MPEG data output is serial -> set ipr_mode[0] */
+ fec_oc_reg_ipr_mode |= FEC_OC_IPR_MODE_SERIAL__M;
+ }
+
+ /* Control slective inversion of output bits */
+ if (cfg_data->invert_data == true)
+ fec_oc_reg_ipr_invert |= invert_data_mask;
+ else
+ fec_oc_reg_ipr_invert &= (~(invert_data_mask));
+
+ if (cfg_data->invert_err == true)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MERR__M;
+ else
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MERR__M));
+
+ if (cfg_data->invert_str == true)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MSTRT__M;
+ else
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MSTRT__M));
+
+ if (cfg_data->invert_val == true)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MVAL__M;
+ else
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MVAL__M));
+
+ if (cfg_data->invert_clk == true)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MCLK__M;
+ else
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MCLK__M));
+
+
+ if (cfg_data->static_clk == true) { /* Static mode */
+ u32 dto_rate = 0;
+ u32 bit_rate = 0;
+ u16 fec_oc_dto_burst_len = 0;
+ u16 fec_oc_dto_period = 0;
+
+ fec_oc_dto_burst_len = FEC_OC_DTO_BURST_LEN__PRE;
+
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_8VSB:
+ fec_oc_dto_period = 4;
+ if (cfg_data->insert_rs_byte == true)
+ fec_oc_dto_burst_len = 208;
+ break;
+ case DRX_STANDARD_ITU_A:
+ {
+ u32 symbol_rate_th = 6400000;
+ if (cfg_data->insert_rs_byte == true) {
+ fec_oc_dto_burst_len = 204;
+ symbol_rate_th = 5900000;
+ }
+ if (ext_attr->curr_symbol_rate >=
+ symbol_rate_th) {
+ fec_oc_dto_period = 0;
+ } else {
+ fec_oc_dto_period = 1;
+ }
+ }
+ break;
+ case DRX_STANDARD_ITU_B:
+ fec_oc_dto_period = 1;
+ if (cfg_data->insert_rs_byte == true)
+ fec_oc_dto_burst_len = 128;
+ break;
+ case DRX_STANDARD_ITU_C:
+ fec_oc_dto_period = 1;
+ if (cfg_data->insert_rs_byte == true)
+ fec_oc_dto_burst_len = 204;
+ break;
+ default:
+ return -EIO;
+ }
+ bit_rate =
+ common_attr->sys_clock_freq * 1000 / (fec_oc_dto_period +
+ 2);
+ dto_rate =
+ frac28(bit_rate, common_attr->sys_clock_freq * 1000);
+ dto_rate >>= 3;
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_RATE_HI__A, (u16)((dto_rate >> 16) & FEC_OC_DTO_RATE_HI__M), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_RATE_LO__A, (u16)(dto_rate & FEC_OC_DTO_RATE_LO_RATE_LO__M), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_MODE__A, FEC_OC_DTO_MODE_DYNAMIC__M | FEC_OC_DTO_MODE_OFFSET_ENABLE__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_MODE__A, FEC_OC_FCT_MODE_RAT_ENA__M | FEC_OC_FCT_MODE_VIRT_ENA__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_BURST_LEN__A, fec_oc_dto_burst_len, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (ext_attr->mpeg_output_clock_rate != DRXJ_MPEGOUTPUT_CLOCK_RATE_AUTO)
+ fec_oc_dto_period = ext_attr->mpeg_output_clock_rate - 1;
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_PERIOD__A, fec_oc_dto_period, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else { /* Dynamic mode */
+
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_MODE__A, FEC_OC_DTO_MODE_DYNAMIC__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_MODE__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ rc = drxdap_fasi_write_reg32(dev_addr, FEC_OC_RCN_CTL_RATE_LO__A, rcn_rate, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Write appropriate registers with requested configuration */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_MODE__A, fec_oc_reg_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_IPR_MODE__A, fec_oc_reg_ipr_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_IPR_INVERT__A, fec_oc_reg_ipr_invert, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* enabling for both parallel and serial now */
+ /* Write magic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0xFABA, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Set MPEG TS pads to outputmode */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MSTRT_CFG__A, 0x0013, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MERR_CFG__A, 0x0013, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MCLK_CFG__A, MPEG_OUTPUT_CLK_DRIVE_STRENGTH << SIO_PDR_MCLK_CFG_DRIVE__B | 0x03 << SIO_PDR_MCLK_CFG_MODE__B, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MVAL_CFG__A, 0x0013, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ sio_pdr_md_cfg =
+ MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH <<
+ SIO_PDR_MD0_CFG_DRIVE__B | 0x03 << SIO_PDR_MD0_CFG_MODE__B;
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD0_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (cfg_data->enable_parallel == true) { /* MPEG data output is parallel -> set MD1 to MD7 to output mode */
+ sio_pdr_md_cfg =
+ MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH <<
+ SIO_PDR_MD0_CFG_DRIVE__B | 0x03 <<
+ SIO_PDR_MD0_CFG_MODE__B;
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD0_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD1_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD2_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD3_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD4_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD5_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD6_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD7_CFG__A, sio_pdr_md_cfg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else { /* MPEG data output is serial -> set MD1 to MD7 to tri-state */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD1_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD2_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD3_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD4_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD5_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD6_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD7_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ /* Enable Monitor Bus output over MPEG pads and ctl input */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MON_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Write nomagic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else {
+ /* Write magic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0xFABA, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Set MPEG TS pads to inputmode */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MSTRT_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MERR_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MCLK_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MVAL_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD0_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD1_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD2_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD3_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD4_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD5_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD6_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD7_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Enable Monitor Bus output over MPEG pads and ctl input */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MON_CFG__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Write nomagic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* save values for restore after re-acquire */
+ common_attr->mpeg_cfg.enable_mpeg_output = cfg_data->enable_mpeg_output;
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*----------------------------------------------------------------------------*/
+
+
+/*----------------------------------------------------------------------------*/
+/* MPEG Output Configuration Functions - end */
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/* miscellaneous configurations - begin */
+/*----------------------------------------------------------------------------*/
+
+/*
+* \fn int set_mpegtei_handling()
+* \brief Activate MPEG TEI handling settings.
+* \param devmod Pointer to demodulator instance.
+* \return int.
+*
+* This routine should be called during a set channel of QAM/VSB
+*
+*/
+static int set_mpegtei_handling(struct drx_demod_instance *demod)
+{
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
+ int rc;
+ u16 fec_oc_dpr_mode = 0;
+ u16 fec_oc_snc_mode = 0;
+ u16 fec_oc_ems_mode = 0;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_DPR_MODE__A, &fec_oc_dpr_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_SNC_MODE__A, &fec_oc_snc_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_EMS_MODE__A, &fec_oc_ems_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* reset to default, allow TEI bit to be changed */
+ fec_oc_dpr_mode &= (~FEC_OC_DPR_MODE_ERR_DISABLE__M);
+ fec_oc_snc_mode &= (~(FEC_OC_SNC_MODE_ERROR_CTL__M |
+ FEC_OC_SNC_MODE_CORR_DISABLE__M));
+ fec_oc_ems_mode &= (~FEC_OC_EMS_MODE_MODE__M);
+
+ if (ext_attr->disable_te_ihandling) {
+ /* do not change TEI bit */
+ fec_oc_dpr_mode |= FEC_OC_DPR_MODE_ERR_DISABLE__M;
+ fec_oc_snc_mode |= FEC_OC_SNC_MODE_CORR_DISABLE__M |
+ ((0x2) << (FEC_OC_SNC_MODE_ERROR_CTL__B));
+ fec_oc_ems_mode |= ((0x01) << (FEC_OC_EMS_MODE_MODE__B));
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DPR_MODE__A, fec_oc_dpr_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_MODE__A, fec_oc_snc_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_EMS_MODE__A, fec_oc_ems_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*----------------------------------------------------------------------------*/
+/*
+* \fn int bit_reverse_mpeg_output()
+* \brief Set MPEG output bit-endian settings.
+* \param devmod Pointer to demodulator instance.
+* \return int.
+*
+* This routine should be called during a set channel of QAM/VSB
+*
+*/
+static int bit_reverse_mpeg_output(struct drx_demod_instance *demod)
+{
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
+ int rc;
+ u16 fec_oc_ipr_mode = 0;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_IPR_MODE__A, &fec_oc_ipr_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* reset to default (normal bit order) */
+ fec_oc_ipr_mode &= (~FEC_OC_IPR_MODE_REVERSE_ORDER__M);
+
+ if (ext_attr->bit_reverse_mpeg_outout)
+ fec_oc_ipr_mode |= FEC_OC_IPR_MODE_REVERSE_ORDER__M;
+
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_IPR_MODE__A, fec_oc_ipr_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*----------------------------------------------------------------------------*/
+/*
+* \fn int set_mpeg_start_width()
+* \brief Set MPEG start width.
+* \param devmod Pointer to demodulator instance.
+* \return int.
+*
+* This routine should be called during a set channel of QAM/VSB
+*
+*/
+static int set_mpeg_start_width(struct drx_demod_instance *demod)
+{
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
+ struct drx_common_attr *common_attr = (struct drx_common_attr *) NULL;
+ int rc;
+ u16 fec_oc_comm_mb = 0;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ common_attr = demod->my_common_attr;
+
+ if ((common_attr->mpeg_cfg.static_clk == true)
+ && (common_attr->mpeg_cfg.enable_parallel == false)) {
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_COMM_MB__A, &fec_oc_comm_mb, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ fec_oc_comm_mb &= ~FEC_OC_COMM_MB_CTL_ON;
+ if (ext_attr->mpeg_start_width == DRXJ_MPEG_START_WIDTH_8CLKCYC)
+ fec_oc_comm_mb |= FEC_OC_COMM_MB_CTL_ON;
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_COMM_MB__A, fec_oc_comm_mb, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*----------------------------------------------------------------------------*/
+/* miscellaneous configurations - end */
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/* UIO Configuration Functions - begin */
+/*----------------------------------------------------------------------------*/
+/*
+* \fn int ctrl_set_uio_cfg()
+* \brief Configure modus oprandi UIO.
+* \param demod Pointer to demodulator instance.
+* \param uio_cfg Pointer to a configuration setting for a certain UIO.
+* \return int.
+*/
+static int ctrl_set_uio_cfg(struct drx_demod_instance *demod, struct drxuio_cfg *uio_cfg)
+{
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ int rc;
+
+ if ((uio_cfg == NULL) || (demod == NULL))
+ return -EINVAL;
+
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ /* Write magic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ switch (uio_cfg->uio) {
+ /*====================================================================*/
+ case DRX_UIO1:
+ /* DRX_UIO1: SMA_TX UIO-1 */
+ if (!ext_attr->has_smatx)
+ return -EIO;
+ switch (uio_cfg->mode) {
+ case DRX_UIO_MODE_FIRMWARE_SMA:
+ case DRX_UIO_MODE_FIRMWARE_SAW:
+ case DRX_UIO_MODE_READWRITE:
+ ext_attr->uio_sma_tx_mode = uio_cfg->mode;
+ break;
+ case DRX_UIO_MODE_DISABLE:
+ ext_attr->uio_sma_tx_mode = uio_cfg->mode;
+ /* pad configuration register is set 0 - input mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_TX_CFG__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EINVAL;
+ } /* switch ( uio_cfg->mode ) */
+ break;
+ /*====================================================================*/
+ case DRX_UIO2:
+ /* DRX_UIO2: SMA_RX UIO-2 */
+ if (!ext_attr->has_smarx)
+ return -EIO;
+ switch (uio_cfg->mode) {
+ case DRX_UIO_MODE_FIRMWARE0:
+ case DRX_UIO_MODE_READWRITE:
+ ext_attr->uio_sma_rx_mode = uio_cfg->mode;
+ break;
+ case DRX_UIO_MODE_DISABLE:
+ ext_attr->uio_sma_rx_mode = uio_cfg->mode;
+ /* pad configuration register is set 0 - input mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_RX_CFG__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EINVAL;
+ } /* switch ( uio_cfg->mode ) */
+ break;
+ /*====================================================================*/
+ case DRX_UIO3:
+ /* DRX_UIO3: GPIO UIO-3 */
+ if (!ext_attr->has_gpio)
+ return -EIO;
+ switch (uio_cfg->mode) {
+ case DRX_UIO_MODE_FIRMWARE0:
+ case DRX_UIO_MODE_READWRITE:
+ ext_attr->uio_gpio_mode = uio_cfg->mode;
+ break;
+ case DRX_UIO_MODE_DISABLE:
+ ext_attr->uio_gpio_mode = uio_cfg->mode;
+ /* pad configuration register is set 0 - input mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_GPIO_CFG__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EINVAL;
+ } /* switch ( uio_cfg->mode ) */
+ break;
+ /*====================================================================*/
+ case DRX_UIO4:
+ /* DRX_UIO4: IRQN UIO-4 */
+ if (!ext_attr->has_irqn)
+ return -EIO;
+ switch (uio_cfg->mode) {
+ case DRX_UIO_MODE_READWRITE:
+ ext_attr->uio_irqn_mode = uio_cfg->mode;
+ break;
+ case DRX_UIO_MODE_DISABLE:
+ /* pad configuration register is set 0 - input mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_IRQN_CFG__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->uio_irqn_mode = uio_cfg->mode;
+ break;
+ case DRX_UIO_MODE_FIRMWARE0:
+ default:
+ return -EINVAL;
+ } /* switch ( uio_cfg->mode ) */
+ break;
+ /*====================================================================*/
+ default:
+ return -EINVAL;
+ } /* switch ( uio_cfg->uio ) */
+
+ /* Write magic word to disable pdr reg write */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int ctrl_uio_write()
+* \brief Write to a UIO.
+* \param demod Pointer to demodulator instance.
+* \param uio_data Pointer to data container for a certain UIO.
+* \return int.
+*/
+static int
+ctrl_uio_write(struct drx_demod_instance *demod, struct drxuio_data *uio_data)
+{
+ struct drxj_data *ext_attr = (struct drxj_data *) (NULL);
+ int rc;
+ u16 pin_cfg_value = 0;
+ u16 value = 0;
+
+ if ((uio_data == NULL) || (demod == NULL))
+ return -EINVAL;
+
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ /* Write magic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ switch (uio_data->uio) {
+ /*====================================================================*/
+ case DRX_UIO1:
+ /* DRX_UIO1: SMA_TX UIO-1 */
+ if (!ext_attr->has_smatx)
+ return -EIO;
+ if ((ext_attr->uio_sma_tx_mode != DRX_UIO_MODE_READWRITE)
+ && (ext_attr->uio_sma_tx_mode != DRX_UIO_MODE_FIRMWARE_SAW)) {
+ return -EIO;
+ }
+ pin_cfg_value = 0;
+ /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */
+ pin_cfg_value |= 0x0113;
+ /* io_pad_cfg_mode output mode is drive always */
+ /* io_pad_cfg_drive is set to power 2 (23 mA) */
+
+ /* write to io pad configuration register - output mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_TX_CFG__A, pin_cfg_value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* use corresponding bit in io data output registar */
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, &value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (!uio_data->value)
+ value &= 0x7FFF; /* write zero to 15th bit - 1st UIO */
+ else
+ value |= 0x8000; /* write one to 15th bit - 1st UIO */
+
+ /* write back to io data output register */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ /*======================================================================*/
+ case DRX_UIO2:
+ /* DRX_UIO2: SMA_RX UIO-2 */
+ if (!ext_attr->has_smarx)
+ return -EIO;
+ if (ext_attr->uio_sma_rx_mode != DRX_UIO_MODE_READWRITE)
+ return -EIO;
+
+ pin_cfg_value = 0;
+ /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */
+ pin_cfg_value |= 0x0113;
+ /* io_pad_cfg_mode output mode is drive always */
+ /* io_pad_cfg_drive is set to power 2 (23 mA) */
+
+ /* write to io pad configuration register - output mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_RX_CFG__A, pin_cfg_value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* use corresponding bit in io data output registar */
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, &value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (!uio_data->value)
+ value &= 0xBFFF; /* write zero to 14th bit - 2nd UIO */
+ else
+ value |= 0x4000; /* write one to 14th bit - 2nd UIO */
+
+ /* write back to io data output register */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ /*====================================================================*/
+ case DRX_UIO3:
+ /* DRX_UIO3: ASEL UIO-3 */
+ if (!ext_attr->has_gpio)
+ return -EIO;
+ if (ext_attr->uio_gpio_mode != DRX_UIO_MODE_READWRITE)
+ return -EIO;
+
+ pin_cfg_value = 0;
+ /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */
+ pin_cfg_value |= 0x0113;
+ /* io_pad_cfg_mode output mode is drive always */
+ /* io_pad_cfg_drive is set to power 2 (23 mA) */
+
+ /* write to io pad configuration register - output mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_GPIO_CFG__A, pin_cfg_value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* use corresponding bit in io data output registar */
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_HI__A, &value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (!uio_data->value)
+ value &= 0xFFFB; /* write zero to 2nd bit - 3rd UIO */
+ else
+ value |= 0x0004; /* write one to 2nd bit - 3rd UIO */
+
+ /* write back to io data output register */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_HI__A, value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ /*=====================================================================*/
+ case DRX_UIO4:
+ /* DRX_UIO4: IRQN UIO-4 */
+ if (!ext_attr->has_irqn)
+ return -EIO;
+
+ if (ext_attr->uio_irqn_mode != DRX_UIO_MODE_READWRITE)
+ return -EIO;
+
+ pin_cfg_value = 0;
+ /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */
+ pin_cfg_value |= 0x0113;
+ /* io_pad_cfg_mode output mode is drive always */
+ /* io_pad_cfg_drive is set to power 2 (23 mA) */
+
+ /* write to io pad configuration register - output mode */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_IRQN_CFG__A, pin_cfg_value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* use corresponding bit in io data output registar */
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, &value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (uio_data->value == false)
+ value &= 0xEFFF; /* write zero to 12th bit - 4th UIO */
+ else
+ value |= 0x1000; /* write one to 12th bit - 4th UIO */
+
+ /* write back to io data output register */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_UIO_OUT_LO__A, value, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ /*=====================================================================*/
+ default:
+ return -EINVAL;
+ } /* switch ( uio_data->uio ) */
+
+ /* Write magic word to disable pdr reg write */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*---------------------------------------------------------------------------*/
+/* UIO Configuration Functions - end */
+/*---------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/* I2C Bridge Functions - begin */
+/*----------------------------------------------------------------------------*/
+/*
+* \fn int ctrl_i2c_bridge()
+* \brief Open or close the I2C switch to tuner.
+* \param demod Pointer to demodulator instance.
+* \param bridge_closed Pointer to bool indication if bridge is closed not.
+* \return int.
+
+*/
+static int
+ctrl_i2c_bridge(struct drx_demod_instance *demod, bool *bridge_closed)
+{
+ struct drxj_hi_cmd hi_cmd;
+ u16 result = 0;
+
+ /* check arguments */
+ if (bridge_closed == NULL)
+ return -EINVAL;
+
+ hi_cmd.cmd = SIO_HI_RA_RAM_CMD_BRDCTRL;
+ hi_cmd.param1 = SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY;
+ if (*bridge_closed)
+ hi_cmd.param2 = SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED;
+ else
+ hi_cmd.param2 = SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN;
+
+ return hi_command(demod->my_i2c_dev_addr, &hi_cmd, &result);
+}
+
+/*----------------------------------------------------------------------------*/
+/* I2C Bridge Functions - end */
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/* Smart antenna Functions - begin */
+/*----------------------------------------------------------------------------*/
+/*
+* \fn int smart_ant_init()
+* \brief Initialize Smart Antenna.
+* \param pointer to struct drx_demod_instance.
+* \return int.
+*
+*/
+static int smart_ant_init(struct drx_demod_instance *demod)
+{
+ struct drxj_data *ext_attr = NULL;
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxuio_cfg uio_cfg = { DRX_UIO1, DRX_UIO_MODE_FIRMWARE_SMA };
+ int rc;
+ u16 data = 0;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ /* Write magic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* init smart antenna */
+ rc = drxj_dap_read_reg16(dev_addr, SIO_SA_TX_COMMAND__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (ext_attr->smart_ant_inverted) {
+ rc = drxj_dap_write_reg16(dev_addr, SIO_SA_TX_COMMAND__A, (data | SIO_SA_TX_COMMAND_TX_INVERT__M) | SIO_SA_TX_COMMAND_TX_ENABLE__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else {
+ rc = drxj_dap_write_reg16(dev_addr, SIO_SA_TX_COMMAND__A, (data & (~SIO_SA_TX_COMMAND_TX_INVERT__M)) | SIO_SA_TX_COMMAND_TX_ENABLE__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* config SMA_TX pin to smart antenna mode */
+ rc = ctrl_set_uio_cfg(demod, &uio_cfg);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_TX_CFG__A, 0x13, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_PDR_SMA_TX_GPIO_FNC__A, 0x03, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Write magic word to disable pdr reg write */
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+static int scu_command(struct i2c_device_addr *dev_addr, struct drxjscu_cmd *cmd)
+{
+ int rc;
+ u16 cur_cmd = 0;
+ unsigned long timeout;
+
+ /* Check param */
+ if (cmd == NULL)
+ return -EINVAL;
+
+ /* Wait until SCU command interface is ready to receive command */
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_COMMAND__A, &cur_cmd, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (cur_cmd != DRX_SCU_READY)
+ return -EIO;
+
+ switch (cmd->parameter_len) {
+ case 5:
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_4__A, *(cmd->parameter + 4), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ fallthrough;
+ case 4:
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_3__A, *(cmd->parameter + 3), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ fallthrough;
+ case 3:
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_2__A, *(cmd->parameter + 2), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ fallthrough;
+ case 2:
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_1__A, *(cmd->parameter + 1), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ fallthrough;
+ case 1:
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_PARAM_0__A, *(cmd->parameter + 0), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ fallthrough;
+ case 0:
+ /* do nothing */
+ break;
+ default:
+ /* this number of parameters is not supported */
+ return -EIO;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_COMMAND__A, cmd->command, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Wait until SCU has processed command */
+ timeout = jiffies + msecs_to_jiffies(DRXJ_MAX_WAITTIME);
+ while (time_is_after_jiffies(timeout)) {
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_COMMAND__A, &cur_cmd, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (cur_cmd == DRX_SCU_READY)
+ break;
+ usleep_range(1000, 2000);
+ }
+
+ if (cur_cmd != DRX_SCU_READY)
+ return -EIO;
+
+ /* read results */
+ if ((cmd->result_len > 0) && (cmd->result != NULL)) {
+ s16 err;
+
+ switch (cmd->result_len) {
+ case 4:
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_PARAM_3__A, cmd->result + 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ fallthrough;
+ case 3:
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_PARAM_2__A, cmd->result + 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ fallthrough;
+ case 2:
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_PARAM_1__A, cmd->result + 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ fallthrough;
+ case 1:
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_PARAM_0__A, cmd->result + 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ fallthrough;
+ case 0:
+ /* do nothing */
+ break;
+ default:
+ /* this number of parameters is not supported */
+ return -EIO;
+ }
+
+ /* Check if an error was reported by SCU */
+ err = cmd->result[0];
+
+ /* check a few fixed error codes */
+ if ((err == (s16) SCU_RAM_PARAM_0_RESULT_UNKSTD)
+ || (err == (s16) SCU_RAM_PARAM_0_RESULT_UNKCMD)
+ || (err == (s16) SCU_RAM_PARAM_0_RESULT_INVPAR)
+ || (err == (s16) SCU_RAM_PARAM_0_RESULT_SIZE)
+ ) {
+ return -EINVAL;
+ }
+ /* here it is assumed that negative means error, and positive no error */
+ else if (err < 0)
+ return -EIO;
+ else
+ return 0;
+ }
+
+ return 0;
+
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int DRXJ_DAP_SCUAtomicReadWriteBlock()
+* \brief Basic access routine for SCU atomic read or write access
+* \param dev_addr pointer to i2c dev address
+* \param addr destination/source address
+* \param datasize size of data buffer in bytes
+* \param data pointer to data buffer
+* \return int
+* \retval 0 Success
+* \retval -EIO Timeout, I2C error, illegal bank
+*
+*/
+#define ADDR_AT_SCU_SPACE(x) ((x - 0x82E000) * 2)
+static
+int drxj_dap_scu_atomic_read_write_block(struct i2c_device_addr *dev_addr, u32 addr, u16 datasize, /* max 30 bytes because the limit of SCU parameter */
+ u8 *data, bool read_flag)
+{
+ struct drxjscu_cmd scu_cmd;
+ int rc;
+ u16 set_param_parameters[18];
+ u16 cmd_result[15];
+
+ /* Parameter check */
+ if (!data || !dev_addr || (datasize % 2) || ((datasize / 2) > 16))
+ return -EINVAL;
+
+ set_param_parameters[1] = (u16) ADDR_AT_SCU_SPACE(addr);
+ if (read_flag) { /* read */
+ set_param_parameters[0] = ((~(0x0080)) & datasize);
+ scu_cmd.parameter_len = 2;
+ scu_cmd.result_len = datasize / 2 + 2;
+ } else {
+ int i = 0;
+
+ set_param_parameters[0] = 0x0080 | datasize;
+ for (i = 0; i < (datasize / 2); i++) {
+ set_param_parameters[i + 2] =
+ (data[2 * i] | (data[(2 * i) + 1] << 8));
+ }
+ scu_cmd.parameter_len = datasize / 2 + 2;
+ scu_cmd.result_len = 1;
+ }
+
+ scu_cmd.command =
+ SCU_RAM_COMMAND_STANDARD_TOP |
+ SCU_RAM_COMMAND_CMD_AUX_SCU_ATOMIC_ACCESS;
+ scu_cmd.result = cmd_result;
+ scu_cmd.parameter = set_param_parameters;
+ rc = scu_command(dev_addr, &scu_cmd);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (read_flag) {
+ int i = 0;
+ /* read data from buffer */
+ for (i = 0; i < (datasize / 2); i++) {
+ data[2 * i] = (u8) (scu_cmd.result[i + 2] & 0xFF);
+ data[(2 * i) + 1] = (u8) (scu_cmd.result[i + 2] >> 8);
+ }
+ }
+
+ return 0;
+
+rw_error:
+ return rc;
+
+}
+
+/*============================================================================*/
+
+/*
+* \fn int DRXJ_DAP_AtomicReadReg16()
+* \brief Atomic read of 16 bits words
+*/
+static
+int drxj_dap_scu_atomic_read_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 *data, u32 flags)
+{
+ u8 buf[2] = { 0 };
+ int rc;
+ u16 word = 0;
+
+ if (!data)
+ return -EINVAL;
+
+ rc = drxj_dap_scu_atomic_read_write_block(dev_addr, addr, 2, buf, true);
+ if (rc < 0)
+ return rc;
+
+ word = (u16) (buf[0] + (buf[1] << 8));
+
+ *data = word;
+
+ return rc;
+}
+
+/*============================================================================*/
+/*
+* \fn int drxj_dap_scu_atomic_write_reg16()
+* \brief Atomic read of 16 bits words
+*/
+static
+int drxj_dap_scu_atomic_write_reg16(struct i2c_device_addr *dev_addr,
+ u32 addr,
+ u16 data, u32 flags)
+{
+ u8 buf[2];
+ int rc;
+
+ buf[0] = (u8) (data & 0xff);
+ buf[1] = (u8) ((data >> 8) & 0xff);
+
+ rc = drxj_dap_scu_atomic_read_write_block(dev_addr, addr, 2, buf, false);
+
+ return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+/*
+* \brief Measure result of ADC synchronisation
+* \param demod demod instance
+* \param count (returned) count
+* \return int.
+* \retval 0 Success
+* \retval -EIO Failure: I2C error
+*
+*/
+static int adc_sync_measurement(struct drx_demod_instance *demod, u16 *count)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ int rc;
+ u16 data = 0;
+
+ dev_addr = demod->my_i2c_dev_addr;
+
+ /* Start measurement */
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_COMM_EXEC__A, IQM_AF_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_START_LOCK__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Wait at least 3*128*(1/sysclk) <<< 1 millisec */
+ msleep(1);
+
+ *count = 0;
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_PHASE0__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (data == 127)
+ *count = *count + 1;
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_PHASE1__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (data == 127)
+ *count = *count + 1;
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_PHASE2__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (data == 127)
+ *count = *count + 1;
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \brief Synchronize analog and digital clock domains
+* \param demod demod instance
+* \return int.
+* \retval 0 Success
+* \retval -EIO Failure: I2C error or failure to synchronize
+*
+* An IQM reset will also reset the results of this synchronization.
+* After an IQM reset this routine needs to be called again.
+*
+*/
+
+static int adc_synchronization(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ int rc;
+ u16 count = 0;
+
+ dev_addr = demod->my_i2c_dev_addr;
+
+ rc = adc_sync_measurement(demod, &count);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (count == 1) {
+ /* Try sampling on a different edge */
+ u16 clk_neg = 0;
+
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_CLKNEG__A, &clk_neg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ clk_neg ^= IQM_AF_CLKNEG_CLKNEGDATA__M;
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLKNEG__A, clk_neg, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = adc_sync_measurement(demod, &count);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* TODO: implement fallback scenarios */
+ if (count < 2)
+ return -EIO;
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+/*== END AUXILIARY FUNCTIONS ==*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== 8VSB & QAM COMMON DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+/*
+* \fn int init_agc ()
+* \brief Initialize AGC for all standards.
+* \param demod instance of demodulator.
+* \param channel pointer to channel data.
+* \return int.
+*/
+static int init_agc(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drx_common_attr *common_attr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ struct drxj_cfg_agc *p_agc_rf_settings = NULL;
+ struct drxj_cfg_agc *p_agc_if_settings = NULL;
+ int rc;
+ u16 ingain_tgt_max = 0;
+ u16 clp_dir_to = 0;
+ u16 sns_sum_max = 0;
+ u16 clp_sum_max = 0;
+ u16 sns_dir_to = 0;
+ u16 ki_innergain_min = 0;
+ u16 agc_ki = 0;
+ u16 ki_max = 0;
+ u16 if_iaccu_hi_tgt_min = 0;
+ u16 data = 0;
+ u16 agc_ki_dgain = 0;
+ u16 ki_min = 0;
+ u16 clp_ctrl_mode = 0;
+ u16 agc_rf = 0;
+ u16 agc_if = 0;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_8VSB:
+ clp_sum_max = 1023;
+ clp_dir_to = (u16) (-9);
+ sns_sum_max = 1023;
+ sns_dir_to = (u16) (-9);
+ ki_innergain_min = (u16) (-32768);
+ ki_max = 0x032C;
+ agc_ki_dgain = 0xC;
+ if_iaccu_hi_tgt_min = 2047;
+ ki_min = 0x0117;
+ ingain_tgt_max = 16383;
+ clp_ctrl_mode = 0;
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MAXGAIN__A, 0x0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_SUM__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_CYCCNT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_WD__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_STP__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_SUM__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_CYCCNT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_WD__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_STP__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN__A, 1024, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_VSB_AGC_POW_TGT__A, 22600, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN_TGT__A, 13200, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ p_agc_if_settings = &(ext_attr->vsb_if_agc_cfg);
+ p_agc_rf_settings = &(ext_attr->vsb_rf_agc_cfg);
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_C:
+ case DRX_STANDARD_ITU_B:
+ ingain_tgt_max = 5119;
+ clp_sum_max = 1023;
+ clp_dir_to = (u16) (-5);
+ sns_sum_max = 127;
+ sns_dir_to = (u16) (-3);
+ ki_innergain_min = 0;
+ ki_max = 0x0657;
+ if_iaccu_hi_tgt_min = 2047;
+ agc_ki_dgain = 0x7;
+ ki_min = 0x0117;
+ clp_ctrl_mode = 0;
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MAXGAIN__A, 0x0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_SUM__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_CYCCNT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_WD__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_STP__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_SUM__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_CYCCNT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_WD__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_STP__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ p_agc_if_settings = &(ext_attr->qam_if_agc_cfg);
+ p_agc_rf_settings = &(ext_attr->qam_rf_agc_cfg);
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN_TGT__A, p_agc_if_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_AGC_KI__A, &agc_ki, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ agc_ki &= 0xf000;
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI__A, agc_ki, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ /* for new AGC interface */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN_TGT_MIN__A, p_agc_if_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN__A, p_agc_if_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* Gain fed from inner to outer AGC */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_INGAIN_TGT_MAX__A, ingain_tgt_max, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A, if_iaccu_hi_tgt_min, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_IF_IACCU_HI__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* set to p_agc_settings->top before */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_IF_IACCU_LO__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_RF_IACCU_HI__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_RF_IACCU_LO__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_RF_MAX__A, 32767, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_SUM_MAX__A, clp_sum_max, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_SUM_MAX__A, sns_sum_max, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_INNERGAIN_MIN__A, ki_innergain_min, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_CYCLEN__A, 500, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_CYCLEN__A, 500, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MAXMINGAIN_TH__A, 20, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MIN__A, ki_min, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_MAX__A, ki_max, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI_RED__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_SUM_MIN__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_CYCLEN__A, 500, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_DIR_TO__A, clp_dir_to, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_SUM_MIN__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_SNS_DIR_TO__A, sns_dir_to, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_CLP_CTRL_MODE__A, clp_ctrl_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ agc_rf = 0x800 + p_agc_rf_settings->cut_off_current;
+ if (common_attr->tuner_rf_agc_pol == true)
+ agc_rf = 0x87ff - agc_rf;
+
+ agc_if = 0x800;
+ if (common_attr->tuner_if_agc_pol == true)
+ agc_rf = 0x87ff - agc_rf;
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_AGC_RF__A, agc_rf, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_AGC_IF__A, agc_if, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Set/restore Ki DGAIN factor */
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_AGC_KI__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_DGAIN__M;
+ data |= (agc_ki_dgain << SCU_RAM_AGC_KI_DGAIN__B);
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_AGC_KI__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int set_frequency ()
+* \brief Set frequency shift.
+* \param demod instance of demodulator.
+* \param channel pointer to channel data.
+* \param tuner_freq_offset residual frequency from tuner.
+* \return int.
+*/
+static int
+set_frequency(struct drx_demod_instance *demod,
+ struct drx_channel *channel, s32 tuner_freq_offset)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drxj_data *ext_attr = demod->my_ext_attr;
+ int rc;
+ s32 sampling_frequency = 0;
+ s32 frequency_shift = 0;
+ s32 if_freq_actual = 0;
+ s32 rf_freq_residual = -1 * tuner_freq_offset;
+ s32 adc_freq = 0;
+ s32 intermediate_freq = 0;
+ u32 iqm_fs_rate_ofs = 0;
+ bool adc_flip = true;
+ bool select_pos_image = false;
+ bool rf_mirror;
+ bool tuner_mirror;
+ bool image_to_select;
+ s32 fm_frequency_shift = 0;
+
+ rf_mirror = ext_attr->mirror == DRX_MIRROR_YES;
+ tuner_mirror = !demod->my_common_attr->mirror_freq_spect;
+ /*
+ Program frequency shifter
+ No need to account for mirroring on RF
+ */
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_C:
+ case DRX_STANDARD_PAL_SECAM_LP:
+ case DRX_STANDARD_8VSB:
+ select_pos_image = true;
+ break;
+ case DRX_STANDARD_FM:
+ /* After IQM FS sound carrier must appear at 4 Mhz in spect.
+ Sound carrier is already 3Mhz above centre frequency due
+ to tuner setting so now add an extra shift of 1MHz... */
+ fm_frequency_shift = 1000;
+ fallthrough;
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_NTSC:
+ case DRX_STANDARD_PAL_SECAM_BG:
+ case DRX_STANDARD_PAL_SECAM_DK:
+ case DRX_STANDARD_PAL_SECAM_I:
+ case DRX_STANDARD_PAL_SECAM_L:
+ select_pos_image = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+ intermediate_freq = demod->my_common_attr->intermediate_freq;
+ sampling_frequency = demod->my_common_attr->sys_clock_freq / 3;
+ if (tuner_mirror)
+ if_freq_actual = intermediate_freq + rf_freq_residual + fm_frequency_shift;
+ else
+ if_freq_actual = intermediate_freq - rf_freq_residual - fm_frequency_shift;
+ if (if_freq_actual > sampling_frequency / 2) {
+ /* adc mirrors */
+ adc_freq = sampling_frequency - if_freq_actual;
+ adc_flip = true;
+ } else {
+ /* adc doesn't mirror */
+ adc_freq = if_freq_actual;
+ adc_flip = false;
+ }
+
+ frequency_shift = adc_freq;
+ image_to_select =
+ (bool) (rf_mirror ^ tuner_mirror ^ adc_flip ^ select_pos_image);
+ iqm_fs_rate_ofs = frac28(frequency_shift, sampling_frequency);
+
+ if (image_to_select)
+ iqm_fs_rate_ofs = ~iqm_fs_rate_ofs + 1;
+
+ /* Program frequency shifter with tuner offset compensation */
+ /* frequency_shift += tuner_freq_offset; TODO */
+ rc = drxdap_fasi_write_reg32(dev_addr, IQM_FS_RATE_OFS_LO__A, iqm_fs_rate_ofs, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->iqm_fs_rate_ofs = iqm_fs_rate_ofs;
+ ext_attr->pos_image = (bool) (rf_mirror ^ tuner_mirror ^ select_pos_image);
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int get_acc_pkt_err()
+* \brief Retrieve signal strength for VSB and QAM.
+* \param demod Pointer to demod instance
+* \param packet_err Pointer to packet error
+* \return int.
+* \retval 0 sig_strength contains valid data.
+* \retval -EINVAL sig_strength is NULL.
+* \retval -EIO Erroneous data, sig_strength contains invalid data.
+*/
+#ifdef DRXJ_SIGNAL_ACCUM_ERR
+static int get_acc_pkt_err(struct drx_demod_instance *demod, u16 *packet_err)
+{
+ int rc;
+ static u16 pkt_err;
+ static u16 last_pkt_err;
+ u16 data = 0;
+ struct drxj_data *ext_attr = NULL;
+ struct i2c_device_addr *dev_addr = NULL;
+
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ dev_addr = demod->my_i2c_dev_addr;
+
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (ext_attr->reset_pkt_err_acc) {
+ last_pkt_err = data;
+ pkt_err = 0;
+ ext_attr->reset_pkt_err_acc = false;
+ }
+
+ if (data < last_pkt_err) {
+ pkt_err += 0xffff - last_pkt_err;
+ pkt_err += data;
+ } else {
+ pkt_err += (data - last_pkt_err);
+ }
+ *packet_err = pkt_err;
+ last_pkt_err = data;
+
+ return 0;
+rw_error:
+ return rc;
+}
+#endif
+
+
+/*============================================================================*/
+
+/*
+* \fn int set_agc_rf ()
+* \brief Configure RF AGC
+* \param demod instance of demodulator.
+* \param agc_settings AGC configuration structure
+* \return int.
+*/
+static int
+set_agc_rf(struct drx_demod_instance *demod, struct drxj_cfg_agc *agc_settings, bool atomic)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ struct drxj_cfg_agc *p_agc_settings = NULL;
+ struct drx_common_attr *common_attr = NULL;
+ int rc;
+ drx_write_reg16func_t scu_wr16 = NULL;
+ drx_read_reg16func_t scu_rr16 = NULL;
+
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ if (atomic) {
+ scu_rr16 = drxj_dap_scu_atomic_read_reg16;
+ scu_wr16 = drxj_dap_scu_atomic_write_reg16;
+ } else {
+ scu_rr16 = drxj_dap_read_reg16;
+ scu_wr16 = drxj_dap_write_reg16;
+ }
+
+ /* Configure AGC only if standard is currently active */
+ if ((ext_attr->standard == agc_settings->standard) ||
+ (DRXJ_ISQAMSTD(ext_attr->standard) &&
+ DRXJ_ISQAMSTD(agc_settings->standard)) ||
+ (DRXJ_ISATVSTD(ext_attr->standard) &&
+ DRXJ_ISATVSTD(agc_settings->standard))) {
+ u16 data = 0;
+
+ switch (agc_settings->ctrl_mode) {
+ case DRX_AGC_CTRL_AUTO:
+
+ /* Enable RF AGC DAC */
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data |= IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE;
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Enable SCU RF AGC loop */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_RF__M;
+ if (ext_attr->standard == DRX_STANDARD_8VSB)
+ data |= (2 << SCU_RAM_AGC_KI_RF__B);
+ else if (DRXJ_ISQAMSTD(ext_attr->standard))
+ data |= (5 << SCU_RAM_AGC_KI_RF__B);
+ else
+ data |= (4 << SCU_RAM_AGC_KI_RF__B);
+
+ if (common_attr->tuner_rf_agc_pol)
+ data |= SCU_RAM_AGC_KI_INV_RF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_KI_INV_RF_POL__M;
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Set speed ( using complementary reduction value ) */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI_RED__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_RED_RAGC_RED__M;
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI_RED__A, (~(agc_settings->speed << SCU_RAM_AGC_KI_RED_RAGC_RED__B) & SCU_RAM_AGC_KI_RED_RAGC_RED__M) | data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (agc_settings->standard == DRX_STANDARD_8VSB)
+ p_agc_settings = &(ext_attr->vsb_if_agc_cfg);
+ else if (DRXJ_ISQAMSTD(agc_settings->standard))
+ p_agc_settings = &(ext_attr->qam_if_agc_cfg);
+ else if (DRXJ_ISATVSTD(agc_settings->standard))
+ p_agc_settings = &(ext_attr->atv_if_agc_cfg);
+ else
+ return -EINVAL;
+
+ /* Set TOP, only if IF-AGC is in AUTO mode */
+ if (p_agc_settings->ctrl_mode == DRX_AGC_CTRL_AUTO) {
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, agc_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, agc_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* Cut-Off current */
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_RF_IACCU_HI_CO__A, agc_settings->cut_off_current, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_AGC_CTRL_USER:
+
+ /* Enable RF AGC DAC */
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data |= IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE;
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Disable SCU RF AGC loop */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_RF__M;
+ if (common_attr->tuner_rf_agc_pol)
+ data |= SCU_RAM_AGC_KI_INV_RF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_KI_INV_RF_POL__M;
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Write value to output pin */
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_RF_IACCU_HI__A, agc_settings->output_level, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_AGC_CTRL_OFF:
+
+ /* Disable RF AGC DAC */
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= (~IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE);
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Disable SCU RF AGC loop */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_RF__M;
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EINVAL;
+ } /* switch ( agcsettings->ctrl_mode ) */
+ }
+
+ /* Store rf agc settings */
+ switch (agc_settings->standard) {
+ case DRX_STANDARD_8VSB:
+ ext_attr->vsb_rf_agc_cfg = *agc_settings;
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ ext_attr->qam_rf_agc_cfg = *agc_settings;
+ break;
+#endif
+ default:
+ return -EIO;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int set_agc_if ()
+* \brief Configure If AGC
+* \param demod instance of demodulator.
+* \param agc_settings AGC configuration structure
+* \return int.
+*/
+static int
+set_agc_if(struct drx_demod_instance *demod, struct drxj_cfg_agc *agc_settings, bool atomic)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ struct drxj_cfg_agc *p_agc_settings = NULL;
+ struct drx_common_attr *common_attr = NULL;
+ drx_write_reg16func_t scu_wr16 = NULL;
+ drx_read_reg16func_t scu_rr16 = NULL;
+ int rc;
+
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ if (atomic) {
+ scu_rr16 = drxj_dap_scu_atomic_read_reg16;
+ scu_wr16 = drxj_dap_scu_atomic_write_reg16;
+ } else {
+ scu_rr16 = drxj_dap_read_reg16;
+ scu_wr16 = drxj_dap_write_reg16;
+ }
+
+ /* Configure AGC only if standard is currently active */
+ if ((ext_attr->standard == agc_settings->standard) ||
+ (DRXJ_ISQAMSTD(ext_attr->standard) &&
+ DRXJ_ISQAMSTD(agc_settings->standard)) ||
+ (DRXJ_ISATVSTD(ext_attr->standard) &&
+ DRXJ_ISATVSTD(agc_settings->standard))) {
+ u16 data = 0;
+
+ switch (agc_settings->ctrl_mode) {
+ case DRX_AGC_CTRL_AUTO:
+ /* Enable IF AGC DAC */
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data |= IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE;
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Enable SCU IF AGC loop */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_IF_AGC_DISABLE__M;
+ data &= ~SCU_RAM_AGC_KI_IF__M;
+ if (ext_attr->standard == DRX_STANDARD_8VSB)
+ data |= (3 << SCU_RAM_AGC_KI_IF__B);
+ else if (DRXJ_ISQAMSTD(ext_attr->standard))
+ data |= (6 << SCU_RAM_AGC_KI_IF__B);
+ else
+ data |= (5 << SCU_RAM_AGC_KI_IF__B);
+
+ if (common_attr->tuner_if_agc_pol)
+ data |= SCU_RAM_AGC_KI_INV_IF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_KI_INV_IF_POL__M;
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Set speed (using complementary reduction value) */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI_RED__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_RED_IAGC_RED__M;
+ rc = (*scu_wr16) (dev_addr, SCU_RAM_AGC_KI_RED__A, (~(agc_settings->speed << SCU_RAM_AGC_KI_RED_IAGC_RED__B) & SCU_RAM_AGC_KI_RED_IAGC_RED__M) | data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (agc_settings->standard == DRX_STANDARD_8VSB)
+ p_agc_settings = &(ext_attr->vsb_rf_agc_cfg);
+ else if (DRXJ_ISQAMSTD(agc_settings->standard))
+ p_agc_settings = &(ext_attr->qam_rf_agc_cfg);
+ else if (DRXJ_ISATVSTD(agc_settings->standard))
+ p_agc_settings = &(ext_attr->atv_rf_agc_cfg);
+ else
+ return -EINVAL;
+
+ /* Restore TOP */
+ if (p_agc_settings->ctrl_mode == DRX_AGC_CTRL_AUTO) {
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, p_agc_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, p_agc_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else {
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ break;
+
+ case DRX_AGC_CTRL_USER:
+
+ /* Enable IF AGC DAC */
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data |= IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE;
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Disable SCU IF AGC loop */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_IF_AGC_DISABLE__M;
+ data |= SCU_RAM_AGC_KI_IF_AGC_DISABLE__M;
+ if (common_attr->tuner_if_agc_pol)
+ data |= SCU_RAM_AGC_KI_INV_IF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_KI_INV_IF_POL__M;
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Write value to output pin */
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, agc_settings->output_level, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+
+ case DRX_AGC_CTRL_OFF:
+
+ /* Disable If AGC DAC */
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= (~IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE);
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Disable SCU IF AGC loop */
+ rc = (*scu_rr16)(dev_addr, SCU_RAM_AGC_KI__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data &= ~SCU_RAM_AGC_KI_IF_AGC_DISABLE__M;
+ data |= SCU_RAM_AGC_KI_IF_AGC_DISABLE__M;
+ rc = (*scu_wr16)(dev_addr, SCU_RAM_AGC_KI__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EINVAL;
+ } /* switch ( agcsettings->ctrl_mode ) */
+
+ /* always set the top to support configurations without if-loop */
+ rc = (*scu_wr16) (dev_addr, SCU_RAM_AGC_INGAIN_TGT_MIN__A, agc_settings->top, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* Store if agc settings */
+ switch (agc_settings->standard) {
+ case DRX_STANDARD_8VSB:
+ ext_attr->vsb_if_agc_cfg = *agc_settings;
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ ext_attr->qam_if_agc_cfg = *agc_settings;
+ break;
+#endif
+ default:
+ return -EIO;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int set_iqm_af ()
+* \brief Configure IQM AF registers
+* \param demod instance of demodulator.
+* \param active
+* \return int.
+*/
+static int set_iqm_af(struct drx_demod_instance *demod, bool active)
+{
+ u16 data = 0;
+ struct i2c_device_addr *dev_addr = NULL;
+ int rc;
+
+ dev_addr = demod->my_i2c_dev_addr;
+
+ /* Configure IQM */
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_STDBY__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (!active)
+ data &= ((~IQM_AF_STDBY_STDBY_ADC_A2_ACTIVE) & (~IQM_AF_STDBY_STDBY_AMP_A2_ACTIVE) & (~IQM_AF_STDBY_STDBY_PD_A2_ACTIVE) & (~IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE) & (~IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE));
+ else
+ data |= (IQM_AF_STDBY_STDBY_ADC_A2_ACTIVE | IQM_AF_STDBY_STDBY_AMP_A2_ACTIVE | IQM_AF_STDBY_STDBY_PD_A2_ACTIVE | IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE | IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE);
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+/*== END 8VSB & QAM COMMON DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== 8VSB DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+
+/*
+* \fn int power_down_vsb ()
+* \brief Powr down QAM related blocks.
+* \param demod instance of demodulator.
+* \param channel pointer to channel data.
+* \return int.
+*/
+static int power_down_vsb(struct drx_demod_instance *demod, bool primary)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drxjscu_cmd cmd_scu = { /* command */ 0,
+ /* parameter_len */ 0,
+ /* result_len */ 0,
+ /* *parameter */ NULL,
+ /* *result */ NULL
+ };
+ struct drx_cfg_mpeg_output cfg_mpeg_output;
+ int rc;
+ u16 cmd_result = 0;
+
+ /*
+ STOP demodulator
+ reset of FEC and VSB HW
+ */
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB |
+ SCU_RAM_COMMAND_CMD_DEMOD_STOP;
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* stop all comm_exec */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_COMM_EXEC__A, VSB_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (primary) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_iqm_af(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ cfg_mpeg_output.enable_mpeg_output = false;
+ rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int set_vsb_leak_n_gain ()
+* \brief Set ATSC demod.
+* \param demod instance of demodulator.
+* \return int.
+*/
+static int set_vsb_leak_n_gain(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ int rc;
+
+ static const u8 vsb_ffe_leak_gain_ram0[] = {
+ DRXJ_16TO8(0x8), /* FFETRAINLKRATIO1 */
+ DRXJ_16TO8(0x8), /* FFETRAINLKRATIO2 */
+ DRXJ_16TO8(0x8), /* FFETRAINLKRATIO3 */
+ DRXJ_16TO8(0xf), /* FFETRAINLKRATIO4 */
+ DRXJ_16TO8(0xf), /* FFETRAINLKRATIO5 */
+ DRXJ_16TO8(0xf), /* FFETRAINLKRATIO6 */
+ DRXJ_16TO8(0xf), /* FFETRAINLKRATIO7 */
+ DRXJ_16TO8(0xf), /* FFETRAINLKRATIO8 */
+ DRXJ_16TO8(0xf), /* FFETRAINLKRATIO9 */
+ DRXJ_16TO8(0x8), /* FFETRAINLKRATIO10 */
+ DRXJ_16TO8(0x8), /* FFETRAINLKRATIO11 */
+ DRXJ_16TO8(0x8), /* FFETRAINLKRATIO12 */
+ DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO1 */
+ DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO2 */
+ DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO3 */
+ DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO4 */
+ DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO5 */
+ DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO6 */
+ DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO7 */
+ DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO8 */
+ DRXJ_16TO8(0x20), /* FFERCA1TRAINLKRATIO9 */
+ DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO10 */
+ DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO11 */
+ DRXJ_16TO8(0x10), /* FFERCA1TRAINLKRATIO12 */
+ DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO1 */
+ DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO2 */
+ DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO3 */
+ DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO4 */
+ DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO5 */
+ DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO6 */
+ DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO7 */
+ DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO8 */
+ DRXJ_16TO8(0x20), /* FFERCA1DATALKRATIO9 */
+ DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO10 */
+ DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO11 */
+ DRXJ_16TO8(0x10), /* FFERCA1DATALKRATIO12 */
+ DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO1 */
+ DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO2 */
+ DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO3 */
+ DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO4 */
+ DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO5 */
+ DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO6 */
+ DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO7 */
+ DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO8 */
+ DRXJ_16TO8(0x20), /* FFERCA2TRAINLKRATIO9 */
+ DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO10 */
+ DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO11 */
+ DRXJ_16TO8(0x10), /* FFERCA2TRAINLKRATIO12 */
+ DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO1 */
+ DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO2 */
+ DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO3 */
+ DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO4 */
+ DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO5 */
+ DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO6 */
+ DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO7 */
+ DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO8 */
+ DRXJ_16TO8(0x20), /* FFERCA2DATALKRATIO9 */
+ DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO10 */
+ DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO11 */
+ DRXJ_16TO8(0x10), /* FFERCA2DATALKRATIO12 */
+ DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO1 */
+ DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO2 */
+ DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO3 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO4 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO5 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO6 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO7 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO8 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1TRAINLKRATIO9 */
+ DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO10 */
+ DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO11 */
+ DRXJ_16TO8(0x07), /* FFEDDM1TRAINLKRATIO12 */
+ DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO1 */
+ DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO2 */
+ DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO3 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO4 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO5 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO6 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO7 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO8 */
+ DRXJ_16TO8(0x0e), /* FFEDDM1DATALKRATIO9 */
+ DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO10 */
+ DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO11 */
+ DRXJ_16TO8(0x07), /* FFEDDM1DATALKRATIO12 */
+ DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO1 */
+ DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO2 */
+ DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO3 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO4 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO5 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO6 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO7 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO8 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2TRAINLKRATIO9 */
+ DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO10 */
+ DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO11 */
+ DRXJ_16TO8(0x06), /* FFEDDM2TRAINLKRATIO12 */
+ DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO1 */
+ DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO2 */
+ DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO3 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO4 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO5 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO6 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO7 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO8 */
+ DRXJ_16TO8(0x0c), /* FFEDDM2DATALKRATIO9 */
+ DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO10 */
+ DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO11 */
+ DRXJ_16TO8(0x06), /* FFEDDM2DATALKRATIO12 */
+ DRXJ_16TO8(0x2020), /* FIRTRAINGAIN1 */
+ DRXJ_16TO8(0x2020), /* FIRTRAINGAIN2 */
+ DRXJ_16TO8(0x2020), /* FIRTRAINGAIN3 */
+ DRXJ_16TO8(0x4040), /* FIRTRAINGAIN4 */
+ DRXJ_16TO8(0x4040), /* FIRTRAINGAIN5 */
+ DRXJ_16TO8(0x4040), /* FIRTRAINGAIN6 */
+ DRXJ_16TO8(0x4040), /* FIRTRAINGAIN7 */
+ DRXJ_16TO8(0x4040), /* FIRTRAINGAIN8 */
+ DRXJ_16TO8(0x4040), /* FIRTRAINGAIN9 */
+ DRXJ_16TO8(0x2020), /* FIRTRAINGAIN10 */
+ DRXJ_16TO8(0x2020), /* FIRTRAINGAIN11 */
+ DRXJ_16TO8(0x2020), /* FIRTRAINGAIN12 */
+ DRXJ_16TO8(0x0808), /* FIRRCA1GAIN1 */
+ DRXJ_16TO8(0x0808), /* FIRRCA1GAIN2 */
+ DRXJ_16TO8(0x0808), /* FIRRCA1GAIN3 */
+ DRXJ_16TO8(0x1010), /* FIRRCA1GAIN4 */
+ DRXJ_16TO8(0x1010), /* FIRRCA1GAIN5 */
+ DRXJ_16TO8(0x1010), /* FIRRCA1GAIN6 */
+ DRXJ_16TO8(0x1010), /* FIRRCA1GAIN7 */
+ DRXJ_16TO8(0x1010) /* FIRRCA1GAIN8 */
+ };
+
+ static const u8 vsb_ffe_leak_gain_ram1[] = {
+ DRXJ_16TO8(0x1010), /* FIRRCA1GAIN9 */
+ DRXJ_16TO8(0x0808), /* FIRRCA1GAIN10 */
+ DRXJ_16TO8(0x0808), /* FIRRCA1GAIN11 */
+ DRXJ_16TO8(0x0808), /* FIRRCA1GAIN12 */
+ DRXJ_16TO8(0x0808), /* FIRRCA2GAIN1 */
+ DRXJ_16TO8(0x0808), /* FIRRCA2GAIN2 */
+ DRXJ_16TO8(0x0808), /* FIRRCA2GAIN3 */
+ DRXJ_16TO8(0x1010), /* FIRRCA2GAIN4 */
+ DRXJ_16TO8(0x1010), /* FIRRCA2GAIN5 */
+ DRXJ_16TO8(0x1010), /* FIRRCA2GAIN6 */
+ DRXJ_16TO8(0x1010), /* FIRRCA2GAIN7 */
+ DRXJ_16TO8(0x1010), /* FIRRCA2GAIN8 */
+ DRXJ_16TO8(0x1010), /* FIRRCA2GAIN9 */
+ DRXJ_16TO8(0x0808), /* FIRRCA2GAIN10 */
+ DRXJ_16TO8(0x0808), /* FIRRCA2GAIN11 */
+ DRXJ_16TO8(0x0808), /* FIRRCA2GAIN12 */
+ DRXJ_16TO8(0x0303), /* FIRDDM1GAIN1 */
+ DRXJ_16TO8(0x0303), /* FIRDDM1GAIN2 */
+ DRXJ_16TO8(0x0303), /* FIRDDM1GAIN3 */
+ DRXJ_16TO8(0x0606), /* FIRDDM1GAIN4 */
+ DRXJ_16TO8(0x0606), /* FIRDDM1GAIN5 */
+ DRXJ_16TO8(0x0606), /* FIRDDM1GAIN6 */
+ DRXJ_16TO8(0x0606), /* FIRDDM1GAIN7 */
+ DRXJ_16TO8(0x0606), /* FIRDDM1GAIN8 */
+ DRXJ_16TO8(0x0606), /* FIRDDM1GAIN9 */
+ DRXJ_16TO8(0x0303), /* FIRDDM1GAIN10 */
+ DRXJ_16TO8(0x0303), /* FIRDDM1GAIN11 */
+ DRXJ_16TO8(0x0303), /* FIRDDM1GAIN12 */
+ DRXJ_16TO8(0x0303), /* FIRDDM2GAIN1 */
+ DRXJ_16TO8(0x0303), /* FIRDDM2GAIN2 */
+ DRXJ_16TO8(0x0303), /* FIRDDM2GAIN3 */
+ DRXJ_16TO8(0x0505), /* FIRDDM2GAIN4 */
+ DRXJ_16TO8(0x0505), /* FIRDDM2GAIN5 */
+ DRXJ_16TO8(0x0505), /* FIRDDM2GAIN6 */
+ DRXJ_16TO8(0x0505), /* FIRDDM2GAIN7 */
+ DRXJ_16TO8(0x0505), /* FIRDDM2GAIN8 */
+ DRXJ_16TO8(0x0505), /* FIRDDM2GAIN9 */
+ DRXJ_16TO8(0x0303), /* FIRDDM2GAIN10 */
+ DRXJ_16TO8(0x0303), /* FIRDDM2GAIN11 */
+ DRXJ_16TO8(0x0303), /* FIRDDM2GAIN12 */
+ DRXJ_16TO8(0x001f), /* DFETRAINLKRATIO */
+ DRXJ_16TO8(0x01ff), /* DFERCA1TRAINLKRATIO */
+ DRXJ_16TO8(0x01ff), /* DFERCA1DATALKRATIO */
+ DRXJ_16TO8(0x004f), /* DFERCA2TRAINLKRATIO */
+ DRXJ_16TO8(0x004f), /* DFERCA2DATALKRATIO */
+ DRXJ_16TO8(0x01ff), /* DFEDDM1TRAINLKRATIO */
+ DRXJ_16TO8(0x01ff), /* DFEDDM1DATALKRATIO */
+ DRXJ_16TO8(0x0352), /* DFEDDM2TRAINLKRATIO */
+ DRXJ_16TO8(0x0352), /* DFEDDM2DATALKRATIO */
+ DRXJ_16TO8(0x0000), /* DFETRAINGAIN */
+ DRXJ_16TO8(0x2020), /* DFERCA1GAIN */
+ DRXJ_16TO8(0x1010), /* DFERCA2GAIN */
+ DRXJ_16TO8(0x1818), /* DFEDDM1GAIN */
+ DRXJ_16TO8(0x1212) /* DFEDDM2GAIN */
+ };
+
+ dev_addr = demod->my_i2c_dev_addr;
+ rc = drxdap_fasi_write_block(dev_addr, VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__A, sizeof(vsb_ffe_leak_gain_ram0), ((u8 *)vsb_ffe_leak_gain_ram0), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__A, sizeof(vsb_ffe_leak_gain_ram1), ((u8 *)vsb_ffe_leak_gain_ram1), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int set_vsb()
+* \brief Set 8VSB demod.
+* \param demod instance of demodulator.
+* \return int.
+*
+*/
+static int set_vsb(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ int rc;
+ struct drx_common_attr *common_attr = NULL;
+ struct drxjscu_cmd cmd_scu;
+ struct drxj_data *ext_attr = NULL;
+ u16 cmd_result = 0;
+ u16 cmd_param = 0;
+ static const u8 vsb_taps_re[] = {
+ DRXJ_16TO8(-2), /* re0 */
+ DRXJ_16TO8(4), /* re1 */
+ DRXJ_16TO8(1), /* re2 */
+ DRXJ_16TO8(-4), /* re3 */
+ DRXJ_16TO8(1), /* re4 */
+ DRXJ_16TO8(4), /* re5 */
+ DRXJ_16TO8(-3), /* re6 */
+ DRXJ_16TO8(-3), /* re7 */
+ DRXJ_16TO8(6), /* re8 */
+ DRXJ_16TO8(1), /* re9 */
+ DRXJ_16TO8(-9), /* re10 */
+ DRXJ_16TO8(3), /* re11 */
+ DRXJ_16TO8(12), /* re12 */
+ DRXJ_16TO8(-9), /* re13 */
+ DRXJ_16TO8(-15), /* re14 */
+ DRXJ_16TO8(17), /* re15 */
+ DRXJ_16TO8(19), /* re16 */
+ DRXJ_16TO8(-29), /* re17 */
+ DRXJ_16TO8(-22), /* re18 */
+ DRXJ_16TO8(45), /* re19 */
+ DRXJ_16TO8(25), /* re20 */
+ DRXJ_16TO8(-70), /* re21 */
+ DRXJ_16TO8(-28), /* re22 */
+ DRXJ_16TO8(111), /* re23 */
+ DRXJ_16TO8(30), /* re24 */
+ DRXJ_16TO8(-201), /* re25 */
+ DRXJ_16TO8(-31), /* re26 */
+ DRXJ_16TO8(629) /* re27 */
+ };
+
+ dev_addr = demod->my_i2c_dev_addr;
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ /* stop all comm_exec */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_COMM_EXEC__A, VSB_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* reset demodulator */
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB
+ | SCU_RAM_COMMAND_CMD_DEMOD_RESET;
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_DCF_BYPASS__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FS_ADJ_SEL__A, IQM_FS_ADJ_SEL_B_VSB, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_ADJ_SEL__A, IQM_RC_ADJ_SEL_B_VSB, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->iqm_rc_rate_ofs = 0x00AD0D79;
+ rc = drxdap_fasi_write_reg32(dev_addr, IQM_RC_RATE_OFS_LO__A, ext_attr->iqm_rc_rate_ofs, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CFAGC_GAINSHIFT__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CYGN1TRK__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_CROUT_ENA__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_STRETCH__A, 28, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RT_ACTIVE__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SYMMETRIC__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_MIDTAP__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_OUT_ENA__A, IQM_CF_OUT_ENA_VSB__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SCALE__A, 1393, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SCALE_SH__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_POW_MEAS_LEN__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(vsb_taps_re), ((u8 *)vsb_taps_re), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(vsb_taps_re), ((u8 *)vsb_taps_re), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_BNTHRESH__A, 330, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* set higher threshold */
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CLPLASTNUM__A, 90, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* burst detection on */
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SNRTH_RCA1__A, 0x0042, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* drop thresholds by 1 dB */
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SNRTH_RCA2__A, 0x0053, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* drop thresholds by 2 dB */
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_EQCTRL__A, 0x1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* cma on */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_GPIO__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* GPIO */
+
+ /* Initialize the FEC Subsystem */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_TOP_ANNEX__A, FEC_TOP_ANNEX_D, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ {
+ u16 fec_oc_snc_mode = 0;
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_SNC_MODE__A, &fec_oc_snc_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* output data even when not locked */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_MODE__A, fec_oc_snc_mode | FEC_OC_SNC_MODE_UNLOCK_ENABLE__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* set clip */
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLP_LEN__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLP_TH__A, 470, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_SNS_LEN__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SNRTH_PT__A, 0xD4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* no transparent, no A&C framing; parity is set in mpegoutput */
+ {
+ u16 fec_oc_reg_mode = 0;
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_MODE__A, &fec_oc_reg_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_MODE__A, fec_oc_reg_mode & (~(FEC_OC_MODE_TRANSPARENT__M | FEC_OC_MODE_CLEAR__M | FEC_OC_MODE_RETAIN_FRAMING__M)), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, FEC_DI_TIMEOUT_LO__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* timeout counter for restarting */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_DI_TIMEOUT_HI__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MODE__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* bypass disabled */
+ /* initialize RS packet error measurement parameters */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MEASUREMENT_PERIOD__A, FEC_RS_MEASUREMENT_PERIOD, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MEASUREMENT_PRESCALE__A, FEC_RS_MEASUREMENT_PRESCALE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* init measurement period of MER/SER */
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_MEASUREMENT_PERIOD__A, VSB_TOP_MEASUREMENT_PERIOD, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_reg32(dev_addr, SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_FEC_MEAS_COUNT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CKGN1TRK__A, 128, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* B-Input to ADC, PGA+filter in standby */
+ if (!ext_attr->has_lna) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_AMUX__A, 0x02, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* turn on IQMAF. It has to be in front of setAgc**() */
+ rc = set_iqm_af(demod, true);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = adc_synchronization(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = init_agc(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_agc_if(demod, &(ext_attr->vsb_if_agc_cfg), false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_agc_rf(demod, &(ext_attr->vsb_rf_agc_cfg), false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ {
+ /* TODO fix this, store a struct drxj_cfg_afe_gain structure in struct drxj_data instead
+ of only the gain */
+ struct drxj_cfg_afe_gain vsb_pga_cfg = { DRX_STANDARD_8VSB, 0 };
+
+ vsb_pga_cfg.gain = ext_attr->vsb_pga_cfg;
+ rc = ctrl_set_cfg_afe_gain(demod, &vsb_pga_cfg);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ rc = ctrl_set_cfg_pre_saw(demod, &(ext_attr->vsb_pre_saw_cfg));
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Mpeg output has to be in front of FEC active */
+ rc = set_mpegtei_handling(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = bit_reverse_mpeg_output(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_mpeg_start_width(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ {
+ /* TODO: move to set_standard after hardware reset value problem is solved */
+ /* Configure initial MPEG output */
+ struct drx_cfg_mpeg_output cfg_mpeg_output;
+
+ memcpy(&cfg_mpeg_output, &common_attr->mpeg_cfg, sizeof(cfg_mpeg_output));
+ cfg_mpeg_output.enable_mpeg_output = true;
+
+ rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* TBD: what parameters should be set */
+ cmd_param = 0x00; /* Default mode AGC on, etc */
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB
+ | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM;
+ cmd_scu.parameter_len = 1;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = &cmd_param;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_BEAGC_GAINSHIFT__A, 0x0004, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SNRTH_PT__A, 0x00D2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_SYSSMTRNCTRL__A, VSB_TOP_SYSSMTRNCTRL__PRE | VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_BEDETCTRL__A, 0x142, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_LBAGCREFLVL__A, 640, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CYGN1ACQ__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CYGN1TRK__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_TOP_CYGN2TRK__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* start demodulator */
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB
+ | SCU_RAM_COMMAND_CMD_DEMOD_START;
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, VSB_COMM_EXEC__A, VSB_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn static short get_vsb_post_rs_pck_err(struct i2c_device_addr *dev_addr, u16 *PckErrs)
+* \brief Get the values of packet error in 8VSB mode
+* \return Error code
+*/
+static int get_vsb_post_rs_pck_err(struct i2c_device_addr *dev_addr,
+ u32 *pck_errs, u32 *pck_count)
+{
+ int rc;
+ u16 data = 0;
+ u16 period = 0;
+ u16 prescale = 0;
+ u16 packet_errors_mant = 0;
+ u16 packet_errors_exp = 0;
+
+ rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_FAILURES__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ packet_errors_mant = data & FEC_RS_NR_FAILURES_FIXED_MANT__M;
+ packet_errors_exp = (data & FEC_RS_NR_FAILURES_EXP__M)
+ >> FEC_RS_NR_FAILURES_EXP__B;
+ period = FEC_RS_MEASUREMENT_PERIOD;
+ prescale = FEC_RS_MEASUREMENT_PRESCALE;
+ /* packet error rate = (error packet number) per second */
+ /* 77.3 us is time for per packet */
+ if (period * prescale == 0) {
+ pr_err("error: period and/or prescale is zero!\n");
+ return -EIO;
+ }
+ *pck_errs = packet_errors_mant * (1 << packet_errors_exp);
+ *pck_count = period * prescale * 77;
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn static short GetVSBBer(struct i2c_device_addr *dev_addr, u32 *ber)
+* \brief Get the values of ber in VSB mode
+* \return Error code
+*/
+static int get_vs_bpost_viterbi_ber(struct i2c_device_addr *dev_addr,
+ u32 *ber, u32 *cnt)
+{
+ int rc;
+ u16 data = 0;
+ u16 period = 0;
+ u16 prescale = 0;
+ u16 bit_errors_mant = 0;
+ u16 bit_errors_exp = 0;
+
+ rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_BIT_ERRORS__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ period = FEC_RS_MEASUREMENT_PERIOD;
+ prescale = FEC_RS_MEASUREMENT_PRESCALE;
+
+ bit_errors_mant = data & FEC_RS_NR_BIT_ERRORS_FIXED_MANT__M;
+ bit_errors_exp = (data & FEC_RS_NR_BIT_ERRORS_EXP__M)
+ >> FEC_RS_NR_BIT_ERRORS_EXP__B;
+
+ *cnt = period * prescale * 207 * ((bit_errors_exp > 2) ? 1 : 8);
+
+ if (((bit_errors_mant << bit_errors_exp) >> 3) > 68700)
+ *ber = (*cnt) * 26570;
+ else {
+ if (period * prescale == 0) {
+ pr_err("error: period and/or prescale is zero!\n");
+ return -EIO;
+ }
+ *ber = bit_errors_mant << ((bit_errors_exp > 2) ?
+ (bit_errors_exp - 3) : bit_errors_exp);
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn static short get_vs_bpre_viterbi_ber(struct i2c_device_addr *dev_addr, u32 *ber)
+* \brief Get the values of ber in VSB mode
+* \return Error code
+*/
+static int get_vs_bpre_viterbi_ber(struct i2c_device_addr *dev_addr,
+ u32 *ber, u32 *cnt)
+{
+ u16 data = 0;
+ int rc;
+
+ rc = drxj_dap_read_reg16(dev_addr, VSB_TOP_NR_SYM_ERRS__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ return -EIO;
+ }
+ *ber = data;
+ *cnt = VSB_TOP_MEASUREMENT_PERIOD * SYMBOLS_PER_SEGMENT;
+
+ return 0;
+}
+
+/*
+* \fn static int get_vsbmer(struct i2c_device_addr *dev_addr, u16 *mer)
+* \brief Get the values of MER
+* \return Error code
+*/
+static int get_vsbmer(struct i2c_device_addr *dev_addr, u16 *mer)
+{
+ int rc;
+ u16 data_hi = 0;
+
+ rc = drxj_dap_read_reg16(dev_addr, VSB_TOP_ERR_ENERGY_H__A, &data_hi, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ *mer =
+ (u16) (log1_times100(21504) - log1_times100((data_hi << 6) / 52));
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+
+/*============================================================================*/
+/*== END 8VSB DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== QAM DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+
+/*
+* \fn int power_down_qam ()
+* \brief Powr down QAM related blocks.
+* \param demod instance of demodulator.
+* \param channel pointer to channel data.
+* \return int.
+*/
+static int power_down_qam(struct drx_demod_instance *demod, bool primary)
+{
+ struct drxjscu_cmd cmd_scu = { /* command */ 0,
+ /* parameter_len */ 0,
+ /* result_len */ 0,
+ /* *parameter */ NULL,
+ /* *result */ NULL
+ };
+ int rc;
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drx_cfg_mpeg_output cfg_mpeg_output;
+ struct drx_common_attr *common_attr = demod->my_common_attr;
+ u16 cmd_result = 0;
+
+ /*
+ STOP demodulator
+ resets IQM, QAM and FEC HW blocks
+ */
+ /* stop all comm_exec */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_STOP;
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (primary) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_iqm_af(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ memcpy(&cfg_mpeg_output, &common_attr->mpeg_cfg, sizeof(cfg_mpeg_output));
+ cfg_mpeg_output.enable_mpeg_output = false;
+
+ rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+
+/*
+* \fn int set_qam_measurement ()
+* \brief Setup of the QAM Measuremnt intervals for signal quality
+* \param demod instance of demod.
+* \param constellation current constellation.
+* \return int.
+*
+* NOTE:
+* Take into account that for certain settings the errorcounters can overflow.
+* The implementation does not check this.
+*
+* TODO: overriding the ext_attr->fec_bits_desired by constellation dependent
+* constants to get a measurement period of approx. 1 sec. Remove fec_bits_desired
+* field ?
+*
+*/
+#ifndef DRXJ_VSB_ONLY
+static int
+set_qam_measurement(struct drx_demod_instance *demod,
+ enum drx_modulation constellation, u32 symbol_rate)
+{
+ struct i2c_device_addr *dev_addr = NULL; /* device address for I2C writes */
+ struct drxj_data *ext_attr = NULL; /* Global data container for DRXJ specific data */
+ int rc;
+ u32 fec_bits_desired = 0; /* BER accounting period */
+ u16 fec_rs_plen = 0; /* defines RS BER measurement period */
+ u16 fec_rs_prescale = 0; /* ReedSolomon Measurement Prescale */
+ u32 fec_rs_period = 0; /* Value for corresponding I2C register */
+ u32 fec_rs_bit_cnt = 0; /* Actual precise amount of bits */
+ u32 fec_oc_snc_fail_period = 0; /* Value for corresponding I2C register */
+ u32 qam_vd_period = 0; /* Value for corresponding I2C register */
+ u32 qam_vd_bit_cnt = 0; /* Actual precise amount of bits */
+ u16 fec_vd_plen = 0; /* no of trellis symbols: VD SER measur period */
+ u16 qam_vd_prescale = 0; /* Viterbi Measurement Prescale */
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ fec_bits_desired = ext_attr->fec_bits_desired;
+ fec_rs_prescale = ext_attr->fec_rs_prescale;
+
+ switch (constellation) {
+ case DRX_CONSTELLATION_QAM16:
+ fec_bits_desired = 4 * symbol_rate;
+ break;
+ case DRX_CONSTELLATION_QAM32:
+ fec_bits_desired = 5 * symbol_rate;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ fec_bits_desired = 6 * symbol_rate;
+ break;
+ case DRX_CONSTELLATION_QAM128:
+ fec_bits_desired = 7 * symbol_rate;
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ fec_bits_desired = 8 * symbol_rate;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Parameters for Reed-Solomon Decoder */
+ /* fecrs_period = (int)ceil(FEC_BITS_DESIRED/(fecrs_prescale*plen)) */
+ /* rs_bit_cnt = fecrs_period*fecrs_prescale*plen */
+ /* result is within 32 bit arithmetic -> */
+ /* no need for mult or frac functions */
+
+ /* TODO: use constant instead of calculation and remove the fec_rs_plen in ext_attr */
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_C:
+ fec_rs_plen = 204 * 8;
+ break;
+ case DRX_STANDARD_ITU_B:
+ fec_rs_plen = 128 * 7;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ext_attr->fec_rs_plen = fec_rs_plen; /* for getSigQual */
+ fec_rs_bit_cnt = fec_rs_prescale * fec_rs_plen; /* temp storage */
+ if (fec_rs_bit_cnt == 0) {
+ pr_err("error: fec_rs_bit_cnt is zero!\n");
+ return -EIO;
+ }
+ fec_rs_period = fec_bits_desired / fec_rs_bit_cnt + 1; /* ceil */
+ if (ext_attr->standard != DRX_STANDARD_ITU_B)
+ fec_oc_snc_fail_period = fec_rs_period;
+
+ /* limit to max 16 bit value (I2C register width) if needed */
+ if (fec_rs_period > 0xFFFF)
+ fec_rs_period = 0xFFFF;
+
+ /* write corresponding registers */
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_C:
+ break;
+ case DRX_STANDARD_ITU_B:
+ switch (constellation) {
+ case DRX_CONSTELLATION_QAM64:
+ fec_rs_period = 31581;
+ fec_oc_snc_fail_period = 17932;
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ fec_rs_period = 45446;
+ fec_oc_snc_fail_period = 25805;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_FAIL_PERIOD__A, (u16)fec_oc_snc_fail_period, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MEASUREMENT_PERIOD__A, (u16)fec_rs_period, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_RS_MEASUREMENT_PRESCALE__A, fec_rs_prescale, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->fec_rs_period = (u16) fec_rs_period;
+ ext_attr->fec_rs_prescale = fec_rs_prescale;
+ rc = drxdap_fasi_write_reg32(dev_addr, SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_FEC_MEAS_COUNT__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (ext_attr->standard == DRX_STANDARD_ITU_B) {
+ /* Parameters for Viterbi Decoder */
+ /* qamvd_period = (int)ceil(FEC_BITS_DESIRED/ */
+ /* (qamvd_prescale*plen*(qam_constellation+1))) */
+ /* vd_bit_cnt = qamvd_period*qamvd_prescale*plen */
+ /* result is within 32 bit arithmetic -> */
+ /* no need for mult or frac functions */
+
+ /* a(8 bit) * b(8 bit) = 16 bit result => mult32 not needed */
+ fec_vd_plen = ext_attr->fec_vd_plen;
+ qam_vd_prescale = ext_attr->qam_vd_prescale;
+ qam_vd_bit_cnt = qam_vd_prescale * fec_vd_plen; /* temp storage */
+
+ switch (constellation) {
+ case DRX_CONSTELLATION_QAM64:
+ /* a(16 bit) * b(4 bit) = 20 bit result => mult32 not needed */
+ qam_vd_period =
+ qam_vd_bit_cnt * (QAM_TOP_CONSTELLATION_QAM64 + 1)
+ * (QAM_TOP_CONSTELLATION_QAM64 + 1);
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ /* a(16 bit) * b(5 bit) = 21 bit result => mult32 not needed */
+ qam_vd_period =
+ qam_vd_bit_cnt * (QAM_TOP_CONSTELLATION_QAM256 + 1)
+ * (QAM_TOP_CONSTELLATION_QAM256 + 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (qam_vd_period == 0) {
+ pr_err("error: qam_vd_period is zero!\n");
+ return -EIO;
+ }
+ qam_vd_period = fec_bits_desired / qam_vd_period;
+ /* limit to max 16 bit value (I2C register width) if needed */
+ if (qam_vd_period > 0xFFFF)
+ qam_vd_period = 0xFFFF;
+
+ /* a(16 bit) * b(16 bit) = 32 bit result => mult32 not needed */
+ qam_vd_bit_cnt *= qam_vd_period;
+
+ rc = drxj_dap_write_reg16(dev_addr, QAM_VD_MEASUREMENT_PERIOD__A, (u16)qam_vd_period, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_VD_MEASUREMENT_PRESCALE__A, qam_vd_prescale, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->qam_vd_period = (u16) qam_vd_period;
+ ext_attr->qam_vd_prescale = qam_vd_prescale;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+
+/*
+* \fn int set_qam16 ()
+* \brief QAM16 specific setup
+* \param demod instance of demod.
+* \return int.
+*/
+static int set_qam16(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ static const u8 qam_dq_qual_fun[] = {
+ DRXJ_16TO8(2), /* fun0 */
+ DRXJ_16TO8(2), /* fun1 */
+ DRXJ_16TO8(2), /* fun2 */
+ DRXJ_16TO8(2), /* fun3 */
+ DRXJ_16TO8(3), /* fun4 */
+ DRXJ_16TO8(3), /* fun5 */
+ };
+ static const u8 qam_eq_cma_rad[] = {
+ DRXJ_16TO8(13517), /* RAD0 */
+ DRXJ_16TO8(13517), /* RAD1 */
+ DRXJ_16TO8(13517), /* RAD2 */
+ DRXJ_16TO8(13517), /* RAD3 */
+ DRXJ_16TO8(13517), /* RAD4 */
+ DRXJ_16TO8(13517), /* RAD5 */
+ };
+
+ rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 140, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 120, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 230, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 95, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 105, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 56, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 220, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, 25, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, 6, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16)(-24), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16)(-65), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-127), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 10, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 32, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 240, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 32, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 40960, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+
+/*
+* \fn int set_qam32 ()
+* \brief QAM32 specific setup
+* \param demod instance of demod.
+* \return int.
+*/
+static int set_qam32(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ static const u8 qam_dq_qual_fun[] = {
+ DRXJ_16TO8(3), /* fun0 */
+ DRXJ_16TO8(3), /* fun1 */
+ DRXJ_16TO8(3), /* fun2 */
+ DRXJ_16TO8(3), /* fun3 */
+ DRXJ_16TO8(4), /* fun4 */
+ DRXJ_16TO8(4), /* fun5 */
+ };
+ static const u8 qam_eq_cma_rad[] = {
+ DRXJ_16TO8(6707), /* RAD0 */
+ DRXJ_16TO8(6707), /* RAD1 */
+ DRXJ_16TO8(6707), /* RAD2 */
+ DRXJ_16TO8(6707), /* RAD3 */
+ DRXJ_16TO8(6707), /* RAD4 */
+ DRXJ_16TO8(6707), /* RAD5 */
+ };
+
+ rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 90, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 100, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 170, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 100, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 56, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 140, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16)(-8), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16)(-16), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16)(-26), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16)(-56), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-86), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 10, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 32, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 176, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 20480, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+
+/*
+* \fn int set_qam64 ()
+* \brief QAM64 specific setup
+* \param demod instance of demod.
+* \return int.
+*/
+static int set_qam64(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ static const u8 qam_dq_qual_fun[] = {
+ /* this is hw reset value. no necessary to re-write */
+ DRXJ_16TO8(4), /* fun0 */
+ DRXJ_16TO8(4), /* fun1 */
+ DRXJ_16TO8(4), /* fun2 */
+ DRXJ_16TO8(4), /* fun3 */
+ DRXJ_16TO8(6), /* fun4 */
+ DRXJ_16TO8(6), /* fun5 */
+ };
+ static const u8 qam_eq_cma_rad[] = {
+ DRXJ_16TO8(13336), /* RAD0 */
+ DRXJ_16TO8(12618), /* RAD1 */
+ DRXJ_16TO8(11988), /* RAD2 */
+ DRXJ_16TO8(13809), /* RAD3 */
+ DRXJ_16TO8(13809), /* RAD4 */
+ DRXJ_16TO8(15609), /* RAD5 */
+ };
+
+ rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 105, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 60, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 100, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 195, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 84, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 32, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 141, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, 7, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16)(-15), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16)(-45), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-80), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 30, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 48, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 160, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 32, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 43008, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+
+/*
+* \fn int set_qam128 ()
+* \brief QAM128 specific setup
+* \param demod: instance of demod.
+* \return int.
+*/
+static int set_qam128(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ static const u8 qam_dq_qual_fun[] = {
+ DRXJ_16TO8(6), /* fun0 */
+ DRXJ_16TO8(6), /* fun1 */
+ DRXJ_16TO8(6), /* fun2 */
+ DRXJ_16TO8(6), /* fun3 */
+ DRXJ_16TO8(9), /* fun4 */
+ DRXJ_16TO8(9), /* fun5 */
+ };
+ static const u8 qam_eq_cma_rad[] = {
+ DRXJ_16TO8(6164), /* RAD0 */
+ DRXJ_16TO8(6598), /* RAD1 */
+ DRXJ_16TO8(6394), /* RAD2 */
+ DRXJ_16TO8(6409), /* RAD3 */
+ DRXJ_16TO8(6656), /* RAD4 */
+ DRXJ_16TO8(7238), /* RAD5 */
+ };
+
+ rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 60, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 100, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 140, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 100, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 32, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 65, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16)(-1), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-23), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 32, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 144, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 20992, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+
+/*
+* \fn int set_qam256 ()
+* \brief QAM256 specific setup
+* \param demod: instance of demod.
+* \return int.
+*/
+static int set_qam256(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ static const u8 qam_dq_qual_fun[] = {
+ DRXJ_16TO8(8), /* fun0 */
+ DRXJ_16TO8(8), /* fun1 */
+ DRXJ_16TO8(8), /* fun2 */
+ DRXJ_16TO8(8), /* fun3 */
+ DRXJ_16TO8(12), /* fun4 */
+ DRXJ_16TO8(12), /* fun5 */
+ };
+ static const u8 qam_eq_cma_rad[] = {
+ DRXJ_16TO8(12345), /* RAD0 */
+ DRXJ_16TO8(12345), /* RAD1 */
+ DRXJ_16TO8(13626), /* RAD2 */
+ DRXJ_16TO8(12931), /* RAD3 */
+ DRXJ_16TO8(14719), /* RAD4 */
+ DRXJ_16TO8(15356), /* RAD5 */
+ };
+
+ rc = drxdap_fasi_write_block(dev_addr, QAM_DQ_QUAL_FUN0__A, sizeof(qam_dq_qual_fun), ((u8 *)qam_dq_qual_fun), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, SCU_RAM_QAM_EQ_CMA_RAD0__A, sizeof(qam_eq_cma_rad), ((u8 *)qam_eq_cma_rad), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RTH__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FTH__A, 60, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_PTH__A, 100, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_QTH__A, 150, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_CTH__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MTH__A, 110, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RATE_LIM__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_FREQ_LIM__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_COUNT_LIM__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, 74, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, 18, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, 13, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, 7, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16)(-8), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_FINE__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CA_COARSE__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_MEDIUM__A, 50, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CP_COARSE__A, 255, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_FINE__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_MEDIUM__A, 25, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CI_COARSE__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EP_COARSE__A, 24, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_FINE__A, 12, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_EI_COARSE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_FINE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_MEDIUM__A, 48, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF_COARSE__A, 80, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_FINE__A, 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 15, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_LC_CF1_COARSE__A, 16, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_SL_SIG_POWER__A, 43520, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+#define QAM_SET_OP_ALL 0x1
+#define QAM_SET_OP_CONSTELLATION 0x2
+#define QAM_SET_OP_SPECTRUM 0X4
+
+/*
+* \fn int set_qam ()
+* \brief Set QAM demod.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \return int.
+*/
+static int
+set_qam(struct drx_demod_instance *demod,
+ struct drx_channel *channel, s32 tuner_freq_offset, u32 op)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ struct drx_common_attr *common_attr = NULL;
+ int rc;
+ u32 adc_frequency = 0;
+ u32 iqm_rc_rate = 0;
+ u16 cmd_result = 0;
+ u16 lc_symbol_freq = 0;
+ u16 iqm_rc_stretch = 0;
+ u16 set_env_parameters = 0;
+ u16 set_param_parameters[2] = { 0 };
+ struct drxjscu_cmd cmd_scu = { /* command */ 0,
+ /* parameter_len */ 0,
+ /* result_len */ 0,
+ /* parameter */ NULL,
+ /* result */ NULL
+ };
+ static const u8 qam_a_taps[] = {
+ DRXJ_16TO8(-1), /* re0 */
+ DRXJ_16TO8(1), /* re1 */
+ DRXJ_16TO8(1), /* re2 */
+ DRXJ_16TO8(-1), /* re3 */
+ DRXJ_16TO8(-1), /* re4 */
+ DRXJ_16TO8(2), /* re5 */
+ DRXJ_16TO8(1), /* re6 */
+ DRXJ_16TO8(-2), /* re7 */
+ DRXJ_16TO8(0), /* re8 */
+ DRXJ_16TO8(3), /* re9 */
+ DRXJ_16TO8(-1), /* re10 */
+ DRXJ_16TO8(-3), /* re11 */
+ DRXJ_16TO8(4), /* re12 */
+ DRXJ_16TO8(1), /* re13 */
+ DRXJ_16TO8(-8), /* re14 */
+ DRXJ_16TO8(4), /* re15 */
+ DRXJ_16TO8(13), /* re16 */
+ DRXJ_16TO8(-13), /* re17 */
+ DRXJ_16TO8(-19), /* re18 */
+ DRXJ_16TO8(28), /* re19 */
+ DRXJ_16TO8(25), /* re20 */
+ DRXJ_16TO8(-53), /* re21 */
+ DRXJ_16TO8(-31), /* re22 */
+ DRXJ_16TO8(96), /* re23 */
+ DRXJ_16TO8(37), /* re24 */
+ DRXJ_16TO8(-190), /* re25 */
+ DRXJ_16TO8(-40), /* re26 */
+ DRXJ_16TO8(619) /* re27 */
+ };
+ static const u8 qam_b64_taps[] = {
+ DRXJ_16TO8(0), /* re0 */
+ DRXJ_16TO8(-2), /* re1 */
+ DRXJ_16TO8(1), /* re2 */
+ DRXJ_16TO8(2), /* re3 */
+ DRXJ_16TO8(-2), /* re4 */
+ DRXJ_16TO8(0), /* re5 */
+ DRXJ_16TO8(4), /* re6 */
+ DRXJ_16TO8(-2), /* re7 */
+ DRXJ_16TO8(-4), /* re8 */
+ DRXJ_16TO8(4), /* re9 */
+ DRXJ_16TO8(3), /* re10 */
+ DRXJ_16TO8(-6), /* re11 */
+ DRXJ_16TO8(0), /* re12 */
+ DRXJ_16TO8(6), /* re13 */
+ DRXJ_16TO8(-5), /* re14 */
+ DRXJ_16TO8(-3), /* re15 */
+ DRXJ_16TO8(11), /* re16 */
+ DRXJ_16TO8(-4), /* re17 */
+ DRXJ_16TO8(-19), /* re18 */
+ DRXJ_16TO8(19), /* re19 */
+ DRXJ_16TO8(28), /* re20 */
+ DRXJ_16TO8(-45), /* re21 */
+ DRXJ_16TO8(-36), /* re22 */
+ DRXJ_16TO8(90), /* re23 */
+ DRXJ_16TO8(42), /* re24 */
+ DRXJ_16TO8(-185), /* re25 */
+ DRXJ_16TO8(-46), /* re26 */
+ DRXJ_16TO8(614) /* re27 */
+ };
+ static const u8 qam_b256_taps[] = {
+ DRXJ_16TO8(-2), /* re0 */
+ DRXJ_16TO8(4), /* re1 */
+ DRXJ_16TO8(1), /* re2 */
+ DRXJ_16TO8(-4), /* re3 */
+ DRXJ_16TO8(0), /* re4 */
+ DRXJ_16TO8(4), /* re5 */
+ DRXJ_16TO8(-2), /* re6 */
+ DRXJ_16TO8(-4), /* re7 */
+ DRXJ_16TO8(5), /* re8 */
+ DRXJ_16TO8(2), /* re9 */
+ DRXJ_16TO8(-8), /* re10 */
+ DRXJ_16TO8(2), /* re11 */
+ DRXJ_16TO8(11), /* re12 */
+ DRXJ_16TO8(-8), /* re13 */
+ DRXJ_16TO8(-15), /* re14 */
+ DRXJ_16TO8(16), /* re15 */
+ DRXJ_16TO8(19), /* re16 */
+ DRXJ_16TO8(-27), /* re17 */
+ DRXJ_16TO8(-22), /* re18 */
+ DRXJ_16TO8(44), /* re19 */
+ DRXJ_16TO8(26), /* re20 */
+ DRXJ_16TO8(-69), /* re21 */
+ DRXJ_16TO8(-28), /* re22 */
+ DRXJ_16TO8(110), /* re23 */
+ DRXJ_16TO8(31), /* re24 */
+ DRXJ_16TO8(-201), /* re25 */
+ DRXJ_16TO8(-32), /* re26 */
+ DRXJ_16TO8(628) /* re27 */
+ };
+ static const u8 qam_c_taps[] = {
+ DRXJ_16TO8(-3), /* re0 */
+ DRXJ_16TO8(3), /* re1 */
+ DRXJ_16TO8(2), /* re2 */
+ DRXJ_16TO8(-4), /* re3 */
+ DRXJ_16TO8(0), /* re4 */
+ DRXJ_16TO8(4), /* re5 */
+ DRXJ_16TO8(-1), /* re6 */
+ DRXJ_16TO8(-4), /* re7 */
+ DRXJ_16TO8(3), /* re8 */
+ DRXJ_16TO8(3), /* re9 */
+ DRXJ_16TO8(-5), /* re10 */
+ DRXJ_16TO8(0), /* re11 */
+ DRXJ_16TO8(9), /* re12 */
+ DRXJ_16TO8(-4), /* re13 */
+ DRXJ_16TO8(-12), /* re14 */
+ DRXJ_16TO8(10), /* re15 */
+ DRXJ_16TO8(16), /* re16 */
+ DRXJ_16TO8(-21), /* re17 */
+ DRXJ_16TO8(-20), /* re18 */
+ DRXJ_16TO8(37), /* re19 */
+ DRXJ_16TO8(25), /* re20 */
+ DRXJ_16TO8(-62), /* re21 */
+ DRXJ_16TO8(-28), /* re22 */
+ DRXJ_16TO8(105), /* re23 */
+ DRXJ_16TO8(31), /* re24 */
+ DRXJ_16TO8(-197), /* re25 */
+ DRXJ_16TO8(-33), /* re26 */
+ DRXJ_16TO8(626) /* re27 */
+ };
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+
+ if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) {
+ if (ext_attr->standard == DRX_STANDARD_ITU_B) {
+ switch (channel->constellation) {
+ case DRX_CONSTELLATION_QAM256:
+ iqm_rc_rate = 0x00AE3562;
+ lc_symbol_freq =
+ QAM_LC_SYMBOL_FREQ_FREQ_QAM_B_256;
+ channel->symbolrate = 5360537;
+ iqm_rc_stretch = IQM_RC_STRETCH_QAM_B_256;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ iqm_rc_rate = 0x00C05A0E;
+ lc_symbol_freq = 409;
+ channel->symbolrate = 5056941;
+ iqm_rc_stretch = IQM_RC_STRETCH_QAM_B_64;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ adc_frequency = (common_attr->sys_clock_freq * 1000) / 3;
+ if (channel->symbolrate == 0) {
+ pr_err("error: channel symbolrate is zero!\n");
+ return -EIO;
+ }
+ iqm_rc_rate =
+ (adc_frequency / channel->symbolrate) * (1 << 21) +
+ (frac28
+ ((adc_frequency % channel->symbolrate),
+ channel->symbolrate) >> 7) - (1 << 23);
+ lc_symbol_freq =
+ (u16) (frac28
+ (channel->symbolrate +
+ (adc_frequency >> 13),
+ adc_frequency) >> 16);
+ if (lc_symbol_freq > 511)
+ lc_symbol_freq = 511;
+
+ iqm_rc_stretch = 21;
+ }
+
+ if (ext_attr->standard == DRX_STANDARD_ITU_A) {
+ set_env_parameters = QAM_TOP_ANNEX_A; /* annex */
+ set_param_parameters[0] = channel->constellation; /* constellation */
+ set_param_parameters[1] = DRX_INTERLEAVEMODE_I12_J17; /* interleave mode */
+ } else if (ext_attr->standard == DRX_STANDARD_ITU_B) {
+ set_env_parameters = QAM_TOP_ANNEX_B; /* annex */
+ set_param_parameters[0] = channel->constellation; /* constellation */
+ set_param_parameters[1] = channel->interleavemode; /* interleave mode */
+ } else if (ext_attr->standard == DRX_STANDARD_ITU_C) {
+ set_env_parameters = QAM_TOP_ANNEX_C; /* annex */
+ set_param_parameters[0] = channel->constellation; /* constellation */
+ set_param_parameters[1] = DRX_INTERLEAVEMODE_I12_J17; /* interleave mode */
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ if (op & QAM_SET_OP_ALL) {
+ /*
+ STEP 1: reset demodulator
+ resets IQM, QAM and FEC HW blocks
+ resets SCU variables
+ */
+ /* stop all comm_exec */
+ rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_RESET;
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) {
+ /*
+ STEP 2: configure demodulator
+ -set env
+ -set params (resets IQM,QAM,FEC HW; initializes some SCU variables )
+ */
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV;
+ cmd_scu.parameter_len = 1;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = &set_env_parameters;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM;
+ cmd_scu.parameter_len = 2;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = set_param_parameters;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* set symbol rate */
+ rc = drxdap_fasi_write_reg32(dev_addr, IQM_RC_RATE_OFS_LO__A, iqm_rc_rate, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->iqm_rc_rate_ofs = iqm_rc_rate;
+ rc = set_qam_measurement(demod, channel->constellation, channel->symbolrate);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ /* STEP 3: enable the system in a mode where the ADC provides valid signal
+ setup constellation independent registers */
+ /* from qam_cmd.py script (qam_driver_b) */
+ /* TODO: remove re-writes of HW reset values */
+ if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_SPECTRUM)) {
+ rc = set_frequency(demod, channel, tuner_freq_offset);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) {
+
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_SYMBOL_FREQ__A, lc_symbol_freq, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_STRETCH__A, iqm_rc_stretch, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ if (op & QAM_SET_OP_ALL) {
+ if (!ext_attr->has_lna) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_AMUX__A, 0x02, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SYMMETRIC__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_MIDTAP__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_OUT_ENA__A, IQM_CF_OUT_ENA_QAM__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_WR_RSV_0__A, 0x5f, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* scu temporary shut down agc */
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_SYNC_SEL__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLP_LEN__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_CLP_TH__A, 448, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_SNS_LEN__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_PDREF__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_STDBY__A, 0x10, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_PGA_GAIN__A, 11, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_POW_MEAS_LEN__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SCALE_SH__A, IQM_CF_SCALE_SH__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /*! reset default val ! */
+
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_TIMEOUT__A, QAM_SY_TIMEOUT__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /*! reset default val ! */
+ if (ext_attr->standard == DRX_STANDARD_ITU_B) {
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_LWM__A, QAM_SY_SYNC_LWM__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /*! reset default val ! */
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_AWM__A, QAM_SY_SYNC_AWM__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /*! reset default val ! */
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_HWM__A, QAM_SY_SYNC_HWM__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /*! reset default val ! */
+ } else {
+ switch (channel->constellation) {
+ case DRX_CONSTELLATION_QAM16:
+ case DRX_CONSTELLATION_QAM64:
+ case DRX_CONSTELLATION_QAM256:
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_LWM__A, 0x03, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_AWM__A, 0x04, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_HWM__A, QAM_SY_SYNC_HWM__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /*! reset default val ! */
+ break;
+ case DRX_CONSTELLATION_QAM32:
+ case DRX_CONSTELLATION_QAM128:
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_LWM__A, 0x03, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_AWM__A, 0x05, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_SY_SYNC_HWM__A, 0x06, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EIO;
+ } /* switch */
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_MODE__A, QAM_LC_MODE__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /*! reset default val ! */
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_RATE_LIMIT__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_LPF_FACTORP__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_LPF_FACTORI__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_MODE__A, 7, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB0__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB1__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB2__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB3__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB4__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB5__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB6__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB8__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB9__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB10__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB12__A, 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB15__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB16__A, 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB20__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_QUAL_TAB25__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FS_ADJ_SEL__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_ADJ_SEL__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_ADJ_SEL__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_POW_MEAS_LEN__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_GPIO__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* No more resets of the IQM, current standard correctly set =>
+ now AGCs can be configured. */
+ /* turn on IQMAF. It has to be in front of setAgc**() */
+ rc = set_iqm_af(demod, true);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = adc_synchronization(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = init_agc(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_agc_if(demod, &(ext_attr->qam_if_agc_cfg), false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_agc_rf(demod, &(ext_attr->qam_rf_agc_cfg), false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ {
+ /* TODO fix this, store a struct drxj_cfg_afe_gain structure in struct drxj_data instead
+ of only the gain */
+ struct drxj_cfg_afe_gain qam_pga_cfg = { DRX_STANDARD_ITU_B, 0 };
+
+ qam_pga_cfg.gain = ext_attr->qam_pga_cfg;
+ rc = ctrl_set_cfg_afe_gain(demod, &qam_pga_cfg);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ rc = ctrl_set_cfg_pre_saw(demod, &(ext_attr->qam_pre_saw_cfg));
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) {
+ if (ext_attr->standard == DRX_STANDARD_ITU_A) {
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(qam_a_taps), ((u8 *)qam_a_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(qam_a_taps), ((u8 *)qam_a_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else if (ext_attr->standard == DRX_STANDARD_ITU_B) {
+ switch (channel->constellation) {
+ case DRX_CONSTELLATION_QAM64:
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(qam_b64_taps), ((u8 *)qam_b64_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(qam_b64_taps), ((u8 *)qam_b64_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(qam_b256_taps), ((u8 *)qam_b256_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(qam_b256_taps), ((u8 *)qam_b256_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EIO;
+ }
+ } else if (ext_attr->standard == DRX_STANDARD_ITU_C) {
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_RE0__A, sizeof(qam_c_taps), ((u8 *)qam_c_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxdap_fasi_write_block(dev_addr, IQM_CF_TAP_IM0__A, sizeof(qam_c_taps), ((u8 *)qam_c_taps), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* SETP 4: constellation specific setup */
+ switch (channel->constellation) {
+ case DRX_CONSTELLATION_QAM16:
+ rc = set_qam16(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_CONSTELLATION_QAM32:
+ rc = set_qam32(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ rc = set_qam64(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_CONSTELLATION_QAM128:
+ rc = set_qam128(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ rc = set_qam256(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ return -EIO;
+ } /* switch */
+ }
+
+ if ((op & QAM_SET_OP_ALL)) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_SCALE_SH__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Mpeg output has to be in front of FEC active */
+ rc = set_mpegtei_handling(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = bit_reverse_mpeg_output(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_mpeg_start_width(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ {
+ /* TODO: move to set_standard after hardware reset value problem is solved */
+ /* Configure initial MPEG output */
+ struct drx_cfg_mpeg_output cfg_mpeg_output;
+
+ memcpy(&cfg_mpeg_output, &common_attr->mpeg_cfg, sizeof(cfg_mpeg_output));
+ cfg_mpeg_output.enable_mpeg_output = true;
+
+ rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ }
+
+ if ((op & QAM_SET_OP_ALL) || (op & QAM_SET_OP_CONSTELLATION)) {
+
+ /* STEP 5: start QAM demodulator (starts FEC, QAM and IQM HW) */
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_START;
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_COMM_EXEC__A, QAM_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+static int ctrl_get_qam_sig_quality(struct drx_demod_instance *demod);
+
+static int qam_flip_spec(struct drx_demod_instance *demod, struct drx_channel *channel)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drxj_data *ext_attr = demod->my_ext_attr;
+ int rc;
+ u32 iqm_fs_rate_ofs = 0;
+ u32 iqm_fs_rate_lo = 0;
+ u16 qam_ctl_ena = 0;
+ u16 data = 0;
+ u16 equ_mode = 0;
+ u16 fsm_state = 0;
+ int i = 0;
+ int ofsofs = 0;
+
+ /* Silence the controlling of lc, equ, and the acquisition state machine */
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_QAM_CTL_ENA__A, &qam_ctl_ena, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_CTL_ENA__A, qam_ctl_ena & ~(SCU_RAM_QAM_CTL_ENA_ACQ__M | SCU_RAM_QAM_CTL_ENA_EQU__M | SCU_RAM_QAM_CTL_ENA_LC__M), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* freeze the frequency control loop */
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_CF__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_CF1__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_atomic_read_reg32(dev_addr, IQM_FS_RATE_OFS_LO__A, &iqm_fs_rate_ofs, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_atomic_read_reg32(dev_addr, IQM_FS_RATE_LO__A, &iqm_fs_rate_lo, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ofsofs = iqm_fs_rate_lo - iqm_fs_rate_ofs;
+ iqm_fs_rate_ofs = ~iqm_fs_rate_ofs + 1;
+ iqm_fs_rate_ofs -= 2 * ofsofs;
+
+ /* freeze dq/fq updating */
+ rc = drxj_dap_read_reg16(dev_addr, QAM_DQ_MODE__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ data = (data & 0xfff9);
+ rc = drxj_dap_write_reg16(dev_addr, QAM_DQ_MODE__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_MODE__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* lc_cp / _ci / _ca */
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_CI__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_LC_EP__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_LA_FACTOR__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* flip the spec */
+ rc = drxdap_fasi_write_reg32(dev_addr, IQM_FS_RATE_OFS_LO__A, iqm_fs_rate_ofs, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ ext_attr->iqm_fs_rate_ofs = iqm_fs_rate_ofs;
+ ext_attr->pos_image = !ext_attr->pos_image;
+
+ /* freeze dq/fq updating */
+ rc = drxj_dap_read_reg16(dev_addr, QAM_DQ_MODE__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ equ_mode = data;
+ data = (data & 0xfff9);
+ rc = drxj_dap_write_reg16(dev_addr, QAM_DQ_MODE__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_MODE__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ for (i = 0; i < 28; i++) {
+ rc = drxj_dap_read_reg16(dev_addr, QAM_DQ_TAP_IM_EL0__A + (2 * i), &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_DQ_TAP_IM_EL0__A + (2 * i), -data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ for (i = 0; i < 24; i++) {
+ rc = drxj_dap_read_reg16(dev_addr, QAM_FQ_TAP_IM_EL0__A + (2 * i), &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_TAP_IM_EL0__A + (2 * i), -data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ data = equ_mode;
+ rc = drxj_dap_write_reg16(dev_addr, QAM_DQ_MODE__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, QAM_FQ_MODE__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_FSM_STATE_TGT__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ i = 0;
+ while ((fsm_state != 4) && (i++ < 100)) {
+ rc = drxj_dap_read_reg16(dev_addr, SCU_RAM_QAM_FSM_STATE__A, &fsm_state, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_QAM_CTL_ENA__A, (qam_ctl_ena | 0x0016), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+
+}
+
+#define NO_LOCK 0x0
+#define DEMOD_LOCKED 0x1
+#define SYNC_FLIPPED 0x2
+#define SPEC_MIRRORED 0x4
+/*
+* \fn int qam64auto ()
+* \brief auto do sync pattern switching and mirroring.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \param tuner_freq_offset: tuner frequency offset.
+* \param lock_status: pointer to lock status.
+* \return int.
+*/
+static int
+qam64auto(struct drx_demod_instance *demod,
+ struct drx_channel *channel,
+ s32 tuner_freq_offset, enum drx_lock_status *lock_status)
+{
+ struct drxj_data *ext_attr = demod->my_ext_attr;
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drx39xxj_state *state = dev_addr->user_data;
+ struct dtv_frontend_properties *p = &state->frontend.dtv_property_cache;
+ int rc;
+ u32 lck_state = NO_LOCK;
+ u32 start_time = 0;
+ u32 d_locked_time = 0;
+ u32 timeout_ofs = 0;
+ u16 data = 0;
+
+ /* external attributes for storing acquired channel constellation */
+ *lock_status = DRX_NOT_LOCKED;
+ start_time = jiffies_to_msecs(jiffies);
+ lck_state = NO_LOCK;
+ do {
+ rc = ctrl_lock_status(demod, lock_status);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ switch (lck_state) {
+ case NO_LOCK:
+ if (*lock_status == DRXJ_DEMOD_LOCK) {
+ rc = ctrl_get_qam_sig_quality(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (p->cnr.stat[0].svalue > 20800) {
+ lck_state = DEMOD_LOCKED;
+ /* some delay to see if fec_lock possible TODO find the right value */
+ timeout_ofs += DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME; /* see something, waiting longer */
+ d_locked_time = jiffies_to_msecs(jiffies);
+ }
+ }
+ break;
+ case DEMOD_LOCKED:
+ if ((*lock_status == DRXJ_DEMOD_LOCK) && /* still demod_lock in 150ms */
+ ((jiffies_to_msecs(jiffies) - d_locked_time) >
+ DRXJ_QAM_FEC_LOCK_WAITTIME)) {
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, data | 0x1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ lck_state = SYNC_FLIPPED;
+ msleep(10);
+ }
+ break;
+ case SYNC_FLIPPED:
+ if (*lock_status == DRXJ_DEMOD_LOCK) {
+ if (channel->mirror == DRX_MIRROR_AUTO) {
+ /* flip sync pattern back */
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, data & 0xFFFE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* flip spectrum */
+ ext_attr->mirror = DRX_MIRROR_YES;
+ rc = qam_flip_spec(demod, channel);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ lck_state = SPEC_MIRRORED;
+ /* reset timer TODO: still need 500ms? */
+ start_time = d_locked_time =
+ jiffies_to_msecs(jiffies);
+ timeout_ofs = 0;
+ } else { /* no need to wait lock */
+
+ start_time =
+ jiffies_to_msecs(jiffies) -
+ DRXJ_QAM_MAX_WAITTIME - timeout_ofs;
+ }
+ }
+ break;
+ case SPEC_MIRRORED:
+ if ((*lock_status == DRXJ_DEMOD_LOCK) && /* still demod_lock in 150ms */
+ ((jiffies_to_msecs(jiffies) - d_locked_time) >
+ DRXJ_QAM_FEC_LOCK_WAITTIME)) {
+ rc = ctrl_get_qam_sig_quality(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (p->cnr.stat[0].svalue > 20800) {
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr, QAM_SY_TIMEOUT__A, data | 0x1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* no need to wait lock */
+ start_time =
+ jiffies_to_msecs(jiffies) -
+ DRXJ_QAM_MAX_WAITTIME - timeout_ofs;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ msleep(10);
+ } while
+ ((*lock_status != DRX_LOCKED) &&
+ (*lock_status != DRX_NEVER_LOCK) &&
+ ((jiffies_to_msecs(jiffies) - start_time) <
+ (DRXJ_QAM_MAX_WAITTIME + timeout_ofs))
+ );
+ /* Returning control to application ... */
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int qam256auto ()
+* \brief auto do sync pattern switching and mirroring.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \param tuner_freq_offset: tuner frequency offset.
+* \param lock_status: pointer to lock status.
+* \return int.
+*/
+static int
+qam256auto(struct drx_demod_instance *demod,
+ struct drx_channel *channel,
+ s32 tuner_freq_offset, enum drx_lock_status *lock_status)
+{
+ struct drxj_data *ext_attr = demod->my_ext_attr;
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drx39xxj_state *state = dev_addr->user_data;
+ struct dtv_frontend_properties *p = &state->frontend.dtv_property_cache;
+ int rc;
+ u32 lck_state = NO_LOCK;
+ u32 start_time = 0;
+ u32 d_locked_time = 0;
+ u32 timeout_ofs = DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME;
+
+ /* external attributes for storing acquired channel constellation */
+ *lock_status = DRX_NOT_LOCKED;
+ start_time = jiffies_to_msecs(jiffies);
+ lck_state = NO_LOCK;
+ do {
+ rc = ctrl_lock_status(demod, lock_status);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ switch (lck_state) {
+ case NO_LOCK:
+ if (*lock_status == DRXJ_DEMOD_LOCK) {
+ rc = ctrl_get_qam_sig_quality(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (p->cnr.stat[0].svalue > 26800) {
+ lck_state = DEMOD_LOCKED;
+ timeout_ofs += DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME; /* see something, wait longer */
+ d_locked_time = jiffies_to_msecs(jiffies);
+ }
+ }
+ break;
+ case DEMOD_LOCKED:
+ if (*lock_status == DRXJ_DEMOD_LOCK) {
+ if ((channel->mirror == DRX_MIRROR_AUTO) &&
+ ((jiffies_to_msecs(jiffies) - d_locked_time) >
+ DRXJ_QAM_FEC_LOCK_WAITTIME)) {
+ ext_attr->mirror = DRX_MIRROR_YES;
+ rc = qam_flip_spec(demod, channel);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ lck_state = SPEC_MIRRORED;
+ /* reset timer TODO: still need 300ms? */
+ start_time = jiffies_to_msecs(jiffies);
+ timeout_ofs = -DRXJ_QAM_MAX_WAITTIME / 2;
+ }
+ }
+ break;
+ case SPEC_MIRRORED:
+ break;
+ default:
+ break;
+ }
+ msleep(10);
+ } while
+ ((*lock_status < DRX_LOCKED) &&
+ (*lock_status != DRX_NEVER_LOCK) &&
+ ((jiffies_to_msecs(jiffies) - start_time) <
+ (DRXJ_QAM_MAX_WAITTIME + timeout_ofs)));
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int set_qam_channel ()
+* \brief Set QAM channel according to the requested constellation.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \return int.
+*/
+static int
+set_qam_channel(struct drx_demod_instance *demod,
+ struct drx_channel *channel, s32 tuner_freq_offset)
+{
+ struct drxj_data *ext_attr = NULL;
+ int rc;
+ enum drx_lock_status lock_status = DRX_NOT_LOCKED;
+ bool auto_flag = false;
+
+ /* external attributes for storing acquired channel constellation */
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ /* set QAM channel constellation */
+ switch (channel->constellation) {
+ case DRX_CONSTELLATION_QAM16:
+ case DRX_CONSTELLATION_QAM32:
+ case DRX_CONSTELLATION_QAM128:
+ return -EINVAL;
+ case DRX_CONSTELLATION_QAM64:
+ case DRX_CONSTELLATION_QAM256:
+ if (ext_attr->standard != DRX_STANDARD_ITU_B)
+ return -EINVAL;
+
+ ext_attr->constellation = channel->constellation;
+ if (channel->mirror == DRX_MIRROR_AUTO)
+ ext_attr->mirror = DRX_MIRROR_NO;
+ else
+ ext_attr->mirror = channel->mirror;
+
+ rc = set_qam(demod, channel, tuner_freq_offset, QAM_SET_OP_ALL);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (channel->constellation == DRX_CONSTELLATION_QAM64)
+ rc = qam64auto(demod, channel, tuner_freq_offset,
+ &lock_status);
+ else
+ rc = qam256auto(demod, channel, tuner_freq_offset,
+ &lock_status);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_CONSTELLATION_AUTO: /* for channel scan */
+ if (ext_attr->standard == DRX_STANDARD_ITU_B) {
+ u16 qam_ctl_ena = 0;
+
+ auto_flag = true;
+
+ /* try to lock default QAM constellation: QAM256 */
+ channel->constellation = DRX_CONSTELLATION_QAM256;
+ ext_attr->constellation = DRX_CONSTELLATION_QAM256;
+ if (channel->mirror == DRX_MIRROR_AUTO)
+ ext_attr->mirror = DRX_MIRROR_NO;
+ else
+ ext_attr->mirror = channel->mirror;
+ rc = set_qam(demod, channel, tuner_freq_offset,
+ QAM_SET_OP_ALL);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = qam256auto(demod, channel, tuner_freq_offset,
+ &lock_status);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (lock_status >= DRX_LOCKED) {
+ channel->constellation = DRX_CONSTELLATION_AUTO;
+ break;
+ }
+
+ /* QAM254 not locked. Try QAM64 constellation */
+ channel->constellation = DRX_CONSTELLATION_QAM64;
+ ext_attr->constellation = DRX_CONSTELLATION_QAM64;
+ if (channel->mirror == DRX_MIRROR_AUTO)
+ ext_attr->mirror = DRX_MIRROR_NO;
+ else
+ ext_attr->mirror = channel->mirror;
+
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_CTL_ENA__A,
+ &qam_ctl_ena, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_CTL_ENA__A,
+ qam_ctl_ena & ~SCU_RAM_QAM_CTL_ENA_ACQ__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_FSM_STATE_TGT__A,
+ 0x2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* force to rate hunting */
+
+ rc = set_qam(demod, channel, tuner_freq_offset,
+ QAM_SET_OP_CONSTELLATION);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_CTL_ENA__A,
+ qam_ctl_ena, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = qam64auto(demod, channel, tuner_freq_offset,
+ &lock_status);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ channel->constellation = DRX_CONSTELLATION_AUTO;
+ } else if (ext_attr->standard == DRX_STANDARD_ITU_C) {
+ u16 qam_ctl_ena = 0;
+
+ channel->constellation = DRX_CONSTELLATION_QAM64;
+ ext_attr->constellation = DRX_CONSTELLATION_QAM64;
+ auto_flag = true;
+
+ if (channel->mirror == DRX_MIRROR_AUTO)
+ ext_attr->mirror = DRX_MIRROR_NO;
+ else
+ ext_attr->mirror = channel->mirror;
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_CTL_ENA__A,
+ &qam_ctl_ena, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_CTL_ENA__A,
+ qam_ctl_ena & ~SCU_RAM_QAM_CTL_ENA_ACQ__M, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_FSM_STATE_TGT__A,
+ 0x2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* force to rate hunting */
+
+ rc = set_qam(demod, channel, tuner_freq_offset,
+ QAM_SET_OP_CONSTELLATION);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(demod->my_i2c_dev_addr,
+ SCU_RAM_QAM_CTL_ENA__A,
+ qam_ctl_ena, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = qam64auto(demod, channel, tuner_freq_offset,
+ &lock_status);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ channel->constellation = DRX_CONSTELLATION_AUTO;
+ } else {
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+rw_error:
+ /* restore starting value */
+ if (auto_flag)
+ channel->constellation = DRX_CONSTELLATION_AUTO;
+ return rc;
+}
+
+/*============================================================================*/
+
+/*
+* \fn static short get_qamrs_err_count(struct i2c_device_addr *dev_addr)
+* \brief Get RS error count in QAM mode (used for post RS BER calculation)
+* \return Error code
+*
+* precondition: measurement period & measurement prescale must be set
+*
+*/
+static int
+get_qamrs_err_count(struct i2c_device_addr *dev_addr,
+ struct drxjrs_errors *rs_errors)
+{
+ int rc;
+ u16 nr_bit_errors = 0,
+ nr_symbol_errors = 0,
+ nr_packet_errors = 0, nr_failures = 0, nr_snc_par_fail_count = 0;
+
+ /* check arguments */
+ if (dev_addr == NULL)
+ return -EINVAL;
+
+ /* all reported errors are received in the */
+ /* most recently finished measurement period */
+ /* no of pre RS bit errors */
+ rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_BIT_ERRORS__A, &nr_bit_errors, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* no of symbol errors */
+ rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_SYMBOL_ERRORS__A, &nr_symbol_errors, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* no of packet errors */
+ rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_PACKET_ERRORS__A, &nr_packet_errors, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* no of failures to decode */
+ rc = drxj_dap_read_reg16(dev_addr, FEC_RS_NR_FAILURES__A, &nr_failures, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* no of post RS bit erros */
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_SNC_FAIL_COUNT__A, &nr_snc_par_fail_count, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* TODO: NOTE */
+ /* These register values are fetched in non-atomic fashion */
+ /* It is possible that the read values contain unrelated information */
+
+ rs_errors->nr_bit_errors = nr_bit_errors & FEC_RS_NR_BIT_ERRORS__M;
+ rs_errors->nr_symbol_errors = nr_symbol_errors & FEC_RS_NR_SYMBOL_ERRORS__M;
+ rs_errors->nr_packet_errors = nr_packet_errors & FEC_RS_NR_PACKET_ERRORS__M;
+ rs_errors->nr_failures = nr_failures & FEC_RS_NR_FAILURES__M;
+ rs_errors->nr_snc_par_fail_count =
+ nr_snc_par_fail_count & FEC_OC_SNC_FAIL_COUNT__M;
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+
+/*
+ * \fn int get_sig_strength()
+ * \brief Retrieve signal strength for VSB and QAM.
+ * \param demod Pointer to demod instance
+ * \param u16-t Pointer to signal strength data; range 0, .. , 100.
+ * \return int.
+ * \retval 0 sig_strength contains valid data.
+ * \retval -EINVAL sig_strength is NULL.
+ * \retval -EIO Erroneous data, sig_strength contains invalid data.
+ */
+#define DRXJ_AGC_TOP 0x2800
+#define DRXJ_AGC_SNS 0x1600
+#define DRXJ_RFAGC_MAX 0x3fff
+#define DRXJ_RFAGC_MIN 0x800
+
+static int get_sig_strength(struct drx_demod_instance *demod, u16 *sig_strength)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ u16 rf_gain = 0;
+ u16 if_gain = 0;
+ u16 if_agc_sns = 0;
+ u16 if_agc_top = 0;
+ u16 rf_agc_max = 0;
+ u16 rf_agc_min = 0;
+
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_AGC_IF__A, &if_gain, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if_gain &= IQM_AF_AGC_IF__M;
+ rc = drxj_dap_read_reg16(dev_addr, IQM_AF_AGC_RF__A, &rf_gain, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rf_gain &= IQM_AF_AGC_RF__M;
+
+ if_agc_sns = DRXJ_AGC_SNS;
+ if_agc_top = DRXJ_AGC_TOP;
+ rf_agc_max = DRXJ_RFAGC_MAX;
+ rf_agc_min = DRXJ_RFAGC_MIN;
+
+ if (if_gain > if_agc_top) {
+ if (rf_gain > rf_agc_max)
+ *sig_strength = 100;
+ else if (rf_gain > rf_agc_min) {
+ if (rf_agc_max == rf_agc_min) {
+ pr_err("error: rf_agc_max == rf_agc_min\n");
+ return -EIO;
+ }
+ *sig_strength =
+ 75 + 25 * (rf_gain - rf_agc_min) / (rf_agc_max -
+ rf_agc_min);
+ } else
+ *sig_strength = 75;
+ } else if (if_gain > if_agc_sns) {
+ if (if_agc_top == if_agc_sns) {
+ pr_err("error: if_agc_top == if_agc_sns\n");
+ return -EIO;
+ }
+ *sig_strength =
+ 20 + 55 * (if_gain - if_agc_sns) / (if_agc_top - if_agc_sns);
+ } else {
+ if (!if_agc_sns) {
+ pr_err("error: if_agc_sns is zero!\n");
+ return -EIO;
+ }
+ *sig_strength = (20 * if_gain / if_agc_sns);
+ }
+
+ if (*sig_strength <= 7)
+ *sig_strength = 0;
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int ctrl_get_qam_sig_quality()
+* \brief Retrieve QAM signal quality from device.
+* \param devmod Pointer to demodulator instance.
+* \param sig_quality Pointer to signal quality data.
+* \return int.
+* \retval 0 sig_quality contains valid data.
+* \retval -EINVAL sig_quality is NULL.
+* \retval -EIO Erroneous data, sig_quality contains invalid data.
+
+* Pre-condition: Device must be started and in lock.
+*/
+static int
+ctrl_get_qam_sig_quality(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drxj_data *ext_attr = demod->my_ext_attr;
+ struct drx39xxj_state *state = dev_addr->user_data;
+ struct dtv_frontend_properties *p = &state->frontend.dtv_property_cache;
+ struct drxjrs_errors measuredrs_errors = { 0, 0, 0, 0, 0 };
+ enum drx_modulation constellation = ext_attr->constellation;
+ int rc;
+
+ u32 pre_bit_err_rs = 0; /* pre RedSolomon Bit Error Rate */
+ u32 post_bit_err_rs = 0; /* post RedSolomon Bit Error Rate */
+ u32 pkt_errs = 0; /* no of packet errors in RS */
+ u16 qam_sl_err_power = 0; /* accumulated error between raw and sliced symbols */
+ u16 qsym_err_vd = 0; /* quadrature symbol errors in QAM_VD */
+ u16 fec_oc_period = 0; /* SNC sync failure measurement period */
+ u16 fec_rs_prescale = 0; /* ReedSolomon Measurement Prescale */
+ u16 fec_rs_period = 0; /* Value for corresponding I2C register */
+ /* calculation constants */
+ u32 rs_bit_cnt = 0; /* RedSolomon Bit Count */
+ u32 qam_sl_sig_power = 0; /* used for MER, depends of QAM constellation */
+ /* intermediate results */
+ u32 e = 0; /* exponent value used for QAM BER/SER */
+ u32 m = 0; /* mantisa value used for QAM BER/SER */
+ u32 ber_cnt = 0; /* BER count */
+ /* signal quality info */
+ u32 qam_sl_mer = 0; /* QAM MER */
+ u32 qam_pre_rs_ber = 0; /* Pre RedSolomon BER */
+ u32 qam_post_rs_ber = 0; /* Post RedSolomon BER */
+ u32 qam_vd_ser = 0; /* ViterbiDecoder SER */
+ u16 qam_vd_prescale = 0; /* Viterbi Measurement Prescale */
+ u16 qam_vd_period = 0; /* Viterbi Measurement period */
+ u32 vd_bit_cnt = 0; /* ViterbiDecoder Bit Count */
+
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ /* read the physical registers */
+ /* Get the RS error data */
+ rc = get_qamrs_err_count(dev_addr, &measuredrs_errors);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* get the register value needed for MER */
+ rc = drxj_dap_read_reg16(dev_addr, QAM_SL_ERR_POWER__A, &qam_sl_err_power, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* get the register value needed for post RS BER */
+ rc = drxj_dap_read_reg16(dev_addr, FEC_OC_SNC_FAIL_PERIOD__A, &fec_oc_period, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* get constants needed for signal quality calculation */
+ fec_rs_period = ext_attr->fec_rs_period;
+ fec_rs_prescale = ext_attr->fec_rs_prescale;
+ rs_bit_cnt = fec_rs_period * fec_rs_prescale * ext_attr->fec_rs_plen;
+ qam_vd_period = ext_attr->qam_vd_period;
+ qam_vd_prescale = ext_attr->qam_vd_prescale;
+ vd_bit_cnt = qam_vd_period * qam_vd_prescale * ext_attr->fec_vd_plen;
+
+ /* DRXJ_QAM_SL_SIG_POWER_QAMxxx * 4 */
+ switch (constellation) {
+ case DRX_CONSTELLATION_QAM16:
+ qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM16 << 2;
+ break;
+ case DRX_CONSTELLATION_QAM32:
+ qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM32 << 2;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM64 << 2;
+ break;
+ case DRX_CONSTELLATION_QAM128:
+ qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM128 << 2;
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ qam_sl_sig_power = DRXJ_QAM_SL_SIG_POWER_QAM256 << 2;
+ break;
+ default:
+ rc = -EIO;
+ goto rw_error;
+ }
+
+ /* ------------------------------ */
+ /* MER Calculation */
+ /* ------------------------------ */
+ /* MER is good if it is above 27.5 for QAM256 or 21.5 for QAM64 */
+
+ /* 10.0*log10(qam_sl_sig_power * 4.0 / qam_sl_err_power); */
+ if (qam_sl_err_power == 0)
+ qam_sl_mer = 0;
+ else
+ qam_sl_mer = log1_times100(qam_sl_sig_power) - log1_times100((u32)qam_sl_err_power);
+
+ /* ----------------------------------------- */
+ /* Pre Viterbi Symbol Error Rate Calculation */
+ /* ----------------------------------------- */
+ /* pre viterbi SER is good if it is below 0.025 */
+
+ /* get the register value */
+ /* no of quadrature symbol errors */
+ rc = drxj_dap_read_reg16(dev_addr, QAM_VD_NR_QSYM_ERRORS__A, &qsym_err_vd, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Extract the Exponent and the Mantisa */
+ /* of number of quadrature symbol errors */
+ e = (qsym_err_vd & QAM_VD_NR_QSYM_ERRORS_EXP__M) >>
+ QAM_VD_NR_QSYM_ERRORS_EXP__B;
+ m = (qsym_err_vd & QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__M) >>
+ QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__B;
+
+ if ((m << e) >> 3 > 549752)
+ qam_vd_ser = 500000 * vd_bit_cnt * ((e > 2) ? 1 : 8) / 8;
+ else
+ qam_vd_ser = m << ((e > 2) ? (e - 3) : e);
+
+ /* --------------------------------------- */
+ /* pre and post RedSolomon BER Calculation */
+ /* --------------------------------------- */
+ /* pre RS BER is good if it is below 3.5e-4 */
+
+ /* get the register values */
+ pre_bit_err_rs = (u32) measuredrs_errors.nr_bit_errors;
+ pkt_errs = post_bit_err_rs = (u32) measuredrs_errors.nr_snc_par_fail_count;
+
+ /* Extract the Exponent and the Mantisa of the */
+ /* pre Reed-Solomon bit error count */
+ e = (pre_bit_err_rs & FEC_RS_NR_BIT_ERRORS_EXP__M) >>
+ FEC_RS_NR_BIT_ERRORS_EXP__B;
+ m = (pre_bit_err_rs & FEC_RS_NR_BIT_ERRORS_FIXED_MANT__M) >>
+ FEC_RS_NR_BIT_ERRORS_FIXED_MANT__B;
+
+ ber_cnt = m << e;
+
+ /*qam_pre_rs_ber = frac_times1e6( ber_cnt, rs_bit_cnt ); */
+ if (m > (rs_bit_cnt >> (e + 1)) || (rs_bit_cnt >> e) == 0)
+ qam_pre_rs_ber = 500000 * rs_bit_cnt >> e;
+ else
+ qam_pre_rs_ber = ber_cnt;
+
+ /* post RS BER = 1000000* (11.17 * FEC_OC_SNC_FAIL_COUNT__A) / */
+ /* (1504.0 * FEC_OC_SNC_FAIL_PERIOD__A) */
+ /*
+ => c = (1000000*100*11.17)/1504 =
+ post RS BER = (( c* FEC_OC_SNC_FAIL_COUNT__A) /
+ (100 * FEC_OC_SNC_FAIL_PERIOD__A)
+ *100 and /100 is for more precision.
+ => (20 bits * 12 bits) /(16 bits * 7 bits) => safe in 32 bits computation
+
+ Precision errors still possible.
+ */
+ if (!fec_oc_period) {
+ qam_post_rs_ber = 0xFFFFFFFF;
+ } else {
+ e = post_bit_err_rs * 742686;
+ m = fec_oc_period * 100;
+ qam_post_rs_ber = e / m;
+ }
+
+ /* fill signal quality data structure */
+ p->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+
+ p->cnr.stat[0].svalue = ((u16) qam_sl_mer) * 100;
+ if (ext_attr->standard == DRX_STANDARD_ITU_B) {
+ p->pre_bit_error.stat[0].uvalue += qam_vd_ser;
+ p->pre_bit_count.stat[0].uvalue += vd_bit_cnt * ((e > 2) ? 1 : 8) / 8;
+ } else {
+ p->pre_bit_error.stat[0].uvalue += qam_pre_rs_ber;
+ p->pre_bit_count.stat[0].uvalue += rs_bit_cnt >> e;
+ }
+
+ p->post_bit_error.stat[0].uvalue += qam_post_rs_ber;
+ p->post_bit_count.stat[0].uvalue += rs_bit_cnt >> e;
+
+ p->block_error.stat[0].uvalue += pkt_errs;
+
+#ifdef DRXJ_SIGNAL_ACCUM_ERR
+ rc = get_acc_pkt_err(demod, &sig_quality->packet_error);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+#endif
+
+ return 0;
+rw_error:
+ p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ return rc;
+}
+
+#endif /* #ifndef DRXJ_VSB_ONLY */
+
+/*============================================================================*/
+/*== END QAM DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== ATV DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+/*============================================================================*/
+
+/*
+ Implementation notes.
+
+ NTSC/FM AGCs
+
+ Four AGCs are used for NTSC:
+ (1) RF (used to attenuate the input signal in case of to much power)
+ (2) IF (used to attenuate the input signal in case of to much power)
+ (3) Video AGC (used to amplify the output signal in case input to low)
+ (4) SIF AGC (used to amplify the output signal in case input to low)
+
+ Video AGC is coupled to RF and IF. SIF AGC is not coupled. It is assumed
+ that the coupling between Video AGC and the RF and IF AGCs also works in
+ favor of the SIF AGC.
+
+ Three AGCs are used for FM:
+ (1) RF (used to attenuate the input signal in case of to much power)
+ (2) IF (used to attenuate the input signal in case of to much power)
+ (3) SIF AGC (used to amplify the output signal in case input to low)
+
+ The SIF AGC is now coupled to the RF/IF AGCs.
+ The SIF AGC is needed for both SIF output and the internal SIF signal to
+ the AUD block.
+
+ RF and IF AGCs DACs are part of AFE, Video and SIF AGC DACs are part of
+ the ATV block. The AGC control algorithms are all implemented in
+ microcode.
+
+ ATV SETTINGS
+
+ (Shadow settings will not be used for now, they will be implemented
+ later on because of the schedule)
+
+ Several HW/SCU "settings" can be used for ATV. The standard selection
+ will reset most of these settings. To avoid that the end user application
+ has to perform these settings each time the ATV or FM standards is
+ selected the driver will shadow these settings. This enables the end user
+ to perform the settings only once after a drx_open(). The driver must
+ write the shadow settings to HW/SCU in case:
+ ( setstandard FM/ATV) ||
+ ( settings have changed && FM/ATV standard is active)
+ The shadow settings will be stored in the device specific data container.
+ A set of flags will be defined to flag changes in shadow settings.
+ A routine will be implemented to write all changed shadow settings to
+ HW/SCU.
+
+ The "settings" will consist of: AGC settings, filter settings etc.
+
+ Disadvantage of use of shadow settings:
+ Direct changes in HW/SCU registers will not be reflected in the
+ shadow settings and these changes will be overwritten during a next
+ update. This can happen during evaluation. This will not be a problem
+ for normal customer usage.
+*/
+/* -------------------------------------------------------------------------- */
+
+/*
+* \fn int power_down_atv ()
+* \brief Power down ATV.
+* \param demod instance of demodulator
+* \param standard either NTSC or FM (sub strandard for ATV )
+* \return int.
+*
+* Stops and thus resets ATV and IQM block
+* SIF and CVBS ADC are powered down
+* Calls audio power down
+*/
+static int
+power_down_atv(struct drx_demod_instance *demod, enum drx_standard standard, bool primary)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drxjscu_cmd cmd_scu = { /* command */ 0,
+ /* parameter_len */ 0,
+ /* result_len */ 0,
+ /* *parameter */ NULL,
+ /* *result */ NULL
+ };
+ int rc;
+ u16 cmd_result = 0;
+
+ /* ATV NTSC */
+
+ /* Stop ATV SCU (will reset ATV and IQM hardware */
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_ATV |
+ SCU_RAM_COMMAND_CMD_DEMOD_STOP;
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 1;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = &cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Disable ATV outputs (ATV reset enables CVBS, undo this) */
+ rc = drxj_dap_write_reg16(dev_addr, ATV_TOP_STDBY__A, (ATV_TOP_STDBY_SIF_STDBY_STANDBY & (~ATV_TOP_STDBY_CVBS_STDBY_A2_ACTIVE)), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, ATV_COMM_EXEC__A, ATV_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (primary) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_COMM_EXEC__A, IQM_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_iqm_af(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } else {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FS_COMM_EXEC__A, IQM_FS_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_FD_COMM_EXEC__A, IQM_FD_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RC_COMM_EXEC__A, IQM_RC_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_RT_COMM_EXEC__A, IQM_RT_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, IQM_CF_COMM_EXEC__A, IQM_CF_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ rc = power_down_aud(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+
+/*
+* \brief Power up AUD.
+* \param demod instance of demodulator
+* \return int.
+*
+*/
+static int power_down_aud(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ int rc;
+
+ dev_addr = (struct i2c_device_addr *)demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ rc = drxj_dap_write_reg16(dev_addr, AUD_COMM_EXEC__A, AUD_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ ext_attr->aud_data.audio_is_active = false;
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int set_orx_nsu_aox()
+* \brief Configure OrxNsuAox for OOB
+* \param demod instance of demodulator.
+* \param active
+* \return int.
+*/
+static int set_orx_nsu_aox(struct drx_demod_instance *demod, bool active)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ u16 data = 0;
+
+ /* Configure NSU_AOX */
+ rc = drxj_dap_read_reg16(dev_addr, ORX_NSU_AOX_STDBY_W__A, &data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (!active)
+ data &= ((~ORX_NSU_AOX_STDBY_W_STDBYADC_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYAMP_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYBIAS_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYPLL_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYPD_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A2_ON) & (~ORX_NSU_AOX_STDBY_W_STDBYFLT_A2_ON));
+ else
+ data |= (ORX_NSU_AOX_STDBY_W_STDBYADC_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYAMP_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYBIAS_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYPLL_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYPD_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A2_ON | ORX_NSU_AOX_STDBY_W_STDBYFLT_A2_ON);
+ rc = drxj_dap_write_reg16(dev_addr, ORX_NSU_AOX_STDBY_W__A, data, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*
+* \fn int ctrl_set_oob()
+* \brief Set OOB channel to be used.
+* \param demod instance of demodulator
+* \param oob_param OOB parameters for channel setting.
+* \frequency should be in KHz
+* \return int.
+*
+* Accepts only. Returns error otherwise.
+* Demapper value is written after scu_command START
+* because START command causes COMM_EXEC transition
+* from 0 to 1 which causes all registers to be
+* overwritten with initial value
+*
+*/
+
+/* Nyquist filter impulse response */
+#define IMPULSE_COSINE_ALPHA_0_3 {-3, -4, -1, 6, 10, 7, -5, -20, -25, -10, 29, 79, 123, 140} /*sqrt raised-cosine filter with alpha=0.3 */
+#define IMPULSE_COSINE_ALPHA_0_5 { 2, 0, -2, -2, 2, 5, 2, -10, -20, -14, 20, 74, 125, 145} /*sqrt raised-cosine filter with alpha=0.5 */
+#define IMPULSE_COSINE_ALPHA_RO_0_5 { 0, 0, 1, 2, 3, 0, -7, -15, -16, 0, 34, 77, 114, 128} /*full raised-cosine filter with alpha=0.5 (receiver only) */
+
+/* Coefficients for the nyquist filter (total: 27 taps) */
+#define NYQFILTERLEN 27
+
+static int ctrl_set_oob(struct drx_demod_instance *demod, struct drxoob *oob_param)
+{
+ int rc;
+ s32 freq = 0; /* KHz */
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ u16 i = 0;
+ bool mirror_freq_spect_oob = false;
+ u16 trk_filter_value = 0;
+ struct drxjscu_cmd scu_cmd;
+ u16 set_param_parameters[3];
+ u16 cmd_result[2] = { 0, 0 };
+ s16 nyquist_coeffs[4][(NYQFILTERLEN + 1) / 2] = {
+ IMPULSE_COSINE_ALPHA_0_3, /* Target Mode 0 */
+ IMPULSE_COSINE_ALPHA_0_3, /* Target Mode 1 */
+ IMPULSE_COSINE_ALPHA_0_5, /* Target Mode 2 */
+ IMPULSE_COSINE_ALPHA_RO_0_5 /* Target Mode 3 */
+ };
+ u8 mode_val[4] = { 2, 2, 0, 1 };
+ u8 pfi_coeffs[4][6] = {
+ {DRXJ_16TO8(-92), DRXJ_16TO8(-108), DRXJ_16TO8(100)}, /* TARGET_MODE = 0: PFI_A = -23/32; PFI_B = -54/32; PFI_C = 25/32; fg = 0.5 MHz (Att=26dB) */
+ {DRXJ_16TO8(-64), DRXJ_16TO8(-80), DRXJ_16TO8(80)}, /* TARGET_MODE = 1: PFI_A = -16/32; PFI_B = -40/32; PFI_C = 20/32; fg = 1.0 MHz (Att=28dB) */
+ {DRXJ_16TO8(-80), DRXJ_16TO8(-98), DRXJ_16TO8(92)}, /* TARGET_MODE = 2, 3: PFI_A = -20/32; PFI_B = -49/32; PFI_C = 23/32; fg = 0.8 MHz (Att=25dB) */
+ {DRXJ_16TO8(-80), DRXJ_16TO8(-98), DRXJ_16TO8(92)} /* TARGET_MODE = 2, 3: PFI_A = -20/32; PFI_B = -49/32; PFI_C = 23/32; fg = 0.8 MHz (Att=25dB) */
+ };
+ u16 mode_index;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ mirror_freq_spect_oob = ext_attr->mirror_freq_spect_oob;
+
+ /* Check parameters */
+ if (oob_param == NULL) {
+ /* power off oob module */
+ scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB
+ | SCU_RAM_COMMAND_CMD_DEMOD_STOP;
+ scu_cmd.parameter_len = 0;
+ scu_cmd.result_len = 1;
+ scu_cmd.result = cmd_result;
+ rc = scu_command(dev_addr, &scu_cmd);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_orx_nsu_aox(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_COMM_EXEC__A, ORX_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ ext_attr->oob_power_on = false;
+ return 0;
+ }
+
+ freq = oob_param->frequency;
+ if ((freq < 70000) || (freq > 130000))
+ return -EIO;
+ freq = (freq - 50000) / 50;
+
+ {
+ u16 index = 0;
+ u16 remainder = 0;
+ u16 *trk_filtercfg = ext_attr->oob_trk_filter_cfg;
+
+ index = (u16) ((freq - 400) / 200);
+ remainder = (u16) ((freq - 400) % 200);
+ trk_filter_value =
+ trk_filtercfg[index] - (trk_filtercfg[index] -
+ trk_filtercfg[index +
+ 1]) / 10 * remainder /
+ 20;
+ }
+
+ /********/
+ /* Stop */
+ /********/
+ rc = drxj_dap_write_reg16(dev_addr, ORX_COMM_EXEC__A, ORX_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB
+ | SCU_RAM_COMMAND_CMD_DEMOD_STOP;
+ scu_cmd.parameter_len = 0;
+ scu_cmd.result_len = 1;
+ scu_cmd.result = cmd_result;
+ rc = scu_command(dev_addr, &scu_cmd);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /********/
+ /* Reset */
+ /********/
+ scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB
+ | SCU_RAM_COMMAND_CMD_DEMOD_RESET;
+ scu_cmd.parameter_len = 0;
+ scu_cmd.result_len = 1;
+ scu_cmd.result = cmd_result;
+ rc = scu_command(dev_addr, &scu_cmd);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /**********/
+ /* SET_ENV */
+ /**********/
+ /* set frequency, spectrum inversion and data rate */
+ scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB
+ | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV;
+ scu_cmd.parameter_len = 3;
+ /* 1-data rate;2-frequency */
+ switch (oob_param->standard) {
+ case DRX_OOB_MODE_A:
+ if (
+ /* signal is transmitted inverted */
+ ((oob_param->spectrum_inverted == true) &&
+ /* and tuner is not mirroring the signal */
+ (!mirror_freq_spect_oob)) |
+ /* or */
+ /* signal is transmitted noninverted */
+ ((oob_param->spectrum_inverted == false) &&
+ /* and tuner is mirroring the signal */
+ (mirror_freq_spect_oob))
+ )
+ set_param_parameters[0] =
+ SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_INVSPEC;
+ else
+ set_param_parameters[0] =
+ SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_REGSPEC;
+ break;
+ case DRX_OOB_MODE_B_GRADE_A:
+ if (
+ /* signal is transmitted inverted */
+ ((oob_param->spectrum_inverted == true) &&
+ /* and tuner is not mirroring the signal */
+ (!mirror_freq_spect_oob)) |
+ /* or */
+ /* signal is transmitted noninverted */
+ ((oob_param->spectrum_inverted == false) &&
+ /* and tuner is mirroring the signal */
+ (mirror_freq_spect_oob))
+ )
+ set_param_parameters[0] =
+ SCU_RAM_ORX_RF_RX_DATA_RATE_1544KBPS_INVSPEC;
+ else
+ set_param_parameters[0] =
+ SCU_RAM_ORX_RF_RX_DATA_RATE_1544KBPS_REGSPEC;
+ break;
+ case DRX_OOB_MODE_B_GRADE_B:
+ default:
+ if (
+ /* signal is transmitted inverted */
+ ((oob_param->spectrum_inverted == true) &&
+ /* and tuner is not mirroring the signal */
+ (!mirror_freq_spect_oob)) |
+ /* or */
+ /* signal is transmitted noninverted */
+ ((oob_param->spectrum_inverted == false) &&
+ /* and tuner is mirroring the signal */
+ (mirror_freq_spect_oob))
+ )
+ set_param_parameters[0] =
+ SCU_RAM_ORX_RF_RX_DATA_RATE_3088KBPS_INVSPEC;
+ else
+ set_param_parameters[0] =
+ SCU_RAM_ORX_RF_RX_DATA_RATE_3088KBPS_REGSPEC;
+ break;
+ }
+ set_param_parameters[1] = (u16) (freq & 0xFFFF);
+ set_param_parameters[2] = trk_filter_value;
+ scu_cmd.parameter = set_param_parameters;
+ scu_cmd.result_len = 1;
+ scu_cmd.result = cmd_result;
+ mode_index = mode_val[(set_param_parameters[0] & 0xC0) >> 6];
+ rc = scu_command(dev_addr, &scu_cmd);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0xFABA, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* Write magic word to enable pdr reg write */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_OOB_CRX_CFG__A, OOB_CRX_DRIVE_STRENGTH << SIO_PDR_OOB_CRX_CFG_DRIVE__B | 0x03 << SIO_PDR_OOB_CRX_CFG_MODE__B, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_OOB_DRX_CFG__A, OOB_DRX_DRIVE_STRENGTH << SIO_PDR_OOB_DRX_CFG_DRIVE__B | 0x03 << SIO_PDR_OOB_DRX_CFG_MODE__B, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0x0000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ } /* Write magic word to disable pdr reg write */
+
+ rc = drxj_dap_write_reg16(dev_addr, ORX_TOP_COMM_KEY__A, 0, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_AAG_LEN_W__A, 16000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_AAG_THR_W__A, 40, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* ddc */
+ rc = drxj_dap_write_reg16(dev_addr, ORX_DDC_OFO_SET_W__A, ORX_DDC_OFO_SET_W__PRE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* nsu */
+ rc = drxj_dap_write_reg16(dev_addr, ORX_NSU_AOX_LOPOW_W__A, ext_attr->oob_lo_pow, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* initialization for target mode */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TARGET_MODE__A, SCU_RAM_ORX_TARGET_MODE_2048KBPS_SQRT, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FREQ_GAIN_CORR__A, SCU_RAM_ORX_FREQ_GAIN_CORR_2048KBPS, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Reset bits for timing and freq. recovery */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_RST_CPH__A, 0x0001, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_RST_CTI__A, 0x0002, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_RST_KRN__A, 0x0004, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_RST_KRP__A, 0x0008, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* AGN_LOCK = {2048>>3, -2048, 8, -8, 0, 1}; */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_LOCK_TH__A, 2048 >> 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_LOCK_TOTH__A, (u16)(-2048), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_ONLOCK_TTH__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_UNLOCK_TTH__A, (u16)(-8), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_AGN_LOCK_MASK__A, 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* DGN_LOCK = {10, -2048, 8, -8, 0, 1<<1}; */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_LOCK_TH__A, 10, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_LOCK_TOTH__A, (u16)(-2048), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_ONLOCK_TTH__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_UNLOCK_TTH__A, (u16)(-8), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_DGN_LOCK_MASK__A, 1 << 1, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* FRQ_LOCK = {15,-2048, 8, -8, 0, 1<<2}; */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_LOCK_TH__A, 17, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_LOCK_TOTH__A, (u16)(-2048), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_ONLOCK_TTH__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_UNLOCK_TTH__A, (u16)(-8), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_FRQ_LOCK_MASK__A, 1 << 2, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* PHA_LOCK = {5000, -2048, 8, -8, 0, 1<<3}; */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_LOCK_TH__A, 3000, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_LOCK_TOTH__A, (u16)(-2048), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_ONLOCK_TTH__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_UNLOCK_TTH__A, (u16)(-8), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_PHA_LOCK_MASK__A, 1 << 3, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* TIM_LOCK = {300, -2048, 8, -8, 0, 1<<4}; */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_LOCK_TH__A, 400, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_LOCK_TOTH__A, (u16)(-2048), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_ONLOCK_TTH__A, 8, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_UNLOCK_TTH__A, (u16)(-8), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_TIM_LOCK_MASK__A, 1 << 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* EQU_LOCK = {20, -2048, 8, -8, 0, 1<<5}; */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_LOCK_TH__A, 20, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_LOCK_TOTH__A, (u16)(-2048), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_ONLOCK_TTH__A, 4, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_UNLOCK_TTH__A, (u16)(-4), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_ORX_EQU_LOCK_MASK__A, 1 << 5, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* PRE-Filter coefficients (PFI) */
+ rc = drxdap_fasi_write_block(dev_addr, ORX_FWP_PFI_A_W__A, sizeof(pfi_coeffs[mode_index]), ((u8 *)pfi_coeffs[mode_index]), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_TOP_MDE_W__A, mode_index, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* NYQUIST-Filter coefficients (NYQ) */
+ for (i = 0; i < (NYQFILTERLEN + 1) / 2; i++) {
+ rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_NYQ_ADR_W__A, i, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_NYQ_COF_RW__A, nyquist_coeffs[mode_index][i], 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_FWP_NYQ_ADR_W__A, 31, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_COMM_EXEC__A, ORX_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /********/
+ /* Start */
+ /********/
+ scu_cmd.command = SCU_RAM_COMMAND_STANDARD_OOB
+ | SCU_RAM_COMMAND_CMD_DEMOD_START;
+ scu_cmd.parameter_len = 0;
+ scu_cmd.result_len = 1;
+ scu_cmd.result = cmd_result;
+ rc = scu_command(dev_addr, &scu_cmd);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = set_orx_nsu_aox(demod, true);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, ORX_NSU_AOX_STHR_W__A, ext_attr->oob_pre_saw, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ ext_attr->oob_power_on = true;
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+/*== END OOB DATAPATH FUNCTIONS ==*/
+/*============================================================================*/
+
+/*=============================================================================
+ ===== MC command related functions ==========================================
+ ===========================================================================*/
+
+/*=============================================================================
+ ===== ctrl_set_channel() ==========================================================
+ ===========================================================================*/
+/*
+* \fn int ctrl_set_channel()
+* \brief Select a new transmission channel.
+* \param demod instance of demod.
+* \param channel Pointer to channel data.
+* \return int.
+*
+* In case the tuner module is not used and in case of NTSC/FM the pogrammer
+* must tune the tuner to the centre frequency of the NTSC/FM channel.
+*
+*/
+static int
+ctrl_set_channel(struct drx_demod_instance *demod, struct drx_channel *channel)
+{
+ int rc;
+ s32 tuner_freq_offset = 0;
+ struct drxj_data *ext_attr = NULL;
+ struct i2c_device_addr *dev_addr = NULL;
+ enum drx_standard standard = DRX_STANDARD_UNKNOWN;
+#ifndef DRXJ_VSB_ONLY
+ u32 min_symbol_rate = 0;
+ u32 max_symbol_rate = 0;
+ int bandwidth_temp = 0;
+ int bandwidth = 0;
+#endif
+ /*== check arguments ======================================================*/
+ if ((demod == NULL) || (channel == NULL))
+ return -EINVAL;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ standard = ext_attr->standard;
+
+ /* check valid standards */
+ switch (standard) {
+ case DRX_STANDARD_8VSB:
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+#endif /* DRXJ_VSB_ONLY */
+ break;
+ case DRX_STANDARD_UNKNOWN:
+ default:
+ return -EINVAL;
+ }
+
+ /* check bandwidth QAM annex B, NTSC and 8VSB */
+ if ((standard == DRX_STANDARD_ITU_B) ||
+ (standard == DRX_STANDARD_8VSB) ||
+ (standard == DRX_STANDARD_NTSC)) {
+ switch (channel->bandwidth) {
+ case DRX_BANDWIDTH_6MHZ:
+ case DRX_BANDWIDTH_UNKNOWN:
+ channel->bandwidth = DRX_BANDWIDTH_6MHZ;
+ break;
+ case DRX_BANDWIDTH_8MHZ:
+ case DRX_BANDWIDTH_7MHZ:
+ default:
+ return -EINVAL;
+ }
+ }
+
+ /* For QAM annex A and annex C:
+ -check symbolrate and constellation
+ -derive bandwidth from symbolrate (input bandwidth is ignored)
+ */
+#ifndef DRXJ_VSB_ONLY
+ if ((standard == DRX_STANDARD_ITU_A) ||
+ (standard == DRX_STANDARD_ITU_C)) {
+ struct drxuio_cfg uio_cfg = { DRX_UIO1, DRX_UIO_MODE_FIRMWARE_SAW };
+ int bw_rolloff_factor = 0;
+
+ bw_rolloff_factor = (standard == DRX_STANDARD_ITU_A) ? 115 : 113;
+ min_symbol_rate = DRXJ_QAM_SYMBOLRATE_MIN;
+ max_symbol_rate = DRXJ_QAM_SYMBOLRATE_MAX;
+ /* config SMA_TX pin to SAW switch mode */
+ rc = ctrl_set_uio_cfg(demod, &uio_cfg);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if (channel->symbolrate < min_symbol_rate ||
+ channel->symbolrate > max_symbol_rate) {
+ return -EINVAL;
+ }
+
+ switch (channel->constellation) {
+ case DRX_CONSTELLATION_QAM16:
+ case DRX_CONSTELLATION_QAM32:
+ case DRX_CONSTELLATION_QAM64:
+ case DRX_CONSTELLATION_QAM128:
+ case DRX_CONSTELLATION_QAM256:
+ bandwidth_temp = channel->symbolrate * bw_rolloff_factor;
+ bandwidth = bandwidth_temp / 100;
+
+ if ((bandwidth_temp % 100) >= 50)
+ bandwidth++;
+
+ if (bandwidth <= 6100000) {
+ channel->bandwidth = DRX_BANDWIDTH_6MHZ;
+ } else if ((bandwidth > 6100000)
+ && (bandwidth <= 7100000)) {
+ channel->bandwidth = DRX_BANDWIDTH_7MHZ;
+ } else if (bandwidth > 7100000) {
+ channel->bandwidth = DRX_BANDWIDTH_8MHZ;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ /* For QAM annex B:
+ -check constellation
+ */
+ if (standard == DRX_STANDARD_ITU_B) {
+ switch (channel->constellation) {
+ case DRX_CONSTELLATION_AUTO:
+ case DRX_CONSTELLATION_QAM256:
+ case DRX_CONSTELLATION_QAM64:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (channel->interleavemode) {
+ case DRX_INTERLEAVEMODE_I128_J1:
+ case DRX_INTERLEAVEMODE_I128_J1_V2:
+ case DRX_INTERLEAVEMODE_I128_J2:
+ case DRX_INTERLEAVEMODE_I64_J2:
+ case DRX_INTERLEAVEMODE_I128_J3:
+ case DRX_INTERLEAVEMODE_I32_J4:
+ case DRX_INTERLEAVEMODE_I128_J4:
+ case DRX_INTERLEAVEMODE_I16_J8:
+ case DRX_INTERLEAVEMODE_I128_J5:
+ case DRX_INTERLEAVEMODE_I8_J16:
+ case DRX_INTERLEAVEMODE_I128_J6:
+ case DRX_INTERLEAVEMODE_I128_J7:
+ case DRX_INTERLEAVEMODE_I128_J8:
+ case DRX_INTERLEAVEMODE_I12_J17:
+ case DRX_INTERLEAVEMODE_I5_J4:
+ case DRX_INTERLEAVEMODE_B52_M240:
+ case DRX_INTERLEAVEMODE_B52_M720:
+ case DRX_INTERLEAVEMODE_UNKNOWN:
+ case DRX_INTERLEAVEMODE_AUTO:
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ if ((ext_attr->uio_sma_tx_mode) == DRX_UIO_MODE_FIRMWARE_SAW) {
+ /* SAW SW, user UIO is used for switchable SAW */
+ struct drxuio_data uio1 = { DRX_UIO1, false };
+
+ switch (channel->bandwidth) {
+ case DRX_BANDWIDTH_8MHZ:
+ uio1.value = true;
+ break;
+ case DRX_BANDWIDTH_7MHZ:
+ uio1.value = false;
+ break;
+ case DRX_BANDWIDTH_6MHZ:
+ uio1.value = false;
+ break;
+ case DRX_BANDWIDTH_UNKNOWN:
+ default:
+ return -EINVAL;
+ }
+
+ rc = ctrl_uio_write(demod, &uio1);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+#endif /* DRXJ_VSB_ONLY */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ tuner_freq_offset = 0;
+
+ /*== Setup demod for specific standard ====================================*/
+ switch (standard) {
+ case DRX_STANDARD_8VSB:
+ if (channel->mirror == DRX_MIRROR_AUTO)
+ ext_attr->mirror = DRX_MIRROR_NO;
+ else
+ ext_attr->mirror = channel->mirror;
+ rc = set_vsb(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_frequency(demod, channel, tuner_freq_offset);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ rc = set_qam_channel(demod, channel, tuner_freq_offset);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+#endif
+ case DRX_STANDARD_UNKNOWN:
+ default:
+ return -EIO;
+ }
+
+ /* flag the packet error counter reset */
+ ext_attr->reset_pkt_err_acc = true;
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*=============================================================================
+ ===== SigQuality() ==========================================================
+ ===========================================================================*/
+
+/*
+* \fn int ctrl_sig_quality()
+* \brief Retrieve signal quality form device.
+* \param devmod Pointer to demodulator instance.
+* \param sig_quality Pointer to signal quality data.
+* \return int.
+* \retval 0 sig_quality contains valid data.
+* \retval -EINVAL sig_quality is NULL.
+* \retval -EIO Erroneous data, sig_quality contains invalid data.
+
+*/
+static int
+ctrl_sig_quality(struct drx_demod_instance *demod,
+ enum drx_lock_status lock_status)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ struct drxj_data *ext_attr = demod->my_ext_attr;
+ struct drx39xxj_state *state = dev_addr->user_data;
+ struct dtv_frontend_properties *p = &state->frontend.dtv_property_cache;
+ enum drx_standard standard = ext_attr->standard;
+ int rc;
+ u32 ber, cnt, err, pkt;
+ u16 mer, strength = 0;
+
+ rc = get_sig_strength(demod, &strength);
+ if (rc < 0) {
+ pr_err("error getting signal strength %d\n", rc);
+ p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ } else {
+ p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ p->strength.stat[0].uvalue = 65535UL * strength/ 100;
+ }
+
+ switch (standard) {
+ case DRX_STANDARD_8VSB:
+#ifdef DRXJ_SIGNAL_ACCUM_ERR
+ rc = get_acc_pkt_err(demod, &pkt);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+#endif
+ if (lock_status != DRXJ_DEMOD_LOCK && lock_status != DRX_LOCKED) {
+ p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ } else {
+ rc = get_vsb_post_rs_pck_err(dev_addr, &err, &pkt);
+ if (rc != 0) {
+ pr_err("error %d getting UCB\n", rc);
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ } else {
+ p->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->block_error.stat[0].uvalue += err;
+ p->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->block_count.stat[0].uvalue += pkt;
+ }
+
+ /* PostViterbi is compute in steps of 10^(-6) */
+ rc = get_vs_bpre_viterbi_ber(dev_addr, &ber, &cnt);
+ if (rc != 0) {
+ pr_err("error %d getting pre-ber\n", rc);
+ p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ } else {
+ p->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->pre_bit_error.stat[0].uvalue += ber;
+ p->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->pre_bit_count.stat[0].uvalue += cnt;
+ }
+
+ rc = get_vs_bpost_viterbi_ber(dev_addr, &ber, &cnt);
+ if (rc != 0) {
+ pr_err("error %d getting post-ber\n", rc);
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ } else {
+ p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_error.stat[0].uvalue += ber;
+ p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_count.stat[0].uvalue += cnt;
+ }
+ rc = get_vsbmer(dev_addr, &mer);
+ if (rc != 0) {
+ pr_err("error %d getting MER\n", rc);
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ } else {
+ p->cnr.stat[0].svalue = mer * 100;
+ p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ }
+ }
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ rc = ctrl_get_qam_sig_quality(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+#endif
+ default:
+ return -EIO;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+
+/*
+* \fn int ctrl_lock_status()
+* \brief Retrieve lock status .
+* \param dev_addr Pointer to demodulator device address.
+* \param lock_stat Pointer to lock status structure.
+* \return int.
+*
+*/
+static int
+ctrl_lock_status(struct drx_demod_instance *demod, enum drx_lock_status *lock_stat)
+{
+ enum drx_standard standard = DRX_STANDARD_UNKNOWN;
+ struct drxj_data *ext_attr = NULL;
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxjscu_cmd cmd_scu = { /* command */ 0,
+ /* parameter_len */ 0,
+ /* result_len */ 0,
+ /* *parameter */ NULL,
+ /* *result */ NULL
+ };
+ int rc;
+ u16 cmd_result[2] = { 0, 0 };
+ u16 demod_lock = SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_DEMOD_LOCKED;
+
+ /* check arguments */
+ if ((demod == NULL) || (lock_stat == NULL))
+ return -EINVAL;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ standard = ext_attr->standard;
+
+ *lock_stat = DRX_NOT_LOCKED;
+
+ /* define the SCU command code */
+ switch (standard) {
+ case DRX_STANDARD_8VSB:
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_VSB |
+ SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK;
+ demod_lock |= 0x6;
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ cmd_scu.command = SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK;
+ break;
+#endif
+ case DRX_STANDARD_UNKNOWN:
+ default:
+ return -EIO;
+ }
+
+ /* define the SCU command parameters and execute the command */
+ cmd_scu.parameter_len = 0;
+ cmd_scu.result_len = 2;
+ cmd_scu.parameter = NULL;
+ cmd_scu.result = cmd_result;
+ rc = scu_command(dev_addr, &cmd_scu);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* set the lock status */
+ if (cmd_scu.result[1] < demod_lock) {
+ /* 0x0000 NOT LOCKED */
+ *lock_stat = DRX_NOT_LOCKED;
+ } else if (cmd_scu.result[1] < SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_LOCKED) {
+ *lock_stat = DRXJ_DEMOD_LOCK;
+ } else if (cmd_scu.result[1] <
+ SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_NEVER_LOCK) {
+ /* 0x8000 DEMOD + FEC LOCKED (system lock) */
+ *lock_stat = DRX_LOCKED;
+ } else {
+ /* 0xC000 NEVER LOCKED */
+ /* (system will never be able to lock to the signal) */
+ *lock_stat = DRX_NEVER_LOCK;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+
+/*
+* \fn int ctrl_set_standard()
+* \brief Set modulation standard to be used.
+* \param standard Modulation standard.
+* \return int.
+*
+* Setup stuff for the desired demodulation standard.
+* Disable and power down the previous selected demodulation standard
+*
+*/
+static int
+ctrl_set_standard(struct drx_demod_instance *demod, enum drx_standard *standard)
+{
+ struct drxj_data *ext_attr = NULL;
+ int rc;
+ enum drx_standard prev_standard;
+
+ /* check arguments */
+ if ((standard == NULL) || (demod == NULL))
+ return -EINVAL;
+
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ prev_standard = ext_attr->standard;
+
+ /*
+ Stop and power down previous standard
+ */
+ switch (prev_standard) {
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ rc = power_down_qam(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+#endif
+ case DRX_STANDARD_8VSB:
+ rc = power_down_vsb(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_STANDARD_UNKNOWN:
+ /* Do nothing */
+ break;
+ case DRX_STANDARD_AUTO:
+ default:
+ rc = -EINVAL;
+ goto rw_error;
+ }
+
+ /*
+ Initialize channel independent registers
+ Power up new standard
+ */
+ ext_attr->standard = *standard;
+
+ switch (*standard) {
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ do {
+ u16 dummy;
+ rc = drxj_dap_read_reg16(demod->my_i2c_dev_addr, SCU_RAM_VERSION_HI__A, &dummy, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ } while (0);
+ break;
+#endif
+ case DRX_STANDARD_8VSB:
+ rc = set_vsb_leak_n_gain(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ default:
+ ext_attr->standard = DRX_STANDARD_UNKNOWN;
+ return -EINVAL;
+ }
+
+ return 0;
+rw_error:
+ /* Don't know what the standard is now ... try again */
+ ext_attr->standard = DRX_STANDARD_UNKNOWN;
+ return rc;
+}
+
+/*============================================================================*/
+
+static void drxj_reset_mode(struct drxj_data *ext_attr)
+{
+ /* Initialize default AFE configuration for QAM */
+ if (ext_attr->has_lna) {
+ /* IF AGC off, PGA active */
+#ifndef DRXJ_VSB_ONLY
+ ext_attr->qam_if_agc_cfg.standard = DRX_STANDARD_ITU_B;
+ ext_attr->qam_if_agc_cfg.ctrl_mode = DRX_AGC_CTRL_OFF;
+ ext_attr->qam_pga_cfg = 140 + (11 * 13);
+#endif
+ ext_attr->vsb_if_agc_cfg.standard = DRX_STANDARD_8VSB;
+ ext_attr->vsb_if_agc_cfg.ctrl_mode = DRX_AGC_CTRL_OFF;
+ ext_attr->vsb_pga_cfg = 140 + (11 * 13);
+ } else {
+ /* IF AGC on, PGA not active */
+#ifndef DRXJ_VSB_ONLY
+ ext_attr->qam_if_agc_cfg.standard = DRX_STANDARD_ITU_B;
+ ext_attr->qam_if_agc_cfg.ctrl_mode = DRX_AGC_CTRL_AUTO;
+ ext_attr->qam_if_agc_cfg.min_output_level = 0;
+ ext_attr->qam_if_agc_cfg.max_output_level = 0x7FFF;
+ ext_attr->qam_if_agc_cfg.speed = 3;
+ ext_attr->qam_if_agc_cfg.top = 1297;
+ ext_attr->qam_pga_cfg = 140;
+#endif
+ ext_attr->vsb_if_agc_cfg.standard = DRX_STANDARD_8VSB;
+ ext_attr->vsb_if_agc_cfg.ctrl_mode = DRX_AGC_CTRL_AUTO;
+ ext_attr->vsb_if_agc_cfg.min_output_level = 0;
+ ext_attr->vsb_if_agc_cfg.max_output_level = 0x7FFF;
+ ext_attr->vsb_if_agc_cfg.speed = 3;
+ ext_attr->vsb_if_agc_cfg.top = 1024;
+ ext_attr->vsb_pga_cfg = 140;
+ }
+ /* TODO: remove min_output_level and max_output_level for both QAM and VSB after */
+ /* mc has not used them */
+#ifndef DRXJ_VSB_ONLY
+ ext_attr->qam_rf_agc_cfg.standard = DRX_STANDARD_ITU_B;
+ ext_attr->qam_rf_agc_cfg.ctrl_mode = DRX_AGC_CTRL_AUTO;
+ ext_attr->qam_rf_agc_cfg.min_output_level = 0;
+ ext_attr->qam_rf_agc_cfg.max_output_level = 0x7FFF;
+ ext_attr->qam_rf_agc_cfg.speed = 3;
+ ext_attr->qam_rf_agc_cfg.top = 9500;
+ ext_attr->qam_rf_agc_cfg.cut_off_current = 4000;
+ ext_attr->qam_pre_saw_cfg.standard = DRX_STANDARD_ITU_B;
+ ext_attr->qam_pre_saw_cfg.reference = 0x07;
+ ext_attr->qam_pre_saw_cfg.use_pre_saw = true;
+#endif
+ /* Initialize default AFE configuration for VSB */
+ ext_attr->vsb_rf_agc_cfg.standard = DRX_STANDARD_8VSB;
+ ext_attr->vsb_rf_agc_cfg.ctrl_mode = DRX_AGC_CTRL_AUTO;
+ ext_attr->vsb_rf_agc_cfg.min_output_level = 0;
+ ext_attr->vsb_rf_agc_cfg.max_output_level = 0x7FFF;
+ ext_attr->vsb_rf_agc_cfg.speed = 3;
+ ext_attr->vsb_rf_agc_cfg.top = 9500;
+ ext_attr->vsb_rf_agc_cfg.cut_off_current = 4000;
+ ext_attr->vsb_pre_saw_cfg.standard = DRX_STANDARD_8VSB;
+ ext_attr->vsb_pre_saw_cfg.reference = 0x07;
+ ext_attr->vsb_pre_saw_cfg.use_pre_saw = true;
+}
+
+/*
+* \fn int ctrl_power_mode()
+* \brief Set the power mode of the device to the specified power mode
+* \param demod Pointer to demodulator instance.
+* \param mode Pointer to new power mode.
+* \return int.
+* \retval 0 Success
+* \retval -EIO I2C error or other failure
+* \retval -EINVAL Invalid mode argument.
+*
+*
+*/
+static int
+ctrl_power_mode(struct drx_demod_instance *demod, enum drx_power_mode *mode)
+{
+ struct drx_common_attr *common_attr = (struct drx_common_attr *) NULL;
+ struct drxj_data *ext_attr = (struct drxj_data *) NULL;
+ struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)NULL;
+ int rc;
+ u16 sio_cc_pwd_mode = 0;
+
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ dev_addr = demod->my_i2c_dev_addr;
+
+ /* Check arguments */
+ if (mode == NULL)
+ return -EINVAL;
+
+ /* If already in requested power mode, do nothing */
+ if (common_attr->current_power_mode == *mode)
+ return 0;
+
+ switch (*mode) {
+ case DRX_POWER_UP:
+ case DRXJ_POWER_DOWN_MAIN_PATH:
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_NONE;
+ break;
+ case DRXJ_POWER_DOWN_CORE:
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_CLOCK;
+ break;
+ case DRXJ_POWER_DOWN_PLL:
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_PLL;
+ break;
+ case DRX_POWER_DOWN:
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_OSC;
+ break;
+ default:
+ /* Unknown sleep mode */
+ return -EINVAL;
+ }
+
+ /* Check if device needs to be powered up */
+ if ((common_attr->current_power_mode != DRX_POWER_UP)) {
+ rc = power_up_device(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ if (*mode == DRX_POWER_UP) {
+ /* Restore analog & pin configuration */
+
+ /* Initialize default AFE configuration for VSB */
+ drxj_reset_mode(ext_attr);
+ } else {
+ /* Power down to requested mode */
+ /* Backup some register settings */
+ /* Set pins with possible pull-ups connected to them in input mode */
+ /* Analog power down */
+ /* ADC power down */
+ /* Power down device */
+ /* stop all comm_exec */
+ /*
+ Stop and power down previous standard
+ */
+
+ switch (ext_attr->standard) {
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ rc = power_down_qam(demod, true);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_STANDARD_8VSB:
+ rc = power_down_vsb(demod, true);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_STANDARD_PAL_SECAM_BG:
+ case DRX_STANDARD_PAL_SECAM_DK:
+ case DRX_STANDARD_PAL_SECAM_I:
+ case DRX_STANDARD_PAL_SECAM_L:
+ case DRX_STANDARD_PAL_SECAM_LP:
+ case DRX_STANDARD_NTSC:
+ case DRX_STANDARD_FM:
+ rc = power_down_atv(demod, ext_attr->standard, true);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ break;
+ case DRX_STANDARD_UNKNOWN:
+ /* Do nothing */
+ break;
+ case DRX_STANDARD_AUTO:
+ default:
+ return -EIO;
+ }
+ ext_attr->standard = DRX_STANDARD_UNKNOWN;
+ }
+
+ if (*mode != DRXJ_POWER_DOWN_MAIN_PATH) {
+ rc = drxj_dap_write_reg16(dev_addr, SIO_CC_PWD_MODE__A, sio_cc_pwd_mode, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ if ((*mode != DRX_POWER_UP)) {
+ /* Initialize HI, wakeup key especially before put IC to sleep */
+ rc = init_hi(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ ext_attr->hi_cfg_ctrl |= SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+ rc = hi_cfg_command(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+ }
+
+ common_attr->current_power_mode = *mode;
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+/*== CTRL Set/Get Config related functions ===================================*/
+/*============================================================================*/
+
+/*
+* \fn int ctrl_set_cfg_pre_saw()
+* \brief Set Pre-saw reference.
+* \param demod demod instance
+* \param u16 *
+* \return int.
+*
+* Check arguments
+* Dispatch handling to standard specific function.
+*
+*/
+static int
+ctrl_set_cfg_pre_saw(struct drx_demod_instance *demod, struct drxj_cfg_pre_saw *pre_saw)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ int rc;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ /* check arguments */
+ if ((pre_saw == NULL) || (pre_saw->reference > IQM_AF_PDREF__M)
+ ) {
+ return -EINVAL;
+ }
+
+ /* Only if standard is currently active */
+ if ((ext_attr->standard == pre_saw->standard) ||
+ (DRXJ_ISQAMSTD(ext_attr->standard) &&
+ DRXJ_ISQAMSTD(pre_saw->standard)) ||
+ (DRXJ_ISATVSTD(ext_attr->standard) &&
+ DRXJ_ISATVSTD(pre_saw->standard))) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_PDREF__A, pre_saw->reference, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* Store pre-saw settings */
+ switch (pre_saw->standard) {
+ case DRX_STANDARD_8VSB:
+ ext_attr->vsb_pre_saw_cfg = *pre_saw;
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ ext_attr->qam_pre_saw_cfg = *pre_saw;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+
+/*
+* \fn int ctrl_set_cfg_afe_gain()
+* \brief Set AFE Gain.
+* \param demod demod instance
+* \param u16 *
+* \return int.
+*
+* Check arguments
+* Dispatch handling to standard specific function.
+*
+*/
+static int
+ctrl_set_cfg_afe_gain(struct drx_demod_instance *demod, struct drxj_cfg_afe_gain *afe_gain)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ int rc;
+ u8 gain = 0;
+
+ /* check arguments */
+ if (afe_gain == NULL)
+ return -EINVAL;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+
+ switch (afe_gain->standard) {
+ case DRX_STANDARD_8VSB: fallthrough;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+#endif
+ /* Do nothing */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* TODO PGA gain is also written by microcode (at least by QAM and VSB)
+ So I (PJ) think interface requires choice between auto, user mode */
+
+ if (afe_gain->gain >= 329)
+ gain = 15;
+ else if (afe_gain->gain <= 147)
+ gain = 0;
+ else
+ gain = (afe_gain->gain - 140 + 6) / 13;
+
+ /* Only if standard is currently active */
+ if (ext_attr->standard == afe_gain->standard) {
+ rc = drxj_dap_write_reg16(dev_addr, IQM_AF_PGA_GAIN__A, gain, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ }
+
+ /* Store AFE Gain settings */
+ switch (afe_gain->standard) {
+ case DRX_STANDARD_8VSB:
+ ext_attr->vsb_pga_cfg = gain * 13 + 140;
+ break;
+#ifndef DRXJ_VSB_ONLY
+ case DRX_STANDARD_ITU_A:
+ case DRX_STANDARD_ITU_B:
+ case DRX_STANDARD_ITU_C:
+ ext_attr->qam_pga_cfg = gain * 13 + 140;
+ break;
+#endif
+ default:
+ return -EIO;
+ }
+
+ return 0;
+rw_error:
+ return rc;
+}
+
+/*============================================================================*/
+
+
+/*=============================================================================
+===== EXPORTED FUNCTIONS ====================================================*/
+
+static int drx_ctrl_u_code(struct drx_demod_instance *demod,
+ struct drxu_code_info *mc_info,
+ enum drxu_code_action action);
+static int drxj_set_lna_state(struct drx_demod_instance *demod, bool state);
+
+/*
+* \fn drxj_open()
+* \brief Open the demod instance, configure device, configure drxdriver
+* \return Status_t Return status.
+*
+* drxj_open() can be called with a NULL ucode image => no ucode upload.
+* This means that drxj_open() must NOT contain SCU commands or, in general,
+* rely on SCU or AUD ucode to be present.
+*
+*/
+
+static int drxj_open(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = NULL;
+ struct drxj_data *ext_attr = NULL;
+ struct drx_common_attr *common_attr = NULL;
+ u32 driver_version = 0;
+ struct drxu_code_info ucode_info;
+ struct drx_cfg_mpeg_output cfg_mpeg_output;
+ int rc;
+ enum drx_power_mode power_mode = DRX_POWER_UP;
+
+ if ((demod == NULL) ||
+ (demod->my_common_attr == NULL) ||
+ (demod->my_ext_attr == NULL) ||
+ (demod->my_i2c_dev_addr == NULL) ||
+ (demod->my_common_attr->is_opened)) {
+ return -EINVAL;
+ }
+
+ /* Check arguments */
+ if (demod->my_ext_attr == NULL)
+ return -EINVAL;
+
+ dev_addr = demod->my_i2c_dev_addr;
+ ext_attr = (struct drxj_data *) demod->my_ext_attr;
+ common_attr = (struct drx_common_attr *) demod->my_common_attr;
+
+ rc = ctrl_power_mode(demod, &power_mode);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ if (power_mode != DRX_POWER_UP) {
+ rc = -EINVAL;
+ pr_err("failed to powerup device\n");
+ goto rw_error;
+ }
+
+ /* has to be in front of setIqmAf and setOrxNsuAox */
+ rc = get_device_capabilities(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /*
+ * Soft reset of sys- and osc-clockdomain
+ *
+ * HACK: On windows, it writes a 0x07 here, instead of just 0x03.
+ * As we didn't load the firmware here yet, we should do the same.
+ * Btw, this is coherent with DRX-K, where we send reset codes
+ * for modulation (OFTM, in DRX-k), SYS and OSC clock domains.
+ */
+ rc = drxj_dap_write_reg16(dev_addr, SIO_CC_SOFT_RST__A, (0x04 | SIO_CC_SOFT_RST_SYS__M | SIO_CC_SOFT_RST_OSC__M), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ msleep(1);
+
+ /* TODO first make sure that everything keeps working before enabling this */
+ /* PowerDownAnalogBlocks() */
+ rc = drxj_dap_write_reg16(dev_addr, ATV_TOP_STDBY__A, (~ATV_TOP_STDBY_CVBS_STDBY_A2_ACTIVE) | ATV_TOP_STDBY_SIF_STDBY_STANDBY, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = set_iqm_af(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = set_orx_nsu_aox(demod, false);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = init_hi(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* disable mpegoutput pins */
+ memcpy(&cfg_mpeg_output, &common_attr->mpeg_cfg, sizeof(cfg_mpeg_output));
+ cfg_mpeg_output.enable_mpeg_output = false;
+
+ rc = ctrl_set_cfg_mpeg_output(demod, &cfg_mpeg_output);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Stop AUD Inform SetAudio it will need to do all setting */
+ rc = power_down_aud(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ /* Stop SCU */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_COMM_EXEC__A, SCU_COMM_EXEC_STOP, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Upload microcode */
+ if (common_attr->microcode_file != NULL) {
+ /* Dirty trick to use common ucode upload & verify,
+ pretend device is already open */
+ common_attr->is_opened = true;
+ ucode_info.mc_file = common_attr->microcode_file;
+
+ if (DRX_ISPOWERDOWNMODE(demod->my_common_attr->current_power_mode)) {
+ pr_err("Should powerup before loading the firmware.");
+ rc = -EINVAL;
+ goto rw_error;
+ }
+
+ rc = drx_ctrl_u_code(demod, &ucode_info, UCODE_UPLOAD);
+ if (rc != 0) {
+ pr_err("error %d while uploading the firmware\n", rc);
+ goto rw_error;
+ }
+ if (common_attr->verify_microcode == true) {
+ rc = drx_ctrl_u_code(demod, &ucode_info, UCODE_VERIFY);
+ if (rc != 0) {
+ pr_err("error %d while verifying the firmware\n",
+ rc);
+ goto rw_error;
+ }
+ }
+ common_attr->is_opened = false;
+ }
+
+ /* Run SCU for a little while to initialize microcode version numbers */
+ rc = drxj_dap_write_reg16(dev_addr, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Initialize scan timeout */
+ common_attr->scan_demod_lock_timeout = DRXJ_SCAN_TIMEOUT;
+ common_attr->scan_desired_lock = DRX_LOCKED;
+
+ drxj_reset_mode(ext_attr);
+ ext_attr->standard = DRX_STANDARD_UNKNOWN;
+
+ rc = smart_ant_init(demod);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* Stamp driver version number in SCU data RAM in BCD code
+ Done to enable field application engineers to retrieve drxdriver version
+ via I2C from SCU RAM
+ */
+ driver_version = (VERSION_MAJOR / 100) % 10;
+ driver_version <<= 4;
+ driver_version += (VERSION_MAJOR / 10) % 10;
+ driver_version <<= 4;
+ driver_version += (VERSION_MAJOR % 10);
+ driver_version <<= 4;
+ driver_version += (VERSION_MINOR % 10);
+ driver_version <<= 4;
+ driver_version += (VERSION_PATCH / 1000) % 10;
+ driver_version <<= 4;
+ driver_version += (VERSION_PATCH / 100) % 10;
+ driver_version <<= 4;
+ driver_version += (VERSION_PATCH / 10) % 10;
+ driver_version <<= 4;
+ driver_version += (VERSION_PATCH % 10);
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_DRIVER_VER_HI__A, (u16)(driver_version >> 16), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ rc = drxj_dap_write_reg16(dev_addr, SCU_RAM_DRIVER_VER_LO__A, (u16)(driver_version & 0xFFFF), 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = ctrl_set_oob(demod, NULL);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ /* refresh the audio data structure with default */
+ ext_attr->aud_data = drxj_default_aud_data_g;
+
+ demod->my_common_attr->is_opened = true;
+ drxj_set_lna_state(demod, false);
+ return 0;
+rw_error:
+ common_attr->is_opened = false;
+ return rc;
+}
+
+/*============================================================================*/
+/*
+* \fn drxj_close()
+* \brief Close the demod instance, power down the device
+* \return Status_t Return status.
+*
+*/
+static int drxj_close(struct drx_demod_instance *demod)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ enum drx_power_mode power_mode = DRX_POWER_UP;
+
+ if ((demod->my_common_attr == NULL) ||
+ (demod->my_ext_attr == NULL) ||
+ (demod->my_i2c_dev_addr == NULL) ||
+ (!demod->my_common_attr->is_opened)) {
+ return -EINVAL;
+ }
+
+ /* power up */
+ rc = ctrl_power_mode(demod, &power_mode);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ rc = drxj_dap_write_reg16(dev_addr, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE, 0);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+ power_mode = DRX_POWER_DOWN;
+ rc = ctrl_power_mode(demod, &power_mode);
+ if (rc != 0) {
+ pr_err("error %d\n", rc);
+ goto rw_error;
+ }
+
+ DRX_ATTR_ISOPENED(demod) = false;
+
+ return 0;
+rw_error:
+ DRX_ATTR_ISOPENED(demod) = false;
+
+ return rc;
+}
+
+/*
+ * Microcode related functions
+ */
+
+/*
+ * drx_u_code_compute_crc - Compute CRC of block of microcode data.
+ * @block_data: Pointer to microcode data.
+ * @nr_words: Size of microcode block (number of 16 bits words).
+ *
+ * returns The computed CRC residue.
+ */
+static u16 drx_u_code_compute_crc(u8 *block_data, u16 nr_words)
+{
+ u16 i = 0;
+ u16 j = 0;
+ u32 crc_word = 0;
+ u32 carry = 0;
+
+ while (i < nr_words) {
+ crc_word |= (u32)be16_to_cpu(*(__be16 *)(block_data));
+ for (j = 0; j < 16; j++) {
+ crc_word <<= 1;
+ if (carry != 0)
+ crc_word ^= 0x80050000UL;
+ carry = crc_word & 0x80000000UL;
+ }
+ i++;
+ block_data += (sizeof(u16));
+ }
+ return (u16)(crc_word >> 16);
+}
+
+/*
+ * drx_check_firmware - checks if the loaded firmware is valid
+ *
+ * @demod: demod structure
+ * @mc_data: pointer to the start of the firmware
+ * @size: firmware size
+ */
+static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data,
+ unsigned size)
+{
+ struct drxu_code_block_hdr block_hdr;
+ int i;
+ unsigned count = 2 * sizeof(u16);
+ u32 mc_dev_type, mc_version, mc_base_version;
+ u16 mc_nr_of_blks = be16_to_cpu(*(__be16 *)(mc_data + sizeof(u16)));
+
+ /*
+ * Scan microcode blocks first for version info
+ * and firmware check
+ */
+
+ /* Clear version block */
+ DRX_ATTR_MCRECORD(demod).aux_type = 0;
+ DRX_ATTR_MCRECORD(demod).mc_dev_type = 0;
+ DRX_ATTR_MCRECORD(demod).mc_version = 0;
+ DRX_ATTR_MCRECORD(demod).mc_base_version = 0;
+
+ for (i = 0; i < mc_nr_of_blks; i++) {
+ if (count + 3 * sizeof(u16) + sizeof(u32) > size)
+ goto eof;
+
+ /* Process block header */
+ block_hdr.addr = be32_to_cpu(*(__be32 *)(mc_data + count));
+ count += sizeof(u32);
+ block_hdr.size = be16_to_cpu(*(__be16 *)(mc_data + count));
+ count += sizeof(u16);
+ block_hdr.flags = be16_to_cpu(*(__be16 *)(mc_data + count));
+ count += sizeof(u16);
+ block_hdr.CRC = be16_to_cpu(*(__be16 *)(mc_data + count));
+ count += sizeof(u16);
+
+ pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n",
+ count, block_hdr.addr, block_hdr.size, block_hdr.flags,
+ block_hdr.CRC);
+
+ if (block_hdr.flags & 0x8) {
+ u8 *auxblk = ((void *)mc_data) + block_hdr.addr;
+ u16 auxtype;
+
+ if (block_hdr.addr + sizeof(u16) > size)
+ goto eof;
+
+ auxtype = be16_to_cpu(*(__be16 *)(auxblk));
+
+ /* Aux block. Check type */
+ if (DRX_ISMCVERTYPE(auxtype)) {
+ if (block_hdr.addr + 2 * sizeof(u16) + 2 * sizeof (u32) > size)
+ goto eof;
+
+ auxblk += sizeof(u16);
+ mc_dev_type = be32_to_cpu(*(__be32 *)(auxblk));
+ auxblk += sizeof(u32);
+ mc_version = be32_to_cpu(*(__be32 *)(auxblk));
+ auxblk += sizeof(u32);
+ mc_base_version = be32_to_cpu(*(__be32 *)(auxblk));
+
+ DRX_ATTR_MCRECORD(demod).aux_type = auxtype;
+ DRX_ATTR_MCRECORD(demod).mc_dev_type = mc_dev_type;
+ DRX_ATTR_MCRECORD(demod).mc_version = mc_version;
+ DRX_ATTR_MCRECORD(demod).mc_base_version = mc_base_version;
+
+ pr_info("Firmware dev %x, ver %x, base ver %x\n",
+ mc_dev_type, mc_version, mc_base_version);
+
+ }
+ } else if (count + block_hdr.size * sizeof(u16) > size)
+ goto eof;
+
+ count += block_hdr.size * sizeof(u16);
+ }
+ return 0;
+eof:
+ pr_err("Firmware is truncated at pos %u/%u\n", count, size);
+ return -EINVAL;
+}
+
+/*
+ * drx_ctrl_u_code - Handle microcode upload or verify.
+ * @dev_addr: Address of device.
+ * @mc_info: Pointer to information about microcode data.
+ * @action: Either UCODE_UPLOAD or UCODE_VERIFY
+ *
+ * This function returns:
+ * 0:
+ * - In case of UCODE_UPLOAD: code is successfully uploaded.
+ * - In case of UCODE_VERIFY: image on device is equal to
+ * image provided to this control function.
+ * -EIO:
+ * - In case of UCODE_UPLOAD: I2C error.
+ * - In case of UCODE_VERIFY: I2C error or image on device
+ * is not equal to image provided to this control function.
+ * -EINVAL:
+ * - Invalid arguments.
+ * - Provided image is corrupt
+ */
+static int drx_ctrl_u_code(struct drx_demod_instance *demod,
+ struct drxu_code_info *mc_info,
+ enum drxu_code_action action)
+{
+ struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
+ int rc;
+ u16 i = 0;
+ u16 mc_nr_of_blks = 0;
+ u16 mc_magic_word = 0;
+ const u8 *mc_data_init = NULL;
+ u8 *mc_data = NULL;
+ unsigned size;
+ char *mc_file;
+ const struct firmware *fw;
+
+ /* Check arguments */
+ if (!mc_info || !mc_info->mc_file)
+ return -EINVAL;
+
+ mc_file = mc_info->mc_file;
+
+ rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent);
+ if (rc < 0) {
+ pr_err("Couldn't read firmware %s\n", mc_file);
+ return rc;
+ }
+
+ if (fw->size < 2 * sizeof(u16)) {
+ rc = -EINVAL;
+ pr_err("Firmware is too short!\n");
+ goto release;
+ }
+
+ pr_info("Firmware %s, size %zu\n", mc_file, fw->size);
+
+ mc_data_init = fw->data;
+ size = fw->size;
+
+ mc_data = (void *)mc_data_init;
+ /* Check data */
+ mc_magic_word = be16_to_cpu(*(__be16 *)(mc_data));
+ mc_data += sizeof(u16);
+ mc_nr_of_blks = be16_to_cpu(*(__be16 *)(mc_data));
+ mc_data += sizeof(u16);
+
+ if ((mc_magic_word != DRX_UCODE_MAGIC_WORD) || (mc_nr_of_blks == 0)) {
+ rc = -EINVAL;
+ pr_err("Firmware magic word doesn't match\n");
+ goto release;
+ }
+
+ if (action == UCODE_UPLOAD) {
+ rc = drx_check_firmware(demod, (u8 *)mc_data_init, size);
+ if (rc)
+ goto release;
+ pr_info("Uploading firmware %s\n", mc_file);
+ } else {
+ pr_info("Verifying if firmware upload was ok.\n");
+ }
+
+ /* Process microcode blocks */
+ for (i = 0; i < mc_nr_of_blks; i++) {
+ struct drxu_code_block_hdr block_hdr;
+ u16 mc_block_nr_bytes = 0;
+
+ /* Process block header */
+ block_hdr.addr = be32_to_cpu(*(__be32 *)(mc_data));
+ mc_data += sizeof(u32);
+ block_hdr.size = be16_to_cpu(*(__be16 *)(mc_data));
+ mc_data += sizeof(u16);
+ block_hdr.flags = be16_to_cpu(*(__be16 *)(mc_data));
+ mc_data += sizeof(u16);
+ block_hdr.CRC = be16_to_cpu(*(__be16 *)(mc_data));
+ mc_data += sizeof(u16);
+
+ pr_debug("%zd: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n",
+ (mc_data - mc_data_init), block_hdr.addr,
+ block_hdr.size, block_hdr.flags, block_hdr.CRC);
+
+ /* Check block header on:
+ - data larger than 64Kb
+ - if CRC enabled check CRC
+ */
+ if ((block_hdr.size > 0x7FFF) ||
+ (((block_hdr.flags & DRX_UCODE_CRC_FLAG) != 0) &&
+ (block_hdr.CRC != drx_u_code_compute_crc(mc_data, block_hdr.size)))
+ ) {
+ /* Wrong data ! */
+ rc = -EINVAL;
+ pr_err("firmware CRC is wrong\n");
+ goto release;
+ }
+
+ if (!block_hdr.size)
+ continue;
+
+ mc_block_nr_bytes = block_hdr.size * ((u16) sizeof(u16));
+
+ /* Perform the desired action */
+ switch (action) {
+ case UCODE_UPLOAD: /* Upload microcode */
+ if (drxdap_fasi_write_block(dev_addr,
+ block_hdr.addr,
+ mc_block_nr_bytes,
+ mc_data, 0x0000)) {
+ rc = -EIO;
+ pr_err("error writing firmware at pos %zd\n",
+ mc_data - mc_data_init);
+ goto release;
+ }
+ break;
+ case UCODE_VERIFY: { /* Verify uploaded microcode */
+ int result = 0;
+ u8 mc_data_buffer[DRX_UCODE_MAX_BUF_SIZE];
+ u32 bytes_to_comp = 0;
+ u32 bytes_left = mc_block_nr_bytes;
+ u32 curr_addr = block_hdr.addr;
+ u8 *curr_ptr = mc_data;
+
+ while (bytes_left != 0) {
+ if (bytes_left > DRX_UCODE_MAX_BUF_SIZE)
+ bytes_to_comp = DRX_UCODE_MAX_BUF_SIZE;
+ else
+ bytes_to_comp = bytes_left;
+
+ if (drxdap_fasi_read_block(dev_addr,
+ curr_addr,
+ (u16)bytes_to_comp,
+ (u8 *)mc_data_buffer,
+ 0x0000)) {
+ pr_err("error reading firmware at pos %zd\n",
+ mc_data - mc_data_init);
+ rc = -EIO;
+ goto release;
+ }
+
+ result = memcmp(curr_ptr, mc_data_buffer,
+ bytes_to_comp);
+
+ if (result) {
+ pr_err("error verifying firmware at pos %zd\n",
+ mc_data - mc_data_init);
+ rc = -EIO;
+ goto release;
+ }
+
+ curr_addr += ((dr_xaddr_t)(bytes_to_comp / 2));
+ curr_ptr =&(curr_ptr[bytes_to_comp]);
+ bytes_left -=((u32) bytes_to_comp);
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ goto release;
+
+ }
+ mc_data += mc_block_nr_bytes;
+ }
+
+ rc = 0;
+
+release:
+ release_firmware(fw);
+
+ return rc;
+}
+
+/* caller is expected to check if lna is supported before enabling */
+static int drxj_set_lna_state(struct drx_demod_instance *demod, bool state)
+{
+ struct drxuio_cfg uio_cfg;
+ struct drxuio_data uio_data;
+ int result;
+
+ uio_cfg.uio = DRX_UIO1;
+ uio_cfg.mode = DRX_UIO_MODE_READWRITE;
+ /* Configure user-I/O #3: enable read/write */
+ result = ctrl_set_uio_cfg(demod, &uio_cfg);
+ if (result) {
+ pr_err("Failed to setup LNA GPIO!\n");
+ return result;
+ }
+
+ uio_data.uio = DRX_UIO1;
+ uio_data.value = state;
+ result = ctrl_uio_write(demod, &uio_data);
+ if (result != 0) {
+ pr_err("Failed to %sable LNA!\n",
+ state ? "en" : "dis");
+ return result;
+ }
+ return 0;
+}
+
+/*
+ * The Linux DVB Driver for Micronas DRX39xx family (drx3933j)
+ *
+ * Written by Devin Heitmueller <devin.heitmueller@kernellabs.com>
+ */
+
+static int drx39xxj_set_powerstate(struct dvb_frontend *fe, int enable)
+{
+ struct drx39xxj_state *state = fe->demodulator_priv;
+ struct drx_demod_instance *demod = state->demod;
+ int result;
+ enum drx_power_mode power_mode;
+
+ if (enable)
+ power_mode = DRX_POWER_UP;
+ else
+ power_mode = DRX_POWER_DOWN;
+
+ result = ctrl_power_mode(demod, &power_mode);
+ if (result != 0) {
+ pr_err("Power state change failed\n");
+ return 0;
+ }
+
+ return 0;
+}
+
+static int drx39xxj_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct drx39xxj_state *state = fe->demodulator_priv;
+ struct drx_demod_instance *demod = state->demod;
+ int result;
+ enum drx_lock_status lock_status;
+
+ *status = 0;
+
+ result = ctrl_lock_status(demod, &lock_status);
+ if (result != 0) {
+ pr_err("drx39xxj: could not get lock status!\n");
+ *status = 0;
+ }
+
+ switch (lock_status) {
+ case DRX_NEVER_LOCK:
+ *status = 0;
+ pr_err("drx says NEVER_LOCK\n");
+ break;
+ case DRX_NOT_LOCKED:
+ *status = 0;
+ break;
+ case DRX_LOCK_STATE_1:
+ case DRX_LOCK_STATE_2:
+ case DRX_LOCK_STATE_3:
+ case DRX_LOCK_STATE_4:
+ case DRX_LOCK_STATE_5:
+ case DRX_LOCK_STATE_6:
+ case DRX_LOCK_STATE_7:
+ case DRX_LOCK_STATE_8:
+ case DRX_LOCK_STATE_9:
+ *status = FE_HAS_SIGNAL
+ | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC;
+ break;
+ case DRX_LOCKED:
+ *status = FE_HAS_SIGNAL
+ | FE_HAS_CARRIER
+ | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ break;
+ default:
+ pr_err("Lock state unknown %d\n", lock_status);
+ }
+ ctrl_sig_quality(demod, lock_status);
+
+ return 0;
+}
+
+static int drx39xxj_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ if (p->pre_bit_error.stat[0].scale == FE_SCALE_NOT_AVAILABLE) {
+ *ber = 0;
+ return 0;
+ }
+
+ if (!p->pre_bit_count.stat[0].uvalue) {
+ if (!p->pre_bit_error.stat[0].uvalue)
+ *ber = 0;
+ else
+ *ber = 1000000;
+ } else {
+ *ber = frac_times1e6(p->pre_bit_error.stat[0].uvalue,
+ p->pre_bit_count.stat[0].uvalue);
+ }
+ return 0;
+}
+
+static int drx39xxj_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ if (p->strength.stat[0].scale == FE_SCALE_NOT_AVAILABLE) {
+ *strength = 0;
+ return 0;
+ }
+
+ *strength = p->strength.stat[0].uvalue;
+ return 0;
+}
+
+static int drx39xxj_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u64 tmp64;
+
+ if (p->cnr.stat[0].scale == FE_SCALE_NOT_AVAILABLE) {
+ *snr = 0;
+ return 0;
+ }
+
+ tmp64 = p->cnr.stat[0].svalue;
+ do_div(tmp64, 10);
+ *snr = tmp64;
+ return 0;
+}
+
+static int drx39xxj_read_ucblocks(struct dvb_frontend *fe, u32 *ucb)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ if (p->block_error.stat[0].scale == FE_SCALE_NOT_AVAILABLE) {
+ *ucb = 0;
+ return 0;
+ }
+
+ *ucb = p->block_error.stat[0].uvalue;
+ return 0;
+}
+
+static int drx39xxj_set_frontend(struct dvb_frontend *fe)
+{
+#ifdef DJH_DEBUG
+ int i;
+#endif
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct drx39xxj_state *state = fe->demodulator_priv;
+ struct drx_demod_instance *demod = state->demod;
+ enum drx_standard standard = DRX_STANDARD_8VSB;
+ struct drx_channel channel;
+ int result;
+ static const struct drx_channel def_channel = {
+ /* frequency */ 0,
+ /* bandwidth */ DRX_BANDWIDTH_6MHZ,
+ /* mirror */ DRX_MIRROR_NO,
+ /* constellation */ DRX_CONSTELLATION_AUTO,
+ /* hierarchy */ DRX_HIERARCHY_UNKNOWN,
+ /* priority */ DRX_PRIORITY_UNKNOWN,
+ /* coderate */ DRX_CODERATE_UNKNOWN,
+ /* guard */ DRX_GUARD_UNKNOWN,
+ /* fftmode */ DRX_FFTMODE_UNKNOWN,
+ /* classification */ DRX_CLASSIFICATION_AUTO,
+ /* symbolrate */ 5057000,
+ /* interleavemode */ DRX_INTERLEAVEMODE_UNKNOWN,
+ /* ldpc */ DRX_LDPC_UNKNOWN,
+ /* carrier */ DRX_CARRIER_UNKNOWN,
+ /* frame mode */ DRX_FRAMEMODE_UNKNOWN
+ };
+ u32 constellation = DRX_CONSTELLATION_AUTO;
+
+ /* Bring the demod out of sleep */
+ drx39xxj_set_powerstate(fe, 1);
+
+ if (fe->ops.tuner_ops.set_params) {
+ u32 int_freq;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ /* Set tuner to desired frequency and standard */
+ fe->ops.tuner_ops.set_params(fe);
+
+ /* Use the tuner's IF */
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ fe->ops.tuner_ops.get_if_frequency(fe, &int_freq);
+ demod->my_common_attr->intermediate_freq = int_freq / 1000;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ switch (p->delivery_system) {
+ case SYS_ATSC:
+ standard = DRX_STANDARD_8VSB;
+ break;
+ case SYS_DVBC_ANNEX_B:
+ standard = DRX_STANDARD_ITU_B;
+
+ switch (p->modulation) {
+ case QAM_64:
+ constellation = DRX_CONSTELLATION_QAM64;
+ break;
+ case QAM_256:
+ constellation = DRX_CONSTELLATION_QAM256;
+ break;
+ default:
+ constellation = DRX_CONSTELLATION_AUTO;
+ break;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* Set the standard (will be powered up if necessary */
+ result = ctrl_set_standard(demod, &standard);
+ if (result != 0) {
+ pr_err("Failed to set standard! result=%02x\n",
+ result);
+ return -EINVAL;
+ }
+
+ /* set channel parameters */
+ channel = def_channel;
+ channel.frequency = p->frequency / 1000;
+ channel.bandwidth = DRX_BANDWIDTH_6MHZ;
+ channel.constellation = constellation;
+
+ /* program channel */
+ result = ctrl_set_channel(demod, &channel);
+ if (result != 0) {
+ pr_err("Failed to set channel!\n");
+ return -EINVAL;
+ }
+ /* Just for giggles, let's shut off the LNA again.... */
+ drxj_set_lna_state(demod, false);
+
+ /* After set_frontend, except for strength, stats aren't available */
+ p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+
+ return 0;
+}
+
+static int drx39xxj_sleep(struct dvb_frontend *fe)
+{
+ /* power-down the demodulator */
+ return drx39xxj_set_powerstate(fe, 0);
+}
+
+static int drx39xxj_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct drx39xxj_state *state = fe->demodulator_priv;
+ struct drx_demod_instance *demod = state->demod;
+ bool i2c_gate_state;
+ int result;
+
+#ifdef DJH_DEBUG
+ pr_debug("i2c gate call: enable=%d state=%d\n", enable,
+ state->i2c_gate_open);
+#endif
+
+ if (enable)
+ i2c_gate_state = true;
+ else
+ i2c_gate_state = false;
+
+ if (state->i2c_gate_open == enable) {
+ /* We're already in the desired state */
+ return 0;
+ }
+
+ result = ctrl_i2c_bridge(demod, &i2c_gate_state);
+ if (result != 0) {
+ pr_err("drx39xxj: could not open i2c gate [%d]\n",
+ result);
+ dump_stack();
+ } else {
+ state->i2c_gate_open = enable;
+ }
+ return 0;
+}
+
+static int drx39xxj_init(struct dvb_frontend *fe)
+{
+ struct drx39xxj_state *state = fe->demodulator_priv;
+ struct drx_demod_instance *demod = state->demod;
+ int rc = 0;
+
+ if (fe->exit == DVB_FE_DEVICE_RESUME) {
+ /* so drxj_open() does what it needs to do */
+ demod->my_common_attr->is_opened = false;
+ rc = drxj_open(demod);
+ if (rc != 0)
+ pr_err("drx39xxj_init(): DRX open failed rc=%d!\n", rc);
+ } else
+ drx39xxj_set_powerstate(fe, 1);
+
+ return rc;
+}
+
+static int drx39xxj_set_lna(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct drx39xxj_state *state = fe->demodulator_priv;
+ struct drx_demod_instance *demod = state->demod;
+ struct drxj_data *ext_attr = demod->my_ext_attr;
+
+ if (c->lna) {
+ if (!ext_attr->has_lna) {
+ pr_err("LNA is not supported on this device!\n");
+ return -EINVAL;
+
+ }
+ }
+
+ return drxj_set_lna_state(demod, c->lna);
+}
+
+static int drx39xxj_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ return 0;
+}
+
+static void drx39xxj_release(struct dvb_frontend *fe)
+{
+ struct drx39xxj_state *state = fe->demodulator_priv;
+ struct drx_demod_instance *demod = state->demod;
+
+ /* if device is removed don't access it */
+ if (fe->exit != DVB_FE_DEVICE_REMOVED)
+ drxj_close(demod);
+
+ kfree(demod->my_ext_attr);
+ kfree(demod->my_common_attr);
+ kfree(demod->my_i2c_dev_addr);
+ kfree(demod);
+ kfree(state);
+}
+
+static const struct dvb_frontend_ops drx39xxj_ops;
+
+struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c)
+{
+ struct drx39xxj_state *state = NULL;
+ struct i2c_device_addr *demod_addr = NULL;
+ struct drx_common_attr *demod_comm_attr = NULL;
+ struct drxj_data *demod_ext_attr = NULL;
+ struct drx_demod_instance *demod = NULL;
+ struct dtv_frontend_properties *p;
+ int result;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct drx39xxj_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ demod = kmemdup(&drxj_default_demod_g,
+ sizeof(struct drx_demod_instance), GFP_KERNEL);
+ if (demod == NULL)
+ goto error;
+
+ demod_addr = kmemdup(&drxj_default_addr_g,
+ sizeof(struct i2c_device_addr), GFP_KERNEL);
+ if (demod_addr == NULL)
+ goto error;
+
+ demod_comm_attr = kmemdup(&drxj_default_comm_attr_g,
+ sizeof(struct drx_common_attr), GFP_KERNEL);
+ if (demod_comm_attr == NULL)
+ goto error;
+
+ demod_ext_attr = kmemdup(&drxj_data_g, sizeof(struct drxj_data),
+ GFP_KERNEL);
+ if (demod_ext_attr == NULL)
+ goto error;
+
+ /* setup the state */
+ state->i2c = i2c;
+ state->demod = demod;
+
+ /* setup the demod data */
+ demod->my_i2c_dev_addr = demod_addr;
+ demod->my_common_attr = demod_comm_attr;
+ demod->my_i2c_dev_addr->user_data = state;
+ demod->my_common_attr->microcode_file = DRX39XX_MAIN_FIRMWARE;
+ demod->my_common_attr->verify_microcode = true;
+ demod->my_common_attr->intermediate_freq = 5000;
+ demod->my_common_attr->current_power_mode = DRX_POWER_DOWN;
+ demod->my_ext_attr = demod_ext_attr;
+ ((struct drxj_data *)demod_ext_attr)->uio_sma_tx_mode = DRX_UIO_MODE_READWRITE;
+ demod->i2c = i2c;
+
+ result = drxj_open(demod);
+ if (result != 0) {
+ pr_err("DRX open failed! Aborting\n");
+ goto error;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &drx39xxj_ops,
+ sizeof(struct dvb_frontend_ops));
+
+ state->frontend.demodulator_priv = state;
+
+ /* Initialize stats - needed for DVBv5 stats to work */
+ p = &state->frontend.dtv_property_cache;
+ p->strength.len = 1;
+ p->pre_bit_count.len = 1;
+ p->pre_bit_error.len = 1;
+ p->post_bit_count.len = 1;
+ p->post_bit_error.len = 1;
+ p->block_count.len = 1;
+ p->block_error.len = 1;
+ p->cnr.len = 1;
+
+ p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ return &state->frontend;
+
+error:
+ kfree(demod_ext_attr);
+ kfree(demod_comm_attr);
+ kfree(demod_addr);
+ kfree(demod);
+ kfree(state);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(drx39xxj_attach);
+
+static const struct dvb_frontend_ops drx39xxj_ops = {
+ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
+ .info = {
+ .name = "Micronas DRX39xxj family Frontend",
+ .frequency_min_hz = 51 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 62500,
+ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+ },
+
+ .init = drx39xxj_init,
+ .i2c_gate_ctrl = drx39xxj_i2c_gate_ctrl,
+ .sleep = drx39xxj_sleep,
+ .set_frontend = drx39xxj_set_frontend,
+ .get_tune_settings = drx39xxj_get_tune_settings,
+ .read_status = drx39xxj_read_status,
+ .read_ber = drx39xxj_read_ber,
+ .read_signal_strength = drx39xxj_read_signal_strength,
+ .read_snr = drx39xxj_read_snr,
+ .read_ucblocks = drx39xxj_read_ucblocks,
+ .release = drx39xxj_release,
+ .set_lna = drx39xxj_set_lna,
+};
+
+MODULE_DESCRIPTION("Micronas DRX39xxj Frontend");
+MODULE_AUTHOR("Devin Heitmueller");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(DRX39XX_MAIN_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.h b/drivers/media/dvb-frontends/drx39xyj/drxj.h
new file mode 100644
index 000000000000..232b3b0d68c8
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/drxj.h
@@ -0,0 +1,651 @@
+
+/*
+ Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of Trident Microsystems nor Hauppauge Computer Works
+ nor the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+ DRXJ specific header file
+
+ Authors: Dragan Savic, Milos Nikolic, Mihajlo Katona, Tao Ding, Paul Janssen
+*/
+
+#ifndef __DRXJ_H__
+#define __DRXJ_H__
+/*-------------------------------------------------------------------------
+INCLUDES
+-------------------------------------------------------------------------*/
+
+#include "drx_driver.h"
+#include "drx_dap_fasi.h"
+
+/* Check DRX-J specific dap condition */
+/* Multi master mode and short addr format only will not work.
+ RMW, CRC reset, broadcast and switching back to single master mode
+ cannot be done with short addr only in multi master mode. */
+#if ((DRXDAP_SINGLE_MASTER == 0) && (DRXDAPFASI_LONG_ADDR_ALLOWED == 0))
+#error "Multi master mode and short addressing only is an illegal combination"
+ *; /* Generate a fatal compiler error to make sure it stops here,
+ this is necessary because not all compilers stop after a #error. */
+#endif
+
+/*-------------------------------------------------------------------------
+TYPEDEFS
+-------------------------------------------------------------------------*/
+/*============================================================================*/
+/*============================================================================*/
+/*== code support ============================================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+/*============================================================================*/
+/*============================================================================*/
+/*== SCU cmd if =============================================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+ struct drxjscu_cmd {
+ u16 command;
+ /*< Command number */
+ u16 parameter_len;
+ /*< Data length in byte */
+ u16 result_len;
+ /*< result length in byte */
+ u16 *parameter;
+ /*< General purpose param */
+ u16 *result;
+ /*< General purpose param */};
+
+/*============================================================================*/
+/*============================================================================*/
+/*== CTRL CFG related data structures ========================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+/* extra intermediate lock state for VSB,QAM,NTSC */
+#define DRXJ_DEMOD_LOCK (DRX_LOCK_STATE_1)
+
+/* OOB lock states */
+#define DRXJ_OOB_AGC_LOCK (DRX_LOCK_STATE_1) /* analog gain control lock */
+#define DRXJ_OOB_SYNC_LOCK (DRX_LOCK_STATE_2) /* digital gain control lock */
+
+/* Intermediate powermodes for DRXJ */
+#define DRXJ_POWER_DOWN_MAIN_PATH DRX_POWER_MODE_8
+#define DRXJ_POWER_DOWN_CORE DRX_POWER_MODE_9
+#define DRXJ_POWER_DOWN_PLL DRX_POWER_MODE_10
+
+/* supstition for GPIO FNC mux */
+#define APP_O (0x0000)
+
+/*#define DRX_CTRL_BASE (0x0000)*/
+
+#define DRXJ_CTRL_CFG_BASE (0x1000)
+ enum drxj_cfg_type {
+ DRXJ_CFG_AGC_RF = DRXJ_CTRL_CFG_BASE,
+ DRXJ_CFG_AGC_IF,
+ DRXJ_CFG_AGC_INTERNAL,
+ DRXJ_CFG_PRE_SAW,
+ DRXJ_CFG_AFE_GAIN,
+ DRXJ_CFG_SYMBOL_CLK_OFFSET,
+ DRXJ_CFG_ACCUM_CR_RS_CW_ERR,
+ DRXJ_CFG_FEC_MERS_SEQ_COUNT,
+ DRXJ_CFG_OOB_MISC,
+ DRXJ_CFG_SMART_ANT,
+ DRXJ_CFG_OOB_PRE_SAW,
+ DRXJ_CFG_VSB_MISC,
+ DRXJ_CFG_RESET_PACKET_ERR,
+
+ /* ATV (FM) */
+ DRXJ_CFG_ATV_OUTPUT, /* also for FM (SIF control) but not likely */
+ DRXJ_CFG_ATV_MISC,
+ DRXJ_CFG_ATV_EQU_COEF,
+ DRXJ_CFG_ATV_AGC_STATUS, /* also for FM ( IF,RF, audioAGC ) */
+
+ DRXJ_CFG_MPEG_OUTPUT_MISC,
+ DRXJ_CFG_HW_CFG,
+ DRXJ_CFG_OOB_LO_POW,
+
+ DRXJ_CFG_MAX /* dummy, never to be used */};
+
+/*
+* /enum drxj_cfg_smart_ant_io * smart antenna i/o.
+*/
+enum drxj_cfg_smart_ant_io {
+ DRXJ_SMT_ANT_OUTPUT = 0,
+ DRXJ_SMT_ANT_INPUT
+};
+
+/*
+* /struct drxj_cfg_smart_ant * Set smart antenna.
+*/
+ struct drxj_cfg_smart_ant {
+ enum drxj_cfg_smart_ant_io io;
+ u16 ctrl_data;
+ };
+
+/*
+* /struct DRXJAGCSTATUS_t
+* AGC status information from the DRXJ-IQM-AF.
+*/
+struct drxj_agc_status {
+ u16 IFAGC;
+ u16 RFAGC;
+ u16 digital_agc;
+};
+
+/* DRXJ_CFG_AGC_RF, DRXJ_CFG_AGC_IF */
+
+/*
+* /enum drxj_agc_ctrl_mode * Available AGCs modes in the DRXJ.
+*/
+ enum drxj_agc_ctrl_mode {
+ DRX_AGC_CTRL_AUTO = 0,
+ DRX_AGC_CTRL_USER,
+ DRX_AGC_CTRL_OFF};
+
+/*
+* /struct drxj_cfg_agc * Generic interface for all AGCs present on the DRXJ.
+*/
+ struct drxj_cfg_agc {
+ enum drx_standard standard; /* standard for which these settings apply */
+ enum drxj_agc_ctrl_mode ctrl_mode; /* off, user, auto */
+ u16 output_level; /* range dependent on AGC */
+ u16 min_output_level; /* range dependent on AGC */
+ u16 max_output_level; /* range dependent on AGC */
+ u16 speed; /* range dependent on AGC */
+ u16 top; /* rf-agc take over point */
+ u16 cut_off_current; /* rf-agc is accelerated if output current
+ is below cut-off current */};
+
+/* DRXJ_CFG_PRE_SAW */
+
+/*
+* /struct drxj_cfg_pre_saw * Interface to configure pre SAW sense.
+*/
+ struct drxj_cfg_pre_saw {
+ enum drx_standard standard; /* standard to which these settings apply */
+ u16 reference; /* pre SAW reference value, range 0 .. 31 */
+ bool use_pre_saw; /* true algorithms must use pre SAW sense */};
+
+/* DRXJ_CFG_AFE_GAIN */
+
+/*
+* /struct drxj_cfg_afe_gain * Interface to configure gain of AFE (LNA + PGA).
+*/
+ struct drxj_cfg_afe_gain {
+ enum drx_standard standard; /* standard to which these settings apply */
+ u16 gain; /* gain in 0.1 dB steps, DRXJ range 140 .. 335 */};
+
+/*
+* /struct drxjrs_errors
+* Available failure information in DRXJ_FEC_RS.
+*
+* Container for errors that are received in the most recently finished measurement period
+*
+*/
+ struct drxjrs_errors {
+ u16 nr_bit_errors;
+ /*< no of pre RS bit errors */
+ u16 nr_symbol_errors;
+ /*< no of pre RS symbol errors */
+ u16 nr_packet_errors;
+ /*< no of pre RS packet errors */
+ u16 nr_failures;
+ /*< no of post RS failures to decode */
+ u16 nr_snc_par_fail_count;
+ /*< no of post RS bit erros */
+ };
+
+/*
+* /struct drxj_cfg_vsb_misc * symbol error rate
+*/
+ struct drxj_cfg_vsb_misc {
+ u32 symb_error;
+ /*< symbol error rate sps */};
+
+/*
+* /enum drxj_mpeg_output_clock_rate * Mpeg output clock rate.
+*
+*/
+ enum drxj_mpeg_start_width {
+ DRXJ_MPEG_START_WIDTH_1CLKCYC,
+ DRXJ_MPEG_START_WIDTH_8CLKCYC};
+
+/*
+* /enum drxj_mpeg_output_clock_rate * Mpeg output clock rate.
+*
+*/
+ enum drxj_mpeg_output_clock_rate {
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_AUTO,
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_75973K,
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_50625K,
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_37968K,
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_30375K,
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_25313K,
+ DRXJ_MPEGOUTPUT_CLOCK_RATE_21696K};
+
+/*
+* /struct DRXJCfgMisc_t
+* Change TEI bit of MPEG output
+* reverse MPEG output bit order
+* set MPEG output clock rate
+*/
+ struct drxj_cfg_mpeg_output_misc {
+ bool disable_tei_handling; /*< if true pass (not change) TEI bit */
+ bool bit_reverse_mpeg_outout; /*< if true, parallel: msb on MD0; serial: lsb out first */
+ enum drxj_mpeg_output_clock_rate mpeg_output_clock_rate;
+ /*< set MPEG output clock rate that overwirtes the derived one from symbol rate */
+ enum drxj_mpeg_start_width mpeg_start_width; /*< set MPEG output start width */};
+
+/*
+* /enum drxj_xtal_freq * Supported external crystal reference frequency.
+*/
+ enum drxj_xtal_freq {
+ DRXJ_XTAL_FREQ_RSVD,
+ DRXJ_XTAL_FREQ_27MHZ,
+ DRXJ_XTAL_FREQ_20P25MHZ,
+ DRXJ_XTAL_FREQ_4MHZ};
+
+/*
+* /enum drxj_xtal_freq * Supported external crystal reference frequency.
+*/
+ enum drxji2c_speed {
+ DRXJ_I2C_SPEED_400KBPS,
+ DRXJ_I2C_SPEED_100KBPS};
+
+/*
+* /struct drxj_cfg_hw_cfg * Get hw configuration, such as crystal
+* reference frequency, I2C speed, etc...
+*/
+ struct drxj_cfg_hw_cfg {
+ enum drxj_xtal_freq xtal_freq;
+ /*< crystal reference frequency */
+ enum drxji2c_speed i2c_speed;
+ /*< 100 or 400 kbps */};
+
+/*
+ * DRXJ_CFG_ATV_MISC
+ */
+ struct drxj_cfg_atv_misc {
+ s16 peak_filter; /* -8 .. 15 */
+ u16 noise_filter; /* 0 .. 15 */};
+
+/*
+ * struct drxj_cfg_oob_misc */
+#define DRXJ_OOB_STATE_RESET 0x0
+#define DRXJ_OOB_STATE_AGN_HUNT 0x1
+#define DRXJ_OOB_STATE_DGN_HUNT 0x2
+#define DRXJ_OOB_STATE_AGC_HUNT 0x3
+#define DRXJ_OOB_STATE_FRQ_HUNT 0x4
+#define DRXJ_OOB_STATE_PHA_HUNT 0x8
+#define DRXJ_OOB_STATE_TIM_HUNT 0x10
+#define DRXJ_OOB_STATE_EQU_HUNT 0x20
+#define DRXJ_OOB_STATE_EQT_HUNT 0x30
+#define DRXJ_OOB_STATE_SYNC 0x40
+
+struct drxj_cfg_oob_misc {
+ struct drxj_agc_status agc;
+ bool eq_lock;
+ bool sym_timing_lock;
+ bool phase_lock;
+ bool freq_lock;
+ bool dig_gain_lock;
+ bool ana_gain_lock;
+ u8 state;
+};
+
+/*
+ * Index of in array of coef
+ */
+ enum drxj_cfg_oob_lo_power {
+ DRXJ_OOB_LO_POW_MINUS0DB = 0,
+ DRXJ_OOB_LO_POW_MINUS5DB,
+ DRXJ_OOB_LO_POW_MINUS10DB,
+ DRXJ_OOB_LO_POW_MINUS15DB,
+ DRXJ_OOB_LO_POW_MAX};
+
+/*
+ * DRXJ_CFG_ATV_EQU_COEF
+ */
+ struct drxj_cfg_atv_equ_coef {
+ s16 coef0; /* -256 .. 255 */
+ s16 coef1; /* -256 .. 255 */
+ s16 coef2; /* -256 .. 255 */
+ s16 coef3; /* -256 .. 255 */};
+
+/*
+ * Index of in array of coef
+ */
+ enum drxj_coef_array_index {
+ DRXJ_COEF_IDX_MN = 0,
+ DRXJ_COEF_IDX_FM,
+ DRXJ_COEF_IDX_L,
+ DRXJ_COEF_IDX_LP,
+ DRXJ_COEF_IDX_BG,
+ DRXJ_COEF_IDX_DK,
+ DRXJ_COEF_IDX_I,
+ DRXJ_COEF_IDX_MAX};
+
+/*
+ * DRXJ_CFG_ATV_OUTPUT
+ */
+
+/*
+* /enum DRXJAttenuation_t
+* Attenuation setting for SIF AGC.
+*
+*/
+ enum drxjsif_attenuation {
+ DRXJ_SIF_ATTENUATION_0DB,
+ DRXJ_SIF_ATTENUATION_3DB,
+ DRXJ_SIF_ATTENUATION_6DB,
+ DRXJ_SIF_ATTENUATION_9DB};
+
+/*
+* /struct drxj_cfg_atv_output * SIF attenuation setting.
+*
+*/
+struct drxj_cfg_atv_output {
+ bool enable_cvbs_output; /* true= enabled */
+ bool enable_sif_output; /* true= enabled */
+ enum drxjsif_attenuation sif_attenuation;
+};
+
+/*
+ DRXJ_CFG_ATV_AGC_STATUS (get only)
+*/
+/* TODO : AFE interface not yet finished, subject to change */
+ struct drxj_cfg_atv_agc_status {
+ u16 rf_agc_gain; /* 0 .. 877 uA */
+ u16 if_agc_gain; /* 0 .. 877 uA */
+ s16 video_agc_gain; /* -75 .. 1972 in 0.1 dB steps */
+ s16 audio_agc_gain; /* -4 .. 1020 in 0.1 dB steps */
+ u16 rf_agc_loop_gain; /* 0 .. 7 */
+ u16 if_agc_loop_gain; /* 0 .. 7 */
+ u16 video_agc_loop_gain; /* 0 .. 7 */};
+
+/*============================================================================*/
+/*============================================================================*/
+/*== CTRL related data structures ============================================*/
+/*============================================================================*/
+/*============================================================================*/
+
+/* NONE */
+
+/*============================================================================*/
+/*============================================================================*/
+
+/*========================================*/
+/*
+* /struct struct drxj_data * DRXJ specific attributes.
+*
+* Global data container for DRXJ specific data.
+*
+*/
+ struct drxj_data {
+ /* device capabilities (determined during drx_open()) */
+ bool has_lna; /*< true if LNA (aka PGA) present */
+ bool has_oob; /*< true if OOB supported */
+ bool has_ntsc; /*< true if NTSC supported */
+ bool has_btsc; /*< true if BTSC supported */
+ bool has_smatx; /*< true if mat_tx is available */
+ bool has_smarx; /*< true if mat_rx is available */
+ bool has_gpio; /*< true if GPIO is available */
+ bool has_irqn; /*< true if IRQN is available */
+ /* A1/A2/A... */
+ u8 mfx; /*< metal fix */
+
+ /* tuner settings */
+ bool mirror_freq_spect_oob;/*< tuner inversion (true = tuner mirrors the signal */
+
+ /* standard/channel settings */
+ enum drx_standard standard; /*< current standard information */
+ enum drx_modulation constellation;
+ /*< current constellation */
+ s32 frequency; /*< center signal frequency in KHz */
+ enum drx_bandwidth curr_bandwidth;
+ /*< current channel bandwidth */
+ enum drx_mirror mirror; /*< current channel mirror */
+
+ /* signal quality information */
+ u32 fec_bits_desired; /*< BER accounting period */
+ u16 fec_vd_plen; /*< no of trellis symbols: VD SER measurement period */
+ u16 qam_vd_prescale; /*< Viterbi Measurement Prescale */
+ u16 qam_vd_period; /*< Viterbi Measurement period */
+ u16 fec_rs_plen; /*< defines RS BER measurement period */
+ u16 fec_rs_prescale; /*< ReedSolomon Measurement Prescale */
+ u16 fec_rs_period; /*< ReedSolomon Measurement period */
+ bool reset_pkt_err_acc; /*< Set a flag to reset accumulated packet error */
+ u16 pkt_err_acc_start; /*< Set a flag to reset accumulated packet error */
+
+ /* HI configuration */
+ u16 hi_cfg_timing_div; /*< HI Configure() parameter 2 */
+ u16 hi_cfg_bridge_delay; /*< HI Configure() parameter 3 */
+ u16 hi_cfg_wake_up_key; /*< HI Configure() parameter 4 */
+ u16 hi_cfg_ctrl; /*< HI Configure() parameter 5 */
+ u16 hi_cfg_transmit; /*< HI Configure() parameter 6 */
+
+ /* UIO configuration */
+ enum drxuio_mode uio_sma_rx_mode;/*< current mode of SmaRx pin */
+ enum drxuio_mode uio_sma_tx_mode;/*< current mode of SmaTx pin */
+ enum drxuio_mode uio_gpio_mode; /*< current mode of ASEL pin */
+ enum drxuio_mode uio_irqn_mode; /*< current mode of IRQN pin */
+
+ /* IQM fs frequency shift and inversion */
+ u32 iqm_fs_rate_ofs; /*< frequency shifter setting after setchannel */
+ bool pos_image; /*< True: positive image */
+ /* IQM RC frequency shift */
+ u32 iqm_rc_rate_ofs; /*< frequency shifter setting after setchannel */
+
+ /* ATV configuration */
+ u32 atv_cfg_changed_flags; /*< flag: flags cfg changes */
+ s16 atv_top_equ0[DRXJ_COEF_IDX_MAX]; /*< shadow of ATV_TOP_EQU0__A */
+ s16 atv_top_equ1[DRXJ_COEF_IDX_MAX]; /*< shadow of ATV_TOP_EQU1__A */
+ s16 atv_top_equ2[DRXJ_COEF_IDX_MAX]; /*< shadow of ATV_TOP_EQU2__A */
+ s16 atv_top_equ3[DRXJ_COEF_IDX_MAX]; /*< shadow of ATV_TOP_EQU3__A */
+ bool phase_correction_bypass;/*< flag: true=bypass */
+ s16 atv_top_vid_peak; /*< shadow of ATV_TOP_VID_PEAK__A */
+ u16 atv_top_noise_th; /*< shadow of ATV_TOP_NOISE_TH__A */
+ bool enable_cvbs_output; /*< flag CVBS output enable */
+ bool enable_sif_output; /*< flag SIF output enable */
+ enum drxjsif_attenuation sif_attenuation;
+ /*< current SIF att setting */
+ /* Agc configuration for QAM and VSB */
+ struct drxj_cfg_agc qam_rf_agc_cfg; /*< qam RF AGC config */
+ struct drxj_cfg_agc qam_if_agc_cfg; /*< qam IF AGC config */
+ struct drxj_cfg_agc vsb_rf_agc_cfg; /*< vsb RF AGC config */
+ struct drxj_cfg_agc vsb_if_agc_cfg; /*< vsb IF AGC config */
+
+ /* PGA gain configuration for QAM and VSB */
+ u16 qam_pga_cfg; /*< qam PGA config */
+ u16 vsb_pga_cfg; /*< vsb PGA config */
+
+ /* Pre SAW configuration for QAM and VSB */
+ struct drxj_cfg_pre_saw qam_pre_saw_cfg;
+ /*< qam pre SAW config */
+ struct drxj_cfg_pre_saw vsb_pre_saw_cfg;
+ /*< qam pre SAW config */
+
+ /* Version information */
+ char v_text[2][12]; /*< allocated text versions */
+ struct drx_version v_version[2]; /*< allocated versions structs */
+ struct drx_version_list v_list_elements[2];
+ /*< allocated version list */
+
+ /* smart antenna configuration */
+ bool smart_ant_inverted;
+
+ /* Tracking filter setting for OOB */
+ u16 oob_trk_filter_cfg[8];
+ bool oob_power_on;
+
+ /* MPEG static bitrate setting */
+ u32 mpeg_ts_static_bitrate; /*< bitrate static MPEG output */
+ bool disable_te_ihandling; /*< MPEG TS TEI handling */
+ bool bit_reverse_mpeg_outout;/*< MPEG output bit order */
+ enum drxj_mpeg_output_clock_rate mpeg_output_clock_rate;
+ /*< MPEG output clock rate */
+ enum drxj_mpeg_start_width mpeg_start_width;
+ /*< MPEG Start width */
+
+ /* Pre SAW & Agc configuration for ATV */
+ struct drxj_cfg_pre_saw atv_pre_saw_cfg;
+ /*< atv pre SAW config */
+ struct drxj_cfg_agc atv_rf_agc_cfg; /*< atv RF AGC config */
+ struct drxj_cfg_agc atv_if_agc_cfg; /*< atv IF AGC config */
+ u16 atv_pga_cfg; /*< atv pga config */
+
+ u32 curr_symbol_rate;
+
+ /* pin-safe mode */
+ bool pdr_safe_mode; /*< PDR safe mode activated */
+ u16 pdr_safe_restore_val_gpio;
+ u16 pdr_safe_restore_val_v_sync;
+ u16 pdr_safe_restore_val_sma_rx;
+ u16 pdr_safe_restore_val_sma_tx;
+
+ /* OOB pre-saw value */
+ u16 oob_pre_saw;
+ enum drxj_cfg_oob_lo_power oob_lo_pow;
+
+ struct drx_aud_data aud_data;
+ /*< audio storage */};
+
+/*-------------------------------------------------------------------------
+Access MACROS
+-------------------------------------------------------------------------*/
+/*
+* \brief Compilable references to attributes
+* \param d pointer to demod instance
+*
+* Used as main reference to an attribute field.
+* Can be used by both macro implementation and function implementation.
+* These macros are defined to avoid duplication of code in macro and function
+* definitions that handle access of demod common or extended attributes.
+*
+*/
+
+#define DRXJ_ATTR_BTSC_DETECT(d) \
+ (((struct drxj_data *)(d)->my_ext_attr)->aud_data.btsc_detect)
+
+/*-------------------------------------------------------------------------
+DEFINES
+-------------------------------------------------------------------------*/
+
+/*
+* \def DRXJ_NTSC_CARRIER_FREQ_OFFSET
+* \brief Offset from picture carrier to centre frequency in kHz, in RF domain
+*
+* For NTSC standard.
+* NTSC channels are listed by their picture carrier frequency (Fpc).
+* The function DRX_CTRL_SET_CHANNEL requires the centre frequency as input.
+* In case the tuner module is not used the DRX-J requires that the tuner is
+* tuned to the centre frequency of the channel:
+*
+* Fcentre = Fpc + DRXJ_NTSC_CARRIER_FREQ_OFFSET
+*
+*/
+#define DRXJ_NTSC_CARRIER_FREQ_OFFSET ((s32)(1750))
+
+/*
+* \def DRXJ_PAL_SECAM_BG_CARRIER_FREQ_OFFSET
+* \brief Offset from picture carrier to centre frequency in kHz, in RF domain
+*
+* For PAL/SECAM - BG standard. This define is needed in case the tuner module
+* is NOT used. PAL/SECAM channels are listed by their picture carrier frequency (Fpc).
+* The DRX-J requires that the tuner is tuned to:
+* Fpc + DRXJ_PAL_SECAM_BG_CARRIER_FREQ_OFFSET
+*
+* In case the tuner module is used the drxdriver takes care of this.
+* In case the tuner module is NOT used the application programmer must take
+* care of this.
+*
+*/
+#define DRXJ_PAL_SECAM_BG_CARRIER_FREQ_OFFSET ((s32)(2375))
+
+/*
+* \def DRXJ_PAL_SECAM_DKIL_CARRIER_FREQ_OFFSET
+* \brief Offset from picture carrier to centre frequency in kHz, in RF domain
+*
+* For PAL/SECAM - DK, I, L standards. This define is needed in case the tuner module
+* is NOT used. PAL/SECAM channels are listed by their picture carrier frequency (Fpc).
+* The DRX-J requires that the tuner is tuned to:
+* Fpc + DRXJ_PAL_SECAM_DKIL_CARRIER_FREQ_OFFSET
+*
+* In case the tuner module is used the drxdriver takes care of this.
+* In case the tuner module is NOT used the application programmer must take
+* care of this.
+*
+*/
+#define DRXJ_PAL_SECAM_DKIL_CARRIER_FREQ_OFFSET ((s32)(2775))
+
+/*
+* \def DRXJ_PAL_SECAM_LP_CARRIER_FREQ_OFFSET
+* \brief Offset from picture carrier to centre frequency in kHz, in RF domain
+*
+* For PAL/SECAM - LP standard. This define is needed in case the tuner module
+* is NOT used. PAL/SECAM channels are listed by their picture carrier frequency (Fpc).
+* The DRX-J requires that the tuner is tuned to:
+* Fpc + DRXJ_PAL_SECAM_LP_CARRIER_FREQ_OFFSET
+*
+* In case the tuner module is used the drxdriver takes care of this.
+* In case the tuner module is NOT used the application programmer must take
+* care of this.
+*/
+#define DRXJ_PAL_SECAM_LP_CARRIER_FREQ_OFFSET ((s32)(-3255))
+
+/*
+* \def DRXJ_FM_CARRIER_FREQ_OFFSET
+* \brief Offset from sound carrier to centre frequency in kHz, in RF domain
+*
+* For FM standard.
+* FM channels are listed by their sound carrier frequency (Fsc).
+* The function DRX_CTRL_SET_CHANNEL requires the Ffm frequency (see below) as
+* input.
+* In case the tuner module is not used the DRX-J requires that the tuner is
+* tuned to the Ffm frequency of the channel.
+*
+* Ffm = Fsc + DRXJ_FM_CARRIER_FREQ_OFFSET
+*
+*/
+#define DRXJ_FM_CARRIER_FREQ_OFFSET ((s32)(-3000))
+
+/* Revision types -------------------------------------------------------*/
+
+#define DRXJ_TYPE_ID (0x3946000DUL)
+
+/* Macros ---------------------------------------------------------------*/
+
+/* Convert OOB lock status to string */
+#define DRXJ_STR_OOB_LOCKSTATUS(x) ( \
+ (x == DRX_NEVER_LOCK) ? "Never" : \
+ (x == DRX_NOT_LOCKED) ? "No" : \
+ (x == DRX_LOCKED) ? "Locked" : \
+ (x == DRX_LOCK_STATE_1) ? "AGC lock" : \
+ (x == DRX_LOCK_STATE_2) ? "sync lock" : \
+ "(Invalid)")
+
+#endif /* __DRXJ_H__ */
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj_map.h b/drivers/media/dvb-frontends/drx39xyj/drxj_map.h
new file mode 100644
index 000000000000..0bbd4ae1f524
--- /dev/null
+++ b/drivers/media/dvb-frontends/drx39xyj/drxj_map.h
@@ -0,0 +1,15055 @@
+/*
+ Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of Trident Microsystems nor Hauppauge Computer Works
+ nor the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ ***********************************************************************************************************************
+ * WARNING - THIS FILE HAS BEEN GENERATED - DO NOT CHANGE
+ *
+ * Filename: drxj_map.h
+ * Generated on: Mon Jan 18 12:09:24 2010
+ * Generated by: IDF:x 1.3.0
+ * Generated from: reg_map
+ * Output start: [entry point]
+ *
+ * filename last modified re-use
+ * -----------------------------------------------------
+ * reg_map.1.tmp Mon Jan 18 12:09:24 2010 -
+ *
+ */
+
+#ifndef __DRXJ_MAP__H__
+#define __DRXJ_MAP__H__ INCLUDED
+
+#ifdef _REGISTERTABLE_
+#include <registertable.h>
+ extern register_table_t drxj_map[];
+ extern register_table_info_t drxj_map_info[];
+#endif
+
+#define ATV_COMM_EXEC__A 0xC00000
+#define ATV_COMM_EXEC__W 2
+#define ATV_COMM_EXEC__M 0x3
+#define ATV_COMM_EXEC__PRE 0x0
+#define ATV_COMM_EXEC_STOP 0x0
+#define ATV_COMM_EXEC_ACTIVE 0x1
+#define ATV_COMM_EXEC_HOLD 0x2
+
+#define ATV_COMM_STATE__A 0xC00001
+#define ATV_COMM_STATE__W 16
+#define ATV_COMM_STATE__M 0xFFFF
+#define ATV_COMM_STATE__PRE 0x0
+#define ATV_COMM_MB__A 0xC00002
+#define ATV_COMM_MB__W 16
+#define ATV_COMM_MB__M 0xFFFF
+#define ATV_COMM_MB__PRE 0x0
+#define ATV_COMM_INT_REQ__A 0xC00003
+#define ATV_COMM_INT_REQ__W 16
+#define ATV_COMM_INT_REQ__M 0xFFFF
+#define ATV_COMM_INT_REQ__PRE 0x0
+#define ATV_COMM_INT_REQ_COMM_INT_REQ__B 0
+#define ATV_COMM_INT_REQ_COMM_INT_REQ__W 1
+#define ATV_COMM_INT_REQ_COMM_INT_REQ__M 0x1
+#define ATV_COMM_INT_REQ_COMM_INT_REQ__PRE 0x0
+
+#define ATV_COMM_INT_STA__A 0xC00005
+#define ATV_COMM_INT_STA__W 16
+#define ATV_COMM_INT_STA__M 0xFFFF
+#define ATV_COMM_INT_STA__PRE 0x0
+#define ATV_COMM_INT_MSK__A 0xC00006
+#define ATV_COMM_INT_MSK__W 16
+#define ATV_COMM_INT_MSK__M 0xFFFF
+#define ATV_COMM_INT_MSK__PRE 0x0
+#define ATV_COMM_INT_STM__A 0xC00007
+#define ATV_COMM_INT_STM__W 16
+#define ATV_COMM_INT_STM__M 0xFFFF
+#define ATV_COMM_INT_STM__PRE 0x0
+
+#define ATV_COMM_KEY__A 0xC0000F
+#define ATV_COMM_KEY__W 16
+#define ATV_COMM_KEY__M 0xFFFF
+#define ATV_COMM_KEY__PRE 0x0
+#define ATV_COMM_KEY_KEY 0xFABA
+#define ATV_COMM_KEY_MIN 0x0
+#define ATV_COMM_KEY_MAX 0xFFFF
+
+#define ATV_TOP_COMM_EXEC__A 0xC10000
+#define ATV_TOP_COMM_EXEC__W 2
+#define ATV_TOP_COMM_EXEC__M 0x3
+#define ATV_TOP_COMM_EXEC__PRE 0x0
+#define ATV_TOP_COMM_EXEC_STOP 0x0
+#define ATV_TOP_COMM_EXEC_ACTIVE 0x1
+#define ATV_TOP_COMM_EXEC_HOLD 0x2
+
+#define ATV_TOP_COMM_STATE__A 0xC10001
+#define ATV_TOP_COMM_STATE__W 16
+#define ATV_TOP_COMM_STATE__M 0xFFFF
+#define ATV_TOP_COMM_STATE__PRE 0x0
+#define ATV_TOP_COMM_STATE_STATE__B 0
+#define ATV_TOP_COMM_STATE_STATE__W 16
+#define ATV_TOP_COMM_STATE_STATE__M 0xFFFF
+#define ATV_TOP_COMM_STATE_STATE__PRE 0x0
+
+#define ATV_TOP_COMM_MB__A 0xC10002
+#define ATV_TOP_COMM_MB__W 16
+#define ATV_TOP_COMM_MB__M 0xFFFF
+#define ATV_TOP_COMM_MB__PRE 0x0
+#define ATV_TOP_COMM_MB_CTL__B 0
+#define ATV_TOP_COMM_MB_CTL__W 1
+#define ATV_TOP_COMM_MB_CTL__M 0x1
+#define ATV_TOP_COMM_MB_CTL__PRE 0x0
+#define ATV_TOP_COMM_MB_OBS__B 1
+#define ATV_TOP_COMM_MB_OBS__W 1
+#define ATV_TOP_COMM_MB_OBS__M 0x2
+#define ATV_TOP_COMM_MB_OBS__PRE 0x0
+
+#define ATV_TOP_COMM_MB_MUX_CTRL__B 2
+#define ATV_TOP_COMM_MB_MUX_CTRL__W 4
+#define ATV_TOP_COMM_MB_MUX_CTRL__M 0x3C
+#define ATV_TOP_COMM_MB_MUX_CTRL__PRE 0x0
+#define ATV_TOP_COMM_MB_MUX_CTRL_PEAK_S 0x0
+#define ATV_TOP_COMM_MB_MUX_CTRL_VID_GAIN 0x4
+#define ATV_TOP_COMM_MB_MUX_CTRL_CORR_O 0x8
+#define ATV_TOP_COMM_MB_MUX_CTRL_CR_ROT_O 0xC
+#define ATV_TOP_COMM_MB_MUX_CTRL_CR_IIR_IQ 0x10
+#define ATV_TOP_COMM_MB_MUX_CTRL_VIDEO_O 0x14
+#define ATV_TOP_COMM_MB_MUX_CTRL_SIF_O 0x18
+#define ATV_TOP_COMM_MB_MUX_CTRL_SIF2025_O 0x1C
+#define ATV_TOP_COMM_MB_MUX_CTRL_POST_S 0x20
+
+#define ATV_TOP_COMM_MB_MUX_OBS__B 6
+#define ATV_TOP_COMM_MB_MUX_OBS__W 4
+#define ATV_TOP_COMM_MB_MUX_OBS__M 0x3C0
+#define ATV_TOP_COMM_MB_MUX_OBS__PRE 0x0
+#define ATV_TOP_COMM_MB_MUX_OBS_PEAK_S 0x0
+#define ATV_TOP_COMM_MB_MUX_OBS_VID_GAIN 0x40
+#define ATV_TOP_COMM_MB_MUX_OBS_CORR_O 0x80
+#define ATV_TOP_COMM_MB_MUX_OBS_CR_ROT_O 0xC0
+#define ATV_TOP_COMM_MB_MUX_OBS_CR_IIR_IQ 0x100
+#define ATV_TOP_COMM_MB_MUX_OBS_VIDEO_O 0x140
+#define ATV_TOP_COMM_MB_MUX_OBS_SIF_O 0x180
+#define ATV_TOP_COMM_MB_MUX_OBS_SIF2025_O 0x1C0
+#define ATV_TOP_COMM_MB_MUX_OBS_POST_S 0x200
+
+#define ATV_TOP_COMM_INT_REQ__A 0xC10003
+#define ATV_TOP_COMM_INT_REQ__W 16
+#define ATV_TOP_COMM_INT_REQ__M 0xFFFF
+#define ATV_TOP_COMM_INT_REQ__PRE 0x0
+#define ATV_TOP_COMM_INT_STA__A 0xC10005
+#define ATV_TOP_COMM_INT_STA__W 16
+#define ATV_TOP_COMM_INT_STA__M 0xFFFF
+#define ATV_TOP_COMM_INT_STA__PRE 0x0
+
+#define ATV_TOP_COMM_INT_STA_FAGC_STA__B 0
+#define ATV_TOP_COMM_INT_STA_FAGC_STA__W 1
+#define ATV_TOP_COMM_INT_STA_FAGC_STA__M 0x1
+#define ATV_TOP_COMM_INT_STA_FAGC_STA__PRE 0x0
+
+#define ATV_TOP_COMM_INT_STA_OVM_STA__B 1
+#define ATV_TOP_COMM_INT_STA_OVM_STA__W 1
+#define ATV_TOP_COMM_INT_STA_OVM_STA__M 0x2
+#define ATV_TOP_COMM_INT_STA_OVM_STA__PRE 0x0
+
+#define ATV_TOP_COMM_INT_STA_AMPTH_STA__B 2
+#define ATV_TOP_COMM_INT_STA_AMPTH_STA__W 1
+#define ATV_TOP_COMM_INT_STA_AMPTH_STA__M 0x4
+#define ATV_TOP_COMM_INT_STA_AMPTH_STA__PRE 0x0
+
+#define ATV_TOP_COMM_INT_MSK__A 0xC10006
+#define ATV_TOP_COMM_INT_MSK__W 16
+#define ATV_TOP_COMM_INT_MSK__M 0xFFFF
+#define ATV_TOP_COMM_INT_MSK__PRE 0x0
+
+#define ATV_TOP_COMM_INT_MSK_FAGC_MSK__B 0
+#define ATV_TOP_COMM_INT_MSK_FAGC_MSK__W 1
+#define ATV_TOP_COMM_INT_MSK_FAGC_MSK__M 0x1
+#define ATV_TOP_COMM_INT_MSK_FAGC_MSK__PRE 0x0
+
+#define ATV_TOP_COMM_INT_MSK_OVM_MSK__B 1
+#define ATV_TOP_COMM_INT_MSK_OVM_MSK__W 1
+#define ATV_TOP_COMM_INT_MSK_OVM_MSK__M 0x2
+#define ATV_TOP_COMM_INT_MSK_OVM_MSK__PRE 0x0
+
+#define ATV_TOP_COMM_INT_MSK_AMPTH_MSK__B 2
+#define ATV_TOP_COMM_INT_MSK_AMPTH_MSK__W 1
+#define ATV_TOP_COMM_INT_MSK_AMPTH_MSK__M 0x4
+#define ATV_TOP_COMM_INT_MSK_AMPTH_MSK__PRE 0x0
+
+#define ATV_TOP_COMM_INT_STM__A 0xC10007
+#define ATV_TOP_COMM_INT_STM__W 16
+#define ATV_TOP_COMM_INT_STM__M 0xFFFF
+#define ATV_TOP_COMM_INT_STM__PRE 0x0
+
+#define ATV_TOP_COMM_INT_STM_FAGC_STM__B 0
+#define ATV_TOP_COMM_INT_STM_FAGC_STM__W 1
+#define ATV_TOP_COMM_INT_STM_FAGC_STM__M 0x1
+#define ATV_TOP_COMM_INT_STM_FAGC_STM__PRE 0x0
+
+#define ATV_TOP_COMM_INT_STM_OVM_STM__B 1
+#define ATV_TOP_COMM_INT_STM_OVM_STM__W 1
+#define ATV_TOP_COMM_INT_STM_OVM_STM__M 0x2
+#define ATV_TOP_COMM_INT_STM_OVM_STM__PRE 0x0
+
+#define ATV_TOP_COMM_INT_STM_AMPTH_STM__B 2
+#define ATV_TOP_COMM_INT_STM_AMPTH_STM__W 1
+#define ATV_TOP_COMM_INT_STM_AMPTH_STM__M 0x4
+#define ATV_TOP_COMM_INT_STM_AMPTH_STM__PRE 0x0
+
+#define ATV_TOP_COMM_KEY__A 0xC1000F
+#define ATV_TOP_COMM_KEY__W 16
+#define ATV_TOP_COMM_KEY__M 0xFFFF
+#define ATV_TOP_COMM_KEY__PRE 0x0
+
+#define ATV_TOP_COMM_KEY_KEY__B 0
+#define ATV_TOP_COMM_KEY_KEY__W 16
+#define ATV_TOP_COMM_KEY_KEY__M 0xFFFF
+#define ATV_TOP_COMM_KEY_KEY__PRE 0x0
+#define ATV_TOP_COMM_KEY_KEY_KEY 0xFABA
+#define ATV_TOP_COMM_KEY_KEY_MIN 0x0
+#define ATV_TOP_COMM_KEY_KEY_MAX 0xFFFF
+
+#define ATV_TOP_CR_AMP_TH__A 0xC10010
+#define ATV_TOP_CR_AMP_TH__W 8
+#define ATV_TOP_CR_AMP_TH__M 0xFF
+#define ATV_TOP_CR_AMP_TH__PRE 0x8
+#define ATV_TOP_CR_AMP_TH_MN 0x8
+
+#define ATV_TOP_CR_CONT__A 0xC10011
+#define ATV_TOP_CR_CONT__W 9
+#define ATV_TOP_CR_CONT__M 0x1FF
+#define ATV_TOP_CR_CONT__PRE 0x9C
+
+#define ATV_TOP_CR_CONT_CR_P__B 0
+#define ATV_TOP_CR_CONT_CR_P__W 3
+#define ATV_TOP_CR_CONT_CR_P__M 0x7
+#define ATV_TOP_CR_CONT_CR_P__PRE 0x4
+#define ATV_TOP_CR_CONT_CR_P_MN 0x4
+#define ATV_TOP_CR_CONT_CR_P_FM 0x0
+
+#define ATV_TOP_CR_CONT_CR_D__B 3
+#define ATV_TOP_CR_CONT_CR_D__W 3
+#define ATV_TOP_CR_CONT_CR_D__M 0x38
+#define ATV_TOP_CR_CONT_CR_D__PRE 0x18
+#define ATV_TOP_CR_CONT_CR_D_MN 0x18
+#define ATV_TOP_CR_CONT_CR_D_FM 0x0
+
+#define ATV_TOP_CR_CONT_CR_I__B 6
+#define ATV_TOP_CR_CONT_CR_I__W 3
+#define ATV_TOP_CR_CONT_CR_I__M 0x1C0
+#define ATV_TOP_CR_CONT_CR_I__PRE 0x80
+#define ATV_TOP_CR_CONT_CR_I_MN 0x80
+#define ATV_TOP_CR_CONT_CR_I_FM 0x0
+
+#define ATV_TOP_CR_OVM_TH__A 0xC10012
+#define ATV_TOP_CR_OVM_TH__W 8
+#define ATV_TOP_CR_OVM_TH__M 0xFF
+#define ATV_TOP_CR_OVM_TH__PRE 0xA0
+#define ATV_TOP_CR_OVM_TH_MN 0xA0
+#define ATV_TOP_CR_OVM_TH_FM 0x0
+
+#define ATV_TOP_NOISE_TH__A 0xC10013
+#define ATV_TOP_NOISE_TH__W 4
+#define ATV_TOP_NOISE_TH__M 0xF
+#define ATV_TOP_NOISE_TH__PRE 0x8
+#define ATV_TOP_NOISE_TH_MN 0x8
+
+#define ATV_TOP_EQU0__A 0xC10014
+#define ATV_TOP_EQU0__W 9
+#define ATV_TOP_EQU0__M 0x1FF
+#define ATV_TOP_EQU0__PRE 0x1FB
+
+#define ATV_TOP_EQU0_EQU_C0__B 0
+#define ATV_TOP_EQU0_EQU_C0__W 9
+#define ATV_TOP_EQU0_EQU_C0__M 0x1FF
+#define ATV_TOP_EQU0_EQU_C0__PRE 0x1FB
+#define ATV_TOP_EQU0_EQU_C0_MN 0xFB
+
+#define ATV_TOP_EQU1__A 0xC10015
+#define ATV_TOP_EQU1__W 9
+#define ATV_TOP_EQU1__M 0x1FF
+#define ATV_TOP_EQU1__PRE 0x1CE
+
+#define ATV_TOP_EQU1_EQU_C1__B 0
+#define ATV_TOP_EQU1_EQU_C1__W 9
+#define ATV_TOP_EQU1_EQU_C1__M 0x1FF
+#define ATV_TOP_EQU1_EQU_C1__PRE 0x1CE
+#define ATV_TOP_EQU1_EQU_C1_MN 0xCE
+
+#define ATV_TOP_EQU2__A 0xC10016
+#define ATV_TOP_EQU2__W 9
+#define ATV_TOP_EQU2__M 0x1FF
+#define ATV_TOP_EQU2__PRE 0xD2
+
+#define ATV_TOP_EQU2_EQU_C2__B 0
+#define ATV_TOP_EQU2_EQU_C2__W 9
+#define ATV_TOP_EQU2_EQU_C2__M 0x1FF
+#define ATV_TOP_EQU2_EQU_C2__PRE 0xD2
+#define ATV_TOP_EQU2_EQU_C2_MN 0xD2
+
+#define ATV_TOP_EQU3__A 0xC10017
+#define ATV_TOP_EQU3__W 9
+#define ATV_TOP_EQU3__M 0x1FF
+#define ATV_TOP_EQU3__PRE 0x160
+
+#define ATV_TOP_EQU3_EQU_C3__B 0
+#define ATV_TOP_EQU3_EQU_C3__W 9
+#define ATV_TOP_EQU3_EQU_C3__M 0x1FF
+#define ATV_TOP_EQU3_EQU_C3__PRE 0x160
+#define ATV_TOP_EQU3_EQU_C3_MN 0x60
+
+#define ATV_TOP_ROT_MODE__A 0xC10018
+#define ATV_TOP_ROT_MODE__W 1
+#define ATV_TOP_ROT_MODE__M 0x1
+#define ATV_TOP_ROT_MODE__PRE 0x0
+#define ATV_TOP_ROT_MODE_AMPTH_DEPEND 0x0
+#define ATV_TOP_ROT_MODE_ALWAYS 0x1
+
+#define ATV_TOP_MOD_CONTROL__A 0xC10019
+#define ATV_TOP_MOD_CONTROL__W 12
+#define ATV_TOP_MOD_CONTROL__M 0xFFF
+#define ATV_TOP_MOD_CONTROL__PRE 0x5B1
+
+#define ATV_TOP_MOD_CONTROL_MOD_IR__B 0
+#define ATV_TOP_MOD_CONTROL_MOD_IR__W 3
+#define ATV_TOP_MOD_CONTROL_MOD_IR__M 0x7
+#define ATV_TOP_MOD_CONTROL_MOD_IR__PRE 0x1
+#define ATV_TOP_MOD_CONTROL_MOD_IR_MN 0x1
+#define ATV_TOP_MOD_CONTROL_MOD_IR_FM 0x0
+
+#define ATV_TOP_MOD_CONTROL_MOD_IF__B 3
+#define ATV_TOP_MOD_CONTROL_MOD_IF__W 4
+#define ATV_TOP_MOD_CONTROL_MOD_IF__M 0x78
+#define ATV_TOP_MOD_CONTROL_MOD_IF__PRE 0x30
+#define ATV_TOP_MOD_CONTROL_MOD_IF_MN 0x30
+#define ATV_TOP_MOD_CONTROL_MOD_IF_FM 0x0
+
+#define ATV_TOP_MOD_CONTROL_MOD_MODE__B 7
+#define ATV_TOP_MOD_CONTROL_MOD_MODE__W 1
+#define ATV_TOP_MOD_CONTROL_MOD_MODE__M 0x80
+#define ATV_TOP_MOD_CONTROL_MOD_MODE__PRE 0x80
+#define ATV_TOP_MOD_CONTROL_MOD_MODE_RISE 0x0
+#define ATV_TOP_MOD_CONTROL_MOD_MODE_RISE_FALL 0x80
+
+#define ATV_TOP_MOD_CONTROL_MOD_TH__B 8
+#define ATV_TOP_MOD_CONTROL_MOD_TH__W 4
+#define ATV_TOP_MOD_CONTROL_MOD_TH__M 0xF00
+#define ATV_TOP_MOD_CONTROL_MOD_TH__PRE 0x500
+#define ATV_TOP_MOD_CONTROL_MOD_TH_MN 0x500
+#define ATV_TOP_MOD_CONTROL_MOD_TH_FM 0x0
+
+#define ATV_TOP_STD__A 0xC1001A
+#define ATV_TOP_STD__W 2
+#define ATV_TOP_STD__M 0x3
+#define ATV_TOP_STD__PRE 0x0
+
+#define ATV_TOP_STD_MODE__B 0
+#define ATV_TOP_STD_MODE__W 1
+#define ATV_TOP_STD_MODE__M 0x1
+#define ATV_TOP_STD_MODE__PRE 0x0
+#define ATV_TOP_STD_MODE_MN 0x0
+#define ATV_TOP_STD_MODE_FM 0x1
+
+#define ATV_TOP_STD_VID_POL__B 1
+#define ATV_TOP_STD_VID_POL__W 1
+#define ATV_TOP_STD_VID_POL__M 0x2
+#define ATV_TOP_STD_VID_POL__PRE 0x0
+#define ATV_TOP_STD_VID_POL_NEG 0x0
+#define ATV_TOP_STD_VID_POL_POS 0x2
+
+#define ATV_TOP_VID_AMP__A 0xC1001B
+#define ATV_TOP_VID_AMP__W 12
+#define ATV_TOP_VID_AMP__M 0xFFF
+#define ATV_TOP_VID_AMP__PRE 0x380
+#define ATV_TOP_VID_AMP_MN 0x380
+#define ATV_TOP_VID_AMP_FM 0x0
+
+#define ATV_TOP_VID_PEAK__A 0xC1001C
+#define ATV_TOP_VID_PEAK__W 5
+#define ATV_TOP_VID_PEAK__M 0x1F
+#define ATV_TOP_VID_PEAK__PRE 0x1
+
+#define ATV_TOP_FAGC_TH__A 0xC1001D
+#define ATV_TOP_FAGC_TH__W 11
+#define ATV_TOP_FAGC_TH__M 0x7FF
+#define ATV_TOP_FAGC_TH__PRE 0x2B2
+#define ATV_TOP_FAGC_TH_MN 0x2B2
+
+#define ATV_TOP_SYNC_SLICE__A 0xC1001E
+#define ATV_TOP_SYNC_SLICE__W 11
+#define ATV_TOP_SYNC_SLICE__M 0x7FF
+#define ATV_TOP_SYNC_SLICE__PRE 0x243
+#define ATV_TOP_SYNC_SLICE_MN 0x243
+
+#define ATV_TOP_SIF_GAIN__A 0xC1001F
+#define ATV_TOP_SIF_GAIN__W 11
+#define ATV_TOP_SIF_GAIN__M 0x7FF
+#define ATV_TOP_SIF_GAIN__PRE 0x0
+
+#define ATV_TOP_SIF_TP__A 0xC10020
+#define ATV_TOP_SIF_TP__W 6
+#define ATV_TOP_SIF_TP__M 0x3F
+#define ATV_TOP_SIF_TP__PRE 0x0
+
+#define ATV_TOP_MOD_ACCU__A 0xC10021
+#define ATV_TOP_MOD_ACCU__W 10
+#define ATV_TOP_MOD_ACCU__M 0x3FF
+#define ATV_TOP_MOD_ACCU__PRE 0x0
+
+#define ATV_TOP_CR_FREQ__A 0xC10022
+#define ATV_TOP_CR_FREQ__W 8
+#define ATV_TOP_CR_FREQ__M 0xFF
+#define ATV_TOP_CR_FREQ__PRE 0x0
+
+#define ATV_TOP_CR_PHAD__A 0xC10023
+#define ATV_TOP_CR_PHAD__W 12
+#define ATV_TOP_CR_PHAD__M 0xFFF
+#define ATV_TOP_CR_PHAD__PRE 0x0
+
+#define ATV_TOP_AF_SIF_ATT__A 0xC10024
+#define ATV_TOP_AF_SIF_ATT__W 2
+#define ATV_TOP_AF_SIF_ATT__M 0x3
+#define ATV_TOP_AF_SIF_ATT__PRE 0x0
+#define ATV_TOP_AF_SIF_ATT_0DB 0x0
+#define ATV_TOP_AF_SIF_ATT_M3DB 0x1
+#define ATV_TOP_AF_SIF_ATT_M6DB 0x2
+#define ATV_TOP_AF_SIF_ATT_M9DB 0x3
+
+#define ATV_TOP_STDBY__A 0xC10025
+#define ATV_TOP_STDBY__W 2
+#define ATV_TOP_STDBY__M 0x3
+#define ATV_TOP_STDBY__PRE 0x1
+
+#define ATV_TOP_STDBY_SIF_STDBY__B 0
+#define ATV_TOP_STDBY_SIF_STDBY__W 1
+#define ATV_TOP_STDBY_SIF_STDBY__M 0x1
+#define ATV_TOP_STDBY_SIF_STDBY__PRE 0x1
+#define ATV_TOP_STDBY_SIF_STDBY_ACTIVE 0x0
+#define ATV_TOP_STDBY_SIF_STDBY_STANDBY 0x1
+
+#define ATV_TOP_STDBY_CVBS_STDBY__B 1
+#define ATV_TOP_STDBY_CVBS_STDBY__W 1
+#define ATV_TOP_STDBY_CVBS_STDBY__M 0x2
+#define ATV_TOP_STDBY_CVBS_STDBY__PRE 0x0
+#define ATV_TOP_STDBY_CVBS_STDBY_A1_ACTIVE 0x0
+#define ATV_TOP_STDBY_CVBS_STDBY_A1_STANDBY 0x2
+#define ATV_TOP_STDBY_CVBS_STDBY_A2_ACTIVE 0x2
+#define ATV_TOP_STDBY_CVBS_STDBY_A2_STANDBY 0x0
+
+#define ATV_TOP_OVERRIDE_SFR__A 0xC10026
+#define ATV_TOP_OVERRIDE_SFR__W 1
+#define ATV_TOP_OVERRIDE_SFR__M 0x1
+#define ATV_TOP_OVERRIDE_SFR__PRE 0x0
+#define ATV_TOP_OVERRIDE_SFR_ACTIVE 0x0
+#define ATV_TOP_OVERRIDE_SFR_OVERRIDE 0x1
+
+#define ATV_TOP_SFR_VID_GAIN__A 0xC10027
+#define ATV_TOP_SFR_VID_GAIN__W 16
+#define ATV_TOP_SFR_VID_GAIN__M 0xFFFF
+#define ATV_TOP_SFR_VID_GAIN__PRE 0x0
+
+#define ATV_TOP_SFR_AGC_RES__A 0xC10028
+#define ATV_TOP_SFR_AGC_RES__W 5
+#define ATV_TOP_SFR_AGC_RES__M 0x1F
+#define ATV_TOP_SFR_AGC_RES__PRE 0x0
+
+#define ATV_TOP_OVM_COMP__A 0xC10029
+#define ATV_TOP_OVM_COMP__W 12
+#define ATV_TOP_OVM_COMP__M 0xFFF
+#define ATV_TOP_OVM_COMP__PRE 0x0
+#define ATV_TOP_OUT_CONF__A 0xC1002A
+#define ATV_TOP_OUT_CONF__W 5
+#define ATV_TOP_OUT_CONF__M 0x1F
+#define ATV_TOP_OUT_CONF__PRE 0x0
+
+#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN__B 0
+#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN__W 1
+#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN__M 0x1
+#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN__PRE 0x0
+#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN_UNSIGNED 0x0
+#define ATV_TOP_OUT_CONF_CVBS_DAC_SIGN_SIGNED 0x1
+
+#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN__B 1
+#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN__W 1
+#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN__M 0x2
+#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN__PRE 0x0
+#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN_UNSIGNED 0x0
+#define ATV_TOP_OUT_CONF_SIF_DAC_SIGN_SIGNED 0x2
+
+#define ATV_TOP_OUT_CONF_SIF20_SIGN__B 2
+#define ATV_TOP_OUT_CONF_SIF20_SIGN__W 1
+#define ATV_TOP_OUT_CONF_SIF20_SIGN__M 0x4
+#define ATV_TOP_OUT_CONF_SIF20_SIGN__PRE 0x0
+#define ATV_TOP_OUT_CONF_SIF20_SIGN_UNSIGNED 0x0
+#define ATV_TOP_OUT_CONF_SIF20_SIGN_SIGNED 0x4
+
+#define ATV_TOP_OUT_CONF_CVBS_DAC_BR__B 3
+#define ATV_TOP_OUT_CONF_CVBS_DAC_BR__W 1
+#define ATV_TOP_OUT_CONF_CVBS_DAC_BR__M 0x8
+#define ATV_TOP_OUT_CONF_CVBS_DAC_BR__PRE 0x0
+#define ATV_TOP_OUT_CONF_CVBS_DAC_BR_NORMAL 0x0
+#define ATV_TOP_OUT_CONF_CVBS_DAC_BR_BITREVERSED 0x8
+
+#define ATV_TOP_OUT_CONF_SIF_DAC_BR__B 4
+#define ATV_TOP_OUT_CONF_SIF_DAC_BR__W 1
+#define ATV_TOP_OUT_CONF_SIF_DAC_BR__M 0x10
+#define ATV_TOP_OUT_CONF_SIF_DAC_BR__PRE 0x0
+#define ATV_TOP_OUT_CONF_SIF_DAC_BR_NORMAL 0x0
+#define ATV_TOP_OUT_CONF_SIF_DAC_BR_BITREVERSED 0x10
+
+#define ATV_AFT_COMM_EXEC__A 0xFF0000
+#define ATV_AFT_COMM_EXEC__W 2
+#define ATV_AFT_COMM_EXEC__M 0x3
+#define ATV_AFT_COMM_EXEC__PRE 0x0
+#define ATV_AFT_COMM_EXEC_STOP 0x0
+#define ATV_AFT_COMM_EXEC_ACTIVE 0x1
+#define ATV_AFT_COMM_EXEC_HOLD 0x2
+
+#define ATV_AFT_TST__A 0xFF0010
+#define ATV_AFT_TST__W 4
+#define ATV_AFT_TST__M 0xF
+#define ATV_AFT_TST__PRE 0x0
+
+#define AUD_COMM_EXEC__A 0x1000000
+#define AUD_COMM_EXEC__W 2
+#define AUD_COMM_EXEC__M 0x3
+#define AUD_COMM_EXEC__PRE 0x0
+#define AUD_COMM_EXEC_STOP 0x0
+#define AUD_COMM_EXEC_ACTIVE 0x1
+
+#define AUD_COMM_MB__A 0x1000002
+#define AUD_COMM_MB__W 16
+#define AUD_COMM_MB__M 0xFFFF
+#define AUD_COMM_MB__PRE 0x0
+
+#define AUD_TOP_COMM_EXEC__A 0x1010000
+#define AUD_TOP_COMM_EXEC__W 2
+#define AUD_TOP_COMM_EXEC__M 0x3
+#define AUD_TOP_COMM_EXEC__PRE 0x0
+#define AUD_TOP_COMM_EXEC_STOP 0x0
+#define AUD_TOP_COMM_EXEC_ACTIVE 0x1
+
+#define AUD_TOP_COMM_MB__A 0x1010002
+#define AUD_TOP_COMM_MB__W 16
+#define AUD_TOP_COMM_MB__M 0xFFFF
+#define AUD_TOP_COMM_MB__PRE 0x0
+
+#define AUD_TOP_COMM_MB_CTL__B 0
+#define AUD_TOP_COMM_MB_CTL__W 1
+#define AUD_TOP_COMM_MB_CTL__M 0x1
+#define AUD_TOP_COMM_MB_CTL__PRE 0x0
+#define AUD_TOP_COMM_MB_CTL_CTR_OFF 0x0
+#define AUD_TOP_COMM_MB_CTL_CTR_ON 0x1
+
+#define AUD_TOP_COMM_MB_OBS__B 1
+#define AUD_TOP_COMM_MB_OBS__W 1
+#define AUD_TOP_COMM_MB_OBS__M 0x2
+#define AUD_TOP_COMM_MB_OBS__PRE 0x0
+#define AUD_TOP_COMM_MB_OBS_OBS_OFF 0x0
+#define AUD_TOP_COMM_MB_OBS_OBS_ON 0x2
+
+#define AUD_TOP_COMM_MB_MUX_CTRL__B 2
+#define AUD_TOP_COMM_MB_MUX_CTRL__W 4
+#define AUD_TOP_COMM_MB_MUX_CTRL__M 0x3C
+#define AUD_TOP_COMM_MB_MUX_CTRL__PRE 0x0
+#define AUD_TOP_COMM_MB_MUX_CTRL_DEMOD_TBO 0x0
+#define AUD_TOP_COMM_MB_MUX_CTRL_XDFP_IRQS 0x4
+#define AUD_TOP_COMM_MB_MUX_CTRL_OBSERVEPC 0x8
+#define AUD_TOP_COMM_MB_MUX_CTRL_SAOUT 0xC
+#define AUD_TOP_COMM_MB_MUX_CTRL_XDFP_SCHEQ 0x10
+
+#define AUD_TOP_COMM_MB_MUX_OBS__B 6
+#define AUD_TOP_COMM_MB_MUX_OBS__W 4
+#define AUD_TOP_COMM_MB_MUX_OBS__M 0x3C0
+#define AUD_TOP_COMM_MB_MUX_OBS__PRE 0x0
+#define AUD_TOP_COMM_MB_MUX_OBS_DEMOD_TBO 0x0
+#define AUD_TOP_COMM_MB_MUX_OBS_XDFP_IRQS 0x40
+#define AUD_TOP_COMM_MB_MUX_OBS_OBSERVEPC 0x80
+#define AUD_TOP_COMM_MB_MUX_OBS_SAOUT 0xC0
+#define AUD_TOP_COMM_MB_MUX_OBS_XDFP_SCHEQ 0x100
+
+#define AUD_TOP_TR_MDE__A 0x1010010
+#define AUD_TOP_TR_MDE__W 5
+#define AUD_TOP_TR_MDE__M 0x1F
+#define AUD_TOP_TR_MDE__PRE 0x18
+
+#define AUD_TOP_TR_MDE_FIFO_SIZE__B 0
+#define AUD_TOP_TR_MDE_FIFO_SIZE__W 4
+#define AUD_TOP_TR_MDE_FIFO_SIZE__M 0xF
+#define AUD_TOP_TR_MDE_FIFO_SIZE__PRE 0x8
+
+#define AUD_TOP_TR_MDE_RD_LOCK__B 4
+#define AUD_TOP_TR_MDE_RD_LOCK__W 1
+#define AUD_TOP_TR_MDE_RD_LOCK__M 0x10
+#define AUD_TOP_TR_MDE_RD_LOCK__PRE 0x10
+#define AUD_TOP_TR_MDE_RD_LOCK_NORMAL 0x0
+#define AUD_TOP_TR_MDE_RD_LOCK_LOCK 0x10
+
+#define AUD_TOP_TR_CTR__A 0x1010011
+#define AUD_TOP_TR_CTR__W 4
+#define AUD_TOP_TR_CTR__M 0xF
+#define AUD_TOP_TR_CTR__PRE 0x0
+
+#define AUD_TOP_TR_CTR_FIFO_RD_RDY__B 0
+#define AUD_TOP_TR_CTR_FIFO_RD_RDY__W 1
+#define AUD_TOP_TR_CTR_FIFO_RD_RDY__M 0x1
+#define AUD_TOP_TR_CTR_FIFO_RD_RDY__PRE 0x0
+#define AUD_TOP_TR_CTR_FIFO_RD_RDY_NOT_READY 0x0
+#define AUD_TOP_TR_CTR_FIFO_RD_RDY_READY 0x1
+
+#define AUD_TOP_TR_CTR_FIFO_EMPTY__B 1
+#define AUD_TOP_TR_CTR_FIFO_EMPTY__W 1
+#define AUD_TOP_TR_CTR_FIFO_EMPTY__M 0x2
+#define AUD_TOP_TR_CTR_FIFO_EMPTY__PRE 0x0
+#define AUD_TOP_TR_CTR_FIFO_EMPTY_NOT_EMPTY 0x0
+#define AUD_TOP_TR_CTR_FIFO_EMPTY_EMPTY 0x2
+
+#define AUD_TOP_TR_CTR_FIFO_LOCK__B 2
+#define AUD_TOP_TR_CTR_FIFO_LOCK__W 1
+#define AUD_TOP_TR_CTR_FIFO_LOCK__M 0x4
+#define AUD_TOP_TR_CTR_FIFO_LOCK__PRE 0x0
+#define AUD_TOP_TR_CTR_FIFO_LOCK_UNLOCKED 0x0
+#define AUD_TOP_TR_CTR_FIFO_LOCK_LOCKED 0x4
+
+#define AUD_TOP_TR_CTR_FIFO_FULL__B 3
+#define AUD_TOP_TR_CTR_FIFO_FULL__W 1
+#define AUD_TOP_TR_CTR_FIFO_FULL__M 0x8
+#define AUD_TOP_TR_CTR_FIFO_FULL__PRE 0x0
+#define AUD_TOP_TR_CTR_FIFO_FULL_EMPTY 0x0
+#define AUD_TOP_TR_CTR_FIFO_FULL_FULL 0x8
+
+#define AUD_TOP_TR_RD_REG__A 0x1010012
+#define AUD_TOP_TR_RD_REG__W 16
+#define AUD_TOP_TR_RD_REG__M 0xFFFF
+#define AUD_TOP_TR_RD_REG__PRE 0x0
+
+#define AUD_TOP_TR_RD_REG_RESULT__B 0
+#define AUD_TOP_TR_RD_REG_RESULT__W 16
+#define AUD_TOP_TR_RD_REG_RESULT__M 0xFFFF
+#define AUD_TOP_TR_RD_REG_RESULT__PRE 0x0
+
+#define AUD_TOP_TR_TIMER__A 0x1010013
+#define AUD_TOP_TR_TIMER__W 16
+#define AUD_TOP_TR_TIMER__M 0xFFFF
+#define AUD_TOP_TR_TIMER__PRE 0x0
+
+#define AUD_TOP_TR_TIMER_CYCLES__B 0
+#define AUD_TOP_TR_TIMER_CYCLES__W 16
+#define AUD_TOP_TR_TIMER_CYCLES__M 0xFFFF
+#define AUD_TOP_TR_TIMER_CYCLES__PRE 0x0
+
+#define AUD_TOP_DEMOD_TBO_SEL__A 0x1010014
+#define AUD_TOP_DEMOD_TBO_SEL__W 5
+#define AUD_TOP_DEMOD_TBO_SEL__M 0x1F
+#define AUD_TOP_DEMOD_TBO_SEL__PRE 0x0
+
+#define AUD_DEM_WR_MODUS__A 0x1030030
+#define AUD_DEM_WR_MODUS__W 16
+#define AUD_DEM_WR_MODUS__M 0xFFFF
+#define AUD_DEM_WR_MODUS__PRE 0x0
+
+#define AUD_DEM_WR_MODUS_MOD_ASS__B 0
+#define AUD_DEM_WR_MODUS_MOD_ASS__W 1
+#define AUD_DEM_WR_MODUS_MOD_ASS__M 0x1
+#define AUD_DEM_WR_MODUS_MOD_ASS__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_ASS_OFF 0x0
+#define AUD_DEM_WR_MODUS_MOD_ASS_ON 0x1
+
+#define AUD_DEM_WR_MODUS_MOD_STATINTERR__B 1
+#define AUD_DEM_WR_MODUS_MOD_STATINTERR__W 1
+#define AUD_DEM_WR_MODUS_MOD_STATINTERR__M 0x2
+#define AUD_DEM_WR_MODUS_MOD_STATINTERR__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_STATINTERR_DISABLE 0x0
+#define AUD_DEM_WR_MODUS_MOD_STATINTERR_ENABLE 0x2
+
+#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG__B 2
+#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG__W 1
+#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG__M 0x4
+#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG_ENABLED 0x0
+#define AUD_DEM_WR_MODUS_MOD_DIS_STD_CHG_DISABLED 0x4
+
+#define AUD_DEM_WR_MODUS_MOD_HDEV_A__B 8
+#define AUD_DEM_WR_MODUS_MOD_HDEV_A__W 1
+#define AUD_DEM_WR_MODUS_MOD_HDEV_A__M 0x100
+#define AUD_DEM_WR_MODUS_MOD_HDEV_A__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_HDEV_A_NORMAL 0x0
+#define AUD_DEM_WR_MODUS_MOD_HDEV_A_HIGH_DEVIATION 0x100
+
+#define AUD_DEM_WR_MODUS_MOD_CM_A__B 9
+#define AUD_DEM_WR_MODUS_MOD_CM_A__W 1
+#define AUD_DEM_WR_MODUS_MOD_CM_A__M 0x200
+#define AUD_DEM_WR_MODUS_MOD_CM_A__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_CM_A_MUTE 0x0
+#define AUD_DEM_WR_MODUS_MOD_CM_A_NOISE 0x200
+
+#define AUD_DEM_WR_MODUS_MOD_CM_B__B 10
+#define AUD_DEM_WR_MODUS_MOD_CM_B__W 1
+#define AUD_DEM_WR_MODUS_MOD_CM_B__M 0x400
+#define AUD_DEM_WR_MODUS_MOD_CM_B__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_CM_B_MUTE 0x0
+#define AUD_DEM_WR_MODUS_MOD_CM_B_NOISE 0x400
+
+#define AUD_DEM_WR_MODUS_MOD_FMRADIO__B 11
+#define AUD_DEM_WR_MODUS_MOD_FMRADIO__W 1
+#define AUD_DEM_WR_MODUS_MOD_FMRADIO__M 0x800
+#define AUD_DEM_WR_MODUS_MOD_FMRADIO__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_FMRADIO_US_75U 0x0
+#define AUD_DEM_WR_MODUS_MOD_FMRADIO_EU_50U 0x800
+
+#define AUD_DEM_WR_MODUS_MOD_6_5MHZ__B 12
+#define AUD_DEM_WR_MODUS_MOD_6_5MHZ__W 1
+#define AUD_DEM_WR_MODUS_MOD_6_5MHZ__M 0x1000
+#define AUD_DEM_WR_MODUS_MOD_6_5MHZ__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_6_5MHZ_SECAM 0x0
+#define AUD_DEM_WR_MODUS_MOD_6_5MHZ_D_K 0x1000
+
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ__B 13
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ__W 2
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ__M 0x6000
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ_M_KOREA 0x0
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ_M_BTSC 0x2000
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ_M_EIAJ 0x4000
+#define AUD_DEM_WR_MODUS_MOD_4_5MHZ_CHROMA 0x6000
+
+#define AUD_DEM_WR_MODUS_MOD_BTSC__B 15
+#define AUD_DEM_WR_MODUS_MOD_BTSC__W 1
+#define AUD_DEM_WR_MODUS_MOD_BTSC__M 0x8000
+#define AUD_DEM_WR_MODUS_MOD_BTSC__PRE 0x0
+#define AUD_DEM_WR_MODUS_MOD_BTSC_BTSC_STEREO 0x0
+#define AUD_DEM_WR_MODUS_MOD_BTSC_BTSC_SAP 0x8000
+
+#define AUD_DEM_WR_STANDARD_SEL__A 0x1030020
+#define AUD_DEM_WR_STANDARD_SEL__W 16
+#define AUD_DEM_WR_STANDARD_SEL__M 0xFFFF
+#define AUD_DEM_WR_STANDARD_SEL__PRE 0x0
+
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL__B 0
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL__W 12
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL__M 0xFFF
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL__PRE 0x0
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_AUTO 0x1
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_M_KOREA 0x2
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_BG_FM 0x3
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_D_K1 0x4
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_D_K2 0x5
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_D_K3 0x7
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_BG_NICAM_FM 0x8
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_L_NICAM_AM 0x9
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_I_NICAM_FM 0xA
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_D_K_NICAM_FM 0xB
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_BTSC_STEREO 0x20
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_BTSC_SAP 0x21
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_EIA_J 0x30
+#define AUD_DEM_WR_STANDARD_SEL_STD_SEL_FM_RADIO 0x40
+
+#define AUD_DEM_RD_STANDARD_RES__A 0x102007E
+#define AUD_DEM_RD_STANDARD_RES__W 16
+#define AUD_DEM_RD_STANDARD_RES__M 0xFFFF
+#define AUD_DEM_RD_STANDARD_RES__PRE 0x0
+
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT__B 0
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT__W 16
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT__M 0xFFFF
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT__PRE 0x0
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_NO_SOUND_STANDARD 0x0
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_NTSC_M_DUAL_CARRIER_FM 0x2
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_B_G_DUAL_CARRIER_FM 0x3
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_D_K1_DUAL_CARRIER_FM 0x4
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_D_K2_DUAL_CARRIER_FM 0x5
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_D_K3_DUAL_CARRIER_FM 0x7
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_B_G_NICAM_FM 0x8
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_L_NICAM_AM 0x9
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_I_NICAM_FM 0xA
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_D_K_NICAM_FM 0xB
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_BTSC_STEREO 0x20
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_BTSC_MONO_SAP 0x21
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_NTSC_EIA_J 0x30
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_FM_RADIO 0x40
+#define AUD_DEM_RD_STANDARD_RES_STD_RESULT_DETECTION_STILL_ACTIVE 0x7FF
+
+#define AUD_DEM_RD_STATUS__A 0x1020200
+#define AUD_DEM_RD_STATUS__W 16
+#define AUD_DEM_RD_STATUS__M 0xFFFF
+#define AUD_DEM_RD_STATUS__PRE 0x0
+
+#define AUD_DEM_RD_STATUS_STAT_NEW_RDS__B 0
+#define AUD_DEM_RD_STATUS_STAT_NEW_RDS__W 1
+#define AUD_DEM_RD_STATUS_STAT_NEW_RDS__M 0x1
+#define AUD_DEM_RD_STATUS_STAT_NEW_RDS__PRE 0x0
+#define AUD_DEM_RD_STATUS_STAT_NEW_RDS_NO_RDS_DATA 0x0
+#define AUD_DEM_RD_STATUS_STAT_NEW_RDS_NEW_RDS_DATA 0x1
+
+#define AUD_DEM_RD_STATUS_STAT_CARR_A__B 1
+#define AUD_DEM_RD_STATUS_STAT_CARR_A__W 1
+#define AUD_DEM_RD_STATUS_STAT_CARR_A__M 0x2
+#define AUD_DEM_RD_STATUS_STAT_CARR_A__PRE 0x0
+#define AUD_DEM_RD_STATUS_STAT_CARR_A_DETECTED 0x0
+#define AUD_DEM_RD_STATUS_STAT_CARR_A_NOT_DETECTED 0x2
+
+#define AUD_DEM_RD_STATUS_STAT_CARR_B__B 2
+#define AUD_DEM_RD_STATUS_STAT_CARR_B__W 1
+#define AUD_DEM_RD_STATUS_STAT_CARR_B__M 0x4
+#define AUD_DEM_RD_STATUS_STAT_CARR_B__PRE 0x0
+#define AUD_DEM_RD_STATUS_STAT_CARR_B_DETECTED 0x0
+#define AUD_DEM_RD_STATUS_STAT_CARR_B_NOT_DETECTED 0x4
+
+#define AUD_DEM_RD_STATUS_STAT_NICAM__B 5
+#define AUD_DEM_RD_STATUS_STAT_NICAM__W 1
+#define AUD_DEM_RD_STATUS_STAT_NICAM__M 0x20
+#define AUD_DEM_RD_STATUS_STAT_NICAM__PRE 0x0
+#define AUD_DEM_RD_STATUS_STAT_NICAM_NO_NICAM 0x0
+#define AUD_DEM_RD_STATUS_STAT_NICAM_NICAM_DETECTED 0x20
+
+#define AUD_DEM_RD_STATUS_STAT_STEREO__B 6
+#define AUD_DEM_RD_STATUS_STAT_STEREO__W 1
+#define AUD_DEM_RD_STATUS_STAT_STEREO__M 0x40
+#define AUD_DEM_RD_STATUS_STAT_STEREO__PRE 0x0
+#define AUD_DEM_RD_STATUS_STAT_STEREO_NO_STEREO 0x0
+#define AUD_DEM_RD_STATUS_STAT_STEREO_STEREO 0x40
+
+#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO__B 7
+#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO__W 1
+#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO__M 0x80
+#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO__PRE 0x0
+#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO_DEPENDENT_FM_MONO_PROGRAM 0x0
+#define AUD_DEM_RD_STATUS_STAT_INDEP_MONO_INDEPENDENT_FM_MONO_PROGRAM 0x80
+
+#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP__B 8
+#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP__W 1
+#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP__M 0x100
+#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP__PRE 0x0
+#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP_NO_SAP 0x0
+#define AUD_DEM_RD_STATUS_STAT_BIL_OR_SAP_SAP 0x100
+
+#define AUD_DEM_RD_STATUS_BAD_NICAM__B 9
+#define AUD_DEM_RD_STATUS_BAD_NICAM__W 1
+#define AUD_DEM_RD_STATUS_BAD_NICAM__M 0x200
+#define AUD_DEM_RD_STATUS_BAD_NICAM__PRE 0x0
+#define AUD_DEM_RD_STATUS_BAD_NICAM_OK 0x0
+#define AUD_DEM_RD_STATUS_BAD_NICAM_BAD 0x200
+
+#define AUD_DEM_RD_RDS_ARRAY_CNT__A 0x102020F
+#define AUD_DEM_RD_RDS_ARRAY_CNT__W 12
+#define AUD_DEM_RD_RDS_ARRAY_CNT__M 0xFFF
+#define AUD_DEM_RD_RDS_ARRAY_CNT__PRE 0x0
+
+#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT__B 0
+#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT__W 12
+#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT__M 0xFFF
+#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT__PRE 0x0
+#define AUD_DEM_RD_RDS_ARRAY_CNT_RDS_ARRAY_CT_RDS_DATA_NOT_VALID 0xFFF
+
+#define AUD_DEM_RD_RDS_DATA__A 0x1020210
+#define AUD_DEM_RD_RDS_DATA__W 12
+#define AUD_DEM_RD_RDS_DATA__M 0xFFF
+#define AUD_DEM_RD_RDS_DATA__PRE 0x0
+
+#define AUD_DSP_WR_FM_PRESC__A 0x105000E
+#define AUD_DSP_WR_FM_PRESC__W 16
+#define AUD_DSP_WR_FM_PRESC__M 0xFFFF
+#define AUD_DSP_WR_FM_PRESC__PRE 0x0
+
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC__B 8
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC__W 8
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC__M 0xFF00
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC__PRE 0x0
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_28_KHZ_FM_DEVIATION 0x7F00
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_50_KHZ_FM_DEVIATION 0x4800
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_75_KHZ_FM_DEVIATION 0x3000
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_100_KHZ_FM_DEVIATION 0x2400
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_150_KHZ_FM_DEVIATION 0x1800
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_180_KHZ_FM_DEVIATION 0x1300
+#define AUD_DSP_WR_FM_PRESC_FM_AM_PRESC_380_KHZ_FM_DEVIATION 0x900
+
+#define AUD_DSP_WR_NICAM_PRESC__A 0x1050010
+#define AUD_DSP_WR_NICAM_PRESC__W 16
+#define AUD_DSP_WR_NICAM_PRESC__M 0xFFFF
+#define AUD_DSP_WR_NICAM_PRESC__PRE 0x0
+#define AUD_DSP_WR_VOLUME__A 0x1050000
+#define AUD_DSP_WR_VOLUME__W 16
+#define AUD_DSP_WR_VOLUME__M 0xFFFF
+#define AUD_DSP_WR_VOLUME__PRE 0x0
+
+#define AUD_DSP_WR_VOLUME_VOL_MAIN__B 8
+#define AUD_DSP_WR_VOLUME_VOL_MAIN__W 8
+#define AUD_DSP_WR_VOLUME_VOL_MAIN__M 0xFF00
+#define AUD_DSP_WR_VOLUME_VOL_MAIN__PRE 0x0
+
+#define AUD_DSP_WR_SRC_I2S_MATR__A 0x1050038
+#define AUD_DSP_WR_SRC_I2S_MATR__W 16
+#define AUD_DSP_WR_SRC_I2S_MATR__M 0xFFFF
+#define AUD_DSP_WR_SRC_I2S_MATR__PRE 0x0
+
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S__B 8
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S__W 8
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S__M 0xFF00
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S__PRE 0x0
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S_MONO 0x0
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S_STEREO_AB 0x100
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S_STEREO_A 0x300
+#define AUD_DSP_WR_SRC_I2S_MATR_SRC_I2S_STEREO_B 0x400
+
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S__B 0
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S__W 8
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S__M 0xFF
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S__PRE 0x0
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S_SOUND_A 0x0
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S_SOUND_B 0x10
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S_STEREO 0x20
+#define AUD_DSP_WR_SRC_I2S_MATR_MAT_I2S_MONO 0x30
+
+#define AUD_DSP_WR_AVC__A 0x1050029
+#define AUD_DSP_WR_AVC__W 16
+#define AUD_DSP_WR_AVC__M 0xFFFF
+#define AUD_DSP_WR_AVC__PRE 0x0
+
+#define AUD_DSP_WR_AVC_AVC_ON__B 14
+#define AUD_DSP_WR_AVC_AVC_ON__W 2
+#define AUD_DSP_WR_AVC_AVC_ON__M 0xC000
+#define AUD_DSP_WR_AVC_AVC_ON__PRE 0x0
+#define AUD_DSP_WR_AVC_AVC_ON_OFF 0x0
+#define AUD_DSP_WR_AVC_AVC_ON_ON 0xC000
+
+#define AUD_DSP_WR_AVC_AVC_DECAY__B 8
+#define AUD_DSP_WR_AVC_AVC_DECAY__W 4
+#define AUD_DSP_WR_AVC_AVC_DECAY__M 0xF00
+#define AUD_DSP_WR_AVC_AVC_DECAY__PRE 0x0
+#define AUD_DSP_WR_AVC_AVC_DECAY_8_SEC 0x800
+#define AUD_DSP_WR_AVC_AVC_DECAY_4_SEC 0x400
+#define AUD_DSP_WR_AVC_AVC_DECAY_2_SEC 0x200
+#define AUD_DSP_WR_AVC_AVC_DECAY_20_MSEC 0x100
+
+#define AUD_DSP_WR_AVC_AVC_REF_LEV__B 4
+#define AUD_DSP_WR_AVC_AVC_REF_LEV__W 4
+#define AUD_DSP_WR_AVC_AVC_REF_LEV__M 0xF0
+#define AUD_DSP_WR_AVC_AVC_REF_LEV__PRE 0x0
+
+#define AUD_DSP_WR_AVC_AVC_MAX_ATT__B 2
+#define AUD_DSP_WR_AVC_AVC_MAX_ATT__W 2
+#define AUD_DSP_WR_AVC_AVC_MAX_ATT__M 0xC
+#define AUD_DSP_WR_AVC_AVC_MAX_ATT__PRE 0x0
+#define AUD_DSP_WR_AVC_AVC_MAX_ATT_24DB 0x0
+#define AUD_DSP_WR_AVC_AVC_MAX_ATT_18DB 0x4
+#define AUD_DSP_WR_AVC_AVC_MAX_ATT_12DB 0x8
+
+#define AUD_DSP_WR_AVC_AVC_MAX_GAIN__B 0
+#define AUD_DSP_WR_AVC_AVC_MAX_GAIN__W 2
+#define AUD_DSP_WR_AVC_AVC_MAX_GAIN__M 0x3
+#define AUD_DSP_WR_AVC_AVC_MAX_GAIN__PRE 0x0
+#define AUD_DSP_WR_AVC_AVC_MAX_GAIN_6DB 0x0
+#define AUD_DSP_WR_AVC_AVC_MAX_GAIN_12DB 0x1
+#define AUD_DSP_WR_AVC_AVC_MAX_GAIN_0DB 0x3
+
+#define AUD_DSP_WR_QPEAK__A 0x105000C
+#define AUD_DSP_WR_QPEAK__W 16
+#define AUD_DSP_WR_QPEAK__M 0xFFFF
+#define AUD_DSP_WR_QPEAK__PRE 0x0
+
+#define AUD_DSP_WR_QPEAK_SRC_QP__B 8
+#define AUD_DSP_WR_QPEAK_SRC_QP__W 8
+#define AUD_DSP_WR_QPEAK_SRC_QP__M 0xFF00
+#define AUD_DSP_WR_QPEAK_SRC_QP__PRE 0x0
+#define AUD_DSP_WR_QPEAK_SRC_QP_MONO 0x0
+#define AUD_DSP_WR_QPEAK_SRC_QP_STEREO_AB 0x100
+#define AUD_DSP_WR_QPEAK_SRC_QP_STEREO_A 0x300
+#define AUD_DSP_WR_QPEAK_SRC_QP_STEREO_B 0x400
+
+#define AUD_DSP_WR_QPEAK_MAT_QP__B 0
+#define AUD_DSP_WR_QPEAK_MAT_QP__W 8
+#define AUD_DSP_WR_QPEAK_MAT_QP__M 0xFF
+#define AUD_DSP_WR_QPEAK_MAT_QP__PRE 0x0
+#define AUD_DSP_WR_QPEAK_MAT_QP_SOUND_A 0x0
+#define AUD_DSP_WR_QPEAK_MAT_QP_SOUND_B 0x10
+#define AUD_DSP_WR_QPEAK_MAT_QP_STEREO 0x20
+#define AUD_DSP_WR_QPEAK_MAT_QP_MONO 0x30
+
+#define AUD_DSP_RD_QPEAK_L__A 0x1040019
+#define AUD_DSP_RD_QPEAK_L__W 16
+#define AUD_DSP_RD_QPEAK_L__M 0xFFFF
+#define AUD_DSP_RD_QPEAK_L__PRE 0x0
+
+#define AUD_DSP_RD_QPEAK_R__A 0x104001A
+#define AUD_DSP_RD_QPEAK_R__W 16
+#define AUD_DSP_RD_QPEAK_R__M 0xFFFF
+#define AUD_DSP_RD_QPEAK_R__PRE 0x0
+
+#define AUD_DSP_WR_BEEPER__A 0x1050014
+#define AUD_DSP_WR_BEEPER__W 16
+#define AUD_DSP_WR_BEEPER__M 0xFFFF
+#define AUD_DSP_WR_BEEPER__PRE 0x0
+
+#define AUD_DSP_WR_BEEPER_BEEP_VOLUME__B 8
+#define AUD_DSP_WR_BEEPER_BEEP_VOLUME__W 7
+#define AUD_DSP_WR_BEEPER_BEEP_VOLUME__M 0x7F00
+#define AUD_DSP_WR_BEEPER_BEEP_VOLUME__PRE 0x0
+
+#define AUD_DSP_WR_BEEPER_BEEP_FREQUENCY__B 0
+#define AUD_DSP_WR_BEEPER_BEEP_FREQUENCY__W 7
+#define AUD_DSP_WR_BEEPER_BEEP_FREQUENCY__M 0x7F
+#define AUD_DSP_WR_BEEPER_BEEP_FREQUENCY__PRE 0x0
+
+#define AUD_DEM_WR_I2S_CONFIG2__A 0x1030050
+#define AUD_DEM_WR_I2S_CONFIG2__W 16
+#define AUD_DEM_WR_I2S_CONFIG2__M 0xFFFF
+#define AUD_DEM_WR_I2S_CONFIG2__PRE 0x0
+
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL__B 6
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL__W 1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL__M 0x40
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL__PRE 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL_NORMAL 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_CL_POL_INVERTED 0x40
+
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE__B 4
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE__W 1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE__M 0x10
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE__PRE 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE_DISABLE 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_ENABLE_ENABLE 0x10
+
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST__B 3
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST__W 1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST__M 0x8
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST__PRE 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST_MASTER 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_SLV_MST_SLAVE 0x8
+
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL__B 2
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL__W 1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL__M 0x4
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL__PRE 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL_LEFT_LOW 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_POL_LEFT_HIGH 0x4
+
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE__B 1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE__W 1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE__M 0x2
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE__PRE 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE_NO_DELAY 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WS_MODE_DELAY 0x2
+
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN__B 0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN__W 1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN__M 0x1
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN__PRE 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN_BIT_32 0x0
+#define AUD_DEM_WR_I2S_CONFIG2_I2S_WORD_LEN_BIT_16 0x1
+
+#define AUD_DSP_WR_I2S_OUT_FS__A 0x105002A
+#define AUD_DSP_WR_I2S_OUT_FS__W 16
+#define AUD_DSP_WR_I2S_OUT_FS__M 0xFFFF
+#define AUD_DSP_WR_I2S_OUT_FS__PRE 0x0
+
+#define AUD_DSP_WR_I2S_OUT_FS_FS_OUT__B 0
+#define AUD_DSP_WR_I2S_OUT_FS_FS_OUT__W 16
+#define AUD_DSP_WR_I2S_OUT_FS_FS_OUT__M 0xFFFF
+#define AUD_DSP_WR_I2S_OUT_FS_FS_OUT__PRE 0x0
+
+#define AUD_DSP_WR_AV_SYNC__A 0x105002B
+#define AUD_DSP_WR_AV_SYNC__W 16
+#define AUD_DSP_WR_AV_SYNC__M 0xFFFF
+#define AUD_DSP_WR_AV_SYNC__PRE 0x0
+
+#define AUD_DSP_WR_AV_SYNC_AV_ON__B 15
+#define AUD_DSP_WR_AV_SYNC_AV_ON__W 1
+#define AUD_DSP_WR_AV_SYNC_AV_ON__M 0x8000
+#define AUD_DSP_WR_AV_SYNC_AV_ON__PRE 0x0
+#define AUD_DSP_WR_AV_SYNC_AV_ON_DISABLE 0x0
+#define AUD_DSP_WR_AV_SYNC_AV_ON_ENABLE 0x8000
+
+#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ__B 14
+#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ__W 1
+#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ__M 0x4000
+#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ__PRE 0x0
+#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ_MONOCHROME 0x0
+#define AUD_DSP_WR_AV_SYNC_AV_AUTO_FREQ_NTSC 0x4000
+
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL__B 0
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL__W 2
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL__M 0x3
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL__PRE 0x0
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL_AUTO 0x0
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL_PAL_SECAM 0x1
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL_NTSC 0x2
+#define AUD_DSP_WR_AV_SYNC_AV_STD_SEL_MONOCHROME 0x3
+
+#define AUD_DSP_RD_STATUS2__A 0x104007B
+#define AUD_DSP_RD_STATUS2__W 16
+#define AUD_DSP_RD_STATUS2__M 0xFFFF
+#define AUD_DSP_RD_STATUS2__PRE 0x0
+
+#define AUD_DSP_RD_STATUS2_AV_ACTIVE__B 15
+#define AUD_DSP_RD_STATUS2_AV_ACTIVE__W 1
+#define AUD_DSP_RD_STATUS2_AV_ACTIVE__M 0x8000
+#define AUD_DSP_RD_STATUS2_AV_ACTIVE__PRE 0x0
+#define AUD_DSP_RD_STATUS2_AV_ACTIVE_NO_SYNC 0x0
+#define AUD_DSP_RD_STATUS2_AV_ACTIVE_SYNC_ACTIVE 0x8000
+
+#define AUD_DSP_RD_XDFP_FW__A 0x104001D
+#define AUD_DSP_RD_XDFP_FW__W 16
+#define AUD_DSP_RD_XDFP_FW__M 0xFFFF
+#define AUD_DSP_RD_XDFP_FW__PRE 0x344
+
+#define AUD_DSP_RD_XDFP_FW_DSP_FW_REV__B 0
+#define AUD_DSP_RD_XDFP_FW_DSP_FW_REV__W 16
+#define AUD_DSP_RD_XDFP_FW_DSP_FW_REV__M 0xFFFF
+#define AUD_DSP_RD_XDFP_FW_DSP_FW_REV__PRE 0x344
+
+#define AUD_DSP_RD_XFP_FW__A 0x10404B8
+#define AUD_DSP_RD_XFP_FW__W 16
+#define AUD_DSP_RD_XFP_FW__M 0xFFFF
+#define AUD_DSP_RD_XFP_FW__PRE 0x42
+
+#define AUD_DSP_RD_XFP_FW_FP_FW_REV__B 0
+#define AUD_DSP_RD_XFP_FW_FP_FW_REV__W 16
+#define AUD_DSP_RD_XFP_FW_FP_FW_REV__M 0xFFFF
+#define AUD_DSP_RD_XFP_FW_FP_FW_REV__PRE 0x42
+
+#define AUD_DEM_WR_DCO_B_HI__A 0x103009B
+#define AUD_DEM_WR_DCO_B_HI__W 16
+#define AUD_DEM_WR_DCO_B_HI__M 0xFFFF
+#define AUD_DEM_WR_DCO_B_HI__PRE 0x0
+
+#define AUD_DEM_WR_DCO_B_LO__A 0x1030093
+#define AUD_DEM_WR_DCO_B_LO__W 16
+#define AUD_DEM_WR_DCO_B_LO__M 0xFFFF
+#define AUD_DEM_WR_DCO_B_LO__PRE 0x0
+
+#define AUD_DEM_WR_DCO_A_HI__A 0x10300AB
+#define AUD_DEM_WR_DCO_A_HI__W 16
+#define AUD_DEM_WR_DCO_A_HI__M 0xFFFF
+#define AUD_DEM_WR_DCO_A_HI__PRE 0x0
+
+#define AUD_DEM_WR_DCO_A_LO__A 0x10300A3
+#define AUD_DEM_WR_DCO_A_LO__W 16
+#define AUD_DEM_WR_DCO_A_LO__M 0xFFFF
+#define AUD_DEM_WR_DCO_A_LO__PRE 0x0
+#define AUD_DEM_WR_NICAM_THRSHLD__A 0x1030021
+#define AUD_DEM_WR_NICAM_THRSHLD__W 16
+#define AUD_DEM_WR_NICAM_THRSHLD__M 0xFFFF
+#define AUD_DEM_WR_NICAM_THRSHLD__PRE 0x2BC
+
+#define AUD_DEM_WR_NICAM_THRSHLD_NICAM_THLD__B 0
+#define AUD_DEM_WR_NICAM_THRSHLD_NICAM_THLD__W 12
+#define AUD_DEM_WR_NICAM_THRSHLD_NICAM_THLD__M 0xFFF
+#define AUD_DEM_WR_NICAM_THRSHLD_NICAM_THLD__PRE 0x2BC
+
+#define AUD_DEM_WR_A2_THRSHLD__A 0x1030022
+#define AUD_DEM_WR_A2_THRSHLD__W 16
+#define AUD_DEM_WR_A2_THRSHLD__M 0xFFFF
+#define AUD_DEM_WR_A2_THRSHLD__PRE 0x190
+
+#define AUD_DEM_WR_A2_THRSHLD_A2_THLD__B 0
+#define AUD_DEM_WR_A2_THRSHLD_A2_THLD__W 12
+#define AUD_DEM_WR_A2_THRSHLD_A2_THLD__M 0xFFF
+#define AUD_DEM_WR_A2_THRSHLD_A2_THLD__PRE 0x190
+
+#define AUD_DEM_WR_BTSC_THRSHLD__A 0x1030023
+#define AUD_DEM_WR_BTSC_THRSHLD__W 16
+#define AUD_DEM_WR_BTSC_THRSHLD__M 0xFFFF
+#define AUD_DEM_WR_BTSC_THRSHLD__PRE 0xC
+
+#define AUD_DEM_WR_BTSC_THRSHLD_BTSC_THLD__B 0
+#define AUD_DEM_WR_BTSC_THRSHLD_BTSC_THLD__W 12
+#define AUD_DEM_WR_BTSC_THRSHLD_BTSC_THLD__M 0xFFF
+#define AUD_DEM_WR_BTSC_THRSHLD_BTSC_THLD__PRE 0xC
+
+#define AUD_DEM_WR_CM_A_THRSHLD__A 0x1030024
+#define AUD_DEM_WR_CM_A_THRSHLD__W 16
+#define AUD_DEM_WR_CM_A_THRSHLD__M 0xFFFF
+#define AUD_DEM_WR_CM_A_THRSHLD__PRE 0x2A
+
+#define AUD_DEM_WR_CM_A_THRSHLD_CM_A_THLD__B 0
+#define AUD_DEM_WR_CM_A_THRSHLD_CM_A_THLD__W 12
+#define AUD_DEM_WR_CM_A_THRSHLD_CM_A_THLD__M 0xFFF
+#define AUD_DEM_WR_CM_A_THRSHLD_CM_A_THLD__PRE 0x2A
+
+#define AUD_DEM_WR_CM_B_THRSHLD__A 0x1030025
+#define AUD_DEM_WR_CM_B_THRSHLD__W 16
+#define AUD_DEM_WR_CM_B_THRSHLD__M 0xFFFF
+#define AUD_DEM_WR_CM_B_THRSHLD__PRE 0x2A
+
+#define AUD_DEM_WR_CM_B_THRSHLD_CM_B_THLD__B 0
+#define AUD_DEM_WR_CM_B_THRSHLD_CM_B_THLD__W 12
+#define AUD_DEM_WR_CM_B_THRSHLD_CM_B_THLD__M 0xFFF
+#define AUD_DEM_WR_CM_B_THRSHLD_CM_B_THLD__PRE 0x2A
+
+#define AUD_DEM_RD_NIC_C_AD_BITS__A 0x1020023
+#define AUD_DEM_RD_NIC_C_AD_BITS__W 16
+#define AUD_DEM_RD_NIC_C_AD_BITS__M 0xFFFF
+#define AUD_DEM_RD_NIC_C_AD_BITS__PRE 0x0
+
+#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC__B 0
+#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC__W 1
+#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC__M 0x1
+#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC__PRE 0x0
+#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC_NOT_SYNCED 0x0
+#define AUD_DEM_RD_NIC_C_AD_BITS_NICAM_SYNC_SYNCED 0x1
+
+#define AUD_DEM_RD_NIC_C_AD_BITS_C__B 1
+#define AUD_DEM_RD_NIC_C_AD_BITS_C__W 4
+#define AUD_DEM_RD_NIC_C_AD_BITS_C__M 0x1E
+#define AUD_DEM_RD_NIC_C_AD_BITS_C__PRE 0x0
+
+#define AUD_DEM_RD_NIC_C_AD_BITS_ADD_BIT_LO__B 5
+#define AUD_DEM_RD_NIC_C_AD_BITS_ADD_BIT_LO__W 3
+#define AUD_DEM_RD_NIC_C_AD_BITS_ADD_BIT_LO__M 0xE0
+#define AUD_DEM_RD_NIC_C_AD_BITS_ADD_BIT_LO__PRE 0x0
+
+#define AUD_DEM_RD_NIC_ADD_BITS_HI__A 0x1020038
+#define AUD_DEM_RD_NIC_ADD_BITS_HI__W 16
+#define AUD_DEM_RD_NIC_ADD_BITS_HI__M 0xFFFF
+#define AUD_DEM_RD_NIC_ADD_BITS_HI__PRE 0x0
+
+#define AUD_DEM_RD_NIC_ADD_BITS_HI_ADD_BIT_HI__B 0
+#define AUD_DEM_RD_NIC_ADD_BITS_HI_ADD_BIT_HI__W 8
+#define AUD_DEM_RD_NIC_ADD_BITS_HI_ADD_BIT_HI__M 0xFF
+#define AUD_DEM_RD_NIC_ADD_BITS_HI_ADD_BIT_HI__PRE 0x0
+
+#define AUD_DEM_RD_NIC_CIB__A 0x1020038
+#define AUD_DEM_RD_NIC_CIB__W 16
+#define AUD_DEM_RD_NIC_CIB__M 0xFFFF
+#define AUD_DEM_RD_NIC_CIB__PRE 0x0
+
+#define AUD_DEM_RD_NIC_CIB_CIB2__B 0
+#define AUD_DEM_RD_NIC_CIB_CIB2__W 1
+#define AUD_DEM_RD_NIC_CIB_CIB2__M 0x1
+#define AUD_DEM_RD_NIC_CIB_CIB2__PRE 0x0
+
+#define AUD_DEM_RD_NIC_CIB_CIB1__B 1
+#define AUD_DEM_RD_NIC_CIB_CIB1__W 1
+#define AUD_DEM_RD_NIC_CIB_CIB1__M 0x2
+#define AUD_DEM_RD_NIC_CIB_CIB1__PRE 0x0
+
+#define AUD_DEM_RD_NIC_ERROR_RATE__A 0x1020057
+#define AUD_DEM_RD_NIC_ERROR_RATE__W 16
+#define AUD_DEM_RD_NIC_ERROR_RATE__M 0xFFFF
+#define AUD_DEM_RD_NIC_ERROR_RATE__PRE 0x0
+
+#define AUD_DEM_RD_NIC_ERROR_RATE_ERROR_RATE__B 0
+#define AUD_DEM_RD_NIC_ERROR_RATE_ERROR_RATE__W 12
+#define AUD_DEM_RD_NIC_ERROR_RATE_ERROR_RATE__M 0xFFF
+#define AUD_DEM_RD_NIC_ERROR_RATE_ERROR_RATE__PRE 0x0
+
+#define AUD_DEM_WR_FM_DEEMPH__A 0x103000F
+#define AUD_DEM_WR_FM_DEEMPH__W 16
+#define AUD_DEM_WR_FM_DEEMPH__M 0xFFFF
+#define AUD_DEM_WR_FM_DEEMPH__PRE 0x0
+#define AUD_DEM_WR_FM_DEEMPH_50US 0x0
+#define AUD_DEM_WR_FM_DEEMPH_75US 0x1
+#define AUD_DEM_WR_FM_DEEMPH_OFF 0x3F
+
+#define AUD_DEM_WR_FM_MATRIX__A 0x103006F
+#define AUD_DEM_WR_FM_MATRIX__W 16
+#define AUD_DEM_WR_FM_MATRIX__M 0xFFFF
+#define AUD_DEM_WR_FM_MATRIX__PRE 0x0
+#define AUD_DEM_WR_FM_MATRIX_NO_MATRIX 0x0
+#define AUD_DEM_WR_FM_MATRIX_GERMAN_MATRIX 0x1
+#define AUD_DEM_WR_FM_MATRIX_KOREAN_MATRIX 0x2
+#define AUD_DEM_WR_FM_MATRIX_SOUND_A 0x3
+#define AUD_DEM_WR_FM_MATRIX_SOUND_B 0x4
+
+#define AUD_DSP_RD_FM_IDENT_VALUE__A 0x1040018
+#define AUD_DSP_RD_FM_IDENT_VALUE__W 16
+#define AUD_DSP_RD_FM_IDENT_VALUE__M 0xFFFF
+#define AUD_DSP_RD_FM_IDENT_VALUE__PRE 0x0
+
+#define AUD_DSP_RD_FM_IDENT_VALUE_FM_IDENT__B 8
+#define AUD_DSP_RD_FM_IDENT_VALUE_FM_IDENT__W 8
+#define AUD_DSP_RD_FM_IDENT_VALUE_FM_IDENT__M 0xFF00
+#define AUD_DSP_RD_FM_IDENT_VALUE_FM_IDENT__PRE 0x0
+
+#define AUD_DSP_RD_FM_DC_LEVEL_A__A 0x104001B
+#define AUD_DSP_RD_FM_DC_LEVEL_A__W 16
+#define AUD_DSP_RD_FM_DC_LEVEL_A__M 0xFFFF
+#define AUD_DSP_RD_FM_DC_LEVEL_A__PRE 0x0
+
+#define AUD_DSP_RD_FM_DC_LEVEL_A_FM_DC_LEV_A__B 0
+#define AUD_DSP_RD_FM_DC_LEVEL_A_FM_DC_LEV_A__W 16
+#define AUD_DSP_RD_FM_DC_LEVEL_A_FM_DC_LEV_A__M 0xFFFF
+#define AUD_DSP_RD_FM_DC_LEVEL_A_FM_DC_LEV_A__PRE 0x0
+
+#define AUD_DSP_RD_FM_DC_LEVEL_B__A 0x104001C
+#define AUD_DSP_RD_FM_DC_LEVEL_B__W 16
+#define AUD_DSP_RD_FM_DC_LEVEL_B__M 0xFFFF
+#define AUD_DSP_RD_FM_DC_LEVEL_B__PRE 0x0
+
+#define AUD_DSP_RD_FM_DC_LEVEL_B_FM_DC_LEV_B__B 0
+#define AUD_DSP_RD_FM_DC_LEVEL_B_FM_DC_LEV_B__W 16
+#define AUD_DSP_RD_FM_DC_LEVEL_B_FM_DC_LEV_B__M 0xFFFF
+#define AUD_DSP_RD_FM_DC_LEVEL_B_FM_DC_LEV_B__PRE 0x0
+
+#define AUD_DEM_WR_FM_DC_NOTCH_SW__A 0x1030017
+#define AUD_DEM_WR_FM_DC_NOTCH_SW__W 16
+#define AUD_DEM_WR_FM_DC_NOTCH_SW__M 0xFFFF
+#define AUD_DEM_WR_FM_DC_NOTCH_SW__PRE 0x0
+
+#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW__B 0
+#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW__W 16
+#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW__M 0xFFFF
+#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW__PRE 0x0
+#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW_ON 0x0
+#define AUD_DEM_WR_FM_DC_NOTCH_SW_FM_DC_NO_SW_OFF 0x3F
+
+#define AUD_DSP_WR_SYNC_OUT__A 0x1050026
+#define AUD_DSP_WR_SYNC_OUT__W 16
+#define AUD_DSP_WR_SYNC_OUT__M 0xFFFF
+#define AUD_DSP_WR_SYNC_OUT__PRE 0x0
+#define AUD_DSP_WR_SYNC_OUT_OFF 0x0
+#define AUD_DSP_WR_SYNC_OUT_SYNCHRONOUS 0x1
+
+#define AUD_XFP_DRAM_1K__A 0x1060000
+#define AUD_XFP_DRAM_1K__W 16
+#define AUD_XFP_DRAM_1K__M 0xFFFF
+#define AUD_XFP_DRAM_1K__PRE 0x0
+#define AUD_XFP_DRAM_1K_D__B 0
+#define AUD_XFP_DRAM_1K_D__W 16
+#define AUD_XFP_DRAM_1K_D__M 0xFFFF
+#define AUD_XFP_DRAM_1K_D__PRE 0x0
+
+#define AUD_XFP_PRAM_4K__A 0x1070000
+#define AUD_XFP_PRAM_4K__W 16
+#define AUD_XFP_PRAM_4K__M 0xFFFF
+#define AUD_XFP_PRAM_4K__PRE 0x0
+#define AUD_XFP_PRAM_4K_D__B 0
+#define AUD_XFP_PRAM_4K_D__W 16
+#define AUD_XFP_PRAM_4K_D__M 0xFFFF
+#define AUD_XFP_PRAM_4K_D__PRE 0x0
+
+#define AUD_XDFP_DRAM_1K__A 0x1080000
+#define AUD_XDFP_DRAM_1K__W 16
+#define AUD_XDFP_DRAM_1K__M 0xFFFF
+#define AUD_XDFP_DRAM_1K__PRE 0x0
+#define AUD_XDFP_DRAM_1K_D__B 0
+#define AUD_XDFP_DRAM_1K_D__W 16
+#define AUD_XDFP_DRAM_1K_D__M 0xFFFF
+#define AUD_XDFP_DRAM_1K_D__PRE 0x0
+
+#define AUD_XDFP_PRAM_4K__A 0x1090000
+#define AUD_XDFP_PRAM_4K__W 16
+#define AUD_XDFP_PRAM_4K__M 0xFFFF
+#define AUD_XDFP_PRAM_4K__PRE 0x0
+#define AUD_XDFP_PRAM_4K_D__B 0
+#define AUD_XDFP_PRAM_4K_D__W 16
+#define AUD_XDFP_PRAM_4K_D__M 0xFFFF
+#define AUD_XDFP_PRAM_4K_D__PRE 0x0
+
+#define FEC_COMM_EXEC__A 0x2400000
+#define FEC_COMM_EXEC__W 2
+#define FEC_COMM_EXEC__M 0x3
+#define FEC_COMM_EXEC__PRE 0x0
+#define FEC_COMM_EXEC_STOP 0x0
+#define FEC_COMM_EXEC_ACTIVE 0x1
+#define FEC_COMM_EXEC_HOLD 0x2
+
+#define FEC_COMM_MB__A 0x2400002
+#define FEC_COMM_MB__W 16
+#define FEC_COMM_MB__M 0xFFFF
+#define FEC_COMM_MB__PRE 0x0
+#define FEC_COMM_INT_REQ__A 0x2400003
+#define FEC_COMM_INT_REQ__W 16
+#define FEC_COMM_INT_REQ__M 0xFFFF
+#define FEC_COMM_INT_REQ__PRE 0x0
+#define FEC_COMM_INT_REQ_OC_REQ__B 0
+#define FEC_COMM_INT_REQ_OC_REQ__W 1
+#define FEC_COMM_INT_REQ_OC_REQ__M 0x1
+#define FEC_COMM_INT_REQ_OC_REQ__PRE 0x0
+#define FEC_COMM_INT_REQ_RS_REQ__B 1
+#define FEC_COMM_INT_REQ_RS_REQ__W 1
+#define FEC_COMM_INT_REQ_RS_REQ__M 0x2
+#define FEC_COMM_INT_REQ_RS_REQ__PRE 0x0
+#define FEC_COMM_INT_REQ_DI_REQ__B 2
+#define FEC_COMM_INT_REQ_DI_REQ__W 1
+#define FEC_COMM_INT_REQ_DI_REQ__M 0x4
+#define FEC_COMM_INT_REQ_DI_REQ__PRE 0x0
+
+#define FEC_COMM_INT_STA__A 0x2400005
+#define FEC_COMM_INT_STA__W 16
+#define FEC_COMM_INT_STA__M 0xFFFF
+#define FEC_COMM_INT_STA__PRE 0x0
+#define FEC_COMM_INT_MSK__A 0x2400006
+#define FEC_COMM_INT_MSK__W 16
+#define FEC_COMM_INT_MSK__M 0xFFFF
+#define FEC_COMM_INT_MSK__PRE 0x0
+#define FEC_COMM_INT_STM__A 0x2400007
+#define FEC_COMM_INT_STM__W 16
+#define FEC_COMM_INT_STM__M 0xFFFF
+#define FEC_COMM_INT_STM__PRE 0x0
+
+#define FEC_TOP_COMM_EXEC__A 0x2410000
+#define FEC_TOP_COMM_EXEC__W 2
+#define FEC_TOP_COMM_EXEC__M 0x3
+#define FEC_TOP_COMM_EXEC__PRE 0x0
+#define FEC_TOP_COMM_EXEC_STOP 0x0
+#define FEC_TOP_COMM_EXEC_ACTIVE 0x1
+#define FEC_TOP_COMM_EXEC_HOLD 0x2
+
+#define FEC_TOP_ANNEX__A 0x2410010
+#define FEC_TOP_ANNEX__W 2
+#define FEC_TOP_ANNEX__M 0x3
+#define FEC_TOP_ANNEX__PRE 0x0
+#define FEC_TOP_ANNEX_A 0x0
+#define FEC_TOP_ANNEX_B 0x1
+#define FEC_TOP_ANNEX_C 0x2
+#define FEC_TOP_ANNEX_D 0x3
+
+#define FEC_DI_COMM_EXEC__A 0x2420000
+#define FEC_DI_COMM_EXEC__W 2
+#define FEC_DI_COMM_EXEC__M 0x3
+#define FEC_DI_COMM_EXEC__PRE 0x0
+#define FEC_DI_COMM_EXEC_STOP 0x0
+#define FEC_DI_COMM_EXEC_ACTIVE 0x1
+#define FEC_DI_COMM_EXEC_HOLD 0x2
+
+#define FEC_DI_COMM_MB__A 0x2420002
+#define FEC_DI_COMM_MB__W 2
+#define FEC_DI_COMM_MB__M 0x3
+#define FEC_DI_COMM_MB__PRE 0x0
+#define FEC_DI_COMM_MB_CTL__B 0
+#define FEC_DI_COMM_MB_CTL__W 1
+#define FEC_DI_COMM_MB_CTL__M 0x1
+#define FEC_DI_COMM_MB_CTL__PRE 0x0
+#define FEC_DI_COMM_MB_CTL_OFF 0x0
+#define FEC_DI_COMM_MB_CTL_ON 0x1
+#define FEC_DI_COMM_MB_OBS__B 1
+#define FEC_DI_COMM_MB_OBS__W 1
+#define FEC_DI_COMM_MB_OBS__M 0x2
+#define FEC_DI_COMM_MB_OBS__PRE 0x0
+#define FEC_DI_COMM_MB_OBS_OFF 0x0
+#define FEC_DI_COMM_MB_OBS_ON 0x2
+
+#define FEC_DI_COMM_INT_REQ__A 0x2420003
+#define FEC_DI_COMM_INT_REQ__W 1
+#define FEC_DI_COMM_INT_REQ__M 0x1
+#define FEC_DI_COMM_INT_REQ__PRE 0x0
+#define FEC_DI_COMM_INT_STA__A 0x2420005
+#define FEC_DI_COMM_INT_STA__W 2
+#define FEC_DI_COMM_INT_STA__M 0x3
+#define FEC_DI_COMM_INT_STA__PRE 0x0
+
+#define FEC_DI_COMM_INT_STA_STAT_INT__B 0
+#define FEC_DI_COMM_INT_STA_STAT_INT__W 1
+#define FEC_DI_COMM_INT_STA_STAT_INT__M 0x1
+#define FEC_DI_COMM_INT_STA_STAT_INT__PRE 0x0
+
+#define FEC_DI_COMM_INT_STA_TIMEOUT_INT__B 1
+#define FEC_DI_COMM_INT_STA_TIMEOUT_INT__W 1
+#define FEC_DI_COMM_INT_STA_TIMEOUT_INT__M 0x2
+#define FEC_DI_COMM_INT_STA_TIMEOUT_INT__PRE 0x0
+
+#define FEC_DI_COMM_INT_MSK__A 0x2420006
+#define FEC_DI_COMM_INT_MSK__W 2
+#define FEC_DI_COMM_INT_MSK__M 0x3
+#define FEC_DI_COMM_INT_MSK__PRE 0x0
+#define FEC_DI_COMM_INT_MSK_STAT_INT__B 0
+#define FEC_DI_COMM_INT_MSK_STAT_INT__W 1
+#define FEC_DI_COMM_INT_MSK_STAT_INT__M 0x1
+#define FEC_DI_COMM_INT_MSK_STAT_INT__PRE 0x0
+#define FEC_DI_COMM_INT_MSK_TIMEOUT_INT__B 1
+#define FEC_DI_COMM_INT_MSK_TIMEOUT_INT__W 1
+#define FEC_DI_COMM_INT_MSK_TIMEOUT_INT__M 0x2
+#define FEC_DI_COMM_INT_MSK_TIMEOUT_INT__PRE 0x0
+
+#define FEC_DI_COMM_INT_STM__A 0x2420007
+#define FEC_DI_COMM_INT_STM__W 2
+#define FEC_DI_COMM_INT_STM__M 0x3
+#define FEC_DI_COMM_INT_STM__PRE 0x0
+#define FEC_DI_COMM_INT_STM_STAT_INT__B 0
+#define FEC_DI_COMM_INT_STM_STAT_INT__W 1
+#define FEC_DI_COMM_INT_STM_STAT_INT__M 0x1
+#define FEC_DI_COMM_INT_STM_STAT_INT__PRE 0x0
+#define FEC_DI_COMM_INT_STM_TIMEOUT_INT__B 1
+#define FEC_DI_COMM_INT_STM_TIMEOUT_INT__W 1
+#define FEC_DI_COMM_INT_STM_TIMEOUT_INT__M 0x2
+#define FEC_DI_COMM_INT_STM_TIMEOUT_INT__PRE 0x0
+
+#define FEC_DI_STATUS__A 0x2420010
+#define FEC_DI_STATUS__W 1
+#define FEC_DI_STATUS__M 0x1
+#define FEC_DI_STATUS__PRE 0x0
+#define FEC_DI_MODE__A 0x2420011
+#define FEC_DI_MODE__W 3
+#define FEC_DI_MODE__M 0x7
+#define FEC_DI_MODE__PRE 0x0
+
+#define FEC_DI_MODE_NO_SYNC__B 0
+#define FEC_DI_MODE_NO_SYNC__W 1
+#define FEC_DI_MODE_NO_SYNC__M 0x1
+#define FEC_DI_MODE_NO_SYNC__PRE 0x0
+
+#define FEC_DI_MODE_IGNORE_LOST_SYNC__B 1
+#define FEC_DI_MODE_IGNORE_LOST_SYNC__W 1
+#define FEC_DI_MODE_IGNORE_LOST_SYNC__M 0x2
+#define FEC_DI_MODE_IGNORE_LOST_SYNC__PRE 0x0
+
+#define FEC_DI_MODE_IGNORE_TIMEOUT__B 2
+#define FEC_DI_MODE_IGNORE_TIMEOUT__W 1
+#define FEC_DI_MODE_IGNORE_TIMEOUT__M 0x4
+#define FEC_DI_MODE_IGNORE_TIMEOUT__PRE 0x0
+
+#define FEC_DI_CONTROL_WORD__A 0x2420012
+#define FEC_DI_CONTROL_WORD__W 4
+#define FEC_DI_CONTROL_WORD__M 0xF
+#define FEC_DI_CONTROL_WORD__PRE 0x0
+
+#define FEC_DI_RESTART__A 0x2420013
+#define FEC_DI_RESTART__W 1
+#define FEC_DI_RESTART__M 0x1
+#define FEC_DI_RESTART__PRE 0x0
+
+#define FEC_DI_TIMEOUT_LO__A 0x2420014
+#define FEC_DI_TIMEOUT_LO__W 16
+#define FEC_DI_TIMEOUT_LO__M 0xFFFF
+#define FEC_DI_TIMEOUT_LO__PRE 0x0
+
+#define FEC_DI_TIMEOUT_HI__A 0x2420015
+#define FEC_DI_TIMEOUT_HI__W 8
+#define FEC_DI_TIMEOUT_HI__M 0xFF
+#define FEC_DI_TIMEOUT_HI__PRE 0xA
+
+#define FEC_RS_COMM_EXEC__A 0x2430000
+#define FEC_RS_COMM_EXEC__W 2
+#define FEC_RS_COMM_EXEC__M 0x3
+#define FEC_RS_COMM_EXEC__PRE 0x0
+#define FEC_RS_COMM_EXEC_STOP 0x0
+#define FEC_RS_COMM_EXEC_ACTIVE 0x1
+#define FEC_RS_COMM_EXEC_HOLD 0x2
+
+#define FEC_RS_COMM_MB__A 0x2430002
+#define FEC_RS_COMM_MB__W 2
+#define FEC_RS_COMM_MB__M 0x3
+#define FEC_RS_COMM_MB__PRE 0x0
+#define FEC_RS_COMM_MB_CTL__B 0
+#define FEC_RS_COMM_MB_CTL__W 1
+#define FEC_RS_COMM_MB_CTL__M 0x1
+#define FEC_RS_COMM_MB_CTL__PRE 0x0
+#define FEC_RS_COMM_MB_CTL_OFF 0x0
+#define FEC_RS_COMM_MB_CTL_ON 0x1
+#define FEC_RS_COMM_MB_OBS__B 1
+#define FEC_RS_COMM_MB_OBS__W 1
+#define FEC_RS_COMM_MB_OBS__M 0x2
+#define FEC_RS_COMM_MB_OBS__PRE 0x0
+#define FEC_RS_COMM_MB_OBS_OFF 0x0
+#define FEC_RS_COMM_MB_OBS_ON 0x2
+
+#define FEC_RS_COMM_INT_REQ__A 0x2430003
+#define FEC_RS_COMM_INT_REQ__W 1
+#define FEC_RS_COMM_INT_REQ__M 0x1
+#define FEC_RS_COMM_INT_REQ__PRE 0x0
+#define FEC_RS_COMM_INT_STA__A 0x2430005
+#define FEC_RS_COMM_INT_STA__W 2
+#define FEC_RS_COMM_INT_STA__M 0x3
+#define FEC_RS_COMM_INT_STA__PRE 0x0
+
+#define FEC_RS_COMM_INT_STA_FAILURE_INT__B 0
+#define FEC_RS_COMM_INT_STA_FAILURE_INT__W 1
+#define FEC_RS_COMM_INT_STA_FAILURE_INT__M 0x1
+#define FEC_RS_COMM_INT_STA_FAILURE_INT__PRE 0x0
+
+#define FEC_RS_COMM_INT_STA_MEASUREMENT_INT__B 1
+#define FEC_RS_COMM_INT_STA_MEASUREMENT_INT__W 1
+#define FEC_RS_COMM_INT_STA_MEASUREMENT_INT__M 0x2
+#define FEC_RS_COMM_INT_STA_MEASUREMENT_INT__PRE 0x0
+
+#define FEC_RS_COMM_INT_MSK__A 0x2430006
+#define FEC_RS_COMM_INT_MSK__W 2
+#define FEC_RS_COMM_INT_MSK__M 0x3
+#define FEC_RS_COMM_INT_MSK__PRE 0x0
+#define FEC_RS_COMM_INT_MSK_FAILURE_MSK__B 0
+#define FEC_RS_COMM_INT_MSK_FAILURE_MSK__W 1
+#define FEC_RS_COMM_INT_MSK_FAILURE_MSK__M 0x1
+#define FEC_RS_COMM_INT_MSK_FAILURE_MSK__PRE 0x0
+#define FEC_RS_COMM_INT_MSK_MEASUREMENT_MSK__B 1
+#define FEC_RS_COMM_INT_MSK_MEASUREMENT_MSK__W 1
+#define FEC_RS_COMM_INT_MSK_MEASUREMENT_MSK__M 0x2
+#define FEC_RS_COMM_INT_MSK_MEASUREMENT_MSK__PRE 0x0
+
+#define FEC_RS_COMM_INT_STM__A 0x2430007
+#define FEC_RS_COMM_INT_STM__W 2
+#define FEC_RS_COMM_INT_STM__M 0x3
+#define FEC_RS_COMM_INT_STM__PRE 0x0
+#define FEC_RS_COMM_INT_STM_FAILURE_MSK__B 0
+#define FEC_RS_COMM_INT_STM_FAILURE_MSK__W 1
+#define FEC_RS_COMM_INT_STM_FAILURE_MSK__M 0x1
+#define FEC_RS_COMM_INT_STM_FAILURE_MSK__PRE 0x0
+#define FEC_RS_COMM_INT_STM_MEASUREMENT_MSK__B 1
+#define FEC_RS_COMM_INT_STM_MEASUREMENT_MSK__W 1
+#define FEC_RS_COMM_INT_STM_MEASUREMENT_MSK__M 0x2
+#define FEC_RS_COMM_INT_STM_MEASUREMENT_MSK__PRE 0x0
+
+#define FEC_RS_STATUS__A 0x2430010
+#define FEC_RS_STATUS__W 1
+#define FEC_RS_STATUS__M 0x1
+#define FEC_RS_STATUS__PRE 0x0
+#define FEC_RS_MODE__A 0x2430011
+#define FEC_RS_MODE__W 1
+#define FEC_RS_MODE__M 0x1
+#define FEC_RS_MODE__PRE 0x0
+
+#define FEC_RS_MODE_BYPASS__B 0
+#define FEC_RS_MODE_BYPASS__W 1
+#define FEC_RS_MODE_BYPASS__M 0x1
+#define FEC_RS_MODE_BYPASS__PRE 0x0
+
+#define FEC_RS_MEASUREMENT_PERIOD__A 0x2430012
+#define FEC_RS_MEASUREMENT_PERIOD__W 16
+#define FEC_RS_MEASUREMENT_PERIOD__M 0xFFFF
+#define FEC_RS_MEASUREMENT_PERIOD__PRE 0x1171
+
+#define FEC_RS_MEASUREMENT_PERIOD_PERIOD__B 0
+#define FEC_RS_MEASUREMENT_PERIOD_PERIOD__W 16
+#define FEC_RS_MEASUREMENT_PERIOD_PERIOD__M 0xFFFF
+#define FEC_RS_MEASUREMENT_PERIOD_PERIOD__PRE 0x1171
+
+#define FEC_RS_MEASUREMENT_PRESCALE__A 0x2430013
+#define FEC_RS_MEASUREMENT_PRESCALE__W 16
+#define FEC_RS_MEASUREMENT_PRESCALE__M 0xFFFF
+#define FEC_RS_MEASUREMENT_PRESCALE__PRE 0x1
+
+#define FEC_RS_MEASUREMENT_PRESCALE_PRESCALE__B 0
+#define FEC_RS_MEASUREMENT_PRESCALE_PRESCALE__W 16
+#define FEC_RS_MEASUREMENT_PRESCALE_PRESCALE__M 0xFFFF
+#define FEC_RS_MEASUREMENT_PRESCALE_PRESCALE__PRE 0x1
+
+#define FEC_RS_NR_BIT_ERRORS__A 0x2430014
+#define FEC_RS_NR_BIT_ERRORS__W 16
+#define FEC_RS_NR_BIT_ERRORS__M 0xFFFF
+#define FEC_RS_NR_BIT_ERRORS__PRE 0xFFFF
+
+#define FEC_RS_NR_BIT_ERRORS_FIXED_MANT__B 0
+#define FEC_RS_NR_BIT_ERRORS_FIXED_MANT__W 12
+#define FEC_RS_NR_BIT_ERRORS_FIXED_MANT__M 0xFFF
+#define FEC_RS_NR_BIT_ERRORS_FIXED_MANT__PRE 0xFFF
+
+#define FEC_RS_NR_BIT_ERRORS_EXP__B 12
+#define FEC_RS_NR_BIT_ERRORS_EXP__W 4
+#define FEC_RS_NR_BIT_ERRORS_EXP__M 0xF000
+#define FEC_RS_NR_BIT_ERRORS_EXP__PRE 0xF000
+
+#define FEC_RS_NR_SYMBOL_ERRORS__A 0x2430015
+#define FEC_RS_NR_SYMBOL_ERRORS__W 16
+#define FEC_RS_NR_SYMBOL_ERRORS__M 0xFFFF
+#define FEC_RS_NR_SYMBOL_ERRORS__PRE 0xFFFF
+
+#define FEC_RS_NR_SYMBOL_ERRORS_FIXED_MANT__B 0
+#define FEC_RS_NR_SYMBOL_ERRORS_FIXED_MANT__W 12
+#define FEC_RS_NR_SYMBOL_ERRORS_FIXED_MANT__M 0xFFF
+#define FEC_RS_NR_SYMBOL_ERRORS_FIXED_MANT__PRE 0xFFF
+
+#define FEC_RS_NR_SYMBOL_ERRORS_EXP__B 12
+#define FEC_RS_NR_SYMBOL_ERRORS_EXP__W 4
+#define FEC_RS_NR_SYMBOL_ERRORS_EXP__M 0xF000
+#define FEC_RS_NR_SYMBOL_ERRORS_EXP__PRE 0xF000
+
+#define FEC_RS_NR_PACKET_ERRORS__A 0x2430016
+#define FEC_RS_NR_PACKET_ERRORS__W 16
+#define FEC_RS_NR_PACKET_ERRORS__M 0xFFFF
+#define FEC_RS_NR_PACKET_ERRORS__PRE 0xFFFF
+
+#define FEC_RS_NR_PACKET_ERRORS_FIXED_MANT__B 0
+#define FEC_RS_NR_PACKET_ERRORS_FIXED_MANT__W 12
+#define FEC_RS_NR_PACKET_ERRORS_FIXED_MANT__M 0xFFF
+#define FEC_RS_NR_PACKET_ERRORS_FIXED_MANT__PRE 0xFFF
+
+#define FEC_RS_NR_PACKET_ERRORS_EXP__B 12
+#define FEC_RS_NR_PACKET_ERRORS_EXP__W 4
+#define FEC_RS_NR_PACKET_ERRORS_EXP__M 0xF000
+#define FEC_RS_NR_PACKET_ERRORS_EXP__PRE 0xF000
+
+#define FEC_RS_NR_FAILURES__A 0x2430017
+#define FEC_RS_NR_FAILURES__W 16
+#define FEC_RS_NR_FAILURES__M 0xFFFF
+#define FEC_RS_NR_FAILURES__PRE 0x0
+
+#define FEC_RS_NR_FAILURES_FIXED_MANT__B 0
+#define FEC_RS_NR_FAILURES_FIXED_MANT__W 12
+#define FEC_RS_NR_FAILURES_FIXED_MANT__M 0xFFF
+#define FEC_RS_NR_FAILURES_FIXED_MANT__PRE 0x0
+
+#define FEC_RS_NR_FAILURES_EXP__B 12
+#define FEC_RS_NR_FAILURES_EXP__W 4
+#define FEC_RS_NR_FAILURES_EXP__M 0xF000
+#define FEC_RS_NR_FAILURES_EXP__PRE 0x0
+
+#define FEC_OC_COMM_EXEC__A 0x2440000
+#define FEC_OC_COMM_EXEC__W 2
+#define FEC_OC_COMM_EXEC__M 0x3
+#define FEC_OC_COMM_EXEC__PRE 0x0
+#define FEC_OC_COMM_EXEC_STOP 0x0
+#define FEC_OC_COMM_EXEC_ACTIVE 0x1
+#define FEC_OC_COMM_EXEC_HOLD 0x2
+
+#define FEC_OC_COMM_MB__A 0x2440002
+#define FEC_OC_COMM_MB__W 2
+#define FEC_OC_COMM_MB__M 0x3
+#define FEC_OC_COMM_MB__PRE 0x0
+#define FEC_OC_COMM_MB_CTL__B 0
+#define FEC_OC_COMM_MB_CTL__W 1
+#define FEC_OC_COMM_MB_CTL__M 0x1
+#define FEC_OC_COMM_MB_CTL__PRE 0x0
+#define FEC_OC_COMM_MB_CTL_OFF 0x0
+#define FEC_OC_COMM_MB_CTL_ON 0x1
+#define FEC_OC_COMM_MB_OBS__B 1
+#define FEC_OC_COMM_MB_OBS__W 1
+#define FEC_OC_COMM_MB_OBS__M 0x2
+#define FEC_OC_COMM_MB_OBS__PRE 0x0
+#define FEC_OC_COMM_MB_OBS_OFF 0x0
+#define FEC_OC_COMM_MB_OBS_ON 0x2
+
+#define FEC_OC_COMM_INT_REQ__A 0x2440003
+#define FEC_OC_COMM_INT_REQ__W 1
+#define FEC_OC_COMM_INT_REQ__M 0x1
+#define FEC_OC_COMM_INT_REQ__PRE 0x0
+#define FEC_OC_COMM_INT_STA__A 0x2440005
+#define FEC_OC_COMM_INT_STA__W 8
+#define FEC_OC_COMM_INT_STA__M 0xFF
+#define FEC_OC_COMM_INT_STA__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_DPR_LOCK_INT__B 0
+#define FEC_OC_COMM_INT_STA_DPR_LOCK_INT__W 1
+#define FEC_OC_COMM_INT_STA_DPR_LOCK_INT__M 0x1
+#define FEC_OC_COMM_INT_STA_DPR_LOCK_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_SNC_LOCK_INT__B 1
+#define FEC_OC_COMM_INT_STA_SNC_LOCK_INT__W 1
+#define FEC_OC_COMM_INT_STA_SNC_LOCK_INT__M 0x2
+#define FEC_OC_COMM_INT_STA_SNC_LOCK_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_SNC_LOST_INT__B 2
+#define FEC_OC_COMM_INT_STA_SNC_LOST_INT__W 1
+#define FEC_OC_COMM_INT_STA_SNC_LOST_INT__M 0x4
+#define FEC_OC_COMM_INT_STA_SNC_LOST_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_SNC_PAR_INT__B 3
+#define FEC_OC_COMM_INT_STA_SNC_PAR_INT__W 1
+#define FEC_OC_COMM_INT_STA_SNC_PAR_INT__M 0x8
+#define FEC_OC_COMM_INT_STA_SNC_PAR_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_FIFO_FULL_INT__B 4
+#define FEC_OC_COMM_INT_STA_FIFO_FULL_INT__W 1
+#define FEC_OC_COMM_INT_STA_FIFO_FULL_INT__M 0x10
+#define FEC_OC_COMM_INT_STA_FIFO_FULL_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_FIFO_EMPTY_INT__B 5
+#define FEC_OC_COMM_INT_STA_FIFO_EMPTY_INT__W 1
+#define FEC_OC_COMM_INT_STA_FIFO_EMPTY_INT__M 0x20
+#define FEC_OC_COMM_INT_STA_FIFO_EMPTY_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_OCR_ACQ_INT__B 6
+#define FEC_OC_COMM_INT_STA_OCR_ACQ_INT__W 1
+#define FEC_OC_COMM_INT_STA_OCR_ACQ_INT__M 0x40
+#define FEC_OC_COMM_INT_STA_OCR_ACQ_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_STA_STAT_CHG_INT__B 7
+#define FEC_OC_COMM_INT_STA_STAT_CHG_INT__W 1
+#define FEC_OC_COMM_INT_STA_STAT_CHG_INT__M 0x80
+#define FEC_OC_COMM_INT_STA_STAT_CHG_INT__PRE 0x0
+
+#define FEC_OC_COMM_INT_MSK__A 0x2440006
+#define FEC_OC_COMM_INT_MSK__W 8
+#define FEC_OC_COMM_INT_MSK__M 0xFF
+#define FEC_OC_COMM_INT_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_DPR_LOCK_MSK__B 0
+#define FEC_OC_COMM_INT_MSK_DPR_LOCK_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_DPR_LOCK_MSK__M 0x1
+#define FEC_OC_COMM_INT_MSK_DPR_LOCK_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_SNC_LOCK_MSK__B 1
+#define FEC_OC_COMM_INT_MSK_SNC_LOCK_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_SNC_LOCK_MSK__M 0x2
+#define FEC_OC_COMM_INT_MSK_SNC_LOCK_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_SNC_LOST_MSK__B 2
+#define FEC_OC_COMM_INT_MSK_SNC_LOST_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_SNC_LOST_MSK__M 0x4
+#define FEC_OC_COMM_INT_MSK_SNC_LOST_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_SNC_PAR_MSK__B 3
+#define FEC_OC_COMM_INT_MSK_SNC_PAR_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_SNC_PAR_MSK__M 0x8
+#define FEC_OC_COMM_INT_MSK_SNC_PAR_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_FIFO_FULL_MSK__B 4
+#define FEC_OC_COMM_INT_MSK_FIFO_FULL_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_FIFO_FULL_MSK__M 0x10
+#define FEC_OC_COMM_INT_MSK_FIFO_FULL_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_FIFO_EMPTY_MSK__B 5
+#define FEC_OC_COMM_INT_MSK_FIFO_EMPTY_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_FIFO_EMPTY_MSK__M 0x20
+#define FEC_OC_COMM_INT_MSK_FIFO_EMPTY_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_OCR_ACQ_MSK__B 6
+#define FEC_OC_COMM_INT_MSK_OCR_ACQ_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_OCR_ACQ_MSK__M 0x40
+#define FEC_OC_COMM_INT_MSK_OCR_ACQ_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_MSK_STAT_CHG_MSK__B 7
+#define FEC_OC_COMM_INT_MSK_STAT_CHG_MSK__W 1
+#define FEC_OC_COMM_INT_MSK_STAT_CHG_MSK__M 0x80
+#define FEC_OC_COMM_INT_MSK_STAT_CHG_MSK__PRE 0x0
+
+#define FEC_OC_COMM_INT_STM__A 0x2440007
+#define FEC_OC_COMM_INT_STM__W 8
+#define FEC_OC_COMM_INT_STM__M 0xFF
+#define FEC_OC_COMM_INT_STM__PRE 0x0
+#define FEC_OC_COMM_INT_STM_DPR_LOCK_MSK__B 0
+#define FEC_OC_COMM_INT_STM_DPR_LOCK_MSK__W 1
+#define FEC_OC_COMM_INT_STM_DPR_LOCK_MSK__M 0x1
+#define FEC_OC_COMM_INT_STM_DPR_LOCK_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_STM_SNC_LOCK_MSK__B 1
+#define FEC_OC_COMM_INT_STM_SNC_LOCK_MSK__W 1
+#define FEC_OC_COMM_INT_STM_SNC_LOCK_MSK__M 0x2
+#define FEC_OC_COMM_INT_STM_SNC_LOCK_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_STM_SNC_LOST_MSK__B 2
+#define FEC_OC_COMM_INT_STM_SNC_LOST_MSK__W 1
+#define FEC_OC_COMM_INT_STM_SNC_LOST_MSK__M 0x4
+#define FEC_OC_COMM_INT_STM_SNC_LOST_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_STM_SNC_PAR_MSK__B 3
+#define FEC_OC_COMM_INT_STM_SNC_PAR_MSK__W 1
+#define FEC_OC_COMM_INT_STM_SNC_PAR_MSK__M 0x8
+#define FEC_OC_COMM_INT_STM_SNC_PAR_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_STM_FIFO_FULL_MSK__B 4
+#define FEC_OC_COMM_INT_STM_FIFO_FULL_MSK__W 1
+#define FEC_OC_COMM_INT_STM_FIFO_FULL_MSK__M 0x10
+#define FEC_OC_COMM_INT_STM_FIFO_FULL_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_STM_FIFO_EMPTY_MSK__B 5
+#define FEC_OC_COMM_INT_STM_FIFO_EMPTY_MSK__W 1
+#define FEC_OC_COMM_INT_STM_FIFO_EMPTY_MSK__M 0x20
+#define FEC_OC_COMM_INT_STM_FIFO_EMPTY_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_STM_OCR_ACQ_MSK__B 6
+#define FEC_OC_COMM_INT_STM_OCR_ACQ_MSK__W 1
+#define FEC_OC_COMM_INT_STM_OCR_ACQ_MSK__M 0x40
+#define FEC_OC_COMM_INT_STM_OCR_ACQ_MSK__PRE 0x0
+#define FEC_OC_COMM_INT_STM_STAT_CHG_MSK__B 7
+#define FEC_OC_COMM_INT_STM_STAT_CHG_MSK__W 1
+#define FEC_OC_COMM_INT_STM_STAT_CHG_MSK__M 0x80
+#define FEC_OC_COMM_INT_STM_STAT_CHG_MSK__PRE 0x0
+
+#define FEC_OC_STATUS__A 0x2440010
+#define FEC_OC_STATUS__W 5
+#define FEC_OC_STATUS__M 0x1F
+#define FEC_OC_STATUS__PRE 0x0
+
+#define FEC_OC_STATUS_DPR_STATUS__B 0
+#define FEC_OC_STATUS_DPR_STATUS__W 1
+#define FEC_OC_STATUS_DPR_STATUS__M 0x1
+#define FEC_OC_STATUS_DPR_STATUS__PRE 0x0
+
+#define FEC_OC_STATUS_SNC_STATUS__B 1
+#define FEC_OC_STATUS_SNC_STATUS__W 2
+#define FEC_OC_STATUS_SNC_STATUS__M 0x6
+#define FEC_OC_STATUS_SNC_STATUS__PRE 0x0
+
+#define FEC_OC_STATUS_FIFO_FULL__B 3
+#define FEC_OC_STATUS_FIFO_FULL__W 1
+#define FEC_OC_STATUS_FIFO_FULL__M 0x8
+#define FEC_OC_STATUS_FIFO_FULL__PRE 0x0
+
+#define FEC_OC_STATUS_FIFO_EMPTY__B 4
+#define FEC_OC_STATUS_FIFO_EMPTY__W 1
+#define FEC_OC_STATUS_FIFO_EMPTY__M 0x10
+#define FEC_OC_STATUS_FIFO_EMPTY__PRE 0x0
+
+#define FEC_OC_MODE__A 0x2440011
+#define FEC_OC_MODE__W 4
+#define FEC_OC_MODE__M 0xF
+#define FEC_OC_MODE__PRE 0x0
+
+#define FEC_OC_MODE_PARITY__B 0
+#define FEC_OC_MODE_PARITY__W 1
+#define FEC_OC_MODE_PARITY__M 0x1
+#define FEC_OC_MODE_PARITY__PRE 0x0
+
+#define FEC_OC_MODE_TRANSPARENT__B 1
+#define FEC_OC_MODE_TRANSPARENT__W 1
+#define FEC_OC_MODE_TRANSPARENT__M 0x2
+#define FEC_OC_MODE_TRANSPARENT__PRE 0x0
+
+#define FEC_OC_MODE_CLEAR__B 2
+#define FEC_OC_MODE_CLEAR__W 1
+#define FEC_OC_MODE_CLEAR__M 0x4
+#define FEC_OC_MODE_CLEAR__PRE 0x0
+
+#define FEC_OC_MODE_RETAIN_FRAMING__B 3
+#define FEC_OC_MODE_RETAIN_FRAMING__W 1
+#define FEC_OC_MODE_RETAIN_FRAMING__M 0x8
+#define FEC_OC_MODE_RETAIN_FRAMING__PRE 0x0
+
+#define FEC_OC_DPR_MODE__A 0x2440012
+#define FEC_OC_DPR_MODE__W 2
+#define FEC_OC_DPR_MODE__M 0x3
+#define FEC_OC_DPR_MODE__PRE 0x0
+
+#define FEC_OC_DPR_MODE_ERR_DISABLE__B 0
+#define FEC_OC_DPR_MODE_ERR_DISABLE__W 1
+#define FEC_OC_DPR_MODE_ERR_DISABLE__M 0x1
+#define FEC_OC_DPR_MODE_ERR_DISABLE__PRE 0x0
+
+#define FEC_OC_DPR_MODE_NOSYNC_ENABLE__B 1
+#define FEC_OC_DPR_MODE_NOSYNC_ENABLE__W 1
+#define FEC_OC_DPR_MODE_NOSYNC_ENABLE__M 0x2
+#define FEC_OC_DPR_MODE_NOSYNC_ENABLE__PRE 0x0
+
+#define FEC_OC_DPR_UNLOCK__A 0x2440013
+#define FEC_OC_DPR_UNLOCK__W 1
+#define FEC_OC_DPR_UNLOCK__M 0x1
+#define FEC_OC_DPR_UNLOCK__PRE 0x0
+#define FEC_OC_DTO_MODE__A 0x2440014
+#define FEC_OC_DTO_MODE__W 3
+#define FEC_OC_DTO_MODE__M 0x7
+#define FEC_OC_DTO_MODE__PRE 0x0
+
+#define FEC_OC_DTO_MODE_DYNAMIC__B 0
+#define FEC_OC_DTO_MODE_DYNAMIC__W 1
+#define FEC_OC_DTO_MODE_DYNAMIC__M 0x1
+#define FEC_OC_DTO_MODE_DYNAMIC__PRE 0x0
+
+#define FEC_OC_DTO_MODE_DUTY_CYCLE__B 1
+#define FEC_OC_DTO_MODE_DUTY_CYCLE__W 1
+#define FEC_OC_DTO_MODE_DUTY_CYCLE__M 0x2
+#define FEC_OC_DTO_MODE_DUTY_CYCLE__PRE 0x0
+
+#define FEC_OC_DTO_MODE_OFFSET_ENABLE__B 2
+#define FEC_OC_DTO_MODE_OFFSET_ENABLE__W 1
+#define FEC_OC_DTO_MODE_OFFSET_ENABLE__M 0x4
+#define FEC_OC_DTO_MODE_OFFSET_ENABLE__PRE 0x0
+
+#define FEC_OC_DTO_PERIOD__A 0x2440015
+#define FEC_OC_DTO_PERIOD__W 8
+#define FEC_OC_DTO_PERIOD__M 0xFF
+#define FEC_OC_DTO_PERIOD__PRE 0x0
+#define FEC_OC_DTO_RATE_LO__A 0x2440016
+#define FEC_OC_DTO_RATE_LO__W 16
+#define FEC_OC_DTO_RATE_LO__M 0xFFFF
+#define FEC_OC_DTO_RATE_LO__PRE 0x0
+
+#define FEC_OC_DTO_RATE_LO_RATE_LO__B 0
+#define FEC_OC_DTO_RATE_LO_RATE_LO__W 16
+#define FEC_OC_DTO_RATE_LO_RATE_LO__M 0xFFFF
+#define FEC_OC_DTO_RATE_LO_RATE_LO__PRE 0x0
+
+#define FEC_OC_DTO_RATE_HI__A 0x2440017
+#define FEC_OC_DTO_RATE_HI__W 10
+#define FEC_OC_DTO_RATE_HI__M 0x3FF
+#define FEC_OC_DTO_RATE_HI__PRE 0xC0
+
+#define FEC_OC_DTO_RATE_HI_RATE_HI__B 0
+#define FEC_OC_DTO_RATE_HI_RATE_HI__W 10
+#define FEC_OC_DTO_RATE_HI_RATE_HI__M 0x3FF
+#define FEC_OC_DTO_RATE_HI_RATE_HI__PRE 0xC0
+
+#define FEC_OC_DTO_BURST_LEN__A 0x2440018
+#define FEC_OC_DTO_BURST_LEN__W 8
+#define FEC_OC_DTO_BURST_LEN__M 0xFF
+#define FEC_OC_DTO_BURST_LEN__PRE 0xBC
+
+#define FEC_OC_DTO_BURST_LEN_BURST_LEN__B 0
+#define FEC_OC_DTO_BURST_LEN_BURST_LEN__W 8
+#define FEC_OC_DTO_BURST_LEN_BURST_LEN__M 0xFF
+#define FEC_OC_DTO_BURST_LEN_BURST_LEN__PRE 0xBC
+
+#define FEC_OC_FCT_MODE__A 0x244001A
+#define FEC_OC_FCT_MODE__W 2
+#define FEC_OC_FCT_MODE__M 0x3
+#define FEC_OC_FCT_MODE__PRE 0x0
+
+#define FEC_OC_FCT_MODE_RAT_ENA__B 0
+#define FEC_OC_FCT_MODE_RAT_ENA__W 1
+#define FEC_OC_FCT_MODE_RAT_ENA__M 0x1
+#define FEC_OC_FCT_MODE_RAT_ENA__PRE 0x0
+
+#define FEC_OC_FCT_MODE_VIRT_ENA__B 1
+#define FEC_OC_FCT_MODE_VIRT_ENA__W 1
+#define FEC_OC_FCT_MODE_VIRT_ENA__M 0x2
+#define FEC_OC_FCT_MODE_VIRT_ENA__PRE 0x0
+
+#define FEC_OC_FCT_USAGE__A 0x244001B
+#define FEC_OC_FCT_USAGE__W 3
+#define FEC_OC_FCT_USAGE__M 0x7
+#define FEC_OC_FCT_USAGE__PRE 0x2
+
+#define FEC_OC_FCT_USAGE_USAGE__B 0
+#define FEC_OC_FCT_USAGE_USAGE__W 3
+#define FEC_OC_FCT_USAGE_USAGE__M 0x7
+#define FEC_OC_FCT_USAGE_USAGE__PRE 0x2
+
+#define FEC_OC_FCT_OCCUPATION__A 0x244001C
+#define FEC_OC_FCT_OCCUPATION__W 12
+#define FEC_OC_FCT_OCCUPATION__M 0xFFF
+#define FEC_OC_FCT_OCCUPATION__PRE 0x0
+
+#define FEC_OC_FCT_OCCUPATION_OCCUPATION__B 0
+#define FEC_OC_FCT_OCCUPATION_OCCUPATION__W 12
+#define FEC_OC_FCT_OCCUPATION_OCCUPATION__M 0xFFF
+#define FEC_OC_FCT_OCCUPATION_OCCUPATION__PRE 0x0
+
+#define FEC_OC_TMD_MODE__A 0x244001E
+#define FEC_OC_TMD_MODE__W 3
+#define FEC_OC_TMD_MODE__M 0x7
+#define FEC_OC_TMD_MODE__PRE 0x4
+
+#define FEC_OC_TMD_MODE_MODE__B 0
+#define FEC_OC_TMD_MODE_MODE__W 3
+#define FEC_OC_TMD_MODE_MODE__M 0x7
+#define FEC_OC_TMD_MODE_MODE__PRE 0x4
+
+#define FEC_OC_TMD_COUNT__A 0x244001F
+#define FEC_OC_TMD_COUNT__W 10
+#define FEC_OC_TMD_COUNT__M 0x3FF
+#define FEC_OC_TMD_COUNT__PRE 0x1F4
+
+#define FEC_OC_TMD_COUNT_COUNT__B 0
+#define FEC_OC_TMD_COUNT_COUNT__W 10
+#define FEC_OC_TMD_COUNT_COUNT__M 0x3FF
+#define FEC_OC_TMD_COUNT_COUNT__PRE 0x1F4
+
+#define FEC_OC_TMD_HI_MARGIN__A 0x2440020
+#define FEC_OC_TMD_HI_MARGIN__W 11
+#define FEC_OC_TMD_HI_MARGIN__M 0x7FF
+#define FEC_OC_TMD_HI_MARGIN__PRE 0x200
+
+#define FEC_OC_TMD_HI_MARGIN_HI_MARGIN__B 0
+#define FEC_OC_TMD_HI_MARGIN_HI_MARGIN__W 11
+#define FEC_OC_TMD_HI_MARGIN_HI_MARGIN__M 0x7FF
+#define FEC_OC_TMD_HI_MARGIN_HI_MARGIN__PRE 0x200
+
+#define FEC_OC_TMD_LO_MARGIN__A 0x2440021
+#define FEC_OC_TMD_LO_MARGIN__W 11
+#define FEC_OC_TMD_LO_MARGIN__M 0x7FF
+#define FEC_OC_TMD_LO_MARGIN__PRE 0x100
+
+#define FEC_OC_TMD_LO_MARGIN_LO_MARGIN__B 0
+#define FEC_OC_TMD_LO_MARGIN_LO_MARGIN__W 11
+#define FEC_OC_TMD_LO_MARGIN_LO_MARGIN__M 0x7FF
+#define FEC_OC_TMD_LO_MARGIN_LO_MARGIN__PRE 0x100
+
+#define FEC_OC_TMD_CTL_UPD_RATE__A 0x2440022
+#define FEC_OC_TMD_CTL_UPD_RATE__W 4
+#define FEC_OC_TMD_CTL_UPD_RATE__M 0xF
+#define FEC_OC_TMD_CTL_UPD_RATE__PRE 0x1
+
+#define FEC_OC_TMD_CTL_UPD_RATE_RATE__B 0
+#define FEC_OC_TMD_CTL_UPD_RATE_RATE__W 4
+#define FEC_OC_TMD_CTL_UPD_RATE_RATE__M 0xF
+#define FEC_OC_TMD_CTL_UPD_RATE_RATE__PRE 0x1
+
+#define FEC_OC_TMD_INT_UPD_RATE__A 0x2440023
+#define FEC_OC_TMD_INT_UPD_RATE__W 4
+#define FEC_OC_TMD_INT_UPD_RATE__M 0xF
+#define FEC_OC_TMD_INT_UPD_RATE__PRE 0x4
+
+#define FEC_OC_TMD_INT_UPD_RATE_RATE__B 0
+#define FEC_OC_TMD_INT_UPD_RATE_RATE__W 4
+#define FEC_OC_TMD_INT_UPD_RATE_RATE__M 0xF
+#define FEC_OC_TMD_INT_UPD_RATE_RATE__PRE 0x4
+
+#define FEC_OC_AVR_PARM_A__A 0x2440026
+#define FEC_OC_AVR_PARM_A__W 4
+#define FEC_OC_AVR_PARM_A__M 0xF
+#define FEC_OC_AVR_PARM_A__PRE 0x6
+
+#define FEC_OC_AVR_PARM_A_PARM__B 0
+#define FEC_OC_AVR_PARM_A_PARM__W 4
+#define FEC_OC_AVR_PARM_A_PARM__M 0xF
+#define FEC_OC_AVR_PARM_A_PARM__PRE 0x6
+
+#define FEC_OC_AVR_PARM_B__A 0x2440027
+#define FEC_OC_AVR_PARM_B__W 4
+#define FEC_OC_AVR_PARM_B__M 0xF
+#define FEC_OC_AVR_PARM_B__PRE 0x4
+
+#define FEC_OC_AVR_PARM_B_PARM__B 0
+#define FEC_OC_AVR_PARM_B_PARM__W 4
+#define FEC_OC_AVR_PARM_B_PARM__M 0xF
+#define FEC_OC_AVR_PARM_B_PARM__PRE 0x4
+
+#define FEC_OC_AVR_AVG_LO__A 0x2440028
+#define FEC_OC_AVR_AVG_LO__W 16
+#define FEC_OC_AVR_AVG_LO__M 0xFFFF
+#define FEC_OC_AVR_AVG_LO__PRE 0x0
+
+#define FEC_OC_AVR_AVG_LO_AVG_LO__B 0
+#define FEC_OC_AVR_AVG_LO_AVG_LO__W 16
+#define FEC_OC_AVR_AVG_LO_AVG_LO__M 0xFFFF
+#define FEC_OC_AVR_AVG_LO_AVG_LO__PRE 0x0
+
+#define FEC_OC_AVR_AVG_HI__A 0x2440029
+#define FEC_OC_AVR_AVG_HI__W 6
+#define FEC_OC_AVR_AVG_HI__M 0x3F
+#define FEC_OC_AVR_AVG_HI__PRE 0x0
+
+#define FEC_OC_AVR_AVG_HI_AVG_HI__B 0
+#define FEC_OC_AVR_AVG_HI_AVG_HI__W 6
+#define FEC_OC_AVR_AVG_HI_AVG_HI__M 0x3F
+#define FEC_OC_AVR_AVG_HI_AVG_HI__PRE 0x0
+
+#define FEC_OC_RCN_MODE__A 0x244002C
+#define FEC_OC_RCN_MODE__W 5
+#define FEC_OC_RCN_MODE__M 0x1F
+#define FEC_OC_RCN_MODE__PRE 0x1F
+
+#define FEC_OC_RCN_MODE_MODE__B 0
+#define FEC_OC_RCN_MODE_MODE__W 5
+#define FEC_OC_RCN_MODE_MODE__M 0x1F
+#define FEC_OC_RCN_MODE_MODE__PRE 0x1F
+
+#define FEC_OC_RCN_OCC_SETTLE__A 0x244002D
+#define FEC_OC_RCN_OCC_SETTLE__W 11
+#define FEC_OC_RCN_OCC_SETTLE__M 0x7FF
+#define FEC_OC_RCN_OCC_SETTLE__PRE 0x180
+
+#define FEC_OC_RCN_OCC_SETTLE_LEVEL__B 0
+#define FEC_OC_RCN_OCC_SETTLE_LEVEL__W 11
+#define FEC_OC_RCN_OCC_SETTLE_LEVEL__M 0x7FF
+#define FEC_OC_RCN_OCC_SETTLE_LEVEL__PRE 0x180
+
+#define FEC_OC_RCN_GAIN__A 0x244002E
+#define FEC_OC_RCN_GAIN__W 4
+#define FEC_OC_RCN_GAIN__M 0xF
+#define FEC_OC_RCN_GAIN__PRE 0xC
+
+#define FEC_OC_RCN_GAIN_GAIN__B 0
+#define FEC_OC_RCN_GAIN_GAIN__W 4
+#define FEC_OC_RCN_GAIN_GAIN__M 0xF
+#define FEC_OC_RCN_GAIN_GAIN__PRE 0xC
+
+#define FEC_OC_RCN_CTL_RATE_LO__A 0x2440030
+#define FEC_OC_RCN_CTL_RATE_LO__W 16
+#define FEC_OC_RCN_CTL_RATE_LO__M 0xFFFF
+#define FEC_OC_RCN_CTL_RATE_LO__PRE 0x0
+
+#define FEC_OC_RCN_CTL_RATE_LO_CTL_LO__B 0
+#define FEC_OC_RCN_CTL_RATE_LO_CTL_LO__W 16
+#define FEC_OC_RCN_CTL_RATE_LO_CTL_LO__M 0xFFFF
+#define FEC_OC_RCN_CTL_RATE_LO_CTL_LO__PRE 0x0
+
+#define FEC_OC_RCN_CTL_RATE_HI__A 0x2440031
+#define FEC_OC_RCN_CTL_RATE_HI__W 8
+#define FEC_OC_RCN_CTL_RATE_HI__M 0xFF
+#define FEC_OC_RCN_CTL_RATE_HI__PRE 0xC0
+
+#define FEC_OC_RCN_CTL_RATE_HI_CTL_HI__B 0
+#define FEC_OC_RCN_CTL_RATE_HI_CTL_HI__W 8
+#define FEC_OC_RCN_CTL_RATE_HI_CTL_HI__M 0xFF
+#define FEC_OC_RCN_CTL_RATE_HI_CTL_HI__PRE 0xC0
+
+#define FEC_OC_RCN_CTL_STEP_LO__A 0x2440032
+#define FEC_OC_RCN_CTL_STEP_LO__W 16
+#define FEC_OC_RCN_CTL_STEP_LO__M 0xFFFF
+#define FEC_OC_RCN_CTL_STEP_LO__PRE 0x0
+
+#define FEC_OC_RCN_CTL_STEP_LO_CTL_LO__B 0
+#define FEC_OC_RCN_CTL_STEP_LO_CTL_LO__W 16
+#define FEC_OC_RCN_CTL_STEP_LO_CTL_LO__M 0xFFFF
+#define FEC_OC_RCN_CTL_STEP_LO_CTL_LO__PRE 0x0
+
+#define FEC_OC_RCN_CTL_STEP_HI__A 0x2440033
+#define FEC_OC_RCN_CTL_STEP_HI__W 8
+#define FEC_OC_RCN_CTL_STEP_HI__M 0xFF
+#define FEC_OC_RCN_CTL_STEP_HI__PRE 0x8
+
+#define FEC_OC_RCN_CTL_STEP_HI_CTL_HI__B 0
+#define FEC_OC_RCN_CTL_STEP_HI_CTL_HI__W 8
+#define FEC_OC_RCN_CTL_STEP_HI_CTL_HI__M 0xFF
+#define FEC_OC_RCN_CTL_STEP_HI_CTL_HI__PRE 0x8
+
+#define FEC_OC_RCN_DTO_OFS_LO__A 0x2440034
+#define FEC_OC_RCN_DTO_OFS_LO__W 16
+#define FEC_OC_RCN_DTO_OFS_LO__M 0xFFFF
+#define FEC_OC_RCN_DTO_OFS_LO__PRE 0x0
+
+#define FEC_OC_RCN_DTO_OFS_LO_OFS_LO__B 0
+#define FEC_OC_RCN_DTO_OFS_LO_OFS_LO__W 16
+#define FEC_OC_RCN_DTO_OFS_LO_OFS_LO__M 0xFFFF
+#define FEC_OC_RCN_DTO_OFS_LO_OFS_LO__PRE 0x0
+
+#define FEC_OC_RCN_DTO_OFS_HI__A 0x2440035
+#define FEC_OC_RCN_DTO_OFS_HI__W 8
+#define FEC_OC_RCN_DTO_OFS_HI__M 0xFF
+#define FEC_OC_RCN_DTO_OFS_HI__PRE 0x0
+
+#define FEC_OC_RCN_DTO_OFS_HI_OFS_HI__B 0
+#define FEC_OC_RCN_DTO_OFS_HI_OFS_HI__W 8
+#define FEC_OC_RCN_DTO_OFS_HI_OFS_HI__M 0xFF
+#define FEC_OC_RCN_DTO_OFS_HI_OFS_HI__PRE 0x0
+
+#define FEC_OC_RCN_DTO_RATE_LO__A 0x2440036
+#define FEC_OC_RCN_DTO_RATE_LO__W 16
+#define FEC_OC_RCN_DTO_RATE_LO__M 0xFFFF
+#define FEC_OC_RCN_DTO_RATE_LO__PRE 0x0
+
+#define FEC_OC_RCN_DTO_RATE_LO_OFS_LO__B 0
+#define FEC_OC_RCN_DTO_RATE_LO_OFS_LO__W 16
+#define FEC_OC_RCN_DTO_RATE_LO_OFS_LO__M 0xFFFF
+#define FEC_OC_RCN_DTO_RATE_LO_OFS_LO__PRE 0x0
+
+#define FEC_OC_RCN_DTO_RATE_HI__A 0x2440037
+#define FEC_OC_RCN_DTO_RATE_HI__W 8
+#define FEC_OC_RCN_DTO_RATE_HI__M 0xFF
+#define FEC_OC_RCN_DTO_RATE_HI__PRE 0x0
+
+#define FEC_OC_RCN_DTO_RATE_HI_OFS_HI__B 0
+#define FEC_OC_RCN_DTO_RATE_HI_OFS_HI__W 8
+#define FEC_OC_RCN_DTO_RATE_HI_OFS_HI__M 0xFF
+#define FEC_OC_RCN_DTO_RATE_HI_OFS_HI__PRE 0x0
+
+#define FEC_OC_RCN_RATE_CLIP_LO__A 0x2440038
+#define FEC_OC_RCN_RATE_CLIP_LO__W 16
+#define FEC_OC_RCN_RATE_CLIP_LO__M 0xFFFF
+#define FEC_OC_RCN_RATE_CLIP_LO__PRE 0x0
+
+#define FEC_OC_RCN_RATE_CLIP_LO_CLIP_LO__B 0
+#define FEC_OC_RCN_RATE_CLIP_LO_CLIP_LO__W 16
+#define FEC_OC_RCN_RATE_CLIP_LO_CLIP_LO__M 0xFFFF
+#define FEC_OC_RCN_RATE_CLIP_LO_CLIP_LO__PRE 0x0
+
+#define FEC_OC_RCN_RATE_CLIP_HI__A 0x2440039
+#define FEC_OC_RCN_RATE_CLIP_HI__W 8
+#define FEC_OC_RCN_RATE_CLIP_HI__M 0xFF
+#define FEC_OC_RCN_RATE_CLIP_HI__PRE 0xF0
+
+#define FEC_OC_RCN_RATE_CLIP_HI_CLIP_HI__B 0
+#define FEC_OC_RCN_RATE_CLIP_HI_CLIP_HI__W 8
+#define FEC_OC_RCN_RATE_CLIP_HI_CLIP_HI__M 0xFF
+#define FEC_OC_RCN_RATE_CLIP_HI_CLIP_HI__PRE 0xF0
+
+#define FEC_OC_RCN_DYN_RATE_LO__A 0x244003A
+#define FEC_OC_RCN_DYN_RATE_LO__W 16
+#define FEC_OC_RCN_DYN_RATE_LO__M 0xFFFF
+#define FEC_OC_RCN_DYN_RATE_LO__PRE 0x0
+
+#define FEC_OC_RCN_DYN_RATE_LO_RATE_LO__B 0
+#define FEC_OC_RCN_DYN_RATE_LO_RATE_LO__W 16
+#define FEC_OC_RCN_DYN_RATE_LO_RATE_LO__M 0xFFFF
+#define FEC_OC_RCN_DYN_RATE_LO_RATE_LO__PRE 0x0
+
+#define FEC_OC_RCN_DYN_RATE_HI__A 0x244003B
+#define FEC_OC_RCN_DYN_RATE_HI__W 8
+#define FEC_OC_RCN_DYN_RATE_HI__M 0xFF
+#define FEC_OC_RCN_DYN_RATE_HI__PRE 0x0
+
+#define FEC_OC_RCN_DYN_RATE_HI_RATE_HI__B 0
+#define FEC_OC_RCN_DYN_RATE_HI_RATE_HI__W 8
+#define FEC_OC_RCN_DYN_RATE_HI_RATE_HI__M 0xFF
+#define FEC_OC_RCN_DYN_RATE_HI_RATE_HI__PRE 0x0
+
+#define FEC_OC_SNC_MODE__A 0x2440040
+#define FEC_OC_SNC_MODE__W 4
+#define FEC_OC_SNC_MODE__M 0xF
+#define FEC_OC_SNC_MODE__PRE 0x0
+
+#define FEC_OC_SNC_MODE_UNLOCK_ENABLE__B 0
+#define FEC_OC_SNC_MODE_UNLOCK_ENABLE__W 1
+#define FEC_OC_SNC_MODE_UNLOCK_ENABLE__M 0x1
+#define FEC_OC_SNC_MODE_UNLOCK_ENABLE__PRE 0x0
+
+#define FEC_OC_SNC_MODE_ERROR_CTL__B 1
+#define FEC_OC_SNC_MODE_ERROR_CTL__W 2
+#define FEC_OC_SNC_MODE_ERROR_CTL__M 0x6
+#define FEC_OC_SNC_MODE_ERROR_CTL__PRE 0x0
+
+#define FEC_OC_SNC_MODE_CORR_DISABLE__B 3
+#define FEC_OC_SNC_MODE_CORR_DISABLE__W 1
+#define FEC_OC_SNC_MODE_CORR_DISABLE__M 0x8
+#define FEC_OC_SNC_MODE_CORR_DISABLE__PRE 0x0
+
+#define FEC_OC_SNC_LWM__A 0x2440041
+#define FEC_OC_SNC_LWM__W 4
+#define FEC_OC_SNC_LWM__M 0xF
+#define FEC_OC_SNC_LWM__PRE 0x3
+
+#define FEC_OC_SNC_LWM_MARK__B 0
+#define FEC_OC_SNC_LWM_MARK__W 4
+#define FEC_OC_SNC_LWM_MARK__M 0xF
+#define FEC_OC_SNC_LWM_MARK__PRE 0x3
+
+#define FEC_OC_SNC_HWM__A 0x2440042
+#define FEC_OC_SNC_HWM__W 4
+#define FEC_OC_SNC_HWM__M 0xF
+#define FEC_OC_SNC_HWM__PRE 0x5
+
+#define FEC_OC_SNC_HWM_MARK__B 0
+#define FEC_OC_SNC_HWM_MARK__W 4
+#define FEC_OC_SNC_HWM_MARK__M 0xF
+#define FEC_OC_SNC_HWM_MARK__PRE 0x5
+
+#define FEC_OC_SNC_UNLOCK__A 0x2440043
+#define FEC_OC_SNC_UNLOCK__W 1
+#define FEC_OC_SNC_UNLOCK__M 0x1
+#define FEC_OC_SNC_UNLOCK__PRE 0x0
+
+#define FEC_OC_SNC_UNLOCK_RESTART__B 0
+#define FEC_OC_SNC_UNLOCK_RESTART__W 1
+#define FEC_OC_SNC_UNLOCK_RESTART__M 0x1
+#define FEC_OC_SNC_UNLOCK_RESTART__PRE 0x0
+
+#define FEC_OC_SNC_LOCK_COUNT__A 0x2440044
+#define FEC_OC_SNC_LOCK_COUNT__W 12
+#define FEC_OC_SNC_LOCK_COUNT__M 0xFFF
+#define FEC_OC_SNC_LOCK_COUNT__PRE 0x0
+
+#define FEC_OC_SNC_LOCK_COUNT_COUNT__B 0
+#define FEC_OC_SNC_LOCK_COUNT_COUNT__W 12
+#define FEC_OC_SNC_LOCK_COUNT_COUNT__M 0xFFF
+#define FEC_OC_SNC_LOCK_COUNT_COUNT__PRE 0x0
+
+#define FEC_OC_SNC_FAIL_COUNT__A 0x2440045
+#define FEC_OC_SNC_FAIL_COUNT__W 12
+#define FEC_OC_SNC_FAIL_COUNT__M 0xFFF
+#define FEC_OC_SNC_FAIL_COUNT__PRE 0x0
+
+#define FEC_OC_SNC_FAIL_COUNT_COUNT__B 0
+#define FEC_OC_SNC_FAIL_COUNT_COUNT__W 12
+#define FEC_OC_SNC_FAIL_COUNT_COUNT__M 0xFFF
+#define FEC_OC_SNC_FAIL_COUNT_COUNT__PRE 0x0
+
+#define FEC_OC_SNC_FAIL_PERIOD__A 0x2440046
+#define FEC_OC_SNC_FAIL_PERIOD__W 16
+#define FEC_OC_SNC_FAIL_PERIOD__M 0xFFFF
+#define FEC_OC_SNC_FAIL_PERIOD__PRE 0x1171
+
+#define FEC_OC_SNC_FAIL_PERIOD_PERIOD__B 0
+#define FEC_OC_SNC_FAIL_PERIOD_PERIOD__W 16
+#define FEC_OC_SNC_FAIL_PERIOD_PERIOD__M 0xFFFF
+#define FEC_OC_SNC_FAIL_PERIOD_PERIOD__PRE 0x1171
+
+#define FEC_OC_EMS_MODE__A 0x2440047
+#define FEC_OC_EMS_MODE__W 2
+#define FEC_OC_EMS_MODE__M 0x3
+#define FEC_OC_EMS_MODE__PRE 0x0
+
+#define FEC_OC_EMS_MODE_MODE__B 0
+#define FEC_OC_EMS_MODE_MODE__W 2
+#define FEC_OC_EMS_MODE_MODE__M 0x3
+#define FEC_OC_EMS_MODE_MODE__PRE 0x0
+
+#define FEC_OC_IPR_MODE__A 0x2440048
+#define FEC_OC_IPR_MODE__W 12
+#define FEC_OC_IPR_MODE__M 0xFFF
+#define FEC_OC_IPR_MODE__PRE 0x0
+
+#define FEC_OC_IPR_MODE_SERIAL__B 0
+#define FEC_OC_IPR_MODE_SERIAL__W 1
+#define FEC_OC_IPR_MODE_SERIAL__M 0x1
+#define FEC_OC_IPR_MODE_SERIAL__PRE 0x0
+
+#define FEC_OC_IPR_MODE_REVERSE_ORDER__B 1
+#define FEC_OC_IPR_MODE_REVERSE_ORDER__W 1
+#define FEC_OC_IPR_MODE_REVERSE_ORDER__M 0x2
+#define FEC_OC_IPR_MODE_REVERSE_ORDER__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__B 2
+#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__W 1
+#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M 0x4
+#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MCLK_DIS_PAR__B 3
+#define FEC_OC_IPR_MODE_MCLK_DIS_PAR__W 1
+#define FEC_OC_IPR_MODE_MCLK_DIS_PAR__M 0x8
+#define FEC_OC_IPR_MODE_MCLK_DIS_PAR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__B 4
+#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__W 1
+#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__M 0x10
+#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MERR_DIS_PAR__B 5
+#define FEC_OC_IPR_MODE_MERR_DIS_PAR__W 1
+#define FEC_OC_IPR_MODE_MERR_DIS_PAR__M 0x20
+#define FEC_OC_IPR_MODE_MERR_DIS_PAR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MD_DIS_PAR__B 6
+#define FEC_OC_IPR_MODE_MD_DIS_PAR__W 1
+#define FEC_OC_IPR_MODE_MD_DIS_PAR__M 0x40
+#define FEC_OC_IPR_MODE_MD_DIS_PAR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MCLK_DIS_ERR__B 7
+#define FEC_OC_IPR_MODE_MCLK_DIS_ERR__W 1
+#define FEC_OC_IPR_MODE_MCLK_DIS_ERR__M 0x80
+#define FEC_OC_IPR_MODE_MCLK_DIS_ERR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MVAL_DIS_ERR__B 8
+#define FEC_OC_IPR_MODE_MVAL_DIS_ERR__W 1
+#define FEC_OC_IPR_MODE_MVAL_DIS_ERR__M 0x100
+#define FEC_OC_IPR_MODE_MVAL_DIS_ERR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MERR_DIS_ERR__B 9
+#define FEC_OC_IPR_MODE_MERR_DIS_ERR__W 1
+#define FEC_OC_IPR_MODE_MERR_DIS_ERR__M 0x200
+#define FEC_OC_IPR_MODE_MERR_DIS_ERR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MD_DIS_ERR__B 10
+#define FEC_OC_IPR_MODE_MD_DIS_ERR__W 1
+#define FEC_OC_IPR_MODE_MD_DIS_ERR__M 0x400
+#define FEC_OC_IPR_MODE_MD_DIS_ERR__PRE 0x0
+
+#define FEC_OC_IPR_MODE_MSTRT_DIS_ERR__B 11
+#define FEC_OC_IPR_MODE_MSTRT_DIS_ERR__W 1
+#define FEC_OC_IPR_MODE_MSTRT_DIS_ERR__M 0x800
+#define FEC_OC_IPR_MODE_MSTRT_DIS_ERR__PRE 0x0
+
+#define FEC_OC_IPR_INVERT__A 0x2440049
+#define FEC_OC_IPR_INVERT__W 12
+#define FEC_OC_IPR_INVERT__M 0xFFF
+#define FEC_OC_IPR_INVERT__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD0__B 0
+#define FEC_OC_IPR_INVERT_MD0__W 1
+#define FEC_OC_IPR_INVERT_MD0__M 0x1
+#define FEC_OC_IPR_INVERT_MD0__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD1__B 1
+#define FEC_OC_IPR_INVERT_MD1__W 1
+#define FEC_OC_IPR_INVERT_MD1__M 0x2
+#define FEC_OC_IPR_INVERT_MD1__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD2__B 2
+#define FEC_OC_IPR_INVERT_MD2__W 1
+#define FEC_OC_IPR_INVERT_MD2__M 0x4
+#define FEC_OC_IPR_INVERT_MD2__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD3__B 3
+#define FEC_OC_IPR_INVERT_MD3__W 1
+#define FEC_OC_IPR_INVERT_MD3__M 0x8
+#define FEC_OC_IPR_INVERT_MD3__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD4__B 4
+#define FEC_OC_IPR_INVERT_MD4__W 1
+#define FEC_OC_IPR_INVERT_MD4__M 0x10
+#define FEC_OC_IPR_INVERT_MD4__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD5__B 5
+#define FEC_OC_IPR_INVERT_MD5__W 1
+#define FEC_OC_IPR_INVERT_MD5__M 0x20
+#define FEC_OC_IPR_INVERT_MD5__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD6__B 6
+#define FEC_OC_IPR_INVERT_MD6__W 1
+#define FEC_OC_IPR_INVERT_MD6__M 0x40
+#define FEC_OC_IPR_INVERT_MD6__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MD7__B 7
+#define FEC_OC_IPR_INVERT_MD7__W 1
+#define FEC_OC_IPR_INVERT_MD7__M 0x80
+#define FEC_OC_IPR_INVERT_MD7__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MERR__B 8
+#define FEC_OC_IPR_INVERT_MERR__W 1
+#define FEC_OC_IPR_INVERT_MERR__M 0x100
+#define FEC_OC_IPR_INVERT_MERR__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MSTRT__B 9
+#define FEC_OC_IPR_INVERT_MSTRT__W 1
+#define FEC_OC_IPR_INVERT_MSTRT__M 0x200
+#define FEC_OC_IPR_INVERT_MSTRT__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MVAL__B 10
+#define FEC_OC_IPR_INVERT_MVAL__W 1
+#define FEC_OC_IPR_INVERT_MVAL__M 0x400
+#define FEC_OC_IPR_INVERT_MVAL__PRE 0x0
+
+#define FEC_OC_IPR_INVERT_MCLK__B 11
+#define FEC_OC_IPR_INVERT_MCLK__W 1
+#define FEC_OC_IPR_INVERT_MCLK__M 0x800
+#define FEC_OC_IPR_INVERT_MCLK__PRE 0x0
+
+#define FEC_OC_OCR_MODE__A 0x2440050
+#define FEC_OC_OCR_MODE__W 4
+#define FEC_OC_OCR_MODE__M 0xF
+#define FEC_OC_OCR_MODE__PRE 0x0
+
+#define FEC_OC_OCR_MODE_MB_SELECT__B 0
+#define FEC_OC_OCR_MODE_MB_SELECT__W 1
+#define FEC_OC_OCR_MODE_MB_SELECT__M 0x1
+#define FEC_OC_OCR_MODE_MB_SELECT__PRE 0x0
+
+#define FEC_OC_OCR_MODE_GRAB_ENABLE__B 1
+#define FEC_OC_OCR_MODE_GRAB_ENABLE__W 1
+#define FEC_OC_OCR_MODE_GRAB_ENABLE__M 0x2
+#define FEC_OC_OCR_MODE_GRAB_ENABLE__PRE 0x0
+
+#define FEC_OC_OCR_MODE_GRAB_SELECT__B 2
+#define FEC_OC_OCR_MODE_GRAB_SELECT__W 1
+#define FEC_OC_OCR_MODE_GRAB_SELECT__M 0x4
+#define FEC_OC_OCR_MODE_GRAB_SELECT__PRE 0x0
+
+#define FEC_OC_OCR_MODE_GRAB_COUNTED__B 3
+#define FEC_OC_OCR_MODE_GRAB_COUNTED__W 1
+#define FEC_OC_OCR_MODE_GRAB_COUNTED__M 0x8
+#define FEC_OC_OCR_MODE_GRAB_COUNTED__PRE 0x0
+
+#define FEC_OC_OCR_RATE__A 0x2440051
+#define FEC_OC_OCR_RATE__W 4
+#define FEC_OC_OCR_RATE__M 0xF
+#define FEC_OC_OCR_RATE__PRE 0x0
+
+#define FEC_OC_OCR_RATE_RATE__B 0
+#define FEC_OC_OCR_RATE_RATE__W 4
+#define FEC_OC_OCR_RATE_RATE__M 0xF
+#define FEC_OC_OCR_RATE_RATE__PRE 0x0
+
+#define FEC_OC_OCR_INVERT__A 0x2440052
+#define FEC_OC_OCR_INVERT__W 12
+#define FEC_OC_OCR_INVERT__M 0xFFF
+#define FEC_OC_OCR_INVERT__PRE 0x800
+
+#define FEC_OC_OCR_INVERT_INVERT__B 0
+#define FEC_OC_OCR_INVERT_INVERT__W 12
+#define FEC_OC_OCR_INVERT_INVERT__M 0xFFF
+#define FEC_OC_OCR_INVERT_INVERT__PRE 0x800
+
+#define FEC_OC_OCR_GRAB_COUNT__A 0x2440053
+#define FEC_OC_OCR_GRAB_COUNT__W 16
+#define FEC_OC_OCR_GRAB_COUNT__M 0xFFFF
+#define FEC_OC_OCR_GRAB_COUNT__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_COUNT_COUNT__B 0
+#define FEC_OC_OCR_GRAB_COUNT_COUNT__W 16
+#define FEC_OC_OCR_GRAB_COUNT_COUNT__M 0xFFFF
+#define FEC_OC_OCR_GRAB_COUNT_COUNT__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_SYNC__A 0x2440054
+#define FEC_OC_OCR_GRAB_SYNC__W 8
+#define FEC_OC_OCR_GRAB_SYNC__M 0xFF
+#define FEC_OC_OCR_GRAB_SYNC__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_SYNC_BYTE_SEL__B 0
+#define FEC_OC_OCR_GRAB_SYNC_BYTE_SEL__W 3
+#define FEC_OC_OCR_GRAB_SYNC_BYTE_SEL__M 0x7
+#define FEC_OC_OCR_GRAB_SYNC_BYTE_SEL__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_SYNC_BIT_SEL__B 3
+#define FEC_OC_OCR_GRAB_SYNC_BIT_SEL__W 4
+#define FEC_OC_OCR_GRAB_SYNC_BIT_SEL__M 0x78
+#define FEC_OC_OCR_GRAB_SYNC_BIT_SEL__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_SYNC_VALUE_SEL__B 7
+#define FEC_OC_OCR_GRAB_SYNC_VALUE_SEL__W 1
+#define FEC_OC_OCR_GRAB_SYNC_VALUE_SEL__M 0x80
+#define FEC_OC_OCR_GRAB_SYNC_VALUE_SEL__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD0__A 0x2440055
+#define FEC_OC_OCR_GRAB_RD0__W 10
+#define FEC_OC_OCR_GRAB_RD0__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD0__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD0_DATA__B 0
+#define FEC_OC_OCR_GRAB_RD0_DATA__W 10
+#define FEC_OC_OCR_GRAB_RD0_DATA__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD0_DATA__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD1__A 0x2440056
+#define FEC_OC_OCR_GRAB_RD1__W 10
+#define FEC_OC_OCR_GRAB_RD1__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD1__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD1_DATA__B 0
+#define FEC_OC_OCR_GRAB_RD1_DATA__W 10
+#define FEC_OC_OCR_GRAB_RD1_DATA__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD1_DATA__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD2__A 0x2440057
+#define FEC_OC_OCR_GRAB_RD2__W 10
+#define FEC_OC_OCR_GRAB_RD2__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD2__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD2_DATA__B 0
+#define FEC_OC_OCR_GRAB_RD2_DATA__W 10
+#define FEC_OC_OCR_GRAB_RD2_DATA__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD2_DATA__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD3__A 0x2440058
+#define FEC_OC_OCR_GRAB_RD3__W 10
+#define FEC_OC_OCR_GRAB_RD3__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD3__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD3_DATA__B 0
+#define FEC_OC_OCR_GRAB_RD3_DATA__W 10
+#define FEC_OC_OCR_GRAB_RD3_DATA__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD3_DATA__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD4__A 0x2440059
+#define FEC_OC_OCR_GRAB_RD4__W 10
+#define FEC_OC_OCR_GRAB_RD4__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD4__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD4_DATA__B 0
+#define FEC_OC_OCR_GRAB_RD4_DATA__W 10
+#define FEC_OC_OCR_GRAB_RD4_DATA__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD4_DATA__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD5__A 0x244005A
+#define FEC_OC_OCR_GRAB_RD5__W 10
+#define FEC_OC_OCR_GRAB_RD5__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD5__PRE 0x0
+
+#define FEC_OC_OCR_GRAB_RD5_DATA__B 0
+#define FEC_OC_OCR_GRAB_RD5_DATA__W 10
+#define FEC_OC_OCR_GRAB_RD5_DATA__M 0x3FF
+#define FEC_OC_OCR_GRAB_RD5_DATA__PRE 0x0
+
+#define FEC_DI_RAM__A 0x2450000
+
+#define FEC_RS_RAM__A 0x2460000
+
+#define FEC_OC_RAM__A 0x2470000
+
+#define IQM_COMM_EXEC__A 0x1800000
+#define IQM_COMM_EXEC__W 2
+#define IQM_COMM_EXEC__M 0x3
+#define IQM_COMM_EXEC__PRE 0x0
+#define IQM_COMM_EXEC_STOP 0x0
+#define IQM_COMM_EXEC_ACTIVE 0x1
+#define IQM_COMM_EXEC_HOLD 0x2
+
+#define IQM_COMM_MB__A 0x1800002
+#define IQM_COMM_MB__W 16
+#define IQM_COMM_MB__M 0xFFFF
+#define IQM_COMM_MB__PRE 0x0
+#define IQM_COMM_INT_REQ__A 0x1800003
+#define IQM_COMM_INT_REQ__W 2
+#define IQM_COMM_INT_REQ__M 0x3
+#define IQM_COMM_INT_REQ__PRE 0x0
+
+#define IQM_COMM_INT_REQ_AF_REQ__B 0
+#define IQM_COMM_INT_REQ_AF_REQ__W 1
+#define IQM_COMM_INT_REQ_AF_REQ__M 0x1
+#define IQM_COMM_INT_REQ_AF_REQ__PRE 0x0
+
+#define IQM_COMM_INT_REQ_CF_REQ__B 1
+#define IQM_COMM_INT_REQ_CF_REQ__W 1
+#define IQM_COMM_INT_REQ_CF_REQ__M 0x2
+#define IQM_COMM_INT_REQ_CF_REQ__PRE 0x0
+
+#define IQM_COMM_INT_STA__A 0x1800005
+#define IQM_COMM_INT_STA__W 16
+#define IQM_COMM_INT_STA__M 0xFFFF
+#define IQM_COMM_INT_STA__PRE 0x0
+#define IQM_COMM_INT_MSK__A 0x1800006
+#define IQM_COMM_INT_MSK__W 16
+#define IQM_COMM_INT_MSK__M 0xFFFF
+#define IQM_COMM_INT_MSK__PRE 0x0
+#define IQM_COMM_INT_STM__A 0x1800007
+#define IQM_COMM_INT_STM__W 16
+#define IQM_COMM_INT_STM__M 0xFFFF
+#define IQM_COMM_INT_STM__PRE 0x0
+
+#define IQM_FS_COMM_EXEC__A 0x1820000
+#define IQM_FS_COMM_EXEC__W 2
+#define IQM_FS_COMM_EXEC__M 0x3
+#define IQM_FS_COMM_EXEC__PRE 0x0
+#define IQM_FS_COMM_EXEC_STOP 0x0
+#define IQM_FS_COMM_EXEC_ACTIVE 0x1
+#define IQM_FS_COMM_EXEC_HOLD 0x2
+
+#define IQM_FS_COMM_MB__A 0x1820002
+#define IQM_FS_COMM_MB__W 2
+#define IQM_FS_COMM_MB__M 0x3
+#define IQM_FS_COMM_MB__PRE 0x0
+#define IQM_FS_COMM_MB_CTL__B 0
+#define IQM_FS_COMM_MB_CTL__W 1
+#define IQM_FS_COMM_MB_CTL__M 0x1
+#define IQM_FS_COMM_MB_CTL__PRE 0x0
+#define IQM_FS_COMM_MB_CTL_CTL_OFF 0x0
+#define IQM_FS_COMM_MB_CTL_CTL_ON 0x1
+#define IQM_FS_COMM_MB_OBS__B 1
+#define IQM_FS_COMM_MB_OBS__W 1
+#define IQM_FS_COMM_MB_OBS__M 0x2
+#define IQM_FS_COMM_MB_OBS__PRE 0x0
+#define IQM_FS_COMM_MB_OBS_OBS_OFF 0x0
+#define IQM_FS_COMM_MB_OBS_OBS_ON 0x2
+
+#define IQM_FS_RATE_OFS_LO__A 0x1820010
+#define IQM_FS_RATE_OFS_LO__W 16
+#define IQM_FS_RATE_OFS_LO__M 0xFFFF
+#define IQM_FS_RATE_OFS_LO__PRE 0x0
+#define IQM_FS_RATE_OFS_HI__A 0x1820011
+#define IQM_FS_RATE_OFS_HI__W 12
+#define IQM_FS_RATE_OFS_HI__M 0xFFF
+#define IQM_FS_RATE_OFS_HI__PRE 0x0
+#define IQM_FS_RATE_LO__A 0x1820012
+#define IQM_FS_RATE_LO__W 16
+#define IQM_FS_RATE_LO__M 0xFFFF
+#define IQM_FS_RATE_LO__PRE 0x0
+#define IQM_FS_RATE_HI__A 0x1820013
+#define IQM_FS_RATE_HI__W 12
+#define IQM_FS_RATE_HI__M 0xFFF
+#define IQM_FS_RATE_HI__PRE 0x0
+
+#define IQM_FS_ADJ_SEL__A 0x1820014
+#define IQM_FS_ADJ_SEL__W 2
+#define IQM_FS_ADJ_SEL__M 0x3
+#define IQM_FS_ADJ_SEL__PRE 0x0
+#define IQM_FS_ADJ_SEL_OFF 0x0
+#define IQM_FS_ADJ_SEL_QAM 0x1
+#define IQM_FS_ADJ_SEL_VSB 0x2
+
+#define IQM_FD_COMM_EXEC__A 0x1830000
+#define IQM_FD_COMM_EXEC__W 2
+#define IQM_FD_COMM_EXEC__M 0x3
+#define IQM_FD_COMM_EXEC__PRE 0x0
+#define IQM_FD_COMM_EXEC_STOP 0x0
+#define IQM_FD_COMM_EXEC_ACTIVE 0x1
+#define IQM_FD_COMM_EXEC_HOLD 0x2
+
+#define IQM_FD_COMM_MB__A 0x1830002
+#define IQM_FD_COMM_MB__W 2
+#define IQM_FD_COMM_MB__M 0x3
+#define IQM_FD_COMM_MB__PRE 0x0
+#define IQM_FD_COMM_MB_CTL__B 0
+#define IQM_FD_COMM_MB_CTL__W 1
+#define IQM_FD_COMM_MB_CTL__M 0x1
+#define IQM_FD_COMM_MB_CTL__PRE 0x0
+#define IQM_FD_COMM_MB_CTL_CTL_OFF 0x0
+#define IQM_FD_COMM_MB_CTL_CTL_ON 0x1
+#define IQM_FD_COMM_MB_OBS__B 1
+#define IQM_FD_COMM_MB_OBS__W 1
+#define IQM_FD_COMM_MB_OBS__M 0x2
+#define IQM_FD_COMM_MB_OBS__PRE 0x0
+#define IQM_FD_COMM_MB_OBS_OBS_OFF 0x0
+#define IQM_FD_COMM_MB_OBS_OBS_ON 0x2
+
+#define IQM_RC_COMM_EXEC__A 0x1840000
+#define IQM_RC_COMM_EXEC__W 2
+#define IQM_RC_COMM_EXEC__M 0x3
+#define IQM_RC_COMM_EXEC__PRE 0x0
+#define IQM_RC_COMM_EXEC_STOP 0x0
+#define IQM_RC_COMM_EXEC_ACTIVE 0x1
+#define IQM_RC_COMM_EXEC_HOLD 0x2
+
+#define IQM_RC_COMM_MB__A 0x1840002
+#define IQM_RC_COMM_MB__W 2
+#define IQM_RC_COMM_MB__M 0x3
+#define IQM_RC_COMM_MB__PRE 0x0
+#define IQM_RC_COMM_MB_CTL__B 0
+#define IQM_RC_COMM_MB_CTL__W 1
+#define IQM_RC_COMM_MB_CTL__M 0x1
+#define IQM_RC_COMM_MB_CTL__PRE 0x0
+#define IQM_RC_COMM_MB_CTL_CTL_OFF 0x0
+#define IQM_RC_COMM_MB_CTL_CTL_ON 0x1
+#define IQM_RC_COMM_MB_OBS__B 1
+#define IQM_RC_COMM_MB_OBS__W 1
+#define IQM_RC_COMM_MB_OBS__M 0x2
+#define IQM_RC_COMM_MB_OBS__PRE 0x0
+#define IQM_RC_COMM_MB_OBS_OBS_OFF 0x0
+#define IQM_RC_COMM_MB_OBS_OBS_ON 0x2
+
+#define IQM_RC_RATE_OFS_LO__A 0x1840010
+#define IQM_RC_RATE_OFS_LO__W 16
+#define IQM_RC_RATE_OFS_LO__M 0xFFFF
+#define IQM_RC_RATE_OFS_LO__PRE 0x0
+#define IQM_RC_RATE_OFS_HI__A 0x1840011
+#define IQM_RC_RATE_OFS_HI__W 8
+#define IQM_RC_RATE_OFS_HI__M 0xFF
+#define IQM_RC_RATE_OFS_HI__PRE 0x0
+#define IQM_RC_RATE_LO__A 0x1840012
+#define IQM_RC_RATE_LO__W 16
+#define IQM_RC_RATE_LO__M 0xFFFF
+#define IQM_RC_RATE_LO__PRE 0x0
+#define IQM_RC_RATE_HI__A 0x1840013
+#define IQM_RC_RATE_HI__W 8
+#define IQM_RC_RATE_HI__M 0xFF
+#define IQM_RC_RATE_HI__PRE 0x0
+
+#define IQM_RC_ADJ_SEL__A 0x1840014
+#define IQM_RC_ADJ_SEL__W 2
+#define IQM_RC_ADJ_SEL__M 0x3
+#define IQM_RC_ADJ_SEL__PRE 0x0
+#define IQM_RC_ADJ_SEL_OFF 0x0
+#define IQM_RC_ADJ_SEL_QAM 0x1
+#define IQM_RC_ADJ_SEL_VSB 0x2
+
+#define IQM_RC_CROUT_ENA__A 0x1840015
+#define IQM_RC_CROUT_ENA__W 1
+#define IQM_RC_CROUT_ENA__M 0x1
+#define IQM_RC_CROUT_ENA__PRE 0x0
+
+#define IQM_RC_CROUT_ENA_ENA__B 0
+#define IQM_RC_CROUT_ENA_ENA__W 1
+#define IQM_RC_CROUT_ENA_ENA__M 0x1
+#define IQM_RC_CROUT_ENA_ENA__PRE 0x0
+
+#define IQM_RC_STRETCH__A 0x1840016
+#define IQM_RC_STRETCH__W 5
+#define IQM_RC_STRETCH__M 0x1F
+#define IQM_RC_STRETCH__PRE 0x0
+#define IQM_RC_STRETCH_QAM_B_64 0x1E
+#define IQM_RC_STRETCH_QAM_B_256 0x1C
+#define IQM_RC_STRETCH_ATV 0xF
+
+#define IQM_RT_COMM_EXEC__A 0x1850000
+#define IQM_RT_COMM_EXEC__W 2
+#define IQM_RT_COMM_EXEC__M 0x3
+#define IQM_RT_COMM_EXEC__PRE 0x0
+#define IQM_RT_COMM_EXEC_STOP 0x0
+#define IQM_RT_COMM_EXEC_ACTIVE 0x1
+#define IQM_RT_COMM_EXEC_HOLD 0x2
+
+#define IQM_RT_COMM_MB__A 0x1850002
+#define IQM_RT_COMM_MB__W 2
+#define IQM_RT_COMM_MB__M 0x3
+#define IQM_RT_COMM_MB__PRE 0x0
+#define IQM_RT_COMM_MB_CTL__B 0
+#define IQM_RT_COMM_MB_CTL__W 1
+#define IQM_RT_COMM_MB_CTL__M 0x1
+#define IQM_RT_COMM_MB_CTL__PRE 0x0
+#define IQM_RT_COMM_MB_CTL_CTL_OFF 0x0
+#define IQM_RT_COMM_MB_CTL_CTL_ON 0x1
+#define IQM_RT_COMM_MB_OBS__B 1
+#define IQM_RT_COMM_MB_OBS__W 1
+#define IQM_RT_COMM_MB_OBS__M 0x2
+#define IQM_RT_COMM_MB_OBS__PRE 0x0
+#define IQM_RT_COMM_MB_OBS_OBS_OFF 0x0
+#define IQM_RT_COMM_MB_OBS_OBS_ON 0x2
+
+#define IQM_RT_ACTIVE__A 0x1850010
+#define IQM_RT_ACTIVE__W 2
+#define IQM_RT_ACTIVE__M 0x3
+#define IQM_RT_ACTIVE__PRE 0x0
+
+#define IQM_RT_ACTIVE_ACTIVE_RT__B 0
+#define IQM_RT_ACTIVE_ACTIVE_RT__W 1
+#define IQM_RT_ACTIVE_ACTIVE_RT__M 0x1
+#define IQM_RT_ACTIVE_ACTIVE_RT__PRE 0x0
+#define IQM_RT_ACTIVE_ACTIVE_RT_ATV_FCR_OFF 0x0
+#define IQM_RT_ACTIVE_ACTIVE_RT_ATV_FCR_ON 0x1
+
+#define IQM_RT_ACTIVE_ACTIVE_CR__B 1
+#define IQM_RT_ACTIVE_ACTIVE_CR__W 1
+#define IQM_RT_ACTIVE_ACTIVE_CR__M 0x2
+#define IQM_RT_ACTIVE_ACTIVE_CR__PRE 0x0
+#define IQM_RT_ACTIVE_ACTIVE_CR_ATV_CR_OFF 0x0
+#define IQM_RT_ACTIVE_ACTIVE_CR_ATV_CR_ON 0x2
+
+#define IQM_RT_LO_INCR__A 0x1850011
+#define IQM_RT_LO_INCR__W 12
+#define IQM_RT_LO_INCR__M 0xFFF
+#define IQM_RT_LO_INCR__PRE 0x588
+#define IQM_RT_LO_INCR_FM 0x0
+#define IQM_RT_LO_INCR_MN 0x588
+
+#define IQM_RT_ROT_BP__A 0x1850012
+#define IQM_RT_ROT_BP__W 2
+#define IQM_RT_ROT_BP__M 0x3
+#define IQM_RT_ROT_BP__PRE 0x0
+
+#define IQM_RT_ROT_BP_ROT_OFF__B 0
+#define IQM_RT_ROT_BP_ROT_OFF__W 1
+#define IQM_RT_ROT_BP_ROT_OFF__M 0x1
+#define IQM_RT_ROT_BP_ROT_OFF__PRE 0x0
+#define IQM_RT_ROT_BP_ROT_OFF_ACTIVE 0x0
+#define IQM_RT_ROT_BP_ROT_OFF_OFF 0x1
+
+#define IQM_RT_ROT_BP_ROT_BPF__B 1
+#define IQM_RT_ROT_BP_ROT_BPF__W 1
+#define IQM_RT_ROT_BP_ROT_BPF__M 0x2
+#define IQM_RT_ROT_BP_ROT_BPF__PRE 0x0
+
+#define IQM_RT_LP_BP__A 0x1850013
+#define IQM_RT_LP_BP__W 1
+#define IQM_RT_LP_BP__M 0x1
+#define IQM_RT_LP_BP__PRE 0x0
+
+#define IQM_RT_DELAY__A 0x1850014
+#define IQM_RT_DELAY__W 7
+#define IQM_RT_DELAY__M 0x7F
+#define IQM_RT_DELAY__PRE 0x45
+
+#define IQM_CF_COMM_EXEC__A 0x1860000
+#define IQM_CF_COMM_EXEC__W 2
+#define IQM_CF_COMM_EXEC__M 0x3
+#define IQM_CF_COMM_EXEC__PRE 0x0
+#define IQM_CF_COMM_EXEC_STOP 0x0
+#define IQM_CF_COMM_EXEC_ACTIVE 0x1
+#define IQM_CF_COMM_EXEC_HOLD 0x2
+
+#define IQM_CF_COMM_MB__A 0x1860002
+#define IQM_CF_COMM_MB__W 2
+#define IQM_CF_COMM_MB__M 0x3
+#define IQM_CF_COMM_MB__PRE 0x0
+#define IQM_CF_COMM_MB_CTL__B 0
+#define IQM_CF_COMM_MB_CTL__W 1
+#define IQM_CF_COMM_MB_CTL__M 0x1
+#define IQM_CF_COMM_MB_CTL__PRE 0x0
+#define IQM_CF_COMM_MB_CTL_CTL_OFF 0x0
+#define IQM_CF_COMM_MB_CTL_CTL_ON 0x1
+#define IQM_CF_COMM_MB_OBS__B 1
+#define IQM_CF_COMM_MB_OBS__W 1
+#define IQM_CF_COMM_MB_OBS__M 0x2
+#define IQM_CF_COMM_MB_OBS__PRE 0x0
+#define IQM_CF_COMM_MB_OBS_OBS_OFF 0x0
+#define IQM_CF_COMM_MB_OBS_OBS_ON 0x2
+
+#define IQM_CF_COMM_INT_REQ__A 0x1860003
+#define IQM_CF_COMM_INT_REQ__W 1
+#define IQM_CF_COMM_INT_REQ__M 0x1
+#define IQM_CF_COMM_INT_REQ__PRE 0x0
+#define IQM_CF_COMM_INT_STA__A 0x1860005
+#define IQM_CF_COMM_INT_STA__W 1
+#define IQM_CF_COMM_INT_STA__M 0x1
+#define IQM_CF_COMM_INT_STA__PRE 0x0
+#define IQM_CF_COMM_INT_STA_PM__B 0
+#define IQM_CF_COMM_INT_STA_PM__W 1
+#define IQM_CF_COMM_INT_STA_PM__M 0x1
+#define IQM_CF_COMM_INT_STA_PM__PRE 0x0
+
+#define IQM_CF_COMM_INT_MSK__A 0x1860006
+#define IQM_CF_COMM_INT_MSK__W 1
+#define IQM_CF_COMM_INT_MSK__M 0x1
+#define IQM_CF_COMM_INT_MSK__PRE 0x0
+#define IQM_CF_COMM_INT_MSK_PM__B 0
+#define IQM_CF_COMM_INT_MSK_PM__W 1
+#define IQM_CF_COMM_INT_MSK_PM__M 0x1
+#define IQM_CF_COMM_INT_MSK_PM__PRE 0x0
+
+#define IQM_CF_COMM_INT_STM__A 0x1860007
+#define IQM_CF_COMM_INT_STM__W 1
+#define IQM_CF_COMM_INT_STM__M 0x1
+#define IQM_CF_COMM_INT_STM__PRE 0x0
+#define IQM_CF_COMM_INT_STM_PM__B 0
+#define IQM_CF_COMM_INT_STM_PM__W 1
+#define IQM_CF_COMM_INT_STM_PM__M 0x1
+#define IQM_CF_COMM_INT_STM_PM__PRE 0x0
+
+#define IQM_CF_SYMMETRIC__A 0x1860010
+#define IQM_CF_SYMMETRIC__W 2
+#define IQM_CF_SYMMETRIC__M 0x3
+#define IQM_CF_SYMMETRIC__PRE 0x0
+
+#define IQM_CF_SYMMETRIC_RE__B 0
+#define IQM_CF_SYMMETRIC_RE__W 1
+#define IQM_CF_SYMMETRIC_RE__M 0x1
+#define IQM_CF_SYMMETRIC_RE__PRE 0x0
+
+#define IQM_CF_SYMMETRIC_IM__B 1
+#define IQM_CF_SYMMETRIC_IM__W 1
+#define IQM_CF_SYMMETRIC_IM__M 0x2
+#define IQM_CF_SYMMETRIC_IM__PRE 0x0
+
+#define IQM_CF_MIDTAP__A 0x1860011
+#define IQM_CF_MIDTAP__W 2
+#define IQM_CF_MIDTAP__M 0x3
+#define IQM_CF_MIDTAP__PRE 0x3
+
+#define IQM_CF_MIDTAP_RE__B 0
+#define IQM_CF_MIDTAP_RE__W 1
+#define IQM_CF_MIDTAP_RE__M 0x1
+#define IQM_CF_MIDTAP_RE__PRE 0x1
+
+#define IQM_CF_MIDTAP_IM__B 1
+#define IQM_CF_MIDTAP_IM__W 1
+#define IQM_CF_MIDTAP_IM__M 0x2
+#define IQM_CF_MIDTAP_IM__PRE 0x2
+
+#define IQM_CF_OUT_ENA__A 0x1860012
+#define IQM_CF_OUT_ENA__W 3
+#define IQM_CF_OUT_ENA__M 0x7
+#define IQM_CF_OUT_ENA__PRE 0x0
+
+#define IQM_CF_OUT_ENA_ATV__B 0
+#define IQM_CF_OUT_ENA_ATV__W 1
+#define IQM_CF_OUT_ENA_ATV__M 0x1
+#define IQM_CF_OUT_ENA_ATV__PRE 0x0
+
+#define IQM_CF_OUT_ENA_QAM__B 1
+#define IQM_CF_OUT_ENA_QAM__W 1
+#define IQM_CF_OUT_ENA_QAM__M 0x2
+#define IQM_CF_OUT_ENA_QAM__PRE 0x0
+
+#define IQM_CF_OUT_ENA_VSB__B 2
+#define IQM_CF_OUT_ENA_VSB__W 1
+#define IQM_CF_OUT_ENA_VSB__M 0x4
+#define IQM_CF_OUT_ENA_VSB__PRE 0x0
+
+#define IQM_CF_ADJ_SEL__A 0x1860013
+#define IQM_CF_ADJ_SEL__W 2
+#define IQM_CF_ADJ_SEL__M 0x3
+#define IQM_CF_ADJ_SEL__PRE 0x0
+#define IQM_CF_SCALE__A 0x1860014
+#define IQM_CF_SCALE__W 14
+#define IQM_CF_SCALE__M 0x3FFF
+#define IQM_CF_SCALE__PRE 0x400
+
+#define IQM_CF_SCALE_SH__A 0x1860015
+#define IQM_CF_SCALE_SH__W 2
+#define IQM_CF_SCALE_SH__M 0x3
+#define IQM_CF_SCALE_SH__PRE 0x0
+
+#define IQM_CF_AMP__A 0x1860016
+#define IQM_CF_AMP__W 14
+#define IQM_CF_AMP__M 0x3FFF
+#define IQM_CF_AMP__PRE 0x0
+
+#define IQM_CF_POW_MEAS_LEN__A 0x1860017
+#define IQM_CF_POW_MEAS_LEN__W 3
+#define IQM_CF_POW_MEAS_LEN__M 0x7
+#define IQM_CF_POW_MEAS_LEN__PRE 0x2
+#define IQM_CF_POW_MEAS_LEN_QAM_B_64 0x1
+#define IQM_CF_POW_MEAS_LEN_QAM_B_256 0x1
+
+#define IQM_CF_POW__A 0x1860018
+#define IQM_CF_POW__W 16
+#define IQM_CF_POW__M 0xFFFF
+#define IQM_CF_POW__PRE 0x2
+#define IQM_CF_TAP_RE0__A 0x1860020
+#define IQM_CF_TAP_RE0__W 7
+#define IQM_CF_TAP_RE0__M 0x7F
+#define IQM_CF_TAP_RE0__PRE 0x2
+#define IQM_CF_TAP_RE1__A 0x1860021
+#define IQM_CF_TAP_RE1__W 7
+#define IQM_CF_TAP_RE1__M 0x7F
+#define IQM_CF_TAP_RE1__PRE 0x2
+#define IQM_CF_TAP_RE2__A 0x1860022
+#define IQM_CF_TAP_RE2__W 7
+#define IQM_CF_TAP_RE2__M 0x7F
+#define IQM_CF_TAP_RE2__PRE 0x2
+#define IQM_CF_TAP_RE3__A 0x1860023
+#define IQM_CF_TAP_RE3__W 7
+#define IQM_CF_TAP_RE3__M 0x7F
+#define IQM_CF_TAP_RE3__PRE 0x2
+#define IQM_CF_TAP_RE4__A 0x1860024
+#define IQM_CF_TAP_RE4__W 7
+#define IQM_CF_TAP_RE4__M 0x7F
+#define IQM_CF_TAP_RE4__PRE 0x2
+#define IQM_CF_TAP_RE5__A 0x1860025
+#define IQM_CF_TAP_RE5__W 7
+#define IQM_CF_TAP_RE5__M 0x7F
+#define IQM_CF_TAP_RE5__PRE 0x2
+#define IQM_CF_TAP_RE6__A 0x1860026
+#define IQM_CF_TAP_RE6__W 7
+#define IQM_CF_TAP_RE6__M 0x7F
+#define IQM_CF_TAP_RE6__PRE 0x2
+#define IQM_CF_TAP_RE7__A 0x1860027
+#define IQM_CF_TAP_RE7__W 9
+#define IQM_CF_TAP_RE7__M 0x1FF
+#define IQM_CF_TAP_RE7__PRE 0x2
+#define IQM_CF_TAP_RE8__A 0x1860028
+#define IQM_CF_TAP_RE8__W 9
+#define IQM_CF_TAP_RE8__M 0x1FF
+#define IQM_CF_TAP_RE8__PRE 0x2
+#define IQM_CF_TAP_RE9__A 0x1860029
+#define IQM_CF_TAP_RE9__W 9
+#define IQM_CF_TAP_RE9__M 0x1FF
+#define IQM_CF_TAP_RE9__PRE 0x2
+#define IQM_CF_TAP_RE10__A 0x186002A
+#define IQM_CF_TAP_RE10__W 9
+#define IQM_CF_TAP_RE10__M 0x1FF
+#define IQM_CF_TAP_RE10__PRE 0x2
+#define IQM_CF_TAP_RE11__A 0x186002B
+#define IQM_CF_TAP_RE11__W 9
+#define IQM_CF_TAP_RE11__M 0x1FF
+#define IQM_CF_TAP_RE11__PRE 0x2
+#define IQM_CF_TAP_RE12__A 0x186002C
+#define IQM_CF_TAP_RE12__W 9
+#define IQM_CF_TAP_RE12__M 0x1FF
+#define IQM_CF_TAP_RE12__PRE 0x2
+#define IQM_CF_TAP_RE13__A 0x186002D
+#define IQM_CF_TAP_RE13__W 9
+#define IQM_CF_TAP_RE13__M 0x1FF
+#define IQM_CF_TAP_RE13__PRE 0x2
+#define IQM_CF_TAP_RE14__A 0x186002E
+#define IQM_CF_TAP_RE14__W 9
+#define IQM_CF_TAP_RE14__M 0x1FF
+#define IQM_CF_TAP_RE14__PRE 0x2
+#define IQM_CF_TAP_RE15__A 0x186002F
+#define IQM_CF_TAP_RE15__W 9
+#define IQM_CF_TAP_RE15__M 0x1FF
+#define IQM_CF_TAP_RE15__PRE 0x2
+#define IQM_CF_TAP_RE16__A 0x1860030
+#define IQM_CF_TAP_RE16__W 9
+#define IQM_CF_TAP_RE16__M 0x1FF
+#define IQM_CF_TAP_RE16__PRE 0x2
+#define IQM_CF_TAP_RE17__A 0x1860031
+#define IQM_CF_TAP_RE17__W 9
+#define IQM_CF_TAP_RE17__M 0x1FF
+#define IQM_CF_TAP_RE17__PRE 0x2
+#define IQM_CF_TAP_RE18__A 0x1860032
+#define IQM_CF_TAP_RE18__W 9
+#define IQM_CF_TAP_RE18__M 0x1FF
+#define IQM_CF_TAP_RE18__PRE 0x2
+#define IQM_CF_TAP_RE19__A 0x1860033
+#define IQM_CF_TAP_RE19__W 9
+#define IQM_CF_TAP_RE19__M 0x1FF
+#define IQM_CF_TAP_RE19__PRE 0x2
+#define IQM_CF_TAP_RE20__A 0x1860034
+#define IQM_CF_TAP_RE20__W 9
+#define IQM_CF_TAP_RE20__M 0x1FF
+#define IQM_CF_TAP_RE20__PRE 0x2
+#define IQM_CF_TAP_RE21__A 0x1860035
+#define IQM_CF_TAP_RE21__W 11
+#define IQM_CF_TAP_RE21__M 0x7FF
+#define IQM_CF_TAP_RE21__PRE 0x2
+#define IQM_CF_TAP_RE22__A 0x1860036
+#define IQM_CF_TAP_RE22__W 11
+#define IQM_CF_TAP_RE22__M 0x7FF
+#define IQM_CF_TAP_RE22__PRE 0x2
+#define IQM_CF_TAP_RE23__A 0x1860037
+#define IQM_CF_TAP_RE23__W 11
+#define IQM_CF_TAP_RE23__M 0x7FF
+#define IQM_CF_TAP_RE23__PRE 0x2
+#define IQM_CF_TAP_RE24__A 0x1860038
+#define IQM_CF_TAP_RE24__W 11
+#define IQM_CF_TAP_RE24__M 0x7FF
+#define IQM_CF_TAP_RE24__PRE 0x2
+#define IQM_CF_TAP_RE25__A 0x1860039
+#define IQM_CF_TAP_RE25__W 11
+#define IQM_CF_TAP_RE25__M 0x7FF
+#define IQM_CF_TAP_RE25__PRE 0x2
+#define IQM_CF_TAP_RE26__A 0x186003A
+#define IQM_CF_TAP_RE26__W 11
+#define IQM_CF_TAP_RE26__M 0x7FF
+#define IQM_CF_TAP_RE26__PRE 0x2
+#define IQM_CF_TAP_RE27__A 0x186003B
+#define IQM_CF_TAP_RE27__W 11
+#define IQM_CF_TAP_RE27__M 0x7FF
+#define IQM_CF_TAP_RE27__PRE 0x2
+#define IQM_CF_TAP_IM0__A 0x1860040
+#define IQM_CF_TAP_IM0__W 7
+#define IQM_CF_TAP_IM0__M 0x7F
+#define IQM_CF_TAP_IM0__PRE 0x2
+#define IQM_CF_TAP_IM1__A 0x1860041
+#define IQM_CF_TAP_IM1__W 7
+#define IQM_CF_TAP_IM1__M 0x7F
+#define IQM_CF_TAP_IM1__PRE 0x2
+#define IQM_CF_TAP_IM2__A 0x1860042
+#define IQM_CF_TAP_IM2__W 7
+#define IQM_CF_TAP_IM2__M 0x7F
+#define IQM_CF_TAP_IM2__PRE 0x2
+#define IQM_CF_TAP_IM3__A 0x1860043
+#define IQM_CF_TAP_IM3__W 7
+#define IQM_CF_TAP_IM3__M 0x7F
+#define IQM_CF_TAP_IM3__PRE 0x2
+#define IQM_CF_TAP_IM4__A 0x1860044
+#define IQM_CF_TAP_IM4__W 7
+#define IQM_CF_TAP_IM4__M 0x7F
+#define IQM_CF_TAP_IM4__PRE 0x2
+#define IQM_CF_TAP_IM5__A 0x1860045
+#define IQM_CF_TAP_IM5__W 7
+#define IQM_CF_TAP_IM5__M 0x7F
+#define IQM_CF_TAP_IM5__PRE 0x2
+#define IQM_CF_TAP_IM6__A 0x1860046
+#define IQM_CF_TAP_IM6__W 7
+#define IQM_CF_TAP_IM6__M 0x7F
+#define IQM_CF_TAP_IM6__PRE 0x2
+#define IQM_CF_TAP_IM7__A 0x1860047
+#define IQM_CF_TAP_IM7__W 9
+#define IQM_CF_TAP_IM7__M 0x1FF
+#define IQM_CF_TAP_IM7__PRE 0x2
+#define IQM_CF_TAP_IM8__A 0x1860048
+#define IQM_CF_TAP_IM8__W 9
+#define IQM_CF_TAP_IM8__M 0x1FF
+#define IQM_CF_TAP_IM8__PRE 0x2
+#define IQM_CF_TAP_IM9__A 0x1860049
+#define IQM_CF_TAP_IM9__W 9
+#define IQM_CF_TAP_IM9__M 0x1FF
+#define IQM_CF_TAP_IM9__PRE 0x2
+#define IQM_CF_TAP_IM10__A 0x186004A
+#define IQM_CF_TAP_IM10__W 9
+#define IQM_CF_TAP_IM10__M 0x1FF
+#define IQM_CF_TAP_IM10__PRE 0x2
+#define IQM_CF_TAP_IM11__A 0x186004B
+#define IQM_CF_TAP_IM11__W 9
+#define IQM_CF_TAP_IM11__M 0x1FF
+#define IQM_CF_TAP_IM11__PRE 0x2
+#define IQM_CF_TAP_IM12__A 0x186004C
+#define IQM_CF_TAP_IM12__W 9
+#define IQM_CF_TAP_IM12__M 0x1FF
+#define IQM_CF_TAP_IM12__PRE 0x2
+#define IQM_CF_TAP_IM13__A 0x186004D
+#define IQM_CF_TAP_IM13__W 9
+#define IQM_CF_TAP_IM13__M 0x1FF
+#define IQM_CF_TAP_IM13__PRE 0x2
+#define IQM_CF_TAP_IM14__A 0x186004E
+#define IQM_CF_TAP_IM14__W 9
+#define IQM_CF_TAP_IM14__M 0x1FF
+#define IQM_CF_TAP_IM14__PRE 0x2
+#define IQM_CF_TAP_IM15__A 0x186004F
+#define IQM_CF_TAP_IM15__W 9
+#define IQM_CF_TAP_IM15__M 0x1FF
+#define IQM_CF_TAP_IM15__PRE 0x2
+#define IQM_CF_TAP_IM16__A 0x1860050
+#define IQM_CF_TAP_IM16__W 9
+#define IQM_CF_TAP_IM16__M 0x1FF
+#define IQM_CF_TAP_IM16__PRE 0x2
+#define IQM_CF_TAP_IM17__A 0x1860051
+#define IQM_CF_TAP_IM17__W 9
+#define IQM_CF_TAP_IM17__M 0x1FF
+#define IQM_CF_TAP_IM17__PRE 0x2
+#define IQM_CF_TAP_IM18__A 0x1860052
+#define IQM_CF_TAP_IM18__W 9
+#define IQM_CF_TAP_IM18__M 0x1FF
+#define IQM_CF_TAP_IM18__PRE 0x2
+#define IQM_CF_TAP_IM19__A 0x1860053
+#define IQM_CF_TAP_IM19__W 9
+#define IQM_CF_TAP_IM19__M 0x1FF
+#define IQM_CF_TAP_IM19__PRE 0x2
+#define IQM_CF_TAP_IM20__A 0x1860054
+#define IQM_CF_TAP_IM20__W 9
+#define IQM_CF_TAP_IM20__M 0x1FF
+#define IQM_CF_TAP_IM20__PRE 0x2
+#define IQM_CF_TAP_IM21__A 0x1860055
+#define IQM_CF_TAP_IM21__W 11
+#define IQM_CF_TAP_IM21__M 0x7FF
+#define IQM_CF_TAP_IM21__PRE 0x2
+#define IQM_CF_TAP_IM22__A 0x1860056
+#define IQM_CF_TAP_IM22__W 11
+#define IQM_CF_TAP_IM22__M 0x7FF
+#define IQM_CF_TAP_IM22__PRE 0x2
+#define IQM_CF_TAP_IM23__A 0x1860057
+#define IQM_CF_TAP_IM23__W 11
+#define IQM_CF_TAP_IM23__M 0x7FF
+#define IQM_CF_TAP_IM23__PRE 0x2
+#define IQM_CF_TAP_IM24__A 0x1860058
+#define IQM_CF_TAP_IM24__W 11
+#define IQM_CF_TAP_IM24__M 0x7FF
+#define IQM_CF_TAP_IM24__PRE 0x2
+#define IQM_CF_TAP_IM25__A 0x1860059
+#define IQM_CF_TAP_IM25__W 11
+#define IQM_CF_TAP_IM25__M 0x7FF
+#define IQM_CF_TAP_IM25__PRE 0x2
+#define IQM_CF_TAP_IM26__A 0x186005A
+#define IQM_CF_TAP_IM26__W 11
+#define IQM_CF_TAP_IM26__M 0x7FF
+#define IQM_CF_TAP_IM26__PRE 0x2
+#define IQM_CF_TAP_IM27__A 0x186005B
+#define IQM_CF_TAP_IM27__W 11
+#define IQM_CF_TAP_IM27__M 0x7FF
+#define IQM_CF_TAP_IM27__PRE 0x2
+
+#define IQM_AF_COMM_EXEC__A 0x1870000
+#define IQM_AF_COMM_EXEC__W 2
+#define IQM_AF_COMM_EXEC__M 0x3
+#define IQM_AF_COMM_EXEC__PRE 0x0
+#define IQM_AF_COMM_EXEC_STOP 0x0
+#define IQM_AF_COMM_EXEC_ACTIVE 0x1
+#define IQM_AF_COMM_EXEC_HOLD 0x2
+
+#define IQM_AF_COMM_MB__A 0x1870002
+#define IQM_AF_COMM_MB__W 8
+#define IQM_AF_COMM_MB__M 0xFF
+#define IQM_AF_COMM_MB__PRE 0x0
+#define IQM_AF_COMM_MB_CTL__B 0
+#define IQM_AF_COMM_MB_CTL__W 1
+#define IQM_AF_COMM_MB_CTL__M 0x1
+#define IQM_AF_COMM_MB_CTL__PRE 0x0
+#define IQM_AF_COMM_MB_CTL_CTL_OFF 0x0
+#define IQM_AF_COMM_MB_CTL_CTL_ON 0x1
+#define IQM_AF_COMM_MB_OBS__B 1
+#define IQM_AF_COMM_MB_OBS__W 1
+#define IQM_AF_COMM_MB_OBS__M 0x2
+#define IQM_AF_COMM_MB_OBS__PRE 0x0
+#define IQM_AF_COMM_MB_OBS_OBS_OFF 0x0
+#define IQM_AF_COMM_MB_OBS_OBS_ON 0x2
+#define IQM_AF_COMM_MB_MUX_CTRL__B 2
+#define IQM_AF_COMM_MB_MUX_CTRL__W 3
+#define IQM_AF_COMM_MB_MUX_CTRL__M 0x1C
+#define IQM_AF_COMM_MB_MUX_CTRL__PRE 0x0
+#define IQM_AF_COMM_MB_MUX_CTRL_AF_DATA_INPUT 0x0
+#define IQM_AF_COMM_MB_MUX_CTRL_SENSE_INPUT 0x4
+#define IQM_AF_COMM_MB_MUX_CTRL_AF_DATA_OUTPUT 0x8
+#define IQM_AF_COMM_MB_MUX_CTRL_IF_AGC_OUTPUT 0xC
+#define IQM_AF_COMM_MB_MUX_CTRL_RF_AGC_OUTPUT 0x10
+#define IQM_AF_COMM_MB_MUX_OBS__B 5
+#define IQM_AF_COMM_MB_MUX_OBS__W 3
+#define IQM_AF_COMM_MB_MUX_OBS__M 0xE0
+#define IQM_AF_COMM_MB_MUX_OBS__PRE 0x0
+#define IQM_AF_COMM_MB_MUX_OBS_AF_DATA_INPUT 0x0
+#define IQM_AF_COMM_MB_MUX_OBS_SENSE_INPUT 0x20
+#define IQM_AF_COMM_MB_MUX_OBS_AF_DATA_OUTPUT 0x40
+#define IQM_AF_COMM_MB_MUX_OBS_IF_AGC_OUTPUT 0x60
+#define IQM_AF_COMM_MB_MUX_OBS_RF_AGC_OUTPUT 0x80
+
+#define IQM_AF_COMM_INT_REQ__A 0x1870003
+#define IQM_AF_COMM_INT_REQ__W 1
+#define IQM_AF_COMM_INT_REQ__M 0x1
+#define IQM_AF_COMM_INT_REQ__PRE 0x0
+#define IQM_AF_COMM_INT_STA__A 0x1870005
+#define IQM_AF_COMM_INT_STA__W 2
+#define IQM_AF_COMM_INT_STA__M 0x3
+#define IQM_AF_COMM_INT_STA__PRE 0x0
+#define IQM_AF_COMM_INT_STA_CLP_INT_STA__B 0
+#define IQM_AF_COMM_INT_STA_CLP_INT_STA__W 1
+#define IQM_AF_COMM_INT_STA_CLP_INT_STA__M 0x1
+#define IQM_AF_COMM_INT_STA_CLP_INT_STA__PRE 0x0
+#define IQM_AF_COMM_INT_STA_SNS_INT_STA__B 1
+#define IQM_AF_COMM_INT_STA_SNS_INT_STA__W 1
+#define IQM_AF_COMM_INT_STA_SNS_INT_STA__M 0x2
+#define IQM_AF_COMM_INT_STA_SNS_INT_STA__PRE 0x0
+
+#define IQM_AF_COMM_INT_MSK__A 0x1870006
+#define IQM_AF_COMM_INT_MSK__W 2
+#define IQM_AF_COMM_INT_MSK__M 0x3
+#define IQM_AF_COMM_INT_MSK__PRE 0x0
+#define IQM_AF_COMM_INT_MSK_CLP_INT_MSK__B 0
+#define IQM_AF_COMM_INT_MSK_CLP_INT_MSK__W 1
+#define IQM_AF_COMM_INT_MSK_CLP_INT_MSK__M 0x1
+#define IQM_AF_COMM_INT_MSK_CLP_INT_MSK__PRE 0x0
+#define IQM_AF_COMM_INT_MSK_SNS_INT_MSK__B 1
+#define IQM_AF_COMM_INT_MSK_SNS_INT_MSK__W 1
+#define IQM_AF_COMM_INT_MSK_SNS_INT_MSK__M 0x2
+#define IQM_AF_COMM_INT_MSK_SNS_INT_MSK__PRE 0x0
+
+#define IQM_AF_COMM_INT_STM__A 0x1870007
+#define IQM_AF_COMM_INT_STM__W 2
+#define IQM_AF_COMM_INT_STM__M 0x3
+#define IQM_AF_COMM_INT_STM__PRE 0x0
+#define IQM_AF_COMM_INT_STM_CLP_INT_STA__B 0
+#define IQM_AF_COMM_INT_STM_CLP_INT_STA__W 1
+#define IQM_AF_COMM_INT_STM_CLP_INT_STA__M 0x1
+#define IQM_AF_COMM_INT_STM_CLP_INT_STA__PRE 0x0
+#define IQM_AF_COMM_INT_STM_SNS_INT_STA__B 1
+#define IQM_AF_COMM_INT_STM_SNS_INT_STA__W 1
+#define IQM_AF_COMM_INT_STM_SNS_INT_STA__M 0x2
+#define IQM_AF_COMM_INT_STM_SNS_INT_STA__PRE 0x0
+
+#define IQM_AF_FDB_SEL__A 0x1870010
+#define IQM_AF_FDB_SEL__W 1
+#define IQM_AF_FDB_SEL__M 0x1
+#define IQM_AF_FDB_SEL__PRE 0x0
+
+#define IQM_AF_INVEXT__A 0x1870011
+#define IQM_AF_INVEXT__W 1
+#define IQM_AF_INVEXT__M 0x1
+#define IQM_AF_INVEXT__PRE 0x0
+#define IQM_AF_CLKNEG__A 0x1870012
+#define IQM_AF_CLKNEG__W 2
+#define IQM_AF_CLKNEG__M 0x3
+#define IQM_AF_CLKNEG__PRE 0x0
+
+#define IQM_AF_CLKNEG_CLKNEGPEAK__B 0
+#define IQM_AF_CLKNEG_CLKNEGPEAK__W 1
+#define IQM_AF_CLKNEG_CLKNEGPEAK__M 0x1
+#define IQM_AF_CLKNEG_CLKNEGPEAK__PRE 0x0
+#define IQM_AF_CLKNEG_CLKNEGPEAK_CLK_ADC_PEAK_POS 0x0
+#define IQM_AF_CLKNEG_CLKNEGPEAK_CLK_ADC_PEAK_NEG 0x1
+
+#define IQM_AF_CLKNEG_CLKNEGDATA__B 1
+#define IQM_AF_CLKNEG_CLKNEGDATA__W 1
+#define IQM_AF_CLKNEG_CLKNEGDATA__M 0x2
+#define IQM_AF_CLKNEG_CLKNEGDATA__PRE 0x0
+#define IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS 0x0
+#define IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG 0x2
+
+#define IQM_AF_MON_IN_MUX__A 0x1870013
+#define IQM_AF_MON_IN_MUX__W 2
+#define IQM_AF_MON_IN_MUX__M 0x3
+#define IQM_AF_MON_IN_MUX__PRE 0x0
+
+#define IQM_AF_MON_IN5__A 0x1870014
+#define IQM_AF_MON_IN5__W 10
+#define IQM_AF_MON_IN5__M 0x3FF
+#define IQM_AF_MON_IN5__PRE 0x0
+
+#define IQM_AF_MON_IN4__A 0x1870015
+#define IQM_AF_MON_IN4__W 10
+#define IQM_AF_MON_IN4__M 0x3FF
+#define IQM_AF_MON_IN4__PRE 0x0
+
+#define IQM_AF_MON_IN3__A 0x1870016
+#define IQM_AF_MON_IN3__W 10
+#define IQM_AF_MON_IN3__M 0x3FF
+#define IQM_AF_MON_IN3__PRE 0x0
+
+#define IQM_AF_MON_IN2__A 0x1870017
+#define IQM_AF_MON_IN2__W 10
+#define IQM_AF_MON_IN2__M 0x3FF
+#define IQM_AF_MON_IN2__PRE 0x0
+
+#define IQM_AF_MON_IN1__A 0x1870018
+#define IQM_AF_MON_IN1__W 10
+#define IQM_AF_MON_IN1__M 0x3FF
+#define IQM_AF_MON_IN1__PRE 0x0
+
+#define IQM_AF_MON_IN0__A 0x1870019
+#define IQM_AF_MON_IN0__W 10
+#define IQM_AF_MON_IN0__M 0x3FF
+#define IQM_AF_MON_IN0__PRE 0x0
+
+#define IQM_AF_MON_IN_VAL__A 0x187001A
+#define IQM_AF_MON_IN_VAL__W 1
+#define IQM_AF_MON_IN_VAL__M 0x1
+#define IQM_AF_MON_IN_VAL__PRE 0x0
+
+#define IQM_AF_START_LOCK__A 0x187001B
+#define IQM_AF_START_LOCK__W 1
+#define IQM_AF_START_LOCK__M 0x1
+#define IQM_AF_START_LOCK__PRE 0x0
+
+#define IQM_AF_PHASE0__A 0x187001C
+#define IQM_AF_PHASE0__W 7
+#define IQM_AF_PHASE0__M 0x7F
+#define IQM_AF_PHASE0__PRE 0x0
+
+#define IQM_AF_PHASE1__A 0x187001D
+#define IQM_AF_PHASE1__W 7
+#define IQM_AF_PHASE1__M 0x7F
+#define IQM_AF_PHASE1__PRE 0x0
+
+#define IQM_AF_PHASE2__A 0x187001E
+#define IQM_AF_PHASE2__W 7
+#define IQM_AF_PHASE2__M 0x7F
+#define IQM_AF_PHASE2__PRE 0x0
+
+#define IQM_AF_SCU_PHASE__A 0x187001F
+#define IQM_AF_SCU_PHASE__W 2
+#define IQM_AF_SCU_PHASE__M 0x3
+#define IQM_AF_SCU_PHASE__PRE 0x0
+
+#define IQM_AF_SYNC_SEL__A 0x1870020
+#define IQM_AF_SYNC_SEL__W 2
+#define IQM_AF_SYNC_SEL__M 0x3
+#define IQM_AF_SYNC_SEL__PRE 0x0
+#define IQM_AF_ADC_CONF__A 0x1870021
+#define IQM_AF_ADC_CONF__W 4
+#define IQM_AF_ADC_CONF__M 0xF
+#define IQM_AF_ADC_CONF__PRE 0x0
+
+#define IQM_AF_ADC_CONF_ADC_SIGN__B 0
+#define IQM_AF_ADC_CONF_ADC_SIGN__W 1
+#define IQM_AF_ADC_CONF_ADC_SIGN__M 0x1
+#define IQM_AF_ADC_CONF_ADC_SIGN__PRE 0x0
+#define IQM_AF_ADC_CONF_ADC_SIGN_ADC_SIGNED 0x0
+#define IQM_AF_ADC_CONF_ADC_SIGN_ADC_UNSIGNED 0x1
+
+#define IQM_AF_ADC_CONF_BITREVERSE_ADC__B 1
+#define IQM_AF_ADC_CONF_BITREVERSE_ADC__W 1
+#define IQM_AF_ADC_CONF_BITREVERSE_ADC__M 0x2
+#define IQM_AF_ADC_CONF_BITREVERSE_ADC__PRE 0x0
+#define IQM_AF_ADC_CONF_BITREVERSE_ADC_ADC_NORMAL 0x0
+#define IQM_AF_ADC_CONF_BITREVERSE_ADC_ADC_BITREVERSED 0x2
+
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSI__B 2
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSI__W 1
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSI__M 0x4
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSI__PRE 0x0
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSI_IFAGC_DAC_NORMAL 0x0
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSI_IFAGC_DAC_BITREVERSED 0x4
+
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSR__B 3
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSR__W 1
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSR__M 0x8
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSR__PRE 0x0
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSR_RFAGC_DAC_NORMAL 0x0
+#define IQM_AF_ADC_CONF_BITREVERSE_NSSR_RFAGC_DAC_BITREVERSED 0x8
+
+#define IQM_AF_CLP_CLIP__A 0x1870022
+#define IQM_AF_CLP_CLIP__W 16
+#define IQM_AF_CLP_CLIP__M 0xFFFF
+#define IQM_AF_CLP_CLIP__PRE 0x0
+
+#define IQM_AF_CLP_LEN__A 0x1870023
+#define IQM_AF_CLP_LEN__W 16
+#define IQM_AF_CLP_LEN__M 0xFFFF
+#define IQM_AF_CLP_LEN__PRE 0x0
+#define IQM_AF_CLP_LEN_QAM_B_64 0x400
+#define IQM_AF_CLP_LEN_QAM_B_256 0x400
+#define IQM_AF_CLP_LEN_ATV 0x0
+
+#define IQM_AF_CLP_TH__A 0x1870024
+#define IQM_AF_CLP_TH__W 9
+#define IQM_AF_CLP_TH__M 0x1FF
+#define IQM_AF_CLP_TH__PRE 0x0
+#define IQM_AF_CLP_TH_QAM_B_64 0x80
+#define IQM_AF_CLP_TH_QAM_B_256 0x80
+#define IQM_AF_CLP_TH_ATV 0x1C0
+
+#define IQM_AF_DCF_BYPASS__A 0x1870025
+#define IQM_AF_DCF_BYPASS__W 1
+#define IQM_AF_DCF_BYPASS__M 0x1
+#define IQM_AF_DCF_BYPASS__PRE 0x0
+#define IQM_AF_DCF_BYPASS_ACTIVE 0x0
+#define IQM_AF_DCF_BYPASS_BYPASS 0x1
+
+#define IQM_AF_SNS_LEN__A 0x1870026
+#define IQM_AF_SNS_LEN__W 16
+#define IQM_AF_SNS_LEN__M 0xFFFF
+#define IQM_AF_SNS_LEN__PRE 0x0
+#define IQM_AF_SNS_LEN_QAM_B_64 0x400
+#define IQM_AF_SNS_LEN_QAM_B_256 0x400
+#define IQM_AF_SNS_LEN_ATV 0x0
+
+#define IQM_AF_SNS_SENSE__A 0x1870027
+#define IQM_AF_SNS_SENSE__W 16
+#define IQM_AF_SNS_SENSE__M 0xFFFF
+#define IQM_AF_SNS_SENSE__PRE 0x0
+
+#define IQM_AF_AGC_IF__A 0x1870028
+#define IQM_AF_AGC_IF__W 15
+#define IQM_AF_AGC_IF__M 0x7FFF
+#define IQM_AF_AGC_IF__PRE 0x0
+
+#define IQM_AF_AGC_RF__A 0x1870029
+#define IQM_AF_AGC_RF__W 15
+#define IQM_AF_AGC_RF__M 0x7FFF
+#define IQM_AF_AGC_RF__PRE 0x0
+
+#define IQM_AF_PGA_GAIN__A 0x187002A
+#define IQM_AF_PGA_GAIN__W 4
+#define IQM_AF_PGA_GAIN__M 0xF
+#define IQM_AF_PGA_GAIN__PRE 0x0
+
+#define IQM_AF_PDREF__A 0x187002B
+#define IQM_AF_PDREF__W 5
+#define IQM_AF_PDREF__M 0x1F
+#define IQM_AF_PDREF__PRE 0x0
+#define IQM_AF_PDREF_QAM_B_64 0xF
+#define IQM_AF_PDREF_QAM_B_256 0xF
+#define IQM_AF_PDREF_ATV 0xF
+
+#define IQM_AF_STDBY__A 0x187002C
+#define IQM_AF_STDBY__W 6
+#define IQM_AF_STDBY__M 0x3F
+#define IQM_AF_STDBY__PRE 0x0
+
+#define IQM_AF_STDBY_STDBY_BIAS__B 0
+#define IQM_AF_STDBY_STDBY_BIAS__W 1
+#define IQM_AF_STDBY_STDBY_BIAS__M 0x1
+#define IQM_AF_STDBY_STDBY_BIAS__PRE 0x0
+#define IQM_AF_STDBY_STDBY_BIAS_ACTIVE 0x0
+#define IQM_AF_STDBY_STDBY_BIAS_STANDBY 0x1
+
+#define IQM_AF_STDBY_STDBY_ADC__B 1
+#define IQM_AF_STDBY_STDBY_ADC__W 1
+#define IQM_AF_STDBY_STDBY_ADC__M 0x2
+#define IQM_AF_STDBY_STDBY_ADC__PRE 0x0
+#define IQM_AF_STDBY_STDBY_ADC_A1_ACTIVE 0x0
+#define IQM_AF_STDBY_STDBY_ADC_A1_STANDBY 0x2
+#define IQM_AF_STDBY_STDBY_ADC_A2_ACTIVE 0x2
+#define IQM_AF_STDBY_STDBY_ADC_A2_STANDBY 0x0
+
+#define IQM_AF_STDBY_STDBY_AMP__B 2
+#define IQM_AF_STDBY_STDBY_AMP__W 1
+#define IQM_AF_STDBY_STDBY_AMP__M 0x4
+#define IQM_AF_STDBY_STDBY_AMP__PRE 0x0
+#define IQM_AF_STDBY_STDBY_AMP_A1_ACTIVE 0x0
+#define IQM_AF_STDBY_STDBY_AMP_A1_STANDBY 0x4
+#define IQM_AF_STDBY_STDBY_AMP_A2_ACTIVE 0x4
+#define IQM_AF_STDBY_STDBY_AMP_A2_STANDBY 0x0
+
+#define IQM_AF_STDBY_STDBY_PD__B 3
+#define IQM_AF_STDBY_STDBY_PD__W 1
+#define IQM_AF_STDBY_STDBY_PD__M 0x8
+#define IQM_AF_STDBY_STDBY_PD__PRE 0x0
+#define IQM_AF_STDBY_STDBY_PD_A1_ACTIVE 0x0
+#define IQM_AF_STDBY_STDBY_PD_A1_STANDBY 0x8
+#define IQM_AF_STDBY_STDBY_PD_A2_ACTIVE 0x8
+#define IQM_AF_STDBY_STDBY_PD_A2_STANDBY 0x0
+
+#define IQM_AF_STDBY_STDBY_TAGC_IF__B 4
+#define IQM_AF_STDBY_STDBY_TAGC_IF__W 1
+#define IQM_AF_STDBY_STDBY_TAGC_IF__M 0x10
+#define IQM_AF_STDBY_STDBY_TAGC_IF__PRE 0x0
+#define IQM_AF_STDBY_STDBY_TAGC_IF_A1_ACTIVE 0x0
+#define IQM_AF_STDBY_STDBY_TAGC_IF_A1_STANDBY 0x10
+#define IQM_AF_STDBY_STDBY_TAGC_IF_A2_ACTIVE 0x10
+#define IQM_AF_STDBY_STDBY_TAGC_IF_A2_STANDBY 0x0
+
+#define IQM_AF_STDBY_STDBY_TAGC_RF__B 5
+#define IQM_AF_STDBY_STDBY_TAGC_RF__W 1
+#define IQM_AF_STDBY_STDBY_TAGC_RF__M 0x20
+#define IQM_AF_STDBY_STDBY_TAGC_RF__PRE 0x0
+#define IQM_AF_STDBY_STDBY_TAGC_RF_A1_ACTIVE 0x0
+#define IQM_AF_STDBY_STDBY_TAGC_RF_A1_STANDBY 0x20
+#define IQM_AF_STDBY_STDBY_TAGC_RF_A2_ACTIVE 0x20
+#define IQM_AF_STDBY_STDBY_TAGC_RF_A2_STANDBY 0x0
+
+#define IQM_AF_AMUX__A 0x187002D
+#define IQM_AF_AMUX__W 2
+#define IQM_AF_AMUX__M 0x3
+#define IQM_AF_AMUX__PRE 0x0
+
+#define IQM_AF_TST_AFEMAIN__A 0x187002E
+#define IQM_AF_TST_AFEMAIN__W 8
+#define IQM_AF_TST_AFEMAIN__M 0xFF
+#define IQM_AF_TST_AFEMAIN__PRE 0x0
+
+#define IQM_RT_RAM__A 0x1880000
+
+#define IQM_RT_RAM_DLY__B 0
+#define IQM_RT_RAM_DLY__W 13
+#define IQM_RT_RAM_DLY__M 0x1FFF
+#define IQM_RT_RAM_DLY__PRE 0x0
+
+#define ORX_COMM_EXEC__A 0x2000000
+#define ORX_COMM_EXEC__W 2
+#define ORX_COMM_EXEC__M 0x3
+#define ORX_COMM_EXEC__PRE 0x0
+#define ORX_COMM_EXEC_STOP 0x0
+#define ORX_COMM_EXEC_ACTIVE 0x1
+#define ORX_COMM_EXEC_HOLD 0x2
+
+#define ORX_COMM_STATE__A 0x2000001
+#define ORX_COMM_STATE__W 16
+#define ORX_COMM_STATE__M 0xFFFF
+#define ORX_COMM_STATE__PRE 0x0
+#define ORX_COMM_MB__A 0x2000002
+#define ORX_COMM_MB__W 16
+#define ORX_COMM_MB__M 0xFFFF
+#define ORX_COMM_MB__PRE 0x0
+#define ORX_COMM_INT_REQ__A 0x2000003
+#define ORX_COMM_INT_REQ__W 16
+#define ORX_COMM_INT_REQ__M 0xFFFF
+#define ORX_COMM_INT_REQ__PRE 0x0
+#define ORX_COMM_INT_REQ_EQU_REQ__B 0
+#define ORX_COMM_INT_REQ_EQU_REQ__W 1
+#define ORX_COMM_INT_REQ_EQU_REQ__M 0x1
+#define ORX_COMM_INT_REQ_EQU_REQ__PRE 0x0
+#define ORX_COMM_INT_REQ_DDC_REQ__B 1
+#define ORX_COMM_INT_REQ_DDC_REQ__W 1
+#define ORX_COMM_INT_REQ_DDC_REQ__M 0x2
+#define ORX_COMM_INT_REQ_DDC_REQ__PRE 0x0
+#define ORX_COMM_INT_REQ_FWP_REQ__B 2
+#define ORX_COMM_INT_REQ_FWP_REQ__W 1
+#define ORX_COMM_INT_REQ_FWP_REQ__M 0x4
+#define ORX_COMM_INT_REQ_FWP_REQ__PRE 0x0
+#define ORX_COMM_INT_REQ_CON_REQ__B 3
+#define ORX_COMM_INT_REQ_CON_REQ__W 1
+#define ORX_COMM_INT_REQ_CON_REQ__M 0x8
+#define ORX_COMM_INT_REQ_CON_REQ__PRE 0x0
+#define ORX_COMM_INT_REQ_NSU_REQ__B 4
+#define ORX_COMM_INT_REQ_NSU_REQ__W 1
+#define ORX_COMM_INT_REQ_NSU_REQ__M 0x10
+#define ORX_COMM_INT_REQ_NSU_REQ__PRE 0x0
+
+#define ORX_COMM_INT_STA__A 0x2000005
+#define ORX_COMM_INT_STA__W 16
+#define ORX_COMM_INT_STA__M 0xFFFF
+#define ORX_COMM_INT_STA__PRE 0x0
+#define ORX_COMM_INT_MSK__A 0x2000006
+#define ORX_COMM_INT_MSK__W 16
+#define ORX_COMM_INT_MSK__M 0xFFFF
+#define ORX_COMM_INT_MSK__PRE 0x0
+#define ORX_COMM_INT_STM__A 0x2000007
+#define ORX_COMM_INT_STM__W 16
+#define ORX_COMM_INT_STM__M 0xFFFF
+#define ORX_COMM_INT_STM__PRE 0x0
+
+#define ORX_TOP_COMM_EXEC__A 0x2010000
+#define ORX_TOP_COMM_EXEC__W 2
+#define ORX_TOP_COMM_EXEC__M 0x3
+#define ORX_TOP_COMM_EXEC__PRE 0x0
+#define ORX_TOP_COMM_EXEC_STOP 0x0
+#define ORX_TOP_COMM_EXEC_ACTIVE 0x1
+#define ORX_TOP_COMM_EXEC_HOLD 0x2
+
+#define ORX_TOP_COMM_KEY__A 0x201000F
+#define ORX_TOP_COMM_KEY__W 16
+#define ORX_TOP_COMM_KEY__M 0xFFFF
+#define ORX_TOP_COMM_KEY__PRE 0x0
+#define ORX_TOP_COMM_KEY_KEY 0xFABA
+
+#define ORX_TOP_MDE_W__A 0x2010010
+#define ORX_TOP_MDE_W__W 2
+#define ORX_TOP_MDE_W__M 0x3
+#define ORX_TOP_MDE_W__PRE 0x2
+#define ORX_TOP_MDE_W_RATE_1544KBPS 0x0
+#define ORX_TOP_MDE_W_RATE_3088KBPS 0x1
+#define ORX_TOP_MDE_W_RATE_2048KBPS_SQRT 0x2
+#define ORX_TOP_MDE_W_RATE_2048KBPS_RO 0x3
+
+#define ORX_TOP_AIF_CTRL_W__A 0x2010011
+#define ORX_TOP_AIF_CTRL_W__W 3
+#define ORX_TOP_AIF_CTRL_W__M 0x7
+#define ORX_TOP_AIF_CTRL_W__PRE 0x0
+#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE__B 0
+#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE__W 1
+#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE__M 0x1
+#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE__PRE 0x0
+#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE_ADC_SAMPL_ON_POS_CLK_EDGE 0x0
+#define ORX_TOP_AIF_CTRL_W_NEG_CLK_EDGE_ADC_SAMPL_ON_NEG_CLK_EDGE 0x1
+#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE__B 1
+#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE__W 1
+#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE__M 0x2
+#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE__PRE 0x0
+#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE_REGULAR_BIT_ORDER_ADC 0x0
+#define ORX_TOP_AIF_CTRL_W_BIT_REVERSE_REVERSAL_BIT_ORDER_ADC 0x2
+#define ORX_TOP_AIF_CTRL_W_INV_MSB__B 2
+#define ORX_TOP_AIF_CTRL_W_INV_MSB__W 1
+#define ORX_TOP_AIF_CTRL_W_INV_MSB__M 0x4
+#define ORX_TOP_AIF_CTRL_W_INV_MSB__PRE 0x0
+#define ORX_TOP_AIF_CTRL_W_INV_MSB_NO_MSB_INVERSION_ADC 0x0
+#define ORX_TOP_AIF_CTRL_W_INV_MSB_MSB_INVERSION_ADC 0x4
+
+#define ORX_FWP_COMM_EXEC__A 0x2020000
+#define ORX_FWP_COMM_EXEC__W 2
+#define ORX_FWP_COMM_EXEC__M 0x3
+#define ORX_FWP_COMM_EXEC__PRE 0x0
+#define ORX_FWP_COMM_EXEC_STOP 0x0
+#define ORX_FWP_COMM_EXEC_ACTIVE 0x1
+#define ORX_FWP_COMM_EXEC_HOLD 0x2
+
+#define ORX_FWP_COMM_MB__A 0x2020002
+#define ORX_FWP_COMM_MB__W 8
+#define ORX_FWP_COMM_MB__M 0xFF
+#define ORX_FWP_COMM_MB__PRE 0x0
+#define ORX_FWP_COMM_MB_CTL__B 0
+#define ORX_FWP_COMM_MB_CTL__W 1
+#define ORX_FWP_COMM_MB_CTL__M 0x1
+#define ORX_FWP_COMM_MB_CTL__PRE 0x0
+#define ORX_FWP_COMM_MB_CTL_OFF 0x0
+#define ORX_FWP_COMM_MB_CTL_ON 0x1
+#define ORX_FWP_COMM_MB_OBS__B 1
+#define ORX_FWP_COMM_MB_OBS__W 1
+#define ORX_FWP_COMM_MB_OBS__M 0x2
+#define ORX_FWP_COMM_MB_OBS__PRE 0x0
+#define ORX_FWP_COMM_MB_OBS_OFF 0x0
+#define ORX_FWP_COMM_MB_OBS_ON 0x2
+
+#define ORX_FWP_COMM_MB_CTL_MUX__B 2
+#define ORX_FWP_COMM_MB_CTL_MUX__W 3
+#define ORX_FWP_COMM_MB_CTL_MUX__M 0x1C
+#define ORX_FWP_COMM_MB_CTL_MUX__PRE 0x0
+
+#define ORX_FWP_COMM_MB_OBS_MUX__B 5
+#define ORX_FWP_COMM_MB_OBS_MUX__W 3
+#define ORX_FWP_COMM_MB_OBS_MUX__M 0xE0
+#define ORX_FWP_COMM_MB_OBS_MUX__PRE 0x0
+
+#define ORX_FWP_AAG_LEN_W__A 0x2020010
+#define ORX_FWP_AAG_LEN_W__W 16
+#define ORX_FWP_AAG_LEN_W__M 0xFFFF
+#define ORX_FWP_AAG_LEN_W__PRE 0x800
+
+#define ORX_FWP_AAG_THR_W__A 0x2020011
+#define ORX_FWP_AAG_THR_W__W 8
+#define ORX_FWP_AAG_THR_W__M 0xFF
+#define ORX_FWP_AAG_THR_W__PRE 0x50
+
+#define ORX_FWP_AAG_THR_CNT_R__A 0x2020012
+#define ORX_FWP_AAG_THR_CNT_R__W 16
+#define ORX_FWP_AAG_THR_CNT_R__M 0xFFFF
+#define ORX_FWP_AAG_THR_CNT_R__PRE 0x0
+
+#define ORX_FWP_AAG_SNS_CNT_R__A 0x2020013
+#define ORX_FWP_AAG_SNS_CNT_R__W 16
+#define ORX_FWP_AAG_SNS_CNT_R__M 0xFFFF
+#define ORX_FWP_AAG_SNS_CNT_R__PRE 0x0
+
+#define ORX_FWP_PFI_A_W__A 0x2020014
+#define ORX_FWP_PFI_A_W__W 8
+#define ORX_FWP_PFI_A_W__M 0xFF
+#define ORX_FWP_PFI_A_W__PRE 0xB0
+#define ORX_FWP_PFI_A_W_RATE_2048KBPS 0xB0
+#define ORX_FWP_PFI_A_W_RATE_1544KBPS 0xA4
+#define ORX_FWP_PFI_A_W_RATE_3088KBPS 0xC0
+
+#define ORX_FWP_PFI_B_W__A 0x2020015
+#define ORX_FWP_PFI_B_W__W 8
+#define ORX_FWP_PFI_B_W__M 0xFF
+#define ORX_FWP_PFI_B_W__PRE 0x9E
+#define ORX_FWP_PFI_B_W_RATE_2048KBPS 0x9E
+#define ORX_FWP_PFI_B_W_RATE_1544KBPS 0x94
+#define ORX_FWP_PFI_B_W_RATE_3088KBPS 0xB0
+
+#define ORX_FWP_PFI_C_W__A 0x2020016
+#define ORX_FWP_PFI_C_W__W 8
+#define ORX_FWP_PFI_C_W__M 0xFF
+#define ORX_FWP_PFI_C_W__PRE 0x5C
+#define ORX_FWP_PFI_C_W_RATE_2048KBPS 0x5C
+#define ORX_FWP_PFI_C_W_RATE_1544KBPS 0x64
+#define ORX_FWP_PFI_C_W_RATE_3088KBPS 0x50
+
+#define ORX_FWP_KR1_AMP_R__A 0x2020017
+#define ORX_FWP_KR1_AMP_R__W 9
+#define ORX_FWP_KR1_AMP_R__M 0x1FF
+#define ORX_FWP_KR1_AMP_R__PRE 0x0
+
+#define ORX_FWP_KR1_LDT_W__A 0x2020018
+#define ORX_FWP_KR1_LDT_W__W 3
+#define ORX_FWP_KR1_LDT_W__M 0x7
+#define ORX_FWP_KR1_LDT_W__PRE 0x2
+#define ORX_FWP_SRC_DGN_W__A 0x2020019
+#define ORX_FWP_SRC_DGN_W__W 16
+#define ORX_FWP_SRC_DGN_W__M 0xFFFF
+#define ORX_FWP_SRC_DGN_W__PRE 0x1FF
+
+#define ORX_FWP_SRC_DGN_W_MANT__B 0
+#define ORX_FWP_SRC_DGN_W_MANT__W 9
+#define ORX_FWP_SRC_DGN_W_MANT__M 0x1FF
+#define ORX_FWP_SRC_DGN_W_MANT__PRE 0x1FF
+
+#define ORX_FWP_SRC_DGN_W_EXP__B 12
+#define ORX_FWP_SRC_DGN_W_EXP__W 4
+#define ORX_FWP_SRC_DGN_W_EXP__M 0xF000
+#define ORX_FWP_SRC_DGN_W_EXP__PRE 0x0
+
+#define ORX_FWP_NYQ_ADR_W__A 0x202001A
+#define ORX_FWP_NYQ_ADR_W__W 5
+#define ORX_FWP_NYQ_ADR_W__M 0x1F
+#define ORX_FWP_NYQ_ADR_W__PRE 0x1F
+
+#define ORX_FWP_NYQ_COF_RW__A 0x202001B
+#define ORX_FWP_NYQ_COF_RW__W 10
+#define ORX_FWP_NYQ_COF_RW__M 0x3FF
+#define ORX_FWP_NYQ_COF_RW__PRE 0x0
+
+#define ORX_FWP_IQM_FRQ_W__A 0x202001C
+#define ORX_FWP_IQM_FRQ_W__W 16
+#define ORX_FWP_IQM_FRQ_W__M 0xFFFF
+#define ORX_FWP_IQM_FRQ_W__PRE 0x4301
+
+#define ORX_EQU_COMM_EXEC__A 0x2030000
+#define ORX_EQU_COMM_EXEC__W 2
+#define ORX_EQU_COMM_EXEC__M 0x3
+#define ORX_EQU_COMM_EXEC__PRE 0x0
+#define ORX_EQU_COMM_EXEC_STOP 0x0
+#define ORX_EQU_COMM_EXEC_ACTIVE 0x1
+#define ORX_EQU_COMM_EXEC_HOLD 0x2
+
+#define ORX_EQU_COMM_MB__A 0x2030002
+#define ORX_EQU_COMM_MB__W 8
+#define ORX_EQU_COMM_MB__M 0xFF
+#define ORX_EQU_COMM_MB__PRE 0x0
+#define ORX_EQU_COMM_MB_CTL__B 0
+#define ORX_EQU_COMM_MB_CTL__W 1
+#define ORX_EQU_COMM_MB_CTL__M 0x1
+#define ORX_EQU_COMM_MB_CTL__PRE 0x0
+#define ORX_EQU_COMM_MB_CTL_OFF 0x0
+#define ORX_EQU_COMM_MB_CTL_ON 0x1
+#define ORX_EQU_COMM_MB_OBS__B 1
+#define ORX_EQU_COMM_MB_OBS__W 1
+#define ORX_EQU_COMM_MB_OBS__M 0x2
+#define ORX_EQU_COMM_MB_OBS__PRE 0x0
+#define ORX_EQU_COMM_MB_OBS_OFF 0x0
+#define ORX_EQU_COMM_MB_OBS_ON 0x2
+
+#define ORX_EQU_COMM_MB_CTL_MUX__B 2
+#define ORX_EQU_COMM_MB_CTL_MUX__W 3
+#define ORX_EQU_COMM_MB_CTL_MUX__M 0x1C
+#define ORX_EQU_COMM_MB_CTL_MUX__PRE 0x0
+
+#define ORX_EQU_COMM_MB_OBS_MUX__B 5
+#define ORX_EQU_COMM_MB_OBS_MUX__W 3
+#define ORX_EQU_COMM_MB_OBS_MUX__M 0xE0
+#define ORX_EQU_COMM_MB_OBS_MUX__PRE 0x0
+
+#define ORX_EQU_COMM_INT_REQ__A 0x2030003
+#define ORX_EQU_COMM_INT_REQ__W 1
+#define ORX_EQU_COMM_INT_REQ__M 0x1
+#define ORX_EQU_COMM_INT_REQ__PRE 0x0
+#define ORX_EQU_COMM_INT_STA__A 0x2030005
+#define ORX_EQU_COMM_INT_STA__W 2
+#define ORX_EQU_COMM_INT_STA__M 0x3
+#define ORX_EQU_COMM_INT_STA__PRE 0x0
+
+#define ORX_EQU_COMM_INT_STA_FFF_READ__B 0
+#define ORX_EQU_COMM_INT_STA_FFF_READ__W 1
+#define ORX_EQU_COMM_INT_STA_FFF_READ__M 0x1
+#define ORX_EQU_COMM_INT_STA_FFF_READ__PRE 0x0
+
+#define ORX_EQU_COMM_INT_STA_FBF_READ__B 1
+#define ORX_EQU_COMM_INT_STA_FBF_READ__W 1
+#define ORX_EQU_COMM_INT_STA_FBF_READ__M 0x2
+#define ORX_EQU_COMM_INT_STA_FBF_READ__PRE 0x0
+
+#define ORX_EQU_COMM_INT_MSK__A 0x2030006
+#define ORX_EQU_COMM_INT_MSK__W 2
+#define ORX_EQU_COMM_INT_MSK__M 0x3
+#define ORX_EQU_COMM_INT_MSK__PRE 0x0
+#define ORX_EQU_COMM_INT_MSK_FFF_READ__B 0
+#define ORX_EQU_COMM_INT_MSK_FFF_READ__W 1
+#define ORX_EQU_COMM_INT_MSK_FFF_READ__M 0x1
+#define ORX_EQU_COMM_INT_MSK_FFF_READ__PRE 0x0
+#define ORX_EQU_COMM_INT_MSK_FBF_READ__B 1
+#define ORX_EQU_COMM_INT_MSK_FBF_READ__W 1
+#define ORX_EQU_COMM_INT_MSK_FBF_READ__M 0x2
+#define ORX_EQU_COMM_INT_MSK_FBF_READ__PRE 0x0
+
+#define ORX_EQU_COMM_INT_STM__A 0x2030007
+#define ORX_EQU_COMM_INT_STM__W 2
+#define ORX_EQU_COMM_INT_STM__M 0x3
+#define ORX_EQU_COMM_INT_STM__PRE 0x0
+#define ORX_EQU_COMM_INT_STM_FFF_READ__B 0
+#define ORX_EQU_COMM_INT_STM_FFF_READ__W 1
+#define ORX_EQU_COMM_INT_STM_FFF_READ__M 0x1
+#define ORX_EQU_COMM_INT_STM_FFF_READ__PRE 0x0
+#define ORX_EQU_COMM_INT_STM_FBF_READ__B 1
+#define ORX_EQU_COMM_INT_STM_FBF_READ__W 1
+#define ORX_EQU_COMM_INT_STM_FBF_READ__M 0x2
+#define ORX_EQU_COMM_INT_STM_FBF_READ__PRE 0x0
+
+#define ORX_EQU_FFF_SCL_W__A 0x2030010
+#define ORX_EQU_FFF_SCL_W__W 1
+#define ORX_EQU_FFF_SCL_W__M 0x1
+#define ORX_EQU_FFF_SCL_W__PRE 0x0
+#define ORX_EQU_FFF_SCL_W_SCALE_GAIN_1 0x0
+#define ORX_EQU_FFF_SCL_W_SCALE_GAIN_2 0x1
+
+#define ORX_EQU_FFF_UPD_W__A 0x2030011
+#define ORX_EQU_FFF_UPD_W__W 1
+#define ORX_EQU_FFF_UPD_W__M 0x1
+#define ORX_EQU_FFF_UPD_W__PRE 0x0
+#define ORX_EQU_FFF_UPD_W_NO_UPDATE 0x0
+#define ORX_EQU_FFF_UPD_W_LMS_UPDATE 0x1
+
+#define ORX_EQU_FFF_STP_W__A 0x2030012
+#define ORX_EQU_FFF_STP_W__W 3
+#define ORX_EQU_FFF_STP_W__M 0x7
+#define ORX_EQU_FFF_STP_W__PRE 0x2
+
+#define ORX_EQU_FFF_LEA_W__A 0x2030013
+#define ORX_EQU_FFF_LEA_W__W 4
+#define ORX_EQU_FFF_LEA_W__M 0xF
+#define ORX_EQU_FFF_LEA_W__PRE 0x4
+
+#define ORX_EQU_FFF_RWT_W__A 0x2030014
+#define ORX_EQU_FFF_RWT_W__W 2
+#define ORX_EQU_FFF_RWT_W__M 0x3
+#define ORX_EQU_FFF_RWT_W__PRE 0x0
+
+#define ORX_EQU_FFF_C0RE_RW__A 0x2030015
+#define ORX_EQU_FFF_C0RE_RW__W 12
+#define ORX_EQU_FFF_C0RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C0RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C0IM_RW__A 0x2030016
+#define ORX_EQU_FFF_C0IM_RW__W 12
+#define ORX_EQU_FFF_C0IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C0IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C1RE_RW__A 0x2030017
+#define ORX_EQU_FFF_C1RE_RW__W 12
+#define ORX_EQU_FFF_C1RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C1RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C1IM_RW__A 0x2030018
+#define ORX_EQU_FFF_C1IM_RW__W 12
+#define ORX_EQU_FFF_C1IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C1IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C2RE_RW__A 0x2030019
+#define ORX_EQU_FFF_C2RE_RW__W 12
+#define ORX_EQU_FFF_C2RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C2RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C2IM_RW__A 0x203001A
+#define ORX_EQU_FFF_C2IM_RW__W 12
+#define ORX_EQU_FFF_C2IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C2IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C3RE_RW__A 0x203001B
+#define ORX_EQU_FFF_C3RE_RW__W 12
+#define ORX_EQU_FFF_C3RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C3RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C3IM_RW__A 0x203001C
+#define ORX_EQU_FFF_C3IM_RW__W 12
+#define ORX_EQU_FFF_C3IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C3IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C4RE_RW__A 0x203001D
+#define ORX_EQU_FFF_C4RE_RW__W 12
+#define ORX_EQU_FFF_C4RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C4RE_RW__PRE 0x400
+
+#define ORX_EQU_FFF_C4IM_RW__A 0x203001E
+#define ORX_EQU_FFF_C4IM_RW__W 12
+#define ORX_EQU_FFF_C4IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C4IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C5RE_RW__A 0x203001F
+#define ORX_EQU_FFF_C5RE_RW__W 12
+#define ORX_EQU_FFF_C5RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C5RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C5IM_RW__A 0x2030020
+#define ORX_EQU_FFF_C5IM_RW__W 12
+#define ORX_EQU_FFF_C5IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C5IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C6RE_RW__A 0x2030021
+#define ORX_EQU_FFF_C6RE_RW__W 12
+#define ORX_EQU_FFF_C6RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C6RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C6IM_RW__A 0x2030022
+#define ORX_EQU_FFF_C6IM_RW__W 12
+#define ORX_EQU_FFF_C6IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C6IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C7RE_RW__A 0x2030023
+#define ORX_EQU_FFF_C7RE_RW__W 12
+#define ORX_EQU_FFF_C7RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C7RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C7IM_RW__A 0x2030024
+#define ORX_EQU_FFF_C7IM_RW__W 12
+#define ORX_EQU_FFF_C7IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C7IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C8RE_RW__A 0x2030025
+#define ORX_EQU_FFF_C8RE_RW__W 12
+#define ORX_EQU_FFF_C8RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C8RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C8IM_RW__A 0x2030026
+#define ORX_EQU_FFF_C8IM_RW__W 12
+#define ORX_EQU_FFF_C8IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C8IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C9RE_RW__A 0x2030027
+#define ORX_EQU_FFF_C9RE_RW__W 12
+#define ORX_EQU_FFF_C9RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C9RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C9IM_RW__A 0x2030028
+#define ORX_EQU_FFF_C9IM_RW__W 12
+#define ORX_EQU_FFF_C9IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C9IM_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C10RE_RW__A 0x2030029
+#define ORX_EQU_FFF_C10RE_RW__W 12
+#define ORX_EQU_FFF_C10RE_RW__M 0xFFF
+#define ORX_EQU_FFF_C10RE_RW__PRE 0x0
+
+#define ORX_EQU_FFF_C10IM_RW__A 0x203002A
+#define ORX_EQU_FFF_C10IM_RW__W 12
+#define ORX_EQU_FFF_C10IM_RW__M 0xFFF
+#define ORX_EQU_FFF_C10IM_RW__PRE 0x0
+
+#define ORX_EQU_MXB_SEL_W__A 0x203002B
+#define ORX_EQU_MXB_SEL_W__W 1
+#define ORX_EQU_MXB_SEL_W__M 0x1
+#define ORX_EQU_MXB_SEL_W__PRE 0x0
+#define ORX_EQU_MXB_SEL_W_UNDECIDED_SYMBOLS 0x0
+#define ORX_EQU_MXB_SEL_W_DECIDED_SYMBOLS 0x1
+
+#define ORX_EQU_FBF_UPD_W__A 0x203002C
+#define ORX_EQU_FBF_UPD_W__W 1
+#define ORX_EQU_FBF_UPD_W__M 0x1
+#define ORX_EQU_FBF_UPD_W__PRE 0x0
+#define ORX_EQU_FBF_UPD_W_NO_UPDATE 0x0
+#define ORX_EQU_FBF_UPD_W_LMS_UPDATE 0x1
+
+#define ORX_EQU_FBF_STP_W__A 0x203002D
+#define ORX_EQU_FBF_STP_W__W 3
+#define ORX_EQU_FBF_STP_W__M 0x7
+#define ORX_EQU_FBF_STP_W__PRE 0x2
+
+#define ORX_EQU_FBF_LEA_W__A 0x203002E
+#define ORX_EQU_FBF_LEA_W__W 4
+#define ORX_EQU_FBF_LEA_W__M 0xF
+#define ORX_EQU_FBF_LEA_W__PRE 0x4
+
+#define ORX_EQU_FBF_RWT_W__A 0x203002F
+#define ORX_EQU_FBF_RWT_W__W 2
+#define ORX_EQU_FBF_RWT_W__M 0x3
+#define ORX_EQU_FBF_RWT_W__PRE 0x0
+
+#define ORX_EQU_FBF_C0RE_RW__A 0x2030030
+#define ORX_EQU_FBF_C0RE_RW__W 12
+#define ORX_EQU_FBF_C0RE_RW__M 0xFFF
+#define ORX_EQU_FBF_C0RE_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C0IM_RW__A 0x2030031
+#define ORX_EQU_FBF_C0IM_RW__W 12
+#define ORX_EQU_FBF_C0IM_RW__M 0xFFF
+#define ORX_EQU_FBF_C0IM_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C1RE_RW__A 0x2030032
+#define ORX_EQU_FBF_C1RE_RW__W 12
+#define ORX_EQU_FBF_C1RE_RW__M 0xFFF
+#define ORX_EQU_FBF_C1RE_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C1IM_RW__A 0x2030033
+#define ORX_EQU_FBF_C1IM_RW__W 12
+#define ORX_EQU_FBF_C1IM_RW__M 0xFFF
+#define ORX_EQU_FBF_C1IM_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C2RE_RW__A 0x2030034
+#define ORX_EQU_FBF_C2RE_RW__W 12
+#define ORX_EQU_FBF_C2RE_RW__M 0xFFF
+#define ORX_EQU_FBF_C2RE_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C2IM_RW__A 0x2030035
+#define ORX_EQU_FBF_C2IM_RW__W 12
+#define ORX_EQU_FBF_C2IM_RW__M 0xFFF
+#define ORX_EQU_FBF_C2IM_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C3RE_RW__A 0x2030036
+#define ORX_EQU_FBF_C3RE_RW__W 12
+#define ORX_EQU_FBF_C3RE_RW__M 0xFFF
+#define ORX_EQU_FBF_C3RE_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C3IM_RW__A 0x2030037
+#define ORX_EQU_FBF_C3IM_RW__W 12
+#define ORX_EQU_FBF_C3IM_RW__M 0xFFF
+#define ORX_EQU_FBF_C3IM_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C4RE_RW__A 0x2030038
+#define ORX_EQU_FBF_C4RE_RW__W 12
+#define ORX_EQU_FBF_C4RE_RW__M 0xFFF
+#define ORX_EQU_FBF_C4RE_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C4IM_RW__A 0x2030039
+#define ORX_EQU_FBF_C4IM_RW__W 12
+#define ORX_EQU_FBF_C4IM_RW__M 0xFFF
+#define ORX_EQU_FBF_C4IM_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C5RE_RW__A 0x203003A
+#define ORX_EQU_FBF_C5RE_RW__W 12
+#define ORX_EQU_FBF_C5RE_RW__M 0xFFF
+#define ORX_EQU_FBF_C5RE_RW__PRE 0x0
+
+#define ORX_EQU_FBF_C5IM_RW__A 0x203003B
+#define ORX_EQU_FBF_C5IM_RW__W 12
+#define ORX_EQU_FBF_C5IM_RW__M 0xFFF
+#define ORX_EQU_FBF_C5IM_RW__PRE 0x0
+
+#define ORX_EQU_ERR_SEL_W__A 0x203003C
+#define ORX_EQU_ERR_SEL_W__W 1
+#define ORX_EQU_ERR_SEL_W__M 0x1
+#define ORX_EQU_ERR_SEL_W__PRE 0x0
+#define ORX_EQU_ERR_SEL_W_CMA_ERROR 0x0
+#define ORX_EQU_ERR_SEL_W_DDA_ERROR 0x1
+
+#define ORX_EQU_ERR_TIS_W__A 0x203003D
+#define ORX_EQU_ERR_TIS_W__W 1
+#define ORX_EQU_ERR_TIS_W__M 0x1
+#define ORX_EQU_ERR_TIS_W__PRE 0x0
+#define ORX_EQU_ERR_TIS_W_CMA_SIGNALS 0x0
+#define ORX_EQU_ERR_TIS_W_DDA_SIGNALS 0x1
+
+#define ORX_EQU_ERR_EDI_R__A 0x203003E
+#define ORX_EQU_ERR_EDI_R__W 5
+#define ORX_EQU_ERR_EDI_R__M 0x1F
+#define ORX_EQU_ERR_EDI_R__PRE 0xF
+
+#define ORX_EQU_ERR_EDQ_R__A 0x203003F
+#define ORX_EQU_ERR_EDQ_R__W 5
+#define ORX_EQU_ERR_EDQ_R__M 0x1F
+#define ORX_EQU_ERR_EDQ_R__PRE 0xF
+
+#define ORX_EQU_ERR_ECI_R__A 0x2030040
+#define ORX_EQU_ERR_ECI_R__W 5
+#define ORX_EQU_ERR_ECI_R__M 0x1F
+#define ORX_EQU_ERR_ECI_R__PRE 0xF
+
+#define ORX_EQU_ERR_ECQ_R__A 0x2030041
+#define ORX_EQU_ERR_ECQ_R__W 5
+#define ORX_EQU_ERR_ECQ_R__M 0x1F
+#define ORX_EQU_ERR_ECQ_R__PRE 0xF
+
+#define ORX_EQU_MER_MER_R__A 0x2030042
+#define ORX_EQU_MER_MER_R__W 6
+#define ORX_EQU_MER_MER_R__M 0x3F
+#define ORX_EQU_MER_MER_R__PRE 0x3F
+
+#define ORX_EQU_MER_LDT_W__A 0x2030043
+#define ORX_EQU_MER_LDT_W__W 3
+#define ORX_EQU_MER_LDT_W__M 0x7
+#define ORX_EQU_MER_LDT_W__PRE 0x4
+
+#define ORX_EQU_SYN_LEN_W__A 0x2030044
+#define ORX_EQU_SYN_LEN_W__W 16
+#define ORX_EQU_SYN_LEN_W__M 0xFFFF
+#define ORX_EQU_SYN_LEN_W__PRE 0x0
+
+#define ORX_DDC_COMM_EXEC__A 0x2040000
+#define ORX_DDC_COMM_EXEC__W 2
+#define ORX_DDC_COMM_EXEC__M 0x3
+#define ORX_DDC_COMM_EXEC__PRE 0x0
+#define ORX_DDC_COMM_EXEC_STOP 0x0
+#define ORX_DDC_COMM_EXEC_ACTIVE 0x1
+#define ORX_DDC_COMM_EXEC_HOLD 0x2
+
+#define ORX_DDC_COMM_MB__A 0x2040002
+#define ORX_DDC_COMM_MB__W 6
+#define ORX_DDC_COMM_MB__M 0x3F
+#define ORX_DDC_COMM_MB__PRE 0x0
+#define ORX_DDC_COMM_MB_CTL__B 0
+#define ORX_DDC_COMM_MB_CTL__W 1
+#define ORX_DDC_COMM_MB_CTL__M 0x1
+#define ORX_DDC_COMM_MB_CTL__PRE 0x0
+#define ORX_DDC_COMM_MB_CTL_OFF 0x0
+#define ORX_DDC_COMM_MB_CTL_ON 0x1
+#define ORX_DDC_COMM_MB_OBS__B 1
+#define ORX_DDC_COMM_MB_OBS__W 1
+#define ORX_DDC_COMM_MB_OBS__M 0x2
+#define ORX_DDC_COMM_MB_OBS__PRE 0x0
+#define ORX_DDC_COMM_MB_OBS_OFF 0x0
+#define ORX_DDC_COMM_MB_OBS_ON 0x2
+
+#define ORX_DDC_COMM_MB_CTL_MUX__B 2
+#define ORX_DDC_COMM_MB_CTL_MUX__W 2
+#define ORX_DDC_COMM_MB_CTL_MUX__M 0xC
+#define ORX_DDC_COMM_MB_CTL_MUX__PRE 0x0
+
+#define ORX_DDC_COMM_MB_OBS_MUX__B 4
+#define ORX_DDC_COMM_MB_OBS_MUX__W 2
+#define ORX_DDC_COMM_MB_OBS_MUX__M 0x30
+#define ORX_DDC_COMM_MB_OBS_MUX__PRE 0x0
+
+#define ORX_DDC_COMM_INT_REQ__A 0x2040003
+#define ORX_DDC_COMM_INT_REQ__W 1
+#define ORX_DDC_COMM_INT_REQ__M 0x1
+#define ORX_DDC_COMM_INT_REQ__PRE 0x0
+#define ORX_DDC_COMM_INT_STA__A 0x2040005
+#define ORX_DDC_COMM_INT_STA__W 1
+#define ORX_DDC_COMM_INT_STA__M 0x1
+#define ORX_DDC_COMM_INT_STA__PRE 0x0
+#define ORX_DDC_COMM_INT_MSK__A 0x2040006
+#define ORX_DDC_COMM_INT_MSK__W 1
+#define ORX_DDC_COMM_INT_MSK__M 0x1
+#define ORX_DDC_COMM_INT_MSK__PRE 0x0
+#define ORX_DDC_COMM_INT_STM__A 0x2040007
+#define ORX_DDC_COMM_INT_STM__W 1
+#define ORX_DDC_COMM_INT_STM__M 0x1
+#define ORX_DDC_COMM_INT_STM__PRE 0x0
+#define ORX_DDC_DEC_MAP_W__A 0x2040010
+#define ORX_DDC_DEC_MAP_W__W 9
+#define ORX_DDC_DEC_MAP_W__M 0x1FF
+#define ORX_DDC_DEC_MAP_W__PRE 0x178
+
+#define ORX_DDC_DEC_MAP_W_QUADR0__B 0
+#define ORX_DDC_DEC_MAP_W_QUADR0__W 2
+#define ORX_DDC_DEC_MAP_W_QUADR0__M 0x3
+#define ORX_DDC_DEC_MAP_W_QUADR0__PRE 0x0
+#define ORX_DDC_DEC_MAP_W_QUADR0_ROTATE_DEFAULT 0x0
+#define ORX_DDC_DEC_MAP_W_QUADR0_ROTATE_ALTERNATE 0x0
+
+#define ORX_DDC_DEC_MAP_W_QUADR1__B 2
+#define ORX_DDC_DEC_MAP_W_QUADR1__W 2
+#define ORX_DDC_DEC_MAP_W_QUADR1__M 0xC
+#define ORX_DDC_DEC_MAP_W_QUADR1__PRE 0x8
+#define ORX_DDC_DEC_MAP_W_QUADR1_ROTATE_DEFAULT 0x8
+#define ORX_DDC_DEC_MAP_W_QUADR1_ROTATE_ALTERNATE 0x4
+
+#define ORX_DDC_DEC_MAP_W_QUADR2__B 4
+#define ORX_DDC_DEC_MAP_W_QUADR2__W 2
+#define ORX_DDC_DEC_MAP_W_QUADR2__M 0x30
+#define ORX_DDC_DEC_MAP_W_QUADR2__PRE 0x30
+#define ORX_DDC_DEC_MAP_W_QUADR2_ROTATE_DEFAULT 0x30
+#define ORX_DDC_DEC_MAP_W_QUADR2_ROTATE_ALTERNATE 0x30
+
+#define ORX_DDC_DEC_MAP_W_QUADR3__B 6
+#define ORX_DDC_DEC_MAP_W_QUADR3__W 2
+#define ORX_DDC_DEC_MAP_W_QUADR3__M 0xC0
+#define ORX_DDC_DEC_MAP_W_QUADR3__PRE 0x40
+#define ORX_DDC_DEC_MAP_W_QUADR3_ROTATE_DEFAULT 0x40
+#define ORX_DDC_DEC_MAP_W_QUADR3_ROTATE_ALTERNATE 0x80
+#define ORX_DDC_DEC_MAP_W_DIFF_DECOD__B 8
+#define ORX_DDC_DEC_MAP_W_DIFF_DECOD__W 1
+#define ORX_DDC_DEC_MAP_W_DIFF_DECOD__M 0x100
+#define ORX_DDC_DEC_MAP_W_DIFF_DECOD__PRE 0x100
+#define ORX_DDC_DEC_MAP_W_DIFF_DECOD_COHERENT_DECODING 0x0
+#define ORX_DDC_DEC_MAP_W_DIFF_DECOD_DIFF_DECODING 0x100
+
+#define ORX_DDC_OFO_SET_W__A 0x2040011
+#define ORX_DDC_OFO_SET_W__W 16
+#define ORX_DDC_OFO_SET_W__M 0xFFFF
+#define ORX_DDC_OFO_SET_W__PRE 0x1402
+
+#define ORX_DDC_OFO_SET_W_PHASE__B 0
+#define ORX_DDC_OFO_SET_W_PHASE__W 7
+#define ORX_DDC_OFO_SET_W_PHASE__M 0x7F
+#define ORX_DDC_OFO_SET_W_PHASE__PRE 0x2
+
+#define ORX_DDC_OFO_SET_W_CRXHITIME__B 7
+#define ORX_DDC_OFO_SET_W_CRXHITIME__W 7
+#define ORX_DDC_OFO_SET_W_CRXHITIME__M 0x3F80
+#define ORX_DDC_OFO_SET_W_CRXHITIME__PRE 0x1400
+
+#define ORX_DDC_OFO_SET_W_CRXINV__B 14
+#define ORX_DDC_OFO_SET_W_CRXINV__W 1
+#define ORX_DDC_OFO_SET_W_CRXINV__M 0x4000
+#define ORX_DDC_OFO_SET_W_CRXINV__PRE 0x0
+
+#define ORX_DDC_OFO_SET_W_DISABLE__B 15
+#define ORX_DDC_OFO_SET_W_DISABLE__W 1
+#define ORX_DDC_OFO_SET_W_DISABLE__M 0x8000
+#define ORX_DDC_OFO_SET_W_DISABLE__PRE 0x0
+
+#define ORX_CON_COMM_EXEC__A 0x2050000
+#define ORX_CON_COMM_EXEC__W 2
+#define ORX_CON_COMM_EXEC__M 0x3
+#define ORX_CON_COMM_EXEC__PRE 0x0
+#define ORX_CON_COMM_EXEC_STOP 0x0
+#define ORX_CON_COMM_EXEC_ACTIVE 0x1
+#define ORX_CON_COMM_EXEC_HOLD 0x2
+
+#define ORX_CON_LDT_W__A 0x2050010
+#define ORX_CON_LDT_W__W 3
+#define ORX_CON_LDT_W__M 0x7
+#define ORX_CON_LDT_W__PRE 0x3
+
+#define ORX_CON_LDT_W_CON_LDT_W__B 0
+#define ORX_CON_LDT_W_CON_LDT_W__W 3
+#define ORX_CON_LDT_W_CON_LDT_W__M 0x7
+#define ORX_CON_LDT_W_CON_LDT_W__PRE 0x3
+
+#define ORX_CON_RST_W__A 0x2050011
+#define ORX_CON_RST_W__W 4
+#define ORX_CON_RST_W__M 0xF
+#define ORX_CON_RST_W__PRE 0x0
+
+#define ORX_CON_RST_W_CPH__B 0
+#define ORX_CON_RST_W_CPH__W 1
+#define ORX_CON_RST_W_CPH__M 0x1
+#define ORX_CON_RST_W_CPH__PRE 0x0
+
+#define ORX_CON_RST_W_CTI__B 1
+#define ORX_CON_RST_W_CTI__W 1
+#define ORX_CON_RST_W_CTI__M 0x2
+#define ORX_CON_RST_W_CTI__PRE 0x0
+
+#define ORX_CON_RST_W_KRN__B 2
+#define ORX_CON_RST_W_KRN__W 1
+#define ORX_CON_RST_W_KRN__M 0x4
+#define ORX_CON_RST_W_KRN__PRE 0x0
+
+#define ORX_CON_RST_W_KRP__B 3
+#define ORX_CON_RST_W_KRP__W 1
+#define ORX_CON_RST_W_KRP__M 0x8
+#define ORX_CON_RST_W_KRP__PRE 0x0
+
+#define ORX_CON_CPH_PHI_R__A 0x2050012
+#define ORX_CON_CPH_PHI_R__W 16
+#define ORX_CON_CPH_PHI_R__M 0xFFFF
+#define ORX_CON_CPH_PHI_R__PRE 0x0
+
+#define ORX_CON_CPH_FRQ_R__A 0x2050013
+#define ORX_CON_CPH_FRQ_R__W 16
+#define ORX_CON_CPH_FRQ_R__M 0xFFFF
+#define ORX_CON_CPH_FRQ_R__PRE 0x0
+
+#define ORX_CON_CPH_AMP_R__A 0x2050014
+#define ORX_CON_CPH_AMP_R__W 16
+#define ORX_CON_CPH_AMP_R__M 0xFFFF
+#define ORX_CON_CPH_AMP_R__PRE 0x0
+
+#define ORX_CON_CPH_KDF_W__A 0x2050015
+#define ORX_CON_CPH_KDF_W__W 4
+#define ORX_CON_CPH_KDF_W__M 0xF
+#define ORX_CON_CPH_KDF_W__PRE 0x0
+
+#define ORX_CON_CPH_KPF_W__A 0x2050016
+#define ORX_CON_CPH_KPF_W__W 4
+#define ORX_CON_CPH_KPF_W__M 0xF
+#define ORX_CON_CPH_KPF_W__PRE 0x0
+
+#define ORX_CON_CPH_KIF_W__A 0x2050017
+#define ORX_CON_CPH_KIF_W__W 4
+#define ORX_CON_CPH_KIF_W__M 0xF
+#define ORX_CON_CPH_KIF_W__PRE 0x0
+#define ORX_CON_CPH_APT_W__A 0x2050018
+#define ORX_CON_CPH_APT_W__W 16
+#define ORX_CON_CPH_APT_W__M 0xFFFF
+#define ORX_CON_CPH_APT_W__PRE 0x804
+
+#define ORX_CON_CPH_APT_W_PTH__B 0
+#define ORX_CON_CPH_APT_W_PTH__W 8
+#define ORX_CON_CPH_APT_W_PTH__M 0xFF
+#define ORX_CON_CPH_APT_W_PTH__PRE 0x4
+
+#define ORX_CON_CPH_APT_W_ATH__B 8
+#define ORX_CON_CPH_APT_W_ATH__W 8
+#define ORX_CON_CPH_APT_W_ATH__M 0xFF00
+#define ORX_CON_CPH_APT_W_ATH__PRE 0x800
+
+#define ORX_CON_CPH_WLC_W__A 0x2050019
+#define ORX_CON_CPH_WLC_W__W 8
+#define ORX_CON_CPH_WLC_W__M 0xFF
+#define ORX_CON_CPH_WLC_W__PRE 0x81
+
+#define ORX_CON_CPH_WLC_W_LATC__B 0
+#define ORX_CON_CPH_WLC_W_LATC__W 4
+#define ORX_CON_CPH_WLC_W_LATC__M 0xF
+#define ORX_CON_CPH_WLC_W_LATC__PRE 0x1
+
+#define ORX_CON_CPH_WLC_W_WLIM__B 4
+#define ORX_CON_CPH_WLC_W_WLIM__W 4
+#define ORX_CON_CPH_WLC_W_WLIM__M 0xF0
+#define ORX_CON_CPH_WLC_W_WLIM__PRE 0x80
+
+#define ORX_CON_CPH_DLY_W__A 0x205001A
+#define ORX_CON_CPH_DLY_W__W 3
+#define ORX_CON_CPH_DLY_W__M 0x7
+#define ORX_CON_CPH_DLY_W__PRE 0x4
+
+#define ORX_CON_CPH_TCL_W__A 0x205001B
+#define ORX_CON_CPH_TCL_W__W 3
+#define ORX_CON_CPH_TCL_W__M 0x7
+#define ORX_CON_CPH_TCL_W__PRE 0x3
+
+#define ORX_CON_KRP_AMP_R__A 0x205001C
+#define ORX_CON_KRP_AMP_R__W 9
+#define ORX_CON_KRP_AMP_R__M 0x1FF
+#define ORX_CON_KRP_AMP_R__PRE 0x0
+
+#define ORX_CON_KRN_AMP_R__A 0x205001D
+#define ORX_CON_KRN_AMP_R__W 9
+#define ORX_CON_KRN_AMP_R__M 0x1FF
+#define ORX_CON_KRN_AMP_R__PRE 0x0
+
+#define ORX_CON_CTI_DTI_R__A 0x205001E
+#define ORX_CON_CTI_DTI_R__W 16
+#define ORX_CON_CTI_DTI_R__M 0xFFFF
+#define ORX_CON_CTI_DTI_R__PRE 0x0
+
+#define ORX_CON_CTI_KDT_W__A 0x205001F
+#define ORX_CON_CTI_KDT_W__W 4
+#define ORX_CON_CTI_KDT_W__M 0xF
+#define ORX_CON_CTI_KDT_W__PRE 0x4
+
+#define ORX_CON_CTI_KPT_W__A 0x2050020
+#define ORX_CON_CTI_KPT_W__W 4
+#define ORX_CON_CTI_KPT_W__M 0xF
+#define ORX_CON_CTI_KPT_W__PRE 0x3
+
+#define ORX_CON_CTI_KIT_W__A 0x2050021
+#define ORX_CON_CTI_KIT_W__W 4
+#define ORX_CON_CTI_KIT_W__M 0xF
+#define ORX_CON_CTI_KIT_W__PRE 0xB
+
+#define ORX_CON_CTI_TAT_W__A 0x2050022
+#define ORX_CON_CTI_TAT_W__W 4
+#define ORX_CON_CTI_TAT_W__M 0xF
+#define ORX_CON_CTI_TAT_W__PRE 0x3
+
+#define ORX_NSU_COMM_EXEC__A 0x2060000
+#define ORX_NSU_COMM_EXEC__W 2
+#define ORX_NSU_COMM_EXEC__M 0x3
+#define ORX_NSU_COMM_EXEC__PRE 0x0
+#define ORX_NSU_COMM_EXEC_STOP 0x0
+#define ORX_NSU_COMM_EXEC_ACTIVE 0x1
+#define ORX_NSU_COMM_EXEC_HOLD 0x2
+
+#define ORX_NSU_AOX_STDBY_W__A 0x2060010
+#define ORX_NSU_AOX_STDBY_W__W 8
+#define ORX_NSU_AOX_STDBY_W__M 0xFF
+#define ORX_NSU_AOX_STDBY_W__PRE 0x0
+
+#define ORX_NSU_AOX_STDBY_W_STDBYADC__B 0
+#define ORX_NSU_AOX_STDBY_W_STDBYADC__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYADC__M 0x1
+#define ORX_NSU_AOX_STDBY_W_STDBYADC__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYADC_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYADC_A1_OFF 0x1
+#define ORX_NSU_AOX_STDBY_W_STDBYADC_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYADC_A2_ON 0x1
+
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP__B 1
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP__M 0x2
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP_A1_OFF 0x2
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYAMP_A2_ON 0x2
+
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS__B 2
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS__M 0x4
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS_A1_OFF 0x4
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYBIAS_A2_ON 0x4
+
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL__B 3
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL__M 0x8
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL_A1_OFF 0x8
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYPLL_A2_ON 0x8
+
+#define ORX_NSU_AOX_STDBY_W_STDBYPD__B 4
+#define ORX_NSU_AOX_STDBY_W_STDBYPD__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYPD__M 0x10
+#define ORX_NSU_AOX_STDBY_W_STDBYPD__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYPD_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYPD_A1_OFF 0x10
+#define ORX_NSU_AOX_STDBY_W_STDBYPD_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYPD_A2_ON 0x10
+
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF__B 5
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF__M 0x20
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A1_OFF 0x20
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_IF_A2_ON 0x20
+
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF__B 6
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF__M 0x40
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A1_OFF 0x40
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYTAGC_RF_A2_ON 0x40
+
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT__B 7
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT__W 1
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT__M 0x80
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT__PRE 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT_A1_ON 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT_A1_OFF 0x80
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT_A2_OFF 0x0
+#define ORX_NSU_AOX_STDBY_W_STDBYFLT_A2_ON 0x80
+
+#define ORX_NSU_AOX_LOFRQ_W__A 0x2060011
+#define ORX_NSU_AOX_LOFRQ_W__W 16
+#define ORX_NSU_AOX_LOFRQ_W__M 0xFFFF
+#define ORX_NSU_AOX_LOFRQ_W__PRE 0x0
+#define ORX_NSU_AOX_LOMDE_W__A 0x2060012
+#define ORX_NSU_AOX_LOMDE_W__W 16
+#define ORX_NSU_AOX_LOMDE_W__M 0xFFFF
+#define ORX_NSU_AOX_LOMDE_W__PRE 0x0
+
+#define ORX_NSU_AOX_LOMDE_W_AOX_LOFRQ_EXT__B 0
+#define ORX_NSU_AOX_LOMDE_W_AOX_LOFRQ_EXT__W 8
+#define ORX_NSU_AOX_LOMDE_W_AOX_LOFRQ_EXT__M 0xFF
+#define ORX_NSU_AOX_LOMDE_W_AOX_LOFRQ_EXT__PRE 0x0
+
+#define ORX_NSU_AOX_LOMDE_W_RESET_VCO__B 13
+#define ORX_NSU_AOX_LOMDE_W_RESET_VCO__W 1
+#define ORX_NSU_AOX_LOMDE_W_RESET_VCO__M 0x2000
+#define ORX_NSU_AOX_LOMDE_W_RESET_VCO__PRE 0x0
+
+#define ORX_NSU_AOX_LOMDE_W_PLL_DIV__B 14
+#define ORX_NSU_AOX_LOMDE_W_PLL_DIV__W 2
+#define ORX_NSU_AOX_LOMDE_W_PLL_DIV__M 0xC000
+#define ORX_NSU_AOX_LOMDE_W_PLL_DIV__PRE 0x0
+
+#define ORX_NSU_AOX_LOPOW_W__A 0x2060013
+#define ORX_NSU_AOX_LOPOW_W__W 2
+#define ORX_NSU_AOX_LOPOW_W__M 0x3
+#define ORX_NSU_AOX_LOPOW_W__PRE 0x0
+#define ORX_NSU_AOX_LOPOW_W_POWER_MINUS0DB 0x0
+#define ORX_NSU_AOX_LOPOW_W_POWER_MINUS5DB 0x1
+#define ORX_NSU_AOX_LOPOW_W_POWER_MINUS10DB 0x2
+#define ORX_NSU_AOX_LOPOW_W_POWER_MINUS15DB 0x3
+
+#define ORX_NSU_AOX_STHR_W__A 0x2060014
+#define ORX_NSU_AOX_STHR_W__W 5
+#define ORX_NSU_AOX_STHR_W__M 0x1F
+#define ORX_NSU_AOX_STHR_W__PRE 0x0
+
+#define ORX_NSU_TUN_RFGAIN_W__A 0x2060015
+#define ORX_NSU_TUN_RFGAIN_W__W 15
+#define ORX_NSU_TUN_RFGAIN_W__M 0x7FFF
+#define ORX_NSU_TUN_RFGAIN_W__PRE 0x0
+
+#define ORX_NSU_TUN_IFGAIN_W__A 0x2060016
+#define ORX_NSU_TUN_IFGAIN_W__W 15
+#define ORX_NSU_TUN_IFGAIN_W__M 0x7FFF
+#define ORX_NSU_TUN_IFGAIN_W__PRE 0x0
+
+#define ORX_NSU_TUN_BPF_W__A 0x2060017
+#define ORX_NSU_TUN_BPF_W__W 15
+#define ORX_NSU_TUN_BPF_W__M 0x7FFF
+#define ORX_NSU_TUN_BPF_W__PRE 0x1F9
+#define ORX_NSU_NSS_BITSWAP_W__A 0x2060018
+#define ORX_NSU_NSS_BITSWAP_W__W 3
+#define ORX_NSU_NSS_BITSWAP_W__M 0x7
+#define ORX_NSU_NSS_BITSWAP_W__PRE 0x0
+
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS0_RF__B 0
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS0_RF__W 1
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS0_RF__M 0x1
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS0_RF__PRE 0x0
+
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS1_IF__B 1
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS1_IF__W 1
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS1_IF__M 0x2
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS1_IF__PRE 0x0
+
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS2_BP__B 2
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS2_BP__W 1
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS2_BP__M 0x4
+#define ORX_NSU_NSS_BITSWAP_W_BITSWAP_NS2_BP__PRE 0x0
+
+#define ORX_TST_COMM_EXEC__A 0x23F0000
+#define ORX_TST_COMM_EXEC__W 2
+#define ORX_TST_COMM_EXEC__M 0x3
+#define ORX_TST_COMM_EXEC__PRE 0x0
+#define ORX_TST_COMM_EXEC_STOP 0x0
+#define ORX_TST_COMM_EXEC_ACTIVE 0x1
+#define ORX_TST_COMM_EXEC_HOLD 0x2
+
+#define ORX_TST_AOX_TST_W__A 0x23F0010
+#define ORX_TST_AOX_TST_W__W 8
+#define ORX_TST_AOX_TST_W__M 0xFF
+#define ORX_TST_AOX_TST_W__PRE 0x0
+
+#define QAM_COMM_EXEC__A 0x1400000
+#define QAM_COMM_EXEC__W 2
+#define QAM_COMM_EXEC__M 0x3
+#define QAM_COMM_EXEC__PRE 0x0
+#define QAM_COMM_EXEC_STOP 0x0
+#define QAM_COMM_EXEC_ACTIVE 0x1
+#define QAM_COMM_EXEC_HOLD 0x2
+
+#define QAM_COMM_MB__A 0x1400002
+#define QAM_COMM_MB__W 16
+#define QAM_COMM_MB__M 0xFFFF
+#define QAM_COMM_MB__PRE 0x0
+#define QAM_COMM_INT_REQ__A 0x1400003
+#define QAM_COMM_INT_REQ__W 16
+#define QAM_COMM_INT_REQ__M 0xFFFF
+#define QAM_COMM_INT_REQ__PRE 0x0
+
+#define QAM_COMM_INT_REQ_SL_REQ__B 0
+#define QAM_COMM_INT_REQ_SL_REQ__W 1
+#define QAM_COMM_INT_REQ_SL_REQ__M 0x1
+#define QAM_COMM_INT_REQ_SL_REQ__PRE 0x0
+
+#define QAM_COMM_INT_REQ_LC_REQ__B 1
+#define QAM_COMM_INT_REQ_LC_REQ__W 1
+#define QAM_COMM_INT_REQ_LC_REQ__M 0x2
+#define QAM_COMM_INT_REQ_LC_REQ__PRE 0x0
+
+#define QAM_COMM_INT_REQ_VD_REQ__B 2
+#define QAM_COMM_INT_REQ_VD_REQ__W 1
+#define QAM_COMM_INT_REQ_VD_REQ__M 0x4
+#define QAM_COMM_INT_REQ_VD_REQ__PRE 0x0
+
+#define QAM_COMM_INT_REQ_SY_REQ__B 3
+#define QAM_COMM_INT_REQ_SY_REQ__W 1
+#define QAM_COMM_INT_REQ_SY_REQ__M 0x8
+#define QAM_COMM_INT_REQ_SY_REQ__PRE 0x0
+
+#define QAM_COMM_INT_STA__A 0x1400005
+#define QAM_COMM_INT_STA__W 16
+#define QAM_COMM_INT_STA__M 0xFFFF
+#define QAM_COMM_INT_STA__PRE 0x0
+#define QAM_COMM_INT_MSK__A 0x1400006
+#define QAM_COMM_INT_MSK__W 16
+#define QAM_COMM_INT_MSK__M 0xFFFF
+#define QAM_COMM_INT_MSK__PRE 0x0
+#define QAM_COMM_INT_STM__A 0x1400007
+#define QAM_COMM_INT_STM__W 16
+#define QAM_COMM_INT_STM__M 0xFFFF
+#define QAM_COMM_INT_STM__PRE 0x0
+
+#define QAM_TOP_COMM_EXEC__A 0x1410000
+#define QAM_TOP_COMM_EXEC__W 2
+#define QAM_TOP_COMM_EXEC__M 0x3
+#define QAM_TOP_COMM_EXEC__PRE 0x0
+#define QAM_TOP_COMM_EXEC_STOP 0x0
+#define QAM_TOP_COMM_EXEC_ACTIVE 0x1
+#define QAM_TOP_COMM_EXEC_HOLD 0x2
+
+#define QAM_TOP_ANNEX__A 0x1410010
+#define QAM_TOP_ANNEX__W 2
+#define QAM_TOP_ANNEX__M 0x3
+#define QAM_TOP_ANNEX__PRE 0x1
+#define QAM_TOP_ANNEX_A 0x0
+#define QAM_TOP_ANNEX_B 0x1
+#define QAM_TOP_ANNEX_C 0x2
+#define QAM_TOP_ANNEX_D 0x3
+
+#define QAM_TOP_CONSTELLATION__A 0x1410011
+#define QAM_TOP_CONSTELLATION__W 3
+#define QAM_TOP_CONSTELLATION__M 0x7
+#define QAM_TOP_CONSTELLATION__PRE 0x5
+#define QAM_TOP_CONSTELLATION_NONE 0x0
+#define QAM_TOP_CONSTELLATION_QPSK 0x1
+#define QAM_TOP_CONSTELLATION_QAM8 0x2
+#define QAM_TOP_CONSTELLATION_QAM16 0x3
+#define QAM_TOP_CONSTELLATION_QAM32 0x4
+#define QAM_TOP_CONSTELLATION_QAM64 0x5
+#define QAM_TOP_CONSTELLATION_QAM128 0x6
+#define QAM_TOP_CONSTELLATION_QAM256 0x7
+
+#define QAM_FQ_COMM_EXEC__A 0x1420000
+#define QAM_FQ_COMM_EXEC__W 2
+#define QAM_FQ_COMM_EXEC__M 0x3
+#define QAM_FQ_COMM_EXEC__PRE 0x0
+#define QAM_FQ_COMM_EXEC_STOP 0x0
+#define QAM_FQ_COMM_EXEC_ACTIVE 0x1
+#define QAM_FQ_COMM_EXEC_HOLD 0x2
+
+#define QAM_FQ_MODE__A 0x1420010
+#define QAM_FQ_MODE__W 3
+#define QAM_FQ_MODE__M 0x7
+#define QAM_FQ_MODE__PRE 0x0
+
+#define QAM_FQ_MODE_TAPRESET__B 0
+#define QAM_FQ_MODE_TAPRESET__W 1
+#define QAM_FQ_MODE_TAPRESET__M 0x1
+#define QAM_FQ_MODE_TAPRESET__PRE 0x0
+#define QAM_FQ_MODE_TAPRESET_RST 0x1
+
+#define QAM_FQ_MODE_TAPLMS__B 1
+#define QAM_FQ_MODE_TAPLMS__W 1
+#define QAM_FQ_MODE_TAPLMS__M 0x2
+#define QAM_FQ_MODE_TAPLMS__PRE 0x0
+#define QAM_FQ_MODE_TAPLMS_UPD 0x2
+
+#define QAM_FQ_MODE_TAPDRAIN__B 2
+#define QAM_FQ_MODE_TAPDRAIN__W 1
+#define QAM_FQ_MODE_TAPDRAIN__M 0x4
+#define QAM_FQ_MODE_TAPDRAIN__PRE 0x0
+#define QAM_FQ_MODE_TAPDRAIN_DRAIN 0x4
+
+#define QAM_FQ_MU_FACTOR__A 0x1420011
+#define QAM_FQ_MU_FACTOR__W 3
+#define QAM_FQ_MU_FACTOR__M 0x7
+#define QAM_FQ_MU_FACTOR__PRE 0x0
+
+#define QAM_FQ_LA_FACTOR__A 0x1420012
+#define QAM_FQ_LA_FACTOR__W 4
+#define QAM_FQ_LA_FACTOR__M 0xF
+#define QAM_FQ_LA_FACTOR__PRE 0xC
+#define QAM_FQ_CENTTAP_IDX__A 0x1420016
+#define QAM_FQ_CENTTAP_IDX__W 5
+#define QAM_FQ_CENTTAP_IDX__M 0x1F
+#define QAM_FQ_CENTTAP_IDX__PRE 0x13
+
+#define QAM_FQ_CENTTAP_IDX_IDX__B 0
+#define QAM_FQ_CENTTAP_IDX_IDX__W 5
+#define QAM_FQ_CENTTAP_IDX_IDX__M 0x1F
+#define QAM_FQ_CENTTAP_IDX_IDX__PRE 0x13
+
+#define QAM_FQ_CENTTAP_VALUE__A 0x1420017
+#define QAM_FQ_CENTTAP_VALUE__W 12
+#define QAM_FQ_CENTTAP_VALUE__M 0xFFF
+#define QAM_FQ_CENTTAP_VALUE__PRE 0x600
+
+#define QAM_FQ_CENTTAP_VALUE_TAP__B 0
+#define QAM_FQ_CENTTAP_VALUE_TAP__W 12
+#define QAM_FQ_CENTTAP_VALUE_TAP__M 0xFFF
+#define QAM_FQ_CENTTAP_VALUE_TAP__PRE 0x600
+
+#define QAM_FQ_TAP_RE_EL0__A 0x1420020
+#define QAM_FQ_TAP_RE_EL0__W 12
+#define QAM_FQ_TAP_RE_EL0__M 0xFFF
+#define QAM_FQ_TAP_RE_EL0__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL0_TAP__B 0
+#define QAM_FQ_TAP_RE_EL0_TAP__W 12
+#define QAM_FQ_TAP_RE_EL0_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL0_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL0__A 0x1420021
+#define QAM_FQ_TAP_IM_EL0__W 12
+#define QAM_FQ_TAP_IM_EL0__M 0xFFF
+#define QAM_FQ_TAP_IM_EL0__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL0_TAP__B 0
+#define QAM_FQ_TAP_IM_EL0_TAP__W 12
+#define QAM_FQ_TAP_IM_EL0_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL0_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL1__A 0x1420022
+#define QAM_FQ_TAP_RE_EL1__W 12
+#define QAM_FQ_TAP_RE_EL1__M 0xFFF
+#define QAM_FQ_TAP_RE_EL1__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL1_TAP__B 0
+#define QAM_FQ_TAP_RE_EL1_TAP__W 12
+#define QAM_FQ_TAP_RE_EL1_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL1_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL1__A 0x1420023
+#define QAM_FQ_TAP_IM_EL1__W 12
+#define QAM_FQ_TAP_IM_EL1__M 0xFFF
+#define QAM_FQ_TAP_IM_EL1__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL1_TAP__B 0
+#define QAM_FQ_TAP_IM_EL1_TAP__W 12
+#define QAM_FQ_TAP_IM_EL1_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL1_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL2__A 0x1420024
+#define QAM_FQ_TAP_RE_EL2__W 12
+#define QAM_FQ_TAP_RE_EL2__M 0xFFF
+#define QAM_FQ_TAP_RE_EL2__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL2_TAP__B 0
+#define QAM_FQ_TAP_RE_EL2_TAP__W 12
+#define QAM_FQ_TAP_RE_EL2_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL2_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL2__A 0x1420025
+#define QAM_FQ_TAP_IM_EL2__W 12
+#define QAM_FQ_TAP_IM_EL2__M 0xFFF
+#define QAM_FQ_TAP_IM_EL2__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL2_TAP__B 0
+#define QAM_FQ_TAP_IM_EL2_TAP__W 12
+#define QAM_FQ_TAP_IM_EL2_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL2_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL3__A 0x1420026
+#define QAM_FQ_TAP_RE_EL3__W 12
+#define QAM_FQ_TAP_RE_EL3__M 0xFFF
+#define QAM_FQ_TAP_RE_EL3__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL3_TAP__B 0
+#define QAM_FQ_TAP_RE_EL3_TAP__W 12
+#define QAM_FQ_TAP_RE_EL3_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL3_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL3__A 0x1420027
+#define QAM_FQ_TAP_IM_EL3__W 12
+#define QAM_FQ_TAP_IM_EL3__M 0xFFF
+#define QAM_FQ_TAP_IM_EL3__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL3_TAP__B 0
+#define QAM_FQ_TAP_IM_EL3_TAP__W 12
+#define QAM_FQ_TAP_IM_EL3_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL3_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL4__A 0x1420028
+#define QAM_FQ_TAP_RE_EL4__W 12
+#define QAM_FQ_TAP_RE_EL4__M 0xFFF
+#define QAM_FQ_TAP_RE_EL4__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL4_TAP__B 0
+#define QAM_FQ_TAP_RE_EL4_TAP__W 12
+#define QAM_FQ_TAP_RE_EL4_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL4_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL4__A 0x1420029
+#define QAM_FQ_TAP_IM_EL4__W 12
+#define QAM_FQ_TAP_IM_EL4__M 0xFFF
+#define QAM_FQ_TAP_IM_EL4__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL4_TAP__B 0
+#define QAM_FQ_TAP_IM_EL4_TAP__W 12
+#define QAM_FQ_TAP_IM_EL4_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL4_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL5__A 0x142002A
+#define QAM_FQ_TAP_RE_EL5__W 12
+#define QAM_FQ_TAP_RE_EL5__M 0xFFF
+#define QAM_FQ_TAP_RE_EL5__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL5_TAP__B 0
+#define QAM_FQ_TAP_RE_EL5_TAP__W 12
+#define QAM_FQ_TAP_RE_EL5_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL5_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL5__A 0x142002B
+#define QAM_FQ_TAP_IM_EL5__W 12
+#define QAM_FQ_TAP_IM_EL5__M 0xFFF
+#define QAM_FQ_TAP_IM_EL5__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL5_TAP__B 0
+#define QAM_FQ_TAP_IM_EL5_TAP__W 12
+#define QAM_FQ_TAP_IM_EL5_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL5_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL6__A 0x142002C
+#define QAM_FQ_TAP_RE_EL6__W 12
+#define QAM_FQ_TAP_RE_EL6__M 0xFFF
+#define QAM_FQ_TAP_RE_EL6__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL6_TAP__B 0
+#define QAM_FQ_TAP_RE_EL6_TAP__W 12
+#define QAM_FQ_TAP_RE_EL6_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL6_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL6__A 0x142002D
+#define QAM_FQ_TAP_IM_EL6__W 12
+#define QAM_FQ_TAP_IM_EL6__M 0xFFF
+#define QAM_FQ_TAP_IM_EL6__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL6_TAP__B 0
+#define QAM_FQ_TAP_IM_EL6_TAP__W 12
+#define QAM_FQ_TAP_IM_EL6_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL6_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL7__A 0x142002E
+#define QAM_FQ_TAP_RE_EL7__W 12
+#define QAM_FQ_TAP_RE_EL7__M 0xFFF
+#define QAM_FQ_TAP_RE_EL7__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL7_TAP__B 0
+#define QAM_FQ_TAP_RE_EL7_TAP__W 12
+#define QAM_FQ_TAP_RE_EL7_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL7_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL7__A 0x142002F
+#define QAM_FQ_TAP_IM_EL7__W 12
+#define QAM_FQ_TAP_IM_EL7__M 0xFFF
+#define QAM_FQ_TAP_IM_EL7__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL7_TAP__B 0
+#define QAM_FQ_TAP_IM_EL7_TAP__W 12
+#define QAM_FQ_TAP_IM_EL7_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL7_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL8__A 0x1420030
+#define QAM_FQ_TAP_RE_EL8__W 12
+#define QAM_FQ_TAP_RE_EL8__M 0xFFF
+#define QAM_FQ_TAP_RE_EL8__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL8_TAP__B 0
+#define QAM_FQ_TAP_RE_EL8_TAP__W 12
+#define QAM_FQ_TAP_RE_EL8_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL8_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL8__A 0x1420031
+#define QAM_FQ_TAP_IM_EL8__W 12
+#define QAM_FQ_TAP_IM_EL8__M 0xFFF
+#define QAM_FQ_TAP_IM_EL8__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL8_TAP__B 0
+#define QAM_FQ_TAP_IM_EL8_TAP__W 12
+#define QAM_FQ_TAP_IM_EL8_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL8_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL9__A 0x1420032
+#define QAM_FQ_TAP_RE_EL9__W 12
+#define QAM_FQ_TAP_RE_EL9__M 0xFFF
+#define QAM_FQ_TAP_RE_EL9__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL9_TAP__B 0
+#define QAM_FQ_TAP_RE_EL9_TAP__W 12
+#define QAM_FQ_TAP_RE_EL9_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL9_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL9__A 0x1420033
+#define QAM_FQ_TAP_IM_EL9__W 12
+#define QAM_FQ_TAP_IM_EL9__M 0xFFF
+#define QAM_FQ_TAP_IM_EL9__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL9_TAP__B 0
+#define QAM_FQ_TAP_IM_EL9_TAP__W 12
+#define QAM_FQ_TAP_IM_EL9_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL9_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL10__A 0x1420034
+#define QAM_FQ_TAP_RE_EL10__W 12
+#define QAM_FQ_TAP_RE_EL10__M 0xFFF
+#define QAM_FQ_TAP_RE_EL10__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL10_TAP__B 0
+#define QAM_FQ_TAP_RE_EL10_TAP__W 12
+#define QAM_FQ_TAP_RE_EL10_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL10_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL10__A 0x1420035
+#define QAM_FQ_TAP_IM_EL10__W 12
+#define QAM_FQ_TAP_IM_EL10__M 0xFFF
+#define QAM_FQ_TAP_IM_EL10__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL10_TAP__B 0
+#define QAM_FQ_TAP_IM_EL10_TAP__W 12
+#define QAM_FQ_TAP_IM_EL10_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL10_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL11__A 0x1420036
+#define QAM_FQ_TAP_RE_EL11__W 12
+#define QAM_FQ_TAP_RE_EL11__M 0xFFF
+#define QAM_FQ_TAP_RE_EL11__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL11_TAP__B 0
+#define QAM_FQ_TAP_RE_EL11_TAP__W 12
+#define QAM_FQ_TAP_RE_EL11_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL11_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL11__A 0x1420037
+#define QAM_FQ_TAP_IM_EL11__W 12
+#define QAM_FQ_TAP_IM_EL11__M 0xFFF
+#define QAM_FQ_TAP_IM_EL11__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL11_TAP__B 0
+#define QAM_FQ_TAP_IM_EL11_TAP__W 12
+#define QAM_FQ_TAP_IM_EL11_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL11_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL12__A 0x1420038
+#define QAM_FQ_TAP_RE_EL12__W 12
+#define QAM_FQ_TAP_RE_EL12__M 0xFFF
+#define QAM_FQ_TAP_RE_EL12__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL12_TAP__B 0
+#define QAM_FQ_TAP_RE_EL12_TAP__W 12
+#define QAM_FQ_TAP_RE_EL12_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL12_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL12__A 0x1420039
+#define QAM_FQ_TAP_IM_EL12__W 12
+#define QAM_FQ_TAP_IM_EL12__M 0xFFF
+#define QAM_FQ_TAP_IM_EL12__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL12_TAP__B 0
+#define QAM_FQ_TAP_IM_EL12_TAP__W 12
+#define QAM_FQ_TAP_IM_EL12_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL12_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL13__A 0x142003A
+#define QAM_FQ_TAP_RE_EL13__W 12
+#define QAM_FQ_TAP_RE_EL13__M 0xFFF
+#define QAM_FQ_TAP_RE_EL13__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL13_TAP__B 0
+#define QAM_FQ_TAP_RE_EL13_TAP__W 12
+#define QAM_FQ_TAP_RE_EL13_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL13_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL13__A 0x142003B
+#define QAM_FQ_TAP_IM_EL13__W 12
+#define QAM_FQ_TAP_IM_EL13__M 0xFFF
+#define QAM_FQ_TAP_IM_EL13__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL13_TAP__B 0
+#define QAM_FQ_TAP_IM_EL13_TAP__W 12
+#define QAM_FQ_TAP_IM_EL13_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL13_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL14__A 0x142003C
+#define QAM_FQ_TAP_RE_EL14__W 12
+#define QAM_FQ_TAP_RE_EL14__M 0xFFF
+#define QAM_FQ_TAP_RE_EL14__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL14_TAP__B 0
+#define QAM_FQ_TAP_RE_EL14_TAP__W 12
+#define QAM_FQ_TAP_RE_EL14_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL14_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL14__A 0x142003D
+#define QAM_FQ_TAP_IM_EL14__W 12
+#define QAM_FQ_TAP_IM_EL14__M 0xFFF
+#define QAM_FQ_TAP_IM_EL14__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL14_TAP__B 0
+#define QAM_FQ_TAP_IM_EL14_TAP__W 12
+#define QAM_FQ_TAP_IM_EL14_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL14_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL15__A 0x142003E
+#define QAM_FQ_TAP_RE_EL15__W 12
+#define QAM_FQ_TAP_RE_EL15__M 0xFFF
+#define QAM_FQ_TAP_RE_EL15__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL15_TAP__B 0
+#define QAM_FQ_TAP_RE_EL15_TAP__W 12
+#define QAM_FQ_TAP_RE_EL15_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL15_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL15__A 0x142003F
+#define QAM_FQ_TAP_IM_EL15__W 12
+#define QAM_FQ_TAP_IM_EL15__M 0xFFF
+#define QAM_FQ_TAP_IM_EL15__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL15_TAP__B 0
+#define QAM_FQ_TAP_IM_EL15_TAP__W 12
+#define QAM_FQ_TAP_IM_EL15_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL15_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL16__A 0x1420040
+#define QAM_FQ_TAP_RE_EL16__W 12
+#define QAM_FQ_TAP_RE_EL16__M 0xFFF
+#define QAM_FQ_TAP_RE_EL16__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL16_TAP__B 0
+#define QAM_FQ_TAP_RE_EL16_TAP__W 12
+#define QAM_FQ_TAP_RE_EL16_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL16_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL16__A 0x1420041
+#define QAM_FQ_TAP_IM_EL16__W 12
+#define QAM_FQ_TAP_IM_EL16__M 0xFFF
+#define QAM_FQ_TAP_IM_EL16__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL16_TAP__B 0
+#define QAM_FQ_TAP_IM_EL16_TAP__W 12
+#define QAM_FQ_TAP_IM_EL16_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL16_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL17__A 0x1420042
+#define QAM_FQ_TAP_RE_EL17__W 12
+#define QAM_FQ_TAP_RE_EL17__M 0xFFF
+#define QAM_FQ_TAP_RE_EL17__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL17_TAP__B 0
+#define QAM_FQ_TAP_RE_EL17_TAP__W 12
+#define QAM_FQ_TAP_RE_EL17_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL17_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL17__A 0x1420043
+#define QAM_FQ_TAP_IM_EL17__W 12
+#define QAM_FQ_TAP_IM_EL17__M 0xFFF
+#define QAM_FQ_TAP_IM_EL17__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL17_TAP__B 0
+#define QAM_FQ_TAP_IM_EL17_TAP__W 12
+#define QAM_FQ_TAP_IM_EL17_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL17_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL18__A 0x1420044
+#define QAM_FQ_TAP_RE_EL18__W 12
+#define QAM_FQ_TAP_RE_EL18__M 0xFFF
+#define QAM_FQ_TAP_RE_EL18__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL18_TAP__B 0
+#define QAM_FQ_TAP_RE_EL18_TAP__W 12
+#define QAM_FQ_TAP_RE_EL18_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL18_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL18__A 0x1420045
+#define QAM_FQ_TAP_IM_EL18__W 12
+#define QAM_FQ_TAP_IM_EL18__M 0xFFF
+#define QAM_FQ_TAP_IM_EL18__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL18_TAP__B 0
+#define QAM_FQ_TAP_IM_EL18_TAP__W 12
+#define QAM_FQ_TAP_IM_EL18_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL18_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL19__A 0x1420046
+#define QAM_FQ_TAP_RE_EL19__W 12
+#define QAM_FQ_TAP_RE_EL19__M 0xFFF
+#define QAM_FQ_TAP_RE_EL19__PRE 0x600
+
+#define QAM_FQ_TAP_RE_EL19_TAP__B 0
+#define QAM_FQ_TAP_RE_EL19_TAP__W 12
+#define QAM_FQ_TAP_RE_EL19_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL19_TAP__PRE 0x600
+
+#define QAM_FQ_TAP_IM_EL19__A 0x1420047
+#define QAM_FQ_TAP_IM_EL19__W 12
+#define QAM_FQ_TAP_IM_EL19__M 0xFFF
+#define QAM_FQ_TAP_IM_EL19__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL19_TAP__B 0
+#define QAM_FQ_TAP_IM_EL19_TAP__W 12
+#define QAM_FQ_TAP_IM_EL19_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL19_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL20__A 0x1420048
+#define QAM_FQ_TAP_RE_EL20__W 12
+#define QAM_FQ_TAP_RE_EL20__M 0xFFF
+#define QAM_FQ_TAP_RE_EL20__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL20_TAP__B 0
+#define QAM_FQ_TAP_RE_EL20_TAP__W 12
+#define QAM_FQ_TAP_RE_EL20_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL20_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL20__A 0x1420049
+#define QAM_FQ_TAP_IM_EL20__W 12
+#define QAM_FQ_TAP_IM_EL20__M 0xFFF
+#define QAM_FQ_TAP_IM_EL20__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL20_TAP__B 0
+#define QAM_FQ_TAP_IM_EL20_TAP__W 12
+#define QAM_FQ_TAP_IM_EL20_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL20_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL21__A 0x142004A
+#define QAM_FQ_TAP_RE_EL21__W 12
+#define QAM_FQ_TAP_RE_EL21__M 0xFFF
+#define QAM_FQ_TAP_RE_EL21__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL21_TAP__B 0
+#define QAM_FQ_TAP_RE_EL21_TAP__W 12
+#define QAM_FQ_TAP_RE_EL21_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL21_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL21__A 0x142004B
+#define QAM_FQ_TAP_IM_EL21__W 12
+#define QAM_FQ_TAP_IM_EL21__M 0xFFF
+#define QAM_FQ_TAP_IM_EL21__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL21_TAP__B 0
+#define QAM_FQ_TAP_IM_EL21_TAP__W 12
+#define QAM_FQ_TAP_IM_EL21_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL21_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL22__A 0x142004C
+#define QAM_FQ_TAP_RE_EL22__W 12
+#define QAM_FQ_TAP_RE_EL22__M 0xFFF
+#define QAM_FQ_TAP_RE_EL22__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL22_TAP__B 0
+#define QAM_FQ_TAP_RE_EL22_TAP__W 12
+#define QAM_FQ_TAP_RE_EL22_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL22_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL22__A 0x142004D
+#define QAM_FQ_TAP_IM_EL22__W 12
+#define QAM_FQ_TAP_IM_EL22__M 0xFFF
+#define QAM_FQ_TAP_IM_EL22__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL22_TAP__B 0
+#define QAM_FQ_TAP_IM_EL22_TAP__W 12
+#define QAM_FQ_TAP_IM_EL22_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL22_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL23__A 0x142004E
+#define QAM_FQ_TAP_RE_EL23__W 12
+#define QAM_FQ_TAP_RE_EL23__M 0xFFF
+#define QAM_FQ_TAP_RE_EL23__PRE 0x2
+
+#define QAM_FQ_TAP_RE_EL23_TAP__B 0
+#define QAM_FQ_TAP_RE_EL23_TAP__W 12
+#define QAM_FQ_TAP_RE_EL23_TAP__M 0xFFF
+#define QAM_FQ_TAP_RE_EL23_TAP__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL23__A 0x142004F
+#define QAM_FQ_TAP_IM_EL23__W 12
+#define QAM_FQ_TAP_IM_EL23__M 0xFFF
+#define QAM_FQ_TAP_IM_EL23__PRE 0x2
+
+#define QAM_FQ_TAP_IM_EL23_TAP__B 0
+#define QAM_FQ_TAP_IM_EL23_TAP__W 12
+#define QAM_FQ_TAP_IM_EL23_TAP__M 0xFFF
+#define QAM_FQ_TAP_IM_EL23_TAP__PRE 0x2
+
+#define QAM_SL_COMM_EXEC__A 0x1430000
+#define QAM_SL_COMM_EXEC__W 2
+#define QAM_SL_COMM_EXEC__M 0x3
+#define QAM_SL_COMM_EXEC__PRE 0x0
+#define QAM_SL_COMM_EXEC_STOP 0x0
+#define QAM_SL_COMM_EXEC_ACTIVE 0x1
+#define QAM_SL_COMM_EXEC_HOLD 0x2
+
+#define QAM_SL_COMM_MB__A 0x1430002
+#define QAM_SL_COMM_MB__W 4
+#define QAM_SL_COMM_MB__M 0xF
+#define QAM_SL_COMM_MB__PRE 0x0
+#define QAM_SL_COMM_MB_CTL__B 0
+#define QAM_SL_COMM_MB_CTL__W 1
+#define QAM_SL_COMM_MB_CTL__M 0x1
+#define QAM_SL_COMM_MB_CTL__PRE 0x0
+#define QAM_SL_COMM_MB_CTL_OFF 0x0
+#define QAM_SL_COMM_MB_CTL_ON 0x1
+#define QAM_SL_COMM_MB_OBS__B 1
+#define QAM_SL_COMM_MB_OBS__W 1
+#define QAM_SL_COMM_MB_OBS__M 0x2
+#define QAM_SL_COMM_MB_OBS__PRE 0x0
+#define QAM_SL_COMM_MB_OBS_OFF 0x0
+#define QAM_SL_COMM_MB_OBS_ON 0x2
+#define QAM_SL_COMM_MB_MUX_OBS__B 2
+#define QAM_SL_COMM_MB_MUX_OBS__W 2
+#define QAM_SL_COMM_MB_MUX_OBS__M 0xC
+#define QAM_SL_COMM_MB_MUX_OBS__PRE 0x0
+#define QAM_SL_COMM_MB_MUX_OBS_CONST_CORR 0x0
+#define QAM_SL_COMM_MB_MUX_OBS_CONST2LC_O 0x4
+#define QAM_SL_COMM_MB_MUX_OBS_CONST2DQ_O 0x8
+#define QAM_SL_COMM_MB_MUX_OBS_VDEC_O 0xC
+
+#define QAM_SL_COMM_INT_REQ__A 0x1430003
+#define QAM_SL_COMM_INT_REQ__W 1
+#define QAM_SL_COMM_INT_REQ__M 0x1
+#define QAM_SL_COMM_INT_REQ__PRE 0x0
+#define QAM_SL_COMM_INT_STA__A 0x1430005
+#define QAM_SL_COMM_INT_STA__W 2
+#define QAM_SL_COMM_INT_STA__M 0x3
+#define QAM_SL_COMM_INT_STA__PRE 0x0
+
+#define QAM_SL_COMM_INT_STA_MED_ERR_INT__B 0
+#define QAM_SL_COMM_INT_STA_MED_ERR_INT__W 1
+#define QAM_SL_COMM_INT_STA_MED_ERR_INT__M 0x1
+#define QAM_SL_COMM_INT_STA_MED_ERR_INT__PRE 0x0
+
+#define QAM_SL_COMM_INT_STA_MER_INT__B 1
+#define QAM_SL_COMM_INT_STA_MER_INT__W 1
+#define QAM_SL_COMM_INT_STA_MER_INT__M 0x2
+#define QAM_SL_COMM_INT_STA_MER_INT__PRE 0x0
+
+#define QAM_SL_COMM_INT_MSK__A 0x1430006
+#define QAM_SL_COMM_INT_MSK__W 2
+#define QAM_SL_COMM_INT_MSK__M 0x3
+#define QAM_SL_COMM_INT_MSK__PRE 0x0
+#define QAM_SL_COMM_INT_MSK_MED_ERR_MSK__B 0
+#define QAM_SL_COMM_INT_MSK_MED_ERR_MSK__W 1
+#define QAM_SL_COMM_INT_MSK_MED_ERR_MSK__M 0x1
+#define QAM_SL_COMM_INT_MSK_MED_ERR_MSK__PRE 0x0
+#define QAM_SL_COMM_INT_MSK_MER_MSK__B 1
+#define QAM_SL_COMM_INT_MSK_MER_MSK__W 1
+#define QAM_SL_COMM_INT_MSK_MER_MSK__M 0x2
+#define QAM_SL_COMM_INT_MSK_MER_MSK__PRE 0x0
+
+#define QAM_SL_COMM_INT_STM__A 0x1430007
+#define QAM_SL_COMM_INT_STM__W 2
+#define QAM_SL_COMM_INT_STM__M 0x3
+#define QAM_SL_COMM_INT_STM__PRE 0x0
+#define QAM_SL_COMM_INT_STM_MED_ERR_STM__B 0
+#define QAM_SL_COMM_INT_STM_MED_ERR_STM__W 1
+#define QAM_SL_COMM_INT_STM_MED_ERR_STM__M 0x1
+#define QAM_SL_COMM_INT_STM_MED_ERR_STM__PRE 0x0
+#define QAM_SL_COMM_INT_STM_MER_STM__B 1
+#define QAM_SL_COMM_INT_STM_MER_STM__W 1
+#define QAM_SL_COMM_INT_STM_MER_STM__M 0x2
+#define QAM_SL_COMM_INT_STM_MER_STM__PRE 0x0
+
+#define QAM_SL_MODE__A 0x1430010
+#define QAM_SL_MODE__W 11
+#define QAM_SL_MODE__M 0x7FF
+#define QAM_SL_MODE__PRE 0x0
+
+#define QAM_SL_MODE_SLICER4LC__B 0
+#define QAM_SL_MODE_SLICER4LC__W 2
+#define QAM_SL_MODE_SLICER4LC__M 0x3
+#define QAM_SL_MODE_SLICER4LC__PRE 0x0
+#define QAM_SL_MODE_SLICER4LC_RECT 0x0
+#define QAM_SL_MODE_SLICER4LC_ONET 0x1
+#define QAM_SL_MODE_SLICER4LC_RAD 0x2
+
+#define QAM_SL_MODE_SLICER4DQ__B 2
+#define QAM_SL_MODE_SLICER4DQ__W 2
+#define QAM_SL_MODE_SLICER4DQ__M 0xC
+#define QAM_SL_MODE_SLICER4DQ__PRE 0x0
+#define QAM_SL_MODE_SLICER4DQ_RECT 0x0
+#define QAM_SL_MODE_SLICER4DQ_ONET 0x4
+#define QAM_SL_MODE_SLICER4DQ_RAD 0x8
+
+#define QAM_SL_MODE_SLICER4VD__B 4
+#define QAM_SL_MODE_SLICER4VD__W 2
+#define QAM_SL_MODE_SLICER4VD__M 0x30
+#define QAM_SL_MODE_SLICER4VD__PRE 0x0
+#define QAM_SL_MODE_SLICER4VD_RECT 0x0
+#define QAM_SL_MODE_SLICER4VD_ONET 0x10
+#define QAM_SL_MODE_SLICER4VD_RAD 0x20
+
+#define QAM_SL_MODE_ROT_DIS__B 6
+#define QAM_SL_MODE_ROT_DIS__W 1
+#define QAM_SL_MODE_ROT_DIS__M 0x40
+#define QAM_SL_MODE_ROT_DIS__PRE 0x0
+
+#define QAM_SL_MODE_DQROT_DIS__B 7
+#define QAM_SL_MODE_DQROT_DIS__W 1
+#define QAM_SL_MODE_DQROT_DIS__M 0x80
+#define QAM_SL_MODE_DQROT_DIS__PRE 0x0
+
+#define QAM_SL_MODE_DFE_DIS__B 8
+#define QAM_SL_MODE_DFE_DIS__W 1
+#define QAM_SL_MODE_DFE_DIS__M 0x100
+#define QAM_SL_MODE_DFE_DIS__PRE 0x0
+
+#define QAM_SL_MODE_RADIUS_MIX__B 9
+#define QAM_SL_MODE_RADIUS_MIX__W 1
+#define QAM_SL_MODE_RADIUS_MIX__M 0x200
+#define QAM_SL_MODE_RADIUS_MIX__PRE 0x0
+
+#define QAM_SL_MODE_TILT_COMP__B 10
+#define QAM_SL_MODE_TILT_COMP__W 1
+#define QAM_SL_MODE_TILT_COMP__M 0x400
+#define QAM_SL_MODE_TILT_COMP__PRE 0x0
+
+#define QAM_SL_K_FACTOR__A 0x1430011
+#define QAM_SL_K_FACTOR__W 4
+#define QAM_SL_K_FACTOR__M 0xF
+#define QAM_SL_K_FACTOR__PRE 0x0
+#define QAM_SL_MEDIAN__A 0x1430012
+#define QAM_SL_MEDIAN__W 14
+#define QAM_SL_MEDIAN__M 0x3FFF
+#define QAM_SL_MEDIAN__PRE 0x0
+
+#define QAM_SL_MEDIAN_LENGTH__B 0
+#define QAM_SL_MEDIAN_LENGTH__W 2
+#define QAM_SL_MEDIAN_LENGTH__M 0x3
+#define QAM_SL_MEDIAN_LENGTH__PRE 0x0
+
+#define QAM_SL_MEDIAN_CORRECT__B 2
+#define QAM_SL_MEDIAN_CORRECT__W 4
+#define QAM_SL_MEDIAN_CORRECT__M 0x3C
+#define QAM_SL_MEDIAN_CORRECT__PRE 0x0
+
+#define QAM_SL_MEDIAN_TOLERANCE__B 6
+#define QAM_SL_MEDIAN_TOLERANCE__W 7
+#define QAM_SL_MEDIAN_TOLERANCE__M 0x1FC0
+#define QAM_SL_MEDIAN_TOLERANCE__PRE 0x0
+
+#define QAM_SL_MEDIAN_FAST__B 13
+#define QAM_SL_MEDIAN_FAST__W 1
+#define QAM_SL_MEDIAN_FAST__M 0x2000
+#define QAM_SL_MEDIAN_FAST__PRE 0x0
+
+#define QAM_SL_ALPHA__A 0x1430013
+#define QAM_SL_ALPHA__W 3
+#define QAM_SL_ALPHA__M 0x7
+#define QAM_SL_ALPHA__PRE 0x0
+
+#define QAM_SL_PHASELIMIT__A 0x1430014
+#define QAM_SL_PHASELIMIT__W 9
+#define QAM_SL_PHASELIMIT__M 0x1FF
+#define QAM_SL_PHASELIMIT__PRE 0x0
+#define QAM_SL_MTA_LENGTH__A 0x1430015
+#define QAM_SL_MTA_LENGTH__W 2
+#define QAM_SL_MTA_LENGTH__M 0x3
+#define QAM_SL_MTA_LENGTH__PRE 0x1
+
+#define QAM_SL_MTA_LENGTH_LENGTH__B 0
+#define QAM_SL_MTA_LENGTH_LENGTH__W 2
+#define QAM_SL_MTA_LENGTH_LENGTH__M 0x3
+#define QAM_SL_MTA_LENGTH_LENGTH__PRE 0x1
+
+#define QAM_SL_MEDIAN_ERROR__A 0x1430016
+#define QAM_SL_MEDIAN_ERROR__W 10
+#define QAM_SL_MEDIAN_ERROR__M 0x3FF
+#define QAM_SL_MEDIAN_ERROR__PRE 0x0
+
+#define QAM_SL_MEDIAN_ERROR_MEDIAN_ERR__B 0
+#define QAM_SL_MEDIAN_ERROR_MEDIAN_ERR__W 10
+#define QAM_SL_MEDIAN_ERROR_MEDIAN_ERR__M 0x3FF
+#define QAM_SL_MEDIAN_ERROR_MEDIAN_ERR__PRE 0x0
+
+#define QAM_SL_ERR_POWER__A 0x1430017
+#define QAM_SL_ERR_POWER__W 16
+#define QAM_SL_ERR_POWER__M 0xFFFF
+#define QAM_SL_ERR_POWER__PRE 0x0
+
+#define QAM_DQ_COMM_EXEC__A 0x1440000
+#define QAM_DQ_COMM_EXEC__W 2
+#define QAM_DQ_COMM_EXEC__M 0x3
+#define QAM_DQ_COMM_EXEC__PRE 0x0
+#define QAM_DQ_COMM_EXEC_STOP 0x0
+#define QAM_DQ_COMM_EXEC_ACTIVE 0x1
+#define QAM_DQ_COMM_EXEC_HOLD 0x2
+
+#define QAM_DQ_MODE__A 0x1440010
+#define QAM_DQ_MODE__W 5
+#define QAM_DQ_MODE__M 0x1F
+#define QAM_DQ_MODE__PRE 0x0
+
+#define QAM_DQ_MODE_TAPRESET__B 0
+#define QAM_DQ_MODE_TAPRESET__W 1
+#define QAM_DQ_MODE_TAPRESET__M 0x1
+#define QAM_DQ_MODE_TAPRESET__PRE 0x0
+#define QAM_DQ_MODE_TAPRESET_RST 0x1
+
+#define QAM_DQ_MODE_TAPLMS__B 1
+#define QAM_DQ_MODE_TAPLMS__W 1
+#define QAM_DQ_MODE_TAPLMS__M 0x2
+#define QAM_DQ_MODE_TAPLMS__PRE 0x0
+#define QAM_DQ_MODE_TAPLMS_UPD 0x2
+
+#define QAM_DQ_MODE_TAPDRAIN__B 2
+#define QAM_DQ_MODE_TAPDRAIN__W 1
+#define QAM_DQ_MODE_TAPDRAIN__M 0x4
+#define QAM_DQ_MODE_TAPDRAIN__PRE 0x0
+#define QAM_DQ_MODE_TAPDRAIN_DRAIN 0x4
+
+#define QAM_DQ_MODE_FB__B 3
+#define QAM_DQ_MODE_FB__W 2
+#define QAM_DQ_MODE_FB__M 0x18
+#define QAM_DQ_MODE_FB__PRE 0x0
+#define QAM_DQ_MODE_FB_CMA 0x0
+#define QAM_DQ_MODE_FB_RADIUS 0x8
+#define QAM_DQ_MODE_FB_DFB 0x10
+#define QAM_DQ_MODE_FB_TRELLIS 0x18
+
+#define QAM_DQ_MU_FACTOR__A 0x1440011
+#define QAM_DQ_MU_FACTOR__W 3
+#define QAM_DQ_MU_FACTOR__M 0x7
+#define QAM_DQ_MU_FACTOR__PRE 0x0
+
+#define QAM_DQ_LA_FACTOR__A 0x1440012
+#define QAM_DQ_LA_FACTOR__W 4
+#define QAM_DQ_LA_FACTOR__M 0xF
+#define QAM_DQ_LA_FACTOR__PRE 0xC
+
+#define QAM_DQ_CMA_RATIO__A 0x1440013
+#define QAM_DQ_CMA_RATIO__W 14
+#define QAM_DQ_CMA_RATIO__M 0x3FFF
+#define QAM_DQ_CMA_RATIO__PRE 0x3CF9
+#define QAM_DQ_CMA_RATIO_QPSK 0x2000
+#define QAM_DQ_CMA_RATIO_QAM16 0x34CD
+#define QAM_DQ_CMA_RATIO_QAM64 0x3A00
+#define QAM_DQ_CMA_RATIO_QAM256 0x3B4D
+#define QAM_DQ_CMA_RATIO_QAM1024 0x3BA0
+
+#define QAM_DQ_QUAL_RADSEL__A 0x1440014
+#define QAM_DQ_QUAL_RADSEL__W 3
+#define QAM_DQ_QUAL_RADSEL__M 0x7
+#define QAM_DQ_QUAL_RADSEL__PRE 0x0
+
+#define QAM_DQ_QUAL_RADSEL_BIT__B 0
+#define QAM_DQ_QUAL_RADSEL_BIT__W 3
+#define QAM_DQ_QUAL_RADSEL_BIT__M 0x7
+#define QAM_DQ_QUAL_RADSEL_BIT__PRE 0x0
+#define QAM_DQ_QUAL_RADSEL_BIT_PURE_RADIUS 0x0
+#define QAM_DQ_QUAL_RADSEL_BIT_PURE_CMA 0x6
+
+#define QAM_DQ_QUAL_ENA__A 0x1440015
+#define QAM_DQ_QUAL_ENA__W 1
+#define QAM_DQ_QUAL_ENA__M 0x1
+#define QAM_DQ_QUAL_ENA__PRE 0x0
+
+#define QAM_DQ_QUAL_ENA_ENA__B 0
+#define QAM_DQ_QUAL_ENA_ENA__W 1
+#define QAM_DQ_QUAL_ENA_ENA__M 0x1
+#define QAM_DQ_QUAL_ENA_ENA__PRE 0x0
+#define QAM_DQ_QUAL_ENA_ENA_QUAL_WEIGHTING 0x1
+
+#define QAM_DQ_QUAL_FUN0__A 0x1440018
+#define QAM_DQ_QUAL_FUN0__W 6
+#define QAM_DQ_QUAL_FUN0__M 0x3F
+#define QAM_DQ_QUAL_FUN0__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN0_BIT__B 0
+#define QAM_DQ_QUAL_FUN0_BIT__W 6
+#define QAM_DQ_QUAL_FUN0_BIT__M 0x3F
+#define QAM_DQ_QUAL_FUN0_BIT__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN1__A 0x1440019
+#define QAM_DQ_QUAL_FUN1__W 6
+#define QAM_DQ_QUAL_FUN1__M 0x3F
+#define QAM_DQ_QUAL_FUN1__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN1_BIT__B 0
+#define QAM_DQ_QUAL_FUN1_BIT__W 6
+#define QAM_DQ_QUAL_FUN1_BIT__M 0x3F
+#define QAM_DQ_QUAL_FUN1_BIT__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN2__A 0x144001A
+#define QAM_DQ_QUAL_FUN2__W 6
+#define QAM_DQ_QUAL_FUN2__M 0x3F
+#define QAM_DQ_QUAL_FUN2__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN2_BIT__B 0
+#define QAM_DQ_QUAL_FUN2_BIT__W 6
+#define QAM_DQ_QUAL_FUN2_BIT__M 0x3F
+#define QAM_DQ_QUAL_FUN2_BIT__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN3__A 0x144001B
+#define QAM_DQ_QUAL_FUN3__W 6
+#define QAM_DQ_QUAL_FUN3__M 0x3F
+#define QAM_DQ_QUAL_FUN3__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN3_BIT__B 0
+#define QAM_DQ_QUAL_FUN3_BIT__W 6
+#define QAM_DQ_QUAL_FUN3_BIT__M 0x3F
+#define QAM_DQ_QUAL_FUN3_BIT__PRE 0x4
+
+#define QAM_DQ_QUAL_FUN4__A 0x144001C
+#define QAM_DQ_QUAL_FUN4__W 6
+#define QAM_DQ_QUAL_FUN4__M 0x3F
+#define QAM_DQ_QUAL_FUN4__PRE 0x6
+
+#define QAM_DQ_QUAL_FUN4_BIT__B 0
+#define QAM_DQ_QUAL_FUN4_BIT__W 6
+#define QAM_DQ_QUAL_FUN4_BIT__M 0x3F
+#define QAM_DQ_QUAL_FUN4_BIT__PRE 0x6
+
+#define QAM_DQ_QUAL_FUN5__A 0x144001D
+#define QAM_DQ_QUAL_FUN5__W 6
+#define QAM_DQ_QUAL_FUN5__M 0x3F
+#define QAM_DQ_QUAL_FUN5__PRE 0x6
+
+#define QAM_DQ_QUAL_FUN5_BIT__B 0
+#define QAM_DQ_QUAL_FUN5_BIT__W 6
+#define QAM_DQ_QUAL_FUN5_BIT__M 0x3F
+#define QAM_DQ_QUAL_FUN5_BIT__PRE 0x6
+
+#define QAM_DQ_RAW_LIM__A 0x144001E
+#define QAM_DQ_RAW_LIM__W 5
+#define QAM_DQ_RAW_LIM__M 0x1F
+#define QAM_DQ_RAW_LIM__PRE 0x1F
+
+#define QAM_DQ_RAW_LIM_BIT__B 0
+#define QAM_DQ_RAW_LIM_BIT__W 5
+#define QAM_DQ_RAW_LIM_BIT__M 0x1F
+#define QAM_DQ_RAW_LIM_BIT__PRE 0x1F
+
+#define QAM_DQ_TAP_RE_EL0__A 0x1440020
+#define QAM_DQ_TAP_RE_EL0__W 12
+#define QAM_DQ_TAP_RE_EL0__M 0xFFF
+#define QAM_DQ_TAP_RE_EL0__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL0_TAP__B 0
+#define QAM_DQ_TAP_RE_EL0_TAP__W 12
+#define QAM_DQ_TAP_RE_EL0_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL0_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL0__A 0x1440021
+#define QAM_DQ_TAP_IM_EL0__W 12
+#define QAM_DQ_TAP_IM_EL0__M 0xFFF
+#define QAM_DQ_TAP_IM_EL0__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL0_TAP__B 0
+#define QAM_DQ_TAP_IM_EL0_TAP__W 12
+#define QAM_DQ_TAP_IM_EL0_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL0_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL1__A 0x1440022
+#define QAM_DQ_TAP_RE_EL1__W 12
+#define QAM_DQ_TAP_RE_EL1__M 0xFFF
+#define QAM_DQ_TAP_RE_EL1__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL1_TAP__B 0
+#define QAM_DQ_TAP_RE_EL1_TAP__W 12
+#define QAM_DQ_TAP_RE_EL1_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL1_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL1__A 0x1440023
+#define QAM_DQ_TAP_IM_EL1__W 12
+#define QAM_DQ_TAP_IM_EL1__M 0xFFF
+#define QAM_DQ_TAP_IM_EL1__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL1_TAP__B 0
+#define QAM_DQ_TAP_IM_EL1_TAP__W 12
+#define QAM_DQ_TAP_IM_EL1_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL1_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL2__A 0x1440024
+#define QAM_DQ_TAP_RE_EL2__W 12
+#define QAM_DQ_TAP_RE_EL2__M 0xFFF
+#define QAM_DQ_TAP_RE_EL2__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL2_TAP__B 0
+#define QAM_DQ_TAP_RE_EL2_TAP__W 12
+#define QAM_DQ_TAP_RE_EL2_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL2_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL2__A 0x1440025
+#define QAM_DQ_TAP_IM_EL2__W 12
+#define QAM_DQ_TAP_IM_EL2__M 0xFFF
+#define QAM_DQ_TAP_IM_EL2__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL2_TAP__B 0
+#define QAM_DQ_TAP_IM_EL2_TAP__W 12
+#define QAM_DQ_TAP_IM_EL2_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL2_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL3__A 0x1440026
+#define QAM_DQ_TAP_RE_EL3__W 12
+#define QAM_DQ_TAP_RE_EL3__M 0xFFF
+#define QAM_DQ_TAP_RE_EL3__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL3_TAP__B 0
+#define QAM_DQ_TAP_RE_EL3_TAP__W 12
+#define QAM_DQ_TAP_RE_EL3_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL3_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL3__A 0x1440027
+#define QAM_DQ_TAP_IM_EL3__W 12
+#define QAM_DQ_TAP_IM_EL3__M 0xFFF
+#define QAM_DQ_TAP_IM_EL3__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL3_TAP__B 0
+#define QAM_DQ_TAP_IM_EL3_TAP__W 12
+#define QAM_DQ_TAP_IM_EL3_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL3_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL4__A 0x1440028
+#define QAM_DQ_TAP_RE_EL4__W 12
+#define QAM_DQ_TAP_RE_EL4__M 0xFFF
+#define QAM_DQ_TAP_RE_EL4__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL4_TAP__B 0
+#define QAM_DQ_TAP_RE_EL4_TAP__W 12
+#define QAM_DQ_TAP_RE_EL4_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL4_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL4__A 0x1440029
+#define QAM_DQ_TAP_IM_EL4__W 12
+#define QAM_DQ_TAP_IM_EL4__M 0xFFF
+#define QAM_DQ_TAP_IM_EL4__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL4_TAP__B 0
+#define QAM_DQ_TAP_IM_EL4_TAP__W 12
+#define QAM_DQ_TAP_IM_EL4_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL4_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL5__A 0x144002A
+#define QAM_DQ_TAP_RE_EL5__W 12
+#define QAM_DQ_TAP_RE_EL5__M 0xFFF
+#define QAM_DQ_TAP_RE_EL5__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL5_TAP__B 0
+#define QAM_DQ_TAP_RE_EL5_TAP__W 12
+#define QAM_DQ_TAP_RE_EL5_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL5_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL5__A 0x144002B
+#define QAM_DQ_TAP_IM_EL5__W 12
+#define QAM_DQ_TAP_IM_EL5__M 0xFFF
+#define QAM_DQ_TAP_IM_EL5__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL5_TAP__B 0
+#define QAM_DQ_TAP_IM_EL5_TAP__W 12
+#define QAM_DQ_TAP_IM_EL5_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL5_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL6__A 0x144002C
+#define QAM_DQ_TAP_RE_EL6__W 12
+#define QAM_DQ_TAP_RE_EL6__M 0xFFF
+#define QAM_DQ_TAP_RE_EL6__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL6_TAP__B 0
+#define QAM_DQ_TAP_RE_EL6_TAP__W 12
+#define QAM_DQ_TAP_RE_EL6_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL6_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL6__A 0x144002D
+#define QAM_DQ_TAP_IM_EL6__W 12
+#define QAM_DQ_TAP_IM_EL6__M 0xFFF
+#define QAM_DQ_TAP_IM_EL6__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL6_TAP__B 0
+#define QAM_DQ_TAP_IM_EL6_TAP__W 12
+#define QAM_DQ_TAP_IM_EL6_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL6_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL7__A 0x144002E
+#define QAM_DQ_TAP_RE_EL7__W 12
+#define QAM_DQ_TAP_RE_EL7__M 0xFFF
+#define QAM_DQ_TAP_RE_EL7__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL7_TAP__B 0
+#define QAM_DQ_TAP_RE_EL7_TAP__W 12
+#define QAM_DQ_TAP_RE_EL7_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL7_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL7__A 0x144002F
+#define QAM_DQ_TAP_IM_EL7__W 12
+#define QAM_DQ_TAP_IM_EL7__M 0xFFF
+#define QAM_DQ_TAP_IM_EL7__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL7_TAP__B 0
+#define QAM_DQ_TAP_IM_EL7_TAP__W 12
+#define QAM_DQ_TAP_IM_EL7_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL7_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL8__A 0x1440030
+#define QAM_DQ_TAP_RE_EL8__W 12
+#define QAM_DQ_TAP_RE_EL8__M 0xFFF
+#define QAM_DQ_TAP_RE_EL8__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL8_TAP__B 0
+#define QAM_DQ_TAP_RE_EL8_TAP__W 12
+#define QAM_DQ_TAP_RE_EL8_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL8_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL8__A 0x1440031
+#define QAM_DQ_TAP_IM_EL8__W 12
+#define QAM_DQ_TAP_IM_EL8__M 0xFFF
+#define QAM_DQ_TAP_IM_EL8__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL8_TAP__B 0
+#define QAM_DQ_TAP_IM_EL8_TAP__W 12
+#define QAM_DQ_TAP_IM_EL8_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL8_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL9__A 0x1440032
+#define QAM_DQ_TAP_RE_EL9__W 12
+#define QAM_DQ_TAP_RE_EL9__M 0xFFF
+#define QAM_DQ_TAP_RE_EL9__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL9_TAP__B 0
+#define QAM_DQ_TAP_RE_EL9_TAP__W 12
+#define QAM_DQ_TAP_RE_EL9_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL9_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL9__A 0x1440033
+#define QAM_DQ_TAP_IM_EL9__W 12
+#define QAM_DQ_TAP_IM_EL9__M 0xFFF
+#define QAM_DQ_TAP_IM_EL9__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL9_TAP__B 0
+#define QAM_DQ_TAP_IM_EL9_TAP__W 12
+#define QAM_DQ_TAP_IM_EL9_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL9_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL10__A 0x1440034
+#define QAM_DQ_TAP_RE_EL10__W 12
+#define QAM_DQ_TAP_RE_EL10__M 0xFFF
+#define QAM_DQ_TAP_RE_EL10__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL10_TAP__B 0
+#define QAM_DQ_TAP_RE_EL10_TAP__W 12
+#define QAM_DQ_TAP_RE_EL10_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL10_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL10__A 0x1440035
+#define QAM_DQ_TAP_IM_EL10__W 12
+#define QAM_DQ_TAP_IM_EL10__M 0xFFF
+#define QAM_DQ_TAP_IM_EL10__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL10_TAP__B 0
+#define QAM_DQ_TAP_IM_EL10_TAP__W 12
+#define QAM_DQ_TAP_IM_EL10_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL10_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL11__A 0x1440036
+#define QAM_DQ_TAP_RE_EL11__W 12
+#define QAM_DQ_TAP_RE_EL11__M 0xFFF
+#define QAM_DQ_TAP_RE_EL11__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL11_TAP__B 0
+#define QAM_DQ_TAP_RE_EL11_TAP__W 12
+#define QAM_DQ_TAP_RE_EL11_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL11_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL11__A 0x1440037
+#define QAM_DQ_TAP_IM_EL11__W 12
+#define QAM_DQ_TAP_IM_EL11__M 0xFFF
+#define QAM_DQ_TAP_IM_EL11__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL11_TAP__B 0
+#define QAM_DQ_TAP_IM_EL11_TAP__W 12
+#define QAM_DQ_TAP_IM_EL11_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL11_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL12__A 0x1440038
+#define QAM_DQ_TAP_RE_EL12__W 12
+#define QAM_DQ_TAP_RE_EL12__M 0xFFF
+#define QAM_DQ_TAP_RE_EL12__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL12_TAP__B 0
+#define QAM_DQ_TAP_RE_EL12_TAP__W 12
+#define QAM_DQ_TAP_RE_EL12_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL12_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL12__A 0x1440039
+#define QAM_DQ_TAP_IM_EL12__W 12
+#define QAM_DQ_TAP_IM_EL12__M 0xFFF
+#define QAM_DQ_TAP_IM_EL12__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL12_TAP__B 0
+#define QAM_DQ_TAP_IM_EL12_TAP__W 12
+#define QAM_DQ_TAP_IM_EL12_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL12_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL13__A 0x144003A
+#define QAM_DQ_TAP_RE_EL13__W 12
+#define QAM_DQ_TAP_RE_EL13__M 0xFFF
+#define QAM_DQ_TAP_RE_EL13__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL13_TAP__B 0
+#define QAM_DQ_TAP_RE_EL13_TAP__W 12
+#define QAM_DQ_TAP_RE_EL13_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL13_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL13__A 0x144003B
+#define QAM_DQ_TAP_IM_EL13__W 12
+#define QAM_DQ_TAP_IM_EL13__M 0xFFF
+#define QAM_DQ_TAP_IM_EL13__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL13_TAP__B 0
+#define QAM_DQ_TAP_IM_EL13_TAP__W 12
+#define QAM_DQ_TAP_IM_EL13_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL13_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL14__A 0x144003C
+#define QAM_DQ_TAP_RE_EL14__W 12
+#define QAM_DQ_TAP_RE_EL14__M 0xFFF
+#define QAM_DQ_TAP_RE_EL14__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL14_TAP__B 0
+#define QAM_DQ_TAP_RE_EL14_TAP__W 12
+#define QAM_DQ_TAP_RE_EL14_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL14_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL14__A 0x144003D
+#define QAM_DQ_TAP_IM_EL14__W 12
+#define QAM_DQ_TAP_IM_EL14__M 0xFFF
+#define QAM_DQ_TAP_IM_EL14__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL14_TAP__B 0
+#define QAM_DQ_TAP_IM_EL14_TAP__W 12
+#define QAM_DQ_TAP_IM_EL14_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL14_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL15__A 0x144003E
+#define QAM_DQ_TAP_RE_EL15__W 12
+#define QAM_DQ_TAP_RE_EL15__M 0xFFF
+#define QAM_DQ_TAP_RE_EL15__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL15_TAP__B 0
+#define QAM_DQ_TAP_RE_EL15_TAP__W 12
+#define QAM_DQ_TAP_RE_EL15_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL15_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL15__A 0x144003F
+#define QAM_DQ_TAP_IM_EL15__W 12
+#define QAM_DQ_TAP_IM_EL15__M 0xFFF
+#define QAM_DQ_TAP_IM_EL15__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL15_TAP__B 0
+#define QAM_DQ_TAP_IM_EL15_TAP__W 12
+#define QAM_DQ_TAP_IM_EL15_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL15_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL16__A 0x1440040
+#define QAM_DQ_TAP_RE_EL16__W 12
+#define QAM_DQ_TAP_RE_EL16__M 0xFFF
+#define QAM_DQ_TAP_RE_EL16__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL16_TAP__B 0
+#define QAM_DQ_TAP_RE_EL16_TAP__W 12
+#define QAM_DQ_TAP_RE_EL16_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL16_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL16__A 0x1440041
+#define QAM_DQ_TAP_IM_EL16__W 12
+#define QAM_DQ_TAP_IM_EL16__M 0xFFF
+#define QAM_DQ_TAP_IM_EL16__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL16_TAP__B 0
+#define QAM_DQ_TAP_IM_EL16_TAP__W 12
+#define QAM_DQ_TAP_IM_EL16_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL16_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL17__A 0x1440042
+#define QAM_DQ_TAP_RE_EL17__W 12
+#define QAM_DQ_TAP_RE_EL17__M 0xFFF
+#define QAM_DQ_TAP_RE_EL17__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL17_TAP__B 0
+#define QAM_DQ_TAP_RE_EL17_TAP__W 12
+#define QAM_DQ_TAP_RE_EL17_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL17_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL17__A 0x1440043
+#define QAM_DQ_TAP_IM_EL17__W 12
+#define QAM_DQ_TAP_IM_EL17__M 0xFFF
+#define QAM_DQ_TAP_IM_EL17__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL17_TAP__B 0
+#define QAM_DQ_TAP_IM_EL17_TAP__W 12
+#define QAM_DQ_TAP_IM_EL17_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL17_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL18__A 0x1440044
+#define QAM_DQ_TAP_RE_EL18__W 12
+#define QAM_DQ_TAP_RE_EL18__M 0xFFF
+#define QAM_DQ_TAP_RE_EL18__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL18_TAP__B 0
+#define QAM_DQ_TAP_RE_EL18_TAP__W 12
+#define QAM_DQ_TAP_RE_EL18_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL18_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL18__A 0x1440045
+#define QAM_DQ_TAP_IM_EL18__W 12
+#define QAM_DQ_TAP_IM_EL18__M 0xFFF
+#define QAM_DQ_TAP_IM_EL18__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL18_TAP__B 0
+#define QAM_DQ_TAP_IM_EL18_TAP__W 12
+#define QAM_DQ_TAP_IM_EL18_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL18_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL19__A 0x1440046
+#define QAM_DQ_TAP_RE_EL19__W 12
+#define QAM_DQ_TAP_RE_EL19__M 0xFFF
+#define QAM_DQ_TAP_RE_EL19__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL19_TAP__B 0
+#define QAM_DQ_TAP_RE_EL19_TAP__W 12
+#define QAM_DQ_TAP_RE_EL19_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL19_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL19__A 0x1440047
+#define QAM_DQ_TAP_IM_EL19__W 12
+#define QAM_DQ_TAP_IM_EL19__M 0xFFF
+#define QAM_DQ_TAP_IM_EL19__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL19_TAP__B 0
+#define QAM_DQ_TAP_IM_EL19_TAP__W 12
+#define QAM_DQ_TAP_IM_EL19_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL19_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL20__A 0x1440048
+#define QAM_DQ_TAP_RE_EL20__W 12
+#define QAM_DQ_TAP_RE_EL20__M 0xFFF
+#define QAM_DQ_TAP_RE_EL20__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL20_TAP__B 0
+#define QAM_DQ_TAP_RE_EL20_TAP__W 12
+#define QAM_DQ_TAP_RE_EL20_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL20_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL20__A 0x1440049
+#define QAM_DQ_TAP_IM_EL20__W 12
+#define QAM_DQ_TAP_IM_EL20__M 0xFFF
+#define QAM_DQ_TAP_IM_EL20__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL20_TAP__B 0
+#define QAM_DQ_TAP_IM_EL20_TAP__W 12
+#define QAM_DQ_TAP_IM_EL20_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL20_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL21__A 0x144004A
+#define QAM_DQ_TAP_RE_EL21__W 12
+#define QAM_DQ_TAP_RE_EL21__M 0xFFF
+#define QAM_DQ_TAP_RE_EL21__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL21_TAP__B 0
+#define QAM_DQ_TAP_RE_EL21_TAP__W 12
+#define QAM_DQ_TAP_RE_EL21_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL21_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL21__A 0x144004B
+#define QAM_DQ_TAP_IM_EL21__W 12
+#define QAM_DQ_TAP_IM_EL21__M 0xFFF
+#define QAM_DQ_TAP_IM_EL21__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL21_TAP__B 0
+#define QAM_DQ_TAP_IM_EL21_TAP__W 12
+#define QAM_DQ_TAP_IM_EL21_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL21_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL22__A 0x144004C
+#define QAM_DQ_TAP_RE_EL22__W 12
+#define QAM_DQ_TAP_RE_EL22__M 0xFFF
+#define QAM_DQ_TAP_RE_EL22__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL22_TAP__B 0
+#define QAM_DQ_TAP_RE_EL22_TAP__W 12
+#define QAM_DQ_TAP_RE_EL22_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL22_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL22__A 0x144004D
+#define QAM_DQ_TAP_IM_EL22__W 12
+#define QAM_DQ_TAP_IM_EL22__M 0xFFF
+#define QAM_DQ_TAP_IM_EL22__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL22_TAP__B 0
+#define QAM_DQ_TAP_IM_EL22_TAP__W 12
+#define QAM_DQ_TAP_IM_EL22_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL22_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL23__A 0x144004E
+#define QAM_DQ_TAP_RE_EL23__W 12
+#define QAM_DQ_TAP_RE_EL23__M 0xFFF
+#define QAM_DQ_TAP_RE_EL23__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL23_TAP__B 0
+#define QAM_DQ_TAP_RE_EL23_TAP__W 12
+#define QAM_DQ_TAP_RE_EL23_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL23_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL23__A 0x144004F
+#define QAM_DQ_TAP_IM_EL23__W 12
+#define QAM_DQ_TAP_IM_EL23__M 0xFFF
+#define QAM_DQ_TAP_IM_EL23__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL23_TAP__B 0
+#define QAM_DQ_TAP_IM_EL23_TAP__W 12
+#define QAM_DQ_TAP_IM_EL23_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL23_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL24__A 0x1440050
+#define QAM_DQ_TAP_RE_EL24__W 12
+#define QAM_DQ_TAP_RE_EL24__M 0xFFF
+#define QAM_DQ_TAP_RE_EL24__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL24_TAP__B 0
+#define QAM_DQ_TAP_RE_EL24_TAP__W 12
+#define QAM_DQ_TAP_RE_EL24_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL24_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL24__A 0x1440051
+#define QAM_DQ_TAP_IM_EL24__W 12
+#define QAM_DQ_TAP_IM_EL24__M 0xFFF
+#define QAM_DQ_TAP_IM_EL24__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL24_TAP__B 0
+#define QAM_DQ_TAP_IM_EL24_TAP__W 12
+#define QAM_DQ_TAP_IM_EL24_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL24_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL25__A 0x1440052
+#define QAM_DQ_TAP_RE_EL25__W 12
+#define QAM_DQ_TAP_RE_EL25__M 0xFFF
+#define QAM_DQ_TAP_RE_EL25__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL25_TAP__B 0
+#define QAM_DQ_TAP_RE_EL25_TAP__W 12
+#define QAM_DQ_TAP_RE_EL25_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL25_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL25__A 0x1440053
+#define QAM_DQ_TAP_IM_EL25__W 12
+#define QAM_DQ_TAP_IM_EL25__M 0xFFF
+#define QAM_DQ_TAP_IM_EL25__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL25_TAP__B 0
+#define QAM_DQ_TAP_IM_EL25_TAP__W 12
+#define QAM_DQ_TAP_IM_EL25_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL25_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL26__A 0x1440054
+#define QAM_DQ_TAP_RE_EL26__W 12
+#define QAM_DQ_TAP_RE_EL26__M 0xFFF
+#define QAM_DQ_TAP_RE_EL26__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL26_TAP__B 0
+#define QAM_DQ_TAP_RE_EL26_TAP__W 12
+#define QAM_DQ_TAP_RE_EL26_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL26_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL26__A 0x1440055
+#define QAM_DQ_TAP_IM_EL26__W 12
+#define QAM_DQ_TAP_IM_EL26__M 0xFFF
+#define QAM_DQ_TAP_IM_EL26__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL26_TAP__B 0
+#define QAM_DQ_TAP_IM_EL26_TAP__W 12
+#define QAM_DQ_TAP_IM_EL26_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL26_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL27__A 0x1440056
+#define QAM_DQ_TAP_RE_EL27__W 12
+#define QAM_DQ_TAP_RE_EL27__M 0xFFF
+#define QAM_DQ_TAP_RE_EL27__PRE 0x2
+
+#define QAM_DQ_TAP_RE_EL27_TAP__B 0
+#define QAM_DQ_TAP_RE_EL27_TAP__W 12
+#define QAM_DQ_TAP_RE_EL27_TAP__M 0xFFF
+#define QAM_DQ_TAP_RE_EL27_TAP__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL27__A 0x1440057
+#define QAM_DQ_TAP_IM_EL27__W 12
+#define QAM_DQ_TAP_IM_EL27__M 0xFFF
+#define QAM_DQ_TAP_IM_EL27__PRE 0x2
+
+#define QAM_DQ_TAP_IM_EL27_TAP__B 0
+#define QAM_DQ_TAP_IM_EL27_TAP__W 12
+#define QAM_DQ_TAP_IM_EL27_TAP__M 0xFFF
+#define QAM_DQ_TAP_IM_EL27_TAP__PRE 0x2
+
+#define QAM_LC_COMM_EXEC__A 0x1450000
+#define QAM_LC_COMM_EXEC__W 2
+#define QAM_LC_COMM_EXEC__M 0x3
+#define QAM_LC_COMM_EXEC__PRE 0x0
+#define QAM_LC_COMM_EXEC_STOP 0x0
+#define QAM_LC_COMM_EXEC_ACTIVE 0x1
+#define QAM_LC_COMM_EXEC_HOLD 0x2
+
+#define QAM_LC_COMM_MB__A 0x1450002
+#define QAM_LC_COMM_MB__W 2
+#define QAM_LC_COMM_MB__M 0x3
+#define QAM_LC_COMM_MB__PRE 0x0
+#define QAM_LC_COMM_MB_CTL__B 0
+#define QAM_LC_COMM_MB_CTL__W 1
+#define QAM_LC_COMM_MB_CTL__M 0x1
+#define QAM_LC_COMM_MB_CTL__PRE 0x0
+#define QAM_LC_COMM_MB_CTL_OFF 0x0
+#define QAM_LC_COMM_MB_CTL_ON 0x1
+#define QAM_LC_COMM_MB_OBS__B 1
+#define QAM_LC_COMM_MB_OBS__W 1
+#define QAM_LC_COMM_MB_OBS__M 0x2
+#define QAM_LC_COMM_MB_OBS__PRE 0x0
+#define QAM_LC_COMM_MB_OBS_OFF 0x0
+#define QAM_LC_COMM_MB_OBS_ON 0x2
+
+#define QAM_LC_COMM_INT_REQ__A 0x1450003
+#define QAM_LC_COMM_INT_REQ__W 1
+#define QAM_LC_COMM_INT_REQ__M 0x1
+#define QAM_LC_COMM_INT_REQ__PRE 0x0
+#define QAM_LC_COMM_INT_STA__A 0x1450005
+#define QAM_LC_COMM_INT_STA__W 3
+#define QAM_LC_COMM_INT_STA__M 0x7
+#define QAM_LC_COMM_INT_STA__PRE 0x0
+
+#define QAM_LC_COMM_INT_STA_READY__B 0
+#define QAM_LC_COMM_INT_STA_READY__W 1
+#define QAM_LC_COMM_INT_STA_READY__M 0x1
+#define QAM_LC_COMM_INT_STA_READY__PRE 0x0
+
+#define QAM_LC_COMM_INT_STA_OVERFLOW__B 1
+#define QAM_LC_COMM_INT_STA_OVERFLOW__W 1
+#define QAM_LC_COMM_INT_STA_OVERFLOW__M 0x2
+#define QAM_LC_COMM_INT_STA_OVERFLOW__PRE 0x0
+
+#define QAM_LC_COMM_INT_STA_FREQ_WRAP__B 2
+#define QAM_LC_COMM_INT_STA_FREQ_WRAP__W 1
+#define QAM_LC_COMM_INT_STA_FREQ_WRAP__M 0x4
+#define QAM_LC_COMM_INT_STA_FREQ_WRAP__PRE 0x0
+
+#define QAM_LC_COMM_INT_MSK__A 0x1450006
+#define QAM_LC_COMM_INT_MSK__W 3
+#define QAM_LC_COMM_INT_MSK__M 0x7
+#define QAM_LC_COMM_INT_MSK__PRE 0x0
+#define QAM_LC_COMM_INT_MSK_READY__B 0
+#define QAM_LC_COMM_INT_MSK_READY__W 1
+#define QAM_LC_COMM_INT_MSK_READY__M 0x1
+#define QAM_LC_COMM_INT_MSK_READY__PRE 0x0
+#define QAM_LC_COMM_INT_MSK_OVERFLOW__B 1
+#define QAM_LC_COMM_INT_MSK_OVERFLOW__W 1
+#define QAM_LC_COMM_INT_MSK_OVERFLOW__M 0x2
+#define QAM_LC_COMM_INT_MSK_OVERFLOW__PRE 0x0
+#define QAM_LC_COMM_INT_MSK_FREQ_WRAP__B 2
+#define QAM_LC_COMM_INT_MSK_FREQ_WRAP__W 1
+#define QAM_LC_COMM_INT_MSK_FREQ_WRAP__M 0x4
+#define QAM_LC_COMM_INT_MSK_FREQ_WRAP__PRE 0x0
+
+#define QAM_LC_COMM_INT_STM__A 0x1450007
+#define QAM_LC_COMM_INT_STM__W 3
+#define QAM_LC_COMM_INT_STM__M 0x7
+#define QAM_LC_COMM_INT_STM__PRE 0x0
+#define QAM_LC_COMM_INT_STM_READY__B 0
+#define QAM_LC_COMM_INT_STM_READY__W 1
+#define QAM_LC_COMM_INT_STM_READY__M 0x1
+#define QAM_LC_COMM_INT_STM_READY__PRE 0x0
+#define QAM_LC_COMM_INT_STM_OVERFLOW__B 1
+#define QAM_LC_COMM_INT_STM_OVERFLOW__W 1
+#define QAM_LC_COMM_INT_STM_OVERFLOW__M 0x2
+#define QAM_LC_COMM_INT_STM_OVERFLOW__PRE 0x0
+#define QAM_LC_COMM_INT_STM_FREQ_WRAP__B 2
+#define QAM_LC_COMM_INT_STM_FREQ_WRAP__W 1
+#define QAM_LC_COMM_INT_STM_FREQ_WRAP__M 0x4
+#define QAM_LC_COMM_INT_STM_FREQ_WRAP__PRE 0x0
+
+#define QAM_LC_MODE__A 0x1450010
+#define QAM_LC_MODE__W 3
+#define QAM_LC_MODE__M 0x7
+#define QAM_LC_MODE__PRE 0x7
+
+#define QAM_LC_MODE_ENABLE_A__B 0
+#define QAM_LC_MODE_ENABLE_A__W 1
+#define QAM_LC_MODE_ENABLE_A__M 0x1
+#define QAM_LC_MODE_ENABLE_A__PRE 0x1
+
+#define QAM_LC_MODE_ENABLE_F__B 1
+#define QAM_LC_MODE_ENABLE_F__W 1
+#define QAM_LC_MODE_ENABLE_F__M 0x2
+#define QAM_LC_MODE_ENABLE_F__PRE 0x2
+
+#define QAM_LC_MODE_ENABLE_R__B 2
+#define QAM_LC_MODE_ENABLE_R__W 1
+#define QAM_LC_MODE_ENABLE_R__M 0x4
+#define QAM_LC_MODE_ENABLE_R__PRE 0x4
+
+#define QAM_LC_CA__A 0x1450011
+#define QAM_LC_CA__W 6
+#define QAM_LC_CA__M 0x3F
+#define QAM_LC_CA__PRE 0x28
+
+#define QAM_LC_CA_COEF__B 0
+#define QAM_LC_CA_COEF__W 6
+#define QAM_LC_CA_COEF__M 0x3F
+#define QAM_LC_CA_COEF__PRE 0x28
+
+#define QAM_LC_CF__A 0x1450012
+#define QAM_LC_CF__W 8
+#define QAM_LC_CF__M 0xFF
+#define QAM_LC_CF__PRE 0x8C
+
+#define QAM_LC_CF_COEF__B 0
+#define QAM_LC_CF_COEF__W 8
+#define QAM_LC_CF_COEF__M 0xFF
+#define QAM_LC_CF_COEF__PRE 0x8C
+
+#define QAM_LC_CF1__A 0x1450013
+#define QAM_LC_CF1__W 8
+#define QAM_LC_CF1__M 0xFF
+#define QAM_LC_CF1__PRE 0x1E
+
+#define QAM_LC_CF1_COEF__B 0
+#define QAM_LC_CF1_COEF__W 8
+#define QAM_LC_CF1_COEF__M 0xFF
+#define QAM_LC_CF1_COEF__PRE 0x1E
+
+#define QAM_LC_CP__A 0x1450014
+#define QAM_LC_CP__W 8
+#define QAM_LC_CP__M 0xFF
+#define QAM_LC_CP__PRE 0x78
+
+#define QAM_LC_CP_COEF__B 0
+#define QAM_LC_CP_COEF__W 8
+#define QAM_LC_CP_COEF__M 0xFF
+#define QAM_LC_CP_COEF__PRE 0x78
+
+#define QAM_LC_CI__A 0x1450015
+#define QAM_LC_CI__W 8
+#define QAM_LC_CI__M 0xFF
+#define QAM_LC_CI__PRE 0x46
+
+#define QAM_LC_CI_COEF__B 0
+#define QAM_LC_CI_COEF__W 8
+#define QAM_LC_CI_COEF__M 0xFF
+#define QAM_LC_CI_COEF__PRE 0x46
+
+#define QAM_LC_EP__A 0x1450016
+#define QAM_LC_EP__W 6
+#define QAM_LC_EP__M 0x3F
+#define QAM_LC_EP__PRE 0x0
+
+#define QAM_LC_EP_COEF__B 0
+#define QAM_LC_EP_COEF__W 6
+#define QAM_LC_EP_COEF__M 0x3F
+#define QAM_LC_EP_COEF__PRE 0x0
+
+#define QAM_LC_EI__A 0x1450017
+#define QAM_LC_EI__W 6
+#define QAM_LC_EI__M 0x3F
+#define QAM_LC_EI__PRE 0x0
+
+#define QAM_LC_EI_COEF__B 0
+#define QAM_LC_EI_COEF__W 6
+#define QAM_LC_EI_COEF__M 0x3F
+#define QAM_LC_EI_COEF__PRE 0x0
+
+#define QAM_LC_QUAL_TAB0__A 0x1450018
+#define QAM_LC_QUAL_TAB0__W 5
+#define QAM_LC_QUAL_TAB0__M 0x1F
+#define QAM_LC_QUAL_TAB0__PRE 0x1
+
+#define QAM_LC_QUAL_TAB0_VALUE__B 0
+#define QAM_LC_QUAL_TAB0_VALUE__W 5
+#define QAM_LC_QUAL_TAB0_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB0_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB1__A 0x1450019
+#define QAM_LC_QUAL_TAB1__W 5
+#define QAM_LC_QUAL_TAB1__M 0x1F
+#define QAM_LC_QUAL_TAB1__PRE 0x1
+
+#define QAM_LC_QUAL_TAB1_VALUE__B 0
+#define QAM_LC_QUAL_TAB1_VALUE__W 5
+#define QAM_LC_QUAL_TAB1_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB1_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB2__A 0x145001A
+#define QAM_LC_QUAL_TAB2__W 5
+#define QAM_LC_QUAL_TAB2__M 0x1F
+#define QAM_LC_QUAL_TAB2__PRE 0x1
+
+#define QAM_LC_QUAL_TAB2_VALUE__B 0
+#define QAM_LC_QUAL_TAB2_VALUE__W 5
+#define QAM_LC_QUAL_TAB2_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB2_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB3__A 0x145001B
+#define QAM_LC_QUAL_TAB3__W 5
+#define QAM_LC_QUAL_TAB3__M 0x1F
+#define QAM_LC_QUAL_TAB3__PRE 0x1
+
+#define QAM_LC_QUAL_TAB3_VALUE__B 0
+#define QAM_LC_QUAL_TAB3_VALUE__W 5
+#define QAM_LC_QUAL_TAB3_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB3_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB4__A 0x145001C
+#define QAM_LC_QUAL_TAB4__W 5
+#define QAM_LC_QUAL_TAB4__M 0x1F
+#define QAM_LC_QUAL_TAB4__PRE 0x1
+
+#define QAM_LC_QUAL_TAB4_VALUE__B 0
+#define QAM_LC_QUAL_TAB4_VALUE__W 5
+#define QAM_LC_QUAL_TAB4_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB4_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB5__A 0x145001D
+#define QAM_LC_QUAL_TAB5__W 5
+#define QAM_LC_QUAL_TAB5__M 0x1F
+#define QAM_LC_QUAL_TAB5__PRE 0x1
+
+#define QAM_LC_QUAL_TAB5_VALUE__B 0
+#define QAM_LC_QUAL_TAB5_VALUE__W 5
+#define QAM_LC_QUAL_TAB5_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB5_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB6__A 0x145001E
+#define QAM_LC_QUAL_TAB6__W 5
+#define QAM_LC_QUAL_TAB6__M 0x1F
+#define QAM_LC_QUAL_TAB6__PRE 0x1
+
+#define QAM_LC_QUAL_TAB6_VALUE__B 0
+#define QAM_LC_QUAL_TAB6_VALUE__W 5
+#define QAM_LC_QUAL_TAB6_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB6_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB8__A 0x145001F
+#define QAM_LC_QUAL_TAB8__W 5
+#define QAM_LC_QUAL_TAB8__M 0x1F
+#define QAM_LC_QUAL_TAB8__PRE 0x1
+
+#define QAM_LC_QUAL_TAB8_VALUE__B 0
+#define QAM_LC_QUAL_TAB8_VALUE__W 5
+#define QAM_LC_QUAL_TAB8_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB8_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB9__A 0x1450020
+#define QAM_LC_QUAL_TAB9__W 5
+#define QAM_LC_QUAL_TAB9__M 0x1F
+#define QAM_LC_QUAL_TAB9__PRE 0x1
+
+#define QAM_LC_QUAL_TAB9_VALUE__B 0
+#define QAM_LC_QUAL_TAB9_VALUE__W 5
+#define QAM_LC_QUAL_TAB9_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB9_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB10__A 0x1450021
+#define QAM_LC_QUAL_TAB10__W 5
+#define QAM_LC_QUAL_TAB10__M 0x1F
+#define QAM_LC_QUAL_TAB10__PRE 0x1
+
+#define QAM_LC_QUAL_TAB10_VALUE__B 0
+#define QAM_LC_QUAL_TAB10_VALUE__W 5
+#define QAM_LC_QUAL_TAB10_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB10_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB12__A 0x1450022
+#define QAM_LC_QUAL_TAB12__W 5
+#define QAM_LC_QUAL_TAB12__M 0x1F
+#define QAM_LC_QUAL_TAB12__PRE 0x1
+
+#define QAM_LC_QUAL_TAB12_VALUE__B 0
+#define QAM_LC_QUAL_TAB12_VALUE__W 5
+#define QAM_LC_QUAL_TAB12_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB12_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB15__A 0x1450023
+#define QAM_LC_QUAL_TAB15__W 5
+#define QAM_LC_QUAL_TAB15__M 0x1F
+#define QAM_LC_QUAL_TAB15__PRE 0x1
+
+#define QAM_LC_QUAL_TAB15_VALUE__B 0
+#define QAM_LC_QUAL_TAB15_VALUE__W 5
+#define QAM_LC_QUAL_TAB15_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB15_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB16__A 0x1450024
+#define QAM_LC_QUAL_TAB16__W 5
+#define QAM_LC_QUAL_TAB16__M 0x1F
+#define QAM_LC_QUAL_TAB16__PRE 0x1
+
+#define QAM_LC_QUAL_TAB16_VALUE__B 0
+#define QAM_LC_QUAL_TAB16_VALUE__W 5
+#define QAM_LC_QUAL_TAB16_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB16_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB20__A 0x1450025
+#define QAM_LC_QUAL_TAB20__W 5
+#define QAM_LC_QUAL_TAB20__M 0x1F
+#define QAM_LC_QUAL_TAB20__PRE 0x1
+
+#define QAM_LC_QUAL_TAB20_VALUE__B 0
+#define QAM_LC_QUAL_TAB20_VALUE__W 5
+#define QAM_LC_QUAL_TAB20_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB20_VALUE__PRE 0x1
+
+#define QAM_LC_QUAL_TAB25__A 0x1450026
+#define QAM_LC_QUAL_TAB25__W 5
+#define QAM_LC_QUAL_TAB25__M 0x1F
+#define QAM_LC_QUAL_TAB25__PRE 0x1
+
+#define QAM_LC_QUAL_TAB25_VALUE__B 0
+#define QAM_LC_QUAL_TAB25_VALUE__W 5
+#define QAM_LC_QUAL_TAB25_VALUE__M 0x1F
+#define QAM_LC_QUAL_TAB25_VALUE__PRE 0x1
+
+#define QAM_LC_EQ_TIMING__A 0x1450027
+#define QAM_LC_EQ_TIMING__W 10
+#define QAM_LC_EQ_TIMING__M 0x3FF
+#define QAM_LC_EQ_TIMING__PRE 0x0
+
+#define QAM_LC_EQ_TIMING_OFFS__B 0
+#define QAM_LC_EQ_TIMING_OFFS__W 10
+#define QAM_LC_EQ_TIMING_OFFS__M 0x3FF
+#define QAM_LC_EQ_TIMING_OFFS__PRE 0x0
+
+#define QAM_LC_LPF_FACTORP__A 0x1450028
+#define QAM_LC_LPF_FACTORP__W 3
+#define QAM_LC_LPF_FACTORP__M 0x7
+#define QAM_LC_LPF_FACTORP__PRE 0x3
+
+#define QAM_LC_LPF_FACTORP_FACTOR__B 0
+#define QAM_LC_LPF_FACTORP_FACTOR__W 3
+#define QAM_LC_LPF_FACTORP_FACTOR__M 0x7
+#define QAM_LC_LPF_FACTORP_FACTOR__PRE 0x3
+
+#define QAM_LC_LPF_FACTORI__A 0x1450029
+#define QAM_LC_LPF_FACTORI__W 3
+#define QAM_LC_LPF_FACTORI__M 0x7
+#define QAM_LC_LPF_FACTORI__PRE 0x3
+
+#define QAM_LC_LPF_FACTORI_FACTOR__B 0
+#define QAM_LC_LPF_FACTORI_FACTOR__W 3
+#define QAM_LC_LPF_FACTORI_FACTOR__M 0x7
+#define QAM_LC_LPF_FACTORI_FACTOR__PRE 0x3
+
+#define QAM_LC_RATE_LIMIT__A 0x145002A
+#define QAM_LC_RATE_LIMIT__W 2
+#define QAM_LC_RATE_LIMIT__M 0x3
+#define QAM_LC_RATE_LIMIT__PRE 0x3
+
+#define QAM_LC_RATE_LIMIT_LIMIT__B 0
+#define QAM_LC_RATE_LIMIT_LIMIT__W 2
+#define QAM_LC_RATE_LIMIT_LIMIT__M 0x3
+#define QAM_LC_RATE_LIMIT_LIMIT__PRE 0x3
+
+#define QAM_LC_SYMBOL_FREQ__A 0x145002B
+#define QAM_LC_SYMBOL_FREQ__W 10
+#define QAM_LC_SYMBOL_FREQ__M 0x3FF
+#define QAM_LC_SYMBOL_FREQ__PRE 0x199
+
+#define QAM_LC_SYMBOL_FREQ_FREQ__B 0
+#define QAM_LC_SYMBOL_FREQ_FREQ__W 10
+#define QAM_LC_SYMBOL_FREQ_FREQ__M 0x3FF
+#define QAM_LC_SYMBOL_FREQ_FREQ__PRE 0x199
+#define QAM_LC_SYMBOL_FREQ_FREQ_QAM_B_64 0x197
+#define QAM_LC_SYMBOL_FREQ_FREQ_QAM_B_256 0x1B2
+
+#define QAM_LC_MTA_LENGTH__A 0x145002C
+#define QAM_LC_MTA_LENGTH__W 2
+#define QAM_LC_MTA_LENGTH__M 0x3
+#define QAM_LC_MTA_LENGTH__PRE 0x2
+
+#define QAM_LC_MTA_LENGTH_LENGTH__B 0
+#define QAM_LC_MTA_LENGTH_LENGTH__W 2
+#define QAM_LC_MTA_LENGTH_LENGTH__M 0x3
+#define QAM_LC_MTA_LENGTH_LENGTH__PRE 0x2
+
+#define QAM_LC_AMP_ACCU__A 0x145002D
+#define QAM_LC_AMP_ACCU__W 14
+#define QAM_LC_AMP_ACCU__M 0x3FFF
+#define QAM_LC_AMP_ACCU__PRE 0x600
+
+#define QAM_LC_AMP_ACCU_ACCU__B 0
+#define QAM_LC_AMP_ACCU_ACCU__W 14
+#define QAM_LC_AMP_ACCU_ACCU__M 0x3FFF
+#define QAM_LC_AMP_ACCU_ACCU__PRE 0x600
+
+#define QAM_LC_FREQ_ACCU__A 0x145002E
+#define QAM_LC_FREQ_ACCU__W 10
+#define QAM_LC_FREQ_ACCU__M 0x3FF
+#define QAM_LC_FREQ_ACCU__PRE 0x0
+
+#define QAM_LC_FREQ_ACCU_ACCU__B 0
+#define QAM_LC_FREQ_ACCU_ACCU__W 10
+#define QAM_LC_FREQ_ACCU_ACCU__M 0x3FF
+#define QAM_LC_FREQ_ACCU_ACCU__PRE 0x0
+
+#define QAM_LC_RATE_ACCU__A 0x145002F
+#define QAM_LC_RATE_ACCU__W 10
+#define QAM_LC_RATE_ACCU__M 0x3FF
+#define QAM_LC_RATE_ACCU__PRE 0x0
+
+#define QAM_LC_RATE_ACCU_ACCU__B 0
+#define QAM_LC_RATE_ACCU_ACCU__W 10
+#define QAM_LC_RATE_ACCU_ACCU__M 0x3FF
+#define QAM_LC_RATE_ACCU_ACCU__PRE 0x0
+
+#define QAM_LC_AMPLITUDE__A 0x1450030
+#define QAM_LC_AMPLITUDE__W 10
+#define QAM_LC_AMPLITUDE__M 0x3FF
+#define QAM_LC_AMPLITUDE__PRE 0x0
+
+#define QAM_LC_AMPLITUDE_SIZE__B 0
+#define QAM_LC_AMPLITUDE_SIZE__W 10
+#define QAM_LC_AMPLITUDE_SIZE__M 0x3FF
+#define QAM_LC_AMPLITUDE_SIZE__PRE 0x0
+
+#define QAM_LC_RAD_ERROR__A 0x1450031
+#define QAM_LC_RAD_ERROR__W 10
+#define QAM_LC_RAD_ERROR__M 0x3FF
+#define QAM_LC_RAD_ERROR__PRE 0x0
+
+#define QAM_LC_RAD_ERROR_SIZE__B 0
+#define QAM_LC_RAD_ERROR_SIZE__W 10
+#define QAM_LC_RAD_ERROR_SIZE__M 0x3FF
+#define QAM_LC_RAD_ERROR_SIZE__PRE 0x0
+
+#define QAM_LC_FREQ_OFFS__A 0x1450032
+#define QAM_LC_FREQ_OFFS__W 10
+#define QAM_LC_FREQ_OFFS__M 0x3FF
+#define QAM_LC_FREQ_OFFS__PRE 0x0
+
+#define QAM_LC_FREQ_OFFS_OFFS__B 0
+#define QAM_LC_FREQ_OFFS_OFFS__W 10
+#define QAM_LC_FREQ_OFFS_OFFS__M 0x3FF
+#define QAM_LC_FREQ_OFFS_OFFS__PRE 0x0
+
+#define QAM_LC_PHASE_ERROR__A 0x1450033
+#define QAM_LC_PHASE_ERROR__W 10
+#define QAM_LC_PHASE_ERROR__M 0x3FF
+#define QAM_LC_PHASE_ERROR__PRE 0x0
+
+#define QAM_LC_PHASE_ERROR_SIZE__B 0
+#define QAM_LC_PHASE_ERROR_SIZE__W 10
+#define QAM_LC_PHASE_ERROR_SIZE__M 0x3FF
+#define QAM_LC_PHASE_ERROR_SIZE__PRE 0x0
+
+#define QAM_VD_COMM_EXEC__A 0x1460000
+#define QAM_VD_COMM_EXEC__W 2
+#define QAM_VD_COMM_EXEC__M 0x3
+#define QAM_VD_COMM_EXEC__PRE 0x0
+#define QAM_VD_COMM_EXEC_STOP 0x0
+#define QAM_VD_COMM_EXEC_ACTIVE 0x1
+#define QAM_VD_COMM_EXEC_HOLD 0x2
+
+#define QAM_VD_COMM_MB__A 0x1460002
+#define QAM_VD_COMM_MB__W 2
+#define QAM_VD_COMM_MB__M 0x3
+#define QAM_VD_COMM_MB__PRE 0x0
+#define QAM_VD_COMM_MB_CTL__B 0
+#define QAM_VD_COMM_MB_CTL__W 1
+#define QAM_VD_COMM_MB_CTL__M 0x1
+#define QAM_VD_COMM_MB_CTL__PRE 0x0
+#define QAM_VD_COMM_MB_CTL_OFF 0x0
+#define QAM_VD_COMM_MB_CTL_ON 0x1
+#define QAM_VD_COMM_MB_OBS__B 1
+#define QAM_VD_COMM_MB_OBS__W 1
+#define QAM_VD_COMM_MB_OBS__M 0x2
+#define QAM_VD_COMM_MB_OBS__PRE 0x0
+#define QAM_VD_COMM_MB_OBS_OFF 0x0
+#define QAM_VD_COMM_MB_OBS_ON 0x2
+
+#define QAM_VD_COMM_INT_REQ__A 0x1460003
+#define QAM_VD_COMM_INT_REQ__W 1
+#define QAM_VD_COMM_INT_REQ__M 0x1
+#define QAM_VD_COMM_INT_REQ__PRE 0x0
+#define QAM_VD_COMM_INT_STA__A 0x1460005
+#define QAM_VD_COMM_INT_STA__W 2
+#define QAM_VD_COMM_INT_STA__M 0x3
+#define QAM_VD_COMM_INT_STA__PRE 0x0
+
+#define QAM_VD_COMM_INT_STA_LOCK_INT__B 0
+#define QAM_VD_COMM_INT_STA_LOCK_INT__W 1
+#define QAM_VD_COMM_INT_STA_LOCK_INT__M 0x1
+#define QAM_VD_COMM_INT_STA_LOCK_INT__PRE 0x0
+
+#define QAM_VD_COMM_INT_STA_PERIOD_INT__B 1
+#define QAM_VD_COMM_INT_STA_PERIOD_INT__W 1
+#define QAM_VD_COMM_INT_STA_PERIOD_INT__M 0x2
+#define QAM_VD_COMM_INT_STA_PERIOD_INT__PRE 0x0
+
+#define QAM_VD_COMM_INT_MSK__A 0x1460006
+#define QAM_VD_COMM_INT_MSK__W 2
+#define QAM_VD_COMM_INT_MSK__M 0x3
+#define QAM_VD_COMM_INT_MSK__PRE 0x0
+#define QAM_VD_COMM_INT_MSK_LOCK_INT__B 0
+#define QAM_VD_COMM_INT_MSK_LOCK_INT__W 1
+#define QAM_VD_COMM_INT_MSK_LOCK_INT__M 0x1
+#define QAM_VD_COMM_INT_MSK_LOCK_INT__PRE 0x0
+#define QAM_VD_COMM_INT_MSK_PERIOD_INT__B 1
+#define QAM_VD_COMM_INT_MSK_PERIOD_INT__W 1
+#define QAM_VD_COMM_INT_MSK_PERIOD_INT__M 0x2
+#define QAM_VD_COMM_INT_MSK_PERIOD_INT__PRE 0x0
+
+#define QAM_VD_COMM_INT_STM__A 0x1460007
+#define QAM_VD_COMM_INT_STM__W 2
+#define QAM_VD_COMM_INT_STM__M 0x3
+#define QAM_VD_COMM_INT_STM__PRE 0x0
+#define QAM_VD_COMM_INT_STM_LOCK_INT__B 0
+#define QAM_VD_COMM_INT_STM_LOCK_INT__W 1
+#define QAM_VD_COMM_INT_STM_LOCK_INT__M 0x1
+#define QAM_VD_COMM_INT_STM_LOCK_INT__PRE 0x0
+#define QAM_VD_COMM_INT_STM_PERIOD_INT__B 1
+#define QAM_VD_COMM_INT_STM_PERIOD_INT__W 1
+#define QAM_VD_COMM_INT_STM_PERIOD_INT__M 0x2
+#define QAM_VD_COMM_INT_STM_PERIOD_INT__PRE 0x0
+
+#define QAM_VD_STATUS__A 0x1460010
+#define QAM_VD_STATUS__W 1
+#define QAM_VD_STATUS__M 0x1
+#define QAM_VD_STATUS__PRE 0x0
+
+#define QAM_VD_STATUS_LOCK__B 0
+#define QAM_VD_STATUS_LOCK__W 1
+#define QAM_VD_STATUS_LOCK__M 0x1
+#define QAM_VD_STATUS_LOCK__PRE 0x0
+
+#define QAM_VD_UNLOCK_CONTROL__A 0x1460011
+#define QAM_VD_UNLOCK_CONTROL__W 1
+#define QAM_VD_UNLOCK_CONTROL__M 0x1
+#define QAM_VD_UNLOCK_CONTROL__PRE 0x0
+
+#define QAM_VD_UNLOCK_CONTROL_UNLOCK_CTRL__B 0
+#define QAM_VD_UNLOCK_CONTROL_UNLOCK_CTRL__W 1
+#define QAM_VD_UNLOCK_CONTROL_UNLOCK_CTRL__M 0x1
+#define QAM_VD_UNLOCK_CONTROL_UNLOCK_CTRL__PRE 0x0
+
+#define QAM_VD_MIN_VOTING_ROUNDS__A 0x1460012
+#define QAM_VD_MIN_VOTING_ROUNDS__W 6
+#define QAM_VD_MIN_VOTING_ROUNDS__M 0x3F
+#define QAM_VD_MIN_VOTING_ROUNDS__PRE 0x10
+
+#define QAM_VD_MIN_VOTING_ROUNDS_ROUNDS__B 0
+#define QAM_VD_MIN_VOTING_ROUNDS_ROUNDS__W 6
+#define QAM_VD_MIN_VOTING_ROUNDS_ROUNDS__M 0x3F
+#define QAM_VD_MIN_VOTING_ROUNDS_ROUNDS__PRE 0x10
+
+#define QAM_VD_MAX_VOTING_ROUNDS__A 0x1460013
+#define QAM_VD_MAX_VOTING_ROUNDS__W 6
+#define QAM_VD_MAX_VOTING_ROUNDS__M 0x3F
+#define QAM_VD_MAX_VOTING_ROUNDS__PRE 0x10
+
+#define QAM_VD_MAX_VOTING_ROUNDS_ROUNDS__B 0
+#define QAM_VD_MAX_VOTING_ROUNDS_ROUNDS__W 6
+#define QAM_VD_MAX_VOTING_ROUNDS_ROUNDS__M 0x3F
+#define QAM_VD_MAX_VOTING_ROUNDS_ROUNDS__PRE 0x10
+
+#define QAM_VD_TRACEBACK_DEPTH__A 0x1460014
+#define QAM_VD_TRACEBACK_DEPTH__W 5
+#define QAM_VD_TRACEBACK_DEPTH__M 0x1F
+#define QAM_VD_TRACEBACK_DEPTH__PRE 0x10
+
+#define QAM_VD_TRACEBACK_DEPTH_LENGTH__B 0
+#define QAM_VD_TRACEBACK_DEPTH_LENGTH__W 5
+#define QAM_VD_TRACEBACK_DEPTH_LENGTH__M 0x1F
+#define QAM_VD_TRACEBACK_DEPTH_LENGTH__PRE 0x10
+
+#define QAM_VD_UNLOCK__A 0x1460015
+#define QAM_VD_UNLOCK__W 1
+#define QAM_VD_UNLOCK__M 0x1
+#define QAM_VD_UNLOCK__PRE 0x0
+#define QAM_VD_MEASUREMENT_PERIOD__A 0x1460016
+#define QAM_VD_MEASUREMENT_PERIOD__W 16
+#define QAM_VD_MEASUREMENT_PERIOD__M 0xFFFF
+#define QAM_VD_MEASUREMENT_PERIOD__PRE 0x8236
+
+#define QAM_VD_MEASUREMENT_PERIOD_PERIOD__B 0
+#define QAM_VD_MEASUREMENT_PERIOD_PERIOD__W 16
+#define QAM_VD_MEASUREMENT_PERIOD_PERIOD__M 0xFFFF
+#define QAM_VD_MEASUREMENT_PERIOD_PERIOD__PRE 0x8236
+
+#define QAM_VD_MEASUREMENT_PRESCALE__A 0x1460017
+#define QAM_VD_MEASUREMENT_PRESCALE__W 16
+#define QAM_VD_MEASUREMENT_PRESCALE__M 0xFFFF
+#define QAM_VD_MEASUREMENT_PRESCALE__PRE 0x4
+
+#define QAM_VD_MEASUREMENT_PRESCALE_PRESCALE__B 0
+#define QAM_VD_MEASUREMENT_PRESCALE_PRESCALE__W 16
+#define QAM_VD_MEASUREMENT_PRESCALE_PRESCALE__M 0xFFFF
+#define QAM_VD_MEASUREMENT_PRESCALE_PRESCALE__PRE 0x4
+
+#define QAM_VD_DELTA_PATH_METRIC__A 0x1460018
+#define QAM_VD_DELTA_PATH_METRIC__W 16
+#define QAM_VD_DELTA_PATH_METRIC__M 0xFFFF
+#define QAM_VD_DELTA_PATH_METRIC__PRE 0xFFFF
+
+#define QAM_VD_DELTA_PATH_METRIC_FIXED_MANT__B 0
+#define QAM_VD_DELTA_PATH_METRIC_FIXED_MANT__W 12
+#define QAM_VD_DELTA_PATH_METRIC_FIXED_MANT__M 0xFFF
+#define QAM_VD_DELTA_PATH_METRIC_FIXED_MANT__PRE 0xFFF
+
+#define QAM_VD_DELTA_PATH_METRIC_EXP__B 12
+#define QAM_VD_DELTA_PATH_METRIC_EXP__W 4
+#define QAM_VD_DELTA_PATH_METRIC_EXP__M 0xF000
+#define QAM_VD_DELTA_PATH_METRIC_EXP__PRE 0xF000
+
+#define QAM_VD_NR_QSYM_ERRORS__A 0x1460019
+#define QAM_VD_NR_QSYM_ERRORS__W 16
+#define QAM_VD_NR_QSYM_ERRORS__M 0xFFFF
+#define QAM_VD_NR_QSYM_ERRORS__PRE 0xFFFF
+
+#define QAM_VD_NR_QSYM_ERRORS_FIXED_MANT__B 0
+#define QAM_VD_NR_QSYM_ERRORS_FIXED_MANT__W 12
+#define QAM_VD_NR_QSYM_ERRORS_FIXED_MANT__M 0xFFF
+#define QAM_VD_NR_QSYM_ERRORS_FIXED_MANT__PRE 0xFFF
+
+#define QAM_VD_NR_QSYM_ERRORS_EXP__B 12
+#define QAM_VD_NR_QSYM_ERRORS_EXP__W 4
+#define QAM_VD_NR_QSYM_ERRORS_EXP__M 0xF000
+#define QAM_VD_NR_QSYM_ERRORS_EXP__PRE 0xF000
+
+#define QAM_VD_NR_SYMBOL_ERRORS__A 0x146001A
+#define QAM_VD_NR_SYMBOL_ERRORS__W 16
+#define QAM_VD_NR_SYMBOL_ERRORS__M 0xFFFF
+#define QAM_VD_NR_SYMBOL_ERRORS__PRE 0xFFFF
+
+#define QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__B 0
+#define QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__W 12
+#define QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__M 0xFFF
+#define QAM_VD_NR_SYMBOL_ERRORS_FIXED_MANT__PRE 0xFFF
+
+#define QAM_VD_NR_SYMBOL_ERRORS_EXP__B 12
+#define QAM_VD_NR_SYMBOL_ERRORS_EXP__W 4
+#define QAM_VD_NR_SYMBOL_ERRORS_EXP__M 0xF000
+#define QAM_VD_NR_SYMBOL_ERRORS_EXP__PRE 0xF000
+
+#define QAM_VD_RELOCK_COUNT__A 0x146001B
+#define QAM_VD_RELOCK_COUNT__W 16
+#define QAM_VD_RELOCK_COUNT__M 0xFFFF
+#define QAM_VD_RELOCK_COUNT__PRE 0x0
+
+#define QAM_VD_RELOCK_COUNT_COUNT__B 0
+#define QAM_VD_RELOCK_COUNT_COUNT__W 8
+#define QAM_VD_RELOCK_COUNT_COUNT__M 0xFF
+#define QAM_VD_RELOCK_COUNT_COUNT__PRE 0x0
+
+#define QAM_SY_COMM_EXEC__A 0x1470000
+#define QAM_SY_COMM_EXEC__W 2
+#define QAM_SY_COMM_EXEC__M 0x3
+#define QAM_SY_COMM_EXEC__PRE 0x0
+#define QAM_SY_COMM_EXEC_STOP 0x0
+#define QAM_SY_COMM_EXEC_ACTIVE 0x1
+#define QAM_SY_COMM_EXEC_HOLD 0x2
+
+#define QAM_SY_COMM_MB__A 0x1470002
+#define QAM_SY_COMM_MB__W 2
+#define QAM_SY_COMM_MB__M 0x3
+#define QAM_SY_COMM_MB__PRE 0x0
+#define QAM_SY_COMM_MB_CTL__B 0
+#define QAM_SY_COMM_MB_CTL__W 1
+#define QAM_SY_COMM_MB_CTL__M 0x1
+#define QAM_SY_COMM_MB_CTL__PRE 0x0
+#define QAM_SY_COMM_MB_CTL_OFF 0x0
+#define QAM_SY_COMM_MB_CTL_ON 0x1
+#define QAM_SY_COMM_MB_OBS__B 1
+#define QAM_SY_COMM_MB_OBS__W 1
+#define QAM_SY_COMM_MB_OBS__M 0x2
+#define QAM_SY_COMM_MB_OBS__PRE 0x0
+#define QAM_SY_COMM_MB_OBS_OFF 0x0
+#define QAM_SY_COMM_MB_OBS_ON 0x2
+
+#define QAM_SY_COMM_INT_REQ__A 0x1470003
+#define QAM_SY_COMM_INT_REQ__W 1
+#define QAM_SY_COMM_INT_REQ__M 0x1
+#define QAM_SY_COMM_INT_REQ__PRE 0x0
+#define QAM_SY_COMM_INT_STA__A 0x1470005
+#define QAM_SY_COMM_INT_STA__W 4
+#define QAM_SY_COMM_INT_STA__M 0xF
+#define QAM_SY_COMM_INT_STA__PRE 0x0
+
+#define QAM_SY_COMM_INT_STA_LOCK_INT__B 0
+#define QAM_SY_COMM_INT_STA_LOCK_INT__W 1
+#define QAM_SY_COMM_INT_STA_LOCK_INT__M 0x1
+#define QAM_SY_COMM_INT_STA_LOCK_INT__PRE 0x0
+
+#define QAM_SY_COMM_INT_STA_UNLOCK_INT__B 1
+#define QAM_SY_COMM_INT_STA_UNLOCK_INT__W 1
+#define QAM_SY_COMM_INT_STA_UNLOCK_INT__M 0x2
+#define QAM_SY_COMM_INT_STA_UNLOCK_INT__PRE 0x0
+
+#define QAM_SY_COMM_INT_STA_TIMEOUT_INT__B 2
+#define QAM_SY_COMM_INT_STA_TIMEOUT_INT__W 1
+#define QAM_SY_COMM_INT_STA_TIMEOUT_INT__M 0x4
+#define QAM_SY_COMM_INT_STA_TIMEOUT_INT__PRE 0x0
+
+#define QAM_SY_COMM_INT_STA_CTL_WORD_INT__B 3
+#define QAM_SY_COMM_INT_STA_CTL_WORD_INT__W 1
+#define QAM_SY_COMM_INT_STA_CTL_WORD_INT__M 0x8
+#define QAM_SY_COMM_INT_STA_CTL_WORD_INT__PRE 0x0
+
+#define QAM_SY_COMM_INT_MSK__A 0x1470006
+#define QAM_SY_COMM_INT_MSK__W 4
+#define QAM_SY_COMM_INT_MSK__M 0xF
+#define QAM_SY_COMM_INT_MSK__PRE 0x0
+#define QAM_SY_COMM_INT_MSK_LOCK_MSK__B 0
+#define QAM_SY_COMM_INT_MSK_LOCK_MSK__W 1
+#define QAM_SY_COMM_INT_MSK_LOCK_MSK__M 0x1
+#define QAM_SY_COMM_INT_MSK_LOCK_MSK__PRE 0x0
+#define QAM_SY_COMM_INT_MSK_UNLOCK_MSK__B 1
+#define QAM_SY_COMM_INT_MSK_UNLOCK_MSK__W 1
+#define QAM_SY_COMM_INT_MSK_UNLOCK_MSK__M 0x2
+#define QAM_SY_COMM_INT_MSK_UNLOCK_MSK__PRE 0x0
+#define QAM_SY_COMM_INT_MSK_TIMEOUT_MSK__B 2
+#define QAM_SY_COMM_INT_MSK_TIMEOUT_MSK__W 1
+#define QAM_SY_COMM_INT_MSK_TIMEOUT_MSK__M 0x4
+#define QAM_SY_COMM_INT_MSK_TIMEOUT_MSK__PRE 0x0
+#define QAM_SY_COMM_INT_MSK_CTL_WORD_MSK__B 3
+#define QAM_SY_COMM_INT_MSK_CTL_WORD_MSK__W 1
+#define QAM_SY_COMM_INT_MSK_CTL_WORD_MSK__M 0x8
+#define QAM_SY_COMM_INT_MSK_CTL_WORD_MSK__PRE 0x0
+
+#define QAM_SY_COMM_INT_STM__A 0x1470007
+#define QAM_SY_COMM_INT_STM__W 4
+#define QAM_SY_COMM_INT_STM__M 0xF
+#define QAM_SY_COMM_INT_STM__PRE 0x0
+#define QAM_SY_COMM_INT_STM_LOCK_MSK__B 0
+#define QAM_SY_COMM_INT_STM_LOCK_MSK__W 1
+#define QAM_SY_COMM_INT_STM_LOCK_MSK__M 0x1
+#define QAM_SY_COMM_INT_STM_LOCK_MSK__PRE 0x0
+#define QAM_SY_COMM_INT_STM_UNLOCK_MSK__B 1
+#define QAM_SY_COMM_INT_STM_UNLOCK_MSK__W 1
+#define QAM_SY_COMM_INT_STM_UNLOCK_MSK__M 0x2
+#define QAM_SY_COMM_INT_STM_UNLOCK_MSK__PRE 0x0
+#define QAM_SY_COMM_INT_STM_TIMEOUT_MSK__B 2
+#define QAM_SY_COMM_INT_STM_TIMEOUT_MSK__W 1
+#define QAM_SY_COMM_INT_STM_TIMEOUT_MSK__M 0x4
+#define QAM_SY_COMM_INT_STM_TIMEOUT_MSK__PRE 0x0
+#define QAM_SY_COMM_INT_STM_CTL_WORD_MSK__B 3
+#define QAM_SY_COMM_INT_STM_CTL_WORD_MSK__W 1
+#define QAM_SY_COMM_INT_STM_CTL_WORD_MSK__M 0x8
+#define QAM_SY_COMM_INT_STM_CTL_WORD_MSK__PRE 0x0
+
+#define QAM_SY_STATUS__A 0x1470010
+#define QAM_SY_STATUS__W 2
+#define QAM_SY_STATUS__M 0x3
+#define QAM_SY_STATUS__PRE 0x0
+
+#define QAM_SY_STATUS_SYNC_STATE__B 0
+#define QAM_SY_STATUS_SYNC_STATE__W 2
+#define QAM_SY_STATUS_SYNC_STATE__M 0x3
+#define QAM_SY_STATUS_SYNC_STATE__PRE 0x0
+
+#define QAM_SY_TIMEOUT__A 0x1470011
+#define QAM_SY_TIMEOUT__W 16
+#define QAM_SY_TIMEOUT__M 0xFFFF
+#define QAM_SY_TIMEOUT__PRE 0x3A98
+
+#define QAM_SY_SYNC_LWM__A 0x1470012
+#define QAM_SY_SYNC_LWM__W 4
+#define QAM_SY_SYNC_LWM__M 0xF
+#define QAM_SY_SYNC_LWM__PRE 0x2
+
+#define QAM_SY_SYNC_AWM__A 0x1470013
+#define QAM_SY_SYNC_AWM__W 4
+#define QAM_SY_SYNC_AWM__M 0xF
+#define QAM_SY_SYNC_AWM__PRE 0x3
+
+#define QAM_SY_SYNC_HWM__A 0x1470014
+#define QAM_SY_SYNC_HWM__W 4
+#define QAM_SY_SYNC_HWM__M 0xF
+#define QAM_SY_SYNC_HWM__PRE 0x5
+
+#define QAM_SY_UNLOCK__A 0x1470015
+#define QAM_SY_UNLOCK__W 1
+#define QAM_SY_UNLOCK__M 0x1
+#define QAM_SY_UNLOCK__PRE 0x0
+#define QAM_SY_CONTROL_WORD__A 0x1470016
+#define QAM_SY_CONTROL_WORD__W 4
+#define QAM_SY_CONTROL_WORD__M 0xF
+#define QAM_SY_CONTROL_WORD__PRE 0x0
+
+#define QAM_SY_CONTROL_WORD_CTRL_WORD__B 0
+#define QAM_SY_CONTROL_WORD_CTRL_WORD__W 4
+#define QAM_SY_CONTROL_WORD_CTRL_WORD__M 0xF
+#define QAM_SY_CONTROL_WORD_CTRL_WORD__PRE 0x0
+
+#define QAM_VD_ISS_RAM__A 0x1480000
+
+#define QAM_VD_QSS_RAM__A 0x1490000
+
+#define QAM_VD_SYM_RAM__A 0x14A0000
+
+#define SCU_COMM_EXEC__A 0x800000
+#define SCU_COMM_EXEC__W 2
+#define SCU_COMM_EXEC__M 0x3
+#define SCU_COMM_EXEC__PRE 0x0
+#define SCU_COMM_EXEC_STOP 0x0
+#define SCU_COMM_EXEC_ACTIVE 0x1
+#define SCU_COMM_EXEC_HOLD 0x2
+
+#define SCU_COMM_STATE__A 0x800001
+#define SCU_COMM_STATE__W 16
+#define SCU_COMM_STATE__M 0xFFFF
+#define SCU_COMM_STATE__PRE 0x0
+
+#define SCU_COMM_STATE_COMM_STATE__B 0
+#define SCU_COMM_STATE_COMM_STATE__W 16
+#define SCU_COMM_STATE_COMM_STATE__M 0xFFFF
+#define SCU_COMM_STATE_COMM_STATE__PRE 0x0
+
+#define SCU_TOP_COMM_EXEC__A 0x810000
+#define SCU_TOP_COMM_EXEC__W 2
+#define SCU_TOP_COMM_EXEC__M 0x3
+#define SCU_TOP_COMM_EXEC__PRE 0x0
+#define SCU_TOP_COMM_EXEC_STOP 0x0
+#define SCU_TOP_COMM_EXEC_ACTIVE 0x1
+#define SCU_TOP_COMM_EXEC_HOLD 0x2
+
+#define SCU_TOP_COMM_STATE__A 0x810001
+#define SCU_TOP_COMM_STATE__W 16
+#define SCU_TOP_COMM_STATE__M 0xFFFF
+#define SCU_TOP_COMM_STATE__PRE 0x0
+#define SCU_TOP_MWAIT_CTR__A 0x810010
+#define SCU_TOP_MWAIT_CTR__W 2
+#define SCU_TOP_MWAIT_CTR__M 0x3
+#define SCU_TOP_MWAIT_CTR__PRE 0x0
+
+#define SCU_TOP_MWAIT_CTR_MWAIT_SEL__B 0
+#define SCU_TOP_MWAIT_CTR_MWAIT_SEL__W 1
+#define SCU_TOP_MWAIT_CTR_MWAIT_SEL__M 0x1
+#define SCU_TOP_MWAIT_CTR_MWAIT_SEL__PRE 0x0
+#define SCU_TOP_MWAIT_CTR_MWAIT_SEL_TR_MW_OFF 0x0
+#define SCU_TOP_MWAIT_CTR_MWAIT_SEL_TR_MW_ON 0x1
+
+#define SCU_TOP_MWAIT_CTR_READY_DIS__B 1
+#define SCU_TOP_MWAIT_CTR_READY_DIS__W 1
+#define SCU_TOP_MWAIT_CTR_READY_DIS__M 0x2
+#define SCU_TOP_MWAIT_CTR_READY_DIS__PRE 0x0
+#define SCU_TOP_MWAIT_CTR_READY_DIS_NMI_ON 0x0
+#define SCU_TOP_MWAIT_CTR_READY_DIS_NMI_OFF 0x2
+
+#define SCU_LOW_RAM__A 0x820000
+
+#define SCU_LOW_RAM_LOW__B 0
+#define SCU_LOW_RAM_LOW__W 16
+#define SCU_LOW_RAM_LOW__M 0xFFFF
+#define SCU_LOW_RAM_LOW__PRE 0x0
+
+#define SCU_HIGH_RAM__A 0x830000
+
+#define SCU_HIGH_RAM_HIGH__B 0
+#define SCU_HIGH_RAM_HIGH__W 16
+#define SCU_HIGH_RAM_HIGH__M 0xFFFF
+#define SCU_HIGH_RAM_HIGH__PRE 0x0
+
+#define SCU_RAM_AGC_RF_MAX__A 0x831E96
+#define SCU_RAM_AGC_RF_MAX__W 15
+#define SCU_RAM_AGC_RF_MAX__M 0x7FFF
+#define SCU_RAM_AGC_RF_MAX__PRE 0x0
+
+#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A 0x831E97
+#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__W 16
+#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__M 0xFFFF
+#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__PRE 0x0
+
+#define SCU_RAM_AGC_KI_CYCCNT__A 0x831E98
+#define SCU_RAM_AGC_KI_CYCCNT__W 16
+#define SCU_RAM_AGC_KI_CYCCNT__M 0xFFFF
+#define SCU_RAM_AGC_KI_CYCCNT__PRE 0x0
+
+#define SCU_RAM_AGC_KI_CYCLEN__A 0x831E99
+#define SCU_RAM_AGC_KI_CYCLEN__W 16
+#define SCU_RAM_AGC_KI_CYCLEN__M 0xFFFF
+#define SCU_RAM_AGC_KI_CYCLEN__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_CYCLEN__A 0x831E9A
+#define SCU_RAM_AGC_SNS_CYCLEN__W 16
+#define SCU_RAM_AGC_SNS_CYCLEN__M 0xFFFF
+#define SCU_RAM_AGC_SNS_CYCLEN__PRE 0x0
+
+#define SCU_RAM_AGC_RF_SNS_DEV_MAX__A 0x831E9B
+#define SCU_RAM_AGC_RF_SNS_DEV_MAX__W 16
+#define SCU_RAM_AGC_RF_SNS_DEV_MAX__M 0xFFFF
+#define SCU_RAM_AGC_RF_SNS_DEV_MAX__PRE 0x0
+
+#define SCU_RAM_AGC_RF_SNS_DEV_MIN__A 0x831E9C
+#define SCU_RAM_AGC_RF_SNS_DEV_MIN__W 16
+#define SCU_RAM_AGC_RF_SNS_DEV_MIN__M 0xFFFF
+#define SCU_RAM_AGC_RF_SNS_DEV_MIN__PRE 0x0
+#define SCU_RAM_AGC_KI__A 0x831E9D
+#define SCU_RAM_AGC_KI__W 15
+#define SCU_RAM_AGC_KI__M 0x7FFF
+#define SCU_RAM_AGC_KI__PRE 0x0
+
+#define SCU_RAM_AGC_KI_DGAIN__B 0
+#define SCU_RAM_AGC_KI_DGAIN__W 4
+#define SCU_RAM_AGC_KI_DGAIN__M 0xF
+#define SCU_RAM_AGC_KI_DGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_RF__B 4
+#define SCU_RAM_AGC_KI_RF__W 4
+#define SCU_RAM_AGC_KI_RF__M 0xF0
+#define SCU_RAM_AGC_KI_RF__PRE 0x0
+
+#define SCU_RAM_AGC_KI_IF__B 8
+#define SCU_RAM_AGC_KI_IF__W 4
+#define SCU_RAM_AGC_KI_IF__M 0xF00
+#define SCU_RAM_AGC_KI_IF__PRE 0x0
+
+#define SCU_RAM_AGC_KI_IF_AGC_DISABLE__B 12
+#define SCU_RAM_AGC_KI_IF_AGC_DISABLE__W 1
+#define SCU_RAM_AGC_KI_IF_AGC_DISABLE__M 0x1000
+#define SCU_RAM_AGC_KI_IF_AGC_DISABLE__PRE 0x0
+
+#define SCU_RAM_AGC_KI_INV_IF_POL__B 13
+#define SCU_RAM_AGC_KI_INV_IF_POL__W 1
+#define SCU_RAM_AGC_KI_INV_IF_POL__M 0x2000
+#define SCU_RAM_AGC_KI_INV_IF_POL__PRE 0x0
+
+#define SCU_RAM_AGC_KI_INV_RF_POL__B 14
+#define SCU_RAM_AGC_KI_INV_RF_POL__W 1
+#define SCU_RAM_AGC_KI_INV_RF_POL__M 0x4000
+#define SCU_RAM_AGC_KI_INV_RF_POL__PRE 0x0
+
+#define SCU_RAM_AGC_KI_RED__A 0x831E9E
+#define SCU_RAM_AGC_KI_RED__W 6
+#define SCU_RAM_AGC_KI_RED__M 0x3F
+#define SCU_RAM_AGC_KI_RED__PRE 0x0
+
+#define SCU_RAM_AGC_KI_RED_INNER_RED__B 0
+#define SCU_RAM_AGC_KI_RED_INNER_RED__W 2
+#define SCU_RAM_AGC_KI_RED_INNER_RED__M 0x3
+#define SCU_RAM_AGC_KI_RED_INNER_RED__PRE 0x0
+
+#define SCU_RAM_AGC_KI_RED_RAGC_RED__B 2
+#define SCU_RAM_AGC_KI_RED_RAGC_RED__W 2
+#define SCU_RAM_AGC_KI_RED_RAGC_RED__M 0xC
+#define SCU_RAM_AGC_KI_RED_RAGC_RED__PRE 0x0
+
+#define SCU_RAM_AGC_KI_RED_IAGC_RED__B 4
+#define SCU_RAM_AGC_KI_RED_IAGC_RED__W 2
+#define SCU_RAM_AGC_KI_RED_IAGC_RED__M 0x30
+#define SCU_RAM_AGC_KI_RED_IAGC_RED__PRE 0x0
+
+#define SCU_RAM_AGC_KI_INNERGAIN_MIN__A 0x831E9F
+#define SCU_RAM_AGC_KI_INNERGAIN_MIN__W 16
+#define SCU_RAM_AGC_KI_INNERGAIN_MIN__M 0xFFFF
+#define SCU_RAM_AGC_KI_INNERGAIN_MIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MINGAIN__A 0x831EA0
+#define SCU_RAM_AGC_KI_MINGAIN__W 16
+#define SCU_RAM_AGC_KI_MINGAIN__M 0xFFFF
+#define SCU_RAM_AGC_KI_MINGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAXGAIN__A 0x831EA1
+#define SCU_RAM_AGC_KI_MAXGAIN__W 16
+#define SCU_RAM_AGC_KI_MAXGAIN__M 0xFFFF
+#define SCU_RAM_AGC_KI_MAXGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__A 0x831EA2
+#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__W 16
+#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__M 0xFFFF
+#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__PRE 0x0
+#define SCU_RAM_AGC_KI_MIN__A 0x831EA3
+#define SCU_RAM_AGC_KI_MIN__W 12
+#define SCU_RAM_AGC_KI_MIN__M 0xFFF
+#define SCU_RAM_AGC_KI_MIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MIN_DGAIN__B 0
+#define SCU_RAM_AGC_KI_MIN_DGAIN__W 4
+#define SCU_RAM_AGC_KI_MIN_DGAIN__M 0xF
+#define SCU_RAM_AGC_KI_MIN_DGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MIN_RF__B 4
+#define SCU_RAM_AGC_KI_MIN_RF__W 4
+#define SCU_RAM_AGC_KI_MIN_RF__M 0xF0
+#define SCU_RAM_AGC_KI_MIN_RF__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MIN_IF__B 8
+#define SCU_RAM_AGC_KI_MIN_IF__W 4
+#define SCU_RAM_AGC_KI_MIN_IF__M 0xF00
+#define SCU_RAM_AGC_KI_MIN_IF__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAX__A 0x831EA4
+#define SCU_RAM_AGC_KI_MAX__W 12
+#define SCU_RAM_AGC_KI_MAX__M 0xFFF
+#define SCU_RAM_AGC_KI_MAX__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAX_DGAIN__B 0
+#define SCU_RAM_AGC_KI_MAX_DGAIN__W 4
+#define SCU_RAM_AGC_KI_MAX_DGAIN__M 0xF
+#define SCU_RAM_AGC_KI_MAX_DGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAX_RF__B 4
+#define SCU_RAM_AGC_KI_MAX_RF__W 4
+#define SCU_RAM_AGC_KI_MAX_RF__M 0xF0
+#define SCU_RAM_AGC_KI_MAX_RF__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAX_IF__B 8
+#define SCU_RAM_AGC_KI_MAX_IF__W 4
+#define SCU_RAM_AGC_KI_MAX_IF__M 0xF00
+#define SCU_RAM_AGC_KI_MAX_IF__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_SUM__A 0x831EA5
+#define SCU_RAM_AGC_CLP_SUM__W 16
+#define SCU_RAM_AGC_CLP_SUM__M 0xFFFF
+#define SCU_RAM_AGC_CLP_SUM__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_SUM_MIN__A 0x831EA6
+#define SCU_RAM_AGC_CLP_SUM_MIN__W 16
+#define SCU_RAM_AGC_CLP_SUM_MIN__M 0xFFFF
+#define SCU_RAM_AGC_CLP_SUM_MIN__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_SUM_MAX__A 0x831EA7
+#define SCU_RAM_AGC_CLP_SUM_MAX__W 16
+#define SCU_RAM_AGC_CLP_SUM_MAX__M 0xFFFF
+#define SCU_RAM_AGC_CLP_SUM_MAX__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_CYCLEN__A 0x831EA8
+#define SCU_RAM_AGC_CLP_CYCLEN__W 16
+#define SCU_RAM_AGC_CLP_CYCLEN__M 0xFFFF
+#define SCU_RAM_AGC_CLP_CYCLEN__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_CYCCNT__A 0x831EA9
+#define SCU_RAM_AGC_CLP_CYCCNT__W 16
+#define SCU_RAM_AGC_CLP_CYCCNT__M 0xFFFF
+#define SCU_RAM_AGC_CLP_CYCCNT__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_DIR_TO__A 0x831EAA
+#define SCU_RAM_AGC_CLP_DIR_TO__W 8
+#define SCU_RAM_AGC_CLP_DIR_TO__M 0xFF
+#define SCU_RAM_AGC_CLP_DIR_TO__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_DIR_WD__A 0x831EAB
+#define SCU_RAM_AGC_CLP_DIR_WD__W 8
+#define SCU_RAM_AGC_CLP_DIR_WD__M 0xFF
+#define SCU_RAM_AGC_CLP_DIR_WD__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_DIR_STP__A 0x831EAC
+#define SCU_RAM_AGC_CLP_DIR_STP__W 16
+#define SCU_RAM_AGC_CLP_DIR_STP__M 0xFFFF
+#define SCU_RAM_AGC_CLP_DIR_STP__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_SUM__A 0x831EAD
+#define SCU_RAM_AGC_SNS_SUM__W 16
+#define SCU_RAM_AGC_SNS_SUM__M 0xFFFF
+#define SCU_RAM_AGC_SNS_SUM__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_SUM_MIN__A 0x831EAE
+#define SCU_RAM_AGC_SNS_SUM_MIN__W 16
+#define SCU_RAM_AGC_SNS_SUM_MIN__M 0xFFFF
+#define SCU_RAM_AGC_SNS_SUM_MIN__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_SUM_MAX__A 0x831EAF
+#define SCU_RAM_AGC_SNS_SUM_MAX__W 16
+#define SCU_RAM_AGC_SNS_SUM_MAX__M 0xFFFF
+#define SCU_RAM_AGC_SNS_SUM_MAX__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_CYCCNT__A 0x831EB0
+#define SCU_RAM_AGC_SNS_CYCCNT__W 16
+#define SCU_RAM_AGC_SNS_CYCCNT__M 0xFFFF
+#define SCU_RAM_AGC_SNS_CYCCNT__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_DIR_TO__A 0x831EB1
+#define SCU_RAM_AGC_SNS_DIR_TO__W 8
+#define SCU_RAM_AGC_SNS_DIR_TO__M 0xFF
+#define SCU_RAM_AGC_SNS_DIR_TO__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_DIR_WD__A 0x831EB2
+#define SCU_RAM_AGC_SNS_DIR_WD__W 8
+#define SCU_RAM_AGC_SNS_DIR_WD__M 0xFF
+#define SCU_RAM_AGC_SNS_DIR_WD__PRE 0x0
+
+#define SCU_RAM_AGC_SNS_DIR_STP__A 0x831EB3
+#define SCU_RAM_AGC_SNS_DIR_STP__W 16
+#define SCU_RAM_AGC_SNS_DIR_STP__M 0xFFFF
+#define SCU_RAM_AGC_SNS_DIR_STP__PRE 0x0
+
+#define SCU_RAM_AGC_INGAIN__A 0x831EB4
+#define SCU_RAM_AGC_INGAIN__W 16
+#define SCU_RAM_AGC_INGAIN__M 0xFFFF
+#define SCU_RAM_AGC_INGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_INGAIN_TGT__A 0x831EB5
+#define SCU_RAM_AGC_INGAIN_TGT__W 15
+#define SCU_RAM_AGC_INGAIN_TGT__M 0x7FFF
+#define SCU_RAM_AGC_INGAIN_TGT__PRE 0x0
+
+#define SCU_RAM_AGC_INGAIN_TGT_MIN__A 0x831EB6
+#define SCU_RAM_AGC_INGAIN_TGT_MIN__W 15
+#define SCU_RAM_AGC_INGAIN_TGT_MIN__M 0x7FFF
+#define SCU_RAM_AGC_INGAIN_TGT_MIN__PRE 0x0
+
+#define SCU_RAM_AGC_INGAIN_TGT_MAX__A 0x831EB7
+#define SCU_RAM_AGC_INGAIN_TGT_MAX__W 15
+#define SCU_RAM_AGC_INGAIN_TGT_MAX__M 0x7FFF
+#define SCU_RAM_AGC_INGAIN_TGT_MAX__PRE 0x0
+
+#define SCU_RAM_AGC_IF_IACCU_HI__A 0x831EB8
+#define SCU_RAM_AGC_IF_IACCU_HI__W 16
+#define SCU_RAM_AGC_IF_IACCU_HI__M 0xFFFF
+#define SCU_RAM_AGC_IF_IACCU_HI__PRE 0x0
+
+#define SCU_RAM_AGC_IF_IACCU_LO__A 0x831EB9
+#define SCU_RAM_AGC_IF_IACCU_LO__W 8
+#define SCU_RAM_AGC_IF_IACCU_LO__M 0xFF
+#define SCU_RAM_AGC_IF_IACCU_LO__PRE 0x0
+
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT__A 0x831EBA
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT__W 15
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT__M 0x7FFF
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT__PRE 0x0
+
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A 0x831EBB
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__W 15
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__M 0x7FFF
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__PRE 0x0
+
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A 0x831EBC
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__W 15
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__M 0x7FFF
+#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__PRE 0x0
+
+#define SCU_RAM_AGC_RF_IACCU_HI__A 0x831EBD
+#define SCU_RAM_AGC_RF_IACCU_HI__W 16
+#define SCU_RAM_AGC_RF_IACCU_HI__M 0xFFFF
+#define SCU_RAM_AGC_RF_IACCU_HI__PRE 0x0
+
+#define SCU_RAM_AGC_RF_IACCU_LO__A 0x831EBE
+#define SCU_RAM_AGC_RF_IACCU_LO__W 8
+#define SCU_RAM_AGC_RF_IACCU_LO__M 0xFF
+#define SCU_RAM_AGC_RF_IACCU_LO__PRE 0x0
+
+#define SCU_RAM_AGC_RF_IACCU_HI_CO__A 0x831EBF
+#define SCU_RAM_AGC_RF_IACCU_HI_CO__W 16
+#define SCU_RAM_AGC_RF_IACCU_HI_CO__M 0xFFFF
+#define SCU_RAM_AGC_RF_IACCU_HI_CO__PRE 0x0
+
+#define SCU_RAM_SP__A 0x831EC0
+#define SCU_RAM_SP__W 16
+#define SCU_RAM_SP__M 0xFFFF
+#define SCU_RAM_SP__PRE 0x0
+
+#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A 0x831EC1
+#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__W 16
+#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__M 0xFFFF
+#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MIN_IFGAIN__A 0x831EC2
+#define SCU_RAM_AGC_KI_MIN_IFGAIN__W 16
+#define SCU_RAM_AGC_KI_MIN_IFGAIN__M 0xFFFF
+#define SCU_RAM_AGC_KI_MIN_IFGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAX_IFGAIN__A 0x831EC3
+#define SCU_RAM_AGC_KI_MAX_IFGAIN__W 16
+#define SCU_RAM_AGC_KI_MAX_IFGAIN__M 0xFFFF
+#define SCU_RAM_AGC_KI_MAX_IFGAIN__PRE 0x0
+
+#define SCU_RAM_FEC_MEAS_COUNT__A 0x831EC4
+#define SCU_RAM_FEC_MEAS_COUNT__W 16
+#define SCU_RAM_FEC_MEAS_COUNT__M 0xFFFF
+#define SCU_RAM_FEC_MEAS_COUNT__PRE 0x0
+
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__A 0x831EC5
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__W 16
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__M 0xFFFF
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_LO__PRE 0x0
+
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_HI__A 0x831EC6
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_HI__W 16
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_HI__M 0xFFFF
+#define SCU_RAM_FEC_ACCUM_CW_CORRECTED_HI__PRE 0x0
+#define SCU_RAM_GPIO__A 0x831EC7
+#define SCU_RAM_GPIO__W 1
+#define SCU_RAM_GPIO__M 0x1
+#define SCU_RAM_GPIO__PRE 0x0
+
+#define SCU_RAM_GPIO_HW_LOCK_IND__B 0
+#define SCU_RAM_GPIO_HW_LOCK_IND__W 1
+#define SCU_RAM_GPIO_HW_LOCK_IND__M 0x1
+#define SCU_RAM_GPIO_HW_LOCK_IND__PRE 0x0
+#define SCU_RAM_GPIO_HW_LOCK_IND_DISABLE 0x0
+#define SCU_RAM_GPIO_HW_LOCK_IND_ENABLE 0x1
+
+#define SCU_RAM_AGC_CLP_CTRL_MODE__A 0x831EC8
+#define SCU_RAM_AGC_CLP_CTRL_MODE__W 8
+#define SCU_RAM_AGC_CLP_CTRL_MODE__M 0xFF
+#define SCU_RAM_AGC_CLP_CTRL_MODE__PRE 0x0
+
+#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW__B 0
+#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW__W 1
+#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW__M 0x1
+#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW__PRE 0x0
+#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW_false 0x0
+#define SCU_RAM_AGC_CLP_CTRL_MODE_NARROW_POW_true 0x1
+
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP__B 1
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP__W 1
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP__M 0x2
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP__PRE 0x0
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP_FCC_ENABLE 0x0
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_BP_FCC_DISABLE 0x2
+
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC__B 2
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC__W 1
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC__M 0x4
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC__PRE 0x0
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC_DEC_DISABLE 0x0
+#define SCU_RAM_AGC_CLP_CTRL_MODE_FAST_CLP_DEC_DEC_ENABLE 0x4
+
+#define SCU_RAM_AGC_KI_MIN_RFGAIN__A 0x831EC9
+#define SCU_RAM_AGC_KI_MIN_RFGAIN__W 16
+#define SCU_RAM_AGC_KI_MIN_RFGAIN__M 0xFFFF
+#define SCU_RAM_AGC_KI_MIN_RFGAIN__PRE 0x0
+
+#define SCU_RAM_AGC_KI_MAX_RFGAIN__A 0x831ECA
+#define SCU_RAM_AGC_KI_MAX_RFGAIN__W 16
+#define SCU_RAM_AGC_KI_MAX_RFGAIN__M 0xFFFF
+#define SCU_RAM_AGC_KI_MAX_RFGAIN__PRE 0x0
+
+#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__A 0x831ECB
+#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__W 16
+#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__M 0xFFFF
+#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__PRE 0x0
+
+#define SCU_RAM_INHIBIT_1__A 0x831ECC
+#define SCU_RAM_INHIBIT_1__W 16
+#define SCU_RAM_INHIBIT_1__M 0xFFFF
+#define SCU_RAM_INHIBIT_1__PRE 0x0
+
+#define SCU_RAM_HTOL_BUF_0__A 0x831ECD
+#define SCU_RAM_HTOL_BUF_0__W 16
+#define SCU_RAM_HTOL_BUF_0__M 0xFFFF
+#define SCU_RAM_HTOL_BUF_0__PRE 0x0
+
+#define SCU_RAM_HTOL_BUF_1__A 0x831ECE
+#define SCU_RAM_HTOL_BUF_1__W 16
+#define SCU_RAM_HTOL_BUF_1__M 0xFFFF
+#define SCU_RAM_HTOL_BUF_1__PRE 0x0
+
+#define SCU_RAM_INHIBIT_2__A 0x831ECF
+#define SCU_RAM_INHIBIT_2__W 16
+#define SCU_RAM_INHIBIT_2__M 0xFFFF
+#define SCU_RAM_INHIBIT_2__PRE 0x0
+
+#define SCU_RAM_TR_SHORT_BUF_0__A 0x831ED0
+#define SCU_RAM_TR_SHORT_BUF_0__W 16
+#define SCU_RAM_TR_SHORT_BUF_0__M 0xFFFF
+#define SCU_RAM_TR_SHORT_BUF_0__PRE 0x0
+
+#define SCU_RAM_TR_SHORT_BUF_1__A 0x831ED1
+#define SCU_RAM_TR_SHORT_BUF_1__W 16
+#define SCU_RAM_TR_SHORT_BUF_1__M 0xFFFF
+#define SCU_RAM_TR_SHORT_BUF_1__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_0__A 0x831ED2
+#define SCU_RAM_TR_LONG_BUF_0__W 16
+#define SCU_RAM_TR_LONG_BUF_0__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_0__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_1__A 0x831ED3
+#define SCU_RAM_TR_LONG_BUF_1__W 16
+#define SCU_RAM_TR_LONG_BUF_1__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_1__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_2__A 0x831ED4
+#define SCU_RAM_TR_LONG_BUF_2__W 16
+#define SCU_RAM_TR_LONG_BUF_2__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_2__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_3__A 0x831ED5
+#define SCU_RAM_TR_LONG_BUF_3__W 16
+#define SCU_RAM_TR_LONG_BUF_3__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_3__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_4__A 0x831ED6
+#define SCU_RAM_TR_LONG_BUF_4__W 16
+#define SCU_RAM_TR_LONG_BUF_4__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_4__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_5__A 0x831ED7
+#define SCU_RAM_TR_LONG_BUF_5__W 16
+#define SCU_RAM_TR_LONG_BUF_5__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_5__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_6__A 0x831ED8
+#define SCU_RAM_TR_LONG_BUF_6__W 16
+#define SCU_RAM_TR_LONG_BUF_6__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_6__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_7__A 0x831ED9
+#define SCU_RAM_TR_LONG_BUF_7__W 16
+#define SCU_RAM_TR_LONG_BUF_7__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_7__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_8__A 0x831EDA
+#define SCU_RAM_TR_LONG_BUF_8__W 16
+#define SCU_RAM_TR_LONG_BUF_8__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_8__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_9__A 0x831EDB
+#define SCU_RAM_TR_LONG_BUF_9__W 16
+#define SCU_RAM_TR_LONG_BUF_9__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_9__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_10__A 0x831EDC
+#define SCU_RAM_TR_LONG_BUF_10__W 16
+#define SCU_RAM_TR_LONG_BUF_10__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_10__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_11__A 0x831EDD
+#define SCU_RAM_TR_LONG_BUF_11__W 16
+#define SCU_RAM_TR_LONG_BUF_11__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_11__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_12__A 0x831EDE
+#define SCU_RAM_TR_LONG_BUF_12__W 16
+#define SCU_RAM_TR_LONG_BUF_12__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_12__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_13__A 0x831EDF
+#define SCU_RAM_TR_LONG_BUF_13__W 16
+#define SCU_RAM_TR_LONG_BUF_13__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_13__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_14__A 0x831EE0
+#define SCU_RAM_TR_LONG_BUF_14__W 16
+#define SCU_RAM_TR_LONG_BUF_14__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_14__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_15__A 0x831EE1
+#define SCU_RAM_TR_LONG_BUF_15__W 16
+#define SCU_RAM_TR_LONG_BUF_15__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_15__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_16__A 0x831EE2
+#define SCU_RAM_TR_LONG_BUF_16__W 16
+#define SCU_RAM_TR_LONG_BUF_16__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_16__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_17__A 0x831EE3
+#define SCU_RAM_TR_LONG_BUF_17__W 16
+#define SCU_RAM_TR_LONG_BUF_17__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_17__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_18__A 0x831EE4
+#define SCU_RAM_TR_LONG_BUF_18__W 16
+#define SCU_RAM_TR_LONG_BUF_18__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_18__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_19__A 0x831EE5
+#define SCU_RAM_TR_LONG_BUF_19__W 16
+#define SCU_RAM_TR_LONG_BUF_19__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_19__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_20__A 0x831EE6
+#define SCU_RAM_TR_LONG_BUF_20__W 16
+#define SCU_RAM_TR_LONG_BUF_20__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_20__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_21__A 0x831EE7
+#define SCU_RAM_TR_LONG_BUF_21__W 16
+#define SCU_RAM_TR_LONG_BUF_21__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_21__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_22__A 0x831EE8
+#define SCU_RAM_TR_LONG_BUF_22__W 16
+#define SCU_RAM_TR_LONG_BUF_22__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_22__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_23__A 0x831EE9
+#define SCU_RAM_TR_LONG_BUF_23__W 16
+#define SCU_RAM_TR_LONG_BUF_23__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_23__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_24__A 0x831EEA
+#define SCU_RAM_TR_LONG_BUF_24__W 16
+#define SCU_RAM_TR_LONG_BUF_24__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_24__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_25__A 0x831EEB
+#define SCU_RAM_TR_LONG_BUF_25__W 16
+#define SCU_RAM_TR_LONG_BUF_25__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_25__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_26__A 0x831EEC
+#define SCU_RAM_TR_LONG_BUF_26__W 16
+#define SCU_RAM_TR_LONG_BUF_26__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_26__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_27__A 0x831EED
+#define SCU_RAM_TR_LONG_BUF_27__W 16
+#define SCU_RAM_TR_LONG_BUF_27__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_27__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_28__A 0x831EEE
+#define SCU_RAM_TR_LONG_BUF_28__W 16
+#define SCU_RAM_TR_LONG_BUF_28__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_28__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_29__A 0x831EEF
+#define SCU_RAM_TR_LONG_BUF_29__W 16
+#define SCU_RAM_TR_LONG_BUF_29__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_29__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_30__A 0x831EF0
+#define SCU_RAM_TR_LONG_BUF_30__W 16
+#define SCU_RAM_TR_LONG_BUF_30__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_30__PRE 0x0
+
+#define SCU_RAM_TR_LONG_BUF_31__A 0x831EF1
+#define SCU_RAM_TR_LONG_BUF_31__W 16
+#define SCU_RAM_TR_LONG_BUF_31__M 0xFFFF
+#define SCU_RAM_TR_LONG_BUF_31__PRE 0x0
+#define SCU_RAM_ATV_AMS_MAX__A 0x831EF2
+#define SCU_RAM_ATV_AMS_MAX__W 11
+#define SCU_RAM_ATV_AMS_MAX__M 0x7FF
+#define SCU_RAM_ATV_AMS_MAX__PRE 0x0
+
+#define SCU_RAM_ATV_AMS_MAX_AMS_MAX__B 0
+#define SCU_RAM_ATV_AMS_MAX_AMS_MAX__W 11
+#define SCU_RAM_ATV_AMS_MAX_AMS_MAX__M 0x7FF
+#define SCU_RAM_ATV_AMS_MAX_AMS_MAX__PRE 0x0
+
+#define SCU_RAM_ATV_AMS_MIN__A 0x831EF3
+#define SCU_RAM_ATV_AMS_MIN__W 11
+#define SCU_RAM_ATV_AMS_MIN__M 0x7FF
+#define SCU_RAM_ATV_AMS_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_AMS_MIN_AMS_MIN__B 0
+#define SCU_RAM_ATV_AMS_MIN_AMS_MIN__W 11
+#define SCU_RAM_ATV_AMS_MIN_AMS_MIN__M 0x7FF
+#define SCU_RAM_ATV_AMS_MIN_AMS_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_FIELD_CNT__A 0x831EF4
+#define SCU_RAM_ATV_FIELD_CNT__W 9
+#define SCU_RAM_ATV_FIELD_CNT__M 0x1FF
+#define SCU_RAM_ATV_FIELD_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_FIELD_CNT_FIELD_CNT__B 0
+#define SCU_RAM_ATV_FIELD_CNT_FIELD_CNT__W 9
+#define SCU_RAM_ATV_FIELD_CNT_FIELD_CNT__M 0x1FF
+#define SCU_RAM_ATV_FIELD_CNT_FIELD_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_AAGC_FAST__A 0x831EF5
+#define SCU_RAM_ATV_AAGC_FAST__W 1
+#define SCU_RAM_ATV_AAGC_FAST__M 0x1
+#define SCU_RAM_ATV_AAGC_FAST__PRE 0x0
+
+#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST__B 0
+#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST__W 1
+#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST__M 0x1
+#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST__PRE 0x0
+#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST_OFF 0x0
+#define SCU_RAM_ATV_AAGC_FAST_AAGC_FAST_ON 0x1
+
+#define SCU_RAM_ATV_AAGC_LP2__A 0x831EF6
+#define SCU_RAM_ATV_AAGC_LP2__W 16
+#define SCU_RAM_ATV_AAGC_LP2__M 0xFFFF
+#define SCU_RAM_ATV_AAGC_LP2__PRE 0x0
+
+#define SCU_RAM_ATV_AAGC_LP2_AAGC_LP2__B 0
+#define SCU_RAM_ATV_AAGC_LP2_AAGC_LP2__W 16
+#define SCU_RAM_ATV_AAGC_LP2_AAGC_LP2__M 0xFFFF
+#define SCU_RAM_ATV_AAGC_LP2_AAGC_LP2__PRE 0x0
+
+#define SCU_RAM_ATV_BP_LVL__A 0x831EF7
+#define SCU_RAM_ATV_BP_LVL__W 11
+#define SCU_RAM_ATV_BP_LVL__M 0x7FF
+#define SCU_RAM_ATV_BP_LVL__PRE 0x0
+
+#define SCU_RAM_ATV_BP_LVL_BP_LVL__B 0
+#define SCU_RAM_ATV_BP_LVL_BP_LVL__W 11
+#define SCU_RAM_ATV_BP_LVL_BP_LVL__M 0x7FF
+#define SCU_RAM_ATV_BP_LVL_BP_LVL__PRE 0x0
+
+#define SCU_RAM_ATV_BP_RELY__A 0x831EF8
+#define SCU_RAM_ATV_BP_RELY__W 8
+#define SCU_RAM_ATV_BP_RELY__M 0xFF
+#define SCU_RAM_ATV_BP_RELY__PRE 0x0
+
+#define SCU_RAM_ATV_BP_RELY_BP_RELY__B 0
+#define SCU_RAM_ATV_BP_RELY_BP_RELY__W 8
+#define SCU_RAM_ATV_BP_RELY_BP_RELY__M 0xFF
+#define SCU_RAM_ATV_BP_RELY_BP_RELY__PRE 0x0
+
+#define SCU_RAM_ATV_BP_MTA__A 0x831EF9
+#define SCU_RAM_ATV_BP_MTA__W 14
+#define SCU_RAM_ATV_BP_MTA__M 0x3FFF
+#define SCU_RAM_ATV_BP_MTA__PRE 0x0
+
+#define SCU_RAM_ATV_BP_MTA_BP_MTA__B 0
+#define SCU_RAM_ATV_BP_MTA_BP_MTA__W 14
+#define SCU_RAM_ATV_BP_MTA_BP_MTA__M 0x3FFF
+#define SCU_RAM_ATV_BP_MTA_BP_MTA__PRE 0x0
+
+#define SCU_RAM_ATV_BP_REF__A 0x831EFA
+#define SCU_RAM_ATV_BP_REF__W 11
+#define SCU_RAM_ATV_BP_REF__M 0x7FF
+#define SCU_RAM_ATV_BP_REF__PRE 0x0
+
+#define SCU_RAM_ATV_BP_REF_BP_REF__B 0
+#define SCU_RAM_ATV_BP_REF_BP_REF__W 11
+#define SCU_RAM_ATV_BP_REF_BP_REF__M 0x7FF
+#define SCU_RAM_ATV_BP_REF_BP_REF__PRE 0x0
+
+#define SCU_RAM_ATV_BP_REF_MIN__A 0x831EFB
+#define SCU_RAM_ATV_BP_REF_MIN__W 11
+#define SCU_RAM_ATV_BP_REF_MIN__M 0x7FF
+#define SCU_RAM_ATV_BP_REF_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_BP_REF_MIN_BP_REF_MIN__B 0
+#define SCU_RAM_ATV_BP_REF_MIN_BP_REF_MIN__W 11
+#define SCU_RAM_ATV_BP_REF_MIN_BP_REF_MIN__M 0x7FF
+#define SCU_RAM_ATV_BP_REF_MIN_BP_REF_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_BP_REF_MAX__A 0x831EFC
+#define SCU_RAM_ATV_BP_REF_MAX__W 11
+#define SCU_RAM_ATV_BP_REF_MAX__M 0x7FF
+#define SCU_RAM_ATV_BP_REF_MAX__PRE 0x0
+
+#define SCU_RAM_ATV_BP_REF_MAX_BP_REF_MAX__B 0
+#define SCU_RAM_ATV_BP_REF_MAX_BP_REF_MAX__W 11
+#define SCU_RAM_ATV_BP_REF_MAX_BP_REF_MAX__M 0x7FF
+#define SCU_RAM_ATV_BP_REF_MAX_BP_REF_MAX__PRE 0x0
+
+#define SCU_RAM_ATV_BP_CNT__A 0x831EFD
+#define SCU_RAM_ATV_BP_CNT__W 8
+#define SCU_RAM_ATV_BP_CNT__M 0xFF
+#define SCU_RAM_ATV_BP_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_BP_CNT_BP_CNT__B 0
+#define SCU_RAM_ATV_BP_CNT_BP_CNT__W 8
+#define SCU_RAM_ATV_BP_CNT_BP_CNT__M 0xFF
+#define SCU_RAM_ATV_BP_CNT_BP_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_BP_XD_CNT__A 0x831EFE
+#define SCU_RAM_ATV_BP_XD_CNT__W 12
+#define SCU_RAM_ATV_BP_XD_CNT__M 0xFFF
+#define SCU_RAM_ATV_BP_XD_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_BP_XD_CNT_BP_XD_CNT__B 0
+#define SCU_RAM_ATV_BP_XD_CNT_BP_XD_CNT__W 12
+#define SCU_RAM_ATV_BP_XD_CNT_BP_XD_CNT__M 0xFFF
+#define SCU_RAM_ATV_BP_XD_CNT_BP_XD_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_PAGC_KI_MIN__A 0x831EFF
+#define SCU_RAM_ATV_PAGC_KI_MIN__W 12
+#define SCU_RAM_ATV_PAGC_KI_MIN__M 0xFFF
+#define SCU_RAM_ATV_PAGC_KI_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_PAGC_KI_MIN_PAGC_KI_MIN__B 0
+#define SCU_RAM_ATV_PAGC_KI_MIN_PAGC_KI_MIN__W 12
+#define SCU_RAM_ATV_PAGC_KI_MIN_PAGC_KI_MIN__M 0xFFF
+#define SCU_RAM_ATV_PAGC_KI_MIN_PAGC_KI_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_BPC_KI_MIN__A 0x831F00
+#define SCU_RAM_ATV_BPC_KI_MIN__W 12
+#define SCU_RAM_ATV_BPC_KI_MIN__M 0xFFF
+#define SCU_RAM_ATV_BPC_KI_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_BPC_KI_MIN_BPC_KI_MIN__B 0
+#define SCU_RAM_ATV_BPC_KI_MIN_BPC_KI_MIN__W 12
+#define SCU_RAM_ATV_BPC_KI_MIN_BPC_KI_MIN__M 0xFFF
+#define SCU_RAM_ATV_BPC_KI_MIN_BPC_KI_MIN__PRE 0x0
+
+#define SCU_RAM_ORX_RF_RX_FREQUENCY_VALUE__A 0x831F01
+#define SCU_RAM_ORX_RF_RX_FREQUENCY_VALUE__W 16
+#define SCU_RAM_ORX_RF_RX_FREQUENCY_VALUE__M 0xFFFF
+#define SCU_RAM_ORX_RF_RX_FREQUENCY_VALUE__PRE 0x0
+
+#define SCU_RAM_ORX_RF_RX_DATA_RATE__A 0x831F02
+#define SCU_RAM_ORX_RF_RX_DATA_RATE__W 8
+#define SCU_RAM_ORX_RF_RX_DATA_RATE__M 0xFF
+#define SCU_RAM_ORX_RF_RX_DATA_RATE__PRE 0x0
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_REGSPEC 0x0
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_INVSPEC 0x1
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_REGSPEC_ALT 0x40
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_2048KBPS_INVSPEC_ALT 0x41
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_1544KBPS_REGSPEC 0x80
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_1544KBPS_INVSPEC 0x81
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_3088KBPS_REGSPEC 0xC0
+#define SCU_RAM_ORX_RF_RX_DATA_RATE_3088KBPS_INVSPEC 0xC1
+
+#define SCU_RAM_ORX_SCU_STATE__A 0x831F03
+#define SCU_RAM_ORX_SCU_STATE__W 8
+#define SCU_RAM_ORX_SCU_STATE__M 0xFF
+#define SCU_RAM_ORX_SCU_STATE__PRE 0x0
+#define SCU_RAM_ORX_SCU_STATE_RESET 0x0
+#define SCU_RAM_ORX_SCU_STATE_AGN_HUNT 0x1
+#define SCU_RAM_ORX_SCU_STATE_DGN_HUNT 0x2
+#define SCU_RAM_ORX_SCU_STATE_AGC_HUNT 0x3
+#define SCU_RAM_ORX_SCU_STATE_FRQ_HUNT 0x4
+#define SCU_RAM_ORX_SCU_STATE_PHA_HUNT 0x8
+#define SCU_RAM_ORX_SCU_STATE_TIM_HUNT 0x10
+#define SCU_RAM_ORX_SCU_STATE_EQU_HUNT 0x20
+#define SCU_RAM_ORX_SCU_STATE_EQT_HUNT 0x30
+#define SCU_RAM_ORX_SCU_STATE_SYNC 0x40
+
+#define SCU_RAM_ORX_SCU_LOCK__A 0x831F04
+#define SCU_RAM_ORX_SCU_LOCK__W 16
+#define SCU_RAM_ORX_SCU_LOCK__M 0xFFFF
+#define SCU_RAM_ORX_SCU_LOCK__PRE 0x0
+
+#define SCU_RAM_ORX_TARGET_MODE__A 0x831F05
+#define SCU_RAM_ORX_TARGET_MODE__W 2
+#define SCU_RAM_ORX_TARGET_MODE__M 0x3
+#define SCU_RAM_ORX_TARGET_MODE__PRE 0x0
+#define SCU_RAM_ORX_TARGET_MODE_1544KBPS 0x0
+#define SCU_RAM_ORX_TARGET_MODE_3088KBPS 0x1
+#define SCU_RAM_ORX_TARGET_MODE_2048KBPS_SQRT 0x2
+#define SCU_RAM_ORX_TARGET_MODE_2048KBPS_RO 0x3
+
+#define SCU_RAM_ORX_MER_MIN_DB__A 0x831F06
+#define SCU_RAM_ORX_MER_MIN_DB__W 8
+#define SCU_RAM_ORX_MER_MIN_DB__M 0xFF
+#define SCU_RAM_ORX_MER_MIN_DB__PRE 0x0
+
+#define SCU_RAM_ORX_RF_GAIN__A 0x831F07
+#define SCU_RAM_ORX_RF_GAIN__W 16
+#define SCU_RAM_ORX_RF_GAIN__M 0xFFFF
+#define SCU_RAM_ORX_RF_GAIN__PRE 0x0
+
+#define SCU_RAM_ORX_RF_GAIN_MIN__A 0x831F08
+#define SCU_RAM_ORX_RF_GAIN_MIN__W 16
+#define SCU_RAM_ORX_RF_GAIN_MIN__M 0xFFFF
+#define SCU_RAM_ORX_RF_GAIN_MIN__PRE 0x0
+
+#define SCU_RAM_ORX_RF_GAIN_MAX__A 0x831F09
+#define SCU_RAM_ORX_RF_GAIN_MAX__W 16
+#define SCU_RAM_ORX_RF_GAIN_MAX__M 0xFFFF
+#define SCU_RAM_ORX_RF_GAIN_MAX__PRE 0x0
+
+#define SCU_RAM_ORX_IF_GAIN__A 0x831F0A
+#define SCU_RAM_ORX_IF_GAIN__W 16
+#define SCU_RAM_ORX_IF_GAIN__M 0xFFFF
+#define SCU_RAM_ORX_IF_GAIN__PRE 0x0
+
+#define SCU_RAM_ORX_IF_GAIN_MIN__A 0x831F0B
+#define SCU_RAM_ORX_IF_GAIN_MIN__W 16
+#define SCU_RAM_ORX_IF_GAIN_MIN__M 0xFFFF
+#define SCU_RAM_ORX_IF_GAIN_MIN__PRE 0x0
+
+#define SCU_RAM_ORX_IF_GAIN_MAX__A 0x831F0C
+#define SCU_RAM_ORX_IF_GAIN_MAX__W 16
+#define SCU_RAM_ORX_IF_GAIN_MAX__M 0xFFFF
+#define SCU_RAM_ORX_IF_GAIN_MAX__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_HEADR__A 0x831F0D
+#define SCU_RAM_ORX_AGN_HEADR__W 16
+#define SCU_RAM_ORX_AGN_HEADR__M 0xFFFF
+#define SCU_RAM_ORX_AGN_HEADR__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_HEADR_STP__A 0x831F0E
+#define SCU_RAM_ORX_AGN_HEADR_STP__W 8
+#define SCU_RAM_ORX_AGN_HEADR_STP__M 0xFF
+#define SCU_RAM_ORX_AGN_HEADR_STP__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_KI__A 0x831F0F
+#define SCU_RAM_ORX_AGN_KI__W 8
+#define SCU_RAM_ORX_AGN_KI__M 0xFF
+#define SCU_RAM_ORX_AGN_KI__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_LOCK_TH__A 0x831F10
+#define SCU_RAM_ORX_AGN_LOCK_TH__W 16
+#define SCU_RAM_ORX_AGN_LOCK_TH__M 0xFFFF
+#define SCU_RAM_ORX_AGN_LOCK_TH__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_LOCK_WD__A 0x831F11
+#define SCU_RAM_ORX_AGN_LOCK_WD__W 16
+#define SCU_RAM_ORX_AGN_LOCK_WD__M 0xFFFF
+#define SCU_RAM_ORX_AGN_LOCK_WD__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_ONLOCK_TTH__A 0x831F12
+#define SCU_RAM_ORX_AGN_ONLOCK_TTH__W 16
+#define SCU_RAM_ORX_AGN_ONLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_AGN_ONLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_UNLOCK_TTH__A 0x831F13
+#define SCU_RAM_ORX_AGN_UNLOCK_TTH__W 16
+#define SCU_RAM_ORX_AGN_UNLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_AGN_UNLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_LOCK_TOTH__A 0x831F14
+#define SCU_RAM_ORX_AGN_LOCK_TOTH__W 16
+#define SCU_RAM_ORX_AGN_LOCK_TOTH__M 0xFFFF
+#define SCU_RAM_ORX_AGN_LOCK_TOTH__PRE 0x0
+
+#define SCU_RAM_ORX_AGN_LOCK_MASK__A 0x831F15
+#define SCU_RAM_ORX_AGN_LOCK_MASK__W 8
+#define SCU_RAM_ORX_AGN_LOCK_MASK__M 0xFF
+#define SCU_RAM_ORX_AGN_LOCK_MASK__PRE 0x0
+
+#define SCU_RAM_ORX_DGN__A 0x831F16
+#define SCU_RAM_ORX_DGN__W 16
+#define SCU_RAM_ORX_DGN__M 0xFFFF
+#define SCU_RAM_ORX_DGN__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_MIN__A 0x831F17
+#define SCU_RAM_ORX_DGN_MIN__W 16
+#define SCU_RAM_ORX_DGN_MIN__M 0xFFFF
+#define SCU_RAM_ORX_DGN_MIN__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_MAX__A 0x831F18
+#define SCU_RAM_ORX_DGN_MAX__W 16
+#define SCU_RAM_ORX_DGN_MAX__M 0xFFFF
+#define SCU_RAM_ORX_DGN_MAX__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_AMP__A 0x831F19
+#define SCU_RAM_ORX_DGN_AMP__W 16
+#define SCU_RAM_ORX_DGN_AMP__M 0xFFFF
+#define SCU_RAM_ORX_DGN_AMP__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_AMPTARGET__A 0x831F1A
+#define SCU_RAM_ORX_DGN_AMPTARGET__W 16
+#define SCU_RAM_ORX_DGN_AMPTARGET__M 0xFFFF
+#define SCU_RAM_ORX_DGN_AMPTARGET__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_KI__A 0x831F1B
+#define SCU_RAM_ORX_DGN_KI__W 8
+#define SCU_RAM_ORX_DGN_KI__M 0xFF
+#define SCU_RAM_ORX_DGN_KI__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_LOCK_TH__A 0x831F1C
+#define SCU_RAM_ORX_DGN_LOCK_TH__W 16
+#define SCU_RAM_ORX_DGN_LOCK_TH__M 0xFFFF
+#define SCU_RAM_ORX_DGN_LOCK_TH__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_LOCK_WD__A 0x831F1D
+#define SCU_RAM_ORX_DGN_LOCK_WD__W 16
+#define SCU_RAM_ORX_DGN_LOCK_WD__M 0xFFFF
+#define SCU_RAM_ORX_DGN_LOCK_WD__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_ONLOCK_TTH__A 0x831F1E
+#define SCU_RAM_ORX_DGN_ONLOCK_TTH__W 16
+#define SCU_RAM_ORX_DGN_ONLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_DGN_ONLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_UNLOCK_TTH__A 0x831F1F
+#define SCU_RAM_ORX_DGN_UNLOCK_TTH__W 16
+#define SCU_RAM_ORX_DGN_UNLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_DGN_UNLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_LOCK_TOTH__A 0x831F20
+#define SCU_RAM_ORX_DGN_LOCK_TOTH__W 16
+#define SCU_RAM_ORX_DGN_LOCK_TOTH__M 0xFFFF
+#define SCU_RAM_ORX_DGN_LOCK_TOTH__PRE 0x0
+
+#define SCU_RAM_ORX_DGN_LOCK_MASK__A 0x831F21
+#define SCU_RAM_ORX_DGN_LOCK_MASK__W 8
+#define SCU_RAM_ORX_DGN_LOCK_MASK__M 0xFF
+#define SCU_RAM_ORX_DGN_LOCK_MASK__PRE 0x0
+
+#define SCU_RAM_ORX_FREQ_GAIN_CORR__A 0x831F22
+#define SCU_RAM_ORX_FREQ_GAIN_CORR__W 8
+#define SCU_RAM_ORX_FREQ_GAIN_CORR__M 0xFF
+#define SCU_RAM_ORX_FREQ_GAIN_CORR__PRE 0x0
+#define SCU_RAM_ORX_FREQ_GAIN_CORR_1544KBPS 0x60
+#define SCU_RAM_ORX_FREQ_GAIN_CORR_2048KBPS 0x80
+#define SCU_RAM_ORX_FREQ_GAIN_CORR_3088KBPS 0xC0
+
+#define SCU_RAM_ORX_FRQ_OFFSET__A 0x831F23
+#define SCU_RAM_ORX_FRQ_OFFSET__W 16
+#define SCU_RAM_ORX_FRQ_OFFSET__M 0xFFFF
+#define SCU_RAM_ORX_FRQ_OFFSET__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_OFFSET_MAX__A 0x831F24
+#define SCU_RAM_ORX_FRQ_OFFSET_MAX__W 15
+#define SCU_RAM_ORX_FRQ_OFFSET_MAX__M 0x7FFF
+#define SCU_RAM_ORX_FRQ_OFFSET_MAX__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_KI__A 0x831F25
+#define SCU_RAM_ORX_FRQ_KI__W 8
+#define SCU_RAM_ORX_FRQ_KI__M 0xFF
+#define SCU_RAM_ORX_FRQ_KI__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_DIFF__A 0x831F26
+#define SCU_RAM_ORX_FRQ_DIFF__W 16
+#define SCU_RAM_ORX_FRQ_DIFF__M 0xFFFF
+#define SCU_RAM_ORX_FRQ_DIFF__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_LOCK_TH__A 0x831F27
+#define SCU_RAM_ORX_FRQ_LOCK_TH__W 16
+#define SCU_RAM_ORX_FRQ_LOCK_TH__M 0xFFFF
+#define SCU_RAM_ORX_FRQ_LOCK_TH__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_LOCK_WD__A 0x831F28
+#define SCU_RAM_ORX_FRQ_LOCK_WD__W 16
+#define SCU_RAM_ORX_FRQ_LOCK_WD__M 0xFFFF
+#define SCU_RAM_ORX_FRQ_LOCK_WD__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_ONLOCK_TTH__A 0x831F29
+#define SCU_RAM_ORX_FRQ_ONLOCK_TTH__W 16
+#define SCU_RAM_ORX_FRQ_ONLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_FRQ_ONLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_UNLOCK_TTH__A 0x831F2A
+#define SCU_RAM_ORX_FRQ_UNLOCK_TTH__W 16
+#define SCU_RAM_ORX_FRQ_UNLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_FRQ_UNLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_LOCK_TOTH__A 0x831F2B
+#define SCU_RAM_ORX_FRQ_LOCK_TOTH__W 16
+#define SCU_RAM_ORX_FRQ_LOCK_TOTH__M 0xFFFF
+#define SCU_RAM_ORX_FRQ_LOCK_TOTH__PRE 0x0
+
+#define SCU_RAM_ORX_FRQ_LOCK_MASK__A 0x831F2C
+#define SCU_RAM_ORX_FRQ_LOCK_MASK__W 8
+#define SCU_RAM_ORX_FRQ_LOCK_MASK__M 0xFF
+#define SCU_RAM_ORX_FRQ_LOCK_MASK__PRE 0x0
+
+#define SCU_RAM_ORX_PHA_DIFF__A 0x831F2D
+#define SCU_RAM_ORX_PHA_DIFF__W 16
+#define SCU_RAM_ORX_PHA_DIFF__M 0xFFFF
+#define SCU_RAM_ORX_PHA_DIFF__PRE 0x0
+
+#define SCU_RAM_ORX_PHA_LOCK_TH__A 0x831F2E
+#define SCU_RAM_ORX_PHA_LOCK_TH__W 16
+#define SCU_RAM_ORX_PHA_LOCK_TH__M 0xFFFF
+#define SCU_RAM_ORX_PHA_LOCK_TH__PRE 0x0
+
+#define SCU_RAM_ORX_PHA_LOCK_WD__A 0x831F2F
+#define SCU_RAM_ORX_PHA_LOCK_WD__W 16
+#define SCU_RAM_ORX_PHA_LOCK_WD__M 0xFFFF
+#define SCU_RAM_ORX_PHA_LOCK_WD__PRE 0x0
+
+#define SCU_RAM_ORX_PHA_ONLOCK_TTH__A 0x831F30
+#define SCU_RAM_ORX_PHA_ONLOCK_TTH__W 16
+#define SCU_RAM_ORX_PHA_ONLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_PHA_ONLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_PHA_UNLOCK_TTH__A 0x831F31
+#define SCU_RAM_ORX_PHA_UNLOCK_TTH__W 16
+#define SCU_RAM_ORX_PHA_UNLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_PHA_UNLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_PHA_LOCK_TOTH__A 0x831F32
+#define SCU_RAM_ORX_PHA_LOCK_TOTH__W 16
+#define SCU_RAM_ORX_PHA_LOCK_TOTH__M 0xFFFF
+#define SCU_RAM_ORX_PHA_LOCK_TOTH__PRE 0x0
+
+#define SCU_RAM_ORX_PHA_LOCK_MASK__A 0x831F33
+#define SCU_RAM_ORX_PHA_LOCK_MASK__W 8
+#define SCU_RAM_ORX_PHA_LOCK_MASK__M 0xFF
+#define SCU_RAM_ORX_PHA_LOCK_MASK__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_OFFSET__A 0x831F34
+#define SCU_RAM_ORX_TIM_OFFSET__W 16
+#define SCU_RAM_ORX_TIM_OFFSET__M 0xFFFF
+#define SCU_RAM_ORX_TIM_OFFSET__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_DIFF__A 0x831F35
+#define SCU_RAM_ORX_TIM_DIFF__W 16
+#define SCU_RAM_ORX_TIM_DIFF__M 0xFFFF
+#define SCU_RAM_ORX_TIM_DIFF__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_LOCK_TH__A 0x831F36
+#define SCU_RAM_ORX_TIM_LOCK_TH__W 16
+#define SCU_RAM_ORX_TIM_LOCK_TH__M 0xFFFF
+#define SCU_RAM_ORX_TIM_LOCK_TH__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_LOCK_WD__A 0x831F37
+#define SCU_RAM_ORX_TIM_LOCK_WD__W 16
+#define SCU_RAM_ORX_TIM_LOCK_WD__M 0xFFFF
+#define SCU_RAM_ORX_TIM_LOCK_WD__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_ONLOCK_TTH__A 0x831F38
+#define SCU_RAM_ORX_TIM_ONLOCK_TTH__W 16
+#define SCU_RAM_ORX_TIM_ONLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_TIM_ONLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_UNLOCK_TTH__A 0x831F39
+#define SCU_RAM_ORX_TIM_UNLOCK_TTH__W 16
+#define SCU_RAM_ORX_TIM_UNLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_TIM_UNLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_LOCK_TOTH__A 0x831F3A
+#define SCU_RAM_ORX_TIM_LOCK_TOTH__W 16
+#define SCU_RAM_ORX_TIM_LOCK_TOTH__M 0xFFFF
+#define SCU_RAM_ORX_TIM_LOCK_TOTH__PRE 0x0
+
+#define SCU_RAM_ORX_TIM_LOCK_MASK__A 0x831F3B
+#define SCU_RAM_ORX_TIM_LOCK_MASK__W 8
+#define SCU_RAM_ORX_TIM_LOCK_MASK__M 0xFF
+#define SCU_RAM_ORX_TIM_LOCK_MASK__PRE 0x0
+
+#define SCU_RAM_ORX_EQU_DIFF__A 0x831F3C
+#define SCU_RAM_ORX_EQU_DIFF__W 16
+#define SCU_RAM_ORX_EQU_DIFF__M 0xFFFF
+#define SCU_RAM_ORX_EQU_DIFF__PRE 0x0
+
+#define SCU_RAM_ORX_EQU_LOCK_TH__A 0x831F3D
+#define SCU_RAM_ORX_EQU_LOCK_TH__W 16
+#define SCU_RAM_ORX_EQU_LOCK_TH__M 0xFFFF
+#define SCU_RAM_ORX_EQU_LOCK_TH__PRE 0x0
+
+#define SCU_RAM_ORX_EQU_LOCK_WD__A 0x831F3E
+#define SCU_RAM_ORX_EQU_LOCK_WD__W 16
+#define SCU_RAM_ORX_EQU_LOCK_WD__M 0xFFFF
+#define SCU_RAM_ORX_EQU_LOCK_WD__PRE 0x0
+
+#define SCU_RAM_ORX_EQU_ONLOCK_TTH__A 0x831F3F
+#define SCU_RAM_ORX_EQU_ONLOCK_TTH__W 16
+#define SCU_RAM_ORX_EQU_ONLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_EQU_ONLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_EQU_UNLOCK_TTH__A 0x831F40
+#define SCU_RAM_ORX_EQU_UNLOCK_TTH__W 16
+#define SCU_RAM_ORX_EQU_UNLOCK_TTH__M 0xFFFF
+#define SCU_RAM_ORX_EQU_UNLOCK_TTH__PRE 0x0
+
+#define SCU_RAM_ORX_EQU_LOCK_TOTH__A 0x831F41
+#define SCU_RAM_ORX_EQU_LOCK_TOTH__W 16
+#define SCU_RAM_ORX_EQU_LOCK_TOTH__M 0xFFFF
+#define SCU_RAM_ORX_EQU_LOCK_TOTH__PRE 0x0
+
+#define SCU_RAM_ORX_EQU_LOCK_MASK__A 0x831F42
+#define SCU_RAM_ORX_EQU_LOCK_MASK__W 8
+#define SCU_RAM_ORX_EQU_LOCK_MASK__M 0xFF
+#define SCU_RAM_ORX_EQU_LOCK_MASK__PRE 0x0
+
+#define SCU_RAM_ORX_FLT_FRQ__A 0x831F43
+#define SCU_RAM_ORX_FLT_FRQ__W 16
+#define SCU_RAM_ORX_FLT_FRQ__M 0xFFFF
+#define SCU_RAM_ORX_FLT_FRQ__PRE 0x0
+#define SCU_RAM_ORX_RST_CPH__A 0x831F44
+#define SCU_RAM_ORX_RST_CPH__W 4
+#define SCU_RAM_ORX_RST_CPH__M 0xF
+#define SCU_RAM_ORX_RST_CPH__PRE 0x0
+
+#define SCU_RAM_ORX_RST_CPH_RST_CPH__B 0
+#define SCU_RAM_ORX_RST_CPH_RST_CPH__W 4
+#define SCU_RAM_ORX_RST_CPH_RST_CPH__M 0xF
+#define SCU_RAM_ORX_RST_CPH_RST_CPH__PRE 0x0
+
+#define SCU_RAM_ORX_RST_CTI__A 0x831F45
+#define SCU_RAM_ORX_RST_CTI__W 4
+#define SCU_RAM_ORX_RST_CTI__M 0xF
+#define SCU_RAM_ORX_RST_CTI__PRE 0x0
+
+#define SCU_RAM_ORX_RST_CTI_RST_CTI__B 0
+#define SCU_RAM_ORX_RST_CTI_RST_CTI__W 4
+#define SCU_RAM_ORX_RST_CTI_RST_CTI__M 0xF
+#define SCU_RAM_ORX_RST_CTI_RST_CTI__PRE 0x0
+
+#define SCU_RAM_ORX_RST_KRN__A 0x831F46
+#define SCU_RAM_ORX_RST_KRN__W 4
+#define SCU_RAM_ORX_RST_KRN__M 0xF
+#define SCU_RAM_ORX_RST_KRN__PRE 0x0
+
+#define SCU_RAM_ORX_RST_KRN_RST_KRN__B 0
+#define SCU_RAM_ORX_RST_KRN_RST_KRN__W 4
+#define SCU_RAM_ORX_RST_KRN_RST_KRN__M 0xF
+#define SCU_RAM_ORX_RST_KRN_RST_KRN__PRE 0x0
+
+#define SCU_RAM_ORX_RST_KRP__A 0x831F47
+#define SCU_RAM_ORX_RST_KRP__W 4
+#define SCU_RAM_ORX_RST_KRP__M 0xF
+#define SCU_RAM_ORX_RST_KRP__PRE 0x0
+
+#define SCU_RAM_ORX_RST_KRP_RST_KRP__B 0
+#define SCU_RAM_ORX_RST_KRP_RST_KRP__W 4
+#define SCU_RAM_ORX_RST_KRP_RST_KRP__M 0xF
+#define SCU_RAM_ORX_RST_KRP_RST_KRP__PRE 0x0
+
+#define SCU_RAM_ATV_STANDARD__A 0x831F48
+#define SCU_RAM_ATV_STANDARD__W 12
+#define SCU_RAM_ATV_STANDARD__M 0xFFF
+#define SCU_RAM_ATV_STANDARD__PRE 0x0
+
+#define SCU_RAM_ATV_STANDARD_STANDARD__B 0
+#define SCU_RAM_ATV_STANDARD_STANDARD__W 12
+#define SCU_RAM_ATV_STANDARD_STANDARD__M 0xFFF
+#define SCU_RAM_ATV_STANDARD_STANDARD__PRE 0x0
+#define SCU_RAM_ATV_STANDARD_STANDARD_MN 0x2
+#define SCU_RAM_ATV_STANDARD_STANDARD_B 0x103
+#define SCU_RAM_ATV_STANDARD_STANDARD_G 0x3
+#define SCU_RAM_ATV_STANDARD_STANDARD_DK 0x4
+#define SCU_RAM_ATV_STANDARD_STANDARD_L 0x9
+#define SCU_RAM_ATV_STANDARD_STANDARD_LP 0x109
+#define SCU_RAM_ATV_STANDARD_STANDARD_I 0xA
+#define SCU_RAM_ATV_STANDARD_STANDARD_FM 0x40
+
+#define SCU_RAM_ATV_DETECT__A 0x831F49
+#define SCU_RAM_ATV_DETECT__W 1
+#define SCU_RAM_ATV_DETECT__M 0x1
+#define SCU_RAM_ATV_DETECT__PRE 0x0
+
+#define SCU_RAM_ATV_DETECT_DETECT__B 0
+#define SCU_RAM_ATV_DETECT_DETECT__W 1
+#define SCU_RAM_ATV_DETECT_DETECT__M 0x1
+#define SCU_RAM_ATV_DETECT_DETECT__PRE 0x0
+#define SCU_RAM_ATV_DETECT_DETECT_false 0x0
+#define SCU_RAM_ATV_DETECT_DETECT_true 0x1
+
+#define SCU_RAM_ATV_DETECT_TH__A 0x831F4A
+#define SCU_RAM_ATV_DETECT_TH__W 8
+#define SCU_RAM_ATV_DETECT_TH__M 0xFF
+#define SCU_RAM_ATV_DETECT_TH__PRE 0x0
+
+#define SCU_RAM_ATV_DETECT_TH_DETECT_TH__B 0
+#define SCU_RAM_ATV_DETECT_TH_DETECT_TH__W 8
+#define SCU_RAM_ATV_DETECT_TH_DETECT_TH__M 0xFF
+#define SCU_RAM_ATV_DETECT_TH_DETECT_TH__PRE 0x0
+
+#define SCU_RAM_ATV_LOCK__A 0x831F4B
+#define SCU_RAM_ATV_LOCK__W 2
+#define SCU_RAM_ATV_LOCK__M 0x3
+#define SCU_RAM_ATV_LOCK__PRE 0x0
+
+#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT__B 0
+#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT__W 1
+#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT__M 0x1
+#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT__PRE 0x0
+#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT_NO_LOCK 0x0
+#define SCU_RAM_ATV_LOCK_CR_LOCK_BIT_LOCK 0x1
+
+#define SCU_RAM_ATV_LOCK_SYNC_FLAG__B 1
+#define SCU_RAM_ATV_LOCK_SYNC_FLAG__W 1
+#define SCU_RAM_ATV_LOCK_SYNC_FLAG__M 0x2
+#define SCU_RAM_ATV_LOCK_SYNC_FLAG__PRE 0x0
+#define SCU_RAM_ATV_LOCK_SYNC_FLAG_NO_SYNC 0x0
+#define SCU_RAM_ATV_LOCK_SYNC_FLAG_SYNC 0x2
+
+#define SCU_RAM_ATV_CR_LOCK__A 0x831F4C
+#define SCU_RAM_ATV_CR_LOCK__W 11
+#define SCU_RAM_ATV_CR_LOCK__M 0x7FF
+#define SCU_RAM_ATV_CR_LOCK__PRE 0x0
+
+#define SCU_RAM_ATV_CR_LOCK_CR_LOCK__B 0
+#define SCU_RAM_ATV_CR_LOCK_CR_LOCK__W 11
+#define SCU_RAM_ATV_CR_LOCK_CR_LOCK__M 0x7FF
+#define SCU_RAM_ATV_CR_LOCK_CR_LOCK__PRE 0x0
+
+#define SCU_RAM_ATV_AGC_MODE__A 0x831F4D
+#define SCU_RAM_ATV_AGC_MODE__W 8
+#define SCU_RAM_ATV_AGC_MODE__M 0xFF
+#define SCU_RAM_ATV_AGC_MODE__PRE 0x0
+
+#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL__B 2
+#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL__W 1
+#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL__M 0x4
+#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL__PRE 0x0
+#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL_AGC_FAST 0x0
+#define SCU_RAM_ATV_AGC_MODE_VAGC_VEL_AGC_SLOW 0x4
+
+#define SCU_RAM_ATV_AGC_MODE_BP_EN__B 3
+#define SCU_RAM_ATV_AGC_MODE_BP_EN__W 1
+#define SCU_RAM_ATV_AGC_MODE_BP_EN__M 0x8
+#define SCU_RAM_ATV_AGC_MODE_BP_EN__PRE 0x0
+#define SCU_RAM_ATV_AGC_MODE_BP_EN_BPC_DISABLE 0x0
+#define SCU_RAM_ATV_AGC_MODE_BP_EN_BPC_ENABLE 0x8
+
+#define SCU_RAM_ATV_AGC_MODE_SIF_STD__B 4
+#define SCU_RAM_ATV_AGC_MODE_SIF_STD__W 2
+#define SCU_RAM_ATV_AGC_MODE_SIF_STD__M 0x30
+#define SCU_RAM_ATV_AGC_MODE_SIF_STD__PRE 0x0
+#define SCU_RAM_ATV_AGC_MODE_SIF_STD_SIF_AGC_OFF 0x0
+#define SCU_RAM_ATV_AGC_MODE_SIF_STD_SIF_AGC_FM 0x10
+#define SCU_RAM_ATV_AGC_MODE_SIF_STD_SIF_AGC_AM 0x20
+
+#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN__B 6
+#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN__W 1
+#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN__M 0x40
+#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN__PRE 0x0
+#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN_FAGC_DISABLE 0x0
+#define SCU_RAM_ATV_AGC_MODE_FAST_VAGC_EN_FAGC_ENABLE 0x40
+
+#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP__B 7
+#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP__W 1
+#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP__M 0x80
+#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP__PRE 0x0
+#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP_MWA_ENABLE 0x0
+#define SCU_RAM_ATV_AGC_MODE_MOD_WA_BP_MWA_DISABLE 0x80
+
+#define SCU_RAM_ATV_RSV_01__A 0x831F4E
+#define SCU_RAM_ATV_RSV_01__W 16
+#define SCU_RAM_ATV_RSV_01__M 0xFFFF
+#define SCU_RAM_ATV_RSV_01__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_02__A 0x831F4F
+#define SCU_RAM_ATV_RSV_02__W 16
+#define SCU_RAM_ATV_RSV_02__M 0xFFFF
+#define SCU_RAM_ATV_RSV_02__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_03__A 0x831F50
+#define SCU_RAM_ATV_RSV_03__W 16
+#define SCU_RAM_ATV_RSV_03__M 0xFFFF
+#define SCU_RAM_ATV_RSV_03__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_04__A 0x831F51
+#define SCU_RAM_ATV_RSV_04__W 16
+#define SCU_RAM_ATV_RSV_04__M 0xFFFF
+#define SCU_RAM_ATV_RSV_04__PRE 0x0
+#define SCU_RAM_ATV_FAGC_TH_RED__A 0x831F52
+#define SCU_RAM_ATV_FAGC_TH_RED__W 8
+#define SCU_RAM_ATV_FAGC_TH_RED__M 0xFF
+#define SCU_RAM_ATV_FAGC_TH_RED__PRE 0x0
+
+#define SCU_RAM_ATV_FAGC_TH_RED_FAGC_TH_RED__B 0
+#define SCU_RAM_ATV_FAGC_TH_RED_FAGC_TH_RED__W 8
+#define SCU_RAM_ATV_FAGC_TH_RED_FAGC_TH_RED__M 0xFF
+#define SCU_RAM_ATV_FAGC_TH_RED_FAGC_TH_RED__PRE 0x0
+
+#define SCU_RAM_ATV_AMS_MAX_REF__A 0x831F53
+#define SCU_RAM_ATV_AMS_MAX_REF__W 11
+#define SCU_RAM_ATV_AMS_MAX_REF__M 0x7FF
+#define SCU_RAM_ATV_AMS_MAX_REF__PRE 0x0
+
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF__B 0
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF__W 11
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF__M 0x7FF
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF__PRE 0x0
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF_BG_MN 0x2BC
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF_DK 0x2D0
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF_I 0x314
+#define SCU_RAM_ATV_AMS_MAX_REF_AMS_MAX_REF_LLP 0x28A
+
+#define SCU_RAM_ATV_ACT_AMX__A 0x831F54
+#define SCU_RAM_ATV_ACT_AMX__W 11
+#define SCU_RAM_ATV_ACT_AMX__M 0x7FF
+#define SCU_RAM_ATV_ACT_AMX__PRE 0x0
+
+#define SCU_RAM_ATV_ACT_AMX_ACT_AMX__B 0
+#define SCU_RAM_ATV_ACT_AMX_ACT_AMX__W 11
+#define SCU_RAM_ATV_ACT_AMX_ACT_AMX__M 0x7FF
+#define SCU_RAM_ATV_ACT_AMX_ACT_AMX__PRE 0x0
+
+#define SCU_RAM_ATV_ACT_AMI__A 0x831F55
+#define SCU_RAM_ATV_ACT_AMI__W 11
+#define SCU_RAM_ATV_ACT_AMI__M 0x7FF
+#define SCU_RAM_ATV_ACT_AMI__PRE 0x0
+
+#define SCU_RAM_ATV_ACT_AMI_ACT_AMI__B 0
+#define SCU_RAM_ATV_ACT_AMI_ACT_AMI__W 11
+#define SCU_RAM_ATV_ACT_AMI_ACT_AMI__M 0x7FF
+#define SCU_RAM_ATV_ACT_AMI_ACT_AMI__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_05__A 0x831F56
+#define SCU_RAM_ATV_RSV_05__W 16
+#define SCU_RAM_ATV_RSV_05__M 0xFFFF
+#define SCU_RAM_ATV_RSV_05__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_06__A 0x831F57
+#define SCU_RAM_ATV_RSV_06__W 16
+#define SCU_RAM_ATV_RSV_06__M 0xFFFF
+#define SCU_RAM_ATV_RSV_06__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_07__A 0x831F58
+#define SCU_RAM_ATV_RSV_07__W 16
+#define SCU_RAM_ATV_RSV_07__M 0xFFFF
+#define SCU_RAM_ATV_RSV_07__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_08__A 0x831F59
+#define SCU_RAM_ATV_RSV_08__W 16
+#define SCU_RAM_ATV_RSV_08__M 0xFFFF
+#define SCU_RAM_ATV_RSV_08__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_09__A 0x831F5A
+#define SCU_RAM_ATV_RSV_09__W 16
+#define SCU_RAM_ATV_RSV_09__M 0xFFFF
+#define SCU_RAM_ATV_RSV_09__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_10__A 0x831F5B
+#define SCU_RAM_ATV_RSV_10__W 16
+#define SCU_RAM_ATV_RSV_10__M 0xFFFF
+#define SCU_RAM_ATV_RSV_10__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_11__A 0x831F5C
+#define SCU_RAM_ATV_RSV_11__W 16
+#define SCU_RAM_ATV_RSV_11__M 0xFFFF
+#define SCU_RAM_ATV_RSV_11__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_12__A 0x831F5D
+#define SCU_RAM_ATV_RSV_12__W 16
+#define SCU_RAM_ATV_RSV_12__M 0xFFFF
+#define SCU_RAM_ATV_RSV_12__PRE 0x0
+#define SCU_RAM_ATV_VID_GAIN_HI__A 0x831F5E
+#define SCU_RAM_ATV_VID_GAIN_HI__W 16
+#define SCU_RAM_ATV_VID_GAIN_HI__M 0xFFFF
+#define SCU_RAM_ATV_VID_GAIN_HI__PRE 0x0
+
+#define SCU_RAM_ATV_VID_GAIN_HI_VID_GAIN_HI__B 0
+#define SCU_RAM_ATV_VID_GAIN_HI_VID_GAIN_HI__W 16
+#define SCU_RAM_ATV_VID_GAIN_HI_VID_GAIN_HI__M 0xFFFF
+#define SCU_RAM_ATV_VID_GAIN_HI_VID_GAIN_HI__PRE 0x0
+
+#define SCU_RAM_ATV_VID_GAIN_LO__A 0x831F5F
+#define SCU_RAM_ATV_VID_GAIN_LO__W 8
+#define SCU_RAM_ATV_VID_GAIN_LO__M 0xFF
+#define SCU_RAM_ATV_VID_GAIN_LO__PRE 0x0
+
+#define SCU_RAM_ATV_VID_GAIN_LO_VID_GAIN_LO__B 0
+#define SCU_RAM_ATV_VID_GAIN_LO_VID_GAIN_LO__W 8
+#define SCU_RAM_ATV_VID_GAIN_LO_VID_GAIN_LO__M 0xFF
+#define SCU_RAM_ATV_VID_GAIN_LO_VID_GAIN_LO__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_13__A 0x831F60
+#define SCU_RAM_ATV_RSV_13__W 16
+#define SCU_RAM_ATV_RSV_13__M 0xFFFF
+#define SCU_RAM_ATV_RSV_13__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_14__A 0x831F61
+#define SCU_RAM_ATV_RSV_14__W 16
+#define SCU_RAM_ATV_RSV_14__M 0xFFFF
+#define SCU_RAM_ATV_RSV_14__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_15__A 0x831F62
+#define SCU_RAM_ATV_RSV_15__W 16
+#define SCU_RAM_ATV_RSV_15__M 0xFFFF
+#define SCU_RAM_ATV_RSV_15__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_16__A 0x831F63
+#define SCU_RAM_ATV_RSV_16__W 16
+#define SCU_RAM_ATV_RSV_16__M 0xFFFF
+#define SCU_RAM_ATV_RSV_16__PRE 0x0
+#define SCU_RAM_ATV_AAGC_CNT__A 0x831F64
+#define SCU_RAM_ATV_AAGC_CNT__W 8
+#define SCU_RAM_ATV_AAGC_CNT__M 0xFF
+#define SCU_RAM_ATV_AAGC_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_AAGC_CNT_AAGC_CNT__B 0
+#define SCU_RAM_ATV_AAGC_CNT_AAGC_CNT__W 8
+#define SCU_RAM_ATV_AAGC_CNT_AAGC_CNT__M 0xFF
+#define SCU_RAM_ATV_AAGC_CNT_AAGC_CNT__PRE 0x0
+
+#define SCU_RAM_ATV_SIF_GAIN__A 0x831F65
+#define SCU_RAM_ATV_SIF_GAIN__W 11
+#define SCU_RAM_ATV_SIF_GAIN__M 0x7FF
+#define SCU_RAM_ATV_SIF_GAIN__PRE 0x0
+
+#define SCU_RAM_ATV_SIF_GAIN_SIF_GAIN__B 0
+#define SCU_RAM_ATV_SIF_GAIN_SIF_GAIN__W 11
+#define SCU_RAM_ATV_SIF_GAIN_SIF_GAIN__M 0x7FF
+#define SCU_RAM_ATV_SIF_GAIN_SIF_GAIN__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_17__A 0x831F66
+#define SCU_RAM_ATV_RSV_17__W 16
+#define SCU_RAM_ATV_RSV_17__M 0xFFFF
+#define SCU_RAM_ATV_RSV_17__PRE 0x0
+
+#define SCU_RAM_ATV_RSV_18__A 0x831F67
+#define SCU_RAM_ATV_RSV_18__W 16
+#define SCU_RAM_ATV_RSV_18__M 0xFFFF
+#define SCU_RAM_ATV_RSV_18__PRE 0x0
+
+#define SCU_RAM_ATV_RATE_OFS__A 0x831F68
+#define SCU_RAM_ATV_RATE_OFS__W 12
+#define SCU_RAM_ATV_RATE_OFS__M 0xFFF
+#define SCU_RAM_ATV_RATE_OFS__PRE 0x0
+
+#define SCU_RAM_ATV_LO_INCR__A 0x831F69
+#define SCU_RAM_ATV_LO_INCR__W 12
+#define SCU_RAM_ATV_LO_INCR__M 0xFFF
+#define SCU_RAM_ATV_LO_INCR__PRE 0x0
+
+#define SCU_RAM_ATV_IIR_CRIT__A 0x831F6A
+#define SCU_RAM_ATV_IIR_CRIT__W 12
+#define SCU_RAM_ATV_IIR_CRIT__M 0xFFF
+#define SCU_RAM_ATV_IIR_CRIT__PRE 0x0
+
+#define SCU_RAM_ATV_DEF_RATE_OFS__A 0x831F6B
+#define SCU_RAM_ATV_DEF_RATE_OFS__W 12
+#define SCU_RAM_ATV_DEF_RATE_OFS__M 0xFFF
+#define SCU_RAM_ATV_DEF_RATE_OFS__PRE 0x0
+
+#define SCU_RAM_ATV_DEF_LO_INCR__A 0x831F6C
+#define SCU_RAM_ATV_DEF_LO_INCR__W 12
+#define SCU_RAM_ATV_DEF_LO_INCR__M 0xFFF
+#define SCU_RAM_ATV_DEF_LO_INCR__PRE 0x0
+
+#define SCU_RAM_ATV_ENABLE_IIR_WA__A 0x831F6D
+#define SCU_RAM_ATV_ENABLE_IIR_WA__W 1
+#define SCU_RAM_ATV_ENABLE_IIR_WA__M 0x1
+#define SCU_RAM_ATV_ENABLE_IIR_WA__PRE 0x0
+
+#define SCU_RAM_ATV_MOD_CONTROL__A 0x831F6E
+#define SCU_RAM_ATV_MOD_CONTROL__W 12
+#define SCU_RAM_ATV_MOD_CONTROL__M 0xFFF
+#define SCU_RAM_ATV_MOD_CONTROL__PRE 0x0
+
+#define SCU_RAM_ATV_PAGC_KI_MAX__A 0x831F6F
+#define SCU_RAM_ATV_PAGC_KI_MAX__W 12
+#define SCU_RAM_ATV_PAGC_KI_MAX__M 0xFFF
+#define SCU_RAM_ATV_PAGC_KI_MAX__PRE 0x0
+
+#define SCU_RAM_ATV_BPC_KI_MAX__A 0x831F70
+#define SCU_RAM_ATV_BPC_KI_MAX__W 12
+#define SCU_RAM_ATV_BPC_KI_MAX__M 0xFFF
+#define SCU_RAM_ATV_BPC_KI_MAX__PRE 0x0
+
+#define SCU_RAM_ATV_NAGC_KI_MAX__A 0x831F71
+#define SCU_RAM_ATV_NAGC_KI_MAX__W 12
+#define SCU_RAM_ATV_NAGC_KI_MAX__M 0xFFF
+#define SCU_RAM_ATV_NAGC_KI_MAX__PRE 0x0
+#define SCU_RAM_ATV_NAGC_KI_MIN__A 0x831F72
+#define SCU_RAM_ATV_NAGC_KI_MIN__W 12
+#define SCU_RAM_ATV_NAGC_KI_MIN__M 0xFFF
+#define SCU_RAM_ATV_NAGC_KI_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_NAGC_KI_MIN_NAGC_KI_MIN__B 0
+#define SCU_RAM_ATV_NAGC_KI_MIN_NAGC_KI_MIN__W 12
+#define SCU_RAM_ATV_NAGC_KI_MIN_NAGC_KI_MIN__M 0xFFF
+#define SCU_RAM_ATV_NAGC_KI_MIN_NAGC_KI_MIN__PRE 0x0
+
+#define SCU_RAM_ATV_KI_CHANGE_TH__A 0x831F73
+#define SCU_RAM_ATV_KI_CHANGE_TH__W 8
+#define SCU_RAM_ATV_KI_CHANGE_TH__M 0xFF
+#define SCU_RAM_ATV_KI_CHANGE_TH__PRE 0x0
+
+#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH__B 0
+#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH__W 8
+#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH__M 0xFF
+#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH__PRE 0x0
+#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH_NEG_MOD 0x14
+#define SCU_RAM_ATV_KI_CHANGE_TH_KI_CHANGE_TH_POS_MOD 0x28
+
+#define SCU_RAM_QAM_PARAM_ANNEX__A 0x831F74
+#define SCU_RAM_QAM_PARAM_ANNEX__W 2
+#define SCU_RAM_QAM_PARAM_ANNEX__M 0x3
+#define SCU_RAM_QAM_PARAM_ANNEX__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT__B 0
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT__W 2
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT__M 0x3
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT__PRE 0x0
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT_ANNEX_A 0x0
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT_ANNEX_B 0x1
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT_ANNEX_C 0x2
+#define SCU_RAM_QAM_PARAM_ANNEX_BIT_ANNEX_D 0x3
+
+#define SCU_RAM_QAM_PARAM_CONSTELLATION__A 0x831F75
+#define SCU_RAM_QAM_PARAM_CONSTELLATION__W 3
+#define SCU_RAM_QAM_PARAM_CONSTELLATION__M 0x7
+#define SCU_RAM_QAM_PARAM_CONSTELLATION__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT__B 0
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT__W 3
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT__M 0x7
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT__PRE 0x0
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_UNKNOWN 0x0
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_16 0x3
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_32 0x4
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_64 0x5
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_128 0x6
+#define SCU_RAM_QAM_PARAM_CONSTELLATION_BIT_QAM_256 0x7
+
+#define SCU_RAM_QAM_PARAM_INTERLEAVE__A 0x831F76
+#define SCU_RAM_QAM_PARAM_INTERLEAVE__W 8
+#define SCU_RAM_QAM_PARAM_INTERLEAVE__M 0xFF
+#define SCU_RAM_QAM_PARAM_INTERLEAVE__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT__B 0
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT__W 8
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT__M 0xFF
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT__PRE 0x0
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J1 0x0
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J1_V2 0x1
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J2 0x2
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I64_J2 0x3
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J3 0x4
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I32_J4 0x5
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J4 0x6
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I16_J8 0x7
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J5 0x8
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I8_J16 0x9
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J6 0xA
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J7 0xC
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I128_J8 0xE
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I12_J17 0x10
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_I5_J4 0x11
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_UNKNOWN 0xFE
+#define SCU_RAM_QAM_PARAM_INTERLEAVE_BIT_AUTO 0xFF
+
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI__A 0x831F77
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI__W 16
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI_BIT__B 0
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI_BIT__W 16
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI_BIT__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_HI_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO__A 0x831F78
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO__W 16
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO_BIT__B 0
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO_BIT__W 16
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO_BIT__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_SYM_RCRATE_LO_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CENTERTAP__A 0x831F79
+#define SCU_RAM_QAM_EQ_CENTERTAP__W 16
+#define SCU_RAM_QAM_EQ_CENTERTAP__M 0xFFFF
+#define SCU_RAM_QAM_EQ_CENTERTAP__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CENTERTAP_BIT__B 0
+#define SCU_RAM_QAM_EQ_CENTERTAP_BIT__W 8
+#define SCU_RAM_QAM_EQ_CENTERTAP_BIT__M 0xFF
+#define SCU_RAM_QAM_EQ_CENTERTAP_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_0__A 0x831F7A
+#define SCU_RAM_QAM_WR_RSV_0__W 16
+#define SCU_RAM_QAM_WR_RSV_0__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_0__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_0_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_0_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_0_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_0_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI__A 0x831F7B
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI__W 16
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI_BIT__B 0
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI_BIT__W 16
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI_BIT__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_HI_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO__A 0x831F7C
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO__W 16
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO__PRE 0x0
+
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO_BIT__B 0
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO_BIT__W 16
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO_BIT__M 0xFFFF
+#define SCU_RAM_QAM_PARAM_ALT_RCRATE_LO_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_5__A 0x831F7D
+#define SCU_RAM_QAM_WR_RSV_5__W 16
+#define SCU_RAM_QAM_WR_RSV_5__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_5__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_5_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_5_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_5_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_5_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_6__A 0x831F7E
+#define SCU_RAM_QAM_WR_RSV_6__W 16
+#define SCU_RAM_QAM_WR_RSV_6__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_6__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_6_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_6_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_6_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_6_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_7__A 0x831F7F
+#define SCU_RAM_QAM_WR_RSV_7__W 16
+#define SCU_RAM_QAM_WR_RSV_7__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_7__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_7_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_7_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_7_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_7_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_8__A 0x831F80
+#define SCU_RAM_QAM_WR_RSV_8__W 16
+#define SCU_RAM_QAM_WR_RSV_8__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_8__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_8_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_8_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_8_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_8_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_9__A 0x831F81
+#define SCU_RAM_QAM_WR_RSV_9__W 16
+#define SCU_RAM_QAM_WR_RSV_9__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_9__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_9_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_9_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_9_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_9_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_10__A 0x831F82
+#define SCU_RAM_QAM_WR_RSV_10__W 16
+#define SCU_RAM_QAM_WR_RSV_10__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_10__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_10_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_10_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_10_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_10_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_FMHUM_TO__A 0x831F83
+#define SCU_RAM_QAM_FSM_FMHUM_TO__W 16
+#define SCU_RAM_QAM_FSM_FMHUM_TO__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FMHUM_TO__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT__B 0
+#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT__W 16
+#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_FMHUM_TO_BIT_NO_FMHUM_TO 0x0
+
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A 0x831F84
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__W 16
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT_BIT__B 0
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT_BIT__W 16
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A 0x831F85
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__W 16
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT_BIT__B 0
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT_BIT__W 16
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A 0x831F86
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1_BIT__B 0
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1_BIT__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A 0x831F87
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2_BIT__B 0
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2_BIT__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A 0x831F88
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3_BIT__B 0
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3_BIT__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A 0x831F89
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4_BIT__B 0
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4_BIT__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A 0x831F8A
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5_BIT__B 0
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5_BIT__W 16
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_STATE_TGT__A 0x831F8B
+#define SCU_RAM_QAM_FSM_STATE_TGT__W 4
+#define SCU_RAM_QAM_FSM_STATE_TGT__M 0xF
+#define SCU_RAM_QAM_FSM_STATE_TGT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT__B 0
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT__W 4
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT__M 0xF
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_AMP 0x0
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_RATE 0x1
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_FREQ 0x2
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_UPRIGHT 0x3
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_HUNTING_PHASE 0x4
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_TRACKING_PHNOISE 0x5
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_TRACKING 0x6
+#define SCU_RAM_QAM_FSM_STATE_TGT_BIT_TRACKING_BURST 0x7
+
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE__A 0x831F8C
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE__W 9
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE__M 0x1FF
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE_LCK_AMP__B 0
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE_LCK_AMP__W 1
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE_LCK_AMP__M 0x1
+#define SCU_RAM_QAM_FSM_LOCK_OVERRIDE_LCK_AMP__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_ATH__A 0x831F8D
+#define SCU_RAM_QAM_FSM_ATH__W 16
+#define SCU_RAM_QAM_FSM_ATH__M 0xFFFF
+#define SCU_RAM_QAM_FSM_ATH__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_ATH_BIT__B 0
+#define SCU_RAM_QAM_FSM_ATH_BIT__W 16
+#define SCU_RAM_QAM_FSM_ATH_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_ATH_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_RTH__A 0x831F8E
+#define SCU_RAM_QAM_FSM_RTH__W 16
+#define SCU_RAM_QAM_FSM_RTH__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RTH__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_RTH_BIT__B 0
+#define SCU_RAM_QAM_FSM_RTH_BIT__W 16
+#define SCU_RAM_QAM_FSM_RTH_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RTH_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_16 0x8C
+#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_32 0x50
+#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_64 0x4E
+#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_128 0x32
+#define SCU_RAM_QAM_FSM_RTH_BIT_QAM_256 0x2D
+
+#define SCU_RAM_QAM_FSM_FTH__A 0x831F8F
+#define SCU_RAM_QAM_FSM_FTH__W 16
+#define SCU_RAM_QAM_FSM_FTH__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FTH__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_FTH_BIT__B 0
+#define SCU_RAM_QAM_FSM_FTH_BIT__W 16
+#define SCU_RAM_QAM_FSM_FTH_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FTH_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_16 0x32
+#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_32 0x1E
+#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_64 0x1E
+#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_128 0x14
+#define SCU_RAM_QAM_FSM_FTH_BIT_QAM_256 0x14
+
+#define SCU_RAM_QAM_FSM_PTH__A 0x831F90
+#define SCU_RAM_QAM_FSM_PTH__W 16
+#define SCU_RAM_QAM_FSM_PTH__M 0xFFFF
+#define SCU_RAM_QAM_FSM_PTH__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_PTH_BIT__B 0
+#define SCU_RAM_QAM_FSM_PTH_BIT__W 16
+#define SCU_RAM_QAM_FSM_PTH_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_PTH_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_16 0xC8
+#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_32 0x96
+#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_64 0x8C
+#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_128 0x64
+#define SCU_RAM_QAM_FSM_PTH_BIT_QAM_256 0x64
+
+#define SCU_RAM_QAM_FSM_MTH__A 0x831F91
+#define SCU_RAM_QAM_FSM_MTH__W 16
+#define SCU_RAM_QAM_FSM_MTH__M 0xFFFF
+#define SCU_RAM_QAM_FSM_MTH__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_MTH_BIT__B 0
+#define SCU_RAM_QAM_FSM_MTH_BIT__W 16
+#define SCU_RAM_QAM_FSM_MTH_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_MTH_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_16 0x5A
+#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_32 0x50
+#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_64 0x46
+#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_128 0x3C
+#define SCU_RAM_QAM_FSM_MTH_BIT_QAM_256 0x50
+
+#define SCU_RAM_QAM_FSM_CTH__A 0x831F92
+#define SCU_RAM_QAM_FSM_CTH__W 16
+#define SCU_RAM_QAM_FSM_CTH__M 0xFFFF
+#define SCU_RAM_QAM_FSM_CTH__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_CTH_BIT__B 0
+#define SCU_RAM_QAM_FSM_CTH_BIT__W 16
+#define SCU_RAM_QAM_FSM_CTH_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_CTH_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_16 0xA0
+#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_32 0x8C
+#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_64 0x8C
+#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_128 0x8C
+#define SCU_RAM_QAM_FSM_CTH_BIT_QAM_256 0x8C
+
+#define SCU_RAM_QAM_FSM_QTH__A 0x831F93
+#define SCU_RAM_QAM_FSM_QTH__W 16
+#define SCU_RAM_QAM_FSM_QTH__M 0xFFFF
+#define SCU_RAM_QAM_FSM_QTH__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_QTH_BIT__B 0
+#define SCU_RAM_QAM_FSM_QTH_BIT__W 16
+#define SCU_RAM_QAM_FSM_QTH_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_QTH_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_16 0xE6
+#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_32 0xAA
+#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_64 0xC3
+#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_128 0x8C
+#define SCU_RAM_QAM_FSM_QTH_BIT_QAM_256 0x96
+
+#define SCU_RAM_QAM_FSM_RATE_LIM__A 0x831F94
+#define SCU_RAM_QAM_FSM_RATE_LIM__W 16
+#define SCU_RAM_QAM_FSM_RATE_LIM__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RATE_LIM__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT__B 0
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT__W 16
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_16 0x46
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_32 0x46
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_64 0x46
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_128 0x46
+#define SCU_RAM_QAM_FSM_RATE_LIM_BIT_QAM_256 0x46
+
+#define SCU_RAM_QAM_FSM_FREQ_LIM__A 0x831F95
+#define SCU_RAM_QAM_FSM_FREQ_LIM__W 16
+#define SCU_RAM_QAM_FSM_FREQ_LIM__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FREQ_LIM__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT__B 0
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT__W 16
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_16 0x1E
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_32 0x14
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_64 0x28
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_128 0x8
+#define SCU_RAM_QAM_FSM_FREQ_LIM_BIT_QAM_256 0x28
+
+#define SCU_RAM_QAM_FSM_COUNT_LIM__A 0x831F96
+#define SCU_RAM_QAM_FSM_COUNT_LIM__W 16
+#define SCU_RAM_QAM_FSM_COUNT_LIM__M 0xFFFF
+#define SCU_RAM_QAM_FSM_COUNT_LIM__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT__B 0
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT__W 16
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_16 0x4
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_32 0x6
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_64 0x6
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_128 0x7
+#define SCU_RAM_QAM_FSM_COUNT_LIM_BIT_QAM_256 0x6
+
+#define SCU_RAM_QAM_LC_CA_COARSE__A 0x831F97
+#define SCU_RAM_QAM_LC_CA_COARSE__W 16
+#define SCU_RAM_QAM_LC_CA_COARSE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CA_COARSE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CA_COARSE_BIT__B 0
+#define SCU_RAM_QAM_LC_CA_COARSE_BIT__W 8
+#define SCU_RAM_QAM_LC_CA_COARSE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CA_COARSE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CA_MEDIUM__A 0x831F98
+#define SCU_RAM_QAM_LC_CA_MEDIUM__W 16
+#define SCU_RAM_QAM_LC_CA_MEDIUM__M 0xFFFF
+#define SCU_RAM_QAM_LC_CA_MEDIUM__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CA_MEDIUM_BIT__B 0
+#define SCU_RAM_QAM_LC_CA_MEDIUM_BIT__W 8
+#define SCU_RAM_QAM_LC_CA_MEDIUM_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CA_MEDIUM_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CA_FINE__A 0x831F99
+#define SCU_RAM_QAM_LC_CA_FINE__W 16
+#define SCU_RAM_QAM_LC_CA_FINE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CA_FINE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CA_FINE_BIT__B 0
+#define SCU_RAM_QAM_LC_CA_FINE_BIT__W 8
+#define SCU_RAM_QAM_LC_CA_FINE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CA_FINE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CP_COARSE__A 0x831F9A
+#define SCU_RAM_QAM_LC_CP_COARSE__W 16
+#define SCU_RAM_QAM_LC_CP_COARSE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CP_COARSE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CP_COARSE_BIT__B 0
+#define SCU_RAM_QAM_LC_CP_COARSE_BIT__W 8
+#define SCU_RAM_QAM_LC_CP_COARSE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CP_COARSE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CP_MEDIUM__A 0x831F9B
+#define SCU_RAM_QAM_LC_CP_MEDIUM__W 16
+#define SCU_RAM_QAM_LC_CP_MEDIUM__M 0xFFFF
+#define SCU_RAM_QAM_LC_CP_MEDIUM__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CP_MEDIUM_BIT__B 0
+#define SCU_RAM_QAM_LC_CP_MEDIUM_BIT__W 8
+#define SCU_RAM_QAM_LC_CP_MEDIUM_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CP_MEDIUM_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CP_FINE__A 0x831F9C
+#define SCU_RAM_QAM_LC_CP_FINE__W 16
+#define SCU_RAM_QAM_LC_CP_FINE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CP_FINE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CP_FINE_BIT__B 0
+#define SCU_RAM_QAM_LC_CP_FINE_BIT__W 8
+#define SCU_RAM_QAM_LC_CP_FINE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CP_FINE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CI_COARSE__A 0x831F9D
+#define SCU_RAM_QAM_LC_CI_COARSE__W 16
+#define SCU_RAM_QAM_LC_CI_COARSE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CI_COARSE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CI_COARSE_BIT__B 0
+#define SCU_RAM_QAM_LC_CI_COARSE_BIT__W 8
+#define SCU_RAM_QAM_LC_CI_COARSE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CI_COARSE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CI_MEDIUM__A 0x831F9E
+#define SCU_RAM_QAM_LC_CI_MEDIUM__W 16
+#define SCU_RAM_QAM_LC_CI_MEDIUM__M 0xFFFF
+#define SCU_RAM_QAM_LC_CI_MEDIUM__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CI_MEDIUM_BIT__B 0
+#define SCU_RAM_QAM_LC_CI_MEDIUM_BIT__W 8
+#define SCU_RAM_QAM_LC_CI_MEDIUM_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CI_MEDIUM_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CI_FINE__A 0x831F9F
+#define SCU_RAM_QAM_LC_CI_FINE__W 16
+#define SCU_RAM_QAM_LC_CI_FINE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CI_FINE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CI_FINE_BIT__B 0
+#define SCU_RAM_QAM_LC_CI_FINE_BIT__W 8
+#define SCU_RAM_QAM_LC_CI_FINE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CI_FINE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EP_COARSE__A 0x831FA0
+#define SCU_RAM_QAM_LC_EP_COARSE__W 16
+#define SCU_RAM_QAM_LC_EP_COARSE__M 0xFFFF
+#define SCU_RAM_QAM_LC_EP_COARSE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EP_COARSE_BIT__B 0
+#define SCU_RAM_QAM_LC_EP_COARSE_BIT__W 8
+#define SCU_RAM_QAM_LC_EP_COARSE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_EP_COARSE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EP_MEDIUM__A 0x831FA1
+#define SCU_RAM_QAM_LC_EP_MEDIUM__W 16
+#define SCU_RAM_QAM_LC_EP_MEDIUM__M 0xFFFF
+#define SCU_RAM_QAM_LC_EP_MEDIUM__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EP_MEDIUM_BIT__B 0
+#define SCU_RAM_QAM_LC_EP_MEDIUM_BIT__W 8
+#define SCU_RAM_QAM_LC_EP_MEDIUM_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_EP_MEDIUM_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EP_FINE__A 0x831FA2
+#define SCU_RAM_QAM_LC_EP_FINE__W 16
+#define SCU_RAM_QAM_LC_EP_FINE__M 0xFFFF
+#define SCU_RAM_QAM_LC_EP_FINE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EP_FINE_BIT__B 0
+#define SCU_RAM_QAM_LC_EP_FINE_BIT__W 8
+#define SCU_RAM_QAM_LC_EP_FINE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_EP_FINE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EI_COARSE__A 0x831FA3
+#define SCU_RAM_QAM_LC_EI_COARSE__W 16
+#define SCU_RAM_QAM_LC_EI_COARSE__M 0xFFFF
+#define SCU_RAM_QAM_LC_EI_COARSE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EI_COARSE_BIT__B 0
+#define SCU_RAM_QAM_LC_EI_COARSE_BIT__W 8
+#define SCU_RAM_QAM_LC_EI_COARSE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_EI_COARSE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EI_MEDIUM__A 0x831FA4
+#define SCU_RAM_QAM_LC_EI_MEDIUM__W 16
+#define SCU_RAM_QAM_LC_EI_MEDIUM__M 0xFFFF
+#define SCU_RAM_QAM_LC_EI_MEDIUM__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EI_MEDIUM_BIT__B 0
+#define SCU_RAM_QAM_LC_EI_MEDIUM_BIT__W 8
+#define SCU_RAM_QAM_LC_EI_MEDIUM_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_EI_MEDIUM_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EI_FINE__A 0x831FA5
+#define SCU_RAM_QAM_LC_EI_FINE__W 16
+#define SCU_RAM_QAM_LC_EI_FINE__M 0xFFFF
+#define SCU_RAM_QAM_LC_EI_FINE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_EI_FINE_BIT__B 0
+#define SCU_RAM_QAM_LC_EI_FINE_BIT__W 8
+#define SCU_RAM_QAM_LC_EI_FINE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_EI_FINE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF_COARSE__A 0x831FA6
+#define SCU_RAM_QAM_LC_CF_COARSE__W 16
+#define SCU_RAM_QAM_LC_CF_COARSE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CF_COARSE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF_COARSE_BIT__B 0
+#define SCU_RAM_QAM_LC_CF_COARSE_BIT__W 8
+#define SCU_RAM_QAM_LC_CF_COARSE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CF_COARSE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF_MEDIUM__A 0x831FA7
+#define SCU_RAM_QAM_LC_CF_MEDIUM__W 16
+#define SCU_RAM_QAM_LC_CF_MEDIUM__M 0xFFFF
+#define SCU_RAM_QAM_LC_CF_MEDIUM__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF_MEDIUM_BIT__B 0
+#define SCU_RAM_QAM_LC_CF_MEDIUM_BIT__W 8
+#define SCU_RAM_QAM_LC_CF_MEDIUM_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CF_MEDIUM_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF_FINE__A 0x831FA8
+#define SCU_RAM_QAM_LC_CF_FINE__W 16
+#define SCU_RAM_QAM_LC_CF_FINE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CF_FINE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF_FINE_BIT__B 0
+#define SCU_RAM_QAM_LC_CF_FINE_BIT__W 8
+#define SCU_RAM_QAM_LC_CF_FINE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CF_FINE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF1_COARSE__A 0x831FA9
+#define SCU_RAM_QAM_LC_CF1_COARSE__W 16
+#define SCU_RAM_QAM_LC_CF1_COARSE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CF1_COARSE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF1_COARSE_BIT__B 0
+#define SCU_RAM_QAM_LC_CF1_COARSE_BIT__W 8
+#define SCU_RAM_QAM_LC_CF1_COARSE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CF1_COARSE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF1_MEDIUM__A 0x831FAA
+#define SCU_RAM_QAM_LC_CF1_MEDIUM__W 16
+#define SCU_RAM_QAM_LC_CF1_MEDIUM__M 0xFFFF
+#define SCU_RAM_QAM_LC_CF1_MEDIUM__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF1_MEDIUM_BIT__B 0
+#define SCU_RAM_QAM_LC_CF1_MEDIUM_BIT__W 8
+#define SCU_RAM_QAM_LC_CF1_MEDIUM_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CF1_MEDIUM_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF1_FINE__A 0x831FAB
+#define SCU_RAM_QAM_LC_CF1_FINE__W 16
+#define SCU_RAM_QAM_LC_CF1_FINE__M 0xFFFF
+#define SCU_RAM_QAM_LC_CF1_FINE__PRE 0x0
+
+#define SCU_RAM_QAM_LC_CF1_FINE_BIT__B 0
+#define SCU_RAM_QAM_LC_CF1_FINE_BIT__W 8
+#define SCU_RAM_QAM_LC_CF1_FINE_BIT__M 0xFF
+#define SCU_RAM_QAM_LC_CF1_FINE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_SL_SIG_POWER__A 0x831FAC
+#define SCU_RAM_QAM_SL_SIG_POWER__W 16
+#define SCU_RAM_QAM_SL_SIG_POWER__M 0xFFFF
+#define SCU_RAM_QAM_SL_SIG_POWER__PRE 0x0
+
+#define SCU_RAM_QAM_SL_SIG_POWER_BIT__B 0
+#define SCU_RAM_QAM_SL_SIG_POWER_BIT__W 16
+#define SCU_RAM_QAM_SL_SIG_POWER_BIT__M 0xFFFF
+#define SCU_RAM_QAM_SL_SIG_POWER_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CMA_RAD0__A 0x831FAD
+#define SCU_RAM_QAM_EQ_CMA_RAD0__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD0__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD0__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT__B 0
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT__PRE 0x0
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_16 0x34CD
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_32 0x1A33
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_64 0x3418
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_128 0x1814
+#define SCU_RAM_QAM_EQ_CMA_RAD0_BIT_QAM_256 0x2CEE
+
+#define SCU_RAM_QAM_EQ_CMA_RAD1__A 0x831FAE
+#define SCU_RAM_QAM_EQ_CMA_RAD1__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD1__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD1__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT__B 0
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT__PRE 0x0
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_16 0x34CD
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_32 0x1A33
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_64 0x314A
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_128 0x19C6
+#define SCU_RAM_QAM_EQ_CMA_RAD1_BIT_QAM_256 0x2F34
+
+#define SCU_RAM_QAM_EQ_CMA_RAD2__A 0x831FAF
+#define SCU_RAM_QAM_EQ_CMA_RAD2__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD2__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD2__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT__B 0
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT__PRE 0x0
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_16 0x34CD
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_32 0x1A33
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_64 0x2ED4
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_128 0x18FA
+#define SCU_RAM_QAM_EQ_CMA_RAD2_BIT_QAM_256 0x30FF
+
+#define SCU_RAM_QAM_EQ_CMA_RAD3__A 0x831FB0
+#define SCU_RAM_QAM_EQ_CMA_RAD3__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD3__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD3__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT__B 0
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT__PRE 0x0
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_16 0x34CD
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_32 0x1A33
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_64 0x35F1
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_128 0x1909
+#define SCU_RAM_QAM_EQ_CMA_RAD3_BIT_QAM_256 0x3283
+
+#define SCU_RAM_QAM_EQ_CMA_RAD4__A 0x831FB1
+#define SCU_RAM_QAM_EQ_CMA_RAD4__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD4__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD4__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT__B 0
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT__PRE 0x0
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_16 0x34CD
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_32 0x1A33
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_64 0x35F1
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_128 0x1A00
+#define SCU_RAM_QAM_EQ_CMA_RAD4_BIT_QAM_256 0x353D
+
+#define SCU_RAM_QAM_EQ_CMA_RAD5__A 0x831FB2
+#define SCU_RAM_QAM_EQ_CMA_RAD5__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD5__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD5__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT__B 0
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT__W 14
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT__M 0x3FFF
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT__PRE 0x0
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_16 0x34CD
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_32 0x1A33
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_64 0x3CF9
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_128 0x1C46
+#define SCU_RAM_QAM_EQ_CMA_RAD5_BIT_QAM_256 0x3C19
+
+#define SCU_RAM_QAM_CTL_ENA__A 0x831FB3
+#define SCU_RAM_QAM_CTL_ENA__W 16
+#define SCU_RAM_QAM_CTL_ENA__M 0xFFFF
+#define SCU_RAM_QAM_CTL_ENA__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_AMP__B 0
+#define SCU_RAM_QAM_CTL_ENA_AMP__W 1
+#define SCU_RAM_QAM_CTL_ENA_AMP__M 0x1
+#define SCU_RAM_QAM_CTL_ENA_AMP__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_ACQ__B 1
+#define SCU_RAM_QAM_CTL_ENA_ACQ__W 1
+#define SCU_RAM_QAM_CTL_ENA_ACQ__M 0x2
+#define SCU_RAM_QAM_CTL_ENA_ACQ__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_EQU__B 2
+#define SCU_RAM_QAM_CTL_ENA_EQU__W 1
+#define SCU_RAM_QAM_CTL_ENA_EQU__M 0x4
+#define SCU_RAM_QAM_CTL_ENA_EQU__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_SLC__B 3
+#define SCU_RAM_QAM_CTL_ENA_SLC__W 1
+#define SCU_RAM_QAM_CTL_ENA_SLC__M 0x8
+#define SCU_RAM_QAM_CTL_ENA_SLC__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_LC__B 4
+#define SCU_RAM_QAM_CTL_ENA_LC__W 1
+#define SCU_RAM_QAM_CTL_ENA_LC__M 0x10
+#define SCU_RAM_QAM_CTL_ENA_LC__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_AGC__B 5
+#define SCU_RAM_QAM_CTL_ENA_AGC__W 1
+#define SCU_RAM_QAM_CTL_ENA_AGC__M 0x20
+#define SCU_RAM_QAM_CTL_ENA_AGC__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_FEC__B 6
+#define SCU_RAM_QAM_CTL_ENA_FEC__W 1
+#define SCU_RAM_QAM_CTL_ENA_FEC__M 0x40
+#define SCU_RAM_QAM_CTL_ENA_FEC__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_AXIS__B 7
+#define SCU_RAM_QAM_CTL_ENA_AXIS__W 1
+#define SCU_RAM_QAM_CTL_ENA_AXIS__M 0x80
+#define SCU_RAM_QAM_CTL_ENA_AXIS__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_FMHUM__B 8
+#define SCU_RAM_QAM_CTL_ENA_FMHUM__W 1
+#define SCU_RAM_QAM_CTL_ENA_FMHUM__M 0x100
+#define SCU_RAM_QAM_CTL_ENA_FMHUM__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_EQTIME__B 9
+#define SCU_RAM_QAM_CTL_ENA_EQTIME__W 1
+#define SCU_RAM_QAM_CTL_ENA_EQTIME__M 0x200
+#define SCU_RAM_QAM_CTL_ENA_EQTIME__PRE 0x0
+
+#define SCU_RAM_QAM_CTL_ENA_EXTLCK__B 10
+#define SCU_RAM_QAM_CTL_ENA_EXTLCK__W 1
+#define SCU_RAM_QAM_CTL_ENA_EXTLCK__M 0x400
+#define SCU_RAM_QAM_CTL_ENA_EXTLCK__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_1__A 0x831FB4
+#define SCU_RAM_QAM_WR_RSV_1__W 16
+#define SCU_RAM_QAM_WR_RSV_1__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_1__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_1_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_1_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_1_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_1_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_2__A 0x831FB5
+#define SCU_RAM_QAM_WR_RSV_2__W 16
+#define SCU_RAM_QAM_WR_RSV_2__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_2__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_2_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_2_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_2_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_2_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_3__A 0x831FB6
+#define SCU_RAM_QAM_WR_RSV_3__W 16
+#define SCU_RAM_QAM_WR_RSV_3__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_3__PRE 0x0
+
+#define SCU_RAM_QAM_WR_RSV_3_BIT__B 0
+#define SCU_RAM_QAM_WR_RSV_3_BIT__W 16
+#define SCU_RAM_QAM_WR_RSV_3_BIT__M 0xFFFF
+#define SCU_RAM_QAM_WR_RSV_3_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION__A 0x831FB7
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION__W 3
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION__M 0x7
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION__PRE 0x0
+
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT__B 0
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT__W 3
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT__M 0x7
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT__PRE 0x0
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_UNKNOWN 0x0
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_16 0x3
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_32 0x4
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_64 0x5
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_128 0x6
+#define SCU_RAM_QAM_ACTIVE_CONSTELLATION_BIT_QAM_256 0x7
+
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE__A 0x831FB8
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE__W 8
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE__M 0xFF
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE__PRE 0x0
+
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT__B 0
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT__W 8
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT__M 0xFF
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT__PRE 0x0
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J1 0x0
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J1_V2 0x1
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J2 0x2
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I64_J2 0x3
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J3 0x4
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I32_J4 0x5
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J4 0x6
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I16_J8 0x7
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J5 0x8
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I8_J16 0x9
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J6 0xA
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J7 0xC
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I128_J8 0xE
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I12_J17 0x10
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_I5_J4 0x11
+#define SCU_RAM_QAM_ACTIVE_INTERLEAVE_BIT_UNKNOWN 0xFE
+
+#define SCU_RAM_QAM_RD_RSV_4__A 0x831FB9
+#define SCU_RAM_QAM_RD_RSV_4__W 16
+#define SCU_RAM_QAM_RD_RSV_4__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_4__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_4_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_4_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_4_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_4_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_LOCKED__A 0x831FBA
+#define SCU_RAM_QAM_LOCKED__W 16
+#define SCU_RAM_QAM_LOCKED__M 0xFFFF
+#define SCU_RAM_QAM_LOCKED__PRE 0x0
+
+#define SCU_RAM_QAM_LOCKED_INTLEVEL__B 0
+#define SCU_RAM_QAM_LOCKED_INTLEVEL__W 8
+#define SCU_RAM_QAM_LOCKED_INTLEVEL__M 0xFF
+#define SCU_RAM_QAM_LOCKED_INTLEVEL__PRE 0x0
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_NOT_LOCKED 0x0
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_AMP_OK 0x1
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_RATE_OK 0x2
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_FREQ_OK 0x3
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_UPRIGHT_OK 0x4
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_PHNOISE_OK 0x5
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_TRACK_OK 0x6
+#define SCU_RAM_QAM_LOCKED_INTLEVEL_IMPNOISE_OK 0x7
+
+#define SCU_RAM_QAM_LOCKED_LOCKED__B 8
+#define SCU_RAM_QAM_LOCKED_LOCKED__W 8
+#define SCU_RAM_QAM_LOCKED_LOCKED__M 0xFF00
+#define SCU_RAM_QAM_LOCKED_LOCKED__PRE 0x0
+#define SCU_RAM_QAM_LOCKED_LOCKED_NOT_LOCKED 0x0
+#define SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED 0x4000
+#define SCU_RAM_QAM_LOCKED_LOCKED_LOCKED 0x8000
+#define SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK 0xC000
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI__A 0x831FBB
+#define SCU_RAM_QAM_EVENTS_OCC_HI__W 16
+#define SCU_RAM_QAM_EVENTS_OCC_HI__M 0xFFFF
+#define SCU_RAM_QAM_EVENTS_OCC_HI__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PREBER__B 0
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PREBER__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PREBER__M 0x1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PREBER__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PACKET_FAIL__B 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PACKET_FAIL__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PACKET_FAIL__M 0x2
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PACKET_FAIL__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PRBS__B 2
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PRBS__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PRBS__M 0x4
+#define SCU_RAM_QAM_EVENTS_OCC_HI_PRBS__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_IN__B 3
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_IN__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_IN__M 0x8
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_IN__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_OUT__B 4
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_OUT__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_OUT__M 0x10
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_LOCK_OUT__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_POSTBER__B 5
+#define SCU_RAM_QAM_EVENTS_OCC_HI_POSTBER__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_POSTBER__M 0x20
+#define SCU_RAM_QAM_EVENTS_OCC_HI_POSTBER__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_FULL__B 6
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_FULL__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_FULL__M 0x40
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_FULL__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_EMPTY__B 7
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_EMPTY__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_EMPTY__M 0x80
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FIFO_EMPTY__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_GRAB__B 8
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_GRAB__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_GRAB__M 0x100
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_GRAB__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_CHANGE__B 9
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_CHANGE__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_CHANGE__M 0x200
+#define SCU_RAM_QAM_EVENTS_OCC_HI_OC_CHANGE__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_LCK_CHG__B 10
+#define SCU_RAM_QAM_EVENTS_OCC_HI_LCK_CHG__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_LCK_CHG__M 0x400
+#define SCU_RAM_QAM_EVENTS_OCC_HI_LCK_CHG__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FSM_CHG__B 11
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FSM_CHG__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FSM_CHG__M 0x800
+#define SCU_RAM_QAM_EVENTS_OCC_HI_FSM_CHG__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_HI_RSV__B 12
+#define SCU_RAM_QAM_EVENTS_OCC_HI_RSV__W 4
+#define SCU_RAM_QAM_EVENTS_OCC_HI_RSV__M 0xF000
+#define SCU_RAM_QAM_EVENTS_OCC_HI_RSV__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO__A 0x831FBC
+#define SCU_RAM_QAM_EVENTS_OCC_LO__W 16
+#define SCU_RAM_QAM_EVENTS_OCC_LO__M 0xFFFF
+#define SCU_RAM_QAM_EVENTS_OCC_LO__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_TIMER__B 0
+#define SCU_RAM_QAM_EVENTS_OCC_LO_TIMER__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_TIMER__M 0x1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_TIMER__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_CLIP__B 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_CLIP__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_CLIP__M 0x2
+#define SCU_RAM_QAM_EVENTS_OCC_LO_CLIP__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SENSE__B 2
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SENSE__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SENSE__M 0x4
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SENSE__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_POWER__B 3
+#define SCU_RAM_QAM_EVENTS_OCC_LO_POWER__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_POWER__M 0x8
+#define SCU_RAM_QAM_EVENTS_OCC_LO_POWER__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MEDIAN__B 4
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MEDIAN__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MEDIAN__M 0x10
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MEDIAN__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MER__B 5
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MER__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MER__M 0x20
+#define SCU_RAM_QAM_EVENTS_OCC_LO_MER__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_LOOP__B 6
+#define SCU_RAM_QAM_EVENTS_OCC_LO_LOOP__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_LOOP__M 0x40
+#define SCU_RAM_QAM_EVENTS_OCC_LO_LOOP__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_FREQWRAP__B 7
+#define SCU_RAM_QAM_EVENTS_OCC_LO_FREQWRAP__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_FREQWRAP__M 0x80
+#define SCU_RAM_QAM_EVENTS_OCC_LO_FREQWRAP__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SER__B 8
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SER__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SER__M 0x100
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SER__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_VD_LOCK_IN__B 9
+#define SCU_RAM_QAM_EVENTS_OCC_LO_VD_LOCK_IN__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_VD_LOCK_IN__M 0x200
+#define SCU_RAM_QAM_EVENTS_OCC_LO_VD_LOCK_IN__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_IN__B 10
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_IN__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_IN__M 0x400
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_IN__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_OUT__B 11
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_OUT__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_OUT__M 0x800
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_LOCK_OUT__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_TIME_OUT__B 12
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_TIME_OUT__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_TIME_OUT__M 0x1000
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SY_TIME_OUT__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SYNCWORD__B 13
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SYNCWORD__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SYNCWORD__M 0x2000
+#define SCU_RAM_QAM_EVENTS_OCC_LO_SYNCWORD__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_IN__B 14
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_IN__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_IN__M 0x4000
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_IN__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_OUT__B 15
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_OUT__W 1
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_OUT__M 0x8000
+#define SCU_RAM_QAM_EVENTS_OCC_LO_DI_LOCK_OUT__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_SCHED_HI__A 0x831FBD
+#define SCU_RAM_QAM_EVENTS_SCHED_HI__W 16
+#define SCU_RAM_QAM_EVENTS_SCHED_HI__M 0xFFFF
+#define SCU_RAM_QAM_EVENTS_SCHED_HI__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_SCHED_HI_BIT__B 0
+#define SCU_RAM_QAM_EVENTS_SCHED_HI_BIT__W 16
+#define SCU_RAM_QAM_EVENTS_SCHED_HI_BIT__M 0xFFFF
+#define SCU_RAM_QAM_EVENTS_SCHED_HI_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_SCHED_LO__A 0x831FBE
+#define SCU_RAM_QAM_EVENTS_SCHED_LO__W 16
+#define SCU_RAM_QAM_EVENTS_SCHED_LO__M 0xFFFF
+#define SCU_RAM_QAM_EVENTS_SCHED_LO__PRE 0x0
+
+#define SCU_RAM_QAM_EVENTS_SCHED_LO_BIT__B 0
+#define SCU_RAM_QAM_EVENTS_SCHED_LO_BIT__W 16
+#define SCU_RAM_QAM_EVENTS_SCHED_LO_BIT__M 0xFFFF
+#define SCU_RAM_QAM_EVENTS_SCHED_LO_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_TASKLETS_SCHED__A 0x831FBF
+#define SCU_RAM_QAM_TASKLETS_SCHED__W 16
+#define SCU_RAM_QAM_TASKLETS_SCHED__M 0xFFFF
+#define SCU_RAM_QAM_TASKLETS_SCHED__PRE 0x0
+
+#define SCU_RAM_QAM_TASKLETS_SCHED_BIT__B 0
+#define SCU_RAM_QAM_TASKLETS_SCHED_BIT__W 16
+#define SCU_RAM_QAM_TASKLETS_SCHED_BIT__M 0xFFFF
+#define SCU_RAM_QAM_TASKLETS_SCHED_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_TASKLETS_RUN__A 0x831FC0
+#define SCU_RAM_QAM_TASKLETS_RUN__W 16
+#define SCU_RAM_QAM_TASKLETS_RUN__M 0xFFFF
+#define SCU_RAM_QAM_TASKLETS_RUN__PRE 0x0
+
+#define SCU_RAM_QAM_TASKLETS_RUN_BIT__B 0
+#define SCU_RAM_QAM_TASKLETS_RUN_BIT__W 16
+#define SCU_RAM_QAM_TASKLETS_RUN_BIT__M 0xFFFF
+#define SCU_RAM_QAM_TASKLETS_RUN_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI__A 0x831FC1
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI__W 16
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI__M 0xFFFF
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI__PRE 0x0
+
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI_BIT__B 0
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI_BIT__W 16
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI_BIT__M 0xFFFF
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_HI_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO__A 0x831FC2
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO__W 16
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO__M 0xFFFF
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO__PRE 0x0
+
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO_BIT__B 0
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO_BIT__W 16
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO_BIT__M 0xFFFF
+#define SCU_RAM_QAM_ACTIVE_SYM_RCRATE_LO_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_5__A 0x831FC3
+#define SCU_RAM_QAM_RD_RSV_5__W 16
+#define SCU_RAM_QAM_RD_RSV_5__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_5__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_5_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_5_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_5_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_5_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_6__A 0x831FC4
+#define SCU_RAM_QAM_RD_RSV_6__W 16
+#define SCU_RAM_QAM_RD_RSV_6__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_6__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_6_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_6_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_6_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_6_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_7__A 0x831FC5
+#define SCU_RAM_QAM_RD_RSV_7__W 16
+#define SCU_RAM_QAM_RD_RSV_7__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_7__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_7_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_7_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_7_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_7_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_8__A 0x831FC6
+#define SCU_RAM_QAM_RD_RSV_8__W 16
+#define SCU_RAM_QAM_RD_RSV_8__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_8__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_8_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_8_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_8_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_8_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_9__A 0x831FC7
+#define SCU_RAM_QAM_RD_RSV_9__W 16
+#define SCU_RAM_QAM_RD_RSV_9__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_9__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_9_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_9_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_9_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_9_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_10__A 0x831FC8
+#define SCU_RAM_QAM_RD_RSV_10__W 16
+#define SCU_RAM_QAM_RD_RSV_10__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_10__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_10_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_10_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_10_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_10_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_AGC_TPOW_OFFS__A 0x831FC9
+#define SCU_RAM_QAM_AGC_TPOW_OFFS__W 16
+#define SCU_RAM_QAM_AGC_TPOW_OFFS__M 0xFFFF
+#define SCU_RAM_QAM_AGC_TPOW_OFFS__PRE 0x0
+
+#define SCU_RAM_QAM_AGC_TPOW_OFFS_BIT__B 0
+#define SCU_RAM_QAM_AGC_TPOW_OFFS_BIT__W 16
+#define SCU_RAM_QAM_AGC_TPOW_OFFS_BIT__M 0xFFFF
+#define SCU_RAM_QAM_AGC_TPOW_OFFS_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_STATE__A 0x831FCA
+#define SCU_RAM_QAM_FSM_STATE__W 4
+#define SCU_RAM_QAM_FSM_STATE__M 0xF
+#define SCU_RAM_QAM_FSM_STATE__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_STATE_BIT__B 0
+#define SCU_RAM_QAM_FSM_STATE_BIT__W 4
+#define SCU_RAM_QAM_FSM_STATE_BIT__M 0xF
+#define SCU_RAM_QAM_FSM_STATE_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_AMP 0x0
+#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_RATE 0x1
+#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_FREQ 0x2
+#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_UPRIGHT 0x3
+#define SCU_RAM_QAM_FSM_STATE_BIT_HUNTING_PHASE 0x4
+#define SCU_RAM_QAM_FSM_STATE_BIT_TRACKING_PHNOISE 0x5
+#define SCU_RAM_QAM_FSM_STATE_BIT_TRACKING 0x6
+#define SCU_RAM_QAM_FSM_STATE_BIT_TRACKING_BURST 0x7
+
+#define SCU_RAM_QAM_FSM_STATE_NEW__A 0x831FCB
+#define SCU_RAM_QAM_FSM_STATE_NEW__W 4
+#define SCU_RAM_QAM_FSM_STATE_NEW__M 0xF
+#define SCU_RAM_QAM_FSM_STATE_NEW__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT__B 0
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT__W 4
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT__M 0xF
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT__PRE 0x0
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_AMP 0x0
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_RATE 0x1
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_FREQ 0x2
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_UPRIGHT 0x3
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_HUNTING_PHASE 0x4
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_TRACKING_PHNOISE 0x5
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_TRACKING 0x6
+#define SCU_RAM_QAM_FSM_STATE_NEW_BIT_TRACKING_BURST 0x7
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS__A 0x831FCC
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS__W 9
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS__M 0x1FF
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_AMP__B 0
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_AMP__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_AMP__M 0x1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_AMP__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RATEVAR__B 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RATEVAR__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RATEVAR__M 0x2
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RATEVAR__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RADIUS__B 2
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RADIUS__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RADIUS__M 0x4
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_RADIUS__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQ__B 3
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQ__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQ__M 0x8
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQ__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQVAR__B 4
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQVAR__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQVAR__M 0x10
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_FREQVAR__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_CPHASE__B 5
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_CPHASE__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_CPHASE__M 0x20
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_CPHASE__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_UPRIGHT__B 6
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_UPRIGHT__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_UPRIGHT__M 0x40
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_UPRIGHT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_PHASE__B 7
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_PHASE__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_PHASE__M 0x80
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_PHASE__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_MEDIAN__B 8
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_MEDIAN__W 1
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_MEDIAN__M 0x100
+#define SCU_RAM_QAM_FSM_LOCK_FLAGS_LCK_MEDIAN__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_RATE_VARIATION__A 0x831FCD
+#define SCU_RAM_QAM_FSM_RATE_VARIATION__W 16
+#define SCU_RAM_QAM_FSM_RATE_VARIATION__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RATE_VARIATION__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_RATE_VARIATION_BIT__B 0
+#define SCU_RAM_QAM_FSM_RATE_VARIATION_BIT__W 16
+#define SCU_RAM_QAM_FSM_RATE_VARIATION_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_RATE_VARIATION_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION__A 0x831FCE
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION__W 16
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION__PRE 0x0
+
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION_BIT__B 0
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION_BIT__W 16
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION_BIT__M 0xFFFF
+#define SCU_RAM_QAM_FSM_FREQ_VARIATION_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_ERR_STATE__A 0x831FCF
+#define SCU_RAM_QAM_ERR_STATE__W 4
+#define SCU_RAM_QAM_ERR_STATE__M 0xF
+#define SCU_RAM_QAM_ERR_STATE__PRE 0x0
+
+#define SCU_RAM_QAM_ERR_STATE_BIT__B 0
+#define SCU_RAM_QAM_ERR_STATE_BIT__W 4
+#define SCU_RAM_QAM_ERR_STATE_BIT__M 0xF
+#define SCU_RAM_QAM_ERR_STATE_BIT__PRE 0x0
+#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_AMP 0x0
+#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_RATE 0x1
+#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_FREQ 0x2
+#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_UPRIGHT 0x3
+#define SCU_RAM_QAM_ERR_STATE_BIT_HUNTING_PHASE 0x4
+#define SCU_RAM_QAM_ERR_STATE_BIT_TRACKING_PHNOISE 0x5
+#define SCU_RAM_QAM_ERR_STATE_BIT_TRACKING 0x6
+#define SCU_RAM_QAM_ERR_STATE_BIT_TRACKING_BURST 0x7
+
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS__A 0x831FD0
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS__W 9
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS__M 0x1FF
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS__PRE 0x0
+
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS_LCK_AMP__B 0
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS_LCK_AMP__W 1
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS_LCK_AMP__M 0x1
+#define SCU_RAM_QAM_ERR_LOCK_FLAGS_LCK_AMP__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_LOCK__A 0x831FD1
+#define SCU_RAM_QAM_EQ_LOCK__W 1
+#define SCU_RAM_QAM_EQ_LOCK__M 0x1
+#define SCU_RAM_QAM_EQ_LOCK__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_LOCK_BIT__B 0
+#define SCU_RAM_QAM_EQ_LOCK_BIT__W 1
+#define SCU_RAM_QAM_EQ_LOCK_BIT__M 0x1
+#define SCU_RAM_QAM_EQ_LOCK_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_STATE__A 0x831FD2
+#define SCU_RAM_QAM_EQ_STATE__W 16
+#define SCU_RAM_QAM_EQ_STATE__M 0xFFFF
+#define SCU_RAM_QAM_EQ_STATE__PRE 0x0
+
+#define SCU_RAM_QAM_EQ_STATE_BIT__B 0
+#define SCU_RAM_QAM_EQ_STATE_BIT__W 16
+#define SCU_RAM_QAM_EQ_STATE_BIT__M 0xFFFF
+#define SCU_RAM_QAM_EQ_STATE_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_0__A 0x831FD3
+#define SCU_RAM_QAM_RD_RSV_0__W 16
+#define SCU_RAM_QAM_RD_RSV_0__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_0__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_0_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_0_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_0_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_0_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_1__A 0x831FD4
+#define SCU_RAM_QAM_RD_RSV_1__W 16
+#define SCU_RAM_QAM_RD_RSV_1__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_1__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_1_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_1_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_1_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_1_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_2__A 0x831FD5
+#define SCU_RAM_QAM_RD_RSV_2__W 16
+#define SCU_RAM_QAM_RD_RSV_2__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_2__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_2_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_2_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_2_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_2_BIT__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_3__A 0x831FD6
+#define SCU_RAM_QAM_RD_RSV_3__W 16
+#define SCU_RAM_QAM_RD_RSV_3__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_3__PRE 0x0
+
+#define SCU_RAM_QAM_RD_RSV_3_BIT__B 0
+#define SCU_RAM_QAM_RD_RSV_3_BIT__W 16
+#define SCU_RAM_QAM_RD_RSV_3_BIT__M 0xFFFF
+#define SCU_RAM_QAM_RD_RSV_3_BIT__PRE 0x0
+
+#define SCU_RAM_VSB_CTL_MODE__A 0x831FD7
+#define SCU_RAM_VSB_CTL_MODE__W 2
+#define SCU_RAM_VSB_CTL_MODE__M 0x3
+#define SCU_RAM_VSB_CTL_MODE__PRE 0x0
+
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC__B 0
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC__W 1
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC__M 0x1
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC__PRE 0x0
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC_OFF 0x0
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_AGC_ON 0x1
+
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON__B 1
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON__W 1
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON__M 0x2
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON__PRE 0x0
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON_OFF 0x0
+#define SCU_RAM_VSB_CTL_MODE_VSB_CTL_MODE_MON_ON 0x2
+
+#define SCU_RAM_VSB_NOTCH_THRESHOLD__A 0x831FD8
+#define SCU_RAM_VSB_NOTCH_THRESHOLD__W 16
+#define SCU_RAM_VSB_NOTCH_THRESHOLD__M 0xFFFF
+#define SCU_RAM_VSB_NOTCH_THRESHOLD__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_0__A 0x831FD9
+#define SCU_RAM_VSB_RSV_0__W 16
+#define SCU_RAM_VSB_RSV_0__M 0xFFFF
+#define SCU_RAM_VSB_RSV_0__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_1__A 0x831FDA
+#define SCU_RAM_VSB_RSV_1__W 16
+#define SCU_RAM_VSB_RSV_1__M 0xFFFF
+#define SCU_RAM_VSB_RSV_1__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_2__A 0x831FDB
+#define SCU_RAM_VSB_RSV_2__W 16
+#define SCU_RAM_VSB_RSV_2__M 0xFFFF
+#define SCU_RAM_VSB_RSV_2__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_3__A 0x831FDC
+#define SCU_RAM_VSB_RSV_3__W 16
+#define SCU_RAM_VSB_RSV_3__M 0xFFFF
+#define SCU_RAM_VSB_RSV_3__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_4__A 0x831FDD
+#define SCU_RAM_VSB_RSV_4__W 16
+#define SCU_RAM_VSB_RSV_4__M 0xFFFF
+#define SCU_RAM_VSB_RSV_4__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_5__A 0x831FDE
+#define SCU_RAM_VSB_RSV_5__W 16
+#define SCU_RAM_VSB_RSV_5__M 0xFFFF
+#define SCU_RAM_VSB_RSV_5__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_6__A 0x831FDF
+#define SCU_RAM_VSB_RSV_6__W 16
+#define SCU_RAM_VSB_RSV_6__M 0xFFFF
+#define SCU_RAM_VSB_RSV_6__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_7__A 0x831FE0
+#define SCU_RAM_VSB_RSV_7__W 16
+#define SCU_RAM_VSB_RSV_7__M 0xFFFF
+#define SCU_RAM_VSB_RSV_7__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_8__A 0x831FE1
+#define SCU_RAM_VSB_RSV_8__W 16
+#define SCU_RAM_VSB_RSV_8__M 0xFFFF
+#define SCU_RAM_VSB_RSV_8__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_9__A 0x831FE2
+#define SCU_RAM_VSB_RSV_9__W 16
+#define SCU_RAM_VSB_RSV_9__M 0xFFFF
+#define SCU_RAM_VSB_RSV_9__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_10__A 0x831FE3
+#define SCU_RAM_VSB_RSV_10__W 16
+#define SCU_RAM_VSB_RSV_10__M 0xFFFF
+#define SCU_RAM_VSB_RSV_10__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_11__A 0x831FE4
+#define SCU_RAM_VSB_RSV_11__W 16
+#define SCU_RAM_VSB_RSV_11__M 0xFFFF
+#define SCU_RAM_VSB_RSV_11__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_12__A 0x831FE5
+#define SCU_RAM_VSB_RSV_12__W 16
+#define SCU_RAM_VSB_RSV_12__M 0xFFFF
+#define SCU_RAM_VSB_RSV_12__PRE 0x0
+
+#define SCU_RAM_VSB_RSV_13__A 0x831FE6
+#define SCU_RAM_VSB_RSV_13__W 16
+#define SCU_RAM_VSB_RSV_13__M 0xFFFF
+#define SCU_RAM_VSB_RSV_13__PRE 0x0
+
+#define SCU_RAM_VSB_AGC_POW_TGT__A 0x831FE7
+#define SCU_RAM_VSB_AGC_POW_TGT__W 15
+#define SCU_RAM_VSB_AGC_POW_TGT__M 0x7FFF
+#define SCU_RAM_VSB_AGC_POW_TGT__PRE 0x0
+
+#define SCU_RAM_VSB_OUTER_LOOP_CYCLE__A 0x831FE8
+#define SCU_RAM_VSB_OUTER_LOOP_CYCLE__W 8
+#define SCU_RAM_VSB_OUTER_LOOP_CYCLE__M 0xFF
+#define SCU_RAM_VSB_OUTER_LOOP_CYCLE__PRE 0x0
+
+#define SCU_RAM_VSB_FIELD_NUMBER__A 0x831FE9
+#define SCU_RAM_VSB_FIELD_NUMBER__W 9
+#define SCU_RAM_VSB_FIELD_NUMBER__M 0x1FF
+#define SCU_RAM_VSB_FIELD_NUMBER__PRE 0x0
+
+#define SCU_RAM_VSB_SEGMENT_NUMBER__A 0x831FEA
+#define SCU_RAM_VSB_SEGMENT_NUMBER__W 10
+#define SCU_RAM_VSB_SEGMENT_NUMBER__M 0x3FF
+#define SCU_RAM_VSB_SEGMENT_NUMBER__PRE 0x0
+
+#define SCU_RAM_DRIVER_VER_HI__A 0x831FEB
+#define SCU_RAM_DRIVER_VER_HI__W 16
+#define SCU_RAM_DRIVER_VER_HI__M 0xFFFF
+#define SCU_RAM_DRIVER_VER_HI__PRE 0x0
+
+#define SCU_RAM_DRIVER_VER_LO__A 0x831FEC
+#define SCU_RAM_DRIVER_VER_LO__W 16
+#define SCU_RAM_DRIVER_VER_LO__M 0xFFFF
+#define SCU_RAM_DRIVER_VER_LO__PRE 0x0
+
+#define SCU_RAM_PARAM_15__A 0x831FED
+#define SCU_RAM_PARAM_15__W 16
+#define SCU_RAM_PARAM_15__M 0xFFFF
+#define SCU_RAM_PARAM_15__PRE 0x0
+
+#define SCU_RAM_PARAM_14__A 0x831FEE
+#define SCU_RAM_PARAM_14__W 16
+#define SCU_RAM_PARAM_14__M 0xFFFF
+#define SCU_RAM_PARAM_14__PRE 0x0
+
+#define SCU_RAM_PARAM_13__A 0x831FEF
+#define SCU_RAM_PARAM_13__W 16
+#define SCU_RAM_PARAM_13__M 0xFFFF
+#define SCU_RAM_PARAM_13__PRE 0x0
+
+#define SCU_RAM_PARAM_12__A 0x831FF0
+#define SCU_RAM_PARAM_12__W 16
+#define SCU_RAM_PARAM_12__M 0xFFFF
+#define SCU_RAM_PARAM_12__PRE 0x0
+
+#define SCU_RAM_PARAM_11__A 0x831FF1
+#define SCU_RAM_PARAM_11__W 16
+#define SCU_RAM_PARAM_11__M 0xFFFF
+#define SCU_RAM_PARAM_11__PRE 0x0
+
+#define SCU_RAM_PARAM_10__A 0x831FF2
+#define SCU_RAM_PARAM_10__W 16
+#define SCU_RAM_PARAM_10__M 0xFFFF
+#define SCU_RAM_PARAM_10__PRE 0x0
+
+#define SCU_RAM_PARAM_9__A 0x831FF3
+#define SCU_RAM_PARAM_9__W 16
+#define SCU_RAM_PARAM_9__M 0xFFFF
+#define SCU_RAM_PARAM_9__PRE 0x0
+
+#define SCU_RAM_PARAM_8__A 0x831FF4
+#define SCU_RAM_PARAM_8__W 16
+#define SCU_RAM_PARAM_8__M 0xFFFF
+#define SCU_RAM_PARAM_8__PRE 0x0
+
+#define SCU_RAM_PARAM_7__A 0x831FF5
+#define SCU_RAM_PARAM_7__W 16
+#define SCU_RAM_PARAM_7__M 0xFFFF
+#define SCU_RAM_PARAM_7__PRE 0x0
+
+#define SCU_RAM_PARAM_6__A 0x831FF6
+#define SCU_RAM_PARAM_6__W 16
+#define SCU_RAM_PARAM_6__M 0xFFFF
+#define SCU_RAM_PARAM_6__PRE 0x0
+
+#define SCU_RAM_PARAM_5__A 0x831FF7
+#define SCU_RAM_PARAM_5__W 16
+#define SCU_RAM_PARAM_5__M 0xFFFF
+#define SCU_RAM_PARAM_5__PRE 0x0
+
+#define SCU_RAM_PARAM_4__A 0x831FF8
+#define SCU_RAM_PARAM_4__W 16
+#define SCU_RAM_PARAM_4__M 0xFFFF
+#define SCU_RAM_PARAM_4__PRE 0x0
+
+#define SCU_RAM_PARAM_3__A 0x831FF9
+#define SCU_RAM_PARAM_3__W 16
+#define SCU_RAM_PARAM_3__M 0xFFFF
+#define SCU_RAM_PARAM_3__PRE 0x0
+
+#define SCU_RAM_PARAM_2__A 0x831FFA
+#define SCU_RAM_PARAM_2__W 16
+#define SCU_RAM_PARAM_2__M 0xFFFF
+#define SCU_RAM_PARAM_2__PRE 0x0
+
+#define SCU_RAM_PARAM_1__A 0x831FFB
+#define SCU_RAM_PARAM_1__W 16
+#define SCU_RAM_PARAM_1__M 0xFFFF
+#define SCU_RAM_PARAM_1__PRE 0x0
+#define SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_NOT_LOCKED 0x0
+#define SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_DEMOD_LOCKED 0x4000
+#define SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_LOCKED 0x8000
+#define SCU_RAM_PARAM_1_RES_DEMOD_GET_LOCK_NEVER_LOCK 0xC000
+
+#define SCU_RAM_PARAM_0__A 0x831FFC
+#define SCU_RAM_PARAM_0__W 16
+#define SCU_RAM_PARAM_0__M 0xFFFF
+#define SCU_RAM_PARAM_0__PRE 0x0
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_MN_STANDARD 0x2
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_B_STANDARD 0x103
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_G_STANDARD 0x3
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_DK_STANDARD 0x4
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_L_STANDARD 0x9
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_LP_STANDARD 0x109
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_I_STANDARD 0xA
+#define SCU_RAM_PARAM_0_ATV_DEMOD_SETENV_FM_STANDARD 0x40
+#define SCU_RAM_PARAM_0_QAM_DEMOD_SETENV_ANNEX_A 0x0
+#define SCU_RAM_PARAM_0_QAM_DEMOD_SETENV_ANNEX_B 0x1
+#define SCU_RAM_PARAM_0_QAM_DEMOD_SETENV_ANNEX_C 0x2
+#define SCU_RAM_PARAM_0_QAM_DEMOD_SETENV_ANNEX_D 0x3
+#define SCU_RAM_PARAM_0_RESULT_OK 0x0
+#define SCU_RAM_PARAM_0_RESULT_UNKCMD 0xFFFF
+#define SCU_RAM_PARAM_0_RESULT_UNKSTD 0xFFFE
+#define SCU_RAM_PARAM_0_RESULT_INVPAR 0xFFFD
+#define SCU_RAM_PARAM_0_RESULT_SIZE 0xFFFC
+
+#define SCU_RAM_COMMAND__A 0x831FFD
+#define SCU_RAM_COMMAND__W 16
+#define SCU_RAM_COMMAND__M 0xFFFF
+#define SCU_RAM_COMMAND__PRE 0x0
+#define SCU_RAM_COMMAND_CMD_DEMOD_RESET 0x1
+#define SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV 0x2
+#define SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM 0x3
+#define SCU_RAM_COMMAND_CMD_DEMOD_START 0x4
+#define SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK 0x5
+#define SCU_RAM_COMMAND_CMD_DEMOD_GET_PARAM 0x6
+#define SCU_RAM_COMMAND_CMD_DEMOD_HOLD 0x7
+#define SCU_RAM_COMMAND_CMD_DEMOD_RESUME 0x8
+#define SCU_RAM_COMMAND_CMD_DEMOD_STOP 0x9
+#define SCU_RAM_COMMAND_CMD_STD_QAM_IRQ_ACTIVATE 0x80
+#define SCU_RAM_COMMAND_CMD_STD_QAM_IRQ_INACTIVATE 0x81
+#define SCU_RAM_COMMAND_CMD_STD_QAM_IRQ_SIGNAL 0x82
+#define SCU_RAM_COMMAND_CMD_STD_QAM_IRQ_MONITOR 0x83
+#define SCU_RAM_COMMAND_CMD_STD_QAM_TSK_ENABLE 0x84
+#define SCU_RAM_COMMAND_CMD_STD_QAM_FSM_SET_STATE 0x85
+#define SCU_RAM_COMMAND_CMD_DEBUG_GET_IRQ_REGS 0x80
+#define SCU_RAM_COMMAND_CMD_DEBUG_HTOL 0x81
+#define SCU_RAM_COMMAND_CMD_DEBUG_GET_STACK_POINTER 0x82
+#define SCU_RAM_COMMAND_CMD_DEBUG_START_STACK_CHECK 0x83
+#define SCU_RAM_COMMAND_CMD_DEBUG_STOP_STACK_CHECK 0x84
+#define SCU_RAM_COMMAND_CMD_ADMIN_NOP 0xFF
+#define SCU_RAM_COMMAND_CMD_ADMIN_GET_VERSION 0xFE
+#define SCU_RAM_COMMAND_CMD_ADMIN_GET_JTAG_VERSION 0xFD
+#define SCU_RAM_COMMAND_CMD_AUX_SCU_ATOMIC_ACCESS 0xC0
+
+#define SCU_RAM_COMMAND_STANDARD__B 8
+#define SCU_RAM_COMMAND_STANDARD__W 8
+#define SCU_RAM_COMMAND_STANDARD__M 0xFF00
+#define SCU_RAM_COMMAND_STANDARD__PRE 0x0
+#define SCU_RAM_COMMAND_STANDARD_ATV 0x100
+#define SCU_RAM_COMMAND_STANDARD_QAM 0x200
+#define SCU_RAM_COMMAND_STANDARD_VSB 0x300
+#define SCU_RAM_COMMAND_STANDARD_OFDM 0x400
+#define SCU_RAM_COMMAND_STANDARD_OOB 0x8000
+#define SCU_RAM_COMMAND_STANDARD_TOP 0xFF00
+
+#define SCU_RAM_VERSION_HI__A 0x831FFE
+#define SCU_RAM_VERSION_HI__W 16
+#define SCU_RAM_VERSION_HI__M 0xFFFF
+#define SCU_RAM_VERSION_HI__PRE 0x0
+
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N3__B 12
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N3__W 4
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N3__M 0xF000
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N3__PRE 0x0
+
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N2__B 8
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N2__W 4
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N2__M 0xF00
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N2__PRE 0x0
+
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N1__B 4
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N1__W 4
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N1__M 0xF0
+#define SCU_RAM_VERSION_HI_VER_MAJOR_N1__PRE 0x0
+
+#define SCU_RAM_VERSION_HI_VER_MINOR_N1__B 0
+#define SCU_RAM_VERSION_HI_VER_MINOR_N1__W 4
+#define SCU_RAM_VERSION_HI_VER_MINOR_N1__M 0xF
+#define SCU_RAM_VERSION_HI_VER_MINOR_N1__PRE 0x0
+
+#define SCU_RAM_VERSION_LO__A 0x831FFF
+#define SCU_RAM_VERSION_LO__W 16
+#define SCU_RAM_VERSION_LO__M 0xFFFF
+#define SCU_RAM_VERSION_LO__PRE 0x0
+
+#define SCU_RAM_VERSION_LO_VER_PATCH_N4__B 12
+#define SCU_RAM_VERSION_LO_VER_PATCH_N4__W 4
+#define SCU_RAM_VERSION_LO_VER_PATCH_N4__M 0xF000
+#define SCU_RAM_VERSION_LO_VER_PATCH_N4__PRE 0x0
+
+#define SCU_RAM_VERSION_LO_VER_PATCH_N3__B 8
+#define SCU_RAM_VERSION_LO_VER_PATCH_N3__W 4
+#define SCU_RAM_VERSION_LO_VER_PATCH_N3__M 0xF00
+#define SCU_RAM_VERSION_LO_VER_PATCH_N3__PRE 0x0
+
+#define SCU_RAM_VERSION_LO_VER_PATCH_N2__B 4
+#define SCU_RAM_VERSION_LO_VER_PATCH_N2__W 4
+#define SCU_RAM_VERSION_LO_VER_PATCH_N2__M 0xF0
+#define SCU_RAM_VERSION_LO_VER_PATCH_N2__PRE 0x0
+
+#define SCU_RAM_VERSION_LO_VER_PATCH_N1__B 0
+#define SCU_RAM_VERSION_LO_VER_PATCH_N1__W 4
+#define SCU_RAM_VERSION_LO_VER_PATCH_N1__M 0xF
+#define SCU_RAM_VERSION_LO_VER_PATCH_N1__PRE 0x0
+
+#define SIO_COMM_EXEC__A 0x400000
+#define SIO_COMM_EXEC__W 2
+#define SIO_COMM_EXEC__M 0x3
+#define SIO_COMM_EXEC__PRE 0x0
+#define SIO_COMM_EXEC_STOP 0x0
+#define SIO_COMM_EXEC_ACTIVE 0x1
+#define SIO_COMM_EXEC_HOLD 0x2
+
+#define SIO_COMM_STATE__A 0x400001
+#define SIO_COMM_STATE__W 16
+#define SIO_COMM_STATE__M 0xFFFF
+#define SIO_COMM_STATE__PRE 0x0
+#define SIO_COMM_MB__A 0x400002
+#define SIO_COMM_MB__W 16
+#define SIO_COMM_MB__M 0xFFFF
+#define SIO_COMM_MB__PRE 0x0
+#define SIO_COMM_INT_REQ__A 0x400003
+#define SIO_COMM_INT_REQ__W 16
+#define SIO_COMM_INT_REQ__M 0xFFFF
+#define SIO_COMM_INT_REQ__PRE 0x0
+
+#define SIO_COMM_INT_REQ_HI_REQ__B 0
+#define SIO_COMM_INT_REQ_HI_REQ__W 1
+#define SIO_COMM_INT_REQ_HI_REQ__M 0x1
+#define SIO_COMM_INT_REQ_HI_REQ__PRE 0x0
+
+#define SIO_COMM_INT_REQ_SA_REQ__B 1
+#define SIO_COMM_INT_REQ_SA_REQ__W 1
+#define SIO_COMM_INT_REQ_SA_REQ__M 0x2
+#define SIO_COMM_INT_REQ_SA_REQ__PRE 0x0
+
+#define SIO_COMM_INT_STA__A 0x400005
+#define SIO_COMM_INT_STA__W 16
+#define SIO_COMM_INT_STA__M 0xFFFF
+#define SIO_COMM_INT_STA__PRE 0x0
+#define SIO_COMM_INT_MSK__A 0x400006
+#define SIO_COMM_INT_MSK__W 16
+#define SIO_COMM_INT_MSK__M 0xFFFF
+#define SIO_COMM_INT_MSK__PRE 0x0
+#define SIO_COMM_INT_STM__A 0x400007
+#define SIO_COMM_INT_STM__W 16
+#define SIO_COMM_INT_STM__M 0xFFFF
+#define SIO_COMM_INT_STM__PRE 0x0
+
+#define SIO_TOP_COMM_EXEC__A 0x410000
+#define SIO_TOP_COMM_EXEC__W 2
+#define SIO_TOP_COMM_EXEC__M 0x3
+#define SIO_TOP_COMM_EXEC__PRE 0x0
+#define SIO_TOP_COMM_EXEC_STOP 0x0
+#define SIO_TOP_COMM_EXEC_ACTIVE 0x1
+#define SIO_TOP_COMM_EXEC_HOLD 0x2
+
+#define SIO_TOP_COMM_KEY__A 0x41000F
+#define SIO_TOP_COMM_KEY__W 16
+#define SIO_TOP_COMM_KEY__M 0xFFFF
+#define SIO_TOP_COMM_KEY__PRE 0x0
+#define SIO_TOP_COMM_KEY_KEY 0xFABA
+
+#define SIO_TOP_JTAGID_LO__A 0x410012
+#define SIO_TOP_JTAGID_LO__W 16
+#define SIO_TOP_JTAGID_LO__M 0xFFFF
+#define SIO_TOP_JTAGID_LO__PRE 0x0
+
+#define SIO_TOP_JTAGID_HI__A 0x410013
+#define SIO_TOP_JTAGID_HI__W 16
+#define SIO_TOP_JTAGID_HI__M 0xFFFF
+#define SIO_TOP_JTAGID_HI__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_FLG_SMM__A 0x420010
+#define SIO_HI_RA_RAM_S0_FLG_SMM__W 1
+#define SIO_HI_RA_RAM_S0_FLG_SMM__M 0x1
+#define SIO_HI_RA_RAM_S0_FLG_SMM__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_DEV_ID__A 0x420011
+#define SIO_HI_RA_RAM_S0_DEV_ID__W 7
+#define SIO_HI_RA_RAM_S0_DEV_ID__M 0x7F
+#define SIO_HI_RA_RAM_S0_DEV_ID__PRE 0x52
+
+#define SIO_HI_RA_RAM_S0_FLG_CRC__A 0x420012
+#define SIO_HI_RA_RAM_S0_FLG_CRC__W 1
+#define SIO_HI_RA_RAM_S0_FLG_CRC__M 0x1
+#define SIO_HI_RA_RAM_S0_FLG_CRC__PRE 0x0
+#define SIO_HI_RA_RAM_S0_FLG_ACC__A 0x420013
+#define SIO_HI_RA_RAM_S0_FLG_ACC__W 4
+#define SIO_HI_RA_RAM_S0_FLG_ACC__M 0xF
+#define SIO_HI_RA_RAM_S0_FLG_ACC__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__B 0
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__W 2
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__M 0x3
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_BRC__B 2
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_BRC__W 1
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_BRC__M 0x4
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_BRC__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_SWP__B 3
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_SWP__W 1
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_SWP__M 0x8
+#define SIO_HI_RA_RAM_S0_FLG_ACC_S0_SLV_SWP__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_STATE__A 0x420014
+#define SIO_HI_RA_RAM_S0_STATE__W 1
+#define SIO_HI_RA_RAM_S0_STATE__M 0x1
+#define SIO_HI_RA_RAM_S0_STATE__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_STATE_S0_SLV_STA__B 0
+#define SIO_HI_RA_RAM_S0_STATE_S0_SLV_STA__W 1
+#define SIO_HI_RA_RAM_S0_STATE_S0_SLV_STA__M 0x1
+#define SIO_HI_RA_RAM_S0_STATE_S0_SLV_STA__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_BLK_BNK__A 0x420015
+#define SIO_HI_RA_RAM_S0_BLK_BNK__W 12
+#define SIO_HI_RA_RAM_S0_BLK_BNK__M 0xFFF
+#define SIO_HI_RA_RAM_S0_BLK_BNK__PRE 0x82
+
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BNK__B 0
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BNK__W 6
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BNK__M 0x3F
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BNK__PRE 0x2
+
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BLK__B 6
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BLK__W 6
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BLK__M 0xFC0
+#define SIO_HI_RA_RAM_S0_BLK_BNK_S0_SLV_BLK__PRE 0x80
+
+#define SIO_HI_RA_RAM_S0_ADDR__A 0x420016
+#define SIO_HI_RA_RAM_S0_ADDR__W 16
+#define SIO_HI_RA_RAM_S0_ADDR__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_ADDR__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_ADDR_S0_SLV_ADDR__B 0
+#define SIO_HI_RA_RAM_S0_ADDR_S0_SLV_ADDR__W 16
+#define SIO_HI_RA_RAM_S0_ADDR_S0_SLV_ADDR__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_ADDR_S0_SLV_ADDR__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_CRC__A 0x420017
+#define SIO_HI_RA_RAM_S0_CRC__W 16
+#define SIO_HI_RA_RAM_S0_CRC__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_CRC__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_BUFFER__A 0x420018
+#define SIO_HI_RA_RAM_S0_BUFFER__W 16
+#define SIO_HI_RA_RAM_S0_BUFFER__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_BUFFER__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_RMWBUF__A 0x420019
+#define SIO_HI_RA_RAM_S0_RMWBUF__W 16
+#define SIO_HI_RA_RAM_S0_RMWBUF__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_RMWBUF__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_FLG_VB__A 0x42001A
+#define SIO_HI_RA_RAM_S0_FLG_VB__W 1
+#define SIO_HI_RA_RAM_S0_FLG_VB__M 0x1
+#define SIO_HI_RA_RAM_S0_FLG_VB__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_TEMP0__A 0x42001B
+#define SIO_HI_RA_RAM_S0_TEMP0__W 16
+#define SIO_HI_RA_RAM_S0_TEMP0__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_TEMP0__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_TEMP1__A 0x42001C
+#define SIO_HI_RA_RAM_S0_TEMP1__W 16
+#define SIO_HI_RA_RAM_S0_TEMP1__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_TEMP1__PRE 0x0
+
+#define SIO_HI_RA_RAM_S0_OFFSET__A 0x42001D
+#define SIO_HI_RA_RAM_S0_OFFSET__W 16
+#define SIO_HI_RA_RAM_S0_OFFSET__M 0xFFFF
+#define SIO_HI_RA_RAM_S0_OFFSET__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_FLG_SMM__A 0x420020
+#define SIO_HI_RA_RAM_S1_FLG_SMM__W 1
+#define SIO_HI_RA_RAM_S1_FLG_SMM__M 0x1
+#define SIO_HI_RA_RAM_S1_FLG_SMM__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_DEV_ID__A 0x420021
+#define SIO_HI_RA_RAM_S1_DEV_ID__W 7
+#define SIO_HI_RA_RAM_S1_DEV_ID__M 0x7F
+#define SIO_HI_RA_RAM_S1_DEV_ID__PRE 0x52
+
+#define SIO_HI_RA_RAM_S1_FLG_CRC__A 0x420022
+#define SIO_HI_RA_RAM_S1_FLG_CRC__W 1
+#define SIO_HI_RA_RAM_S1_FLG_CRC__M 0x1
+#define SIO_HI_RA_RAM_S1_FLG_CRC__PRE 0x0
+#define SIO_HI_RA_RAM_S1_FLG_ACC__A 0x420023
+#define SIO_HI_RA_RAM_S1_FLG_ACC__W 4
+#define SIO_HI_RA_RAM_S1_FLG_ACC__M 0xF
+#define SIO_HI_RA_RAM_S1_FLG_ACC__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_RWM__B 0
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_RWM__W 2
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_RWM__M 0x3
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_RWM__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_BRC__B 2
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_BRC__W 1
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_BRC__M 0x4
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_BRC__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_SWP__B 3
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_SWP__W 1
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_SWP__M 0x8
+#define SIO_HI_RA_RAM_S1_FLG_ACC_S1_SLV_SWP__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_STATE__A 0x420024
+#define SIO_HI_RA_RAM_S1_STATE__W 1
+#define SIO_HI_RA_RAM_S1_STATE__M 0x1
+#define SIO_HI_RA_RAM_S1_STATE__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_STATE_S1_SLV_STA__B 0
+#define SIO_HI_RA_RAM_S1_STATE_S1_SLV_STA__W 1
+#define SIO_HI_RA_RAM_S1_STATE_S1_SLV_STA__M 0x1
+#define SIO_HI_RA_RAM_S1_STATE_S1_SLV_STA__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_BLK_BNK__A 0x420025
+#define SIO_HI_RA_RAM_S1_BLK_BNK__W 12
+#define SIO_HI_RA_RAM_S1_BLK_BNK__M 0xFFF
+#define SIO_HI_RA_RAM_S1_BLK_BNK__PRE 0x82
+
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BNK__B 0
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BNK__W 6
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BNK__M 0x3F
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BNK__PRE 0x2
+
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BLK__B 6
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BLK__W 6
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BLK__M 0xFC0
+#define SIO_HI_RA_RAM_S1_BLK_BNK_S1_SLV_BLK__PRE 0x80
+
+#define SIO_HI_RA_RAM_S1_ADDR__A 0x420026
+#define SIO_HI_RA_RAM_S1_ADDR__W 16
+#define SIO_HI_RA_RAM_S1_ADDR__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_ADDR__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_ADDR_S1_SLV_ADDR__B 0
+#define SIO_HI_RA_RAM_S1_ADDR_S1_SLV_ADDR__W 16
+#define SIO_HI_RA_RAM_S1_ADDR_S1_SLV_ADDR__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_ADDR_S1_SLV_ADDR__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_CRC__A 0x420027
+#define SIO_HI_RA_RAM_S1_CRC__W 16
+#define SIO_HI_RA_RAM_S1_CRC__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_CRC__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_BUFFER__A 0x420028
+#define SIO_HI_RA_RAM_S1_BUFFER__W 16
+#define SIO_HI_RA_RAM_S1_BUFFER__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_BUFFER__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_RMWBUF__A 0x420029
+#define SIO_HI_RA_RAM_S1_RMWBUF__W 16
+#define SIO_HI_RA_RAM_S1_RMWBUF__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_RMWBUF__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_FLG_VB__A 0x42002A
+#define SIO_HI_RA_RAM_S1_FLG_VB__W 1
+#define SIO_HI_RA_RAM_S1_FLG_VB__M 0x1
+#define SIO_HI_RA_RAM_S1_FLG_VB__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_TEMP0__A 0x42002B
+#define SIO_HI_RA_RAM_S1_TEMP0__W 16
+#define SIO_HI_RA_RAM_S1_TEMP0__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_TEMP0__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_TEMP1__A 0x42002C
+#define SIO_HI_RA_RAM_S1_TEMP1__W 16
+#define SIO_HI_RA_RAM_S1_TEMP1__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_TEMP1__PRE 0x0
+
+#define SIO_HI_RA_RAM_S1_OFFSET__A 0x42002D
+#define SIO_HI_RA_RAM_S1_OFFSET__W 16
+#define SIO_HI_RA_RAM_S1_OFFSET__M 0xFFFF
+#define SIO_HI_RA_RAM_S1_OFFSET__PRE 0x0
+#define SIO_HI_RA_RAM_SEMA__A 0x420030
+#define SIO_HI_RA_RAM_SEMA__W 1
+#define SIO_HI_RA_RAM_SEMA__M 0x1
+#define SIO_HI_RA_RAM_SEMA__PRE 0x0
+#define SIO_HI_RA_RAM_SEMA_FREE 0x0
+#define SIO_HI_RA_RAM_SEMA_BUSY 0x1
+
+#define SIO_HI_RA_RAM_RES__A 0x420031
+#define SIO_HI_RA_RAM_RES__W 3
+#define SIO_HI_RA_RAM_RES__M 0x7
+#define SIO_HI_RA_RAM_RES__PRE 0x0
+#define SIO_HI_RA_RAM_RES_OK 0x0
+#define SIO_HI_RA_RAM_RES_ERROR 0x1
+#define SIO_HI_RA_RAM_RES_I2C_START_FOUND 0x1
+#define SIO_HI_RA_RAM_RES_I2C_STOP_FOUND 0x2
+#define SIO_HI_RA_RAM_RES_I2C_ARB_LOST 0x3
+#define SIO_HI_RA_RAM_RES_I2C_ERROR 0x4
+
+#define SIO_HI_RA_RAM_CMD__A 0x420032
+#define SIO_HI_RA_RAM_CMD__W 4
+#define SIO_HI_RA_RAM_CMD__M 0xF
+#define SIO_HI_RA_RAM_CMD__PRE 0x0
+#define SIO_HI_RA_RAM_CMD_NULL 0x0
+#define SIO_HI_RA_RAM_CMD_UIO 0x1
+#define SIO_HI_RA_RAM_CMD_RESET 0x2
+#define SIO_HI_RA_RAM_CMD_CONFIG 0x3
+#define SIO_HI_RA_RAM_CMD_INTERNAL_TRANSFER 0x4
+#define SIO_HI_RA_RAM_CMD_I2C_TRANSMIT 0x5
+#define SIO_HI_RA_RAM_CMD_EXEC 0x6
+#define SIO_HI_RA_RAM_CMD_BRDCTRL 0x7
+#define SIO_HI_RA_RAM_CMD_ATOMIC_COPY 0x8
+
+#define SIO_HI_RA_RAM_PAR_1__A 0x420033
+#define SIO_HI_RA_RAM_PAR_1__W 16
+#define SIO_HI_RA_RAM_PAR_1__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_1__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_1_PAR1__B 0
+#define SIO_HI_RA_RAM_PAR_1_PAR1__W 16
+#define SIO_HI_RA_RAM_PAR_1_PAR1__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_1_PAR1__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY 0x3945
+
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BNK__B 0
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BNK__W 6
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BNK__M 0x3F
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BNK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BLK__B 6
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BLK__W 6
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BLK__M 0xFC0
+#define SIO_HI_RA_RAM_PAR_1_ITX_SRC_BLK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_PORT__B 0
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_PORT__W 1
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_PORT__M 0x1
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_PORT__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE__B 1
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE__W 1
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE__M 0x2
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE_DISABLE 0x0
+#define SIO_HI_RA_RAM_PAR_1_I2CTX_TOE_ENABLE 0x2
+
+#define SIO_HI_RA_RAM_PAR_1_EXEC_FUNC__B 0
+#define SIO_HI_RA_RAM_PAR_1_EXEC_FUNC__W 10
+#define SIO_HI_RA_RAM_PAR_1_EXEC_FUNC__M 0x3FF
+#define SIO_HI_RA_RAM_PAR_1_EXEC_FUNC__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BNK__B 0
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BNK__W 6
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BNK__M 0x3F
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BNK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BLK__B 6
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BLK__W 6
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BLK__M 0xFC0
+#define SIO_HI_RA_RAM_PAR_1_ACP_INT_BLK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_2__A 0x420034
+#define SIO_HI_RA_RAM_PAR_2__W 16
+#define SIO_HI_RA_RAM_PAR_2__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_2__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_2_PAR2__B 0
+#define SIO_HI_RA_RAM_PAR_2_PAR2__W 16
+#define SIO_HI_RA_RAM_PAR_2_PAR2__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_2_PAR2__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__B 0
+#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__W 7
+#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__M 0x7F
+#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__PRE 0x25
+
+#define SIO_HI_RA_RAM_PAR_2_ITX_SRC_OFF__B 0
+#define SIO_HI_RA_RAM_PAR_2_ITX_SRC_OFF__W 16
+#define SIO_HI_RA_RAM_PAR_2_ITX_SRC_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_2_ITX_SRC_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_2_I2CTX_BUF__B 0
+#define SIO_HI_RA_RAM_PAR_2_I2CTX_BUF__W 16
+#define SIO_HI_RA_RAM_PAR_2_I2CTX_BUF__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_2_I2CTX_BUF__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_2_BRD_CFG__B 2
+#define SIO_HI_RA_RAM_PAR_2_BRD_CFG__W 1
+#define SIO_HI_RA_RAM_PAR_2_BRD_CFG__M 0x4
+#define SIO_HI_RA_RAM_PAR_2_BRD_CFG__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN 0x0
+#define SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED 0x4
+
+#define SIO_HI_RA_RAM_PAR_2_ACP_INT_OFF__B 0
+#define SIO_HI_RA_RAM_PAR_2_ACP_INT_OFF__W 16
+#define SIO_HI_RA_RAM_PAR_2_ACP_INT_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_2_ACP_INT_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_3__A 0x420035
+#define SIO_HI_RA_RAM_PAR_3__W 16
+#define SIO_HI_RA_RAM_PAR_3__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_3__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_3_PAR3__B 0
+#define SIO_HI_RA_RAM_PAR_3_PAR3__W 16
+#define SIO_HI_RA_RAM_PAR_3_PAR3__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_3_PAR3__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__B 0
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__W 7
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M 0x7F
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__PRE 0x3F
+
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B 7
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__W 7
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__M 0x3F80
+#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__PRE 0x1F80
+
+#define SIO_HI_RA_RAM_PAR_3_ITX_LEN__B 0
+#define SIO_HI_RA_RAM_PAR_3_ITX_LEN__W 16
+#define SIO_HI_RA_RAM_PAR_3_ITX_LEN__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_3_ITX_LEN__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_3_ACP_LEN__B 0
+#define SIO_HI_RA_RAM_PAR_3_ACP_LEN__W 3
+#define SIO_HI_RA_RAM_PAR_3_ACP_LEN__M 0x7
+#define SIO_HI_RA_RAM_PAR_3_ACP_LEN__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_3_ACP_RW__B 3
+#define SIO_HI_RA_RAM_PAR_3_ACP_RW__W 1
+#define SIO_HI_RA_RAM_PAR_3_ACP_RW__M 0x8
+#define SIO_HI_RA_RAM_PAR_3_ACP_RW__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_3_ACP_RW_READ 0x0
+#define SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE 0x8
+
+#define SIO_HI_RA_RAM_PAR_4__A 0x420036
+#define SIO_HI_RA_RAM_PAR_4__W 16
+#define SIO_HI_RA_RAM_PAR_4__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_4__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_4_PAR4__B 0
+#define SIO_HI_RA_RAM_PAR_4_PAR4__W 16
+#define SIO_HI_RA_RAM_PAR_4_PAR4__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_4_PAR4__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_4_CFG_WUP__B 0
+#define SIO_HI_RA_RAM_PAR_4_CFG_WUP__W 8
+#define SIO_HI_RA_RAM_PAR_4_CFG_WUP__M 0xFF
+#define SIO_HI_RA_RAM_PAR_4_CFG_WUP__PRE 0xC1
+
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BNK__B 0
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BNK__W 6
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BNK__M 0x3F
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BNK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BLK__B 6
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BLK__W 6
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BLK__M 0xFC0
+#define SIO_HI_RA_RAM_PAR_4_ITX_DST_BLK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BNK__B 0
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BNK__W 6
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BNK__M 0x3F
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BNK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BLK__B 6
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BLK__W 6
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BLK__M 0xFC0
+#define SIO_HI_RA_RAM_PAR_4_ACP_EXT_BLK__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_5__A 0x420037
+#define SIO_HI_RA_RAM_PAR_5__W 16
+#define SIO_HI_RA_RAM_PAR_5__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_5__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_5_PAR5__B 0
+#define SIO_HI_RA_RAM_PAR_5_PAR5__W 16
+#define SIO_HI_RA_RAM_PAR_5_PAR5__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_5_PAR5__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0__B 0
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0__W 1
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0__M 0x1
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0_NO_SLAVE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE 0x1
+
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1__B 1
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1__W 1
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1__M 0x2
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1_NO_SLAVE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLV1_SLAVE 0x2
+
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__B 3
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__W 1
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M 0x8
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_AWAKE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ 0x8
+
+#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST__B 5
+#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST__W 1
+#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST__M 0x20
+#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST__PRE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST_DISABLE 0x0
+#define SIO_HI_RA_RAM_PAR_5_CFG_BDGST_ENABLE 0x20
+
+#define SIO_HI_RA_RAM_PAR_5_ITX_DST_OFF__B 0
+#define SIO_HI_RA_RAM_PAR_5_ITX_DST_OFF__W 16
+#define SIO_HI_RA_RAM_PAR_5_ITX_DST_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_5_ITX_DST_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_5_ACP_EXT_OFF__B 0
+#define SIO_HI_RA_RAM_PAR_5_ACP_EXT_OFF__W 16
+#define SIO_HI_RA_RAM_PAR_5_ACP_EXT_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_5_ACP_EXT_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_6__A 0x420038
+#define SIO_HI_RA_RAM_PAR_6__W 16
+#define SIO_HI_RA_RAM_PAR_6__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_6__PRE 0x95FF
+#define SIO_HI_RA_RAM_PAR_6_PAR6__B 0
+#define SIO_HI_RA_RAM_PAR_6_PAR6__W 16
+#define SIO_HI_RA_RAM_PAR_6_PAR6__M 0xFFFF
+#define SIO_HI_RA_RAM_PAR_6_PAR6__PRE 0x0
+
+#define SIO_HI_RA_RAM_PAR_6_CFG_TOD__B 0
+#define SIO_HI_RA_RAM_PAR_6_CFG_TOD__W 8
+#define SIO_HI_RA_RAM_PAR_6_CFG_TOD__M 0xFF
+#define SIO_HI_RA_RAM_PAR_6_CFG_TOD__PRE 0xFF
+
+#define SIO_HI_RA_RAM_PAR_6_CFG_WDD__B 8
+#define SIO_HI_RA_RAM_PAR_6_CFG_WDD__W 8
+#define SIO_HI_RA_RAM_PAR_6_CFG_WDD__M 0xFF00
+#define SIO_HI_RA_RAM_PAR_6_CFG_WDD__PRE 0x9500
+
+#define SIO_HI_RA_RAM_AB_TEMP__A 0x42006E
+#define SIO_HI_RA_RAM_AB_TEMP__W 16
+#define SIO_HI_RA_RAM_AB_TEMP__M 0xFFFF
+#define SIO_HI_RA_RAM_AB_TEMP__PRE 0x0
+
+#define SIO_HI_RA_RAM_I2C_CTL__A 0x42006F
+#define SIO_HI_RA_RAM_I2C_CTL__W 16
+#define SIO_HI_RA_RAM_I2C_CTL__M 0xFFFF
+#define SIO_HI_RA_RAM_I2C_CTL__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY0__A 0x420070
+#define SIO_HI_RA_RAM_VB_ENTRY0__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY0__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY0__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BNK__B 0
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BNK__W 4
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BNK__M 0xF
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BNK__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BLK__B 4
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BLK__W 4
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BLK__M 0xF0
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_MAP_BLK__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BNK__B 8
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BNK__W 4
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BNK__M 0xF00
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BNK__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BLK__B 12
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BLK__W 4
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BLK__M 0xF000
+#define SIO_HI_RA_RAM_VB_ENTRY0_HI_VIRT_BLK__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET0__A 0x420071
+#define SIO_HI_RA_RAM_VB_OFFSET0__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET0__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET0__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET0_HI_MAP_OFF0__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET0_HI_MAP_OFF0__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET0_HI_MAP_OFF0__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET0_HI_MAP_OFF0__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY1__A 0x420072
+#define SIO_HI_RA_RAM_VB_ENTRY1__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY1__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY1__PRE 0x0
+#define SIO_HI_RA_RAM_VB_OFFSET1__A 0x420073
+#define SIO_HI_RA_RAM_VB_OFFSET1__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET1__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET1__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET1_HI_MAP_OFF__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET1_HI_MAP_OFF__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET1_HI_MAP_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET1_HI_MAP_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY2__A 0x420074
+#define SIO_HI_RA_RAM_VB_ENTRY2__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY2__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY2__PRE 0x0
+#define SIO_HI_RA_RAM_VB_OFFSET2__A 0x420075
+#define SIO_HI_RA_RAM_VB_OFFSET2__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET2__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET2__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET2_HI_MAP_OFF__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET2_HI_MAP_OFF__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET2_HI_MAP_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET2_HI_MAP_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY3__A 0x420076
+#define SIO_HI_RA_RAM_VB_ENTRY3__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY3__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY3__PRE 0x0
+#define SIO_HI_RA_RAM_VB_OFFSET3__A 0x420077
+#define SIO_HI_RA_RAM_VB_OFFSET3__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET3__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET3__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET3_HI_MAP_OFF__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET3_HI_MAP_OFF__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET3_HI_MAP_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET3_HI_MAP_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY4__A 0x420078
+#define SIO_HI_RA_RAM_VB_ENTRY4__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY4__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY4__PRE 0x0
+#define SIO_HI_RA_RAM_VB_OFFSET4__A 0x420079
+#define SIO_HI_RA_RAM_VB_OFFSET4__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET4__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET4__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET4_HI_MAP_OFF__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET4_HI_MAP_OFF__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET4_HI_MAP_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET4_HI_MAP_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY5__A 0x42007A
+#define SIO_HI_RA_RAM_VB_ENTRY5__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY5__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY5__PRE 0x0
+#define SIO_HI_RA_RAM_VB_OFFSET5__A 0x42007B
+#define SIO_HI_RA_RAM_VB_OFFSET5__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET5__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET5__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET5_HI_MAP_OFF__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET5_HI_MAP_OFF__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET5_HI_MAP_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET5_HI_MAP_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY6__A 0x42007C
+#define SIO_HI_RA_RAM_VB_ENTRY6__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY6__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY6__PRE 0x0
+#define SIO_HI_RA_RAM_VB_OFFSET6__A 0x42007D
+#define SIO_HI_RA_RAM_VB_OFFSET6__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET6__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET6__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET6_HI_MAP_OFF__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET6_HI_MAP_OFF__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET6_HI_MAP_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET6_HI_MAP_OFF__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_ENTRY7__A 0x42007E
+#define SIO_HI_RA_RAM_VB_ENTRY7__W 16
+#define SIO_HI_RA_RAM_VB_ENTRY7__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_ENTRY7__PRE 0x0
+#define SIO_HI_RA_RAM_VB_OFFSET7__A 0x42007F
+#define SIO_HI_RA_RAM_VB_OFFSET7__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET7__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET7__PRE 0x0
+
+#define SIO_HI_RA_RAM_VB_OFFSET7_HI_MAP_OFF__B 0
+#define SIO_HI_RA_RAM_VB_OFFSET7_HI_MAP_OFF__W 16
+#define SIO_HI_RA_RAM_VB_OFFSET7_HI_MAP_OFF__M 0xFFFF
+#define SIO_HI_RA_RAM_VB_OFFSET7_HI_MAP_OFF__PRE 0x0
+
+#define SIO_HI_IF_RAM_TRP_BPT_0__A 0x430000
+#define SIO_HI_IF_RAM_TRP_BPT_0__W 12
+#define SIO_HI_IF_RAM_TRP_BPT_0__M 0xFFF
+#define SIO_HI_IF_RAM_TRP_BPT_0__PRE 0x0
+#define SIO_HI_IF_RAM_TRP_BPT_1__A 0x430001
+#define SIO_HI_IF_RAM_TRP_BPT_1__W 12
+#define SIO_HI_IF_RAM_TRP_BPT_1__M 0xFFF
+#define SIO_HI_IF_RAM_TRP_BPT_1__PRE 0x0
+#define SIO_HI_IF_RAM_TRP_STK_0__A 0x430002
+#define SIO_HI_IF_RAM_TRP_STK_0__W 12
+#define SIO_HI_IF_RAM_TRP_STK_0__M 0xFFF
+#define SIO_HI_IF_RAM_TRP_STK_0__PRE 0x0
+#define SIO_HI_IF_RAM_TRP_STK_1__A 0x430003
+#define SIO_HI_IF_RAM_TRP_STK_1__W 12
+#define SIO_HI_IF_RAM_TRP_STK_1__M 0xFFF
+#define SIO_HI_IF_RAM_TRP_STK_1__PRE 0x0
+#define SIO_HI_IF_RAM_FUN_BASE__A 0x430300
+#define SIO_HI_IF_RAM_FUN_BASE__W 12
+#define SIO_HI_IF_RAM_FUN_BASE__M 0xFFF
+#define SIO_HI_IF_RAM_FUN_BASE__PRE 0x0
+
+#define SIO_HI_IF_COMM_EXEC__A 0x440000
+#define SIO_HI_IF_COMM_EXEC__W 2
+#define SIO_HI_IF_COMM_EXEC__M 0x3
+#define SIO_HI_IF_COMM_EXEC__PRE 0x0
+#define SIO_HI_IF_COMM_EXEC_STOP 0x0
+#define SIO_HI_IF_COMM_EXEC_ACTIVE 0x1
+#define SIO_HI_IF_COMM_EXEC_HOLD 0x2
+#define SIO_HI_IF_COMM_EXEC_STEP 0x3
+
+#define SIO_HI_IF_COMM_STATE__A 0x440001
+#define SIO_HI_IF_COMM_STATE__W 10
+#define SIO_HI_IF_COMM_STATE__M 0x3FF
+#define SIO_HI_IF_COMM_STATE__PRE 0x0
+#define SIO_HI_IF_COMM_INT_REQ__A 0x440003
+#define SIO_HI_IF_COMM_INT_REQ__W 1
+#define SIO_HI_IF_COMM_INT_REQ__M 0x1
+#define SIO_HI_IF_COMM_INT_REQ__PRE 0x0
+#define SIO_HI_IF_COMM_INT_STA__A 0x440005
+#define SIO_HI_IF_COMM_INT_STA__W 1
+#define SIO_HI_IF_COMM_INT_STA__M 0x1
+#define SIO_HI_IF_COMM_INT_STA__PRE 0x0
+#define SIO_HI_IF_COMM_INT_STA_STAT__B 0
+#define SIO_HI_IF_COMM_INT_STA_STAT__W 1
+#define SIO_HI_IF_COMM_INT_STA_STAT__M 0x1
+#define SIO_HI_IF_COMM_INT_STA_STAT__PRE 0x0
+
+#define SIO_HI_IF_COMM_INT_MSK__A 0x440006
+#define SIO_HI_IF_COMM_INT_MSK__W 1
+#define SIO_HI_IF_COMM_INT_MSK__M 0x1
+#define SIO_HI_IF_COMM_INT_MSK__PRE 0x0
+#define SIO_HI_IF_COMM_INT_MSK_STAT__B 0
+#define SIO_HI_IF_COMM_INT_MSK_STAT__W 1
+#define SIO_HI_IF_COMM_INT_MSK_STAT__M 0x1
+#define SIO_HI_IF_COMM_INT_MSK_STAT__PRE 0x0
+
+#define SIO_HI_IF_COMM_INT_STM__A 0x440007
+#define SIO_HI_IF_COMM_INT_STM__W 1
+#define SIO_HI_IF_COMM_INT_STM__M 0x1
+#define SIO_HI_IF_COMM_INT_STM__PRE 0x0
+#define SIO_HI_IF_COMM_INT_STM_STAT__B 0
+#define SIO_HI_IF_COMM_INT_STM_STAT__W 1
+#define SIO_HI_IF_COMM_INT_STM_STAT__M 0x1
+#define SIO_HI_IF_COMM_INT_STM_STAT__PRE 0x0
+
+#define SIO_HI_IF_STK_0__A 0x440010
+#define SIO_HI_IF_STK_0__W 10
+#define SIO_HI_IF_STK_0__M 0x3FF
+#define SIO_HI_IF_STK_0__PRE 0x2
+
+#define SIO_HI_IF_STK_0_ADDR__B 0
+#define SIO_HI_IF_STK_0_ADDR__W 10
+#define SIO_HI_IF_STK_0_ADDR__M 0x3FF
+#define SIO_HI_IF_STK_0_ADDR__PRE 0x2
+
+#define SIO_HI_IF_STK_1__A 0x440011
+#define SIO_HI_IF_STK_1__W 10
+#define SIO_HI_IF_STK_1__M 0x3FF
+#define SIO_HI_IF_STK_1__PRE 0x2
+#define SIO_HI_IF_STK_1_ADDR__B 0
+#define SIO_HI_IF_STK_1_ADDR__W 10
+#define SIO_HI_IF_STK_1_ADDR__M 0x3FF
+#define SIO_HI_IF_STK_1_ADDR__PRE 0x2
+
+#define SIO_HI_IF_STK_2__A 0x440012
+#define SIO_HI_IF_STK_2__W 10
+#define SIO_HI_IF_STK_2__M 0x3FF
+#define SIO_HI_IF_STK_2__PRE 0x2
+#define SIO_HI_IF_STK_2_ADDR__B 0
+#define SIO_HI_IF_STK_2_ADDR__W 10
+#define SIO_HI_IF_STK_2_ADDR__M 0x3FF
+#define SIO_HI_IF_STK_2_ADDR__PRE 0x2
+
+#define SIO_HI_IF_STK_3__A 0x440013
+#define SIO_HI_IF_STK_3__W 10
+#define SIO_HI_IF_STK_3__M 0x3FF
+#define SIO_HI_IF_STK_3__PRE 0x2
+
+#define SIO_HI_IF_STK_3_ADDR__B 0
+#define SIO_HI_IF_STK_3_ADDR__W 10
+#define SIO_HI_IF_STK_3_ADDR__M 0x3FF
+#define SIO_HI_IF_STK_3_ADDR__PRE 0x2
+
+#define SIO_HI_IF_BPT_IDX__A 0x44001F
+#define SIO_HI_IF_BPT_IDX__W 1
+#define SIO_HI_IF_BPT_IDX__M 0x1
+#define SIO_HI_IF_BPT_IDX__PRE 0x0
+
+#define SIO_HI_IF_BPT_IDX_ADDR__B 0
+#define SIO_HI_IF_BPT_IDX_ADDR__W 1
+#define SIO_HI_IF_BPT_IDX_ADDR__M 0x1
+#define SIO_HI_IF_BPT_IDX_ADDR__PRE 0x0
+
+#define SIO_HI_IF_BPT__A 0x440020
+#define SIO_HI_IF_BPT__W 10
+#define SIO_HI_IF_BPT__M 0x3FF
+#define SIO_HI_IF_BPT__PRE 0x2
+
+#define SIO_HI_IF_BPT_ADDR__B 0
+#define SIO_HI_IF_BPT_ADDR__W 10
+#define SIO_HI_IF_BPT_ADDR__M 0x3FF
+#define SIO_HI_IF_BPT_ADDR__PRE 0x2
+
+#define SIO_CC_COMM_EXEC__A 0x450000
+#define SIO_CC_COMM_EXEC__W 2
+#define SIO_CC_COMM_EXEC__M 0x3
+#define SIO_CC_COMM_EXEC__PRE 0x0
+#define SIO_CC_COMM_EXEC_STOP 0x0
+#define SIO_CC_COMM_EXEC_ACTIVE 0x1
+#define SIO_CC_COMM_EXEC_HOLD 0x2
+
+#define SIO_CC_PLL_MODE__A 0x450010
+#define SIO_CC_PLL_MODE__W 6
+#define SIO_CC_PLL_MODE__M 0x3F
+#define SIO_CC_PLL_MODE__PRE 0x0
+
+#define SIO_CC_PLL_MODE_FREF_SEL__B 0
+#define SIO_CC_PLL_MODE_FREF_SEL__W 2
+#define SIO_CC_PLL_MODE_FREF_SEL__M 0x3
+#define SIO_CC_PLL_MODE_FREF_SEL__PRE 0x0
+#define SIO_CC_PLL_MODE_FREF_SEL_OHW 0x0
+#define SIO_CC_PLL_MODE_FREF_SEL_27_00 0x1
+#define SIO_CC_PLL_MODE_FREF_SEL_20_25 0x2
+#define SIO_CC_PLL_MODE_FREF_SEL_4_00 0x3
+
+#define SIO_CC_PLL_MODE_LOCKSEL__B 2
+#define SIO_CC_PLL_MODE_LOCKSEL__W 2
+#define SIO_CC_PLL_MODE_LOCKSEL__M 0xC
+#define SIO_CC_PLL_MODE_LOCKSEL__PRE 0x0
+
+#define SIO_CC_PLL_MODE_BYPASS__B 4
+#define SIO_CC_PLL_MODE_BYPASS__W 2
+#define SIO_CC_PLL_MODE_BYPASS__M 0x30
+#define SIO_CC_PLL_MODE_BYPASS__PRE 0x0
+#define SIO_CC_PLL_MODE_BYPASS_OHW 0x0
+#define SIO_CC_PLL_MODE_BYPASS_OFF 0x10
+#define SIO_CC_PLL_MODE_BYPASS_ON 0x20
+
+#define SIO_CC_PLL_TEST__A 0x450011
+#define SIO_CC_PLL_TEST__W 8
+#define SIO_CC_PLL_TEST__M 0xFF
+#define SIO_CC_PLL_TEST__PRE 0x0
+
+#define SIO_CC_PLL_LOCK__A 0x450012
+#define SIO_CC_PLL_LOCK__W 1
+#define SIO_CC_PLL_LOCK__M 0x1
+#define SIO_CC_PLL_LOCK__PRE 0x0
+#define SIO_CC_CLK_MODE__A 0x450014
+#define SIO_CC_CLK_MODE__W 5
+#define SIO_CC_CLK_MODE__M 0x1F
+#define SIO_CC_CLK_MODE__PRE 0x0
+
+#define SIO_CC_CLK_MODE_DELAY__B 0
+#define SIO_CC_CLK_MODE_DELAY__W 4
+#define SIO_CC_CLK_MODE_DELAY__M 0xF
+#define SIO_CC_CLK_MODE_DELAY__PRE 0x0
+
+#define SIO_CC_CLK_MODE_INVERT__B 4
+#define SIO_CC_CLK_MODE_INVERT__W 1
+#define SIO_CC_CLK_MODE_INVERT__M 0x10
+#define SIO_CC_CLK_MODE_INVERT__PRE 0x0
+
+#define SIO_CC_PWD_MODE__A 0x450015
+#define SIO_CC_PWD_MODE__W 3
+#define SIO_CC_PWD_MODE__M 0x7
+#define SIO_CC_PWD_MODE__PRE 0x0
+
+#define SIO_CC_PWD_MODE_LEVEL__B 0
+#define SIO_CC_PWD_MODE_LEVEL__W 2
+#define SIO_CC_PWD_MODE_LEVEL__M 0x3
+#define SIO_CC_PWD_MODE_LEVEL__PRE 0x0
+#define SIO_CC_PWD_MODE_LEVEL_NONE 0x0
+#define SIO_CC_PWD_MODE_LEVEL_CLOCK 0x1
+#define SIO_CC_PWD_MODE_LEVEL_PLL 0x2
+#define SIO_CC_PWD_MODE_LEVEL_OSC 0x3
+
+#define SIO_CC_PWD_MODE_USE_LOCK__B 2
+#define SIO_CC_PWD_MODE_USE_LOCK__W 1
+#define SIO_CC_PWD_MODE_USE_LOCK__M 0x4
+#define SIO_CC_PWD_MODE_USE_LOCK__PRE 0x0
+
+#define SIO_CC_SOFT_RST__A 0x450016
+#define SIO_CC_SOFT_RST__W 2
+#define SIO_CC_SOFT_RST__M 0x3
+#define SIO_CC_SOFT_RST__PRE 0x0
+
+#define SIO_CC_SOFT_RST_SYS__B 0
+#define SIO_CC_SOFT_RST_SYS__W 1
+#define SIO_CC_SOFT_RST_SYS__M 0x1
+#define SIO_CC_SOFT_RST_SYS__PRE 0x0
+
+#define SIO_CC_SOFT_RST_OSC__B 1
+#define SIO_CC_SOFT_RST_OSC__W 1
+#define SIO_CC_SOFT_RST_OSC__M 0x2
+#define SIO_CC_SOFT_RST_OSC__PRE 0x0
+
+#define SIO_CC_UPDATE__A 0x450017
+#define SIO_CC_UPDATE__W 16
+#define SIO_CC_UPDATE__M 0xFFFF
+#define SIO_CC_UPDATE__PRE 0x0
+#define SIO_CC_UPDATE_KEY 0xFABA
+
+#define SIO_SA_COMM_EXEC__A 0x460000
+#define SIO_SA_COMM_EXEC__W 2
+#define SIO_SA_COMM_EXEC__M 0x3
+#define SIO_SA_COMM_EXEC__PRE 0x0
+#define SIO_SA_COMM_EXEC_STOP 0x0
+#define SIO_SA_COMM_EXEC_ACTIVE 0x1
+#define SIO_SA_COMM_EXEC_HOLD 0x2
+
+#define SIO_SA_COMM_INT_REQ__A 0x460003
+#define SIO_SA_COMM_INT_REQ__W 1
+#define SIO_SA_COMM_INT_REQ__M 0x1
+#define SIO_SA_COMM_INT_REQ__PRE 0x0
+#define SIO_SA_COMM_INT_STA__A 0x460005
+#define SIO_SA_COMM_INT_STA__W 4
+#define SIO_SA_COMM_INT_STA__M 0xF
+#define SIO_SA_COMM_INT_STA__PRE 0x0
+
+#define SIO_SA_COMM_INT_STA_TR_END_INT_STA__B 0
+#define SIO_SA_COMM_INT_STA_TR_END_INT_STA__W 1
+#define SIO_SA_COMM_INT_STA_TR_END_INT_STA__M 0x1
+#define SIO_SA_COMM_INT_STA_TR_END_INT_STA__PRE 0x0
+
+#define SIO_SA_COMM_INT_STA_TR_BUFF_EMPTY_INT__B 1
+#define SIO_SA_COMM_INT_STA_TR_BUFF_EMPTY_INT__W 1
+#define SIO_SA_COMM_INT_STA_TR_BUFF_EMPTY_INT__M 0x2
+#define SIO_SA_COMM_INT_STA_TR_BUFF_EMPTY_INT__PRE 0x0
+
+#define SIO_SA_COMM_INT_STA_RX_END_INT_STA__B 2
+#define SIO_SA_COMM_INT_STA_RX_END_INT_STA__W 1
+#define SIO_SA_COMM_INT_STA_RX_END_INT_STA__M 0x4
+#define SIO_SA_COMM_INT_STA_RX_END_INT_STA__PRE 0x0
+
+#define SIO_SA_COMM_INT_STA_RX_BUFF_FULL_INT__B 3
+#define SIO_SA_COMM_INT_STA_RX_BUFF_FULL_INT__W 1
+#define SIO_SA_COMM_INT_STA_RX_BUFF_FULL_INT__M 0x8
+#define SIO_SA_COMM_INT_STA_RX_BUFF_FULL_INT__PRE 0x0
+
+#define SIO_SA_COMM_INT_MSK__A 0x460006
+#define SIO_SA_COMM_INT_MSK__W 4
+#define SIO_SA_COMM_INT_MSK__M 0xF
+#define SIO_SA_COMM_INT_MSK__PRE 0x0
+
+#define SIO_SA_COMM_INT_MSK_TR_END_INT_MASK__B 0
+#define SIO_SA_COMM_INT_MSK_TR_END_INT_MASK__W 1
+#define SIO_SA_COMM_INT_MSK_TR_END_INT_MASK__M 0x1
+#define SIO_SA_COMM_INT_MSK_TR_END_INT_MASK__PRE 0x0
+
+#define SIO_SA_COMM_INT_MSK_TR_BUFF_EMPTY_MASK__B 1
+#define SIO_SA_COMM_INT_MSK_TR_BUFF_EMPTY_MASK__W 1
+#define SIO_SA_COMM_INT_MSK_TR_BUFF_EMPTY_MASK__M 0x2
+#define SIO_SA_COMM_INT_MSK_TR_BUFF_EMPTY_MASK__PRE 0x0
+
+#define SIO_SA_COMM_INT_MSK_RX_END_INT_MASK__B 2
+#define SIO_SA_COMM_INT_MSK_RX_END_INT_MASK__W 1
+#define SIO_SA_COMM_INT_MSK_RX_END_INT_MASK__M 0x4
+#define SIO_SA_COMM_INT_MSK_RX_END_INT_MASK__PRE 0x0
+
+#define SIO_SA_COMM_INT_MSK_RX_BUFF_FULL_MASK__B 3
+#define SIO_SA_COMM_INT_MSK_RX_BUFF_FULL_MASK__W 1
+#define SIO_SA_COMM_INT_MSK_RX_BUFF_FULL_MASK__M 0x8
+#define SIO_SA_COMM_INT_MSK_RX_BUFF_FULL_MASK__PRE 0x0
+
+#define SIO_SA_COMM_INT_STM__A 0x460007
+#define SIO_SA_COMM_INT_STM__W 4
+#define SIO_SA_COMM_INT_STM__M 0xF
+#define SIO_SA_COMM_INT_STM__PRE 0x0
+
+#define SIO_SA_COMM_INT_STM_TR_END_INT_MASK__B 0
+#define SIO_SA_COMM_INT_STM_TR_END_INT_MASK__W 1
+#define SIO_SA_COMM_INT_STM_TR_END_INT_MASK__M 0x1
+#define SIO_SA_COMM_INT_STM_TR_END_INT_MASK__PRE 0x0
+
+#define SIO_SA_COMM_INT_STM_TR_BUFF_EMPTY_MASK__B 1
+#define SIO_SA_COMM_INT_STM_TR_BUFF_EMPTY_MASK__W 1
+#define SIO_SA_COMM_INT_STM_TR_BUFF_EMPTY_MASK__M 0x2
+#define SIO_SA_COMM_INT_STM_TR_BUFF_EMPTY_MASK__PRE 0x0
+
+#define SIO_SA_COMM_INT_STM_RX_END_INT_MASK__B 2
+#define SIO_SA_COMM_INT_STM_RX_END_INT_MASK__W 1
+#define SIO_SA_COMM_INT_STM_RX_END_INT_MASK__M 0x4
+#define SIO_SA_COMM_INT_STM_RX_END_INT_MASK__PRE 0x0
+
+#define SIO_SA_COMM_INT_STM_RX_BUFF_FULL_MASK__B 3
+#define SIO_SA_COMM_INT_STM_RX_BUFF_FULL_MASK__W 1
+#define SIO_SA_COMM_INT_STM_RX_BUFF_FULL_MASK__M 0x8
+#define SIO_SA_COMM_INT_STM_RX_BUFF_FULL_MASK__PRE 0x0
+
+#define SIO_SA_PRESCALER__A 0x460010
+#define SIO_SA_PRESCALER__W 13
+#define SIO_SA_PRESCALER__M 0x1FFF
+#define SIO_SA_PRESCALER__PRE 0x18B7
+#define SIO_SA_TX_DATA0__A 0x460011
+#define SIO_SA_TX_DATA0__W 16
+#define SIO_SA_TX_DATA0__M 0xFFFF
+#define SIO_SA_TX_DATA0__PRE 0x0
+#define SIO_SA_TX_DATA1__A 0x460012
+#define SIO_SA_TX_DATA1__W 16
+#define SIO_SA_TX_DATA1__M 0xFFFF
+#define SIO_SA_TX_DATA1__PRE 0x0
+#define SIO_SA_TX_DATA2__A 0x460013
+#define SIO_SA_TX_DATA2__W 16
+#define SIO_SA_TX_DATA2__M 0xFFFF
+#define SIO_SA_TX_DATA2__PRE 0x0
+#define SIO_SA_TX_DATA3__A 0x460014
+#define SIO_SA_TX_DATA3__W 16
+#define SIO_SA_TX_DATA3__M 0xFFFF
+#define SIO_SA_TX_DATA3__PRE 0x0
+#define SIO_SA_TX_LENGTH__A 0x460015
+#define SIO_SA_TX_LENGTH__W 6
+#define SIO_SA_TX_LENGTH__M 0x3F
+#define SIO_SA_TX_LENGTH__PRE 0x0
+#define SIO_SA_TX_COMMAND__A 0x460016
+#define SIO_SA_TX_COMMAND__W 2
+#define SIO_SA_TX_COMMAND__M 0x3
+#define SIO_SA_TX_COMMAND__PRE 0x3
+
+#define SIO_SA_TX_COMMAND_TX_INVERT__B 0
+#define SIO_SA_TX_COMMAND_TX_INVERT__W 1
+#define SIO_SA_TX_COMMAND_TX_INVERT__M 0x1
+#define SIO_SA_TX_COMMAND_TX_INVERT__PRE 0x1
+
+#define SIO_SA_TX_COMMAND_TX_ENABLE__B 1
+#define SIO_SA_TX_COMMAND_TX_ENABLE__W 1
+#define SIO_SA_TX_COMMAND_TX_ENABLE__M 0x2
+#define SIO_SA_TX_COMMAND_TX_ENABLE__PRE 0x2
+
+#define SIO_SA_TX_STATUS__A 0x460017
+#define SIO_SA_TX_STATUS__W 2
+#define SIO_SA_TX_STATUS__M 0x3
+#define SIO_SA_TX_STATUS__PRE 0x0
+
+#define SIO_SA_TX_STATUS_BUSY__B 0
+#define SIO_SA_TX_STATUS_BUSY__W 1
+#define SIO_SA_TX_STATUS_BUSY__M 0x1
+#define SIO_SA_TX_STATUS_BUSY__PRE 0x0
+
+#define SIO_SA_TX_STATUS_BUFF_FULL__B 1
+#define SIO_SA_TX_STATUS_BUFF_FULL__W 1
+#define SIO_SA_TX_STATUS_BUFF_FULL__M 0x2
+#define SIO_SA_TX_STATUS_BUFF_FULL__PRE 0x0
+
+#define SIO_SA_RX_DATA0__A 0x460018
+#define SIO_SA_RX_DATA0__W 16
+#define SIO_SA_RX_DATA0__M 0xFFFF
+#define SIO_SA_RX_DATA0__PRE 0x0
+#define SIO_SA_RX_DATA1__A 0x460019
+#define SIO_SA_RX_DATA1__W 16
+#define SIO_SA_RX_DATA1__M 0xFFFF
+#define SIO_SA_RX_DATA1__PRE 0x0
+#define SIO_SA_RX_LENGTH__A 0x46001A
+#define SIO_SA_RX_LENGTH__W 6
+#define SIO_SA_RX_LENGTH__M 0x3F
+#define SIO_SA_RX_LENGTH__PRE 0x0
+#define SIO_SA_RX_COMMAND__A 0x46001B
+#define SIO_SA_RX_COMMAND__W 1
+#define SIO_SA_RX_COMMAND__M 0x1
+#define SIO_SA_RX_COMMAND__PRE 0x1
+
+#define SIO_SA_RX_COMMAND_RX_INVERT__B 0
+#define SIO_SA_RX_COMMAND_RX_INVERT__W 1
+#define SIO_SA_RX_COMMAND_RX_INVERT__M 0x1
+#define SIO_SA_RX_COMMAND_RX_INVERT__PRE 0x1
+
+#define SIO_SA_RX_STATUS__A 0x46001C
+#define SIO_SA_RX_STATUS__W 2
+#define SIO_SA_RX_STATUS__M 0x3
+#define SIO_SA_RX_STATUS__PRE 0x0
+
+#define SIO_SA_RX_STATUS_BUSY__B 0
+#define SIO_SA_RX_STATUS_BUSY__W 1
+#define SIO_SA_RX_STATUS_BUSY__M 0x1
+#define SIO_SA_RX_STATUS_BUSY__PRE 0x0
+
+#define SIO_SA_RX_STATUS_BUFF_FULL__B 1
+#define SIO_SA_RX_STATUS_BUFF_FULL__W 1
+#define SIO_SA_RX_STATUS_BUFF_FULL__M 0x2
+#define SIO_SA_RX_STATUS_BUFF_FULL__PRE 0x0
+
+#define SIO_PDR_COMM_EXEC__A 0x7F0000
+#define SIO_PDR_COMM_EXEC__W 2
+#define SIO_PDR_COMM_EXEC__M 0x3
+#define SIO_PDR_COMM_EXEC__PRE 0x0
+#define SIO_PDR_COMM_EXEC_STOP 0x0
+#define SIO_PDR_COMM_EXEC_ACTIVE 0x1
+#define SIO_PDR_COMM_EXEC_HOLD 0x2
+
+#define SIO_PDR_MON_CFG__A 0x7F0010
+#define SIO_PDR_MON_CFG__W 2
+#define SIO_PDR_MON_CFG__M 0x3
+#define SIO_PDR_MON_CFG__PRE 0x0
+
+#define SIO_PDR_MON_CFG_OSEL__B 0
+#define SIO_PDR_MON_CFG_OSEL__W 1
+#define SIO_PDR_MON_CFG_OSEL__M 0x1
+#define SIO_PDR_MON_CFG_OSEL__PRE 0x0
+
+#define SIO_PDR_MON_CFG_IACT__B 1
+#define SIO_PDR_MON_CFG_IACT__W 1
+#define SIO_PDR_MON_CFG_IACT__M 0x2
+#define SIO_PDR_MON_CFG_IACT__PRE 0x0
+
+#define SIO_PDR_FDB_CFG__A 0x7F0011
+#define SIO_PDR_FDB_CFG__W 2
+#define SIO_PDR_FDB_CFG__M 0x3
+#define SIO_PDR_FDB_CFG__PRE 0x0
+#define SIO_PDR_FDB_CFG_SEL__B 0
+#define SIO_PDR_FDB_CFG_SEL__W 2
+#define SIO_PDR_FDB_CFG_SEL__M 0x3
+#define SIO_PDR_FDB_CFG_SEL__PRE 0x0
+
+#define SIO_PDR_SMA_RX_SEL__A 0x7F0012
+#define SIO_PDR_SMA_RX_SEL__W 4
+#define SIO_PDR_SMA_RX_SEL__M 0xF
+#define SIO_PDR_SMA_RX_SEL__PRE 0x0
+#define SIO_PDR_SMA_RX_SEL_SEL__B 0
+#define SIO_PDR_SMA_RX_SEL_SEL__W 4
+#define SIO_PDR_SMA_RX_SEL_SEL__M 0xF
+#define SIO_PDR_SMA_RX_SEL_SEL__PRE 0x0
+
+#define SIO_PDR_SMA_TX_SILENT__A 0x7F0013
+#define SIO_PDR_SMA_TX_SILENT__W 1
+#define SIO_PDR_SMA_TX_SILENT__M 0x1
+#define SIO_PDR_SMA_TX_SILENT__PRE 0x0
+#define SIO_PDR_UIO_IN_LO__A 0x7F0014
+#define SIO_PDR_UIO_IN_LO__W 16
+#define SIO_PDR_UIO_IN_LO__M 0xFFFF
+#define SIO_PDR_UIO_IN_LO__PRE 0x0
+#define SIO_PDR_UIO_IN_LO_DATA__B 0
+#define SIO_PDR_UIO_IN_LO_DATA__W 16
+#define SIO_PDR_UIO_IN_LO_DATA__M 0xFFFF
+#define SIO_PDR_UIO_IN_LO_DATA__PRE 0x0
+
+#define SIO_PDR_UIO_IN_HI__A 0x7F0015
+#define SIO_PDR_UIO_IN_HI__W 14
+#define SIO_PDR_UIO_IN_HI__M 0x3FFF
+#define SIO_PDR_UIO_IN_HI__PRE 0x0
+#define SIO_PDR_UIO_IN_HI_DATA__B 0
+#define SIO_PDR_UIO_IN_HI_DATA__W 14
+#define SIO_PDR_UIO_IN_HI_DATA__M 0x3FFF
+#define SIO_PDR_UIO_IN_HI_DATA__PRE 0x0
+
+#define SIO_PDR_UIO_OUT_LO__A 0x7F0016
+#define SIO_PDR_UIO_OUT_LO__W 16
+#define SIO_PDR_UIO_OUT_LO__M 0xFFFF
+#define SIO_PDR_UIO_OUT_LO__PRE 0x0
+#define SIO_PDR_UIO_OUT_LO_DATA__B 0
+#define SIO_PDR_UIO_OUT_LO_DATA__W 16
+#define SIO_PDR_UIO_OUT_LO_DATA__M 0xFFFF
+#define SIO_PDR_UIO_OUT_LO_DATA__PRE 0x0
+
+#define SIO_PDR_UIO_OUT_HI__A 0x7F0017
+#define SIO_PDR_UIO_OUT_HI__W 14
+#define SIO_PDR_UIO_OUT_HI__M 0x3FFF
+#define SIO_PDR_UIO_OUT_HI__PRE 0x0
+#define SIO_PDR_UIO_OUT_HI_DATA__B 0
+#define SIO_PDR_UIO_OUT_HI_DATA__W 14
+#define SIO_PDR_UIO_OUT_HI_DATA__M 0x3FFF
+#define SIO_PDR_UIO_OUT_HI_DATA__PRE 0x0
+
+#define SIO_PDR_PWM1_MODE__A 0x7F0018
+#define SIO_PDR_PWM1_MODE__W 2
+#define SIO_PDR_PWM1_MODE__M 0x3
+#define SIO_PDR_PWM1_MODE__PRE 0x0
+#define SIO_PDR_PWM1_PRESCALE__A 0x7F0019
+#define SIO_PDR_PWM1_PRESCALE__W 6
+#define SIO_PDR_PWM1_PRESCALE__M 0x3F
+#define SIO_PDR_PWM1_PRESCALE__PRE 0x0
+#define SIO_PDR_PWM1_VALUE__A 0x7F001A
+#define SIO_PDR_PWM1_VALUE__W 11
+#define SIO_PDR_PWM1_VALUE__M 0x7FF
+#define SIO_PDR_PWM1_VALUE__PRE 0x0
+#define SIO_PDR_PWM2_MODE__A 0x7F001C
+#define SIO_PDR_PWM2_MODE__W 2
+#define SIO_PDR_PWM2_MODE__M 0x3
+#define SIO_PDR_PWM2_MODE__PRE 0x0
+#define SIO_PDR_PWM2_PRESCALE__A 0x7F001D
+#define SIO_PDR_PWM2_PRESCALE__W 6
+#define SIO_PDR_PWM2_PRESCALE__M 0x3F
+#define SIO_PDR_PWM2_PRESCALE__PRE 0x0
+#define SIO_PDR_PWM2_VALUE__A 0x7F001E
+#define SIO_PDR_PWM2_VALUE__W 11
+#define SIO_PDR_PWM2_VALUE__M 0x7FF
+#define SIO_PDR_PWM2_VALUE__PRE 0x0
+#define SIO_PDR_OHW_CFG__A 0x7F001F
+#define SIO_PDR_OHW_CFG__W 7
+#define SIO_PDR_OHW_CFG__M 0x7F
+#define SIO_PDR_OHW_CFG__PRE 0x0
+
+#define SIO_PDR_OHW_CFG_FREF_SEL__B 0
+#define SIO_PDR_OHW_CFG_FREF_SEL__W 2
+#define SIO_PDR_OHW_CFG_FREF_SEL__M 0x3
+#define SIO_PDR_OHW_CFG_FREF_SEL__PRE 0x0
+
+#define SIO_PDR_OHW_CFG_BYPASS__B 2
+#define SIO_PDR_OHW_CFG_BYPASS__W 1
+#define SIO_PDR_OHW_CFG_BYPASS__M 0x4
+#define SIO_PDR_OHW_CFG_BYPASS__PRE 0x0
+
+#define SIO_PDR_OHW_CFG_ASEL__B 3
+#define SIO_PDR_OHW_CFG_ASEL__W 3
+#define SIO_PDR_OHW_CFG_ASEL__M 0x38
+#define SIO_PDR_OHW_CFG_ASEL__PRE 0x0
+
+#define SIO_PDR_OHW_CFG_SPEED__B 6
+#define SIO_PDR_OHW_CFG_SPEED__W 1
+#define SIO_PDR_OHW_CFG_SPEED__M 0x40
+#define SIO_PDR_OHW_CFG_SPEED__PRE 0x0
+
+#define SIO_PDR_I2S_WS_CFG__A 0x7F0020
+#define SIO_PDR_I2S_WS_CFG__W 9
+#define SIO_PDR_I2S_WS_CFG__M 0x1FF
+#define SIO_PDR_I2S_WS_CFG__PRE 0x10
+#define SIO_PDR_I2S_WS_CFG_MODE__B 0
+#define SIO_PDR_I2S_WS_CFG_MODE__W 3
+#define SIO_PDR_I2S_WS_CFG_MODE__M 0x7
+#define SIO_PDR_I2S_WS_CFG_MODE__PRE 0x0
+#define SIO_PDR_I2S_WS_CFG_DRIVE__B 3
+#define SIO_PDR_I2S_WS_CFG_DRIVE__W 3
+#define SIO_PDR_I2S_WS_CFG_DRIVE__M 0x38
+#define SIO_PDR_I2S_WS_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_I2S_WS_CFG_KEEP__B 6
+#define SIO_PDR_I2S_WS_CFG_KEEP__W 2
+#define SIO_PDR_I2S_WS_CFG_KEEP__M 0xC0
+#define SIO_PDR_I2S_WS_CFG_KEEP__PRE 0x0
+#define SIO_PDR_I2S_WS_CFG_UIO__B 8
+#define SIO_PDR_I2S_WS_CFG_UIO__W 1
+#define SIO_PDR_I2S_WS_CFG_UIO__M 0x100
+#define SIO_PDR_I2S_WS_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_GPIO_CFG__A 0x7F0021
+#define SIO_PDR_GPIO_CFG__W 9
+#define SIO_PDR_GPIO_CFG__M 0x1FF
+#define SIO_PDR_GPIO_CFG__PRE 0x10
+#define SIO_PDR_GPIO_CFG_MODE__B 0
+#define SIO_PDR_GPIO_CFG_MODE__W 3
+#define SIO_PDR_GPIO_CFG_MODE__M 0x7
+#define SIO_PDR_GPIO_CFG_MODE__PRE 0x0
+#define SIO_PDR_GPIO_CFG_DRIVE__B 3
+#define SIO_PDR_GPIO_CFG_DRIVE__W 3
+#define SIO_PDR_GPIO_CFG_DRIVE__M 0x38
+#define SIO_PDR_GPIO_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_GPIO_CFG_KEEP__B 6
+#define SIO_PDR_GPIO_CFG_KEEP__W 2
+#define SIO_PDR_GPIO_CFG_KEEP__M 0xC0
+#define SIO_PDR_GPIO_CFG_KEEP__PRE 0x0
+#define SIO_PDR_GPIO_CFG_UIO__B 8
+#define SIO_PDR_GPIO_CFG_UIO__W 1
+#define SIO_PDR_GPIO_CFG_UIO__M 0x100
+#define SIO_PDR_GPIO_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_IRQN_CFG__A 0x7F0022
+#define SIO_PDR_IRQN_CFG__W 9
+#define SIO_PDR_IRQN_CFG__M 0x1FF
+#define SIO_PDR_IRQN_CFG__PRE 0x10
+#define SIO_PDR_IRQN_CFG_MODE__B 0
+#define SIO_PDR_IRQN_CFG_MODE__W 3
+#define SIO_PDR_IRQN_CFG_MODE__M 0x7
+#define SIO_PDR_IRQN_CFG_MODE__PRE 0x0
+#define SIO_PDR_IRQN_CFG_DRIVE__B 3
+#define SIO_PDR_IRQN_CFG_DRIVE__W 3
+#define SIO_PDR_IRQN_CFG_DRIVE__M 0x38
+#define SIO_PDR_IRQN_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_IRQN_CFG_KEEP__B 6
+#define SIO_PDR_IRQN_CFG_KEEP__W 2
+#define SIO_PDR_IRQN_CFG_KEEP__M 0xC0
+#define SIO_PDR_IRQN_CFG_KEEP__PRE 0x0
+#define SIO_PDR_IRQN_CFG_UIO__B 8
+#define SIO_PDR_IRQN_CFG_UIO__W 1
+#define SIO_PDR_IRQN_CFG_UIO__M 0x100
+#define SIO_PDR_IRQN_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_OOB_CRX_CFG__A 0x7F0023
+#define SIO_PDR_OOB_CRX_CFG__W 9
+#define SIO_PDR_OOB_CRX_CFG__M 0x1FF
+#define SIO_PDR_OOB_CRX_CFG__PRE 0x10
+#define SIO_PDR_OOB_CRX_CFG_MODE__B 0
+#define SIO_PDR_OOB_CRX_CFG_MODE__W 3
+#define SIO_PDR_OOB_CRX_CFG_MODE__M 0x7
+#define SIO_PDR_OOB_CRX_CFG_MODE__PRE 0x0
+#define SIO_PDR_OOB_CRX_CFG_DRIVE__B 3
+#define SIO_PDR_OOB_CRX_CFG_DRIVE__W 3
+#define SIO_PDR_OOB_CRX_CFG_DRIVE__M 0x38
+#define SIO_PDR_OOB_CRX_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_OOB_CRX_CFG_KEEP__B 6
+#define SIO_PDR_OOB_CRX_CFG_KEEP__W 2
+#define SIO_PDR_OOB_CRX_CFG_KEEP__M 0xC0
+#define SIO_PDR_OOB_CRX_CFG_KEEP__PRE 0x0
+#define SIO_PDR_OOB_CRX_CFG_UIO__B 8
+#define SIO_PDR_OOB_CRX_CFG_UIO__W 1
+#define SIO_PDR_OOB_CRX_CFG_UIO__M 0x100
+#define SIO_PDR_OOB_CRX_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_OOB_DRX_CFG__A 0x7F0024
+#define SIO_PDR_OOB_DRX_CFG__W 9
+#define SIO_PDR_OOB_DRX_CFG__M 0x1FF
+#define SIO_PDR_OOB_DRX_CFG__PRE 0x10
+#define SIO_PDR_OOB_DRX_CFG_MODE__B 0
+#define SIO_PDR_OOB_DRX_CFG_MODE__W 3
+#define SIO_PDR_OOB_DRX_CFG_MODE__M 0x7
+#define SIO_PDR_OOB_DRX_CFG_MODE__PRE 0x0
+#define SIO_PDR_OOB_DRX_CFG_DRIVE__B 3
+#define SIO_PDR_OOB_DRX_CFG_DRIVE__W 3
+#define SIO_PDR_OOB_DRX_CFG_DRIVE__M 0x38
+#define SIO_PDR_OOB_DRX_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_OOB_DRX_CFG_KEEP__B 6
+#define SIO_PDR_OOB_DRX_CFG_KEEP__W 2
+#define SIO_PDR_OOB_DRX_CFG_KEEP__M 0xC0
+#define SIO_PDR_OOB_DRX_CFG_KEEP__PRE 0x0
+#define SIO_PDR_OOB_DRX_CFG_UIO__B 8
+#define SIO_PDR_OOB_DRX_CFG_UIO__W 1
+#define SIO_PDR_OOB_DRX_CFG_UIO__M 0x100
+#define SIO_PDR_OOB_DRX_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MSTRT_CFG__A 0x7F0025
+#define SIO_PDR_MSTRT_CFG__W 9
+#define SIO_PDR_MSTRT_CFG__M 0x1FF
+#define SIO_PDR_MSTRT_CFG__PRE 0x50
+#define SIO_PDR_MSTRT_CFG_MODE__B 0
+#define SIO_PDR_MSTRT_CFG_MODE__W 3
+#define SIO_PDR_MSTRT_CFG_MODE__M 0x7
+#define SIO_PDR_MSTRT_CFG_MODE__PRE 0x0
+#define SIO_PDR_MSTRT_CFG_DRIVE__B 3
+#define SIO_PDR_MSTRT_CFG_DRIVE__W 3
+#define SIO_PDR_MSTRT_CFG_DRIVE__M 0x38
+#define SIO_PDR_MSTRT_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MSTRT_CFG_KEEP__B 6
+#define SIO_PDR_MSTRT_CFG_KEEP__W 2
+#define SIO_PDR_MSTRT_CFG_KEEP__M 0xC0
+#define SIO_PDR_MSTRT_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MSTRT_CFG_UIO__B 8
+#define SIO_PDR_MSTRT_CFG_UIO__W 1
+#define SIO_PDR_MSTRT_CFG_UIO__M 0x100
+#define SIO_PDR_MSTRT_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MERR_CFG__A 0x7F0026
+#define SIO_PDR_MERR_CFG__W 9
+#define SIO_PDR_MERR_CFG__M 0x1FF
+#define SIO_PDR_MERR_CFG__PRE 0x50
+#define SIO_PDR_MERR_CFG_MODE__B 0
+#define SIO_PDR_MERR_CFG_MODE__W 3
+#define SIO_PDR_MERR_CFG_MODE__M 0x7
+#define SIO_PDR_MERR_CFG_MODE__PRE 0x0
+#define SIO_PDR_MERR_CFG_DRIVE__B 3
+#define SIO_PDR_MERR_CFG_DRIVE__W 3
+#define SIO_PDR_MERR_CFG_DRIVE__M 0x38
+#define SIO_PDR_MERR_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MERR_CFG_KEEP__B 6
+#define SIO_PDR_MERR_CFG_KEEP__W 2
+#define SIO_PDR_MERR_CFG_KEEP__M 0xC0
+#define SIO_PDR_MERR_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MERR_CFG_UIO__B 8
+#define SIO_PDR_MERR_CFG_UIO__W 1
+#define SIO_PDR_MERR_CFG_UIO__M 0x100
+#define SIO_PDR_MERR_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MCLK_CFG__A 0x7F0028
+#define SIO_PDR_MCLK_CFG__W 9
+#define SIO_PDR_MCLK_CFG__M 0x1FF
+#define SIO_PDR_MCLK_CFG__PRE 0x50
+#define SIO_PDR_MCLK_CFG_MODE__B 0
+#define SIO_PDR_MCLK_CFG_MODE__W 3
+#define SIO_PDR_MCLK_CFG_MODE__M 0x7
+#define SIO_PDR_MCLK_CFG_MODE__PRE 0x0
+#define SIO_PDR_MCLK_CFG_DRIVE__B 3
+#define SIO_PDR_MCLK_CFG_DRIVE__W 3
+#define SIO_PDR_MCLK_CFG_DRIVE__M 0x38
+#define SIO_PDR_MCLK_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MCLK_CFG_KEEP__B 6
+#define SIO_PDR_MCLK_CFG_KEEP__W 2
+#define SIO_PDR_MCLK_CFG_KEEP__M 0xC0
+#define SIO_PDR_MCLK_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MCLK_CFG_UIO__B 8
+#define SIO_PDR_MCLK_CFG_UIO__W 1
+#define SIO_PDR_MCLK_CFG_UIO__M 0x100
+#define SIO_PDR_MCLK_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MVAL_CFG__A 0x7F0029
+#define SIO_PDR_MVAL_CFG__W 9
+#define SIO_PDR_MVAL_CFG__M 0x1FF
+#define SIO_PDR_MVAL_CFG__PRE 0x50
+#define SIO_PDR_MVAL_CFG_MODE__B 0
+#define SIO_PDR_MVAL_CFG_MODE__W 3
+#define SIO_PDR_MVAL_CFG_MODE__M 0x7
+#define SIO_PDR_MVAL_CFG_MODE__PRE 0x0
+#define SIO_PDR_MVAL_CFG_DRIVE__B 3
+#define SIO_PDR_MVAL_CFG_DRIVE__W 3
+#define SIO_PDR_MVAL_CFG_DRIVE__M 0x38
+#define SIO_PDR_MVAL_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MVAL_CFG_KEEP__B 6
+#define SIO_PDR_MVAL_CFG_KEEP__W 2
+#define SIO_PDR_MVAL_CFG_KEEP__M 0xC0
+#define SIO_PDR_MVAL_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MVAL_CFG_UIO__B 8
+#define SIO_PDR_MVAL_CFG_UIO__W 1
+#define SIO_PDR_MVAL_CFG_UIO__M 0x100
+#define SIO_PDR_MVAL_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD0_CFG__A 0x7F002A
+#define SIO_PDR_MD0_CFG__W 9
+#define SIO_PDR_MD0_CFG__M 0x1FF
+#define SIO_PDR_MD0_CFG__PRE 0x50
+#define SIO_PDR_MD0_CFG_MODE__B 0
+#define SIO_PDR_MD0_CFG_MODE__W 3
+#define SIO_PDR_MD0_CFG_MODE__M 0x7
+#define SIO_PDR_MD0_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD0_CFG_DRIVE__B 3
+#define SIO_PDR_MD0_CFG_DRIVE__W 3
+#define SIO_PDR_MD0_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD0_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD0_CFG_KEEP__B 6
+#define SIO_PDR_MD0_CFG_KEEP__W 2
+#define SIO_PDR_MD0_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD0_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD0_CFG_UIO__B 8
+#define SIO_PDR_MD0_CFG_UIO__W 1
+#define SIO_PDR_MD0_CFG_UIO__M 0x100
+#define SIO_PDR_MD0_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD1_CFG__A 0x7F002B
+#define SIO_PDR_MD1_CFG__W 9
+#define SIO_PDR_MD1_CFG__M 0x1FF
+#define SIO_PDR_MD1_CFG__PRE 0x50
+#define SIO_PDR_MD1_CFG_MODE__B 0
+#define SIO_PDR_MD1_CFG_MODE__W 3
+#define SIO_PDR_MD1_CFG_MODE__M 0x7
+#define SIO_PDR_MD1_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD1_CFG_DRIVE__B 3
+#define SIO_PDR_MD1_CFG_DRIVE__W 3
+#define SIO_PDR_MD1_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD1_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD1_CFG_KEEP__B 6
+#define SIO_PDR_MD1_CFG_KEEP__W 2
+#define SIO_PDR_MD1_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD1_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD1_CFG_UIO__B 8
+#define SIO_PDR_MD1_CFG_UIO__W 1
+#define SIO_PDR_MD1_CFG_UIO__M 0x100
+#define SIO_PDR_MD1_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD2_CFG__A 0x7F002C
+#define SIO_PDR_MD2_CFG__W 9
+#define SIO_PDR_MD2_CFG__M 0x1FF
+#define SIO_PDR_MD2_CFG__PRE 0x50
+#define SIO_PDR_MD2_CFG_MODE__B 0
+#define SIO_PDR_MD2_CFG_MODE__W 3
+#define SIO_PDR_MD2_CFG_MODE__M 0x7
+#define SIO_PDR_MD2_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD2_CFG_DRIVE__B 3
+#define SIO_PDR_MD2_CFG_DRIVE__W 3
+#define SIO_PDR_MD2_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD2_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD2_CFG_KEEP__B 6
+#define SIO_PDR_MD2_CFG_KEEP__W 2
+#define SIO_PDR_MD2_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD2_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD2_CFG_UIO__B 8
+#define SIO_PDR_MD2_CFG_UIO__W 1
+#define SIO_PDR_MD2_CFG_UIO__M 0x100
+#define SIO_PDR_MD2_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD3_CFG__A 0x7F002D
+#define SIO_PDR_MD3_CFG__W 9
+#define SIO_PDR_MD3_CFG__M 0x1FF
+#define SIO_PDR_MD3_CFG__PRE 0x50
+#define SIO_PDR_MD3_CFG_MODE__B 0
+#define SIO_PDR_MD3_CFG_MODE__W 3
+#define SIO_PDR_MD3_CFG_MODE__M 0x7
+#define SIO_PDR_MD3_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD3_CFG_DRIVE__B 3
+#define SIO_PDR_MD3_CFG_DRIVE__W 3
+#define SIO_PDR_MD3_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD3_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD3_CFG_KEEP__B 6
+#define SIO_PDR_MD3_CFG_KEEP__W 2
+#define SIO_PDR_MD3_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD3_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD3_CFG_UIO__B 8
+#define SIO_PDR_MD3_CFG_UIO__W 1
+#define SIO_PDR_MD3_CFG_UIO__M 0x100
+#define SIO_PDR_MD3_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD4_CFG__A 0x7F002F
+#define SIO_PDR_MD4_CFG__W 9
+#define SIO_PDR_MD4_CFG__M 0x1FF
+#define SIO_PDR_MD4_CFG__PRE 0x50
+#define SIO_PDR_MD4_CFG_MODE__B 0
+#define SIO_PDR_MD4_CFG_MODE__W 3
+#define SIO_PDR_MD4_CFG_MODE__M 0x7
+#define SIO_PDR_MD4_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD4_CFG_DRIVE__B 3
+#define SIO_PDR_MD4_CFG_DRIVE__W 3
+#define SIO_PDR_MD4_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD4_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD4_CFG_KEEP__B 6
+#define SIO_PDR_MD4_CFG_KEEP__W 2
+#define SIO_PDR_MD4_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD4_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD4_CFG_UIO__B 8
+#define SIO_PDR_MD4_CFG_UIO__W 1
+#define SIO_PDR_MD4_CFG_UIO__M 0x100
+#define SIO_PDR_MD4_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD5_CFG__A 0x7F0030
+#define SIO_PDR_MD5_CFG__W 9
+#define SIO_PDR_MD5_CFG__M 0x1FF
+#define SIO_PDR_MD5_CFG__PRE 0x50
+#define SIO_PDR_MD5_CFG_MODE__B 0
+#define SIO_PDR_MD5_CFG_MODE__W 3
+#define SIO_PDR_MD5_CFG_MODE__M 0x7
+#define SIO_PDR_MD5_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD5_CFG_DRIVE__B 3
+#define SIO_PDR_MD5_CFG_DRIVE__W 3
+#define SIO_PDR_MD5_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD5_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD5_CFG_KEEP__B 6
+#define SIO_PDR_MD5_CFG_KEEP__W 2
+#define SIO_PDR_MD5_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD5_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD5_CFG_UIO__B 8
+#define SIO_PDR_MD5_CFG_UIO__W 1
+#define SIO_PDR_MD5_CFG_UIO__M 0x100
+#define SIO_PDR_MD5_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD6_CFG__A 0x7F0031
+#define SIO_PDR_MD6_CFG__W 9
+#define SIO_PDR_MD6_CFG__M 0x1FF
+#define SIO_PDR_MD6_CFG__PRE 0x50
+#define SIO_PDR_MD6_CFG_MODE__B 0
+#define SIO_PDR_MD6_CFG_MODE__W 3
+#define SIO_PDR_MD6_CFG_MODE__M 0x7
+#define SIO_PDR_MD6_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD6_CFG_DRIVE__B 3
+#define SIO_PDR_MD6_CFG_DRIVE__W 3
+#define SIO_PDR_MD6_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD6_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD6_CFG_KEEP__B 6
+#define SIO_PDR_MD6_CFG_KEEP__W 2
+#define SIO_PDR_MD6_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD6_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD6_CFG_UIO__B 8
+#define SIO_PDR_MD6_CFG_UIO__W 1
+#define SIO_PDR_MD6_CFG_UIO__M 0x100
+#define SIO_PDR_MD6_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_MD7_CFG__A 0x7F0032
+#define SIO_PDR_MD7_CFG__W 9
+#define SIO_PDR_MD7_CFG__M 0x1FF
+#define SIO_PDR_MD7_CFG__PRE 0x50
+#define SIO_PDR_MD7_CFG_MODE__B 0
+#define SIO_PDR_MD7_CFG_MODE__W 3
+#define SIO_PDR_MD7_CFG_MODE__M 0x7
+#define SIO_PDR_MD7_CFG_MODE__PRE 0x0
+#define SIO_PDR_MD7_CFG_DRIVE__B 3
+#define SIO_PDR_MD7_CFG_DRIVE__W 3
+#define SIO_PDR_MD7_CFG_DRIVE__M 0x38
+#define SIO_PDR_MD7_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_MD7_CFG_KEEP__B 6
+#define SIO_PDR_MD7_CFG_KEEP__W 2
+#define SIO_PDR_MD7_CFG_KEEP__M 0xC0
+#define SIO_PDR_MD7_CFG_KEEP__PRE 0x40
+#define SIO_PDR_MD7_CFG_UIO__B 8
+#define SIO_PDR_MD7_CFG_UIO__W 1
+#define SIO_PDR_MD7_CFG_UIO__M 0x100
+#define SIO_PDR_MD7_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_I2C_SCL1_CFG__A 0x7F0033
+#define SIO_PDR_I2C_SCL1_CFG__W 9
+#define SIO_PDR_I2C_SCL1_CFG__M 0x1FF
+#define SIO_PDR_I2C_SCL1_CFG__PRE 0x11
+#define SIO_PDR_I2C_SCL1_CFG_MODE__B 0
+#define SIO_PDR_I2C_SCL1_CFG_MODE__W 3
+#define SIO_PDR_I2C_SCL1_CFG_MODE__M 0x7
+#define SIO_PDR_I2C_SCL1_CFG_MODE__PRE 0x1
+#define SIO_PDR_I2C_SCL1_CFG_DRIVE__B 3
+#define SIO_PDR_I2C_SCL1_CFG_DRIVE__W 3
+#define SIO_PDR_I2C_SCL1_CFG_DRIVE__M 0x38
+#define SIO_PDR_I2C_SCL1_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_I2C_SCL1_CFG_KEEP__B 6
+#define SIO_PDR_I2C_SCL1_CFG_KEEP__W 2
+#define SIO_PDR_I2C_SCL1_CFG_KEEP__M 0xC0
+#define SIO_PDR_I2C_SCL1_CFG_KEEP__PRE 0x0
+#define SIO_PDR_I2C_SCL1_CFG_UIO__B 8
+#define SIO_PDR_I2C_SCL1_CFG_UIO__W 1
+#define SIO_PDR_I2C_SCL1_CFG_UIO__M 0x100
+#define SIO_PDR_I2C_SCL1_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_I2C_SDA1_CFG__A 0x7F0034
+#define SIO_PDR_I2C_SDA1_CFG__W 9
+#define SIO_PDR_I2C_SDA1_CFG__M 0x1FF
+#define SIO_PDR_I2C_SDA1_CFG__PRE 0x11
+#define SIO_PDR_I2C_SDA1_CFG_MODE__B 0
+#define SIO_PDR_I2C_SDA1_CFG_MODE__W 3
+#define SIO_PDR_I2C_SDA1_CFG_MODE__M 0x7
+#define SIO_PDR_I2C_SDA1_CFG_MODE__PRE 0x1
+#define SIO_PDR_I2C_SDA1_CFG_DRIVE__B 3
+#define SIO_PDR_I2C_SDA1_CFG_DRIVE__W 3
+#define SIO_PDR_I2C_SDA1_CFG_DRIVE__M 0x38
+#define SIO_PDR_I2C_SDA1_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_I2C_SDA1_CFG_KEEP__B 6
+#define SIO_PDR_I2C_SDA1_CFG_KEEP__W 2
+#define SIO_PDR_I2C_SDA1_CFG_KEEP__M 0xC0
+#define SIO_PDR_I2C_SDA1_CFG_KEEP__PRE 0x0
+#define SIO_PDR_I2C_SDA1_CFG_UIO__B 8
+#define SIO_PDR_I2C_SDA1_CFG_UIO__W 1
+#define SIO_PDR_I2C_SDA1_CFG_UIO__M 0x100
+#define SIO_PDR_I2C_SDA1_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_VSYNC_CFG__A 0x7F0036
+#define SIO_PDR_VSYNC_CFG__W 9
+#define SIO_PDR_VSYNC_CFG__M 0x1FF
+#define SIO_PDR_VSYNC_CFG__PRE 0x10
+#define SIO_PDR_VSYNC_CFG_MODE__B 0
+#define SIO_PDR_VSYNC_CFG_MODE__W 3
+#define SIO_PDR_VSYNC_CFG_MODE__M 0x7
+#define SIO_PDR_VSYNC_CFG_MODE__PRE 0x0
+#define SIO_PDR_VSYNC_CFG_DRIVE__B 3
+#define SIO_PDR_VSYNC_CFG_DRIVE__W 3
+#define SIO_PDR_VSYNC_CFG_DRIVE__M 0x38
+#define SIO_PDR_VSYNC_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_VSYNC_CFG_KEEP__B 6
+#define SIO_PDR_VSYNC_CFG_KEEP__W 2
+#define SIO_PDR_VSYNC_CFG_KEEP__M 0xC0
+#define SIO_PDR_VSYNC_CFG_KEEP__PRE 0x0
+#define SIO_PDR_VSYNC_CFG_UIO__B 8
+#define SIO_PDR_VSYNC_CFG_UIO__W 1
+#define SIO_PDR_VSYNC_CFG_UIO__M 0x100
+#define SIO_PDR_VSYNC_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_SMA_RX_CFG__A 0x7F0037
+#define SIO_PDR_SMA_RX_CFG__W 9
+#define SIO_PDR_SMA_RX_CFG__M 0x1FF
+#define SIO_PDR_SMA_RX_CFG__PRE 0x10
+#define SIO_PDR_SMA_RX_CFG_MODE__B 0
+#define SIO_PDR_SMA_RX_CFG_MODE__W 3
+#define SIO_PDR_SMA_RX_CFG_MODE__M 0x7
+#define SIO_PDR_SMA_RX_CFG_MODE__PRE 0x0
+#define SIO_PDR_SMA_RX_CFG_DRIVE__B 3
+#define SIO_PDR_SMA_RX_CFG_DRIVE__W 3
+#define SIO_PDR_SMA_RX_CFG_DRIVE__M 0x38
+#define SIO_PDR_SMA_RX_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_SMA_RX_CFG_KEEP__B 6
+#define SIO_PDR_SMA_RX_CFG_KEEP__W 2
+#define SIO_PDR_SMA_RX_CFG_KEEP__M 0xC0
+#define SIO_PDR_SMA_RX_CFG_KEEP__PRE 0x0
+#define SIO_PDR_SMA_RX_CFG_UIO__B 8
+#define SIO_PDR_SMA_RX_CFG_UIO__W 1
+#define SIO_PDR_SMA_RX_CFG_UIO__M 0x100
+#define SIO_PDR_SMA_RX_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_SMA_TX_CFG__A 0x7F0038
+#define SIO_PDR_SMA_TX_CFG__W 9
+#define SIO_PDR_SMA_TX_CFG__M 0x1FF
+#define SIO_PDR_SMA_TX_CFG__PRE 0x90
+#define SIO_PDR_SMA_TX_CFG_MODE__B 0
+#define SIO_PDR_SMA_TX_CFG_MODE__W 3
+#define SIO_PDR_SMA_TX_CFG_MODE__M 0x7
+#define SIO_PDR_SMA_TX_CFG_MODE__PRE 0x0
+#define SIO_PDR_SMA_TX_CFG_DRIVE__B 3
+#define SIO_PDR_SMA_TX_CFG_DRIVE__W 3
+#define SIO_PDR_SMA_TX_CFG_DRIVE__M 0x38
+#define SIO_PDR_SMA_TX_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_SMA_TX_CFG_KEEP__B 6
+#define SIO_PDR_SMA_TX_CFG_KEEP__W 2
+#define SIO_PDR_SMA_TX_CFG_KEEP__M 0xC0
+#define SIO_PDR_SMA_TX_CFG_KEEP__PRE 0x80
+#define SIO_PDR_SMA_TX_CFG_UIO__B 8
+#define SIO_PDR_SMA_TX_CFG_UIO__W 1
+#define SIO_PDR_SMA_TX_CFG_UIO__M 0x100
+#define SIO_PDR_SMA_TX_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_I2C_SDA2_CFG__A 0x7F003F
+#define SIO_PDR_I2C_SDA2_CFG__W 9
+#define SIO_PDR_I2C_SDA2_CFG__M 0x1FF
+#define SIO_PDR_I2C_SDA2_CFG__PRE 0x11
+#define SIO_PDR_I2C_SDA2_CFG_MODE__B 0
+#define SIO_PDR_I2C_SDA2_CFG_MODE__W 3
+#define SIO_PDR_I2C_SDA2_CFG_MODE__M 0x7
+#define SIO_PDR_I2C_SDA2_CFG_MODE__PRE 0x1
+#define SIO_PDR_I2C_SDA2_CFG_DRIVE__B 3
+#define SIO_PDR_I2C_SDA2_CFG_DRIVE__W 3
+#define SIO_PDR_I2C_SDA2_CFG_DRIVE__M 0x38
+#define SIO_PDR_I2C_SDA2_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_I2C_SDA2_CFG_KEEP__B 6
+#define SIO_PDR_I2C_SDA2_CFG_KEEP__W 2
+#define SIO_PDR_I2C_SDA2_CFG_KEEP__M 0xC0
+#define SIO_PDR_I2C_SDA2_CFG_KEEP__PRE 0x0
+#define SIO_PDR_I2C_SDA2_CFG_UIO__B 8
+#define SIO_PDR_I2C_SDA2_CFG_UIO__W 1
+#define SIO_PDR_I2C_SDA2_CFG_UIO__M 0x100
+#define SIO_PDR_I2C_SDA2_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_I2C_SCL2_CFG__A 0x7F0040
+#define SIO_PDR_I2C_SCL2_CFG__W 9
+#define SIO_PDR_I2C_SCL2_CFG__M 0x1FF
+#define SIO_PDR_I2C_SCL2_CFG__PRE 0x11
+#define SIO_PDR_I2C_SCL2_CFG_MODE__B 0
+#define SIO_PDR_I2C_SCL2_CFG_MODE__W 3
+#define SIO_PDR_I2C_SCL2_CFG_MODE__M 0x7
+#define SIO_PDR_I2C_SCL2_CFG_MODE__PRE 0x1
+#define SIO_PDR_I2C_SCL2_CFG_DRIVE__B 3
+#define SIO_PDR_I2C_SCL2_CFG_DRIVE__W 3
+#define SIO_PDR_I2C_SCL2_CFG_DRIVE__M 0x38
+#define SIO_PDR_I2C_SCL2_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_I2C_SCL2_CFG_KEEP__B 6
+#define SIO_PDR_I2C_SCL2_CFG_KEEP__W 2
+#define SIO_PDR_I2C_SCL2_CFG_KEEP__M 0xC0
+#define SIO_PDR_I2C_SCL2_CFG_KEEP__PRE 0x0
+#define SIO_PDR_I2C_SCL2_CFG_UIO__B 8
+#define SIO_PDR_I2C_SCL2_CFG_UIO__W 1
+#define SIO_PDR_I2C_SCL2_CFG_UIO__M 0x100
+#define SIO_PDR_I2C_SCL2_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_I2S_CL_CFG__A 0x7F0041
+#define SIO_PDR_I2S_CL_CFG__W 9
+#define SIO_PDR_I2S_CL_CFG__M 0x1FF
+#define SIO_PDR_I2S_CL_CFG__PRE 0x10
+#define SIO_PDR_I2S_CL_CFG_MODE__B 0
+#define SIO_PDR_I2S_CL_CFG_MODE__W 3
+#define SIO_PDR_I2S_CL_CFG_MODE__M 0x7
+#define SIO_PDR_I2S_CL_CFG_MODE__PRE 0x0
+#define SIO_PDR_I2S_CL_CFG_DRIVE__B 3
+#define SIO_PDR_I2S_CL_CFG_DRIVE__W 3
+#define SIO_PDR_I2S_CL_CFG_DRIVE__M 0x38
+#define SIO_PDR_I2S_CL_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_I2S_CL_CFG_KEEP__B 6
+#define SIO_PDR_I2S_CL_CFG_KEEP__W 2
+#define SIO_PDR_I2S_CL_CFG_KEEP__M 0xC0
+#define SIO_PDR_I2S_CL_CFG_KEEP__PRE 0x0
+#define SIO_PDR_I2S_CL_CFG_UIO__B 8
+#define SIO_PDR_I2S_CL_CFG_UIO__W 1
+#define SIO_PDR_I2S_CL_CFG_UIO__M 0x100
+#define SIO_PDR_I2S_CL_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_I2S_DA_CFG__A 0x7F0042
+#define SIO_PDR_I2S_DA_CFG__W 9
+#define SIO_PDR_I2S_DA_CFG__M 0x1FF
+#define SIO_PDR_I2S_DA_CFG__PRE 0x10
+#define SIO_PDR_I2S_DA_CFG_MODE__B 0
+#define SIO_PDR_I2S_DA_CFG_MODE__W 3
+#define SIO_PDR_I2S_DA_CFG_MODE__M 0x7
+#define SIO_PDR_I2S_DA_CFG_MODE__PRE 0x0
+#define SIO_PDR_I2S_DA_CFG_DRIVE__B 3
+#define SIO_PDR_I2S_DA_CFG_DRIVE__W 3
+#define SIO_PDR_I2S_DA_CFG_DRIVE__M 0x38
+#define SIO_PDR_I2S_DA_CFG_DRIVE__PRE 0x10
+#define SIO_PDR_I2S_DA_CFG_KEEP__B 6
+#define SIO_PDR_I2S_DA_CFG_KEEP__W 2
+#define SIO_PDR_I2S_DA_CFG_KEEP__M 0xC0
+#define SIO_PDR_I2S_DA_CFG_KEEP__PRE 0x0
+#define SIO_PDR_I2S_DA_CFG_UIO__B 8
+#define SIO_PDR_I2S_DA_CFG_UIO__W 1
+#define SIO_PDR_I2S_DA_CFG_UIO__M 0x100
+#define SIO_PDR_I2S_DA_CFG_UIO__PRE 0x0
+
+#define SIO_PDR_GPIO_GPIO_FNC__A 0x7F0050
+#define SIO_PDR_GPIO_GPIO_FNC__W 2
+#define SIO_PDR_GPIO_GPIO_FNC__M 0x3
+#define SIO_PDR_GPIO_GPIO_FNC__PRE 0x0
+#define SIO_PDR_GPIO_GPIO_FNC_SEL__B 0
+#define SIO_PDR_GPIO_GPIO_FNC_SEL__W 2
+#define SIO_PDR_GPIO_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_GPIO_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_IRQN_GPIO_FNC__A 0x7F0051
+#define SIO_PDR_IRQN_GPIO_FNC__W 2
+#define SIO_PDR_IRQN_GPIO_FNC__M 0x3
+#define SIO_PDR_IRQN_GPIO_FNC__PRE 0x0
+#define SIO_PDR_IRQN_GPIO_FNC_SEL__B 0
+#define SIO_PDR_IRQN_GPIO_FNC_SEL__W 2
+#define SIO_PDR_IRQN_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_IRQN_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MSTRT_GPIO_FNC__A 0x7F0052
+#define SIO_PDR_MSTRT_GPIO_FNC__W 2
+#define SIO_PDR_MSTRT_GPIO_FNC__M 0x3
+#define SIO_PDR_MSTRT_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MSTRT_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MSTRT_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MSTRT_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MSTRT_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MERR_GPIO_FNC__A 0x7F0053
+#define SIO_PDR_MERR_GPIO_FNC__W 2
+#define SIO_PDR_MERR_GPIO_FNC__M 0x3
+#define SIO_PDR_MERR_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MERR_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MERR_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MERR_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MERR_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MCLK_GPIO_FNC__A 0x7F0054
+#define SIO_PDR_MCLK_GPIO_FNC__W 2
+#define SIO_PDR_MCLK_GPIO_FNC__M 0x3
+#define SIO_PDR_MCLK_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MCLK_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MCLK_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MCLK_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MCLK_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MVAL_GPIO_FNC__A 0x7F0055
+#define SIO_PDR_MVAL_GPIO_FNC__W 2
+#define SIO_PDR_MVAL_GPIO_FNC__M 0x3
+#define SIO_PDR_MVAL_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MVAL_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MVAL_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MVAL_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MVAL_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD0_GPIO_FNC__A 0x7F0056
+#define SIO_PDR_MD0_GPIO_FNC__W 2
+#define SIO_PDR_MD0_GPIO_FNC__M 0x3
+#define SIO_PDR_MD0_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD0_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD0_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD0_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD0_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD1_GPIO_FNC__A 0x7F0057
+#define SIO_PDR_MD1_GPIO_FNC__W 2
+#define SIO_PDR_MD1_GPIO_FNC__M 0x3
+#define SIO_PDR_MD1_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD1_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD1_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD1_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD1_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD2_GPIO_FNC__A 0x7F0058
+#define SIO_PDR_MD2_GPIO_FNC__W 2
+#define SIO_PDR_MD2_GPIO_FNC__M 0x3
+#define SIO_PDR_MD2_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD2_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD2_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD2_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD2_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD3_GPIO_FNC__A 0x7F0059
+#define SIO_PDR_MD3_GPIO_FNC__W 2
+#define SIO_PDR_MD3_GPIO_FNC__M 0x3
+#define SIO_PDR_MD3_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD3_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD3_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD3_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD3_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD4_GPIO_FNC__A 0x7F005A
+#define SIO_PDR_MD4_GPIO_FNC__W 2
+#define SIO_PDR_MD4_GPIO_FNC__M 0x3
+#define SIO_PDR_MD4_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD4_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD4_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD4_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD4_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD5_GPIO_FNC__A 0x7F005B
+#define SIO_PDR_MD5_GPIO_FNC__W 2
+#define SIO_PDR_MD5_GPIO_FNC__M 0x3
+#define SIO_PDR_MD5_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD5_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD5_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD5_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD5_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD6_GPIO_FNC__A 0x7F005C
+#define SIO_PDR_MD6_GPIO_FNC__W 2
+#define SIO_PDR_MD6_GPIO_FNC__M 0x3
+#define SIO_PDR_MD6_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD6_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD6_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD6_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD6_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_MD7_GPIO_FNC__A 0x7F005D
+#define SIO_PDR_MD7_GPIO_FNC__W 2
+#define SIO_PDR_MD7_GPIO_FNC__M 0x3
+#define SIO_PDR_MD7_GPIO_FNC__PRE 0x0
+#define SIO_PDR_MD7_GPIO_FNC_SEL__B 0
+#define SIO_PDR_MD7_GPIO_FNC_SEL__W 2
+#define SIO_PDR_MD7_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_MD7_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_SMA_RX_GPIO_FNC__A 0x7F005E
+#define SIO_PDR_SMA_RX_GPIO_FNC__W 2
+#define SIO_PDR_SMA_RX_GPIO_FNC__M 0x3
+#define SIO_PDR_SMA_RX_GPIO_FNC__PRE 0x0
+#define SIO_PDR_SMA_RX_GPIO_FNC_SEL__B 0
+#define SIO_PDR_SMA_RX_GPIO_FNC_SEL__W 2
+#define SIO_PDR_SMA_RX_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_SMA_RX_GPIO_FNC_SEL__PRE 0x0
+
+#define SIO_PDR_SMA_TX_GPIO_FNC__A 0x7F005F
+#define SIO_PDR_SMA_TX_GPIO_FNC__W 2
+#define SIO_PDR_SMA_TX_GPIO_FNC__M 0x3
+#define SIO_PDR_SMA_TX_GPIO_FNC__PRE 0x0
+#define SIO_PDR_SMA_TX_GPIO_FNC_SEL__B 0
+#define SIO_PDR_SMA_TX_GPIO_FNC_SEL__W 2
+#define SIO_PDR_SMA_TX_GPIO_FNC_SEL__M 0x3
+#define SIO_PDR_SMA_TX_GPIO_FNC_SEL__PRE 0x0
+
+#define VSB_COMM_EXEC__A 0x1C00000
+#define VSB_COMM_EXEC__W 2
+#define VSB_COMM_EXEC__M 0x3
+#define VSB_COMM_EXEC__PRE 0x0
+#define VSB_COMM_EXEC_STOP 0x0
+#define VSB_COMM_EXEC_ACTIVE 0x1
+#define VSB_COMM_EXEC_HOLD 0x2
+
+#define VSB_COMM_MB__A 0x1C00002
+#define VSB_COMM_MB__W 16
+#define VSB_COMM_MB__M 0xFFFF
+#define VSB_COMM_MB__PRE 0x0
+#define VSB_COMM_INT_REQ__A 0x1C00003
+#define VSB_COMM_INT_REQ__W 1
+#define VSB_COMM_INT_REQ__M 0x1
+#define VSB_COMM_INT_REQ__PRE 0x0
+
+#define VSB_COMM_INT_REQ_TOP_INT_REQ__B 0
+#define VSB_COMM_INT_REQ_TOP_INT_REQ__W 1
+#define VSB_COMM_INT_REQ_TOP_INT_REQ__M 0x1
+#define VSB_COMM_INT_REQ_TOP_INT_REQ__PRE 0x0
+
+#define VSB_COMM_INT_STA__A 0x1C00005
+#define VSB_COMM_INT_STA__W 16
+#define VSB_COMM_INT_STA__M 0xFFFF
+#define VSB_COMM_INT_STA__PRE 0x0
+
+#define VSB_COMM_INT_MSK__A 0x1C00006
+#define VSB_COMM_INT_MSK__W 16
+#define VSB_COMM_INT_MSK__M 0xFFFF
+#define VSB_COMM_INT_MSK__PRE 0x0
+
+#define VSB_COMM_INT_STM__A 0x1C00007
+#define VSB_COMM_INT_STM__W 16
+#define VSB_COMM_INT_STM__M 0xFFFF
+#define VSB_COMM_INT_STM__PRE 0x0
+
+#define VSB_TOP_COMM_EXEC__A 0x1C10000
+#define VSB_TOP_COMM_EXEC__W 2
+#define VSB_TOP_COMM_EXEC__M 0x3
+#define VSB_TOP_COMM_EXEC__PRE 0x0
+#define VSB_TOP_COMM_EXEC_STOP 0x0
+#define VSB_TOP_COMM_EXEC_ACTIVE 0x1
+#define VSB_TOP_COMM_EXEC_HOLD 0x2
+
+#define VSB_TOP_COMM_MB__A 0x1C10002
+#define VSB_TOP_COMM_MB__W 10
+#define VSB_TOP_COMM_MB__M 0x3FF
+#define VSB_TOP_COMM_MB__PRE 0x0
+
+#define VSB_TOP_COMM_MB_CTL__B 0
+#define VSB_TOP_COMM_MB_CTL__W 1
+#define VSB_TOP_COMM_MB_CTL__M 0x1
+#define VSB_TOP_COMM_MB_CTL__PRE 0x0
+#define VSB_TOP_COMM_MB_CTL_CTL_OFF 0x0
+#define VSB_TOP_COMM_MB_CTL_CTL_ON 0x1
+
+#define VSB_TOP_COMM_MB_OBS__B 1
+#define VSB_TOP_COMM_MB_OBS__W 1
+#define VSB_TOP_COMM_MB_OBS__M 0x2
+#define VSB_TOP_COMM_MB_OBS__PRE 0x0
+#define VSB_TOP_COMM_MB_OBS_OBS_OFF 0x0
+#define VSB_TOP_COMM_MB_OBS_OBS_ON 0x2
+
+#define VSB_TOP_COMM_MB_MUX_CTL__B 2
+#define VSB_TOP_COMM_MB_MUX_CTL__W 4
+#define VSB_TOP_COMM_MB_MUX_CTL__M 0x3C
+#define VSB_TOP_COMM_MB_MUX_CTL__PRE 0x0
+
+#define VSB_TOP_COMM_MB_MUX_OBS__B 6
+#define VSB_TOP_COMM_MB_MUX_OBS__W 4
+#define VSB_TOP_COMM_MB_MUX_OBS__M 0x3C0
+#define VSB_TOP_COMM_MB_MUX_OBS__PRE 0x0
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_FEC 0x0
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_IQM 0x40
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_IQM_AMPLITUDE 0x80
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_TCMEQ_1 0xC0
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_TCMEQ_2 0x100
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_FFE_1 0x140
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_FFE_2 0x180
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_DFE_1 0x1C0
+#define VSB_TOP_COMM_MB_MUX_OBS_VSB_DFE_2 0x200
+
+#define VSB_TOP_COMM_INT_REQ__A 0x1C10003
+#define VSB_TOP_COMM_INT_REQ__W 1
+#define VSB_TOP_COMM_INT_REQ__M 0x1
+#define VSB_TOP_COMM_INT_REQ__PRE 0x0
+#define VSB_TOP_COMM_INT_STA__A 0x1C10005
+#define VSB_TOP_COMM_INT_STA__W 6
+#define VSB_TOP_COMM_INT_STA__M 0x3F
+#define VSB_TOP_COMM_INT_STA__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STA_FIELD_INT_STA__B 0
+#define VSB_TOP_COMM_INT_STA_FIELD_INT_STA__W 1
+#define VSB_TOP_COMM_INT_STA_FIELD_INT_STA__M 0x1
+#define VSB_TOP_COMM_INT_STA_FIELD_INT_STA__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STA_LOCK_STA__B 1
+#define VSB_TOP_COMM_INT_STA_LOCK_STA__W 1
+#define VSB_TOP_COMM_INT_STA_LOCK_STA__M 0x2
+#define VSB_TOP_COMM_INT_STA_LOCK_STA__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STA_UNLOCK_STA__B 2
+#define VSB_TOP_COMM_INT_STA_UNLOCK_STA__W 1
+#define VSB_TOP_COMM_INT_STA_UNLOCK_STA__M 0x4
+#define VSB_TOP_COMM_INT_STA_UNLOCK_STA__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STA_TAPREADER_STA__B 3
+#define VSB_TOP_COMM_INT_STA_TAPREADER_STA__W 1
+#define VSB_TOP_COMM_INT_STA_TAPREADER_STA__M 0x8
+#define VSB_TOP_COMM_INT_STA_TAPREADER_STA__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STA_SEGSYNCINTR_STA__B 4
+#define VSB_TOP_COMM_INT_STA_SEGSYNCINTR_STA__W 1
+#define VSB_TOP_COMM_INT_STA_SEGSYNCINTR_STA__M 0x10
+#define VSB_TOP_COMM_INT_STA_SEGSYNCINTR_STA__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STA_MERSER_STA__B 5
+#define VSB_TOP_COMM_INT_STA_MERSER_STA__W 1
+#define VSB_TOP_COMM_INT_STA_MERSER_STA__M 0x20
+#define VSB_TOP_COMM_INT_STA_MERSER_STA__PRE 0x0
+
+#define VSB_TOP_COMM_INT_MSK__A 0x1C10006
+#define VSB_TOP_COMM_INT_MSK__W 6
+#define VSB_TOP_COMM_INT_MSK__M 0x3F
+#define VSB_TOP_COMM_INT_MSK__PRE 0x0
+
+#define VSB_TOP_COMM_INT_MSK_FIELD_INT_MSK__B 0
+#define VSB_TOP_COMM_INT_MSK_FIELD_INT_MSK__W 1
+#define VSB_TOP_COMM_INT_MSK_FIELD_INT_MSK__M 0x1
+#define VSB_TOP_COMM_INT_MSK_FIELD_INT_MSK__PRE 0x0
+
+#define VSB_TOP_COMM_INT_MSK_LOCK_MSK__B 1
+#define VSB_TOP_COMM_INT_MSK_LOCK_MSK__W 1
+#define VSB_TOP_COMM_INT_MSK_LOCK_MSK__M 0x2
+#define VSB_TOP_COMM_INT_MSK_LOCK_MSK__PRE 0x0
+
+#define VSB_TOP_COMM_INT_MSK_UNLOCK_MSK__B 2
+#define VSB_TOP_COMM_INT_MSK_UNLOCK_MSK__W 1
+#define VSB_TOP_COMM_INT_MSK_UNLOCK_MSK__M 0x4
+#define VSB_TOP_COMM_INT_MSK_UNLOCK_MSK__PRE 0x0
+
+#define VSB_TOP_COMM_INT_MSK_TAPREADER_MSK__B 3
+#define VSB_TOP_COMM_INT_MSK_TAPREADER_MSK__W 1
+#define VSB_TOP_COMM_INT_MSK_TAPREADER_MSK__M 0x8
+#define VSB_TOP_COMM_INT_MSK_TAPREADER_MSK__PRE 0x0
+
+#define VSB_TOP_COMM_INT_MSK_SEGSYNCINTR_MSK__B 4
+#define VSB_TOP_COMM_INT_MSK_SEGSYNCINTR_MSK__W 1
+#define VSB_TOP_COMM_INT_MSK_SEGSYNCINTR_MSK__M 0x10
+#define VSB_TOP_COMM_INT_MSK_SEGSYNCINTR_MSK__PRE 0x0
+
+#define VSB_TOP_COMM_INT_MSK_MERSER_MSK__B 5
+#define VSB_TOP_COMM_INT_MSK_MERSER_MSK__W 1
+#define VSB_TOP_COMM_INT_MSK_MERSER_MSK__M 0x20
+#define VSB_TOP_COMM_INT_MSK_MERSER_MSK__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STM__A 0x1C10007
+#define VSB_TOP_COMM_INT_STM__W 6
+#define VSB_TOP_COMM_INT_STM__M 0x3F
+#define VSB_TOP_COMM_INT_STM__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STM_FIELD_INT_STM__B 0
+#define VSB_TOP_COMM_INT_STM_FIELD_INT_STM__W 1
+#define VSB_TOP_COMM_INT_STM_FIELD_INT_STM__M 0x1
+#define VSB_TOP_COMM_INT_STM_FIELD_INT_STM__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STM_LOCK_STM__B 1
+#define VSB_TOP_COMM_INT_STM_LOCK_STM__W 1
+#define VSB_TOP_COMM_INT_STM_LOCK_STM__M 0x2
+#define VSB_TOP_COMM_INT_STM_LOCK_STM__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STM_UNLOCK_STM__B 2
+#define VSB_TOP_COMM_INT_STM_UNLOCK_STM__W 1
+#define VSB_TOP_COMM_INT_STM_UNLOCK_STM__M 0x4
+#define VSB_TOP_COMM_INT_STM_UNLOCK_STM__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STM_TAPREADER_STM__B 3
+#define VSB_TOP_COMM_INT_STM_TAPREADER_STM__W 1
+#define VSB_TOP_COMM_INT_STM_TAPREADER_STM__M 0x8
+#define VSB_TOP_COMM_INT_STM_TAPREADER_STM__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STM_SEGSYNCINTR_STM__B 4
+#define VSB_TOP_COMM_INT_STM_SEGSYNCINTR_STM__W 1
+#define VSB_TOP_COMM_INT_STM_SEGSYNCINTR_STM__M 0x10
+#define VSB_TOP_COMM_INT_STM_SEGSYNCINTR_STM__PRE 0x0
+
+#define VSB_TOP_COMM_INT_STM_MERSER_STM__B 5
+#define VSB_TOP_COMM_INT_STM_MERSER_STM__W 1
+#define VSB_TOP_COMM_INT_STM_MERSER_STM__M 0x20
+#define VSB_TOP_COMM_INT_STM_MERSER_STM__PRE 0x0
+
+#define VSB_TOP_CKGN1ACQ__A 0x1C10010
+#define VSB_TOP_CKGN1ACQ__W 8
+#define VSB_TOP_CKGN1ACQ__M 0xFF
+#define VSB_TOP_CKGN1ACQ__PRE 0x4
+
+#define VSB_TOP_CKGN1TRK__A 0x1C10011
+#define VSB_TOP_CKGN1TRK__W 8
+#define VSB_TOP_CKGN1TRK__M 0xFF
+#define VSB_TOP_CKGN1TRK__PRE 0x0
+
+#define VSB_TOP_CKGN2ACQ__A 0x1C10012
+#define VSB_TOP_CKGN2ACQ__W 8
+#define VSB_TOP_CKGN2ACQ__M 0xFF
+#define VSB_TOP_CKGN2ACQ__PRE 0x2
+
+#define VSB_TOP_CKGN2TRK__A 0x1C10013
+#define VSB_TOP_CKGN2TRK__W 8
+#define VSB_TOP_CKGN2TRK__M 0xFF
+#define VSB_TOP_CKGN2TRK__PRE 0x1
+
+#define VSB_TOP_CKGN3__A 0x1C10014
+#define VSB_TOP_CKGN3__W 8
+#define VSB_TOP_CKGN3__M 0xFF
+#define VSB_TOP_CKGN3__PRE 0x5
+
+#define VSB_TOP_CYGN1ACQ__A 0x1C10015
+#define VSB_TOP_CYGN1ACQ__W 8
+#define VSB_TOP_CYGN1ACQ__M 0xFF
+#define VSB_TOP_CYGN1ACQ__PRE 0x3
+
+#define VSB_TOP_CYGN1TRK__A 0x1C10016
+#define VSB_TOP_CYGN1TRK__W 8
+#define VSB_TOP_CYGN1TRK__M 0xFF
+#define VSB_TOP_CYGN1TRK__PRE 0x0
+
+#define VSB_TOP_CYGN2ACQ__A 0x1C10017
+#define VSB_TOP_CYGN2ACQ__W 8
+#define VSB_TOP_CYGN2ACQ__M 0xFF
+#define VSB_TOP_CYGN2ACQ__PRE 0x3
+
+#define VSB_TOP_CYGN2TRK__A 0x1C10018
+#define VSB_TOP_CYGN2TRK__W 8
+#define VSB_TOP_CYGN2TRK__M 0xFF
+#define VSB_TOP_CYGN2TRK__PRE 0x2
+
+#define VSB_TOP_CYGN3__A 0x1C10019
+#define VSB_TOP_CYGN3__W 8
+#define VSB_TOP_CYGN3__M 0xFF
+#define VSB_TOP_CYGN3__PRE 0x6
+#define VSB_TOP_SYNCCTRLWORD__A 0x1C1001A
+#define VSB_TOP_SYNCCTRLWORD__W 5
+#define VSB_TOP_SYNCCTRLWORD__M 0x1F
+#define VSB_TOP_SYNCCTRLWORD__PRE 0x0
+
+#define VSB_TOP_SYNCCTRLWORD_PRST__B 0
+#define VSB_TOP_SYNCCTRLWORD_PRST__W 1
+#define VSB_TOP_SYNCCTRLWORD_PRST__M 0x1
+#define VSB_TOP_SYNCCTRLWORD_PRST__PRE 0x0
+
+#define VSB_TOP_SYNCCTRLWORD_DCFREEZ__B 1
+#define VSB_TOP_SYNCCTRLWORD_DCFREEZ__W 1
+#define VSB_TOP_SYNCCTRLWORD_DCFREEZ__M 0x2
+#define VSB_TOP_SYNCCTRLWORD_DCFREEZ__PRE 0x0
+
+#define VSB_TOP_SYNCCTRLWORD_INVCNST__B 2
+#define VSB_TOP_SYNCCTRLWORD_INVCNST__W 1
+#define VSB_TOP_SYNCCTRLWORD_INVCNST__M 0x4
+#define VSB_TOP_SYNCCTRLWORD_INVCNST__PRE 0x0
+
+#define VSB_TOP_SYNCCTRLWORD_CPUAGCRST__B 3
+#define VSB_TOP_SYNCCTRLWORD_CPUAGCRST__W 1
+#define VSB_TOP_SYNCCTRLWORD_CPUAGCRST__M 0x8
+#define VSB_TOP_SYNCCTRLWORD_CPUAGCRST__PRE 0x0
+
+#define VSB_TOP_SYNCCTRLWORD_AGCIGNOREFS__B 4
+#define VSB_TOP_SYNCCTRLWORD_AGCIGNOREFS__W 1
+#define VSB_TOP_SYNCCTRLWORD_AGCIGNOREFS__M 0x10
+#define VSB_TOP_SYNCCTRLWORD_AGCIGNOREFS__PRE 0x0
+
+#define VSB_TOP_MAINSMUP__A 0x1C1001B
+#define VSB_TOP_MAINSMUP__W 8
+#define VSB_TOP_MAINSMUP__M 0xFF
+#define VSB_TOP_MAINSMUP__PRE 0xFF
+
+#define VSB_TOP_EQSMUP__A 0x1C1001C
+#define VSB_TOP_EQSMUP__W 8
+#define VSB_TOP_EQSMUP__M 0xFF
+#define VSB_TOP_EQSMUP__PRE 0xFF
+#define VSB_TOP_SYSMUXCTRL__A 0x1C1001D
+#define VSB_TOP_SYSMUXCTRL__W 13
+#define VSB_TOP_SYSMUXCTRL__M 0x1FFF
+#define VSB_TOP_SYSMUXCTRL__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_CYLK_STATIC__B 0
+#define VSB_TOP_SYSMUXCTRL_CYLK_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_CYLK_STATIC__M 0x1
+#define VSB_TOP_SYSMUXCTRL_CYLK_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_CYLK_SEL_STATIC__B 1
+#define VSB_TOP_SYSMUXCTRL_CYLK_SEL_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_CYLK_SEL_STATIC__M 0x2
+#define VSB_TOP_SYSMUXCTRL_CYLK_SEL_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_STATIC__B 2
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_STATIC__M 0x4
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_SEL_STATIC__B 3
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_SEL_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_SEL_STATIC__M 0x8
+#define VSB_TOP_SYSMUXCTRL_CTCALDONE_SEL_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_STATIC__B 4
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_STATIC__M 0x10
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_SEL_STATIC__B 5
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_SEL_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_SEL_STATIC__M 0x20
+#define VSB_TOP_SYSMUXCTRL_FRAMELOCK_SEL_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_STATIC__B 6
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_STATIC__M 0x40
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_SEL_STATIC__B 7
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_SEL_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_SEL_STATIC__M 0x80
+#define VSB_TOP_SYSMUXCTRL_FRAMESYNC_SEL_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_STATIC__B 8
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_STATIC__W 4
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_STATIC__M 0xF00
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_STATIC__PRE 0x0
+
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_SEL_STATIC__B 12
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_SEL_STATIC__W 1
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_SEL_STATIC__M 0x1000
+#define VSB_TOP_SYSMUXCTRL_SNROVTH_SEL_STATIC__PRE 0x0
+
+#define VSB_TOP_SNRTH_RCA1__A 0x1C1001E
+#define VSB_TOP_SNRTH_RCA1__W 8
+#define VSB_TOP_SNRTH_RCA1__M 0xFF
+#define VSB_TOP_SNRTH_RCA1__PRE 0x53
+
+#define VSB_TOP_SNRTH_RCA1_DN__B 0
+#define VSB_TOP_SNRTH_RCA1_DN__W 4
+#define VSB_TOP_SNRTH_RCA1_DN__M 0xF
+#define VSB_TOP_SNRTH_RCA1_DN__PRE 0x3
+
+#define VSB_TOP_SNRTH_RCA1_UP__B 4
+#define VSB_TOP_SNRTH_RCA1_UP__W 4
+#define VSB_TOP_SNRTH_RCA1_UP__M 0xF0
+#define VSB_TOP_SNRTH_RCA1_UP__PRE 0x50
+
+#define VSB_TOP_SNRTH_RCA2__A 0x1C1001F
+#define VSB_TOP_SNRTH_RCA2__W 8
+#define VSB_TOP_SNRTH_RCA2__M 0xFF
+#define VSB_TOP_SNRTH_RCA2__PRE 0x75
+
+#define VSB_TOP_SNRTH_RCA2_DN__B 0
+#define VSB_TOP_SNRTH_RCA2_DN__W 4
+#define VSB_TOP_SNRTH_RCA2_DN__M 0xF
+#define VSB_TOP_SNRTH_RCA2_DN__PRE 0x5
+
+#define VSB_TOP_SNRTH_RCA2_UP__B 4
+#define VSB_TOP_SNRTH_RCA2_UP__W 4
+#define VSB_TOP_SNRTH_RCA2_UP__M 0xF0
+#define VSB_TOP_SNRTH_RCA2_UP__PRE 0x70
+
+#define VSB_TOP_SNRTH_DDM1__A 0x1C10020
+#define VSB_TOP_SNRTH_DDM1__W 8
+#define VSB_TOP_SNRTH_DDM1__M 0xFF
+#define VSB_TOP_SNRTH_DDM1__PRE 0xCA
+
+#define VSB_TOP_SNRTH_DDM1_DN__B 0
+#define VSB_TOP_SNRTH_DDM1_DN__W 4
+#define VSB_TOP_SNRTH_DDM1_DN__M 0xF
+#define VSB_TOP_SNRTH_DDM1_DN__PRE 0xA
+
+#define VSB_TOP_SNRTH_DDM1_UP__B 4
+#define VSB_TOP_SNRTH_DDM1_UP__W 4
+#define VSB_TOP_SNRTH_DDM1_UP__M 0xF0
+#define VSB_TOP_SNRTH_DDM1_UP__PRE 0xC0
+
+#define VSB_TOP_SNRTH_DDM2__A 0x1C10021
+#define VSB_TOP_SNRTH_DDM2__W 8
+#define VSB_TOP_SNRTH_DDM2__M 0xFF
+#define VSB_TOP_SNRTH_DDM2__PRE 0xCA
+
+#define VSB_TOP_SNRTH_DDM2_DN__B 0
+#define VSB_TOP_SNRTH_DDM2_DN__W 4
+#define VSB_TOP_SNRTH_DDM2_DN__M 0xF
+#define VSB_TOP_SNRTH_DDM2_DN__PRE 0xA
+
+#define VSB_TOP_SNRTH_DDM2_UP__B 4
+#define VSB_TOP_SNRTH_DDM2_UP__W 4
+#define VSB_TOP_SNRTH_DDM2_UP__M 0xF0
+#define VSB_TOP_SNRTH_DDM2_UP__PRE 0xC0
+
+#define VSB_TOP_SNRTH_PT__A 0x1C10022
+#define VSB_TOP_SNRTH_PT__W 8
+#define VSB_TOP_SNRTH_PT__M 0xFF
+#define VSB_TOP_SNRTH_PT__PRE 0xD8
+
+#define VSB_TOP_SNRTH_PT_DN__B 0
+#define VSB_TOP_SNRTH_PT_DN__W 4
+#define VSB_TOP_SNRTH_PT_DN__M 0xF
+#define VSB_TOP_SNRTH_PT_DN__PRE 0x8
+
+#define VSB_TOP_SNRTH_PT_UP__B 4
+#define VSB_TOP_SNRTH_PT_UP__W 4
+#define VSB_TOP_SNRTH_PT_UP__M 0xF0
+#define VSB_TOP_SNRTH_PT_UP__PRE 0xD0
+
+#define VSB_TOP_CYSMSTATES__A 0x1C10023
+#define VSB_TOP_CYSMSTATES__W 8
+#define VSB_TOP_CYSMSTATES__M 0xFF
+#define VSB_TOP_CYSMSTATES__PRE 0x0
+
+#define VSB_TOP_CYSMSTATES_SYSST__B 0
+#define VSB_TOP_CYSMSTATES_SYSST__W 4
+#define VSB_TOP_CYSMSTATES_SYSST__M 0xF
+#define VSB_TOP_CYSMSTATES_SYSST__PRE 0x0
+
+#define VSB_TOP_CYSMSTATES_EQST__B 4
+#define VSB_TOP_CYSMSTATES_EQST__W 4
+#define VSB_TOP_CYSMSTATES_EQST__M 0xF0
+#define VSB_TOP_CYSMSTATES_EQST__PRE 0x0
+
+#define VSB_TOP_SMALL_NOTCH_CONTROL__A 0x1C10024
+#define VSB_TOP_SMALL_NOTCH_CONTROL__W 8
+#define VSB_TOP_SMALL_NOTCH_CONTROL__M 0xFF
+#define VSB_TOP_SMALL_NOTCH_CONTROL__PRE 0x0
+
+#define VSB_TOP_SMALL_NOTCH_CONTROL_GO__B 0
+#define VSB_TOP_SMALL_NOTCH_CONTROL_GO__W 1
+#define VSB_TOP_SMALL_NOTCH_CONTROL_GO__M 0x1
+#define VSB_TOP_SMALL_NOTCH_CONTROL_GO__PRE 0x0
+
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS1__B 1
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS1__W 1
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS1__M 0x2
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS1__PRE 0x0
+
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS2__B 2
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS2__W 1
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS2__M 0x4
+#define VSB_TOP_SMALL_NOTCH_CONTROL_BYPASS2__PRE 0x0
+
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SPARE__B 3
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SPARE__W 4
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SPARE__M 0x78
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SPARE__PRE 0x0
+
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SOFT_RESET__B 7
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SOFT_RESET__W 1
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SOFT_RESET__M 0x80
+#define VSB_TOP_SMALL_NOTCH_CONTROL_SOFT_RESET__PRE 0x0
+
+#define VSB_TOP_TAPREADCYC__A 0x1C10025
+#define VSB_TOP_TAPREADCYC__W 9
+#define VSB_TOP_TAPREADCYC__M 0x1FF
+#define VSB_TOP_TAPREADCYC__PRE 0x1
+
+#define VSB_TOP_VALIDPKLVL__A 0x1C10026
+#define VSB_TOP_VALIDPKLVL__W 13
+#define VSB_TOP_VALIDPKLVL__M 0x1FFF
+#define VSB_TOP_VALIDPKLVL__PRE 0x100
+
+#define VSB_TOP_CENTROID_FINE_DELAY__A 0x1C10027
+#define VSB_TOP_CENTROID_FINE_DELAY__W 10
+#define VSB_TOP_CENTROID_FINE_DELAY__M 0x3FF
+#define VSB_TOP_CENTROID_FINE_DELAY__PRE 0xFF
+
+#define VSB_TOP_CENTROID_SMACH_DELAY__A 0x1C10028
+#define VSB_TOP_CENTROID_SMACH_DELAY__W 10
+#define VSB_TOP_CENTROID_SMACH_DELAY__M 0x3FF
+#define VSB_TOP_CENTROID_SMACH_DELAY__PRE 0x1FF
+
+#define VSB_TOP_SNR__A 0x1C10029
+#define VSB_TOP_SNR__W 14
+#define VSB_TOP_SNR__M 0x3FFF
+#define VSB_TOP_SNR__PRE 0x0
+#define VSB_TOP_LOCKSTATUS__A 0x1C1002A
+#define VSB_TOP_LOCKSTATUS__W 7
+#define VSB_TOP_LOCKSTATUS__M 0x7F
+#define VSB_TOP_LOCKSTATUS__PRE 0x0
+
+#define VSB_TOP_LOCKSTATUS_VSBMODE__B 0
+#define VSB_TOP_LOCKSTATUS_VSBMODE__W 4
+#define VSB_TOP_LOCKSTATUS_VSBMODE__M 0xF
+#define VSB_TOP_LOCKSTATUS_VSBMODE__PRE 0x0
+
+#define VSB_TOP_LOCKSTATUS_FRMLOCK__B 4
+#define VSB_TOP_LOCKSTATUS_FRMLOCK__W 1
+#define VSB_TOP_LOCKSTATUS_FRMLOCK__M 0x10
+#define VSB_TOP_LOCKSTATUS_FRMLOCK__PRE 0x0
+
+#define VSB_TOP_LOCKSTATUS_CYLOCK__B 5
+#define VSB_TOP_LOCKSTATUS_CYLOCK__W 1
+#define VSB_TOP_LOCKSTATUS_CYLOCK__M 0x20
+#define VSB_TOP_LOCKSTATUS_CYLOCK__PRE 0x0
+
+#define VSB_TOP_LOCKSTATUS_DDMON__B 6
+#define VSB_TOP_LOCKSTATUS_DDMON__W 1
+#define VSB_TOP_LOCKSTATUS_DDMON__M 0x40
+#define VSB_TOP_LOCKSTATUS_DDMON__PRE 0x0
+
+#define VSB_TOP_CTST__A 0x1C1002B
+#define VSB_TOP_CTST__W 4
+#define VSB_TOP_CTST__M 0xF
+#define VSB_TOP_CTST__PRE 0x0
+#define VSB_TOP_EQSMRSTCTRL__A 0x1C1002C
+#define VSB_TOP_EQSMRSTCTRL__W 7
+#define VSB_TOP_EQSMRSTCTRL__M 0x7F
+#define VSB_TOP_EQSMRSTCTRL__PRE 0x0
+
+#define VSB_TOP_EQSMRSTCTRL_RCAON__B 0
+#define VSB_TOP_EQSMRSTCTRL_RCAON__W 1
+#define VSB_TOP_EQSMRSTCTRL_RCAON__M 0x1
+#define VSB_TOP_EQSMRSTCTRL_RCAON__PRE 0x0
+
+#define VSB_TOP_EQSMRSTCTRL_DFEON__B 1
+#define VSB_TOP_EQSMRSTCTRL_DFEON__W 1
+#define VSB_TOP_EQSMRSTCTRL_DFEON__M 0x2
+#define VSB_TOP_EQSMRSTCTRL_DFEON__PRE 0x0
+
+#define VSB_TOP_EQSMRSTCTRL_DDMEN1__B 2
+#define VSB_TOP_EQSMRSTCTRL_DDMEN1__W 1
+#define VSB_TOP_EQSMRSTCTRL_DDMEN1__M 0x4
+#define VSB_TOP_EQSMRSTCTRL_DDMEN1__PRE 0x0
+
+#define VSB_TOP_EQSMRSTCTRL_DDMEN2__B 3
+#define VSB_TOP_EQSMRSTCTRL_DDMEN2__W 1
+#define VSB_TOP_EQSMRSTCTRL_DDMEN2__M 0x8
+#define VSB_TOP_EQSMRSTCTRL_DDMEN2__PRE 0x0
+
+#define VSB_TOP_EQSMRSTCTRL_DIGIAGCON__B 4
+#define VSB_TOP_EQSMRSTCTRL_DIGIAGCON__W 1
+#define VSB_TOP_EQSMRSTCTRL_DIGIAGCON__M 0x10
+#define VSB_TOP_EQSMRSTCTRL_DIGIAGCON__PRE 0x0
+
+#define VSB_TOP_EQSMRSTCTRL_PARAINITEN__B 5
+#define VSB_TOP_EQSMRSTCTRL_PARAINITEN__W 1
+#define VSB_TOP_EQSMRSTCTRL_PARAINITEN__M 0x20
+#define VSB_TOP_EQSMRSTCTRL_PARAINITEN__PRE 0x0
+
+#define VSB_TOP_EQSMRSTCTRL_TIMEOUTFRMCNTEN__B 6
+#define VSB_TOP_EQSMRSTCTRL_TIMEOUTFRMCNTEN__W 1
+#define VSB_TOP_EQSMRSTCTRL_TIMEOUTFRMCNTEN__M 0x40
+#define VSB_TOP_EQSMRSTCTRL_TIMEOUTFRMCNTEN__PRE 0x0
+
+#define VSB_TOP_EQSMTRNCTRL__A 0x1C1002D
+#define VSB_TOP_EQSMTRNCTRL__W 7
+#define VSB_TOP_EQSMTRNCTRL__M 0x7F
+#define VSB_TOP_EQSMTRNCTRL__PRE 0x40
+
+#define VSB_TOP_EQSMTRNCTRL_RCAON__B 0
+#define VSB_TOP_EQSMTRNCTRL_RCAON__W 1
+#define VSB_TOP_EQSMTRNCTRL_RCAON__M 0x1
+#define VSB_TOP_EQSMTRNCTRL_RCAON__PRE 0x0
+
+#define VSB_TOP_EQSMTRNCTRL_DFEON__B 1
+#define VSB_TOP_EQSMTRNCTRL_DFEON__W 1
+#define VSB_TOP_EQSMTRNCTRL_DFEON__M 0x2
+#define VSB_TOP_EQSMTRNCTRL_DFEON__PRE 0x0
+
+#define VSB_TOP_EQSMTRNCTRL_DDMEN1__B 2
+#define VSB_TOP_EQSMTRNCTRL_DDMEN1__W 1
+#define VSB_TOP_EQSMTRNCTRL_DDMEN1__M 0x4
+#define VSB_TOP_EQSMTRNCTRL_DDMEN1__PRE 0x0
+
+#define VSB_TOP_EQSMTRNCTRL_DDMEN2__B 3
+#define VSB_TOP_EQSMTRNCTRL_DDMEN2__W 1
+#define VSB_TOP_EQSMTRNCTRL_DDMEN2__M 0x8
+#define VSB_TOP_EQSMTRNCTRL_DDMEN2__PRE 0x0
+
+#define VSB_TOP_EQSMTRNCTRL_DIGIAGCON__B 4
+#define VSB_TOP_EQSMTRNCTRL_DIGIAGCON__W 1
+#define VSB_TOP_EQSMTRNCTRL_DIGIAGCON__M 0x10
+#define VSB_TOP_EQSMTRNCTRL_DIGIAGCON__PRE 0x0
+
+#define VSB_TOP_EQSMTRNCTRL_PARAINITEN__B 5
+#define VSB_TOP_EQSMTRNCTRL_PARAINITEN__W 1
+#define VSB_TOP_EQSMTRNCTRL_PARAINITEN__M 0x20
+#define VSB_TOP_EQSMTRNCTRL_PARAINITEN__PRE 0x0
+
+#define VSB_TOP_EQSMTRNCTRL_TIMEOUTFRMCNTEN__B 6
+#define VSB_TOP_EQSMTRNCTRL_TIMEOUTFRMCNTEN__W 1
+#define VSB_TOP_EQSMTRNCTRL_TIMEOUTFRMCNTEN__M 0x40
+#define VSB_TOP_EQSMTRNCTRL_TIMEOUTFRMCNTEN__PRE 0x40
+
+#define VSB_TOP_EQSMRCA1CTRL__A 0x1C1002E
+#define VSB_TOP_EQSMRCA1CTRL__W 7
+#define VSB_TOP_EQSMRCA1CTRL__M 0x7F
+#define VSB_TOP_EQSMRCA1CTRL__PRE 0x1
+
+#define VSB_TOP_EQSMRCA1CTRL_RCAON__B 0
+#define VSB_TOP_EQSMRCA1CTRL_RCAON__W 1
+#define VSB_TOP_EQSMRCA1CTRL_RCAON__M 0x1
+#define VSB_TOP_EQSMRCA1CTRL_RCAON__PRE 0x1
+
+#define VSB_TOP_EQSMRCA1CTRL_DFEON__B 1
+#define VSB_TOP_EQSMRCA1CTRL_DFEON__W 1
+#define VSB_TOP_EQSMRCA1CTRL_DFEON__M 0x2
+#define VSB_TOP_EQSMRCA1CTRL_DFEON__PRE 0x0
+
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN1__B 2
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN1__W 1
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN1__M 0x4
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN1__PRE 0x0
+
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN2__B 3
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN2__W 1
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN2__M 0x8
+#define VSB_TOP_EQSMRCA1CTRL_DDMEN2__PRE 0x0
+
+#define VSB_TOP_EQSMRCA1CTRL_DIGIAGCON__B 4
+#define VSB_TOP_EQSMRCA1CTRL_DIGIAGCON__W 1
+#define VSB_TOP_EQSMRCA1CTRL_DIGIAGCON__M 0x10
+#define VSB_TOP_EQSMRCA1CTRL_DIGIAGCON__PRE 0x0
+
+#define VSB_TOP_EQSMRCA1CTRL_PARAINITEN__B 5
+#define VSB_TOP_EQSMRCA1CTRL_PARAINITEN__W 1
+#define VSB_TOP_EQSMRCA1CTRL_PARAINITEN__M 0x20
+#define VSB_TOP_EQSMRCA1CTRL_PARAINITEN__PRE 0x0
+
+#define VSB_TOP_EQSMRCA1CTRL_TIMEOUTFRMCNTEN__B 6
+#define VSB_TOP_EQSMRCA1CTRL_TIMEOUTFRMCNTEN__W 1
+#define VSB_TOP_EQSMRCA1CTRL_TIMEOUTFRMCNTEN__M 0x40
+#define VSB_TOP_EQSMRCA1CTRL_TIMEOUTFRMCNTEN__PRE 0x0
+
+#define VSB_TOP_EQSMRCA2CTRL__A 0x1C1002F
+#define VSB_TOP_EQSMRCA2CTRL__W 7
+#define VSB_TOP_EQSMRCA2CTRL__M 0x7F
+#define VSB_TOP_EQSMRCA2CTRL__PRE 0x3
+
+#define VSB_TOP_EQSMRCA2CTRL_RCAON__B 0
+#define VSB_TOP_EQSMRCA2CTRL_RCAON__W 1
+#define VSB_TOP_EQSMRCA2CTRL_RCAON__M 0x1
+#define VSB_TOP_EQSMRCA2CTRL_RCAON__PRE 0x1
+
+#define VSB_TOP_EQSMRCA2CTRL_DFEON__B 1
+#define VSB_TOP_EQSMRCA2CTRL_DFEON__W 1
+#define VSB_TOP_EQSMRCA2CTRL_DFEON__M 0x2
+#define VSB_TOP_EQSMRCA2CTRL_DFEON__PRE 0x2
+
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN1__B 2
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN1__W 1
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN1__M 0x4
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN1__PRE 0x0
+
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN2__B 3
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN2__W 1
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN2__M 0x8
+#define VSB_TOP_EQSMRCA2CTRL_DDMEN2__PRE 0x0
+
+#define VSB_TOP_EQSMRCA2CTRL_DIGIAGCON__B 4
+#define VSB_TOP_EQSMRCA2CTRL_DIGIAGCON__W 1
+#define VSB_TOP_EQSMRCA2CTRL_DIGIAGCON__M 0x10
+#define VSB_TOP_EQSMRCA2CTRL_DIGIAGCON__PRE 0x0
+
+#define VSB_TOP_EQSMRCA2CTRL_PARAINITEN__B 5
+#define VSB_TOP_EQSMRCA2CTRL_PARAINITEN__W 1
+#define VSB_TOP_EQSMRCA2CTRL_PARAINITEN__M 0x20
+#define VSB_TOP_EQSMRCA2CTRL_PARAINITEN__PRE 0x0
+
+#define VSB_TOP_EQSMRCA2CTRL_TIMEOUTFRMCNTEN__B 6
+#define VSB_TOP_EQSMRCA2CTRL_TIMEOUTFRMCNTEN__W 1
+#define VSB_TOP_EQSMRCA2CTRL_TIMEOUTFRMCNTEN__M 0x40
+#define VSB_TOP_EQSMRCA2CTRL_TIMEOUTFRMCNTEN__PRE 0x0
+
+#define VSB_TOP_EQSMDDM1CTRL__A 0x1C10030
+#define VSB_TOP_EQSMDDM1CTRL__W 7
+#define VSB_TOP_EQSMDDM1CTRL__M 0x7F
+#define VSB_TOP_EQSMDDM1CTRL__PRE 0x6
+
+#define VSB_TOP_EQSMDDM1CTRL_RCAON__B 0
+#define VSB_TOP_EQSMDDM1CTRL_RCAON__W 1
+#define VSB_TOP_EQSMDDM1CTRL_RCAON__M 0x1
+#define VSB_TOP_EQSMDDM1CTRL_RCAON__PRE 0x0
+
+#define VSB_TOP_EQSMDDM1CTRL_DFEON__B 1
+#define VSB_TOP_EQSMDDM1CTRL_DFEON__W 1
+#define VSB_TOP_EQSMDDM1CTRL_DFEON__M 0x2
+#define VSB_TOP_EQSMDDM1CTRL_DFEON__PRE 0x2
+
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN1__B 2
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN1__W 1
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN1__M 0x4
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN1__PRE 0x4
+
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN2__B 3
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN2__W 1
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN2__M 0x8
+#define VSB_TOP_EQSMDDM1CTRL_DDMEN2__PRE 0x0
+
+#define VSB_TOP_EQSMDDM1CTRL_DIGIAGCON__B 4
+#define VSB_TOP_EQSMDDM1CTRL_DIGIAGCON__W 1
+#define VSB_TOP_EQSMDDM1CTRL_DIGIAGCON__M 0x10
+#define VSB_TOP_EQSMDDM1CTRL_DIGIAGCON__PRE 0x0
+
+#define VSB_TOP_EQSMDDM1CTRL_PARAINITEN__B 5
+#define VSB_TOP_EQSMDDM1CTRL_PARAINITEN__W 1
+#define VSB_TOP_EQSMDDM1CTRL_PARAINITEN__M 0x20
+#define VSB_TOP_EQSMDDM1CTRL_PARAINITEN__PRE 0x0
+
+#define VSB_TOP_EQSMDDM1CTRL_TIMEOUTFRMCNTEN__B 6
+#define VSB_TOP_EQSMDDM1CTRL_TIMEOUTFRMCNTEN__W 1
+#define VSB_TOP_EQSMDDM1CTRL_TIMEOUTFRMCNTEN__M 0x40
+#define VSB_TOP_EQSMDDM1CTRL_TIMEOUTFRMCNTEN__PRE 0x0
+
+#define VSB_TOP_EQSMDDM2CTRL__A 0x1C10031
+#define VSB_TOP_EQSMDDM2CTRL__W 7
+#define VSB_TOP_EQSMDDM2CTRL__M 0x7F
+#define VSB_TOP_EQSMDDM2CTRL__PRE 0x1E
+
+#define VSB_TOP_EQSMDDM2CTRL_RCAON__B 0
+#define VSB_TOP_EQSMDDM2CTRL_RCAON__W 1
+#define VSB_TOP_EQSMDDM2CTRL_RCAON__M 0x1
+#define VSB_TOP_EQSMDDM2CTRL_RCAON__PRE 0x0
+
+#define VSB_TOP_EQSMDDM2CTRL_DFEON__B 1
+#define VSB_TOP_EQSMDDM2CTRL_DFEON__W 1
+#define VSB_TOP_EQSMDDM2CTRL_DFEON__M 0x2
+#define VSB_TOP_EQSMDDM2CTRL_DFEON__PRE 0x2
+
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN1__B 2
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN1__W 1
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN1__M 0x4
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN1__PRE 0x4
+
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN2__B 3
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN2__W 1
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN2__M 0x8
+#define VSB_TOP_EQSMDDM2CTRL_DDMEN2__PRE 0x8
+
+#define VSB_TOP_EQSMDDM2CTRL_DIGIAGCON__B 4
+#define VSB_TOP_EQSMDDM2CTRL_DIGIAGCON__W 1
+#define VSB_TOP_EQSMDDM2CTRL_DIGIAGCON__M 0x10
+#define VSB_TOP_EQSMDDM2CTRL_DIGIAGCON__PRE 0x10
+
+#define VSB_TOP_EQSMDDM2CTRL_PARAINITEN__B 5
+#define VSB_TOP_EQSMDDM2CTRL_PARAINITEN__W 1
+#define VSB_TOP_EQSMDDM2CTRL_PARAINITEN__M 0x20
+#define VSB_TOP_EQSMDDM2CTRL_PARAINITEN__PRE 0x0
+
+#define VSB_TOP_EQSMDDM2CTRL_TIMEOUTFRMCNTEN__B 6
+#define VSB_TOP_EQSMDDM2CTRL_TIMEOUTFRMCNTEN__W 1
+#define VSB_TOP_EQSMDDM2CTRL_TIMEOUTFRMCNTEN__M 0x40
+#define VSB_TOP_EQSMDDM2CTRL_TIMEOUTFRMCNTEN__PRE 0x0
+
+#define VSB_TOP_SYSSMRSTCTRL__A 0x1C10032
+#define VSB_TOP_SYSSMRSTCTRL__W 11
+#define VSB_TOP_SYSSMRSTCTRL__M 0x7FF
+#define VSB_TOP_SYSSMRSTCTRL__PRE 0x7F9
+
+#define VSB_TOP_SYSSMRSTCTRL_RSTCTCAL__B 0
+#define VSB_TOP_SYSSMRSTCTRL_RSTCTCAL__W 1
+#define VSB_TOP_SYSSMRSTCTRL_RSTCTCAL__M 0x1
+#define VSB_TOP_SYSSMRSTCTRL_RSTCTCAL__PRE 0x1
+
+#define VSB_TOP_SYSSMRSTCTRL_CTCALEN__B 1
+#define VSB_TOP_SYSSMRSTCTRL_CTCALEN__W 1
+#define VSB_TOP_SYSSMRSTCTRL_CTCALEN__M 0x2
+#define VSB_TOP_SYSSMRSTCTRL_CTCALEN__PRE 0x0
+
+#define VSB_TOP_SYSSMRSTCTRL_STARTTRN__B 2
+#define VSB_TOP_SYSSMRSTCTRL_STARTTRN__W 1
+#define VSB_TOP_SYSSMRSTCTRL_STARTTRN__M 0x4
+#define VSB_TOP_SYSSMRSTCTRL_STARTTRN__PRE 0x0
+
+#define VSB_TOP_SYSSMRSTCTRL_RSTFRMSYNCDET__B 3
+#define VSB_TOP_SYSSMRSTCTRL_RSTFRMSYNCDET__W 1
+#define VSB_TOP_SYSSMRSTCTRL_RSTFRMSYNCDET__M 0x8
+#define VSB_TOP_SYSSMRSTCTRL_RSTFRMSYNCDET__PRE 0x8
+
+#define VSB_TOP_SYSSMRSTCTRL_RSTCYDET__B 4
+#define VSB_TOP_SYSSMRSTCTRL_RSTCYDET__W 1
+#define VSB_TOP_SYSSMRSTCTRL_RSTCYDET__M 0x10
+#define VSB_TOP_SYSSMRSTCTRL_RSTCYDET__PRE 0x10
+
+#define VSB_TOP_SYSSMRSTCTRL_RSTDCRMV__B 5
+#define VSB_TOP_SYSSMRSTCTRL_RSTDCRMV__W 1
+#define VSB_TOP_SYSSMRSTCTRL_RSTDCRMV__M 0x20
+#define VSB_TOP_SYSSMRSTCTRL_RSTDCRMV__PRE 0x20
+
+#define VSB_TOP_SYSSMRSTCTRL_RSTEQSIG__B 6
+#define VSB_TOP_SYSSMRSTCTRL_RSTEQSIG__W 1
+#define VSB_TOP_SYSSMRSTCTRL_RSTEQSIG__M 0x40
+#define VSB_TOP_SYSSMRSTCTRL_RSTEQSIG__PRE 0x40
+
+#define VSB_TOP_SYSSMRSTCTRL_CKFRZ__B 7
+#define VSB_TOP_SYSSMRSTCTRL_CKFRZ__W 1
+#define VSB_TOP_SYSSMRSTCTRL_CKFRZ__M 0x80
+#define VSB_TOP_SYSSMRSTCTRL_CKFRZ__PRE 0x80
+
+#define VSB_TOP_SYSSMRSTCTRL_CKBWSW__B 8
+#define VSB_TOP_SYSSMRSTCTRL_CKBWSW__W 1
+#define VSB_TOP_SYSSMRSTCTRL_CKBWSW__M 0x100
+#define VSB_TOP_SYSSMRSTCTRL_CKBWSW__PRE 0x100
+
+#define VSB_TOP_SYSSMRSTCTRL_NCOBWSW__B 9
+#define VSB_TOP_SYSSMRSTCTRL_NCOBWSW__W 1
+#define VSB_TOP_SYSSMRSTCTRL_NCOBWSW__M 0x200
+#define VSB_TOP_SYSSMRSTCTRL_NCOBWSW__PRE 0x200
+
+#define VSB_TOP_SYSSMRSTCTRL_NCOTIMEOUTCNTEN__B 10
+#define VSB_TOP_SYSSMRSTCTRL_NCOTIMEOUTCNTEN__W 1
+#define VSB_TOP_SYSSMRSTCTRL_NCOTIMEOUTCNTEN__M 0x400
+#define VSB_TOP_SYSSMRSTCTRL_NCOTIMEOUTCNTEN__PRE 0x400
+
+#define VSB_TOP_SYSSMCYCTRL__A 0x1C10033
+#define VSB_TOP_SYSSMCYCTRL__W 11
+#define VSB_TOP_SYSSMCYCTRL__M 0x7FF
+#define VSB_TOP_SYSSMCYCTRL__PRE 0x4E9
+
+#define VSB_TOP_SYSSMCYCTRL_RSTCTCAL__B 0
+#define VSB_TOP_SYSSMCYCTRL_RSTCTCAL__W 1
+#define VSB_TOP_SYSSMCYCTRL_RSTCTCAL__M 0x1
+#define VSB_TOP_SYSSMCYCTRL_RSTCTCAL__PRE 0x1
+
+#define VSB_TOP_SYSSMCYCTRL_CTCALEN__B 1
+#define VSB_TOP_SYSSMCYCTRL_CTCALEN__W 1
+#define VSB_TOP_SYSSMCYCTRL_CTCALEN__M 0x2
+#define VSB_TOP_SYSSMCYCTRL_CTCALEN__PRE 0x0
+
+#define VSB_TOP_SYSSMCYCTRL_STARTTRN__B 2
+#define VSB_TOP_SYSSMCYCTRL_STARTTRN__W 1
+#define VSB_TOP_SYSSMCYCTRL_STARTTRN__M 0x4
+#define VSB_TOP_SYSSMCYCTRL_STARTTRN__PRE 0x0
+
+#define VSB_TOP_SYSSMCYCTRL_RSTFRMSYNCDET__B 3
+#define VSB_TOP_SYSSMCYCTRL_RSTFRMSYNCDET__W 1
+#define VSB_TOP_SYSSMCYCTRL_RSTFRMSYNCDET__M 0x8
+#define VSB_TOP_SYSSMCYCTRL_RSTFRMSYNCDET__PRE 0x8
+
+#define VSB_TOP_SYSSMCYCTRL_RSTCYDET__B 4
+#define VSB_TOP_SYSSMCYCTRL_RSTCYDET__W 1
+#define VSB_TOP_SYSSMCYCTRL_RSTCYDET__M 0x10
+#define VSB_TOP_SYSSMCYCTRL_RSTCYDET__PRE 0x0
+
+#define VSB_TOP_SYSSMCYCTRL_RSTDCRMV__B 5
+#define VSB_TOP_SYSSMCYCTRL_RSTDCRMV__W 1
+#define VSB_TOP_SYSSMCYCTRL_RSTDCRMV__M 0x20
+#define VSB_TOP_SYSSMCYCTRL_RSTDCRMV__PRE 0x20
+
+#define VSB_TOP_SYSSMCYCTRL_RSTEQSIG__B 6
+#define VSB_TOP_SYSSMCYCTRL_RSTEQSIG__W 1
+#define VSB_TOP_SYSSMCYCTRL_RSTEQSIG__M 0x40
+#define VSB_TOP_SYSSMCYCTRL_RSTEQSIG__PRE 0x40
+
+#define VSB_TOP_SYSSMCYCTRL_CKFRZ__B 7
+#define VSB_TOP_SYSSMCYCTRL_CKFRZ__W 1
+#define VSB_TOP_SYSSMCYCTRL_CKFRZ__M 0x80
+#define VSB_TOP_SYSSMCYCTRL_CKFRZ__PRE 0x80
+
+#define VSB_TOP_SYSSMCYCTRL_CKBWSW__B 8
+#define VSB_TOP_SYSSMCYCTRL_CKBWSW__W 1
+#define VSB_TOP_SYSSMCYCTRL_CKBWSW__M 0x100
+#define VSB_TOP_SYSSMCYCTRL_CKBWSW__PRE 0x0
+
+#define VSB_TOP_SYSSMCYCTRL_NCOBWSW__B 9
+#define VSB_TOP_SYSSMCYCTRL_NCOBWSW__W 1
+#define VSB_TOP_SYSSMCYCTRL_NCOBWSW__M 0x200
+#define VSB_TOP_SYSSMCYCTRL_NCOBWSW__PRE 0x0
+
+#define VSB_TOP_SYSSMCYCTRL_NCOTIMEOUTCNTEN__B 10
+#define VSB_TOP_SYSSMCYCTRL_NCOTIMEOUTCNTEN__W 1
+#define VSB_TOP_SYSSMCYCTRL_NCOTIMEOUTCNTEN__M 0x400
+#define VSB_TOP_SYSSMCYCTRL_NCOTIMEOUTCNTEN__PRE 0x400
+
+#define VSB_TOP_SYSSMTRNCTRL__A 0x1C10034
+#define VSB_TOP_SYSSMTRNCTRL__W 11
+#define VSB_TOP_SYSSMTRNCTRL__M 0x7FF
+#define VSB_TOP_SYSSMTRNCTRL__PRE 0x204
+
+#define VSB_TOP_SYSSMTRNCTRL_RSTCTCAL__B 0
+#define VSB_TOP_SYSSMTRNCTRL_RSTCTCAL__W 1
+#define VSB_TOP_SYSSMTRNCTRL_RSTCTCAL__M 0x1
+#define VSB_TOP_SYSSMTRNCTRL_RSTCTCAL__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_CTCALEN__B 1
+#define VSB_TOP_SYSSMTRNCTRL_CTCALEN__W 1
+#define VSB_TOP_SYSSMTRNCTRL_CTCALEN__M 0x2
+#define VSB_TOP_SYSSMTRNCTRL_CTCALEN__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_STARTTRN__B 2
+#define VSB_TOP_SYSSMTRNCTRL_STARTTRN__W 1
+#define VSB_TOP_SYSSMTRNCTRL_STARTTRN__M 0x4
+#define VSB_TOP_SYSSMTRNCTRL_STARTTRN__PRE 0x4
+
+#define VSB_TOP_SYSSMTRNCTRL_RSTFRMSYNCDET__B 3
+#define VSB_TOP_SYSSMTRNCTRL_RSTFRMSYNCDET__W 1
+#define VSB_TOP_SYSSMTRNCTRL_RSTFRMSYNCDET__M 0x8
+#define VSB_TOP_SYSSMTRNCTRL_RSTFRMSYNCDET__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_RSTCYDET__B 4
+#define VSB_TOP_SYSSMTRNCTRL_RSTCYDET__W 1
+#define VSB_TOP_SYSSMTRNCTRL_RSTCYDET__M 0x10
+#define VSB_TOP_SYSSMTRNCTRL_RSTCYDET__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_RSTDCRMV__B 5
+#define VSB_TOP_SYSSMTRNCTRL_RSTDCRMV__W 1
+#define VSB_TOP_SYSSMTRNCTRL_RSTDCRMV__M 0x20
+#define VSB_TOP_SYSSMTRNCTRL_RSTDCRMV__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_RSTEQSIG__B 6
+#define VSB_TOP_SYSSMTRNCTRL_RSTEQSIG__W 1
+#define VSB_TOP_SYSSMTRNCTRL_RSTEQSIG__M 0x40
+#define VSB_TOP_SYSSMTRNCTRL_RSTEQSIG__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_CKFRZ__B 7
+#define VSB_TOP_SYSSMTRNCTRL_CKFRZ__W 1
+#define VSB_TOP_SYSSMTRNCTRL_CKFRZ__M 0x80
+#define VSB_TOP_SYSSMTRNCTRL_CKFRZ__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_CKBWSW__B 8
+#define VSB_TOP_SYSSMTRNCTRL_CKBWSW__W 1
+#define VSB_TOP_SYSSMTRNCTRL_CKBWSW__M 0x100
+#define VSB_TOP_SYSSMTRNCTRL_CKBWSW__PRE 0x0
+
+#define VSB_TOP_SYSSMTRNCTRL_NCOBWSW__B 9
+#define VSB_TOP_SYSSMTRNCTRL_NCOBWSW__W 1
+#define VSB_TOP_SYSSMTRNCTRL_NCOBWSW__M 0x200
+#define VSB_TOP_SYSSMTRNCTRL_NCOBWSW__PRE 0x200
+
+#define VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__B 10
+#define VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__W 1
+#define VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__M 0x400
+#define VSB_TOP_SYSSMTRNCTRL_NCOTIMEOUTCNTEN__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL__A 0x1C10035
+#define VSB_TOP_SYSSMEQCTRL__W 11
+#define VSB_TOP_SYSSMEQCTRL__M 0x7FF
+#define VSB_TOP_SYSSMEQCTRL__PRE 0x304
+
+#define VSB_TOP_SYSSMEQCTRL_RSTCTCAL__B 0
+#define VSB_TOP_SYSSMEQCTRL_RSTCTCAL__W 1
+#define VSB_TOP_SYSSMEQCTRL_RSTCTCAL__M 0x1
+#define VSB_TOP_SYSSMEQCTRL_RSTCTCAL__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL_CTCALEN__B 1
+#define VSB_TOP_SYSSMEQCTRL_CTCALEN__W 1
+#define VSB_TOP_SYSSMEQCTRL_CTCALEN__M 0x2
+#define VSB_TOP_SYSSMEQCTRL_CTCALEN__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL_STARTTRN__B 2
+#define VSB_TOP_SYSSMEQCTRL_STARTTRN__W 1
+#define VSB_TOP_SYSSMEQCTRL_STARTTRN__M 0x4
+#define VSB_TOP_SYSSMEQCTRL_STARTTRN__PRE 0x4
+
+#define VSB_TOP_SYSSMEQCTRL_RSTFRMSYNCDET__B 3
+#define VSB_TOP_SYSSMEQCTRL_RSTFRMSYNCDET__W 1
+#define VSB_TOP_SYSSMEQCTRL_RSTFRMSYNCDET__M 0x8
+#define VSB_TOP_SYSSMEQCTRL_RSTFRMSYNCDET__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL_RSTCYDET__B 4
+#define VSB_TOP_SYSSMEQCTRL_RSTCYDET__W 1
+#define VSB_TOP_SYSSMEQCTRL_RSTCYDET__M 0x10
+#define VSB_TOP_SYSSMEQCTRL_RSTCYDET__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL_RSTDCRMV__B 5
+#define VSB_TOP_SYSSMEQCTRL_RSTDCRMV__W 1
+#define VSB_TOP_SYSSMEQCTRL_RSTDCRMV__M 0x20
+#define VSB_TOP_SYSSMEQCTRL_RSTDCRMV__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL_RSTEQSIG__B 6
+#define VSB_TOP_SYSSMEQCTRL_RSTEQSIG__W 1
+#define VSB_TOP_SYSSMEQCTRL_RSTEQSIG__M 0x40
+#define VSB_TOP_SYSSMEQCTRL_RSTEQSIG__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL_CKFRZ__B 7
+#define VSB_TOP_SYSSMEQCTRL_CKFRZ__W 1
+#define VSB_TOP_SYSSMEQCTRL_CKFRZ__M 0x80
+#define VSB_TOP_SYSSMEQCTRL_CKFRZ__PRE 0x0
+
+#define VSB_TOP_SYSSMEQCTRL_CKBWSW__B 8
+#define VSB_TOP_SYSSMEQCTRL_CKBWSW__W 1
+#define VSB_TOP_SYSSMEQCTRL_CKBWSW__M 0x100
+#define VSB_TOP_SYSSMEQCTRL_CKBWSW__PRE 0x100
+
+#define VSB_TOP_SYSSMEQCTRL_NCOBWSW__B 9
+#define VSB_TOP_SYSSMEQCTRL_NCOBWSW__W 1
+#define VSB_TOP_SYSSMEQCTRL_NCOBWSW__M 0x200
+#define VSB_TOP_SYSSMEQCTRL_NCOBWSW__PRE 0x200
+
+#define VSB_TOP_SYSSMEQCTRL_NCOTIMEOUTCNTEN__B 10
+#define VSB_TOP_SYSSMEQCTRL_NCOTIMEOUTCNTEN__W 1
+#define VSB_TOP_SYSSMEQCTRL_NCOTIMEOUTCNTEN__M 0x400
+#define VSB_TOP_SYSSMEQCTRL_NCOTIMEOUTCNTEN__PRE 0x0
+
+#define VSB_TOP_SYSSMAGCCTRL__A 0x1C10036
+#define VSB_TOP_SYSSMAGCCTRL__W 11
+#define VSB_TOP_SYSSMAGCCTRL__M 0x7FF
+#define VSB_TOP_SYSSMAGCCTRL__PRE 0xF9
+
+#define VSB_TOP_SYSSMAGCCTRL_RSTCTCAL__B 0
+#define VSB_TOP_SYSSMAGCCTRL_RSTCTCAL__W 1
+#define VSB_TOP_SYSSMAGCCTRL_RSTCTCAL__M 0x1
+#define VSB_TOP_SYSSMAGCCTRL_RSTCTCAL__PRE 0x1
+
+#define VSB_TOP_SYSSMAGCCTRL_CTCALEN__B 1
+#define VSB_TOP_SYSSMAGCCTRL_CTCALEN__W 1
+#define VSB_TOP_SYSSMAGCCTRL_CTCALEN__M 0x2
+#define VSB_TOP_SYSSMAGCCTRL_CTCALEN__PRE 0x0
+
+#define VSB_TOP_SYSSMAGCCTRL_STARTTRN__B 2
+#define VSB_TOP_SYSSMAGCCTRL_STARTTRN__W 1
+#define VSB_TOP_SYSSMAGCCTRL_STARTTRN__M 0x4
+#define VSB_TOP_SYSSMAGCCTRL_STARTTRN__PRE 0x0
+
+#define VSB_TOP_SYSSMAGCCTRL_RSTFRMSYNCDET__B 3
+#define VSB_TOP_SYSSMAGCCTRL_RSTFRMSYNCDET__W 1
+#define VSB_TOP_SYSSMAGCCTRL_RSTFRMSYNCDET__M 0x8
+#define VSB_TOP_SYSSMAGCCTRL_RSTFRMSYNCDET__PRE 0x8
+
+#define VSB_TOP_SYSSMAGCCTRL_RSTCYDET__B 4
+#define VSB_TOP_SYSSMAGCCTRL_RSTCYDET__W 1
+#define VSB_TOP_SYSSMAGCCTRL_RSTCYDET__M 0x10
+#define VSB_TOP_SYSSMAGCCTRL_RSTCYDET__PRE 0x10
+
+#define VSB_TOP_SYSSMAGCCTRL_RSTDCRMV__B 5
+#define VSB_TOP_SYSSMAGCCTRL_RSTDCRMV__W 1
+#define VSB_TOP_SYSSMAGCCTRL_RSTDCRMV__M 0x20
+#define VSB_TOP_SYSSMAGCCTRL_RSTDCRMV__PRE 0x20
+
+#define VSB_TOP_SYSSMAGCCTRL_RSTEQSIG__B 6
+#define VSB_TOP_SYSSMAGCCTRL_RSTEQSIG__W 1
+#define VSB_TOP_SYSSMAGCCTRL_RSTEQSIG__M 0x40
+#define VSB_TOP_SYSSMAGCCTRL_RSTEQSIG__PRE 0x40
+
+#define VSB_TOP_SYSSMAGCCTRL_CKFRZ__B 7
+#define VSB_TOP_SYSSMAGCCTRL_CKFRZ__W 1
+#define VSB_TOP_SYSSMAGCCTRL_CKFRZ__M 0x80
+#define VSB_TOP_SYSSMAGCCTRL_CKFRZ__PRE 0x80
+
+#define VSB_TOP_SYSSMAGCCTRL_CKBWSW__B 8
+#define VSB_TOP_SYSSMAGCCTRL_CKBWSW__W 1
+#define VSB_TOP_SYSSMAGCCTRL_CKBWSW__M 0x100
+#define VSB_TOP_SYSSMAGCCTRL_CKBWSW__PRE 0x0
+
+#define VSB_TOP_SYSSMAGCCTRL_NCOBWSW__B 9
+#define VSB_TOP_SYSSMAGCCTRL_NCOBWSW__W 1
+#define VSB_TOP_SYSSMAGCCTRL_NCOBWSW__M 0x200
+#define VSB_TOP_SYSSMAGCCTRL_NCOBWSW__PRE 0x0
+
+#define VSB_TOP_SYSSMAGCCTRL_NCOTIMEOUTCNTEN__B 10
+#define VSB_TOP_SYSSMAGCCTRL_NCOTIMEOUTCNTEN__W 1
+#define VSB_TOP_SYSSMAGCCTRL_NCOTIMEOUTCNTEN__M 0x400
+#define VSB_TOP_SYSSMAGCCTRL_NCOTIMEOUTCNTEN__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL__A 0x1C10037
+#define VSB_TOP_SYSSMCTCTRL__W 11
+#define VSB_TOP_SYSSMCTCTRL__M 0x7FF
+#define VSB_TOP_SYSSMCTCTRL__PRE 0x4A
+
+#define VSB_TOP_SYSSMCTCTRL_RSTCTCAL__B 0
+#define VSB_TOP_SYSSMCTCTRL_RSTCTCAL__W 1
+#define VSB_TOP_SYSSMCTCTRL_RSTCTCAL__M 0x1
+#define VSB_TOP_SYSSMCTCTRL_RSTCTCAL__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL_CTCALEN__B 1
+#define VSB_TOP_SYSSMCTCTRL_CTCALEN__W 1
+#define VSB_TOP_SYSSMCTCTRL_CTCALEN__M 0x2
+#define VSB_TOP_SYSSMCTCTRL_CTCALEN__PRE 0x2
+
+#define VSB_TOP_SYSSMCTCTRL_STARTTRN__B 2
+#define VSB_TOP_SYSSMCTCTRL_STARTTRN__W 1
+#define VSB_TOP_SYSSMCTCTRL_STARTTRN__M 0x4
+#define VSB_TOP_SYSSMCTCTRL_STARTTRN__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL_RSTFRMSYNCDET__B 3
+#define VSB_TOP_SYSSMCTCTRL_RSTFRMSYNCDET__W 1
+#define VSB_TOP_SYSSMCTCTRL_RSTFRMSYNCDET__M 0x8
+#define VSB_TOP_SYSSMCTCTRL_RSTFRMSYNCDET__PRE 0x8
+
+#define VSB_TOP_SYSSMCTCTRL_RSTCYDET__B 4
+#define VSB_TOP_SYSSMCTCTRL_RSTCYDET__W 1
+#define VSB_TOP_SYSSMCTCTRL_RSTCYDET__M 0x10
+#define VSB_TOP_SYSSMCTCTRL_RSTCYDET__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL_RSTDCRMV__B 5
+#define VSB_TOP_SYSSMCTCTRL_RSTDCRMV__W 1
+#define VSB_TOP_SYSSMCTCTRL_RSTDCRMV__M 0x20
+#define VSB_TOP_SYSSMCTCTRL_RSTDCRMV__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL_RSTEQSIG__B 6
+#define VSB_TOP_SYSSMCTCTRL_RSTEQSIG__W 1
+#define VSB_TOP_SYSSMCTCTRL_RSTEQSIG__M 0x40
+#define VSB_TOP_SYSSMCTCTRL_RSTEQSIG__PRE 0x40
+
+#define VSB_TOP_SYSSMCTCTRL_CKFRZ__B 7
+#define VSB_TOP_SYSSMCTCTRL_CKFRZ__W 1
+#define VSB_TOP_SYSSMCTCTRL_CKFRZ__M 0x80
+#define VSB_TOP_SYSSMCTCTRL_CKFRZ__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL_CKBWSW__B 8
+#define VSB_TOP_SYSSMCTCTRL_CKBWSW__W 1
+#define VSB_TOP_SYSSMCTCTRL_CKBWSW__M 0x100
+#define VSB_TOP_SYSSMCTCTRL_CKBWSW__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL_NCOBWSW__B 9
+#define VSB_TOP_SYSSMCTCTRL_NCOBWSW__W 1
+#define VSB_TOP_SYSSMCTCTRL_NCOBWSW__M 0x200
+#define VSB_TOP_SYSSMCTCTRL_NCOBWSW__PRE 0x0
+
+#define VSB_TOP_SYSSMCTCTRL_NCOTIMEOUTCNTEN__B 10
+#define VSB_TOP_SYSSMCTCTRL_NCOTIMEOUTCNTEN__W 1
+#define VSB_TOP_SYSSMCTCTRL_NCOTIMEOUTCNTEN__M 0x400
+#define VSB_TOP_SYSSMCTCTRL_NCOTIMEOUTCNTEN__PRE 0x0
+
+#define VSB_TOP_EQCTRL__A 0x1C10038
+#define VSB_TOP_EQCTRL__W 10
+#define VSB_TOP_EQCTRL__M 0x3FF
+#define VSB_TOP_EQCTRL__PRE 0x6
+
+#define VSB_TOP_EQCTRL_STASSIGNEN__B 0
+#define VSB_TOP_EQCTRL_STASSIGNEN__W 1
+#define VSB_TOP_EQCTRL_STASSIGNEN__M 0x1
+#define VSB_TOP_EQCTRL_STASSIGNEN__PRE 0x0
+
+#define VSB_TOP_EQCTRL_ORCANCMAEN__B 1
+#define VSB_TOP_EQCTRL_ORCANCMAEN__W 1
+#define VSB_TOP_EQCTRL_ORCANCMAEN__M 0x2
+#define VSB_TOP_EQCTRL_ORCANCMAEN__PRE 0x2
+
+#define VSB_TOP_EQCTRL_ODAGCGO__B 2
+#define VSB_TOP_EQCTRL_ODAGCGO__W 1
+#define VSB_TOP_EQCTRL_ODAGCGO__M 0x4
+#define VSB_TOP_EQCTRL_ODAGCGO__PRE 0x4
+
+#define VSB_TOP_EQCTRL_OPTGAIN__B 3
+#define VSB_TOP_EQCTRL_OPTGAIN__W 3
+#define VSB_TOP_EQCTRL_OPTGAIN__M 0x38
+#define VSB_TOP_EQCTRL_OPTGAIN__PRE 0x0
+
+#define VSB_TOP_EQCTRL_TAPRAMWRTEN__B 6
+#define VSB_TOP_EQCTRL_TAPRAMWRTEN__W 1
+#define VSB_TOP_EQCTRL_TAPRAMWRTEN__M 0x40
+#define VSB_TOP_EQCTRL_TAPRAMWRTEN__PRE 0x0
+
+#define VSB_TOP_EQCTRL_CMAGAIN__B 7
+#define VSB_TOP_EQCTRL_CMAGAIN__W 3
+#define VSB_TOP_EQCTRL_CMAGAIN__M 0x380
+#define VSB_TOP_EQCTRL_CMAGAIN__PRE 0x0
+
+#define VSB_TOP_PREEQAGCCTRL__A 0x1C10039
+#define VSB_TOP_PREEQAGCCTRL__W 5
+#define VSB_TOP_PREEQAGCCTRL__M 0x1F
+#define VSB_TOP_PREEQAGCCTRL__PRE 0x10
+
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCBWSEL__B 0
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCBWSEL__W 4
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCBWSEL__M 0xF
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCBWSEL__PRE 0x0
+
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCFRZ__B 4
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCFRZ__W 1
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCFRZ__M 0x10
+#define VSB_TOP_PREEQAGCCTRL_PREEQAGCFRZ__PRE 0x10
+
+#define VSB_TOP_PREEQAGCPWRREFLVLHI__A 0x1C1003A
+#define VSB_TOP_PREEQAGCPWRREFLVLHI__W 8
+#define VSB_TOP_PREEQAGCPWRREFLVLHI__M 0xFF
+#define VSB_TOP_PREEQAGCPWRREFLVLHI__PRE 0x0
+
+#define VSB_TOP_PREEQAGCPWRREFLVLLO__A 0x1C1003B
+#define VSB_TOP_PREEQAGCPWRREFLVLLO__W 16
+#define VSB_TOP_PREEQAGCPWRREFLVLLO__M 0xFFFF
+#define VSB_TOP_PREEQAGCPWRREFLVLLO__PRE 0x1D66
+
+#define VSB_TOP_CORINGSEL__A 0x1C1003C
+#define VSB_TOP_CORINGSEL__W 8
+#define VSB_TOP_CORINGSEL__M 0xFF
+#define VSB_TOP_CORINGSEL__PRE 0x3
+#define VSB_TOP_BEDETCTRL__A 0x1C1003D
+#define VSB_TOP_BEDETCTRL__W 9
+#define VSB_TOP_BEDETCTRL__M 0x1FF
+#define VSB_TOP_BEDETCTRL__PRE 0x145
+
+#define VSB_TOP_BEDETCTRL_MIXRATIO__B 0
+#define VSB_TOP_BEDETCTRL_MIXRATIO__W 3
+#define VSB_TOP_BEDETCTRL_MIXRATIO__M 0x7
+#define VSB_TOP_BEDETCTRL_MIXRATIO__PRE 0x5
+
+#define VSB_TOP_BEDETCTRL_CYOFFSEL__B 3
+#define VSB_TOP_BEDETCTRL_CYOFFSEL__W 1
+#define VSB_TOP_BEDETCTRL_CYOFFSEL__M 0x8
+#define VSB_TOP_BEDETCTRL_CYOFFSEL__PRE 0x0
+
+#define VSB_TOP_BEDETCTRL_DATAOFFSEL__B 4
+#define VSB_TOP_BEDETCTRL_DATAOFFSEL__W 1
+#define VSB_TOP_BEDETCTRL_DATAOFFSEL__M 0x10
+#define VSB_TOP_BEDETCTRL_DATAOFFSEL__PRE 0x0
+
+#define VSB_TOP_BEDETCTRL_BYPASS_DSQ__B 5
+#define VSB_TOP_BEDETCTRL_BYPASS_DSQ__W 1
+#define VSB_TOP_BEDETCTRL_BYPASS_DSQ__M 0x20
+#define VSB_TOP_BEDETCTRL_BYPASS_DSQ__PRE 0x0
+
+#define VSB_TOP_BEDETCTRL_BYPASS_PSQ__B 6
+#define VSB_TOP_BEDETCTRL_BYPASS_PSQ__W 1
+#define VSB_TOP_BEDETCTRL_BYPASS_PSQ__M 0x40
+#define VSB_TOP_BEDETCTRL_BYPASS_PSQ__PRE 0x40
+
+#define VSB_TOP_BEDETCTRL_BYPASS_CSQ__B 7
+#define VSB_TOP_BEDETCTRL_BYPASS_CSQ__W 1
+#define VSB_TOP_BEDETCTRL_BYPASS_CSQ__M 0x80
+#define VSB_TOP_BEDETCTRL_BYPASS_CSQ__PRE 0x0
+
+#define VSB_TOP_BEDETCTRL_BYPASS_DMP__B 8
+#define VSB_TOP_BEDETCTRL_BYPASS_DMP__W 1
+#define VSB_TOP_BEDETCTRL_BYPASS_DMP__M 0x100
+#define VSB_TOP_BEDETCTRL_BYPASS_DMP__PRE 0x100
+
+#define VSB_TOP_LBAGCREFLVL__A 0x1C1003E
+#define VSB_TOP_LBAGCREFLVL__W 12
+#define VSB_TOP_LBAGCREFLVL__M 0xFFF
+#define VSB_TOP_LBAGCREFLVL__PRE 0x200
+
+#define VSB_TOP_UBAGCREFLVL__A 0x1C1003F
+#define VSB_TOP_UBAGCREFLVL__W 12
+#define VSB_TOP_UBAGCREFLVL__M 0xFFF
+#define VSB_TOP_UBAGCREFLVL__PRE 0x400
+
+#define VSB_TOP_NOTCH1_BIN_NUM__A 0x1C10040
+#define VSB_TOP_NOTCH1_BIN_NUM__W 11
+#define VSB_TOP_NOTCH1_BIN_NUM__M 0x7FF
+#define VSB_TOP_NOTCH1_BIN_NUM__PRE 0xB2
+
+#define VSB_TOP_NOTCH2_BIN_NUM__A 0x1C10041
+#define VSB_TOP_NOTCH2_BIN_NUM__W 11
+#define VSB_TOP_NOTCH2_BIN_NUM__M 0x7FF
+#define VSB_TOP_NOTCH2_BIN_NUM__PRE 0x40B
+
+#define VSB_TOP_NOTCH_START_BIN_NUM__A 0x1C10042
+#define VSB_TOP_NOTCH_START_BIN_NUM__W 11
+#define VSB_TOP_NOTCH_START_BIN_NUM__M 0x7FF
+#define VSB_TOP_NOTCH_START_BIN_NUM__PRE 0x7C0
+
+#define VSB_TOP_NOTCH_STOP_BIN_NUM__A 0x1C10043
+#define VSB_TOP_NOTCH_STOP_BIN_NUM__W 11
+#define VSB_TOP_NOTCH_STOP_BIN_NUM__M 0x7FF
+#define VSB_TOP_NOTCH_STOP_BIN_NUM__PRE 0x43F
+
+#define VSB_TOP_NOTCH_TEST_DURATION__A 0x1C10044
+#define VSB_TOP_NOTCH_TEST_DURATION__W 11
+#define VSB_TOP_NOTCH_TEST_DURATION__M 0x7FF
+#define VSB_TOP_NOTCH_TEST_DURATION__PRE 0x7FF
+
+#define VSB_TOP_RESULT_LARGE_PEAK_BIN__A 0x1C10045
+#define VSB_TOP_RESULT_LARGE_PEAK_BIN__W 11
+#define VSB_TOP_RESULT_LARGE_PEAK_BIN__M 0x7FF
+#define VSB_TOP_RESULT_LARGE_PEAK_BIN__PRE 0x0
+
+#define VSB_TOP_RESULT_LARGE_PEAK_VALUE__A 0x1C10046
+#define VSB_TOP_RESULT_LARGE_PEAK_VALUE__W 16
+#define VSB_TOP_RESULT_LARGE_PEAK_VALUE__M 0xFFFF
+#define VSB_TOP_RESULT_LARGE_PEAK_VALUE__PRE 0x0
+
+#define VSB_TOP_RESULT_SMALL_PEAK_BIN__A 0x1C10047
+#define VSB_TOP_RESULT_SMALL_PEAK_BIN__W 11
+#define VSB_TOP_RESULT_SMALL_PEAK_BIN__M 0x7FF
+#define VSB_TOP_RESULT_SMALL_PEAK_BIN__PRE 0x0
+
+#define VSB_TOP_RESULT_SMALL_PEAK_VALUE__A 0x1C10048
+#define VSB_TOP_RESULT_SMALL_PEAK_VALUE__W 16
+#define VSB_TOP_RESULT_SMALL_PEAK_VALUE__M 0xFFFF
+#define VSB_TOP_RESULT_SMALL_PEAK_VALUE__PRE 0x0
+
+#define VSB_TOP_NOTCH_SWEEP_RUNNING__A 0x1C10049
+#define VSB_TOP_NOTCH_SWEEP_RUNNING__W 1
+#define VSB_TOP_NOTCH_SWEEP_RUNNING__M 0x1
+#define VSB_TOP_NOTCH_SWEEP_RUNNING__PRE 0x0
+
+#define VSB_TOP_PREEQDAGCRATIO__A 0x1C1004A
+#define VSB_TOP_PREEQDAGCRATIO__W 13
+#define VSB_TOP_PREEQDAGCRATIO__M 0x1FFF
+#define VSB_TOP_PREEQDAGCRATIO__PRE 0x0
+#define VSB_TOP_AGC_TRUNCCTRL__A 0x1C1004B
+#define VSB_TOP_AGC_TRUNCCTRL__W 4
+#define VSB_TOP_AGC_TRUNCCTRL__M 0xF
+#define VSB_TOP_AGC_TRUNCCTRL__PRE 0xF
+
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_LSB__B 0
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_LSB__W 2
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_LSB__M 0x3
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_LSB__PRE 0x3
+
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_12N__B 2
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_12N__W 1
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_12N__M 0x4
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_12N__PRE 0x4
+
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_EN__B 3
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_EN__W 1
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_EN__M 0x8
+#define VSB_TOP_AGC_TRUNCCTRL_TRUNC_EN__PRE 0x8
+
+#define VSB_TOP_BEAGC_DEADZONEINIT__A 0x1C1004C
+#define VSB_TOP_BEAGC_DEADZONEINIT__W 8
+#define VSB_TOP_BEAGC_DEADZONEINIT__M 0xFF
+#define VSB_TOP_BEAGC_DEADZONEINIT__PRE 0x50
+
+#define VSB_TOP_BEAGC_REFLEVEL__A 0x1C1004D
+#define VSB_TOP_BEAGC_REFLEVEL__W 9
+#define VSB_TOP_BEAGC_REFLEVEL__M 0x1FF
+#define VSB_TOP_BEAGC_REFLEVEL__PRE 0xAE
+
+#define VSB_TOP_BEAGC_GAINSHIFT__A 0x1C1004E
+#define VSB_TOP_BEAGC_GAINSHIFT__W 3
+#define VSB_TOP_BEAGC_GAINSHIFT__M 0x7
+#define VSB_TOP_BEAGC_GAINSHIFT__PRE 0x3
+
+#define VSB_TOP_BEAGC_REGINIT__A 0x1C1004F
+#define VSB_TOP_BEAGC_REGINIT__W 15
+#define VSB_TOP_BEAGC_REGINIT__M 0x7FFF
+#define VSB_TOP_BEAGC_REGINIT__PRE 0x40
+
+#define VSB_TOP_BEAGC_REGINIT_BEAGC_RST__B 14
+#define VSB_TOP_BEAGC_REGINIT_BEAGC_RST__W 1
+#define VSB_TOP_BEAGC_REGINIT_BEAGC_RST__M 0x4000
+#define VSB_TOP_BEAGC_REGINIT_BEAGC_RST__PRE 0x0
+
+#define VSB_TOP_BEAGC_SCALE__A 0x1C10050
+#define VSB_TOP_BEAGC_SCALE__W 14
+#define VSB_TOP_BEAGC_SCALE__M 0x3FFF
+#define VSB_TOP_BEAGC_SCALE__PRE 0x0
+
+#define VSB_TOP_CFAGC_DEADZONEINIT__A 0x1C10051
+#define VSB_TOP_CFAGC_DEADZONEINIT__W 8
+#define VSB_TOP_CFAGC_DEADZONEINIT__M 0xFF
+#define VSB_TOP_CFAGC_DEADZONEINIT__PRE 0x50
+
+#define VSB_TOP_CFAGC_REFLEVEL__A 0x1C10052
+#define VSB_TOP_CFAGC_REFLEVEL__W 9
+#define VSB_TOP_CFAGC_REFLEVEL__M 0x1FF
+#define VSB_TOP_CFAGC_REFLEVEL__PRE 0xAE
+
+#define VSB_TOP_CFAGC_GAINSHIFT__A 0x1C10053
+#define VSB_TOP_CFAGC_GAINSHIFT__W 3
+#define VSB_TOP_CFAGC_GAINSHIFT__M 0x7
+#define VSB_TOP_CFAGC_GAINSHIFT__PRE 0x3
+
+#define VSB_TOP_CFAGC_REGINIT__A 0x1C10054
+#define VSB_TOP_CFAGC_REGINIT__W 15
+#define VSB_TOP_CFAGC_REGINIT__M 0x7FFF
+#define VSB_TOP_CFAGC_REGINIT__PRE 0x80
+
+#define VSB_TOP_CFAGC_REGINIT_CFAGC_RST__B 14
+#define VSB_TOP_CFAGC_REGINIT_CFAGC_RST__W 1
+#define VSB_TOP_CFAGC_REGINIT_CFAGC_RST__M 0x4000
+#define VSB_TOP_CFAGC_REGINIT_CFAGC_RST__PRE 0x0
+
+#define VSB_TOP_CFAGC_SCALE__A 0x1C10055
+#define VSB_TOP_CFAGC_SCALE__W 14
+#define VSB_TOP_CFAGC_SCALE__M 0x3FFF
+#define VSB_TOP_CFAGC_SCALE__PRE 0x0
+
+#define VSB_TOP_CKTRKONCTL__A 0x1C10056
+#define VSB_TOP_CKTRKONCTL__W 2
+#define VSB_TOP_CKTRKONCTL__M 0x3
+#define VSB_TOP_CKTRKONCTL__PRE 0x0
+
+#define VSB_TOP_CYTRKONCTL__A 0x1C10057
+#define VSB_TOP_CYTRKONCTL__W 2
+#define VSB_TOP_CYTRKONCTL__M 0x3
+#define VSB_TOP_CYTRKONCTL__PRE 0x0
+
+#define VSB_TOP_PTONCTL__A 0x1C10058
+#define VSB_TOP_PTONCTL__W 2
+#define VSB_TOP_PTONCTL__M 0x3
+#define VSB_TOP_PTONCTL__PRE 0x0
+
+#define VSB_TOP_NOTCH_SCALE_1__A 0x1C10059
+#define VSB_TOP_NOTCH_SCALE_1__W 8
+#define VSB_TOP_NOTCH_SCALE_1__M 0xFF
+#define VSB_TOP_NOTCH_SCALE_1__PRE 0xA
+
+#define VSB_TOP_NOTCH_SCALE_2__A 0x1C1005A
+#define VSB_TOP_NOTCH_SCALE_2__W 8
+#define VSB_TOP_NOTCH_SCALE_2__M 0xFF
+#define VSB_TOP_NOTCH_SCALE_2__PRE 0xA
+
+#define VSB_TOP_FIRSTLARGFFETAP__A 0x1C1005B
+#define VSB_TOP_FIRSTLARGFFETAP__W 12
+#define VSB_TOP_FIRSTLARGFFETAP__M 0xFFF
+#define VSB_TOP_FIRSTLARGFFETAP__PRE 0x0
+
+#define VSB_TOP_FIRSTLARGFFETAPADDR__A 0x1C1005C
+#define VSB_TOP_FIRSTLARGFFETAPADDR__W 11
+#define VSB_TOP_FIRSTLARGFFETAPADDR__M 0x7FF
+#define VSB_TOP_FIRSTLARGFFETAPADDR__PRE 0x0
+
+#define VSB_TOP_SECONDLARGFFETAP__A 0x1C1005D
+#define VSB_TOP_SECONDLARGFFETAP__W 12
+#define VSB_TOP_SECONDLARGFFETAP__M 0xFFF
+#define VSB_TOP_SECONDLARGFFETAP__PRE 0x0
+
+#define VSB_TOP_SECONDLARGFFETAPADDR__A 0x1C1005E
+#define VSB_TOP_SECONDLARGFFETAPADDR__W 11
+#define VSB_TOP_SECONDLARGFFETAPADDR__M 0x7FF
+#define VSB_TOP_SECONDLARGFFETAPADDR__PRE 0x0
+
+#define VSB_TOP_FIRSTLARGDFETAP__A 0x1C1005F
+#define VSB_TOP_FIRSTLARGDFETAP__W 12
+#define VSB_TOP_FIRSTLARGDFETAP__M 0xFFF
+#define VSB_TOP_FIRSTLARGDFETAP__PRE 0x0
+
+#define VSB_TOP_FIRSTLARGDFETAPADDR__A 0x1C10060
+#define VSB_TOP_FIRSTLARGDFETAPADDR__W 11
+#define VSB_TOP_FIRSTLARGDFETAPADDR__M 0x7FF
+#define VSB_TOP_FIRSTLARGDFETAPADDR__PRE 0x0
+
+#define VSB_TOP_SECONDLARGDFETAP__A 0x1C10061
+#define VSB_TOP_SECONDLARGDFETAP__W 12
+#define VSB_TOP_SECONDLARGDFETAP__M 0xFFF
+#define VSB_TOP_SECONDLARGDFETAP__PRE 0x0
+
+#define VSB_TOP_SECONDLARGDFETAPADDR__A 0x1C10062
+#define VSB_TOP_SECONDLARGDFETAPADDR__W 11
+#define VSB_TOP_SECONDLARGDFETAPADDR__M 0x7FF
+#define VSB_TOP_SECONDLARGDFETAPADDR__PRE 0x0
+
+#define VSB_TOP_PARAOWDBUS__A 0x1C10063
+#define VSB_TOP_PARAOWDBUS__W 12
+#define VSB_TOP_PARAOWDBUS__M 0xFFF
+#define VSB_TOP_PARAOWDBUS__PRE 0x0
+#define VSB_TOP_PARAOWCTRL__A 0x1C10064
+#define VSB_TOP_PARAOWCTRL__W 7
+#define VSB_TOP_PARAOWCTRL__M 0x7F
+#define VSB_TOP_PARAOWCTRL__PRE 0x0
+
+#define VSB_TOP_PARAOWCTRL_PARAOWABUS__B 0
+#define VSB_TOP_PARAOWCTRL_PARAOWABUS__W 6
+#define VSB_TOP_PARAOWCTRL_PARAOWABUS__M 0x3F
+#define VSB_TOP_PARAOWCTRL_PARAOWABUS__PRE 0x0
+
+#define VSB_TOP_PARAOWCTRL_PARAOWEN__B 6
+#define VSB_TOP_PARAOWCTRL_PARAOWEN__W 1
+#define VSB_TOP_PARAOWCTRL_PARAOWEN__M 0x40
+#define VSB_TOP_PARAOWCTRL_PARAOWEN__PRE 0x0
+
+#define VSB_TOP_CURRENTSEGLOCAT__A 0x1C10065
+#define VSB_TOP_CURRENTSEGLOCAT__W 10
+#define VSB_TOP_CURRENTSEGLOCAT__M 0x3FF
+#define VSB_TOP_CURRENTSEGLOCAT__PRE 0x0
+
+#define VSB_TOP_MEASUREMENT_PERIOD__A 0x1C10066
+#define VSB_TOP_MEASUREMENT_PERIOD__W 16
+#define VSB_TOP_MEASUREMENT_PERIOD__M 0xFFFF
+#define VSB_TOP_MEASUREMENT_PERIOD__PRE 0x0
+
+#define VSB_TOP_NR_SYM_ERRS__A 0x1C10067
+#define VSB_TOP_NR_SYM_ERRS__W 16
+#define VSB_TOP_NR_SYM_ERRS__M 0xFFFF
+#define VSB_TOP_NR_SYM_ERRS__PRE 0xFFFF
+
+#define VSB_TOP_ERR_ENERGY_L__A 0x1C10068
+#define VSB_TOP_ERR_ENERGY_L__W 16
+#define VSB_TOP_ERR_ENERGY_L__M 0xFFFF
+#define VSB_TOP_ERR_ENERGY_L__PRE 0xFFFF
+
+#define VSB_TOP_ERR_ENERGY_H__A 0x1C10069
+#define VSB_TOP_ERR_ENERGY_H__W 16
+#define VSB_TOP_ERR_ENERGY_H__M 0xFFFF
+#define VSB_TOP_ERR_ENERGY_H__PRE 0xFFFF
+
+#define VSB_TOP_SLICER_SEL_8LEV__A 0x1C1006A
+#define VSB_TOP_SLICER_SEL_8LEV__W 1
+#define VSB_TOP_SLICER_SEL_8LEV__M 0x1
+#define VSB_TOP_SLICER_SEL_8LEV__PRE 0x1
+
+#define VSB_TOP_BNFIELD__A 0x1C1006B
+#define VSB_TOP_BNFIELD__W 3
+#define VSB_TOP_BNFIELD__M 0x7
+#define VSB_TOP_BNFIELD__PRE 0x3
+
+#define VSB_TOP_CLPLASTNUM__A 0x1C1006C
+#define VSB_TOP_CLPLASTNUM__W 8
+#define VSB_TOP_CLPLASTNUM__M 0xFF
+#define VSB_TOP_CLPLASTNUM__PRE 0x0
+
+#define VSB_TOP_BNSQERR__A 0x1C1006D
+#define VSB_TOP_BNSQERR__W 16
+#define VSB_TOP_BNSQERR__M 0xFFFF
+#define VSB_TOP_BNSQERR__PRE 0x1AD
+
+#define VSB_TOP_BNTHRESH__A 0x1C1006E
+#define VSB_TOP_BNTHRESH__W 9
+#define VSB_TOP_BNTHRESH__M 0x1FF
+#define VSB_TOP_BNTHRESH__PRE 0x120
+
+#define VSB_TOP_BNCLPNUM__A 0x1C1006F
+#define VSB_TOP_BNCLPNUM__W 16
+#define VSB_TOP_BNCLPNUM__M 0xFFFF
+#define VSB_TOP_BNCLPNUM__PRE 0x0
+#define VSB_TOP_PHASELOCKCTRL__A 0x1C10070
+#define VSB_TOP_PHASELOCKCTRL__W 7
+#define VSB_TOP_PHASELOCKCTRL__M 0x7F
+#define VSB_TOP_PHASELOCKCTRL__PRE 0x0
+
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPOLARITY__B 0
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPOLARITY__W 1
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPOLARITY__M 0x1
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPOLARITY__PRE 0x0
+
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPLL__B 1
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPLL__W 1
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPLL__M 0x2
+#define VSB_TOP_PHASELOCKCTRL_DFORCEPLL__PRE 0x0
+
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPOLARITY__B 2
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPOLARITY__W 1
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPOLARITY__M 0x4
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPOLARITY__PRE 0x0
+
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPLL__B 3
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPLL__W 1
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPLL__M 0x8
+#define VSB_TOP_PHASELOCKCTRL_PFORCEPLL__PRE 0x0
+
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPOLARITY__B 4
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPOLARITY__W 1
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPOLARITY__M 0x10
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPOLARITY__PRE 0x0
+
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPLL__B 5
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPLL__W 1
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPLL__M 0x20
+#define VSB_TOP_PHASELOCKCTRL_CFORCEPLL__PRE 0x0
+
+#define VSB_TOP_PHASELOCKCTRL_IQSWITCH__B 6
+#define VSB_TOP_PHASELOCKCTRL_IQSWITCH__W 1
+#define VSB_TOP_PHASELOCKCTRL_IQSWITCH__M 0x40
+#define VSB_TOP_PHASELOCKCTRL_IQSWITCH__PRE 0x0
+
+#define VSB_TOP_DLOCKACCUM__A 0x1C10071
+#define VSB_TOP_DLOCKACCUM__W 16
+#define VSB_TOP_DLOCKACCUM__M 0xFFFF
+#define VSB_TOP_DLOCKACCUM__PRE 0x0
+
+#define VSB_TOP_PLOCKACCUM__A 0x1C10072
+#define VSB_TOP_PLOCKACCUM__W 16
+#define VSB_TOP_PLOCKACCUM__M 0xFFFF
+#define VSB_TOP_PLOCKACCUM__PRE 0x0
+
+#define VSB_TOP_CLOCKACCUM__A 0x1C10073
+#define VSB_TOP_CLOCKACCUM__W 16
+#define VSB_TOP_CLOCKACCUM__M 0xFFFF
+#define VSB_TOP_CLOCKACCUM__PRE 0x0
+
+#define VSB_TOP_DCRMVACUMI__A 0x1C10074
+#define VSB_TOP_DCRMVACUMI__W 10
+#define VSB_TOP_DCRMVACUMI__M 0x3FF
+#define VSB_TOP_DCRMVACUMI__PRE 0x0
+
+#define VSB_TOP_DCRMVACUMQ__A 0x1C10075
+#define VSB_TOP_DCRMVACUMQ__W 10
+#define VSB_TOP_DCRMVACUMQ__M 0x3FF
+#define VSB_TOP_DCRMVACUMQ__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__A 0x1C20000
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO2__A 0x1C20001
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO3__A 0x1C20002
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO4__A 0x1C20003
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO5__A 0x1C20004
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO6__A 0x1C20005
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO7__A 0x1C20006
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO8__A 0x1C20007
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO9__A 0x1C20008
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO10__A 0x1C20009
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO11__A 0x1C2000A
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO12__A 0x1C2000B
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFETRAINLKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO1__A 0x1C2000C
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO2__A 0x1C2000D
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO3__A 0x1C2000E
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO4__A 0x1C2000F
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO5__A 0x1C20010
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO6__A 0x1C20011
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO7__A 0x1C20012
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO8__A 0x1C20013
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO9__A 0x1C20014
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO10__A 0x1C20015
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO11__A 0x1C20016
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO12__A 0x1C20017
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1TRAINLKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO1__A 0x1C20018
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO2__A 0x1C20019
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO3__A 0x1C2001A
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO4__A 0x1C2001B
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO5__A 0x1C2001C
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO6__A 0x1C2001D
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO7__A 0x1C2001E
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO8__A 0x1C2001F
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO9__A 0x1C20020
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO10__A 0x1C20021
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO11__A 0x1C20022
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO12__A 0x1C20023
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA1DATALKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO1__A 0x1C20024
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO2__A 0x1C20025
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO3__A 0x1C20026
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO4__A 0x1C20027
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO5__A 0x1C20028
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO6__A 0x1C20029
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO7__A 0x1C2002A
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO8__A 0x1C2002B
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO9__A 0x1C2002C
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO10__A 0x1C2002D
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO11__A 0x1C2002E
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO12__A 0x1C2002F
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2TRAINLKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO1__A 0x1C20030
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO2__A 0x1C20031
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO3__A 0x1C20032
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO4__A 0x1C20033
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO5__A 0x1C20034
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO6__A 0x1C20035
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO7__A 0x1C20036
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO8__A 0x1C20037
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO9__A 0x1C20038
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO10__A 0x1C20039
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO11__A 0x1C2003A
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO12__A 0x1C2003B
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFERCA2DATALKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO1__A 0x1C2003C
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO2__A 0x1C2003D
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO3__A 0x1C2003E
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO4__A 0x1C2003F
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO5__A 0x1C20040
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO6__A 0x1C20041
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO7__A 0x1C20042
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO8__A 0x1C20043
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO9__A 0x1C20044
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO10__A 0x1C20045
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO11__A 0x1C20046
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO12__A 0x1C20047
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1TRAINLKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO1__A 0x1C20048
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO2__A 0x1C20049
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO3__A 0x1C2004A
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO4__A 0x1C2004B
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO5__A 0x1C2004C
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO6__A 0x1C2004D
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO7__A 0x1C2004E
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO8__A 0x1C2004F
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO9__A 0x1C20050
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO10__A 0x1C20051
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO11__A 0x1C20052
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO12__A 0x1C20053
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM1DATALKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO1__A 0x1C20054
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO2__A 0x1C20055
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO3__A 0x1C20056
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO4__A 0x1C20057
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO5__A 0x1C20058
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO6__A 0x1C20059
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO7__A 0x1C2005A
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO8__A 0x1C2005B
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO9__A 0x1C2005C
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO10__A 0x1C2005D
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO11__A 0x1C2005E
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO12__A 0x1C2005F
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2TRAINLKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO1__A 0x1C20060
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO1__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO1__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO2__A 0x1C20061
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO2__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO2__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO3__A 0x1C20062
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO3__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO3__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO4__A 0x1C20063
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO4__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO4__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO5__A 0x1C20064
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO5__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO5__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO6__A 0x1C20065
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO6__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO6__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO7__A 0x1C20066
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO7__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO7__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO8__A 0x1C20067
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO8__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO8__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO9__A 0x1C20068
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO9__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO9__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO10__A 0x1C20069
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO10__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO10__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO11__A 0x1C2006A
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO11__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO11__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO12__A 0x1C2006B
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO12__W 12
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO12__M 0xFFF
+#define VSB_SYSCTRL_RAM0_FFEDDM2DATALKRATIO12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN1__A 0x1C2006C
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN1__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN1__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN2__A 0x1C2006D
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN2__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN2__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN3__A 0x1C2006E
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN3__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN3__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN4__A 0x1C2006F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN4__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN4__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN5__A 0x1C20070
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN5__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN5__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN6__A 0x1C20071
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN6__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN6__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN7__A 0x1C20072
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN7__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN7__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN8__A 0x1C20073
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN8__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN8__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN9__A 0x1C20074
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN9__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN9__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN10__A 0x1C20075
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN10__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN10__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN11__A 0x1C20076
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN11__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN11__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN12__A 0x1C20077
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN12__W 7
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN12__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRTRAINGAIN12__PRE 0x0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1__A 0x1C20078
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1TRAINGAIN1__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1TRAINGAIN1__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1TRAINGAIN1__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1TRAINGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1DATAGAIN1__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1DATAGAIN1__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1DATAGAIN1__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN1_FIRRCA1DATAGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2__A 0x1C20079
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1TRAINGAIN2__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1TRAINGAIN2__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1TRAINGAIN2__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1TRAINGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1DATAGAIN2__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1DATAGAIN2__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1DATAGAIN2__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN2_FIRRCA1DATAGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3__A 0x1C2007A
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1TRAINGAIN3__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1TRAINGAIN3__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1TRAINGAIN3__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1TRAINGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1DATAGAIN3__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1DATAGAIN3__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1DATAGAIN3__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN3_FIRRCA1DATAGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4__A 0x1C2007B
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1TRAINGAIN4__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1TRAINGAIN4__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1TRAINGAIN4__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1TRAINGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1DATAGAIN4__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1DATAGAIN4__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1DATAGAIN4__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN4_FIRRCA1DATAGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5__A 0x1C2007C
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1TRAINGAIN5__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1TRAINGAIN5__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1TRAINGAIN5__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1TRAINGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1DATAGAIN5__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1DATAGAIN5__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1DATAGAIN5__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN5_FIRRCA1DATAGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6__A 0x1C2007D
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1TRAINGAIN6__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1TRAINGAIN6__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1TRAINGAIN6__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1TRAINGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1DATAGAIN6__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1DATAGAIN6__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1DATAGAIN6__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN6_FIRRCA1DATAGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7__A 0x1C2007E
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1TRAINGAIN7__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1TRAINGAIN7__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1TRAINGAIN7__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1TRAINGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1DATAGAIN7__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1DATAGAIN7__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1DATAGAIN7__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN7_FIRRCA1DATAGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8__A 0x1C2007F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8__W 15
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8__M 0x7FFF
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1TRAINGAIN8__B 0
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1TRAINGAIN8__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1TRAINGAIN8__M 0x7F
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1TRAINGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1DATAGAIN8__B 8
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1DATAGAIN8__W 7
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1DATAGAIN8__M 0x7F00
+#define VSB_SYSCTRL_RAM0_FIRRCA1GAIN8_FIRRCA1DATAGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__A 0x1C30000
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1TRAINGAIN9__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1TRAINGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1TRAINGAIN9__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1TRAINGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1DATAGAIN9__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1DATAGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1DATAGAIN9__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN9_FIRRCA1DATAGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10__A 0x1C30001
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1TRAINGAIN10__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1TRAINGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1TRAINGAIN10__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1TRAINGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1DATAGAIN10__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1DATAGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1DATAGAIN10__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN10_FIRRCA1DATAGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11__A 0x1C30002
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1TRAINGAIN11__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1TRAINGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1TRAINGAIN11__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1TRAINGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1DATAGAIN11__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1DATAGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1DATAGAIN11__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN11_FIRRCA1DATAGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12__A 0x1C30003
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1TRAINGAIN12__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1TRAINGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1TRAINGAIN12__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1TRAINGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1DATAGAIN12__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1DATAGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1DATAGAIN12__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA1GAIN12_FIRRCA1DATAGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1__A 0x1C30004
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2TRAINGAIN1__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2TRAINGAIN1__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2TRAINGAIN1__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2TRAINGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2DATAGAIN1__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2DATAGAIN1__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2DATAGAIN1__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN1_FIRRCA2DATAGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2__A 0x1C30005
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2TRAINGAIN2__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2TRAINGAIN2__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2TRAINGAIN2__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2TRAINGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2DATAGAIN2__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2DATAGAIN2__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2DATAGAIN2__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN2_FIRRCA2DATAGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3__A 0x1C30006
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2TRAINGAIN3__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2TRAINGAIN3__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2TRAINGAIN3__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2TRAINGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2DATAGAIN3__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2DATAGAIN3__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2DATAGAIN3__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN3_FIRRCA2DATAGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4__A 0x1C30007
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2TRAINGAIN4__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2TRAINGAIN4__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2TRAINGAIN4__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2TRAINGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2DATAGAIN4__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2DATAGAIN4__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2DATAGAIN4__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN4_FIRRCA2DATAGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5__A 0x1C30008
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2TRAINGAIN5__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2TRAINGAIN5__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2TRAINGAIN5__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2TRAINGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2DATAGAIN5__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2DATAGAIN5__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2DATAGAIN5__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN5_FIRRCA2DATAGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6__A 0x1C30009
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2TRAINGAIN6__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2TRAINGAIN6__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2TRAINGAIN6__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2TRAINGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2DATAGAIN6__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2DATAGAIN6__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2DATAGAIN6__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN6_FIRRCA2DATAGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7__A 0x1C3000A
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2TRAINGAIN7__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2TRAINGAIN7__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2TRAINGAIN7__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2TRAINGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2DATAGAIN7__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2DATAGAIN7__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2DATAGAIN7__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN7_FIRRCA2DATAGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8__A 0x1C3000B
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2TRAINGAIN8__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2TRAINGAIN8__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2TRAINGAIN8__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2TRAINGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2DATAGAIN8__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2DATAGAIN8__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2DATAGAIN8__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN8_FIRRCA2DATAGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9__A 0x1C3000C
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2TRAINGAIN9__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2TRAINGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2TRAINGAIN9__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2TRAINGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2DATAGAIN9__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2DATAGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2DATAGAIN9__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN9_FIRRCA2DATAGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10__A 0x1C3000D
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2TRAINGAIN10__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2TRAINGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2TRAINGAIN10__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2TRAINGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2DATAGAIN10__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2DATAGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2DATAGAIN10__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN10_FIRRCA2DATAGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11__A 0x1C3000E
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2TRAINGAIN11__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2TRAINGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2TRAINGAIN11__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2TRAINGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2DATAGAIN11__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2DATAGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2DATAGAIN11__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN11_FIRRCA2DATAGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12__A 0x1C3000F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12__W 15
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2TRAINGAIN12__B 0
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2TRAINGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2TRAINGAIN12__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2TRAINGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2DATAGAIN12__B 8
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2DATAGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2DATAGAIN12__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRRCA2GAIN12_FIRRCA2DATAGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1__A 0x1C30010
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1TRAINGAIN1__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1TRAINGAIN1__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1TRAINGAIN1__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1TRAINGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1DATAGAIN1__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1DATAGAIN1__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1DATAGAIN1__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN1_FIRDDM1DATAGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2__A 0x1C30011
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1TRAINGAIN2__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1TRAINGAIN2__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1TRAINGAIN2__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1TRAINGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1DATAGAIN2__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1DATAGAIN2__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1DATAGAIN2__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN2_FIRDDM1DATAGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3__A 0x1C30012
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1TRAINGAIN3__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1TRAINGAIN3__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1TRAINGAIN3__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1TRAINGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1DATAGAIN3__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1DATAGAIN3__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1DATAGAIN3__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN3_FIRDDM1DATAGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4__A 0x1C30013
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1TRAINGAIN4__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1TRAINGAIN4__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1TRAINGAIN4__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1TRAINGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1DATAGAIN4__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1DATAGAIN4__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1DATAGAIN4__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN4_FIRDDM1DATAGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5__A 0x1C30014
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1TRAINGAIN5__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1TRAINGAIN5__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1TRAINGAIN5__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1TRAINGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1DATAGAIN5__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1DATAGAIN5__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1DATAGAIN5__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN5_FIRDDM1DATAGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6__A 0x1C30015
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1TRAINGAIN6__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1TRAINGAIN6__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1TRAINGAIN6__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1TRAINGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1DATAGAIN6__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1DATAGAIN6__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1DATAGAIN6__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN6_FIRDDM1DATAGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7__A 0x1C30016
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1TRAINGAIN7__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1TRAINGAIN7__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1TRAINGAIN7__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1TRAINGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1DATAGAIN7__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1DATAGAIN7__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1DATAGAIN7__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN7_FIRDDM1DATAGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8__A 0x1C30017
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1TRAINGAIN8__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1TRAINGAIN8__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1TRAINGAIN8__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1TRAINGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1DATAGAIN8__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1DATAGAIN8__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1DATAGAIN8__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN8_FIRDDM1DATAGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9__A 0x1C30018
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1TRAINGAIN9__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1TRAINGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1TRAINGAIN9__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1TRAINGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1DATAGAIN9__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1DATAGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1DATAGAIN9__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN9_FIRDDM1DATAGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10__A 0x1C30019
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1TRAINGAIN10__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1TRAINGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1TRAINGAIN10__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1TRAINGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1DATAGAIN10__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1DATAGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1DATAGAIN10__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN10_FIRDDM1DATAGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11__A 0x1C3001A
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1TRAINGAIN11__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1TRAINGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1TRAINGAIN11__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1TRAINGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1DATAGAIN11__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1DATAGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1DATAGAIN11__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN11_FIRDDM1DATAGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12__A 0x1C3001B
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1TRAINGAIN12__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1TRAINGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1TRAINGAIN12__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1TRAINGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1DATAGAIN12__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1DATAGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1DATAGAIN12__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM1GAIN12_FIRDDM1DATAGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1__A 0x1C3001C
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2TRAINGAIN1__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2TRAINGAIN1__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2TRAINGAIN1__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2TRAINGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2DATAGAIN1__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2DATAGAIN1__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2DATAGAIN1__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN1_FIRDDM2DATAGAIN1__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2__A 0x1C3001D
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2TRAINGAIN2__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2TRAINGAIN2__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2TRAINGAIN2__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2TRAINGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2DATAGAIN2__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2DATAGAIN2__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2DATAGAIN2__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN2_FIRDDM2DATAGAIN2__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3__A 0x1C3001E
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2TRAINGAIN3__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2TRAINGAIN3__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2TRAINGAIN3__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2TRAINGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2DATAGAIN3__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2DATAGAIN3__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2DATAGAIN3__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN3_FIRDDM2DATAGAIN3__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4__A 0x1C3001F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2TRAINGAIN4__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2TRAINGAIN4__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2TRAINGAIN4__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2TRAINGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2DATAGAIN4__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2DATAGAIN4__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2DATAGAIN4__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN4_FIRDDM2DATAGAIN4__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5__A 0x1C30020
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2TRAINGAIN5__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2TRAINGAIN5__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2TRAINGAIN5__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2TRAINGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2DATAGAIN5__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2DATAGAIN5__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2DATAGAIN5__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN5_FIRDDM2DATAGAIN5__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6__A 0x1C30021
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2TRAINGAIN6__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2TRAINGAIN6__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2TRAINGAIN6__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2TRAINGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2DATAGAIN6__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2DATAGAIN6__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2DATAGAIN6__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN6_FIRDDM2DATAGAIN6__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7__A 0x1C30022
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2TRAINGAIN7__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2TRAINGAIN7__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2TRAINGAIN7__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2TRAINGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2DATAGAIN7__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2DATAGAIN7__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2DATAGAIN7__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN7_FIRDDM2DATAGAIN7__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8__A 0x1C30023
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2TRAINGAIN8__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2TRAINGAIN8__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2TRAINGAIN8__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2TRAINGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2DATAGAIN8__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2DATAGAIN8__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2DATAGAIN8__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN8_FIRDDM2DATAGAIN8__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9__A 0x1C30024
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2TRAINGAIN9__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2TRAINGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2TRAINGAIN9__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2TRAINGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2DATAGAIN9__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2DATAGAIN9__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2DATAGAIN9__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN9_FIRDDM2DATAGAIN9__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10__A 0x1C30025
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2TRAINGAIN10__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2TRAINGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2TRAINGAIN10__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2TRAINGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2DATAGAIN10__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2DATAGAIN10__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2DATAGAIN10__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN10_FIRDDM2DATAGAIN10__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11__A 0x1C30026
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2TRAINGAIN11__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2TRAINGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2TRAINGAIN11__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2TRAINGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2DATAGAIN11__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2DATAGAIN11__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2DATAGAIN11__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN11_FIRDDM2DATAGAIN11__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12__A 0x1C30027
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12__W 15
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2TRAINGAIN12__B 0
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2TRAINGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2TRAINGAIN12__M 0x7F
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2TRAINGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2DATAGAIN12__B 8
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2DATAGAIN12__W 7
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2DATAGAIN12__M 0x7F00
+#define VSB_SYSCTRL_RAM1_FIRDDM2GAIN12_FIRDDM2DATAGAIN12__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFETRAINLKRATIO__A 0x1C30028
+#define VSB_SYSCTRL_RAM1_DFETRAINLKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFETRAINLKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFETRAINLKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA1TRAINLKRATIO__A 0x1C30029
+#define VSB_SYSCTRL_RAM1_DFERCA1TRAINLKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFERCA1TRAINLKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFERCA1TRAINLKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA1DATALKRATIO__A 0x1C3002A
+#define VSB_SYSCTRL_RAM1_DFERCA1DATALKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFERCA1DATALKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFERCA1DATALKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA2TRAINLKRATIO__A 0x1C3002B
+#define VSB_SYSCTRL_RAM1_DFERCA2TRAINLKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFERCA2TRAINLKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFERCA2TRAINLKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA2DATALKRATIO__A 0x1C3002C
+#define VSB_SYSCTRL_RAM1_DFERCA2DATALKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFERCA2DATALKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFERCA2DATALKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM1TRAINLKRATIO__A 0x1C3002D
+#define VSB_SYSCTRL_RAM1_DFEDDM1TRAINLKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFEDDM1TRAINLKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFEDDM1TRAINLKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM1DATALKRATIO__A 0x1C3002E
+#define VSB_SYSCTRL_RAM1_DFEDDM1DATALKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFEDDM1DATALKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFEDDM1DATALKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM2TRAINLKRATIO__A 0x1C3002F
+#define VSB_SYSCTRL_RAM1_DFEDDM2TRAINLKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFEDDM2TRAINLKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFEDDM2TRAINLKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM2DATALKRATIO__A 0x1C30030
+#define VSB_SYSCTRL_RAM1_DFEDDM2DATALKRATIO__W 12
+#define VSB_SYSCTRL_RAM1_DFEDDM2DATALKRATIO__M 0xFFF
+#define VSB_SYSCTRL_RAM1_DFEDDM2DATALKRATIO__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFETRAINGAIN__A 0x1C30031
+#define VSB_SYSCTRL_RAM1_DFETRAINGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFETRAINGAIN__M 0x7F
+#define VSB_SYSCTRL_RAM1_DFETRAINGAIN__PRE 0x0
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN__A 0x1C30032
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN__W 15
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1TRAINGAIN__B 0
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1TRAINGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1TRAINGAIN__M 0x7F
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1TRAINGAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1DATAGAIN__B 8
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1DATAGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1DATAGAIN__M 0x7F00
+#define VSB_SYSCTRL_RAM1_DFERCA1GAIN_DFERCA1DATAGAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN__A 0x1C30033
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN__W 15
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2TRAINGAIN__B 0
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2TRAINGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2TRAINGAIN__M 0x7F
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2TRAINGAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2DATAGAIN__B 8
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2DATAGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2DATAGAIN__M 0x7F00
+#define VSB_SYSCTRL_RAM1_DFERCA2GAIN_DFERCA2DATAGAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN__A 0x1C30034
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN__W 15
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1TRAINGAIN__B 0
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1TRAINGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1TRAINGAIN__M 0x7F
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1TRAINGAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1DATAGAIN__B 8
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1DATAGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1DATAGAIN__M 0x7F00
+#define VSB_SYSCTRL_RAM1_DFEDDM1GAIN_DFEDDM1DATAGAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN__A 0x1C30035
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN__W 15
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN__M 0x7FFF
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2TRAINGAIN__B 0
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2TRAINGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2TRAINGAIN__M 0x7F
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2TRAINGAIN__PRE 0x0
+
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2DATAGAIN__B 8
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2DATAGAIN__W 7
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2DATAGAIN__M 0x7F00
+#define VSB_SYSCTRL_RAM1_DFEDDM2GAIN_DFEDDM2DATAGAIN__PRE 0x0
+
+#define VSB_TCMEQ_RAM__A 0x1C40000
+
+#define VSB_TCMEQ_RAM_TCMEQ_RAM__B 0
+#define VSB_TCMEQ_RAM_TCMEQ_RAM__W 16
+#define VSB_TCMEQ_RAM_TCMEQ_RAM__M 0xFFFF
+#define VSB_TCMEQ_RAM_TCMEQ_RAM__PRE 0x0
+
+#define VSB_FCPRE_RAM__A 0x1C50000
+
+#define VSB_FCPRE_RAM_FCPRE_RAM__B 0
+#define VSB_FCPRE_RAM_FCPRE_RAM__W 16
+#define VSB_FCPRE_RAM_FCPRE_RAM__M 0xFFFF
+#define VSB_FCPRE_RAM_FCPRE_RAM__PRE 0x0
+
+#define VSB_EQTAP_RAM__A 0x1C60000
+
+#define VSB_EQTAP_RAM_EQTAP_RAM__B 0
+#define VSB_EQTAP_RAM_EQTAP_RAM__W 12
+#define VSB_EQTAP_RAM_EQTAP_RAM__M 0xFFF
+#define VSB_EQTAP_RAM_EQTAP_RAM__PRE 0x0
+
+#endif
diff --git a/drivers/media/dvb-frontends/drxd.h b/drivers/media/dvb-frontends/drxd.h
new file mode 100644
index 000000000000..ba54fd9adab9
--- /dev/null
+++ b/drivers/media/dvb-frontends/drxd.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * drxd.h: DRXD DVB-T demodulator driver
+ *
+ * Copyright (C) 2005-2007 Micronas
+ */
+
+#ifndef _DRXD_H_
+#define _DRXD_H_
+
+#include <linux/types.h>
+#include <linux/i2c.h>
+
+struct drxd_config {
+ u8 index;
+
+ u8 pll_address;
+ u8 pll_type;
+#define DRXD_PLL_NONE 0
+#define DRXD_PLL_DTT7520X 1
+#define DRXD_PLL_MT3X0823 2
+
+ u32 clock;
+ u8 insert_rs_byte;
+
+ u8 demod_address;
+ u8 demoda_address;
+ u8 demod_revision;
+
+ /* If the tuner is not behind an i2c gate, be sure to flip this bit
+ or else the i2c bus could get wedged */
+ u8 disable_i2c_gate_ctrl;
+
+ u32 IF;
+ s16(*osc_deviation) (void *priv, s16 dev, int flag);
+};
+
+#if IS_REACHABLE(CONFIG_DVB_DRXD)
+extern
+struct dvb_frontend *drxd_attach(const struct drxd_config *config,
+ void *priv, struct i2c_adapter *i2c,
+ struct device *dev);
+#else
+static inline
+struct dvb_frontend *drxd_attach(const struct drxd_config *config,
+ void *priv, struct i2c_adapter *i2c,
+ struct device *dev)
+{
+ printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
+ __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/frontends/drxd_firm.c b/drivers/media/dvb-frontends/drxd_firm.c
index 5418b0b1dadc..8aae3e0350d3 100644
--- a/drivers/media/dvb/frontends/drxd_firm.c
+++ b/drivers/media/dvb-frontends/drxd_firm.c
@@ -1,24 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* drxd_firm.c : DRXD firmware tables
*
* Copyright (C) 2006-2007 Micronas
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 only, as published by the Free Software Foundation.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*/
/* TODO: generate this file with a script from a settings file */
@@ -894,7 +878,7 @@ u8 DRXD_StartDiversityEnd[] = {
/* End demod, combining RF in and diversity in, MPEG TS out */
WR16(B_FE_CF_REG_IMP_VAL__A, 0x0), /* disable impulse noise cruncher */
WR16(B_FE_AD_REG_INVEXT__A, 0x0), /* clock inversion (for sohard board) */
- WR16(B_CP_REG_BR_STR_DEL__A, 10), /* apperently no mb delay matching is best */
+ WR16(B_CP_REG_BR_STR_DEL__A, 10), /* apparently no mb delay matching is best */
WR16(B_EQ_REG_RC_SEL_CAR__A, B_EQ_REG_RC_SEL_CAR_DIV_ON | /* org = 0x81 combining enabled */
B_EQ_REG_RC_SEL_CAR_MEAS_A_CC |
diff --git a/drivers/media/dvb/frontends/drxd_firm.h b/drivers/media/dvb-frontends/drxd_firm.h
index 41597e89941c..b3f04dfe248c 100644
--- a/drivers/media/dvb/frontends/drxd_firm.h
+++ b/drivers/media/dvb-frontends/drxd_firm.h
@@ -1,24 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* drxd_firm.h
*
* Copyright (C) 2006-2007 Micronas
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 only, as published by the Free Software Foundation.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*/
#ifndef _DRXD_FIRM_H_
diff --git a/drivers/media/dvb/frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c
index f380eb43e9d5..6a531937f4bb 100644
--- a/drivers/media/dvb/frontends/drxd_hard.c
+++ b/drivers/media/dvb-frontends/drxd_hard.c
@@ -1,24 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* drxd_hard.c: DVB-T Demodulator Micronas DRX3975D-A2,DRX397xD-B1
*
* Copyright (C) 2003-2007 Micronas
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 only, as published by the Free Software Foundation.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/kernel.h>
@@ -30,7 +14,7 @@
#include <linux/i2c.h>
#include <asm/div64.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "drxd.h"
#include "drxd_firm.h"
@@ -46,10 +30,6 @@
#define DRX_I2C_MODEFLAGS 0xC0
#define DRX_I2C_FLAGS 0xF0
-#ifndef SIZEOF_ARRAY
-#define SIZEOF_ARRAY(array) (sizeof((array))/sizeof((array)[0]))
-#endif
-
#define DEFAULT_LOCK_TIMEOUT 1100
#define DRX_CHANNEL_AUTO 0
@@ -336,7 +316,7 @@ static int WriteTable(struct drxd_state *state, u8 * pTable)
{
int status = 0;
- if (pTable == NULL)
+ if (!pTable)
return 0;
while (!status) {
@@ -646,8 +626,10 @@ static int SetCfgIfAgc(struct drxd_state *state, struct SCfgAgc *cfg)
/* == Speed == */
{
const u16 maxRur = 8;
- const u16 slowIncrDecLUT[] = { 3, 4, 4, 5, 6 };
- const u16 fastIncrDecLUT[] = { 14, 15, 15, 16,
+ static const u16 slowIncrDecLUT[] = {
+ 3, 4, 4, 5, 6 };
+ static const u16 fastIncrDecLUT[] = {
+ 14, 15, 15, 16,
17, 18, 18, 19,
20, 21, 22, 23,
24, 26, 27, 28,
@@ -915,9 +897,8 @@ static int load_firmware(struct drxd_state *state, const char *fw_name)
}
state->microcode = kmemdup(fw->data, fw->size, GFP_KERNEL);
- if (state->microcode == NULL) {
+ if (!state->microcode) {
release_firmware(fw);
- printk(KERN_ERR "drxd: firmware load failure: no memory\n");
return -ENOMEM;
}
@@ -933,44 +914,36 @@ static int DownloadMicrocode(struct drxd_state *state,
u32 Address;
u16 nBlocks;
u16 BlockSize;
- u32 offset = 0;
int i, status = 0;
pSrc = (u8 *) pMCImage;
/* We're not using Flags */
/* Flags = (pSrc[0] << 8) | pSrc[1]; */
pSrc += sizeof(u16);
- offset += sizeof(u16);
nBlocks = (pSrc[0] << 8) | pSrc[1];
pSrc += sizeof(u16);
- offset += sizeof(u16);
for (i = 0; i < nBlocks; i++) {
Address = (pSrc[0] << 24) | (pSrc[1] << 16) |
(pSrc[2] << 8) | pSrc[3];
pSrc += sizeof(u32);
- offset += sizeof(u32);
BlockSize = ((pSrc[0] << 8) | pSrc[1]) * sizeof(u16);
pSrc += sizeof(u16);
- offset += sizeof(u16);
/* We're not using Flags */
/* u16 Flags = (pSrc[0] << 8) | pSrc[1]; */
pSrc += sizeof(u16);
- offset += sizeof(u16);
/* We're not using BlockCRC */
/* u16 BlockCRC = (pSrc[0] << 8) | pSrc[1]; */
pSrc += sizeof(u16);
- offset += sizeof(u16);
status = WriteBlock(state, Address, BlockSize,
pSrc, DRX_I2C_CLEARCRC);
if (status < 0)
break;
pSrc += BlockSize;
- offset += BlockSize;
}
return status;
@@ -979,7 +952,6 @@ static int DownloadMicrocode(struct drxd_state *state,
static int HI_Command(struct drxd_state *state, u16 cmd, u16 * pResult)
{
u32 nrRetries = 0;
- u16 waitCmd;
int status;
status = Write16(state, HI_RA_RAM_SRV_CMD__A, cmd, 0);
@@ -991,9 +963,9 @@ static int HI_Command(struct drxd_state *state, u16 cmd, u16 * pResult)
if (nrRetries > DRXD_MAX_RETRIES) {
status = -1;
break;
- };
- status = Read16(state, HI_RA_RAM_SRV_CMD__A, &waitCmd, 0);
- } while (waitCmd != 0);
+ }
+ status = Read16(state, HI_RA_RAM_SRV_CMD__A, NULL, 0);
+ } while (status != 0);
if (status >= 0)
status = Read16(state, HI_RA_RAM_SRV_RES__A, pResult, 0);
@@ -1018,7 +990,7 @@ static int HI_CfgCommand(struct drxd_state *state)
status = Write16(state, HI_RA_RAM_SRV_CMD__A,
HI_RA_RAM_SRV_CMD_CONFIG, 0);
else
- status = HI_Command(state, HI_RA_RAM_SRV_CMD_CONFIG, 0);
+ status = HI_Command(state, HI_RA_RAM_SRV_CMD_CONFIG, NULL);
mutex_unlock(&state->mutex);
return status;
}
@@ -1039,7 +1011,7 @@ static int HI_ResetCommand(struct drxd_state *state)
status = Write16(state, HI_RA_RAM_SRV_RST_KEY__A,
HI_RA_RAM_SRV_RST_KEY_ACT, 0);
if (status == 0)
- status = HI_Command(state, HI_RA_RAM_SRV_CMD_RESET, 0);
+ status = HI_Command(state, HI_RA_RAM_SRV_CMD_RESET, NULL);
mutex_unlock(&state->mutex);
msleep(1);
return status;
@@ -1152,6 +1124,8 @@ static int EnableAndResetMB(struct drxd_state *state)
static int InitCC(struct drxd_state *state)
{
+ int status = 0;
+
if (state->osc_clock_freq == 0 ||
state->osc_clock_freq > 20000 ||
(state->osc_clock_freq % 4000) != 0) {
@@ -1159,14 +1133,17 @@ static int InitCC(struct drxd_state *state)
return -1;
}
- Write16(state, CC_REG_OSC_MODE__A, CC_REG_OSC_MODE_M20, 0);
- Write16(state, CC_REG_PLL_MODE__A, CC_REG_PLL_MODE_BYPASS_PLL |
- CC_REG_PLL_MODE_PUMP_CUR_12, 0);
- Write16(state, CC_REG_REF_DIVIDE__A, state->osc_clock_freq / 4000, 0);
- Write16(state, CC_REG_PWD_MODE__A, CC_REG_PWD_MODE_DOWN_PLL, 0);
- Write16(state, CC_REG_UPDATE__A, CC_REG_UPDATE_KEY, 0);
+ status |= Write16(state, CC_REG_OSC_MODE__A, CC_REG_OSC_MODE_M20, 0);
+ status |= Write16(state, CC_REG_PLL_MODE__A,
+ CC_REG_PLL_MODE_BYPASS_PLL |
+ CC_REG_PLL_MODE_PUMP_CUR_12, 0);
+ status |= Write16(state, CC_REG_REF_DIVIDE__A,
+ state->osc_clock_freq / 4000, 0);
+ status |= Write16(state, CC_REG_PWD_MODE__A, CC_REG_PWD_MODE_DOWN_PLL,
+ 0);
+ status |= Write16(state, CC_REG_UPDATE__A, CC_REG_UPDATE_KEY, 0);
- return 0;
+ return status;
}
static int ResetECOD(struct drxd_state *state)
@@ -1305,12 +1282,11 @@ static int InitFT(struct drxd_state *state)
static int SC_WaitForReady(struct drxd_state *state)
{
- u16 curCmd;
int i;
for (i = 0; i < DRXD_MAX_RETRIES; i += 1) {
- int status = Read16(state, SC_RA_RAM_CMD__A, &curCmd, 0);
- if (status == 0 || curCmd == 0)
+ int status = Read16(state, SC_RA_RAM_CMD__A, NULL, 0);
+ if (status == 0)
return status;
}
return -1;
@@ -1318,15 +1294,18 @@ static int SC_WaitForReady(struct drxd_state *state)
static int SC_SendCommand(struct drxd_state *state, u16 cmd)
{
- int status = 0;
+ int status = 0, ret;
u16 errCode;
- Write16(state, SC_RA_RAM_CMD__A, cmd, 0);
+ status = Write16(state, SC_RA_RAM_CMD__A, cmd, 0);
+ if (status < 0)
+ return status;
+
SC_WaitForReady(state);
- Read16(state, SC_RA_RAM_CMD_ADDR__A, &errCode, 0);
+ ret = Read16(state, SC_RA_RAM_CMD_ADDR__A, &errCode, 0);
- if (errCode == 0xFFFF) {
+ if (ret < 0 || errCode == 0xFFFF) {
printk(KERN_ERR "Command Error\n");
status = -1;
}
@@ -1337,20 +1316,20 @@ static int SC_SendCommand(struct drxd_state *state, u16 cmd)
static int SC_ProcStartCommand(struct drxd_state *state,
u16 subCmd, u16 param0, u16 param1)
{
- int status = 0;
+ int ret, status = 0;
u16 scExec;
mutex_lock(&state->mutex);
do {
- Read16(state, SC_COMM_EXEC__A, &scExec, 0);
- if (scExec != 1) {
+ ret = Read16(state, SC_COMM_EXEC__A, &scExec, 0);
+ if (ret < 0 || scExec != 1) {
status = -1;
break;
}
SC_WaitForReady(state);
- Write16(state, SC_RA_RAM_CMD_ADDR__A, subCmd, 0);
- Write16(state, SC_RA_RAM_PARAM1__A, param1, 0);
- Write16(state, SC_RA_RAM_PARAM0__A, param0, 0);
+ status |= Write16(state, SC_RA_RAM_CMD_ADDR__A, subCmd, 0);
+ status |= Write16(state, SC_RA_RAM_PARAM1__A, param1, 0);
+ status |= Write16(state, SC_RA_RAM_PARAM0__A, param0, 0);
SC_SendCommand(state, SC_RA_RAM_CMD_PROC_START);
} while (0);
@@ -1525,12 +1504,14 @@ static int SetDeviceTypeId(struct drxd_state *state)
switch (deviceId) {
case 4:
state->diversity = 1;
+ fallthrough;
case 3:
case 7:
state->PGA = 1;
break;
case 6:
state->diversity = 1;
+ fallthrough;
case 5:
case 8:
break;
@@ -1633,7 +1614,6 @@ static int CorrectSysClockDeviation(struct drxd_state *state)
break;
default:
return -1;
- break;
}
/* Compute new sysclock value
@@ -1748,7 +1728,8 @@ static int DRX_Stop(struct drxd_state *state)
return status;
}
-int SetOperationMode(struct drxd_state *state, int oMode)
+#if 0 /* Currently unused */
+static int SetOperationMode(struct drxd_state *state, int oMode)
{
int status;
@@ -1788,6 +1769,7 @@ int SetOperationMode(struct drxd_state *state, int oMode)
state->operation_mode = oMode;
return status;
}
+#endif
static int StartDiversity(struct drxd_state *state)
{
@@ -1975,7 +1957,7 @@ static int DRX_Start(struct drxd_state *state, s32 off)
switch (p->transmission_mode) {
default: /* Not set, detect it automatically */
operationMode |= SC_RA_RAM_OP_AUTO_MODE__M;
- /* fall through , try first guess DRX_FFTMODE_8K */
+ fallthrough; /* try first guess DRX_FFTMODE_8K */
case TRANSMISSION_MODE_8K:
transmissionParams |= SC_RA_RAM_OP_PARAM_MODE_8K;
if (state->type_A) {
@@ -2142,15 +2124,13 @@ static int DRX_Start(struct drxd_state *state, s32 off)
}
break;
}
- status = status;
if (status < 0)
break;
switch (p->modulation) {
default:
operationMode |= SC_RA_RAM_OP_AUTO_CONST__M;
- /* fall through , try first guess
- DRX_CONSTELLATION_QAM64 */
+ fallthrough; /* try first guess DRX_CONSTELLATION_QAM64 */
case QAM_64:
transmissionParams |= SC_RA_RAM_OP_PARAM_CONST_QAM64;
if (state->type_A) {
@@ -2253,7 +2233,6 @@ static int DRX_Start(struct drxd_state *state, s32 off)
break;
}
- status = status;
if (status < 0)
break;
@@ -2263,63 +2242,43 @@ static int DRX_Start(struct drxd_state *state, s32 off)
case DRX_CHANNEL_LOW:
transmissionParams |= SC_RA_RAM_OP_PARAM_PRIO_LO;
status = Write16(state, EC_SB_REG_PRIOR__A, EC_SB_REG_PRIOR_LO, 0x0000);
- if (status < 0)
- break;
break;
case DRX_CHANNEL_HIGH:
transmissionParams |= SC_RA_RAM_OP_PARAM_PRIO_HI;
status = Write16(state, EC_SB_REG_PRIOR__A, EC_SB_REG_PRIOR_HI, 0x0000);
- if (status < 0)
- break;
break;
-
}
switch (p->code_rate_HP) {
case FEC_1_2:
transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_1_2;
- if (state->type_A) {
+ if (state->type_A)
status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C1_2, 0x0000);
- if (status < 0)
- break;
- }
break;
default:
operationMode |= SC_RA_RAM_OP_AUTO_RATE__M;
+ fallthrough;
case FEC_2_3:
transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_2_3;
- if (state->type_A) {
+ if (state->type_A)
status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C2_3, 0x0000);
- if (status < 0)
- break;
- }
break;
case FEC_3_4:
transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_3_4;
- if (state->type_A) {
+ if (state->type_A)
status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C3_4, 0x0000);
- if (status < 0)
- break;
- }
break;
case FEC_5_6:
transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_5_6;
- if (state->type_A) {
+ if (state->type_A)
status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C5_6, 0x0000);
- if (status < 0)
- break;
- }
break;
case FEC_7_8:
transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_7_8;
- if (state->type_A) {
+ if (state->type_A)
status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C7_8, 0x0000);
- if (status < 0)
- break;
- }
break;
}
- status = status;
if (status < 0)
break;
@@ -2333,7 +2292,7 @@ static int DRX_Start(struct drxd_state *state, s32 off)
switch (p->bandwidth_hz) {
case 0:
p->bandwidth_hz = 8000000;
- /* fall through */
+ fallthrough;
case 8000000:
/* (64/7)*(8/8)*1000000 */
bandwidth = DRXD_BANDWIDTH_8MHZ_IN_HZ;
@@ -2612,7 +2571,7 @@ static int CDRXD(struct drxd_state *state, u32 IntermediateFrequency)
return 0;
}
-int DRXD_init(struct drxd_state *state, const u8 * fw, u32 fw_size)
+static int DRXD_init(struct drxd_state *state, const u8 *fw, u32 fw_size)
{
int status = 0;
u32 driverVersion;
@@ -2630,10 +2589,11 @@ int DRXD_init(struct drxd_state *state, const u8 * fw, u32 fw_size)
break;
/* Apply I2c address patch to B1 */
- if (!state->type_A && state->m_HiI2cPatch != NULL)
+ if (!state->type_A && state->m_HiI2cPatch) {
status = WriteTable(state, state->m_HiI2cPatch);
if (status < 0)
break;
+ }
if (state->type_A) {
/* HI firmware patch for UIO readout,
@@ -2690,11 +2650,11 @@ int DRXD_init(struct drxd_state *state, const u8 * fw, u32 fw_size)
status = EnableAndResetMB(state);
if (status < 0)
break;
- if (state->type_A)
+ if (state->type_A) {
status = ResetCEFR(state);
if (status < 0)
break;
-
+ }
if (fw) {
status = DownloadMicrocode(state, fw, fw_size);
if (status < 0)
@@ -2774,7 +2734,7 @@ int DRXD_init(struct drxd_state *state, const u8 * fw, u32 fw_size)
return status;
}
-int DRXD_status(struct drxd_state *state, u32 * pLockStatus)
+static int DRXD_status(struct drxd_state *state, u32 *pLockStatus)
{
DRX_GetLockStatus(state, pLockStatus);
@@ -2806,7 +2766,7 @@ static int drxd_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
return 0;
}
-static int drxd_read_status(struct dvb_frontend *fe, fe_status_t * status)
+static int drxd_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct drxd_state *state = fe->demodulator_priv;
u32 lock;
@@ -2832,17 +2792,11 @@ static int drxd_read_status(struct dvb_frontend *fe, fe_status_t * status)
static int drxd_init(struct dvb_frontend *fe)
{
struct drxd_state *state = fe->demodulator_priv;
- int err = 0;
-/* if (request_firmware(&state->fw, "drxd.fw", state->dev)<0) */
- return DRXD_init(state, 0, 0);
-
- err = DRXD_init(state, state->fw->data, state->fw->size);
- release_firmware(state->fw);
- return err;
+ return DRXD_init(state, NULL, 0);
}
-int drxd_config_i2c(struct dvb_frontend *fe, int onoff)
+static int drxd_config_i2c(struct dvb_frontend *fe, int onoff)
{
struct drxd_state *state = fe->demodulator_priv;
@@ -2851,7 +2805,6 @@ int drxd_config_i2c(struct dvb_frontend *fe, int onoff)
return DRX_ConfigureI2CBridge(state, onoff);
}
-EXPORT_SYMBOL(drxd_config_i2c);
static int drxd_get_tune_settings(struct dvb_frontend *fe,
struct dvb_frontend_tune_settings *sets)
@@ -2920,14 +2873,13 @@ static void drxd_release(struct dvb_frontend *fe)
kfree(state);
}
-static struct dvb_frontend_ops drxd_ops = {
+static const struct dvb_frontend_ops drxd_ops = {
.delsys = { SYS_DVBT},
.info = {
.name = "Micronas DRXD DVB-T",
- .frequency_min = 47125000,
- .frequency_max = 855250000,
- .frequency_stepsize = 166667,
- .frequency_tolerance = 0,
+ .frequency_min_hz = 47125 * kHz,
+ .frequency_max_hz = 855250 * kHz,
+ .frequency_stepsize_hz = 166667,
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
FE_CAN_FEC_AUTO |
@@ -2958,12 +2910,11 @@ struct dvb_frontend *drxd_attach(const struct drxd_config *config,
{
struct drxd_state *state = NULL;
- state = kmalloc(sizeof(struct drxd_state), GFP_KERNEL);
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return NULL;
- memset(state, 0, sizeof(*state));
- memcpy(&state->ops, &drxd_ops, sizeof(struct dvb_frontend_ops));
+ state->ops = drxd_ops;
state->dev = dev;
state->config = *config;
state->i2c = i2c;
@@ -2971,13 +2922,16 @@ struct dvb_frontend *drxd_attach(const struct drxd_config *config,
mutex_init(&state->mutex);
- if (Read16(state, 0, 0, 0) < 0)
+ if (Read16(state, 0, NULL, 0) < 0)
goto error;
- memcpy(&state->frontend.ops, &drxd_ops,
- sizeof(struct dvb_frontend_ops));
+ state->frontend.ops = drxd_ops;
state->frontend.demodulator_priv = state;
ConfigureMPEGOutput(state, 0);
+ /* add few initialization to allow gate control */
+ CDRXD(state, state->config.IF ? state->config.IF : 36000000);
+ InitHI(state);
+
return &state->frontend;
error:
@@ -2985,7 +2939,7 @@ error:
kfree(state);
return NULL;
}
-EXPORT_SYMBOL(drxd_attach);
+EXPORT_SYMBOL_GPL(drxd_attach);
MODULE_DESCRIPTION("DRXD driver");
MODULE_AUTHOR("Micronas");
diff --git a/drivers/media/dvb/frontends/drxd_map_firm.h b/drivers/media/dvb-frontends/drxd_map_firm.h
index 6bc553abf215..bdcc63576df1 100644
--- a/drivers/media/dvb/frontends/drxd_map_firm.h
+++ b/drivers/media/dvb-frontends/drxd_map_firm.h
@@ -1,24 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* drx3973d_map_firm.h
*
* Copyright (C) 2006-2007 Micronas
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 only, as published by the Free Software Foundation.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*/
#ifndef __DRX3973D_MAP__H__
diff --git a/drivers/media/dvb-frontends/drxk.h b/drivers/media/dvb-frontends/drxk.h
new file mode 100644
index 000000000000..69fdca00f364
--- /dev/null
+++ b/drivers/media/dvb-frontends/drxk.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _DRXK_H_
+#define _DRXK_H_
+
+#include <linux/types.h>
+#include <linux/i2c.h>
+
+/**
+ * struct drxk_config - Configure the initial parameters for DRX-K
+ *
+ * @adr: I2C address of the DRX-K
+ * @parallel_ts: True means that the device uses parallel TS,
+ * Serial otherwise.
+ * @dynamic_clk: True means that the clock will be dynamically
+ * adjusted. Static clock otherwise.
+ * @enable_merr_cfg: Enable SIO_PDR_PERR_CFG/SIO_PDR_MVAL_CFG.
+ * @single_master: Device is on the single master mode
+ * @no_i2c_bridge: Don't switch the I2C bridge to talk with tuner
+ * @antenna_gpio: GPIO bit used to control the antenna
+ * @antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1
+ * means that 1=DVBC, 0 = DVBT. Zero means the opposite.
+ * @mpeg_out_clk_strength: DRXK Mpeg output clock drive strength.
+ * @chunk_size: maximum size for I2C messages
+ * @microcode_name: Name of the firmware file with the microcode
+ * @qam_demod_parameter_count: The number of parameters used for the command
+ * to set the demodulator parameters. All
+ * firmwares are using the 2-parameter command.
+ * An exception is the ``drxk_a3.mc`` firmware,
+ * which uses the 4-parameter command.
+ * A value of 0 (default) or lower indicates that
+ * the correct number of parameters will be
+ * automatically detected.
+ *
+ * On the ``*_gpio`` vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is
+ * UIO-3.
+ */
+struct drxk_config {
+ u8 adr;
+ bool single_master;
+ bool no_i2c_bridge;
+ bool parallel_ts;
+ bool dynamic_clk;
+ bool enable_merr_cfg;
+
+ bool antenna_dvbt;
+ u16 antenna_gpio;
+
+ u8 mpeg_out_clk_strength;
+ int chunk_size;
+
+ const char *microcode_name;
+ int qam_demod_parameter_count;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_DRXK)
+/**
+ * drxk_attach - Attach a drxk demod
+ *
+ * @config: pointer to &struct drxk_config with demod configuration.
+ * @i2c: i2c adapter to use.
+ *
+ * return: FE pointer on success, NULL on failure.
+ */
+extern struct dvb_frontend *drxk_attach(const struct drxk_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *drxk_attach(const struct drxk_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c
new file mode 100644
index 000000000000..9ef367918824
--- /dev/null
+++ b/drivers/media/dvb-frontends/drxk_hard.c
@@ -0,0 +1,6820 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * drxk_hard: DRX-K DVB-C/T demodulator driver
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/hardirq.h>
+#include <asm/div64.h>
+
+#include <media/dvb_frontend.h>
+#include "drxk.h"
+#include "drxk_hard.h"
+#include <linux/int_log.h>
+
+static int power_down_dvbt(struct drxk_state *state, bool set_power_mode);
+static int power_down_qam(struct drxk_state *state);
+static int set_dvbt_standard(struct drxk_state *state,
+ enum operation_mode o_mode);
+static int set_qam_standard(struct drxk_state *state,
+ enum operation_mode o_mode);
+static int set_qam(struct drxk_state *state, u16 intermediate_freqk_hz,
+ s32 tuner_freq_offset);
+static int set_dvbt_standard(struct drxk_state *state,
+ enum operation_mode o_mode);
+static int dvbt_start(struct drxk_state *state);
+static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz,
+ s32 tuner_freq_offset);
+static int get_qam_lock_status(struct drxk_state *state, u32 *p_lock_status);
+static int get_dvbt_lock_status(struct drxk_state *state, u32 *p_lock_status);
+static int switch_antenna_to_qam(struct drxk_state *state);
+static int switch_antenna_to_dvbt(struct drxk_state *state);
+
+static bool is_dvbt(struct drxk_state *state)
+{
+ return state->m_operation_mode == OM_DVBT;
+}
+
+static bool is_qam(struct drxk_state *state)
+{
+ return state->m_operation_mode == OM_QAM_ITU_A ||
+ state->m_operation_mode == OM_QAM_ITU_B ||
+ state->m_operation_mode == OM_QAM_ITU_C;
+}
+
+#define NOA1ROM 0
+
+#define DRXDAP_FASI_SHORT_FORMAT(addr) (((addr) & 0xFC30FF80) == 0)
+#define DRXDAP_FASI_LONG_FORMAT(addr) (((addr) & 0xFC30FF80) != 0)
+
+#define DEFAULT_MER_83 165
+#define DEFAULT_MER_93 250
+
+#ifndef DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH
+#define DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH (0x02)
+#endif
+
+#ifndef DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH
+#define DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH (0x03)
+#endif
+
+#define DEFAULT_DRXK_MPEG_LOCK_TIMEOUT 700
+#define DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT 500
+
+#ifndef DRXK_KI_RAGC_ATV
+#define DRXK_KI_RAGC_ATV 4
+#endif
+#ifndef DRXK_KI_IAGC_ATV
+#define DRXK_KI_IAGC_ATV 6
+#endif
+#ifndef DRXK_KI_DAGC_ATV
+#define DRXK_KI_DAGC_ATV 7
+#endif
+
+#ifndef DRXK_KI_RAGC_QAM
+#define DRXK_KI_RAGC_QAM 3
+#endif
+#ifndef DRXK_KI_IAGC_QAM
+#define DRXK_KI_IAGC_QAM 4
+#endif
+#ifndef DRXK_KI_DAGC_QAM
+#define DRXK_KI_DAGC_QAM 7
+#endif
+#ifndef DRXK_KI_RAGC_DVBT
+#define DRXK_KI_RAGC_DVBT (IsA1WithPatchCode(state) ? 3 : 2)
+#endif
+#ifndef DRXK_KI_IAGC_DVBT
+#define DRXK_KI_IAGC_DVBT (IsA1WithPatchCode(state) ? 4 : 2)
+#endif
+#ifndef DRXK_KI_DAGC_DVBT
+#define DRXK_KI_DAGC_DVBT (IsA1WithPatchCode(state) ? 10 : 7)
+#endif
+
+#ifndef DRXK_AGC_DAC_OFFSET
+#define DRXK_AGC_DAC_OFFSET (0x800)
+#endif
+
+#ifndef DRXK_BANDWIDTH_8MHZ_IN_HZ
+#define DRXK_BANDWIDTH_8MHZ_IN_HZ (0x8B8249L)
+#endif
+
+#ifndef DRXK_BANDWIDTH_7MHZ_IN_HZ
+#define DRXK_BANDWIDTH_7MHZ_IN_HZ (0x7A1200L)
+#endif
+
+#ifndef DRXK_BANDWIDTH_6MHZ_IN_HZ
+#define DRXK_BANDWIDTH_6MHZ_IN_HZ (0x68A1B6L)
+#endif
+
+#ifndef DRXK_QAM_SYMBOLRATE_MAX
+#define DRXK_QAM_SYMBOLRATE_MAX (7233000)
+#endif
+
+#define DRXK_BL_ROM_OFFSET_TAPS_DVBT 56
+#define DRXK_BL_ROM_OFFSET_TAPS_ITU_A 64
+#define DRXK_BL_ROM_OFFSET_TAPS_ITU_C 0x5FE0
+#define DRXK_BL_ROM_OFFSET_TAPS_BG 24
+#define DRXK_BL_ROM_OFFSET_TAPS_DKILLP 32
+#define DRXK_BL_ROM_OFFSET_TAPS_NTSC 40
+#define DRXK_BL_ROM_OFFSET_TAPS_FM 48
+#define DRXK_BL_ROM_OFFSET_UCODE 0
+
+#define DRXK_BLC_TIMEOUT 100
+
+#define DRXK_BLCC_NR_ELEMENTS_TAPS 2
+#define DRXK_BLCC_NR_ELEMENTS_UCODE 6
+
+#define DRXK_BLDC_NR_ELEMENTS_TAPS 28
+
+#ifndef DRXK_OFDM_NE_NOTCH_WIDTH
+#define DRXK_OFDM_NE_NOTCH_WIDTH (4)
+#endif
+
+#define DRXK_QAM_SL_SIG_POWER_QAM16 (40960)
+#define DRXK_QAM_SL_SIG_POWER_QAM32 (20480)
+#define DRXK_QAM_SL_SIG_POWER_QAM64 (43008)
+#define DRXK_QAM_SL_SIG_POWER_QAM128 (20992)
+#define DRXK_QAM_SL_SIG_POWER_QAM256 (43520)
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+#define dprintk(level, fmt, arg...) do { \
+if (debug >= level) \
+ printk(KERN_DEBUG KBUILD_MODNAME ": %s " fmt, __func__, ##arg); \
+} while (0)
+
+static inline u32 Frac28a(u32 a, u32 c)
+{
+ int i = 0;
+ u32 Q1 = 0;
+ u32 R0 = 0;
+
+ R0 = (a % c) << 4; /* 32-28 == 4 shifts possible at max */
+ Q1 = a / c; /*
+ * integer part, only the 4 least significant
+ * bits will be visible in the result
+ */
+
+ /* division using radix 16, 7 nibbles in the result */
+ for (i = 0; i < 7; i++) {
+ Q1 = (Q1 << 4) | (R0 / c);
+ R0 = (R0 % c) << 4;
+ }
+ /* rounding */
+ if ((R0 >> 3) >= c)
+ Q1++;
+
+ return Q1;
+}
+
+static inline u32 log10times100(u32 value)
+{
+ return (100L * intlog10(value)) >> 24;
+}
+
+/***************************************************************************/
+/* I2C **********************************************************************/
+/***************************************************************************/
+
+static int drxk_i2c_lock(struct drxk_state *state)
+{
+ i2c_lock_bus(state->i2c, I2C_LOCK_SEGMENT);
+ state->drxk_i2c_exclusive_lock = true;
+
+ return 0;
+}
+
+static void drxk_i2c_unlock(struct drxk_state *state)
+{
+ if (!state->drxk_i2c_exclusive_lock)
+ return;
+
+ i2c_unlock_bus(state->i2c, I2C_LOCK_SEGMENT);
+ state->drxk_i2c_exclusive_lock = false;
+}
+
+static int drxk_i2c_transfer(struct drxk_state *state, struct i2c_msg *msgs,
+ unsigned len)
+{
+ if (state->drxk_i2c_exclusive_lock)
+ return __i2c_transfer(state->i2c, msgs, len);
+ else
+ return i2c_transfer(state->i2c, msgs, len);
+}
+
+static int i2c_read1(struct drxk_state *state, u8 adr, u8 *val)
+{
+ struct i2c_msg msgs[1] = { {.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1}
+ };
+
+ return drxk_i2c_transfer(state, msgs, 1);
+}
+
+static int i2c_write(struct drxk_state *state, u8 adr, u8 *data, int len)
+{
+ int status;
+ struct i2c_msg msg = {
+ .addr = adr, .flags = 0, .buf = data, .len = len };
+
+ dprintk(3, ": %*ph\n", len, data);
+
+ status = drxk_i2c_transfer(state, &msg, 1);
+ if (status >= 0 && status != 1)
+ status = -EIO;
+
+ if (status < 0)
+ pr_err("i2c write error at addr 0x%02x\n", adr);
+
+ return status;
+}
+
+static int i2c_read(struct drxk_state *state,
+ u8 adr, u8 *msg, int len, u8 *answ, int alen)
+{
+ int status;
+ struct i2c_msg msgs[2] = {
+ {.addr = adr, .flags = 0,
+ .buf = msg, .len = len},
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = answ, .len = alen}
+ };
+
+ status = drxk_i2c_transfer(state, msgs, 2);
+ if (status != 2) {
+ if (debug > 2)
+ pr_cont(": ERROR!\n");
+ if (status >= 0)
+ status = -EIO;
+
+ pr_err("i2c read error at addr 0x%02x\n", adr);
+ return status;
+ }
+ dprintk(3, ": read from %*ph, value = %*ph\n", len, msg, alen, answ);
+ return 0;
+}
+
+static int read16_flags(struct drxk_state *state, u32 reg, u16 *data, u8 flags)
+{
+ int status;
+ u8 adr = state->demod_address, mm1[4], mm2[2], len;
+
+ if (state->single_master)
+ flags |= 0xC0;
+
+ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+ mm1[0] = (((reg << 1) & 0xFF) | 0x01);
+ mm1[1] = ((reg >> 16) & 0xFF);
+ mm1[2] = ((reg >> 24) & 0xFF) | flags;
+ mm1[3] = ((reg >> 7) & 0xFF);
+ len = 4;
+ } else {
+ mm1[0] = ((reg << 1) & 0xFF);
+ mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+ len = 2;
+ }
+ dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags);
+ status = i2c_read(state, adr, mm1, len, mm2, 2);
+ if (status < 0)
+ return status;
+ if (data)
+ *data = mm2[0] | (mm2[1] << 8);
+
+ return 0;
+}
+
+static int read16(struct drxk_state *state, u32 reg, u16 *data)
+{
+ return read16_flags(state, reg, data, 0);
+}
+
+static int read32_flags(struct drxk_state *state, u32 reg, u32 *data, u8 flags)
+{
+ int status;
+ u8 adr = state->demod_address, mm1[4], mm2[4], len;
+
+ if (state->single_master)
+ flags |= 0xC0;
+
+ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+ mm1[0] = (((reg << 1) & 0xFF) | 0x01);
+ mm1[1] = ((reg >> 16) & 0xFF);
+ mm1[2] = ((reg >> 24) & 0xFF) | flags;
+ mm1[3] = ((reg >> 7) & 0xFF);
+ len = 4;
+ } else {
+ mm1[0] = ((reg << 1) & 0xFF);
+ mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+ len = 2;
+ }
+ dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags);
+ status = i2c_read(state, adr, mm1, len, mm2, 4);
+ if (status < 0)
+ return status;
+ if (data)
+ *data = mm2[0] | (mm2[1] << 8) |
+ (mm2[2] << 16) | (mm2[3] << 24);
+
+ return 0;
+}
+
+static int read32(struct drxk_state *state, u32 reg, u32 *data)
+{
+ return read32_flags(state, reg, data, 0);
+}
+
+static int write16_flags(struct drxk_state *state, u32 reg, u16 data, u8 flags)
+{
+ u8 adr = state->demod_address, mm[6], len;
+
+ if (state->single_master)
+ flags |= 0xC0;
+ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+ mm[0] = (((reg << 1) & 0xFF) | 0x01);
+ mm[1] = ((reg >> 16) & 0xFF);
+ mm[2] = ((reg >> 24) & 0xFF) | flags;
+ mm[3] = ((reg >> 7) & 0xFF);
+ len = 4;
+ } else {
+ mm[0] = ((reg << 1) & 0xFF);
+ mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+ len = 2;
+ }
+ mm[len] = data & 0xff;
+ mm[len + 1] = (data >> 8) & 0xff;
+
+ dprintk(2, "(0x%08x, 0x%04x, 0x%02x)\n", reg, data, flags);
+ return i2c_write(state, adr, mm, len + 2);
+}
+
+static int write16(struct drxk_state *state, u32 reg, u16 data)
+{
+ return write16_flags(state, reg, data, 0);
+}
+
+static int write32_flags(struct drxk_state *state, u32 reg, u32 data, u8 flags)
+{
+ u8 adr = state->demod_address, mm[8], len;
+
+ if (state->single_master)
+ flags |= 0xC0;
+ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+ mm[0] = (((reg << 1) & 0xFF) | 0x01);
+ mm[1] = ((reg >> 16) & 0xFF);
+ mm[2] = ((reg >> 24) & 0xFF) | flags;
+ mm[3] = ((reg >> 7) & 0xFF);
+ len = 4;
+ } else {
+ mm[0] = ((reg << 1) & 0xFF);
+ mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+ len = 2;
+ }
+ mm[len] = data & 0xff;
+ mm[len + 1] = (data >> 8) & 0xff;
+ mm[len + 2] = (data >> 16) & 0xff;
+ mm[len + 3] = (data >> 24) & 0xff;
+ dprintk(2, "(0x%08x, 0x%08x, 0x%02x)\n", reg, data, flags);
+
+ return i2c_write(state, adr, mm, len + 4);
+}
+
+static int write32(struct drxk_state *state, u32 reg, u32 data)
+{
+ return write32_flags(state, reg, data, 0);
+}
+
+static int write_block(struct drxk_state *state, u32 address,
+ const int block_size, const u8 p_block[])
+{
+ int status = 0, blk_size = block_size;
+ u8 flags = 0;
+
+ if (state->single_master)
+ flags |= 0xC0;
+
+ while (blk_size > 0) {
+ int chunk = blk_size > state->m_chunk_size ?
+ state->m_chunk_size : blk_size;
+ u8 *adr_buf = &state->chunk[0];
+ u32 adr_length = 0;
+
+ if (DRXDAP_FASI_LONG_FORMAT(address) || (flags != 0)) {
+ adr_buf[0] = (((address << 1) & 0xFF) | 0x01);
+ adr_buf[1] = ((address >> 16) & 0xFF);
+ adr_buf[2] = ((address >> 24) & 0xFF);
+ adr_buf[3] = ((address >> 7) & 0xFF);
+ adr_buf[2] |= flags;
+ adr_length = 4;
+ if (chunk == state->m_chunk_size)
+ chunk -= 2;
+ } else {
+ adr_buf[0] = ((address << 1) & 0xFF);
+ adr_buf[1] = (((address >> 16) & 0x0F) |
+ ((address >> 18) & 0xF0));
+ adr_length = 2;
+ }
+ memcpy(&state->chunk[adr_length], p_block, chunk);
+ dprintk(2, "(0x%08x, 0x%02x)\n", address, flags);
+ if (p_block)
+ dprintk(2, "%*ph\n", chunk, p_block);
+ status = i2c_write(state, state->demod_address,
+ &state->chunk[0], chunk + adr_length);
+ if (status < 0) {
+ pr_err("%s: i2c write error at addr 0x%02x\n",
+ __func__, address);
+ break;
+ }
+ p_block += chunk;
+ address += (chunk >> 1);
+ blk_size -= chunk;
+ }
+ return status;
+}
+
+#ifndef DRXK_MAX_RETRIES_POWERUP
+#define DRXK_MAX_RETRIES_POWERUP 20
+#endif
+
+static int power_up_device(struct drxk_state *state)
+{
+ int status;
+ u8 data = 0;
+ u16 retry_count = 0;
+
+ dprintk(1, "\n");
+
+ status = i2c_read1(state, state->demod_address, &data);
+ if (status < 0) {
+ do {
+ data = 0;
+ status = i2c_write(state, state->demod_address,
+ &data, 1);
+ usleep_range(10000, 11000);
+ retry_count++;
+ if (status < 0)
+ continue;
+ status = i2c_read1(state, state->demod_address,
+ &data);
+ } while (status < 0 &&
+ (retry_count < DRXK_MAX_RETRIES_POWERUP));
+ if (status < 0 && retry_count >= DRXK_MAX_RETRIES_POWERUP)
+ goto error;
+ }
+
+ /* Make sure all clk domains are active */
+ status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_NONE);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+ if (status < 0)
+ goto error;
+ /* Enable pll lock tests */
+ status = write16(state, SIO_CC_PLL_LOCK__A, 1);
+ if (status < 0)
+ goto error;
+
+ state->m_current_power_mode = DRX_POWER_UP;
+
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+
+static int init_state(struct drxk_state *state)
+{
+ /*
+ * FIXME: most (all?) of the values below should be moved into
+ * struct drxk_config, as they are probably board-specific
+ */
+ u32 ul_vsb_if_agc_mode = DRXK_AGC_CTRL_AUTO;
+ u32 ul_vsb_if_agc_output_level = 0;
+ u32 ul_vsb_if_agc_min_level = 0;
+ u32 ul_vsb_if_agc_max_level = 0x7FFF;
+ u32 ul_vsb_if_agc_speed = 3;
+
+ u32 ul_vsb_rf_agc_mode = DRXK_AGC_CTRL_AUTO;
+ u32 ul_vsb_rf_agc_output_level = 0;
+ u32 ul_vsb_rf_agc_min_level = 0;
+ u32 ul_vsb_rf_agc_max_level = 0x7FFF;
+ u32 ul_vsb_rf_agc_speed = 3;
+ u32 ul_vsb_rf_agc_top = 9500;
+ u32 ul_vsb_rf_agc_cut_off_current = 4000;
+
+ u32 ul_atv_if_agc_mode = DRXK_AGC_CTRL_AUTO;
+ u32 ul_atv_if_agc_output_level = 0;
+ u32 ul_atv_if_agc_min_level = 0;
+ u32 ul_atv_if_agc_max_level = 0;
+ u32 ul_atv_if_agc_speed = 3;
+
+ u32 ul_atv_rf_agc_mode = DRXK_AGC_CTRL_OFF;
+ u32 ul_atv_rf_agc_output_level = 0;
+ u32 ul_atv_rf_agc_min_level = 0;
+ u32 ul_atv_rf_agc_max_level = 0;
+ u32 ul_atv_rf_agc_top = 9500;
+ u32 ul_atv_rf_agc_cut_off_current = 4000;
+ u32 ul_atv_rf_agc_speed = 3;
+
+ u32 ulQual83 = DEFAULT_MER_83;
+ u32 ulQual93 = DEFAULT_MER_93;
+
+ u32 ul_mpeg_lock_time_out = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
+ u32 ul_demod_lock_time_out = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
+
+ /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */
+ /* io_pad_cfg_mode output mode is drive always */
+ /* io_pad_cfg_drive is set to power 2 (23 mA) */
+ u32 ul_gpio_cfg = 0x0113;
+ u32 ul_invert_ts_clock = 0;
+ u32 ul_ts_data_strength = DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH;
+ u32 ul_dvbt_bitrate = 50000000;
+ u32 ul_dvbc_bitrate = DRXK_QAM_SYMBOLRATE_MAX * 8;
+
+ u32 ul_insert_rs_byte = 0;
+
+ u32 ul_rf_mirror = 1;
+ u32 ul_power_down = 0;
+
+ dprintk(1, "\n");
+
+ state->m_has_lna = false;
+ state->m_has_dvbt = false;
+ state->m_has_dvbc = false;
+ state->m_has_atv = false;
+ state->m_has_oob = false;
+ state->m_has_audio = false;
+
+ if (!state->m_chunk_size)
+ state->m_chunk_size = 124;
+
+ state->m_osc_clock_freq = 0;
+ state->m_smart_ant_inverted = false;
+ state->m_b_p_down_open_bridge = false;
+
+ /* real system clock frequency in kHz */
+ state->m_sys_clock_freq = 151875;
+ /* Timing div, 250ns/Psys */
+ /* Timing div, = (delay (nano seconds) * sysclk (kHz))/ 1000 */
+ state->m_hi_cfg_timing_div = ((state->m_sys_clock_freq / 1000) *
+ HI_I2C_DELAY) / 1000;
+ /* Clipping */
+ if (state->m_hi_cfg_timing_div > SIO_HI_RA_RAM_PAR_2_CFG_DIV__M)
+ state->m_hi_cfg_timing_div = SIO_HI_RA_RAM_PAR_2_CFG_DIV__M;
+ state->m_hi_cfg_wake_up_key = (state->demod_address << 1);
+ /* port/bridge/power down ctrl */
+ state->m_hi_cfg_ctrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE;
+
+ state->m_b_power_down = (ul_power_down != 0);
+
+ state->m_drxk_a3_patch_code = false;
+
+ /* Init AGC and PGA parameters */
+ /* VSB IF */
+ state->m_vsb_if_agc_cfg.ctrl_mode = ul_vsb_if_agc_mode;
+ state->m_vsb_if_agc_cfg.output_level = ul_vsb_if_agc_output_level;
+ state->m_vsb_if_agc_cfg.min_output_level = ul_vsb_if_agc_min_level;
+ state->m_vsb_if_agc_cfg.max_output_level = ul_vsb_if_agc_max_level;
+ state->m_vsb_if_agc_cfg.speed = ul_vsb_if_agc_speed;
+ state->m_vsb_pga_cfg = 140;
+
+ /* VSB RF */
+ state->m_vsb_rf_agc_cfg.ctrl_mode = ul_vsb_rf_agc_mode;
+ state->m_vsb_rf_agc_cfg.output_level = ul_vsb_rf_agc_output_level;
+ state->m_vsb_rf_agc_cfg.min_output_level = ul_vsb_rf_agc_min_level;
+ state->m_vsb_rf_agc_cfg.max_output_level = ul_vsb_rf_agc_max_level;
+ state->m_vsb_rf_agc_cfg.speed = ul_vsb_rf_agc_speed;
+ state->m_vsb_rf_agc_cfg.top = ul_vsb_rf_agc_top;
+ state->m_vsb_rf_agc_cfg.cut_off_current = ul_vsb_rf_agc_cut_off_current;
+ state->m_vsb_pre_saw_cfg.reference = 0x07;
+ state->m_vsb_pre_saw_cfg.use_pre_saw = true;
+
+ state->m_Quality83percent = DEFAULT_MER_83;
+ state->m_Quality93percent = DEFAULT_MER_93;
+ if (ulQual93 <= 500 && ulQual83 < ulQual93) {
+ state->m_Quality83percent = ulQual83;
+ state->m_Quality93percent = ulQual93;
+ }
+
+ /* ATV IF */
+ state->m_atv_if_agc_cfg.ctrl_mode = ul_atv_if_agc_mode;
+ state->m_atv_if_agc_cfg.output_level = ul_atv_if_agc_output_level;
+ state->m_atv_if_agc_cfg.min_output_level = ul_atv_if_agc_min_level;
+ state->m_atv_if_agc_cfg.max_output_level = ul_atv_if_agc_max_level;
+ state->m_atv_if_agc_cfg.speed = ul_atv_if_agc_speed;
+
+ /* ATV RF */
+ state->m_atv_rf_agc_cfg.ctrl_mode = ul_atv_rf_agc_mode;
+ state->m_atv_rf_agc_cfg.output_level = ul_atv_rf_agc_output_level;
+ state->m_atv_rf_agc_cfg.min_output_level = ul_atv_rf_agc_min_level;
+ state->m_atv_rf_agc_cfg.max_output_level = ul_atv_rf_agc_max_level;
+ state->m_atv_rf_agc_cfg.speed = ul_atv_rf_agc_speed;
+ state->m_atv_rf_agc_cfg.top = ul_atv_rf_agc_top;
+ state->m_atv_rf_agc_cfg.cut_off_current = ul_atv_rf_agc_cut_off_current;
+ state->m_atv_pre_saw_cfg.reference = 0x04;
+ state->m_atv_pre_saw_cfg.use_pre_saw = true;
+
+
+ /* DVBT RF */
+ state->m_dvbt_rf_agc_cfg.ctrl_mode = DRXK_AGC_CTRL_OFF;
+ state->m_dvbt_rf_agc_cfg.output_level = 0;
+ state->m_dvbt_rf_agc_cfg.min_output_level = 0;
+ state->m_dvbt_rf_agc_cfg.max_output_level = 0xFFFF;
+ state->m_dvbt_rf_agc_cfg.top = 0x2100;
+ state->m_dvbt_rf_agc_cfg.cut_off_current = 4000;
+ state->m_dvbt_rf_agc_cfg.speed = 1;
+
+
+ /* DVBT IF */
+ state->m_dvbt_if_agc_cfg.ctrl_mode = DRXK_AGC_CTRL_AUTO;
+ state->m_dvbt_if_agc_cfg.output_level = 0;
+ state->m_dvbt_if_agc_cfg.min_output_level = 0;
+ state->m_dvbt_if_agc_cfg.max_output_level = 9000;
+ state->m_dvbt_if_agc_cfg.top = 13424;
+ state->m_dvbt_if_agc_cfg.cut_off_current = 0;
+ state->m_dvbt_if_agc_cfg.speed = 3;
+ state->m_dvbt_if_agc_cfg.fast_clip_ctrl_delay = 30;
+ state->m_dvbt_if_agc_cfg.ingain_tgt_max = 30000;
+ /* state->m_dvbtPgaCfg = 140; */
+
+ state->m_dvbt_pre_saw_cfg.reference = 4;
+ state->m_dvbt_pre_saw_cfg.use_pre_saw = false;
+
+ /* QAM RF */
+ state->m_qam_rf_agc_cfg.ctrl_mode = DRXK_AGC_CTRL_OFF;
+ state->m_qam_rf_agc_cfg.output_level = 0;
+ state->m_qam_rf_agc_cfg.min_output_level = 6023;
+ state->m_qam_rf_agc_cfg.max_output_level = 27000;
+ state->m_qam_rf_agc_cfg.top = 0x2380;
+ state->m_qam_rf_agc_cfg.cut_off_current = 4000;
+ state->m_qam_rf_agc_cfg.speed = 3;
+
+ /* QAM IF */
+ state->m_qam_if_agc_cfg.ctrl_mode = DRXK_AGC_CTRL_AUTO;
+ state->m_qam_if_agc_cfg.output_level = 0;
+ state->m_qam_if_agc_cfg.min_output_level = 0;
+ state->m_qam_if_agc_cfg.max_output_level = 9000;
+ state->m_qam_if_agc_cfg.top = 0x0511;
+ state->m_qam_if_agc_cfg.cut_off_current = 0;
+ state->m_qam_if_agc_cfg.speed = 3;
+ state->m_qam_if_agc_cfg.ingain_tgt_max = 5119;
+ state->m_qam_if_agc_cfg.fast_clip_ctrl_delay = 50;
+
+ state->m_qam_pga_cfg = 140;
+ state->m_qam_pre_saw_cfg.reference = 4;
+ state->m_qam_pre_saw_cfg.use_pre_saw = false;
+
+ state->m_operation_mode = OM_NONE;
+ state->m_drxk_state = DRXK_UNINITIALIZED;
+
+ /* MPEG output configuration */
+ state->m_enable_mpeg_output = true; /* If TRUE; enable MPEG output */
+ state->m_insert_rs_byte = false; /* If TRUE; insert RS byte */
+ state->m_invert_data = false; /* If TRUE; invert DATA signals */
+ state->m_invert_err = false; /* If TRUE; invert ERR signal */
+ state->m_invert_str = false; /* If TRUE; invert STR signals */
+ state->m_invert_val = false; /* If TRUE; invert VAL signals */
+ state->m_invert_clk = (ul_invert_ts_clock != 0); /* If TRUE; invert CLK signals */
+
+ /* If TRUE; static MPEG clockrate will be used;
+ otherwise clockrate will adapt to the bitrate of the TS */
+
+ state->m_dvbt_bitrate = ul_dvbt_bitrate;
+ state->m_dvbc_bitrate = ul_dvbc_bitrate;
+
+ state->m_ts_data_strength = (ul_ts_data_strength & 0x07);
+
+ /* Maximum bitrate in b/s in case static clockrate is selected */
+ state->m_mpeg_ts_static_bitrate = 19392658;
+ state->m_disable_te_ihandling = false;
+
+ if (ul_insert_rs_byte)
+ state->m_insert_rs_byte = true;
+
+ state->m_mpeg_lock_time_out = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
+ if (ul_mpeg_lock_time_out < 10000)
+ state->m_mpeg_lock_time_out = ul_mpeg_lock_time_out;
+ state->m_demod_lock_time_out = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
+ if (ul_demod_lock_time_out < 10000)
+ state->m_demod_lock_time_out = ul_demod_lock_time_out;
+
+ /* QAM defaults */
+ state->m_constellation = DRX_CONSTELLATION_AUTO;
+ state->m_qam_interleave_mode = DRXK_QAM_I12_J17;
+ state->m_fec_rs_plen = 204 * 8; /* fecRsPlen annex A */
+ state->m_fec_rs_prescale = 1;
+
+ state->m_sqi_speed = DRXK_DVBT_SQI_SPEED_MEDIUM;
+ state->m_agcfast_clip_ctrl_delay = 0;
+
+ state->m_gpio_cfg = ul_gpio_cfg;
+
+ state->m_b_power_down = false;
+ state->m_current_power_mode = DRX_POWER_DOWN;
+
+ state->m_rfmirror = (ul_rf_mirror == 0);
+ state->m_if_agc_pol = false;
+ return 0;
+}
+
+static int drxx_open(struct drxk_state *state)
+{
+ int status = 0;
+ u32 jtag = 0;
+ u16 bid = 0;
+ u16 key = 0;
+
+ dprintk(1, "\n");
+ /* stop lock indicator process */
+ status = write16(state, SCU_RAM_GPIO__A,
+ SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+ /* Check device id */
+ status = read16(state, SIO_TOP_COMM_KEY__A, &key);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
+ if (status < 0)
+ goto error;
+ status = read32(state, SIO_TOP_JTAGID_LO__A, &jtag);
+ if (status < 0)
+ goto error;
+ status = read16(state, SIO_PDR_UIO_IN_HI__A, &bid);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_TOP_COMM_KEY__A, key);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int get_device_capabilities(struct drxk_state *state)
+{
+ u16 sio_pdr_ohw_cfg = 0;
+ u32 sio_top_jtagid_lo = 0;
+ int status;
+ const char *spin = "";
+
+ dprintk(1, "\n");
+
+ /* driver 0.9.0 */
+ /* stop lock indicator process */
+ status = write16(state, SCU_RAM_GPIO__A,
+ SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
+ if (status < 0)
+ goto error;
+ status = read16(state, SIO_PDR_OHW_CFG__A, &sio_pdr_ohw_cfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
+ if (status < 0)
+ goto error;
+
+ switch ((sio_pdr_ohw_cfg & SIO_PDR_OHW_CFG_FREF_SEL__M)) {
+ case 0:
+ /* ignore (bypass ?) */
+ break;
+ case 1:
+ /* 27 MHz */
+ state->m_osc_clock_freq = 27000;
+ break;
+ case 2:
+ /* 20.25 MHz */
+ state->m_osc_clock_freq = 20250;
+ break;
+ case 3:
+ /* 4 MHz */
+ state->m_osc_clock_freq = 20250;
+ break;
+ default:
+ pr_err("Clock Frequency is unknown\n");
+ return -EINVAL;
+ }
+ /*
+ Determine device capabilities
+ Based on pinning v14
+ */
+ status = read32(state, SIO_TOP_JTAGID_LO__A, &sio_top_jtagid_lo);
+ if (status < 0)
+ goto error;
+
+ pr_info("status = 0x%08x\n", sio_top_jtagid_lo);
+
+ /* driver 0.9.0 */
+ switch ((sio_top_jtagid_lo >> 29) & 0xF) {
+ case 0:
+ state->m_device_spin = DRXK_SPIN_A1;
+ spin = "A1";
+ break;
+ case 2:
+ state->m_device_spin = DRXK_SPIN_A2;
+ spin = "A2";
+ break;
+ case 3:
+ state->m_device_spin = DRXK_SPIN_A3;
+ spin = "A3";
+ break;
+ default:
+ state->m_device_spin = DRXK_SPIN_UNKNOWN;
+ status = -EINVAL;
+ pr_err("Spin %d unknown\n", (sio_top_jtagid_lo >> 29) & 0xF);
+ goto error2;
+ }
+ switch ((sio_top_jtagid_lo >> 12) & 0xFF) {
+ case 0x13:
+ /* typeId = DRX3913K_TYPE_ID */
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = false;
+ state->m_has_audio = false;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = true;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = false;
+ state->m_has_gpio1 = false;
+ state->m_has_irqn = false;
+ break;
+ case 0x15:
+ /* typeId = DRX3915K_TYPE_ID */
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = true;
+ state->m_has_audio = false;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = false;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = true;
+ state->m_has_gpio1 = true;
+ state->m_has_irqn = false;
+ break;
+ case 0x16:
+ /* typeId = DRX3916K_TYPE_ID */
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = true;
+ state->m_has_audio = false;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = false;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = true;
+ state->m_has_gpio1 = true;
+ state->m_has_irqn = false;
+ break;
+ case 0x18:
+ /* typeId = DRX3918K_TYPE_ID */
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = true;
+ state->m_has_audio = true;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = false;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = true;
+ state->m_has_gpio1 = true;
+ state->m_has_irqn = false;
+ break;
+ case 0x21:
+ /* typeId = DRX3921K_TYPE_ID */
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = true;
+ state->m_has_audio = true;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = true;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = true;
+ state->m_has_gpio1 = true;
+ state->m_has_irqn = false;
+ break;
+ case 0x23:
+ /* typeId = DRX3923K_TYPE_ID */
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = true;
+ state->m_has_audio = true;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = true;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = true;
+ state->m_has_gpio1 = true;
+ state->m_has_irqn = false;
+ break;
+ case 0x25:
+ /* typeId = DRX3925K_TYPE_ID */
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = true;
+ state->m_has_audio = true;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = true;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = true;
+ state->m_has_gpio1 = true;
+ state->m_has_irqn = false;
+ break;
+ case 0x26:
+ /* typeId = DRX3926K_TYPE_ID */
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = true;
+ state->m_has_audio = false;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = true;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = true;
+ state->m_has_gpio1 = true;
+ state->m_has_irqn = false;
+ break;
+ default:
+ pr_err("DeviceID 0x%02x not supported\n",
+ ((sio_top_jtagid_lo >> 12) & 0xFF));
+ status = -EINVAL;
+ goto error2;
+ }
+
+ pr_info("detected a drx-39%02xk, spin %s, xtal %d.%03d MHz\n",
+ ((sio_top_jtagid_lo >> 12) & 0xFF), spin,
+ state->m_osc_clock_freq / 1000,
+ state->m_osc_clock_freq % 1000);
+
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+error2:
+ return status;
+}
+
+static int hi_command(struct drxk_state *state, u16 cmd, u16 *p_result)
+{
+ int status;
+ bool powerdown_cmd;
+
+ dprintk(1, "\n");
+
+ /* Write command */
+ status = write16(state, SIO_HI_RA_RAM_CMD__A, cmd);
+ if (status < 0)
+ goto error;
+ if (cmd == SIO_HI_RA_RAM_CMD_RESET)
+ usleep_range(1000, 2000);
+
+ powerdown_cmd =
+ (bool) ((cmd == SIO_HI_RA_RAM_CMD_CONFIG) &&
+ ((state->m_hi_cfg_ctrl) &
+ SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M) ==
+ SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ);
+ if (!powerdown_cmd) {
+ /* Wait until command rdy */
+ u32 retry_count = 0;
+ u16 wait_cmd;
+
+ do {
+ usleep_range(1000, 2000);
+ retry_count += 1;
+ status = read16(state, SIO_HI_RA_RAM_CMD__A,
+ &wait_cmd);
+ } while ((status < 0 || wait_cmd) && (retry_count < DRXK_MAX_RETRIES));
+ if (status < 0)
+ goto error;
+ status = read16(state, SIO_HI_RA_RAM_RES__A, p_result);
+ }
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int hi_cfg_command(struct drxk_state *state)
+{
+ int status;
+
+ dprintk(1, "\n");
+
+ mutex_lock(&state->mutex);
+
+ status = write16(state, SIO_HI_RA_RAM_PAR_6__A,
+ state->m_hi_cfg_timeout);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_5__A,
+ state->m_hi_cfg_ctrl);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_4__A,
+ state->m_hi_cfg_wake_up_key);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_3__A,
+ state->m_hi_cfg_bridge_delay);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_2__A,
+ state->m_hi_cfg_timing_div);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_1__A,
+ SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
+ if (status < 0)
+ goto error;
+ status = hi_command(state, SIO_HI_RA_RAM_CMD_CONFIG, NULL);
+ if (status < 0)
+ goto error;
+
+ state->m_hi_cfg_ctrl &= ~SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+error:
+ mutex_unlock(&state->mutex);
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int init_hi(struct drxk_state *state)
+{
+ dprintk(1, "\n");
+
+ state->m_hi_cfg_wake_up_key = (state->demod_address << 1);
+ state->m_hi_cfg_timeout = 0x96FF;
+ /* port/bridge/power down ctrl */
+ state->m_hi_cfg_ctrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE;
+
+ return hi_cfg_command(state);
+}
+
+static int mpegts_configure_pins(struct drxk_state *state, bool mpeg_enable)
+{
+ int status;
+ u16 sio_pdr_mclk_cfg = 0;
+ u16 sio_pdr_mdx_cfg = 0;
+ u16 err_cfg = 0;
+
+ dprintk(1, ": mpeg %s, %s mode\n",
+ mpeg_enable ? "enable" : "disable",
+ state->m_enable_parallel ? "parallel" : "serial");
+
+ /* stop lock indicator process */
+ status = write16(state, SCU_RAM_GPIO__A,
+ SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+
+ /* MPEG TS pad configuration */
+ status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
+ if (status < 0)
+ goto error;
+
+ if (!mpeg_enable) {
+ /* Set MPEG TS pads to inputmode */
+ status = write16(state, SIO_PDR_MSTRT_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MCLK_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD0_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ } else {
+ /* Enable MPEG output */
+ sio_pdr_mdx_cfg =
+ ((state->m_ts_data_strength <<
+ SIO_PDR_MD0_CFG_DRIVE__B) | 0x0003);
+ sio_pdr_mclk_cfg = ((state->m_ts_clockk_strength <<
+ SIO_PDR_MCLK_CFG_DRIVE__B) |
+ 0x0003);
+
+ status = write16(state, SIO_PDR_MSTRT_CFG__A, sio_pdr_mdx_cfg);
+ if (status < 0)
+ goto error;
+
+ if (state->enable_merr_cfg)
+ err_cfg = sio_pdr_mdx_cfg;
+
+ status = write16(state, SIO_PDR_MERR_CFG__A, err_cfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MVAL_CFG__A, err_cfg);
+ if (status < 0)
+ goto error;
+
+ if (state->m_enable_parallel) {
+ /* parallel -> enable MD1 to MD7 */
+ status = write16(state, SIO_PDR_MD1_CFG__A,
+ sio_pdr_mdx_cfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD2_CFG__A,
+ sio_pdr_mdx_cfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD3_CFG__A,
+ sio_pdr_mdx_cfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD4_CFG__A,
+ sio_pdr_mdx_cfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD5_CFG__A,
+ sio_pdr_mdx_cfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD6_CFG__A,
+ sio_pdr_mdx_cfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD7_CFG__A,
+ sio_pdr_mdx_cfg);
+ if (status < 0)
+ goto error;
+ } else {
+ sio_pdr_mdx_cfg = ((state->m_ts_data_strength <<
+ SIO_PDR_MD0_CFG_DRIVE__B)
+ | 0x0003);
+ /* serial -> disable MD1 to MD7 */
+ status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ }
+ status = write16(state, SIO_PDR_MCLK_CFG__A, sio_pdr_mclk_cfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD0_CFG__A, sio_pdr_mdx_cfg);
+ if (status < 0)
+ goto error;
+ }
+ /* Enable MB output over MPEG pads and ctl input */
+ status = write16(state, SIO_PDR_MON_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ /* Write nomagic word to enable pdr reg write */
+ status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int mpegts_disable(struct drxk_state *state)
+{
+ dprintk(1, "\n");
+
+ return mpegts_configure_pins(state, false);
+}
+
+static int bl_chain_cmd(struct drxk_state *state,
+ u16 rom_offset, u16 nr_of_elements, u32 time_out)
+{
+ u16 bl_status = 0;
+ int status;
+ unsigned long end;
+
+ dprintk(1, "\n");
+ mutex_lock(&state->mutex);
+ status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_CHAIN);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_CHAIN_ADDR__A, rom_offset);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_CHAIN_LEN__A, nr_of_elements);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON);
+ if (status < 0)
+ goto error;
+
+ end = jiffies + msecs_to_jiffies(time_out);
+ do {
+ usleep_range(1000, 2000);
+ status = read16(state, SIO_BL_STATUS__A, &bl_status);
+ if (status < 0)
+ goto error;
+ } while ((bl_status == 0x1) &&
+ ((time_is_after_jiffies(end))));
+
+ if (bl_status == 0x1) {
+ pr_err("SIO not ready\n");
+ status = -EINVAL;
+ goto error2;
+ }
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+error2:
+ mutex_unlock(&state->mutex);
+ return status;
+}
+
+
+static int download_microcode(struct drxk_state *state,
+ const u8 p_mc_image[], u32 length)
+{
+ const u8 *p_src = p_mc_image;
+ u32 address;
+ u16 n_blocks;
+ u16 block_size;
+ u32 offset = 0;
+ u32 i;
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ /* down the drain (we don't care about MAGIC_WORD) */
+#if 0
+ /* For future reference */
+ drain = (p_src[0] << 8) | p_src[1];
+#endif
+ p_src += sizeof(u16);
+ offset += sizeof(u16);
+ n_blocks = (p_src[0] << 8) | p_src[1];
+ p_src += sizeof(u16);
+ offset += sizeof(u16);
+
+ for (i = 0; i < n_blocks; i += 1) {
+ address = (p_src[0] << 24) | (p_src[1] << 16) |
+ (p_src[2] << 8) | p_src[3];
+ p_src += sizeof(u32);
+ offset += sizeof(u32);
+
+ block_size = ((p_src[0] << 8) | p_src[1]) * sizeof(u16);
+ p_src += sizeof(u16);
+ offset += sizeof(u16);
+
+#if 0
+ /* For future reference */
+ flags = (p_src[0] << 8) | p_src[1];
+#endif
+ p_src += sizeof(u16);
+ offset += sizeof(u16);
+
+#if 0
+ /* For future reference */
+ block_crc = (p_src[0] << 8) | p_src[1];
+#endif
+ p_src += sizeof(u16);
+ offset += sizeof(u16);
+
+ if (offset + block_size > length) {
+ pr_err("Firmware is corrupted.\n");
+ return -EINVAL;
+ }
+
+ status = write_block(state, address, block_size, p_src);
+ if (status < 0) {
+ pr_err("Error %d while loading firmware\n", status);
+ break;
+ }
+ p_src += block_size;
+ offset += block_size;
+ }
+ return status;
+}
+
+static int dvbt_enable_ofdm_token_ring(struct drxk_state *state, bool enable)
+{
+ int status;
+ u16 data = 0;
+ u16 desired_ctrl = SIO_OFDM_SH_OFDM_RING_ENABLE_ON;
+ u16 desired_status = SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED;
+ unsigned long end;
+
+ dprintk(1, "\n");
+
+ if (!enable) {
+ desired_ctrl = SIO_OFDM_SH_OFDM_RING_ENABLE_OFF;
+ desired_status = SIO_OFDM_SH_OFDM_RING_STATUS_DOWN;
+ }
+
+ status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data);
+ if (status >= 0 && data == desired_status) {
+ /* tokenring already has correct status */
+ return status;
+ }
+ /* Disable/enable dvbt tokenring bridge */
+ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, desired_ctrl);
+
+ end = jiffies + msecs_to_jiffies(DRXK_OFDM_TR_SHUTDOWN_TIMEOUT);
+ do {
+ status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data);
+ if ((status >= 0 && data == desired_status)
+ || time_is_after_jiffies(end))
+ break;
+ usleep_range(1000, 2000);
+ } while (1);
+ if (data != desired_status) {
+ pr_err("SIO not ready\n");
+ return -EINVAL;
+ }
+ return status;
+}
+
+static int mpegts_stop(struct drxk_state *state)
+{
+ int status = 0;
+ u16 fec_oc_snc_mode = 0;
+ u16 fec_oc_ipr_mode = 0;
+
+ dprintk(1, "\n");
+
+ /* Graceful shutdown (byte boundaries) */
+ status = read16(state, FEC_OC_SNC_MODE__A, &fec_oc_snc_mode);
+ if (status < 0)
+ goto error;
+ fec_oc_snc_mode |= FEC_OC_SNC_MODE_SHUTDOWN__M;
+ status = write16(state, FEC_OC_SNC_MODE__A, fec_oc_snc_mode);
+ if (status < 0)
+ goto error;
+
+ /* Suppress MCLK during absence of data */
+ status = read16(state, FEC_OC_IPR_MODE__A, &fec_oc_ipr_mode);
+ if (status < 0)
+ goto error;
+ fec_oc_ipr_mode |= FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M;
+ status = write16(state, FEC_OC_IPR_MODE__A, fec_oc_ipr_mode);
+
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int scu_command(struct drxk_state *state,
+ u16 cmd, u8 parameter_len,
+ u16 *parameter, u8 result_len, u16 *result)
+{
+#if (SCU_RAM_PARAM_0__A - SCU_RAM_PARAM_15__A) != 15
+#error DRXK register mapping no longer compatible with this routine!
+#endif
+ u16 cur_cmd = 0;
+ int status = -EINVAL;
+ unsigned long end;
+ u8 buffer[34];
+ int cnt = 0, ii;
+ const char *p;
+ char errname[30];
+
+ dprintk(1, "\n");
+
+ if ((cmd == 0) || ((parameter_len > 0) && (parameter == NULL)) ||
+ ((result_len > 0) && (result == NULL))) {
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+ }
+
+ mutex_lock(&state->mutex);
+
+ /* assume that the command register is ready
+ since it is checked afterwards */
+ if (parameter) {
+ for (ii = parameter_len - 1; ii >= 0; ii -= 1) {
+ buffer[cnt++] = (parameter[ii] & 0xFF);
+ buffer[cnt++] = ((parameter[ii] >> 8) & 0xFF);
+ }
+ }
+ buffer[cnt++] = (cmd & 0xFF);
+ buffer[cnt++] = ((cmd >> 8) & 0xFF);
+
+ write_block(state, SCU_RAM_PARAM_0__A -
+ (parameter_len - 1), cnt, buffer);
+ /* Wait until SCU has processed command */
+ end = jiffies + msecs_to_jiffies(DRXK_MAX_WAITTIME);
+ do {
+ usleep_range(1000, 2000);
+ status = read16(state, SCU_RAM_COMMAND__A, &cur_cmd);
+ if (status < 0)
+ goto error;
+ } while (!(cur_cmd == DRX_SCU_READY) && (time_is_after_jiffies(end)));
+ if (cur_cmd != DRX_SCU_READY) {
+ pr_err("SCU not ready\n");
+ status = -EIO;
+ goto error2;
+ }
+ /* read results */
+ if ((result_len > 0) && (result != NULL)) {
+ s16 err;
+ int ii;
+
+ for (ii = result_len - 1; ii >= 0; ii -= 1) {
+ status = read16(state, SCU_RAM_PARAM_0__A - ii,
+ &result[ii]);
+ if (status < 0)
+ goto error;
+ }
+
+ /* Check if an error was reported by SCU */
+ err = (s16)result[0];
+ if (err >= 0)
+ goto error;
+
+ /* check for the known error codes */
+ switch (err) {
+ case SCU_RESULT_UNKCMD:
+ p = "SCU_RESULT_UNKCMD";
+ break;
+ case SCU_RESULT_UNKSTD:
+ p = "SCU_RESULT_UNKSTD";
+ break;
+ case SCU_RESULT_SIZE:
+ p = "SCU_RESULT_SIZE";
+ break;
+ case SCU_RESULT_INVPAR:
+ p = "SCU_RESULT_INVPAR";
+ break;
+ default: /* Other negative values are errors */
+ sprintf(errname, "ERROR: %d\n", err);
+ p = errname;
+ }
+ pr_err("%s while sending cmd 0x%04x with params:", p, cmd);
+ print_hex_dump_bytes("drxk: ", DUMP_PREFIX_NONE, buffer, cnt);
+ status = -EINVAL;
+ goto error2;
+ }
+
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+error2:
+ mutex_unlock(&state->mutex);
+ return status;
+}
+
+static int set_iqm_af(struct drxk_state *state, bool active)
+{
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ /* Configure IQM */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+
+ if (!active) {
+ data |= (IQM_AF_STDBY_STDBY_ADC_STANDBY
+ | IQM_AF_STDBY_STDBY_AMP_STANDBY
+ | IQM_AF_STDBY_STDBY_PD_STANDBY
+ | IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY
+ | IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY);
+ } else {
+ data &= ((~IQM_AF_STDBY_STDBY_ADC_STANDBY)
+ & (~IQM_AF_STDBY_STDBY_AMP_STANDBY)
+ & (~IQM_AF_STDBY_STDBY_PD_STANDBY)
+ & (~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY)
+ & (~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY)
+ );
+ }
+ status = write16(state, IQM_AF_STDBY__A, data);
+
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int ctrl_power_mode(struct drxk_state *state, enum drx_power_mode *mode)
+{
+ int status = 0;
+ u16 sio_cc_pwd_mode = 0;
+
+ dprintk(1, "\n");
+
+ /* Check arguments */
+ if (mode == NULL)
+ return -EINVAL;
+
+ switch (*mode) {
+ case DRX_POWER_UP:
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_NONE;
+ break;
+ case DRXK_POWER_DOWN_OFDM:
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_OFDM;
+ break;
+ case DRXK_POWER_DOWN_CORE:
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_CLOCK;
+ break;
+ case DRXK_POWER_DOWN_PLL:
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_PLL;
+ break;
+ case DRX_POWER_DOWN:
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_OSC;
+ break;
+ default:
+ /* Unknown sleep mode */
+ return -EINVAL;
+ }
+
+ /* If already in requested power mode, do nothing */
+ if (state->m_current_power_mode == *mode)
+ return 0;
+
+ /* For next steps make sure to start from DRX_POWER_UP mode */
+ if (state->m_current_power_mode != DRX_POWER_UP) {
+ status = power_up_device(state);
+ if (status < 0)
+ goto error;
+ status = dvbt_enable_ofdm_token_ring(state, true);
+ if (status < 0)
+ goto error;
+ }
+
+ if (*mode == DRX_POWER_UP) {
+ /* Restore analog & pin configuration */
+ } else {
+ /* Power down to requested mode */
+ /* Backup some register settings */
+ /* Set pins with possible pull-ups connected
+ to them in input mode */
+ /* Analog power down */
+ /* ADC power down */
+ /* Power down device */
+ /* stop all comm_exec */
+ /* Stop and power down previous standard */
+ switch (state->m_operation_mode) {
+ case OM_DVBT:
+ status = mpegts_stop(state);
+ if (status < 0)
+ goto error;
+ status = power_down_dvbt(state, false);
+ if (status < 0)
+ goto error;
+ break;
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_C:
+ status = mpegts_stop(state);
+ if (status < 0)
+ goto error;
+ status = power_down_qam(state);
+ if (status < 0)
+ goto error;
+ break;
+ default:
+ break;
+ }
+ status = dvbt_enable_ofdm_token_ring(state, false);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_PWD_MODE__A, sio_cc_pwd_mode);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+ if (status < 0)
+ goto error;
+
+ if (*mode != DRXK_POWER_DOWN_OFDM) {
+ state->m_hi_cfg_ctrl |=
+ SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+ status = hi_cfg_command(state);
+ if (status < 0)
+ goto error;
+ }
+ }
+ state->m_current_power_mode = *mode;
+
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int power_down_dvbt(struct drxk_state *state, bool set_power_mode)
+{
+ enum drx_power_mode power_mode = DRXK_POWER_DOWN_OFDM;
+ u16 cmd_result = 0;
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ status = read16(state, SCU_COMM_EXEC__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == SCU_COMM_EXEC_ACTIVE) {
+ /* Send OFDM stop command */
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_OFDM
+ | SCU_RAM_COMMAND_CMD_DEMOD_STOP,
+ 0, NULL, 1, &cmd_result);
+ if (status < 0)
+ goto error;
+ /* Send OFDM reset command */
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_OFDM
+ | SCU_RAM_COMMAND_CMD_DEMOD_RESET,
+ 0, NULL, 1, &cmd_result);
+ if (status < 0)
+ goto error;
+ }
+
+ /* Reset datapath for OFDM, processors first */
+ status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
+ if (status < 0)
+ goto error;
+
+ /* powerdown AFE */
+ status = set_iqm_af(state, false);
+ if (status < 0)
+ goto error;
+
+ /* powerdown to OFDM mode */
+ if (set_power_mode) {
+ status = ctrl_power_mode(state, &power_mode);
+ if (status < 0)
+ goto error;
+ }
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int setoperation_mode(struct drxk_state *state,
+ enum operation_mode o_mode)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /*
+ Stop and power down previous standard
+ TODO investigate total power down instead of partial
+ power down depending on "previous" standard.
+ */
+
+ /* disable HW lock indicator */
+ status = write16(state, SCU_RAM_GPIO__A,
+ SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+
+ /* Device is already at the required mode */
+ if (state->m_operation_mode == o_mode)
+ return 0;
+
+ switch (state->m_operation_mode) {
+ /* OM_NONE was added for start up */
+ case OM_NONE:
+ break;
+ case OM_DVBT:
+ status = mpegts_stop(state);
+ if (status < 0)
+ goto error;
+ status = power_down_dvbt(state, true);
+ if (status < 0)
+ goto error;
+ state->m_operation_mode = OM_NONE;
+ break;
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_C:
+ status = mpegts_stop(state);
+ if (status < 0)
+ goto error;
+ status = power_down_qam(state);
+ if (status < 0)
+ goto error;
+ state->m_operation_mode = OM_NONE;
+ break;
+ case OM_QAM_ITU_B:
+ default:
+ status = -EINVAL;
+ goto error;
+ }
+
+ /*
+ Power up new standard
+ */
+ switch (o_mode) {
+ case OM_DVBT:
+ dprintk(1, ": DVB-T\n");
+ state->m_operation_mode = o_mode;
+ status = set_dvbt_standard(state, o_mode);
+ if (status < 0)
+ goto error;
+ break;
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_C:
+ dprintk(1, ": DVB-C Annex %c\n",
+ (state->m_operation_mode == OM_QAM_ITU_A) ? 'A' : 'C');
+ state->m_operation_mode = o_mode;
+ status = set_qam_standard(state, o_mode);
+ if (status < 0)
+ goto error;
+ break;
+ case OM_QAM_ITU_B:
+ default:
+ status = -EINVAL;
+ }
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int start(struct drxk_state *state, s32 offset_freq,
+ s32 intermediate_frequency)
+{
+ int status = -EINVAL;
+
+ u16 i_freqk_hz;
+ s32 offsetk_hz = offset_freq / 1000;
+
+ dprintk(1, "\n");
+ if (state->m_drxk_state != DRXK_STOPPED &&
+ state->m_drxk_state != DRXK_DTV_STARTED)
+ goto error;
+
+ state->m_b_mirror_freq_spect = (state->props.inversion == INVERSION_ON);
+
+ if (intermediate_frequency < 0) {
+ state->m_b_mirror_freq_spect = !state->m_b_mirror_freq_spect;
+ intermediate_frequency = -intermediate_frequency;
+ }
+
+ switch (state->m_operation_mode) {
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_C:
+ i_freqk_hz = (intermediate_frequency / 1000);
+ status = set_qam(state, i_freqk_hz, offsetk_hz);
+ if (status < 0)
+ goto error;
+ state->m_drxk_state = DRXK_DTV_STARTED;
+ break;
+ case OM_DVBT:
+ i_freqk_hz = (intermediate_frequency / 1000);
+ status = mpegts_stop(state);
+ if (status < 0)
+ goto error;
+ status = set_dvbt(state, i_freqk_hz, offsetk_hz);
+ if (status < 0)
+ goto error;
+ status = dvbt_start(state);
+ if (status < 0)
+ goto error;
+ state->m_drxk_state = DRXK_DTV_STARTED;
+ break;
+ default:
+ break;
+ }
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int shut_down(struct drxk_state *state)
+{
+ dprintk(1, "\n");
+
+ mpegts_stop(state);
+ return 0;
+}
+
+static int get_lock_status(struct drxk_state *state, u32 *p_lock_status)
+{
+ int status = -EINVAL;
+
+ dprintk(1, "\n");
+
+ if (p_lock_status == NULL)
+ goto error;
+
+ *p_lock_status = NOT_LOCKED;
+
+ /* define the SCU command code */
+ switch (state->m_operation_mode) {
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_B:
+ case OM_QAM_ITU_C:
+ status = get_qam_lock_status(state, p_lock_status);
+ break;
+ case OM_DVBT:
+ status = get_dvbt_lock_status(state, p_lock_status);
+ break;
+ default:
+ pr_debug("Unsupported operation mode %d in %s\n",
+ state->m_operation_mode, __func__);
+ return 0;
+ }
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int mpegts_start(struct drxk_state *state)
+{
+ int status;
+
+ u16 fec_oc_snc_mode = 0;
+
+ /* Allow OC to sync again */
+ status = read16(state, FEC_OC_SNC_MODE__A, &fec_oc_snc_mode);
+ if (status < 0)
+ goto error;
+ fec_oc_snc_mode &= ~FEC_OC_SNC_MODE_SHUTDOWN__M;
+ status = write16(state, FEC_OC_SNC_MODE__A, fec_oc_snc_mode);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_SNC_UNLOCK__A, 1);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int mpegts_dto_init(struct drxk_state *state)
+{
+ int status;
+
+ dprintk(1, "\n");
+
+ /* Rate integration settings */
+ status = write16(state, FEC_OC_RCN_CTL_STEP_LO__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_RCN_CTL_STEP_HI__A, 0x000C);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_RCN_GAIN__A, 0x000A);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_AVR_PARM_A__A, 0x0008);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_AVR_PARM_B__A, 0x0006);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_HI_MARGIN__A, 0x0680);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_LO_MARGIN__A, 0x0080);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_COUNT__A, 0x03F4);
+ if (status < 0)
+ goto error;
+
+ /* Additional configuration */
+ status = write16(state, FEC_OC_OCR_INVERT__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_SNC_LWM__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_SNC_HWM__A, 12);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int mpegts_dto_setup(struct drxk_state *state,
+ enum operation_mode o_mode)
+{
+ int status;
+
+ u16 fec_oc_reg_mode = 0; /* FEC_OC_MODE register value */
+ u16 fec_oc_reg_ipr_mode = 0; /* FEC_OC_IPR_MODE register value */
+ u16 fec_oc_dto_mode = 0; /* FEC_OC_IPR_INVERT register value */
+ u16 fec_oc_fct_mode = 0; /* FEC_OC_IPR_INVERT register value */
+ u16 fec_oc_dto_period = 2; /* FEC_OC_IPR_INVERT register value */
+ u16 fec_oc_dto_burst_len = 188; /* FEC_OC_IPR_INVERT register value */
+ u32 fec_oc_rcn_ctl_rate = 0; /* FEC_OC_IPR_INVERT register value */
+ u16 fec_oc_tmd_mode = 0;
+ u16 fec_oc_tmd_int_upd_rate = 0;
+ u32 max_bit_rate = 0;
+ bool static_clk = false;
+
+ dprintk(1, "\n");
+
+ /* Check insertion of the Reed-Solomon parity bytes */
+ status = read16(state, FEC_OC_MODE__A, &fec_oc_reg_mode);
+ if (status < 0)
+ goto error;
+ status = read16(state, FEC_OC_IPR_MODE__A, &fec_oc_reg_ipr_mode);
+ if (status < 0)
+ goto error;
+ fec_oc_reg_mode &= (~FEC_OC_MODE_PARITY__M);
+ fec_oc_reg_ipr_mode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M);
+ if (state->m_insert_rs_byte) {
+ /* enable parity symbol forward */
+ fec_oc_reg_mode |= FEC_OC_MODE_PARITY__M;
+ /* MVAL disable during parity bytes */
+ fec_oc_reg_ipr_mode |= FEC_OC_IPR_MODE_MVAL_DIS_PAR__M;
+ /* TS burst length to 204 */
+ fec_oc_dto_burst_len = 204;
+ }
+
+ /* Check serial or parallel output */
+ fec_oc_reg_ipr_mode &= (~(FEC_OC_IPR_MODE_SERIAL__M));
+ if (!state->m_enable_parallel) {
+ /* MPEG data output is serial -> set ipr_mode[0] */
+ fec_oc_reg_ipr_mode |= FEC_OC_IPR_MODE_SERIAL__M;
+ }
+
+ switch (o_mode) {
+ case OM_DVBT:
+ max_bit_rate = state->m_dvbt_bitrate;
+ fec_oc_tmd_mode = 3;
+ fec_oc_rcn_ctl_rate = 0xC00000;
+ static_clk = state->m_dvbt_static_clk;
+ break;
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_C:
+ fec_oc_tmd_mode = 0x0004;
+ fec_oc_rcn_ctl_rate = 0xD2B4EE; /* good for >63 Mb/s */
+ max_bit_rate = state->m_dvbc_bitrate;
+ static_clk = state->m_dvbc_static_clk;
+ break;
+ default:
+ status = -EINVAL;
+ } /* switch (standard) */
+ if (status < 0)
+ goto error;
+
+ /* Configure DTO's */
+ if (static_clk) {
+ u32 bit_rate = 0;
+
+ /* Rational DTO for MCLK source (static MCLK rate),
+ Dynamic DTO for optimal grouping
+ (avoid intra-packet gaps),
+ DTO offset enable to sync TS burst with MSTRT */
+ fec_oc_dto_mode = (FEC_OC_DTO_MODE_DYNAMIC__M |
+ FEC_OC_DTO_MODE_OFFSET_ENABLE__M);
+ fec_oc_fct_mode = (FEC_OC_FCT_MODE_RAT_ENA__M |
+ FEC_OC_FCT_MODE_VIRT_ENA__M);
+
+ /* Check user defined bitrate */
+ bit_rate = max_bit_rate;
+ if (bit_rate > 75900000UL) { /* max is 75.9 Mb/s */
+ bit_rate = 75900000UL;
+ }
+ /* Rational DTO period:
+ dto_period = (Fsys / bitrate) - 2
+
+ result should be floored,
+ to make sure >= requested bitrate
+ */
+ fec_oc_dto_period = (u16) (((state->m_sys_clock_freq)
+ * 1000) / bit_rate);
+ if (fec_oc_dto_period <= 2)
+ fec_oc_dto_period = 0;
+ else
+ fec_oc_dto_period -= 2;
+ fec_oc_tmd_int_upd_rate = 8;
+ } else {
+ /* (commonAttr->static_clk == false) => dynamic mode */
+ fec_oc_dto_mode = FEC_OC_DTO_MODE_DYNAMIC__M;
+ fec_oc_fct_mode = FEC_OC_FCT_MODE__PRE;
+ fec_oc_tmd_int_upd_rate = 5;
+ }
+
+ /* Write appropriate registers with requested configuration */
+ status = write16(state, FEC_OC_DTO_BURST_LEN__A, fec_oc_dto_burst_len);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_DTO_PERIOD__A, fec_oc_dto_period);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_DTO_MODE__A, fec_oc_dto_mode);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_FCT_MODE__A, fec_oc_fct_mode);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_MODE__A, fec_oc_reg_mode);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_IPR_MODE__A, fec_oc_reg_ipr_mode);
+ if (status < 0)
+ goto error;
+
+ /* Rate integration settings */
+ status = write32(state, FEC_OC_RCN_CTL_RATE_LO__A, fec_oc_rcn_ctl_rate);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_INT_UPD_RATE__A,
+ fec_oc_tmd_int_upd_rate);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_MODE__A, fec_oc_tmd_mode);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int mpegts_configure_polarity(struct drxk_state *state)
+{
+ u16 fec_oc_reg_ipr_invert = 0;
+
+ /* Data mask for the output data byte */
+ u16 invert_data_mask =
+ FEC_OC_IPR_INVERT_MD7__M | FEC_OC_IPR_INVERT_MD6__M |
+ FEC_OC_IPR_INVERT_MD5__M | FEC_OC_IPR_INVERT_MD4__M |
+ FEC_OC_IPR_INVERT_MD3__M | FEC_OC_IPR_INVERT_MD2__M |
+ FEC_OC_IPR_INVERT_MD1__M | FEC_OC_IPR_INVERT_MD0__M;
+
+ dprintk(1, "\n");
+
+ /* Control selective inversion of output bits */
+ fec_oc_reg_ipr_invert &= (~(invert_data_mask));
+ if (state->m_invert_data)
+ fec_oc_reg_ipr_invert |= invert_data_mask;
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MERR__M));
+ if (state->m_invert_err)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MERR__M;
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MSTRT__M));
+ if (state->m_invert_str)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MSTRT__M;
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MVAL__M));
+ if (state->m_invert_val)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MVAL__M;
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MCLK__M));
+ if (state->m_invert_clk)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MCLK__M;
+
+ return write16(state, FEC_OC_IPR_INVERT__A, fec_oc_reg_ipr_invert);
+}
+
+#define SCU_RAM_AGC_KI_INV_RF_POL__M 0x4000
+
+static int set_agc_rf(struct drxk_state *state,
+ struct s_cfg_agc *p_agc_cfg, bool is_dtv)
+{
+ int status = -EINVAL;
+ u16 data = 0;
+ struct s_cfg_agc *p_if_agc_settings;
+
+ dprintk(1, "\n");
+
+ if (p_agc_cfg == NULL)
+ goto error;
+
+ switch (p_agc_cfg->ctrl_mode) {
+ case DRXK_AGC_CTRL_AUTO:
+ /* Enable RF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+
+ /* Enable SCU RF AGC loop */
+ data &= ~SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
+
+ /* Polarity */
+ if (state->m_rf_agc_pol)
+ data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Set speed (using complementary reduction value) */
+ status = read16(state, SCU_RAM_AGC_KI_RED__A, &data);
+ if (status < 0)
+ goto error;
+
+ data &= ~SCU_RAM_AGC_KI_RED_RAGC_RED__M;
+ data |= (~(p_agc_cfg->speed <<
+ SCU_RAM_AGC_KI_RED_RAGC_RED__B)
+ & SCU_RAM_AGC_KI_RED_RAGC_RED__M);
+
+ status = write16(state, SCU_RAM_AGC_KI_RED__A, data);
+ if (status < 0)
+ goto error;
+
+ if (is_dvbt(state))
+ p_if_agc_settings = &state->m_dvbt_if_agc_cfg;
+ else if (is_qam(state))
+ p_if_agc_settings = &state->m_qam_if_agc_cfg;
+ else
+ p_if_agc_settings = &state->m_atv_if_agc_cfg;
+ if (p_if_agc_settings == NULL) {
+ status = -EINVAL;
+ goto error;
+ }
+
+ /* Set TOP, only if IF-AGC is in AUTO mode */
+ if (p_if_agc_settings->ctrl_mode == DRXK_AGC_CTRL_AUTO) {
+ status = write16(state,
+ SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A,
+ p_agc_cfg->top);
+ if (status < 0)
+ goto error;
+ }
+
+ /* Cut-Off current */
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A,
+ p_agc_cfg->cut_off_current);
+ if (status < 0)
+ goto error;
+
+ /* Max. output level */
+ status = write16(state, SCU_RAM_AGC_RF_MAX__A,
+ p_agc_cfg->max_output_level);
+ if (status < 0)
+ goto error;
+
+ break;
+
+ case DRXK_AGC_CTRL_USER:
+ /* Enable RF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Disable SCU RF AGC loop */
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+ data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
+ if (state->m_rf_agc_pol)
+ data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* SCU c.o.c. to 0, enabling full control range */
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, 0);
+ if (status < 0)
+ goto error;
+
+ /* Write value to output pin */
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A,
+ p_agc_cfg->output_level);
+ if (status < 0)
+ goto error;
+ break;
+
+ case DRXK_AGC_CTRL_OFF:
+ /* Disable RF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data |= IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Disable SCU RF AGC loop */
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+ data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+ break;
+
+ default:
+ status = -EINVAL;
+
+ }
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+#define SCU_RAM_AGC_KI_INV_IF_POL__M 0x2000
+
+static int set_agc_if(struct drxk_state *state,
+ struct s_cfg_agc *p_agc_cfg, bool is_dtv)
+{
+ u16 data = 0;
+ int status = 0;
+ struct s_cfg_agc *p_rf_agc_settings;
+
+ dprintk(1, "\n");
+
+ switch (p_agc_cfg->ctrl_mode) {
+ case DRXK_AGC_CTRL_AUTO:
+
+ /* Enable IF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+
+ /* Enable SCU IF AGC loop */
+ data &= ~SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
+
+ /* Polarity */
+ if (state->m_if_agc_pol)
+ data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Set speed (using complementary reduction value) */
+ status = read16(state, SCU_RAM_AGC_KI_RED__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~SCU_RAM_AGC_KI_RED_IAGC_RED__M;
+ data |= (~(p_agc_cfg->speed <<
+ SCU_RAM_AGC_KI_RED_IAGC_RED__B)
+ & SCU_RAM_AGC_KI_RED_IAGC_RED__M);
+
+ status = write16(state, SCU_RAM_AGC_KI_RED__A, data);
+ if (status < 0)
+ goto error;
+
+ if (is_qam(state))
+ p_rf_agc_settings = &state->m_qam_rf_agc_cfg;
+ else
+ p_rf_agc_settings = &state->m_atv_rf_agc_cfg;
+ if (p_rf_agc_settings == NULL)
+ return -1;
+ /* Restore TOP */
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A,
+ p_rf_agc_settings->top);
+ if (status < 0)
+ goto error;
+ break;
+
+ case DRXK_AGC_CTRL_USER:
+
+ /* Enable IF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+
+ /* Disable SCU IF AGC loop */
+ data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
+
+ /* Polarity */
+ if (state->m_if_agc_pol)
+ data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Write value to output pin */
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A,
+ p_agc_cfg->output_level);
+ if (status < 0)
+ goto error;
+ break;
+
+ case DRXK_AGC_CTRL_OFF:
+
+ /* Disable If AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data |= IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Disable SCU IF AGC loop */
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+ data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+ break;
+ } /* switch (agcSettingsIf->ctrl_mode) */
+
+ /* always set the top to support
+ configurations without if-loop */
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, p_agc_cfg->top);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int get_qam_signal_to_noise(struct drxk_state *state,
+ s32 *p_signal_to_noise)
+{
+ int status = 0;
+ u16 qam_sl_err_power = 0; /* accum. error between
+ raw and sliced symbols */
+ u32 qam_sl_sig_power = 0; /* used for MER, depends of
+ QAM modulation */
+ u32 qam_sl_mer = 0; /* QAM MER */
+
+ dprintk(1, "\n");
+
+ /* MER calculation */
+
+ /* get the register value needed for MER */
+ status = read16(state, QAM_SL_ERR_POWER__A, &qam_sl_err_power);
+ if (status < 0) {
+ pr_err("Error %d on %s\n", status, __func__);
+ return -EINVAL;
+ }
+
+ switch (state->props.modulation) {
+ case QAM_16:
+ qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM16 << 2;
+ break;
+ case QAM_32:
+ qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM32 << 2;
+ break;
+ case QAM_64:
+ qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM64 << 2;
+ break;
+ case QAM_128:
+ qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM128 << 2;
+ break;
+ default:
+ case QAM_256:
+ qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM256 << 2;
+ break;
+ }
+
+ if (qam_sl_err_power > 0) {
+ qam_sl_mer = log10times100(qam_sl_sig_power) -
+ log10times100((u32) qam_sl_err_power);
+ }
+ *p_signal_to_noise = qam_sl_mer;
+
+ return status;
+}
+
+static int get_dvbt_signal_to_noise(struct drxk_state *state,
+ s32 *p_signal_to_noise)
+{
+ int status;
+ u16 reg_data = 0;
+ u32 eq_reg_td_sqr_err_i = 0;
+ u32 eq_reg_td_sqr_err_q = 0;
+ u16 eq_reg_td_sqr_err_exp = 0;
+ u16 eq_reg_td_tps_pwr_ofs = 0;
+ u16 eq_reg_td_req_smb_cnt = 0;
+ u32 tps_cnt = 0;
+ u32 sqr_err_iq = 0;
+ u32 a = 0;
+ u32 b = 0;
+ u32 c = 0;
+ u32 i_mer = 0;
+ u16 transmission_params = 0;
+
+ dprintk(1, "\n");
+
+ status = read16(state, OFDM_EQ_TOP_TD_TPS_PWR_OFS__A,
+ &eq_reg_td_tps_pwr_ofs);
+ if (status < 0)
+ goto error;
+ status = read16(state, OFDM_EQ_TOP_TD_REQ_SMB_CNT__A,
+ &eq_reg_td_req_smb_cnt);
+ if (status < 0)
+ goto error;
+ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_EXP__A,
+ &eq_reg_td_sqr_err_exp);
+ if (status < 0)
+ goto error;
+ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_I__A,
+ &reg_data);
+ if (status < 0)
+ goto error;
+ /* Extend SQR_ERR_I operational range */
+ eq_reg_td_sqr_err_i = (u32) reg_data;
+ if ((eq_reg_td_sqr_err_exp > 11) &&
+ (eq_reg_td_sqr_err_i < 0x00000FFFUL)) {
+ eq_reg_td_sqr_err_i += 0x00010000UL;
+ }
+ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_Q__A, &reg_data);
+ if (status < 0)
+ goto error;
+ /* Extend SQR_ERR_Q operational range */
+ eq_reg_td_sqr_err_q = (u32) reg_data;
+ if ((eq_reg_td_sqr_err_exp > 11) &&
+ (eq_reg_td_sqr_err_q < 0x00000FFFUL))
+ eq_reg_td_sqr_err_q += 0x00010000UL;
+
+ status = read16(state, OFDM_SC_RA_RAM_OP_PARAM__A,
+ &transmission_params);
+ if (status < 0)
+ goto error;
+
+ /* Check input data for MER */
+
+ /* MER calculation (in 0.1 dB) without math.h */
+ if ((eq_reg_td_tps_pwr_ofs == 0) || (eq_reg_td_req_smb_cnt == 0))
+ i_mer = 0;
+ else if ((eq_reg_td_sqr_err_i + eq_reg_td_sqr_err_q) == 0) {
+ /* No error at all, this must be the HW reset value
+ * Apparently no first measurement yet
+ * Set MER to 0.0 */
+ i_mer = 0;
+ } else {
+ sqr_err_iq = (eq_reg_td_sqr_err_i + eq_reg_td_sqr_err_q) <<
+ eq_reg_td_sqr_err_exp;
+ if ((transmission_params &
+ OFDM_SC_RA_RAM_OP_PARAM_MODE__M)
+ == OFDM_SC_RA_RAM_OP_PARAM_MODE_2K)
+ tps_cnt = 17;
+ else
+ tps_cnt = 68;
+
+ /* IMER = 100 * log10 (x)
+ where x = (eq_reg_td_tps_pwr_ofs^2 *
+ eq_reg_td_req_smb_cnt * tps_cnt)/sqr_err_iq
+
+ => IMER = a + b -c
+ where a = 100 * log10 (eq_reg_td_tps_pwr_ofs^2)
+ b = 100 * log10 (eq_reg_td_req_smb_cnt * tps_cnt)
+ c = 100 * log10 (sqr_err_iq)
+ */
+
+ /* log(x) x = 9bits * 9bits->18 bits */
+ a = log10times100(eq_reg_td_tps_pwr_ofs *
+ eq_reg_td_tps_pwr_ofs);
+ /* log(x) x = 16bits * 7bits->23 bits */
+ b = log10times100(eq_reg_td_req_smb_cnt * tps_cnt);
+ /* log(x) x = (16bits + 16bits) << 15 ->32 bits */
+ c = log10times100(sqr_err_iq);
+
+ i_mer = a + b - c;
+ }
+ *p_signal_to_noise = i_mer;
+
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int get_signal_to_noise(struct drxk_state *state, s32 *p_signal_to_noise)
+{
+ dprintk(1, "\n");
+
+ *p_signal_to_noise = 0;
+ switch (state->m_operation_mode) {
+ case OM_DVBT:
+ return get_dvbt_signal_to_noise(state, p_signal_to_noise);
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_C:
+ return get_qam_signal_to_noise(state, p_signal_to_noise);
+ default:
+ break;
+ }
+ return 0;
+}
+
+#if 0
+static int get_dvbt_quality(struct drxk_state *state, s32 *p_quality)
+{
+ /* SNR Values for quasi errorfree reception rom Nordig 2.2 */
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ static s32 QE_SN[] = {
+ 51, /* QPSK 1/2 */
+ 69, /* QPSK 2/3 */
+ 79, /* QPSK 3/4 */
+ 89, /* QPSK 5/6 */
+ 97, /* QPSK 7/8 */
+ 108, /* 16-QAM 1/2 */
+ 131, /* 16-QAM 2/3 */
+ 146, /* 16-QAM 3/4 */
+ 156, /* 16-QAM 5/6 */
+ 160, /* 16-QAM 7/8 */
+ 165, /* 64-QAM 1/2 */
+ 187, /* 64-QAM 2/3 */
+ 202, /* 64-QAM 3/4 */
+ 216, /* 64-QAM 5/6 */
+ 225, /* 64-QAM 7/8 */
+ };
+
+ *p_quality = 0;
+
+ do {
+ s32 signal_to_noise = 0;
+ u16 constellation = 0;
+ u16 code_rate = 0;
+ u32 signal_to_noise_rel;
+ u32 ber_quality;
+
+ status = get_dvbt_signal_to_noise(state, &signal_to_noise);
+ if (status < 0)
+ break;
+ status = read16(state, OFDM_EQ_TOP_TD_TPS_CONST__A,
+ &constellation);
+ if (status < 0)
+ break;
+ constellation &= OFDM_EQ_TOP_TD_TPS_CONST__M;
+
+ status = read16(state, OFDM_EQ_TOP_TD_TPS_CODE_HP__A,
+ &code_rate);
+ if (status < 0)
+ break;
+ code_rate &= OFDM_EQ_TOP_TD_TPS_CODE_HP__M;
+
+ if (constellation > OFDM_EQ_TOP_TD_TPS_CONST_64QAM ||
+ code_rate > OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8)
+ break;
+ signal_to_noise_rel = signal_to_noise -
+ QE_SN[constellation * 5 + code_rate];
+ ber_quality = 100;
+
+ if (signal_to_noise_rel < -70)
+ *p_quality = 0;
+ else if (signal_to_noise_rel < 30)
+ *p_quality = ((signal_to_noise_rel + 70) *
+ ber_quality) / 100;
+ else
+ *p_quality = ber_quality;
+ } while (0);
+ return 0;
+};
+
+static int get_dvbc_quality(struct drxk_state *state, s32 *p_quality)
+{
+ int status = 0;
+ *p_quality = 0;
+
+ dprintk(1, "\n");
+
+ do {
+ u32 signal_to_noise = 0;
+ u32 ber_quality = 100;
+ u32 signal_to_noise_rel = 0;
+
+ status = get_qam_signal_to_noise(state, &signal_to_noise);
+ if (status < 0)
+ break;
+
+ switch (state->props.modulation) {
+ case QAM_16:
+ signal_to_noise_rel = signal_to_noise - 200;
+ break;
+ case QAM_32:
+ signal_to_noise_rel = signal_to_noise - 230;
+ break; /* Not in NorDig */
+ case QAM_64:
+ signal_to_noise_rel = signal_to_noise - 260;
+ break;
+ case QAM_128:
+ signal_to_noise_rel = signal_to_noise - 290;
+ break;
+ default:
+ case QAM_256:
+ signal_to_noise_rel = signal_to_noise - 320;
+ break;
+ }
+
+ if (signal_to_noise_rel < -70)
+ *p_quality = 0;
+ else if (signal_to_noise_rel < 30)
+ *p_quality = ((signal_to_noise_rel + 70) *
+ ber_quality) / 100;
+ else
+ *p_quality = ber_quality;
+ } while (0);
+
+ return status;
+}
+
+static int get_quality(struct drxk_state *state, s32 *p_quality)
+{
+ dprintk(1, "\n");
+
+ switch (state->m_operation_mode) {
+ case OM_DVBT:
+ return get_dvbt_quality(state, p_quality);
+ case OM_QAM_ITU_A:
+ return get_dvbc_quality(state, p_quality);
+ default:
+ break;
+ }
+
+ return 0;
+}
+#endif
+
+/* Free data ram in SIO HI */
+#define SIO_HI_RA_RAM_USR_BEGIN__A 0x420040
+#define SIO_HI_RA_RAM_USR_END__A 0x420060
+
+#define DRXK_HI_ATOMIC_BUF_START (SIO_HI_RA_RAM_USR_BEGIN__A)
+#define DRXK_HI_ATOMIC_BUF_END (SIO_HI_RA_RAM_USR_BEGIN__A + 7)
+#define DRXK_HI_ATOMIC_READ SIO_HI_RA_RAM_PAR_3_ACP_RW_READ
+#define DRXK_HI_ATOMIC_WRITE SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE
+
+#define DRXDAP_FASI_ADDR2BLOCK(addr) (((addr) >> 22) & 0x3F)
+#define DRXDAP_FASI_ADDR2BANK(addr) (((addr) >> 16) & 0x3F)
+#define DRXDAP_FASI_ADDR2OFFSET(addr) ((addr) & 0x7FFF)
+
+static int ConfigureI2CBridge(struct drxk_state *state, bool b_enable_bridge)
+{
+ int status = -EINVAL;
+
+ dprintk(1, "\n");
+
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
+ return 0;
+ if (state->m_drxk_state == DRXK_POWERED_DOWN)
+ goto error;
+
+ if (state->no_i2c_bridge)
+ return 0;
+
+ status = write16(state, SIO_HI_RA_RAM_PAR_1__A,
+ SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
+ if (status < 0)
+ goto error;
+ if (b_enable_bridge) {
+ status = write16(state, SIO_HI_RA_RAM_PAR_2__A,
+ SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED);
+ if (status < 0)
+ goto error;
+ } else {
+ status = write16(state, SIO_HI_RA_RAM_PAR_2__A,
+ SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN);
+ if (status < 0)
+ goto error;
+ }
+
+ status = hi_command(state, SIO_HI_RA_RAM_CMD_BRDCTRL, NULL);
+
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int set_pre_saw(struct drxk_state *state,
+ struct s_cfg_pre_saw *p_pre_saw_cfg)
+{
+ int status = -EINVAL;
+
+ dprintk(1, "\n");
+
+ if ((p_pre_saw_cfg == NULL)
+ || (p_pre_saw_cfg->reference > IQM_AF_PDREF__M))
+ goto error;
+
+ status = write16(state, IQM_AF_PDREF__A, p_pre_saw_cfg->reference);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int bl_direct_cmd(struct drxk_state *state, u32 target_addr,
+ u16 rom_offset, u16 nr_of_elements, u32 time_out)
+{
+ u16 bl_status = 0;
+ u16 offset = (u16) ((target_addr >> 0) & 0x00FFFF);
+ u16 blockbank = (u16) ((target_addr >> 16) & 0x000FFF);
+ int status;
+ unsigned long end;
+
+ dprintk(1, "\n");
+
+ mutex_lock(&state->mutex);
+ status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_DIRECT);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_TGT_HDR__A, blockbank);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_TGT_ADDR__A, offset);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_SRC_ADDR__A, rom_offset);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_SRC_LEN__A, nr_of_elements);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON);
+ if (status < 0)
+ goto error;
+
+ end = jiffies + msecs_to_jiffies(time_out);
+ do {
+ status = read16(state, SIO_BL_STATUS__A, &bl_status);
+ if (status < 0)
+ goto error;
+ } while ((bl_status == 0x1) && time_is_after_jiffies(end));
+ if (bl_status == 0x1) {
+ pr_err("SIO not ready\n");
+ status = -EINVAL;
+ goto error2;
+ }
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+error2:
+ mutex_unlock(&state->mutex);
+ return status;
+
+}
+
+static int adc_sync_measurement(struct drxk_state *state, u16 *count)
+{
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ /* start measurement */
+ status = write16(state, IQM_AF_COMM_EXEC__A, IQM_AF_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_START_LOCK__A, 1);
+ if (status < 0)
+ goto error;
+
+ *count = 0;
+ status = read16(state, IQM_AF_PHASE0__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == 127)
+ *count = *count + 1;
+ status = read16(state, IQM_AF_PHASE1__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == 127)
+ *count = *count + 1;
+ status = read16(state, IQM_AF_PHASE2__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == 127)
+ *count = *count + 1;
+
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int adc_synchronization(struct drxk_state *state)
+{
+ u16 count = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ status = adc_sync_measurement(state, &count);
+ if (status < 0)
+ goto error;
+
+ if (count == 1) {
+ /* Try sampling on a different edge */
+ u16 clk_neg = 0;
+
+ status = read16(state, IQM_AF_CLKNEG__A, &clk_neg);
+ if (status < 0)
+ goto error;
+ if ((clk_neg & IQM_AF_CLKNEG_CLKNEGDATA__M) ==
+ IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS) {
+ clk_neg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
+ clk_neg |=
+ IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG;
+ } else {
+ clk_neg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
+ clk_neg |=
+ IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS;
+ }
+ status = write16(state, IQM_AF_CLKNEG__A, clk_neg);
+ if (status < 0)
+ goto error;
+ status = adc_sync_measurement(state, &count);
+ if (status < 0)
+ goto error;
+ }
+
+ if (count < 2)
+ status = -EINVAL;
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int set_frequency_shifter(struct drxk_state *state,
+ u16 intermediate_freqk_hz,
+ s32 tuner_freq_offset, bool is_dtv)
+{
+ bool select_pos_image = false;
+ u32 rf_freq_residual = tuner_freq_offset;
+ u32 fm_frequency_shift = 0;
+ bool tuner_mirror = !state->m_b_mirror_freq_spect;
+ u32 adc_freq;
+ bool adc_flip;
+ int status;
+ u32 if_freq_actual;
+ u32 sampling_frequency = (u32) (state->m_sys_clock_freq / 3);
+ u32 frequency_shift;
+ bool image_to_select;
+
+ dprintk(1, "\n");
+
+ /*
+ Program frequency shifter
+ No need to account for mirroring on RF
+ */
+ if (is_dtv) {
+ if ((state->m_operation_mode == OM_QAM_ITU_A) ||
+ (state->m_operation_mode == OM_QAM_ITU_C) ||
+ (state->m_operation_mode == OM_DVBT))
+ select_pos_image = true;
+ else
+ select_pos_image = false;
+ }
+ if (tuner_mirror)
+ /* tuner doesn't mirror */
+ if_freq_actual = intermediate_freqk_hz +
+ rf_freq_residual + fm_frequency_shift;
+ else
+ /* tuner mirrors */
+ if_freq_actual = intermediate_freqk_hz -
+ rf_freq_residual - fm_frequency_shift;
+ if (if_freq_actual > sampling_frequency / 2) {
+ /* adc mirrors */
+ adc_freq = sampling_frequency - if_freq_actual;
+ adc_flip = true;
+ } else {
+ /* adc doesn't mirror */
+ adc_freq = if_freq_actual;
+ adc_flip = false;
+ }
+
+ frequency_shift = adc_freq;
+ image_to_select = state->m_rfmirror ^ tuner_mirror ^
+ adc_flip ^ select_pos_image;
+ state->m_iqm_fs_rate_ofs =
+ Frac28a((frequency_shift), sampling_frequency);
+
+ if (image_to_select)
+ state->m_iqm_fs_rate_ofs = ~state->m_iqm_fs_rate_ofs + 1;
+
+ /* Program frequency shifter with tuner offset compensation */
+ /* frequency_shift += tuner_freq_offset; TODO */
+ status = write32(state, IQM_FS_RATE_OFS_LO__A,
+ state->m_iqm_fs_rate_ofs);
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int init_agc(struct drxk_state *state, bool is_dtv)
+{
+ u16 ingain_tgt = 0;
+ u16 ingain_tgt_min = 0;
+ u16 ingain_tgt_max = 0;
+ u16 clp_cyclen = 0;
+ u16 clp_sum_min = 0;
+ u16 clp_dir_to = 0;
+ u16 sns_sum_min = 0;
+ u16 sns_sum_max = 0;
+ u16 clp_sum_max = 0;
+ u16 sns_dir_to = 0;
+ u16 ki_innergain_min = 0;
+ u16 if_iaccu_hi_tgt = 0;
+ u16 if_iaccu_hi_tgt_min = 0;
+ u16 if_iaccu_hi_tgt_max = 0;
+ u16 data = 0;
+ u16 fast_clp_ctrl_delay = 0;
+ u16 clp_ctrl_mode = 0;
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ /* Common settings */
+ sns_sum_max = 1023;
+ if_iaccu_hi_tgt_min = 2047;
+ clp_cyclen = 500;
+ clp_sum_max = 1023;
+
+ /* AGCInit() not available for DVBT; init done in microcode */
+ if (!is_qam(state)) {
+ pr_err("%s: mode %d is not DVB-C\n",
+ __func__, state->m_operation_mode);
+ return -EINVAL;
+ }
+
+ /* FIXME: Analog TV AGC require different settings */
+
+ /* Standard specific settings */
+ clp_sum_min = 8;
+ clp_dir_to = (u16) -9;
+ clp_ctrl_mode = 0;
+ sns_sum_min = 8;
+ sns_dir_to = (u16) -9;
+ ki_innergain_min = (u16) -1030;
+ if_iaccu_hi_tgt_max = 0x2380;
+ if_iaccu_hi_tgt = 0x2380;
+ ingain_tgt_min = 0x0511;
+ ingain_tgt = 0x0511;
+ ingain_tgt_max = 5119;
+ fast_clp_ctrl_delay = state->m_qam_if_agc_cfg.fast_clip_ctrl_delay;
+
+ status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A,
+ fast_clp_ctrl_delay);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_AGC_CLP_CTRL_MODE__A, clp_ctrl_mode);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT__A, ingain_tgt);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, ingain_tgt_min);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, ingain_tgt_max);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A,
+ if_iaccu_hi_tgt_min);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A,
+ if_iaccu_hi_tgt_max);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_LO__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_LO__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_SUM_MAX__A, clp_sum_max);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_SUM_MAX__A, sns_sum_max);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_AGC_KI_INNERGAIN_MIN__A,
+ ki_innergain_min);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT__A,
+ if_iaccu_hi_tgt);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_CYCLEN__A, clp_cyclen);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MAX__A, 1023);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MIN__A, (u16) -1023);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A, 50);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_AGC_KI_MAXMINGAIN_TH__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_SUM_MIN__A, clp_sum_min);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_SUM_MIN__A, sns_sum_min);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_DIR_TO__A, clp_dir_to);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_DIR_TO__A, sns_dir_to);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_MAXGAIN__A, 0x0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_MIN__A, 0x0117);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_MAX__A, 0x0657);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_SUM__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_CYCCNT__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_DIR_WD__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_DIR_STP__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_SUM__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_CYCCNT__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_DIR_WD__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_DIR_STP__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_CYCLEN__A, 500);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_CYCLEN__A, 500);
+ if (status < 0)
+ goto error;
+
+ /* Initialize inner-loop KI gain factors */
+ status = read16(state, SCU_RAM_AGC_KI__A, &data);
+ if (status < 0)
+ goto error;
+
+ data = 0x0657;
+ data &= ~SCU_RAM_AGC_KI_RF__M;
+ data |= (DRXK_KI_RAGC_QAM << SCU_RAM_AGC_KI_RF__B);
+ data &= ~SCU_RAM_AGC_KI_IF__M;
+ data |= (DRXK_KI_IAGC_QAM << SCU_RAM_AGC_KI_IF__B);
+
+ status = write16(state, SCU_RAM_AGC_KI__A, data);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int dvbtqam_get_acc_pkt_err(struct drxk_state *state, u16 *packet_err)
+{
+ int status;
+
+ dprintk(1, "\n");
+ if (packet_err == NULL)
+ status = write16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0);
+ else
+ status = read16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A,
+ packet_err);
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int dvbt_sc_command(struct drxk_state *state,
+ u16 cmd, u16 subcmd,
+ u16 param0, u16 param1, u16 param2,
+ u16 param3, u16 param4)
+{
+ u16 cur_cmd = 0;
+ u16 err_code = 0;
+ u16 retry_cnt = 0;
+ u16 sc_exec = 0;
+ int status;
+
+ dprintk(1, "\n");
+ status = read16(state, OFDM_SC_COMM_EXEC__A, &sc_exec);
+ if (sc_exec != 1) {
+ /* SC is not running */
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ /* Wait until sc is ready to receive command */
+ retry_cnt = 0;
+ do {
+ usleep_range(1000, 2000);
+ status = read16(state, OFDM_SC_RA_RAM_CMD__A, &cur_cmd);
+ retry_cnt++;
+ } while ((cur_cmd != 0) && (retry_cnt < DRXK_MAX_RETRIES));
+ if (retry_cnt >= DRXK_MAX_RETRIES && (status < 0))
+ goto error;
+
+ /* Write sub-command */
+ switch (cmd) {
+ /* All commands using sub-cmd */
+ case OFDM_SC_RA_RAM_CMD_PROC_START:
+ case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
+ case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
+ status = write16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, subcmd);
+ if (status < 0)
+ goto error;
+ break;
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ /* Write needed parameters and the command */
+ status = 0;
+ switch (cmd) {
+ /* All commands using 5 parameters */
+ /* All commands using 4 parameters */
+ /* All commands using 3 parameters */
+ /* All commands using 2 parameters */
+ case OFDM_SC_RA_RAM_CMD_PROC_START:
+ case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
+ case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
+ status |= write16(state, OFDM_SC_RA_RAM_PARAM1__A, param1);
+ fallthrough; /* All commands using 1 parameters */
+ case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING:
+ case OFDM_SC_RA_RAM_CMD_USER_IO:
+ status |= write16(state, OFDM_SC_RA_RAM_PARAM0__A, param0);
+ fallthrough; /* All commands using 0 parameters */
+ case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM:
+ case OFDM_SC_RA_RAM_CMD_NULL:
+ /* Write command */
+ status |= write16(state, OFDM_SC_RA_RAM_CMD__A, cmd);
+ break;
+ default:
+ /* Unknown command */
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ /* Wait until sc is ready processing command */
+ retry_cnt = 0;
+ do {
+ usleep_range(1000, 2000);
+ status = read16(state, OFDM_SC_RA_RAM_CMD__A, &cur_cmd);
+ retry_cnt++;
+ } while ((cur_cmd != 0) && (retry_cnt < DRXK_MAX_RETRIES));
+ if (retry_cnt >= DRXK_MAX_RETRIES && (status < 0))
+ goto error;
+
+ /* Check for illegal cmd */
+ status = read16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, &err_code);
+ if (err_code == 0xFFFF) {
+ /* illegal command */
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ /* Retrieve results parameters from SC */
+ switch (cmd) {
+ /* All commands yielding 5 results */
+ /* All commands yielding 4 results */
+ /* All commands yielding 3 results */
+ /* All commands yielding 2 results */
+ /* All commands yielding 1 result */
+ case OFDM_SC_RA_RAM_CMD_USER_IO:
+ case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM:
+ status = read16(state, OFDM_SC_RA_RAM_PARAM0__A, &(param0));
+ break;
+ /* All commands yielding 0 results */
+ case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING:
+ case OFDM_SC_RA_RAM_CMD_SET_TIMER:
+ case OFDM_SC_RA_RAM_CMD_PROC_START:
+ case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
+ case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
+ case OFDM_SC_RA_RAM_CMD_NULL:
+ break;
+ default:
+ /* Unknown command */
+ status = -EINVAL;
+ break;
+ } /* switch (cmd->cmd) */
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int power_up_dvbt(struct drxk_state *state)
+{
+ enum drx_power_mode power_mode = DRX_POWER_UP;
+ int status;
+
+ dprintk(1, "\n");
+ status = ctrl_power_mode(state, &power_mode);
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int dvbt_ctrl_set_inc_enable(struct drxk_state *state, bool *enabled)
+{
+ int status;
+
+ dprintk(1, "\n");
+ if (*enabled)
+ status = write16(state, IQM_CF_BYPASSDET__A, 0);
+ else
+ status = write16(state, IQM_CF_BYPASSDET__A, 1);
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+#define DEFAULT_FR_THRES_8K 4000
+static int dvbt_ctrl_set_fr_enable(struct drxk_state *state, bool *enabled)
+{
+
+ int status;
+
+ dprintk(1, "\n");
+ if (*enabled) {
+ /* write mask to 1 */
+ status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A,
+ DEFAULT_FR_THRES_8K);
+ } else {
+ /* write mask to 0 */
+ status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A, 0);
+ }
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int dvbt_ctrl_set_echo_threshold(struct drxk_state *state,
+ struct drxk_cfg_dvbt_echo_thres_t *echo_thres)
+{
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+ status = read16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, &data);
+ if (status < 0)
+ goto error;
+
+ switch (echo_thres->fft_mode) {
+ case DRX_FFTMODE_2K:
+ data &= ~OFDM_SC_RA_RAM_ECHO_THRES_2K__M;
+ data |= ((echo_thres->threshold <<
+ OFDM_SC_RA_RAM_ECHO_THRES_2K__B)
+ & (OFDM_SC_RA_RAM_ECHO_THRES_2K__M));
+ break;
+ case DRX_FFTMODE_8K:
+ data &= ~OFDM_SC_RA_RAM_ECHO_THRES_8K__M;
+ data |= ((echo_thres->threshold <<
+ OFDM_SC_RA_RAM_ECHO_THRES_8K__B)
+ & (OFDM_SC_RA_RAM_ECHO_THRES_8K__M));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ status = write16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, data);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int dvbt_ctrl_set_sqi_speed(struct drxk_state *state,
+ enum drxk_cfg_dvbt_sqi_speed *speed)
+{
+ int status = -EINVAL;
+
+ dprintk(1, "\n");
+
+ switch (*speed) {
+ case DRXK_DVBT_SQI_SPEED_FAST:
+ case DRXK_DVBT_SQI_SPEED_MEDIUM:
+ case DRXK_DVBT_SQI_SPEED_SLOW:
+ break;
+ default:
+ goto error;
+ }
+ status = write16(state, SCU_RAM_FEC_PRE_RS_BER_FILTER_SH__A,
+ (u16) *speed);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/*
+* \brief Activate DVBT specific presets
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+*
+* Called in DVBTSetStandard
+*
+*/
+static int dvbt_activate_presets(struct drxk_state *state)
+{
+ int status;
+ bool setincenable = false;
+ bool setfrenable = true;
+
+ struct drxk_cfg_dvbt_echo_thres_t echo_thres2k = { 0, DRX_FFTMODE_2K };
+ struct drxk_cfg_dvbt_echo_thres_t echo_thres8k = { 0, DRX_FFTMODE_8K };
+
+ dprintk(1, "\n");
+ status = dvbt_ctrl_set_inc_enable(state, &setincenable);
+ if (status < 0)
+ goto error;
+ status = dvbt_ctrl_set_fr_enable(state, &setfrenable);
+ if (status < 0)
+ goto error;
+ status = dvbt_ctrl_set_echo_threshold(state, &echo_thres2k);
+ if (status < 0)
+ goto error;
+ status = dvbt_ctrl_set_echo_threshold(state, &echo_thres8k);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A,
+ state->m_dvbt_if_agc_cfg.ingain_tgt_max);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/*
+* \brief Initialize channelswitch-independent settings for DVBT.
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+*
+* For ROM code channel filter taps are loaded from the bootloader. For microcode
+* the DVB-T taps from the drxk_filters.h are used.
+*/
+static int set_dvbt_standard(struct drxk_state *state,
+ enum operation_mode o_mode)
+{
+ u16 cmd_result = 0;
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ power_up_dvbt(state);
+ /* added antenna switch */
+ switch_antenna_to_dvbt(state);
+ /* send OFDM reset command */
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_OFDM
+ | SCU_RAM_COMMAND_CMD_DEMOD_RESET,
+ 0, NULL, 1, &cmd_result);
+ if (status < 0)
+ goto error;
+
+ /* send OFDM setenv command */
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM
+ | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV,
+ 0, NULL, 1, &cmd_result);
+ if (status < 0)
+ goto error;
+
+ /* reset datapath for OFDM, processors first */
+ status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
+ if (status < 0)
+ goto error;
+
+ /* IQM setup */
+ /* synchronize on ofdstate->m_festart */
+ status = write16(state, IQM_AF_UPD_SEL__A, 1);
+ if (status < 0)
+ goto error;
+ /* window size for clipping ADC detection */
+ status = write16(state, IQM_AF_CLP_LEN__A, 0);
+ if (status < 0)
+ goto error;
+ /* window size for sense pre-SAW detection */
+ status = write16(state, IQM_AF_SNS_LEN__A, 0);
+ if (status < 0)
+ goto error;
+ /* sense threshold for sense pre-SAW detection */
+ status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC);
+ if (status < 0)
+ goto error;
+ status = set_iqm_af(state, true);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_AF_AGC_RF__A, 0);
+ if (status < 0)
+ goto error;
+
+ /* Impulse noise cruncher setup */
+ status = write16(state, IQM_AF_INC_LCT__A, 0); /* crunch in IQM_CF */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DET_LCT__A, 0); /* detect in IQM_CF */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_WND_LEN__A, 3); /* peak detector window length */
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_RC_STRETCH__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_OUT_ENA__A, 0x4); /* enable output 2 */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DS_ENA__A, 0x4); /* decimate output 2 */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_SCALE__A, 1600);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_SCALE_SH__A, 0);
+ if (status < 0)
+ goto error;
+
+ /* virtual clipping threshold for clipping ADC detection */
+ status = write16(state, IQM_AF_CLP_TH__A, 448);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DATATH__A, 495); /* crunching threshold */
+ if (status < 0)
+ goto error;
+
+ status = bl_chain_cmd(state, DRXK_BL_ROM_OFFSET_TAPS_DVBT,
+ DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_CF_PKDTH__A, 2); /* peak detector threshold */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_POW_MEAS_LEN__A, 2);
+ if (status < 0)
+ goto error;
+ /* enable power measurement interrupt */
+ status = write16(state, IQM_CF_COMM_INT_MSK__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ /* IQM will not be reset from here, sync ADC and update/init AGC */
+ status = adc_synchronization(state);
+ if (status < 0)
+ goto error;
+ status = set_pre_saw(state, &state->m_dvbt_pre_saw_cfg);
+ if (status < 0)
+ goto error;
+
+ /* Halt SCU to enable safe non-atomic accesses */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+ if (status < 0)
+ goto error;
+
+ status = set_agc_rf(state, &state->m_dvbt_rf_agc_cfg, true);
+ if (status < 0)
+ goto error;
+ status = set_agc_if(state, &state->m_dvbt_if_agc_cfg, true);
+ if (status < 0)
+ goto error;
+
+ /* Set Noise Estimation notch width and enable DC fix */
+ status = read16(state, OFDM_SC_RA_RAM_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+ data |= OFDM_SC_RA_RAM_CONFIG_NE_FIX_ENABLE__M;
+ status = write16(state, OFDM_SC_RA_RAM_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Activate SCU to enable SCU commands */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ if (!state->m_drxk_a3_rom_code) {
+ /* AGCInit() is not done for DVBT, so set agcfast_clip_ctrl_delay */
+ status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A,
+ state->m_dvbt_if_agc_cfg.fast_clip_ctrl_delay);
+ if (status < 0)
+ goto error;
+ }
+
+ /* OFDM_SC setup */
+#ifdef COMPILE_FOR_NONRT
+ status = write16(state, OFDM_SC_RA_RAM_BE_OPT_DELAY__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_BE_OPT_INIT_DELAY__A, 2);
+ if (status < 0)
+ goto error;
+#endif
+
+ /* FEC setup */
+ status = write16(state, FEC_DI_INPUT_CTL__A, 1); /* OFDM input */
+ if (status < 0)
+ goto error;
+
+
+#ifdef COMPILE_FOR_NONRT
+ status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x400);
+ if (status < 0)
+ goto error;
+#else
+ status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x1000);
+ if (status < 0)
+ goto error;
+#endif
+ status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, 0x0001);
+ if (status < 0)
+ goto error;
+
+ /* Setup MPEG bus */
+ status = mpegts_dto_setup(state, OM_DVBT);
+ if (status < 0)
+ goto error;
+ /* Set DVBT Presets */
+ status = dvbt_activate_presets(state);
+ if (status < 0)
+ goto error;
+
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+/*
+* \brief start dvbt demodulating for channel.
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+*/
+static int dvbt_start(struct drxk_state *state)
+{
+ u16 param1;
+ int status;
+ /* drxk_ofdm_sc_cmd_t scCmd; */
+
+ dprintk(1, "\n");
+ /* start correct processes to get in lock */
+ /* DRXK: OFDM_SC_RA_RAM_PROC_LOCKTRACK is no longer in mapfile! */
+ param1 = OFDM_SC_RA_RAM_LOCKTRACK_MIN;
+ status = dvbt_sc_command(state, OFDM_SC_RA_RAM_CMD_PROC_START, 0,
+ OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M, param1,
+ 0, 0, 0);
+ if (status < 0)
+ goto error;
+ /* start FEC OC */
+ status = mpegts_start(state);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+
+/*============================================================================*/
+
+/*
+* \brief Set up dvbt demodulator for channel.
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+* // original DVBTSetChannel()
+*/
+static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz,
+ s32 tuner_freq_offset)
+{
+ u16 cmd_result = 0;
+ u16 transmission_params = 0;
+ u32 iqm_rc_rate_ofs = 0;
+ u32 bandwidth = 0;
+ u16 param1;
+ int status;
+
+ dprintk(1, "IF =%d, TFO = %d\n",
+ intermediate_freqk_hz, tuner_freq_offset);
+
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM
+ | SCU_RAM_COMMAND_CMD_DEMOD_STOP,
+ 0, NULL, 1, &cmd_result);
+ if (status < 0)
+ goto error;
+
+ /* Halt SCU to enable safe non-atomic accesses */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+ if (status < 0)
+ goto error;
+
+ /* Stop processors */
+ status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+
+ /* Mandatory fix, always stop CP, required to set spl offset back to
+ hardware default (is set to 0 by ucode during pilot detection */
+ status = write16(state, OFDM_CP_COMM_EXEC__A, OFDM_CP_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+
+ /*== Write channel settings to device ================================*/
+
+ /* mode */
+ switch (state->props.transmission_mode) {
+ case TRANSMISSION_MODE_AUTO:
+ case TRANSMISSION_MODE_8K:
+ default:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_MODE_8K;
+ break;
+ case TRANSMISSION_MODE_2K:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_MODE_2K;
+ break;
+ }
+
+ /* guard */
+ switch (state->props.guard_interval) {
+ default:
+ case GUARD_INTERVAL_AUTO: /* try first guess DRX_GUARD_1DIV4 */
+ case GUARD_INTERVAL_1_4:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_4;
+ break;
+ case GUARD_INTERVAL_1_32:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_32;
+ break;
+ case GUARD_INTERVAL_1_16:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_16;
+ break;
+ case GUARD_INTERVAL_1_8:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_8;
+ break;
+ }
+
+ /* hierarchy */
+ switch (state->props.hierarchy) {
+ case HIERARCHY_AUTO:
+ case HIERARCHY_NONE:
+ default: /* try first guess SC_RA_RAM_OP_PARAM_HIER_NO */
+ case HIERARCHY_1:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A1;
+ break;
+ case HIERARCHY_2:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A2;
+ break;
+ case HIERARCHY_4:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A4;
+ break;
+ }
+
+
+ /* modulation */
+ switch (state->props.modulation) {
+ case QAM_AUTO:
+ default: /* try first guess DRX_CONSTELLATION_QAM64 */
+ case QAM_64:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64;
+ break;
+ case QPSK:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK;
+ break;
+ case QAM_16:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16;
+ break;
+ }
+#if 0
+ /* No hierarchical channels support in BDA */
+ /* Priority (only for hierarchical channels) */
+ switch (channel->priority) {
+ case DRX_PRIORITY_LOW:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO;
+ WR16(dev_addr, OFDM_EC_SB_PRIOR__A,
+ OFDM_EC_SB_PRIOR_LO);
+ break;
+ case DRX_PRIORITY_HIGH:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
+ WR16(dev_addr, OFDM_EC_SB_PRIOR__A,
+ OFDM_EC_SB_PRIOR_HI));
+ break;
+ case DRX_PRIORITY_UNKNOWN:
+ default:
+ status = -EINVAL;
+ goto error;
+ }
+#else
+ /* Set Priority high */
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
+ status = write16(state, OFDM_EC_SB_PRIOR__A, OFDM_EC_SB_PRIOR_HI);
+ if (status < 0)
+ goto error;
+#endif
+
+ /* coderate */
+ switch (state->props.code_rate_HP) {
+ case FEC_AUTO:
+ default: /* try first guess DRX_CODERATE_2DIV3 */
+ case FEC_2_3:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3;
+ break;
+ case FEC_1_2:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2;
+ break;
+ case FEC_3_4:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4;
+ break;
+ case FEC_5_6:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6;
+ break;
+ case FEC_7_8:
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8;
+ break;
+ }
+
+ /*
+ * SAW filter selection: normally not necessary, but if wanted
+ * the application can select a SAW filter via the driver by
+ * using UIOs
+ */
+
+ /* First determine real bandwidth (Hz) */
+ /* Also set delay for impulse noise cruncher */
+ /*
+ * Also set parameters for EC_OC fix, note EC_OC_REG_TMD_HIL_MAR is
+ * changed by SC for fix for some 8K,1/8 guard but is restored by
+ * InitEC and ResetEC functions
+ */
+ switch (state->props.bandwidth_hz) {
+ case 0:
+ state->props.bandwidth_hz = 8000000;
+ fallthrough;
+ case 8000000:
+ bandwidth = DRXK_BANDWIDTH_8MHZ_IN_HZ;
+ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A,
+ 3052);
+ if (status < 0)
+ goto error;
+ /* cochannel protection for PAL 8 MHz */
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A,
+ 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A,
+ 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A,
+ 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A,
+ 1);
+ if (status < 0)
+ goto error;
+ break;
+ case 7000000:
+ bandwidth = DRXK_BANDWIDTH_7MHZ_IN_HZ;
+ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A,
+ 3491);
+ if (status < 0)
+ goto error;
+ /* cochannel protection for PAL 7 MHz */
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A,
+ 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A,
+ 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A,
+ 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A,
+ 1);
+ if (status < 0)
+ goto error;
+ break;
+ case 6000000:
+ bandwidth = DRXK_BANDWIDTH_6MHZ_IN_HZ;
+ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A,
+ 4073);
+ if (status < 0)
+ goto error;
+ /* cochannel protection for NTSC 6 MHz */
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A,
+ 19);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A,
+ 19);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A,
+ 14);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A,
+ 1);
+ if (status < 0)
+ goto error;
+ break;
+ default:
+ status = -EINVAL;
+ goto error;
+ }
+
+ if (iqm_rc_rate_ofs == 0) {
+ /* Now compute IQM_RC_RATE_OFS
+ (((SysFreq/BandWidth)/2)/2) -1) * 2^23)
+ =>
+ ((SysFreq / BandWidth) * (2^21)) - (2^23)
+ */
+ /* (SysFreq / BandWidth) * (2^28) */
+ /*
+ * assert (MAX(sysClk)/MIN(bandwidth) < 16)
+ * => assert(MAX(sysClk) < 16*MIN(bandwidth))
+ * => assert(109714272 > 48000000) = true
+ * so Frac 28 can be used
+ */
+ iqm_rc_rate_ofs = Frac28a((u32)
+ ((state->m_sys_clock_freq *
+ 1000) / 3), bandwidth);
+ /* (SysFreq / BandWidth) * (2^21), rounding before truncating */
+ if ((iqm_rc_rate_ofs & 0x7fL) >= 0x40)
+ iqm_rc_rate_ofs += 0x80L;
+ iqm_rc_rate_ofs = iqm_rc_rate_ofs >> 7;
+ /* ((SysFreq / BandWidth) * (2^21)) - (2^23) */
+ iqm_rc_rate_ofs = iqm_rc_rate_ofs - (1 << 23);
+ }
+
+ iqm_rc_rate_ofs &=
+ ((((u32) IQM_RC_RATE_OFS_HI__M) <<
+ IQM_RC_RATE_OFS_LO__W) | IQM_RC_RATE_OFS_LO__M);
+ status = write32(state, IQM_RC_RATE_OFS_LO__A, iqm_rc_rate_ofs);
+ if (status < 0)
+ goto error;
+
+ /* Bandwidth setting done */
+
+#if 0
+ status = dvbt_set_frequency_shift(demod, channel, tuner_offset);
+ if (status < 0)
+ goto error;
+#endif
+ status = set_frequency_shifter(state, intermediate_freqk_hz,
+ tuner_freq_offset, true);
+ if (status < 0)
+ goto error;
+
+ /*== start SC, write channel settings to SC ==========================*/
+
+ /* Activate SCU to enable SCU commands */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ /* Enable SC after setting all other parameters */
+ status = write16(state, OFDM_SC_COMM_STATE__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_COMM_EXEC__A, 1);
+ if (status < 0)
+ goto error;
+
+
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM
+ | SCU_RAM_COMMAND_CMD_DEMOD_START,
+ 0, NULL, 1, &cmd_result);
+ if (status < 0)
+ goto error;
+
+ /* Write SC parameter registers, set all AUTO flags in operation mode */
+ param1 = (OFDM_SC_RA_RAM_OP_AUTO_MODE__M |
+ OFDM_SC_RA_RAM_OP_AUTO_GUARD__M |
+ OFDM_SC_RA_RAM_OP_AUTO_CONST__M |
+ OFDM_SC_RA_RAM_OP_AUTO_HIER__M |
+ OFDM_SC_RA_RAM_OP_AUTO_RATE__M);
+ status = dvbt_sc_command(state, OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM,
+ 0, transmission_params, param1, 0, 0, 0);
+ if (status < 0)
+ goto error;
+
+ if (!state->m_drxk_a3_rom_code)
+ status = dvbt_ctrl_set_sqi_speed(state, &state->m_sqi_speed);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+
+/*============================================================================*/
+
+/*
+* \brief Retrieve lock status .
+* \param demod Pointer to demodulator instance.
+* \param lockStat Pointer to lock status structure.
+* \return DRXStatus_t.
+*
+*/
+static int get_dvbt_lock_status(struct drxk_state *state, u32 *p_lock_status)
+{
+ int status;
+ const u16 mpeg_lock_mask = (OFDM_SC_RA_RAM_LOCK_MPEG__M |
+ OFDM_SC_RA_RAM_LOCK_FEC__M);
+ const u16 fec_lock_mask = (OFDM_SC_RA_RAM_LOCK_FEC__M);
+ const u16 demod_lock_mask = OFDM_SC_RA_RAM_LOCK_DEMOD__M;
+
+ u16 sc_ra_ram_lock = 0;
+ u16 sc_comm_exec = 0;
+
+ dprintk(1, "\n");
+
+ *p_lock_status = NOT_LOCKED;
+ /* driver 0.9.0 */
+ /* Check if SC is running */
+ status = read16(state, OFDM_SC_COMM_EXEC__A, &sc_comm_exec);
+ if (status < 0)
+ goto end;
+ if (sc_comm_exec == OFDM_SC_COMM_EXEC_STOP)
+ goto end;
+
+ status = read16(state, OFDM_SC_RA_RAM_LOCK__A, &sc_ra_ram_lock);
+ if (status < 0)
+ goto end;
+
+ if ((sc_ra_ram_lock & mpeg_lock_mask) == mpeg_lock_mask)
+ *p_lock_status = MPEG_LOCK;
+ else if ((sc_ra_ram_lock & fec_lock_mask) == fec_lock_mask)
+ *p_lock_status = FEC_LOCK;
+ else if ((sc_ra_ram_lock & demod_lock_mask) == demod_lock_mask)
+ *p_lock_status = DEMOD_LOCK;
+ else if (sc_ra_ram_lock & OFDM_SC_RA_RAM_LOCK_NODVBT__M)
+ *p_lock_status = NEVER_LOCK;
+end:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int power_up_qam(struct drxk_state *state)
+{
+ enum drx_power_mode power_mode = DRXK_POWER_DOWN_OFDM;
+ int status;
+
+ dprintk(1, "\n");
+ status = ctrl_power_mode(state, &power_mode);
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+
+/* Power Down QAM */
+static int power_down_qam(struct drxk_state *state)
+{
+ u16 data = 0;
+ u16 cmd_result;
+ int status = 0;
+
+ dprintk(1, "\n");
+ status = read16(state, SCU_COMM_EXEC__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == SCU_COMM_EXEC_ACTIVE) {
+ /*
+ STOP demodulator
+ QAM and HW blocks
+ */
+ /* stop all comstate->m_exec */
+ status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM
+ | SCU_RAM_COMMAND_CMD_DEMOD_STOP,
+ 0, NULL, 1, &cmd_result);
+ if (status < 0)
+ goto error;
+ }
+ /* powerdown AFE */
+ status = set_iqm_af(state, false);
+
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+/*============================================================================*/
+
+/*
+* \brief Setup of the QAM Measurement intervals for signal quality
+* \param demod instance of demod.
+* \param modulation current modulation.
+* \return DRXStatus_t.
+*
+* NOTE:
+* Take into account that for certain settings the errorcounters can overflow.
+* The implementation does not check this.
+*
+*/
+static int set_qam_measurement(struct drxk_state *state,
+ enum e_drxk_constellation modulation,
+ u32 symbol_rate)
+{
+ u32 fec_bits_desired = 0; /* BER accounting period */
+ u32 fec_rs_period_total = 0; /* Total period */
+ u16 fec_rs_prescale = 0; /* ReedSolomon Measurement Prescale */
+ u16 fec_rs_period = 0; /* Value for corresponding I2C register */
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ fec_rs_prescale = 1;
+ /* fec_bits_desired = symbol_rate [kHz] *
+ FrameLenght [ms] *
+ (modulation + 1) *
+ SyncLoss (== 1) *
+ ViterbiLoss (==1)
+ */
+ switch (modulation) {
+ case DRX_CONSTELLATION_QAM16:
+ fec_bits_desired = 4 * symbol_rate;
+ break;
+ case DRX_CONSTELLATION_QAM32:
+ fec_bits_desired = 5 * symbol_rate;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ fec_bits_desired = 6 * symbol_rate;
+ break;
+ case DRX_CONSTELLATION_QAM128:
+ fec_bits_desired = 7 * symbol_rate;
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ fec_bits_desired = 8 * symbol_rate;
+ break;
+ default:
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ fec_bits_desired /= 1000; /* symbol_rate [Hz] -> symbol_rate [kHz] */
+ fec_bits_desired *= 500; /* meas. period [ms] */
+
+ /* Annex A/C: bits/RsPeriod = 204 * 8 = 1632 */
+ /* fec_rs_period_total = fec_bits_desired / 1632 */
+ fec_rs_period_total = (fec_bits_desired / 1632UL) + 1; /* roughly ceil */
+
+ /* fec_rs_period_total = fec_rs_prescale * fec_rs_period */
+ fec_rs_prescale = 1 + (u16) (fec_rs_period_total >> 16);
+ if (fec_rs_prescale == 0) {
+ /* Divide by zero (though impossible) */
+ status = -EINVAL;
+ if (status < 0)
+ goto error;
+ }
+ fec_rs_period =
+ ((u16) fec_rs_period_total +
+ (fec_rs_prescale >> 1)) / fec_rs_prescale;
+
+ /* write corresponding registers */
+ status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, fec_rs_period);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A,
+ fec_rs_prescale);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_SNC_FAIL_PERIOD__A, fec_rs_period);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int set_qam16(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 13517);
+ if (status < 0)
+ goto error;
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+ /* QAM Slicer Settings */
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A,
+ DRXK_QAM_SL_SIG_POWER_QAM16);
+ if (status < 0)
+ goto error;
+
+ /* QAM Loop Controller Coeficients */
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 32);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 140);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 95);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 120);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 230);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 105);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 24);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 220);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 25);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -65);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -127);
+ if (status < 0)
+ goto error;
+
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/*
+* \brief QAM32 specific setup
+* \param demod instance of demod.
+* \return DRXStatus_t.
+*/
+static int set_qam32(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 6707);
+ if (status < 0)
+ goto error;
+
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+ /* QAM Slicer Settings */
+
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A,
+ DRXK_QAM_SL_SIG_POWER_QAM32);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Loop Controller Coeficients */
+
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 90);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 170);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 10);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 140);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) -8);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) -16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -26);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -56);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -86);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/*
+* \brief QAM64 specific setup
+* \param demod instance of demod.
+* \return DRXStatus_t.
+*/
+static int set_qam64(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13336);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12618);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 11988);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13809);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13809);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15609);
+ if (status < 0)
+ goto error;
+
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+ /* QAM Slicer Settings */
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A,
+ DRXK_QAM_SL_SIG_POWER_QAM64);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Loop Controller Coeficients */
+
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 30);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 30);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 110);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 200);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 95);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 15);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 141);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -45);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -80);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+/*============================================================================*/
+
+/*
+* \brief QAM128 specific setup
+* \param demod: instance of demod.
+* \return DRXStatus_t.
+*/
+static int set_qam128(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6564);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6598);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6394);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6409);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6656);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 7238);
+ if (status < 0)
+ goto error;
+
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Slicer Settings */
+
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A,
+ DRXK_QAM_SL_SIG_POWER_QAM128);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Loop Controller Coeficients */
+
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 120);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 60);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 64);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 140);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 5);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12);
+ if (status < 0)
+ goto error;
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 65);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -1);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -23);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+/*============================================================================*/
+
+/*
+* \brief QAM256 specific setup
+* \param demod: instance of demod.
+* \return DRXStatus_t.
+*/
+static int set_qam256(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 11502);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12084);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 12543);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 12931);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13629);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15385);
+ if (status < 0)
+ goto error;
+
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+ /* QAM Slicer Settings */
+
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A,
+ DRXK_QAM_SL_SIG_POWER_QAM256);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Loop Controller Coeficients */
+
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 250);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 125);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 150);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 110);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 74);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 18);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 13);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -8);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+
+/*============================================================================*/
+/*
+* \brief Reset QAM block.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \return DRXStatus_t.
+*/
+static int qam_reset_qam(struct drxk_state *state)
+{
+ int status;
+ u16 cmd_result;
+
+ dprintk(1, "\n");
+ /* Stop QAM comstate->m_exec */
+ status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM
+ | SCU_RAM_COMMAND_CMD_DEMOD_RESET,
+ 0, NULL, 1, &cmd_result);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/*
+* \brief Set QAM symbolrate.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \return DRXStatus_t.
+*/
+static int qam_set_symbolrate(struct drxk_state *state)
+{
+ u32 adc_frequency = 0;
+ u32 symb_freq = 0;
+ u32 iqm_rc_rate = 0;
+ u16 ratesel = 0;
+ u32 lc_symb_rate = 0;
+ int status;
+
+ dprintk(1, "\n");
+ /* Select & calculate correct IQM rate */
+ adc_frequency = (state->m_sys_clock_freq * 1000) / 3;
+ ratesel = 0;
+ if (state->props.symbol_rate <= 1188750)
+ ratesel = 3;
+ else if (state->props.symbol_rate <= 2377500)
+ ratesel = 2;
+ else if (state->props.symbol_rate <= 4755000)
+ ratesel = 1;
+ status = write16(state, IQM_FD_RATESEL__A, ratesel);
+ if (status < 0)
+ goto error;
+
+ /*
+ IqmRcRate = ((Fadc / (symbolrate * (4<<ratesel))) - 1) * (1<<23)
+ */
+ symb_freq = state->props.symbol_rate * (1 << ratesel);
+ if (symb_freq == 0) {
+ /* Divide by zero */
+ status = -EINVAL;
+ goto error;
+ }
+ iqm_rc_rate = (adc_frequency / symb_freq) * (1 << 21) +
+ (Frac28a((adc_frequency % symb_freq), symb_freq) >> 7) -
+ (1 << 23);
+ status = write32(state, IQM_RC_RATE_OFS_LO__A, iqm_rc_rate);
+ if (status < 0)
+ goto error;
+ state->m_iqm_rc_rate = iqm_rc_rate;
+ /*
+ LcSymbFreq = round (.125 * symbolrate / adc_freq * (1<<15))
+ */
+ symb_freq = state->props.symbol_rate;
+ if (adc_frequency == 0) {
+ /* Divide by zero */
+ status = -EINVAL;
+ goto error;
+ }
+ lc_symb_rate = (symb_freq / adc_frequency) * (1 << 12) +
+ (Frac28a((symb_freq % adc_frequency), adc_frequency) >>
+ 16);
+ if (lc_symb_rate > 511)
+ lc_symb_rate = 511;
+ status = write16(state, QAM_LC_SYMBOL_FREQ__A, (u16) lc_symb_rate);
+
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/*
+* \brief Get QAM lock status.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \return DRXStatus_t.
+*/
+
+static int get_qam_lock_status(struct drxk_state *state, u32 *p_lock_status)
+{
+ int status;
+ u16 result[2] = { 0, 0 };
+
+ dprintk(1, "\n");
+ *p_lock_status = NOT_LOCKED;
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK, 0, NULL, 2,
+ result);
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+ if (result[1] < SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED) {
+ /* 0x0000 NOT LOCKED */
+ } else if (result[1] < SCU_RAM_QAM_LOCKED_LOCKED_LOCKED) {
+ /* 0x4000 DEMOD LOCKED */
+ *p_lock_status = DEMOD_LOCK;
+ } else if (result[1] < SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK) {
+ /* 0x8000 DEMOD + FEC LOCKED (system lock) */
+ *p_lock_status = MPEG_LOCK;
+ } else {
+ /* 0xC000 NEVER LOCKED */
+ /* (system will never be able to lock to the signal) */
+ /*
+ * TODO: check this, intermediate & standard specific lock
+ * states are not taken into account here
+ */
+ *p_lock_status = NEVER_LOCK;
+ }
+ return status;
+}
+
+#define QAM_MIRROR__M 0x03
+#define QAM_MIRROR_NORMAL 0x00
+#define QAM_MIRRORED 0x01
+#define QAM_MIRROR_AUTO_ON 0x02
+#define QAM_LOCKRANGE__M 0x10
+#define QAM_LOCKRANGE_NORMAL 0x10
+
+static int qam_demodulator_command(struct drxk_state *state,
+ int number_of_parameters)
+{
+ int status;
+ u16 cmd_result;
+ u16 set_param_parameters[4] = { 0, 0, 0, 0 };
+
+ set_param_parameters[0] = state->m_constellation; /* modulation */
+ set_param_parameters[1] = DRXK_QAM_I12_J17; /* interleave mode */
+
+ if (number_of_parameters == 2) {
+ u16 set_env_parameters[1] = { 0 };
+
+ if (state->m_operation_mode == OM_QAM_ITU_C)
+ set_env_parameters[0] = QAM_TOP_ANNEX_C;
+ else
+ set_env_parameters[0] = QAM_TOP_ANNEX_A;
+
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_QAM
+ | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV,
+ 1, set_env_parameters, 1, &cmd_result);
+ if (status < 0)
+ goto error;
+
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_QAM
+ | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM,
+ number_of_parameters, set_param_parameters,
+ 1, &cmd_result);
+ } else if (number_of_parameters == 4) {
+ if (state->m_operation_mode == OM_QAM_ITU_C)
+ set_param_parameters[2] = QAM_TOP_ANNEX_C;
+ else
+ set_param_parameters[2] = QAM_TOP_ANNEX_A;
+
+ set_param_parameters[3] |= (QAM_MIRROR_AUTO_ON);
+ /* Env parameters */
+ /* check for LOCKRANGE Extended */
+ /* set_param_parameters[3] |= QAM_LOCKRANGE_NORMAL; */
+
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_QAM
+ | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM,
+ number_of_parameters, set_param_parameters,
+ 1, &cmd_result);
+ } else {
+ pr_warn("Unknown QAM demodulator parameter count %d\n",
+ number_of_parameters);
+ status = -EINVAL;
+ }
+
+error:
+ if (status < 0)
+ pr_warn("Warning %d on %s\n", status, __func__);
+ return status;
+}
+
+static int set_qam(struct drxk_state *state, u16 intermediate_freqk_hz,
+ s32 tuner_freq_offset)
+{
+ int status;
+ u16 cmd_result;
+ int qam_demod_param_count = state->qam_demod_parameter_count;
+
+ dprintk(1, "\n");
+ /*
+ * STEP 1: reset demodulator
+ * resets FEC DI and FEC RS
+ * resets QAM block
+ * resets SCU variables
+ */
+ status = write16(state, FEC_DI_COMM_EXEC__A, FEC_DI_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_RS_COMM_EXEC__A, FEC_RS_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = qam_reset_qam(state);
+ if (status < 0)
+ goto error;
+
+ /*
+ * STEP 2: configure demodulator
+ * -set params; resets IQM,QAM,FEC HW; initializes some
+ * SCU variables
+ */
+ status = qam_set_symbolrate(state);
+ if (status < 0)
+ goto error;
+
+ /* Set params */
+ switch (state->props.modulation) {
+ case QAM_256:
+ state->m_constellation = DRX_CONSTELLATION_QAM256;
+ break;
+ case QAM_AUTO:
+ case QAM_64:
+ state->m_constellation = DRX_CONSTELLATION_QAM64;
+ break;
+ case QAM_16:
+ state->m_constellation = DRX_CONSTELLATION_QAM16;
+ break;
+ case QAM_32:
+ state->m_constellation = DRX_CONSTELLATION_QAM32;
+ break;
+ case QAM_128:
+ state->m_constellation = DRX_CONSTELLATION_QAM128;
+ break;
+ default:
+ status = -EINVAL;
+ break;
+ }
+ if (status < 0)
+ goto error;
+
+ /* Use the 4-parameter if it's requested or we're probing for
+ * the correct command. */
+ if (state->qam_demod_parameter_count == 4
+ || !state->qam_demod_parameter_count) {
+ qam_demod_param_count = 4;
+ status = qam_demodulator_command(state, qam_demod_param_count);
+ }
+
+ /* Use the 2-parameter command if it was requested or if we're
+ * probing for the correct command and the 4-parameter command
+ * failed. */
+ if (state->qam_demod_parameter_count == 2
+ || (!state->qam_demod_parameter_count && status < 0)) {
+ qam_demod_param_count = 2;
+ status = qam_demodulator_command(state, qam_demod_param_count);
+ }
+
+ if (status < 0) {
+ dprintk(1, "Could not set demodulator parameters.\n");
+ dprintk(1,
+ "Make sure qam_demod_parameter_count (%d) is correct for your firmware (%s).\n",
+ state->qam_demod_parameter_count,
+ state->microcode_name);
+ goto error;
+ } else if (!state->qam_demod_parameter_count) {
+ dprintk(1,
+ "Auto-probing the QAM command parameters was successful - using %d parameters.\n",
+ qam_demod_param_count);
+
+ /*
+ * One of our commands was successful. We don't need to
+ * auto-probe anymore, now that we got the correct command.
+ */
+ state->qam_demod_parameter_count = qam_demod_param_count;
+ }
+
+ /*
+ * STEP 3: enable the system in a mode where the ADC provides valid
+ * signal setup modulation independent registers
+ */
+#if 0
+ status = set_frequency(channel, tuner_freq_offset));
+ if (status < 0)
+ goto error;
+#endif
+ status = set_frequency_shifter(state, intermediate_freqk_hz,
+ tuner_freq_offset, true);
+ if (status < 0)
+ goto error;
+
+ /* Setup BER measurement */
+ status = set_qam_measurement(state, state->m_constellation,
+ state->props.symbol_rate);
+ if (status < 0)
+ goto error;
+
+ /* Reset default values */
+ status = write16(state, IQM_CF_SCALE_SH__A, IQM_CF_SCALE_SH__PRE);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_TIMEOUT__A, QAM_SY_TIMEOUT__PRE);
+ if (status < 0)
+ goto error;
+
+ /* Reset default LC values */
+ status = write16(state, QAM_LC_RATE_LIMIT__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_LPF_FACTORP__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_LPF_FACTORI__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_MODE__A, 7);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_LC_QUAL_TAB0__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB1__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB2__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB3__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB4__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB5__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB6__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB8__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB9__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB10__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB12__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB15__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB16__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB20__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB25__A, 4);
+ if (status < 0)
+ goto error;
+
+ /* Mirroring, QAM-block starting point not inverted */
+ status = write16(state, QAM_SY_SP_INV__A,
+ QAM_SY_SP_INV_SPECTRUM_INV_DIS);
+ if (status < 0)
+ goto error;
+
+ /* Halt SCU to enable safe non-atomic accesses */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+ if (status < 0)
+ goto error;
+
+ /* STEP 4: modulation specific setup */
+ switch (state->props.modulation) {
+ case QAM_16:
+ status = set_qam16(state);
+ break;
+ case QAM_32:
+ status = set_qam32(state);
+ break;
+ case QAM_AUTO:
+ case QAM_64:
+ status = set_qam64(state);
+ break;
+ case QAM_128:
+ status = set_qam128(state);
+ break;
+ case QAM_256:
+ status = set_qam256(state);
+ break;
+ default:
+ status = -EINVAL;
+ break;
+ }
+ if (status < 0)
+ goto error;
+
+ /* Activate SCU to enable SCU commands */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ /* Re-configure MPEG output, requires knowledge of channel bitrate */
+ /* extAttr->currentChannel.modulation = channel->modulation; */
+ /* extAttr->currentChannel.symbolrate = channel->symbolrate; */
+ status = mpegts_dto_setup(state, state->m_operation_mode);
+ if (status < 0)
+ goto error;
+
+ /* start processes */
+ status = mpegts_start(state);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ /* STEP 5: start QAM demodulator (starts FEC, QAM and IQM HW) */
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM
+ | SCU_RAM_COMMAND_CMD_DEMOD_START,
+ 0, NULL, 1, &cmd_result);
+ if (status < 0)
+ goto error;
+
+ /* update global DRXK data container */
+/*? extAttr->qamInterleaveMode = DRXK_QAM_I12_J17; */
+
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int set_qam_standard(struct drxk_state *state,
+ enum operation_mode o_mode)
+{
+ int status;
+#ifdef DRXK_QAM_TAPS
+#define DRXK_QAMA_TAPS_SELECT
+#include "drxk_filters.h"
+#undef DRXK_QAMA_TAPS_SELECT
+#endif
+
+ dprintk(1, "\n");
+
+ /* added antenna switch */
+ switch_antenna_to_qam(state);
+
+ /* Ensure correct power-up mode */
+ status = power_up_qam(state);
+ if (status < 0)
+ goto error;
+ /* Reset QAM block */
+ status = qam_reset_qam(state);
+ if (status < 0)
+ goto error;
+
+ /* Setup IQM */
+
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC);
+ if (status < 0)
+ goto error;
+
+ /* Upload IQM Channel Filter settings by
+ boot loader from ROM table */
+ switch (o_mode) {
+ case OM_QAM_ITU_A:
+ status = bl_chain_cmd(state, DRXK_BL_ROM_OFFSET_TAPS_ITU_A,
+ DRXK_BLCC_NR_ELEMENTS_TAPS,
+ DRXK_BLC_TIMEOUT);
+ break;
+ case OM_QAM_ITU_C:
+ status = bl_direct_cmd(state, IQM_CF_TAP_RE0__A,
+ DRXK_BL_ROM_OFFSET_TAPS_ITU_C,
+ DRXK_BLDC_NR_ELEMENTS_TAPS,
+ DRXK_BLC_TIMEOUT);
+ if (status < 0)
+ goto error;
+ status = bl_direct_cmd(state,
+ IQM_CF_TAP_IM0__A,
+ DRXK_BL_ROM_OFFSET_TAPS_ITU_C,
+ DRXK_BLDC_NR_ELEMENTS_TAPS,
+ DRXK_BLC_TIMEOUT);
+ break;
+ default:
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_CF_OUT_ENA__A, 1 << IQM_CF_OUT_ENA_QAM__B);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_SYMMETRIC__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_MIDTAP__A,
+ ((1 << IQM_CF_MIDTAP_RE__B) | (1 << IQM_CF_MIDTAP_IM__B)));
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_RC_STRETCH__A, 21);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_CLP_LEN__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_CLP_TH__A, 448);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_SNS_LEN__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_POW_MEAS_LEN__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_FS_ADJ_SEL__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_RC_ADJ_SEL__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_ADJ_SEL__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_UPD_SEL__A, 0);
+ if (status < 0)
+ goto error;
+
+ /* IQM Impulse Noise Processing Unit */
+ status = write16(state, IQM_CF_CLP_VAL__A, 500);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DATATH__A, 1000);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_BYPASSDET__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DET_LCT__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_WND_LEN__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_PKDTH__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_INC_BYPASS__A, 1);
+ if (status < 0)
+ goto error;
+
+ /* turn on IQMAF. Must be done before setAgc**() */
+ status = set_iqm_af(state, true);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_START_LOCK__A, 0x01);
+ if (status < 0)
+ goto error;
+
+ /* IQM will not be reset from here, sync ADC and update/init AGC */
+ status = adc_synchronization(state);
+ if (status < 0)
+ goto error;
+
+ /* Set the FSM step period */
+ status = write16(state, SCU_RAM_QAM_FSM_STEP_PERIOD__A, 2000);
+ if (status < 0)
+ goto error;
+
+ /* Halt SCU to enable safe non-atomic accesses */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+ if (status < 0)
+ goto error;
+
+ /* No more resets of the IQM, current standard correctly set =>
+ now AGCs can be configured. */
+
+ status = init_agc(state, true);
+ if (status < 0)
+ goto error;
+ status = set_pre_saw(state, &(state->m_qam_pre_saw_cfg));
+ if (status < 0)
+ goto error;
+
+ /* Configure AGC's */
+ status = set_agc_rf(state, &(state->m_qam_rf_agc_cfg), true);
+ if (status < 0)
+ goto error;
+ status = set_agc_if(state, &(state->m_qam_if_agc_cfg), true);
+ if (status < 0)
+ goto error;
+
+ /* Activate SCU to enable SCU commands */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int write_gpio(struct drxk_state *state)
+{
+ int status;
+ u16 value = 0;
+
+ dprintk(1, "\n");
+ /* stop lock indicator process */
+ status = write16(state, SCU_RAM_GPIO__A,
+ SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+
+ /* Write magic word to enable pdr reg write */
+ status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
+ if (status < 0)
+ goto error;
+
+ if (state->m_has_sawsw) {
+ if (state->uio_mask & 0x0001) { /* UIO-1 */
+ /* write to io pad configuration register - output mode */
+ status = write16(state, SIO_PDR_SMA_TX_CFG__A,
+ state->m_gpio_cfg);
+ if (status < 0)
+ goto error;
+
+ /* use corresponding bit in io data output registar */
+ status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
+ if (status < 0)
+ goto error;
+ if ((state->m_gpio & 0x0001) == 0)
+ value &= 0x7FFF; /* write zero to 15th bit - 1st UIO */
+ else
+ value |= 0x8000; /* write one to 15th bit - 1st UIO */
+ /* write back to io data output register */
+ status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
+ if (status < 0)
+ goto error;
+ }
+ if (state->uio_mask & 0x0002) { /* UIO-2 */
+ /* write to io pad configuration register - output mode */
+ status = write16(state, SIO_PDR_SMA_RX_CFG__A,
+ state->m_gpio_cfg);
+ if (status < 0)
+ goto error;
+
+ /* use corresponding bit in io data output registar */
+ status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
+ if (status < 0)
+ goto error;
+ if ((state->m_gpio & 0x0002) == 0)
+ value &= 0xBFFF; /* write zero to 14th bit - 2st UIO */
+ else
+ value |= 0x4000; /* write one to 14th bit - 2st UIO */
+ /* write back to io data output register */
+ status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
+ if (status < 0)
+ goto error;
+ }
+ if (state->uio_mask & 0x0004) { /* UIO-3 */
+ /* write to io pad configuration register - output mode */
+ status = write16(state, SIO_PDR_GPIO_CFG__A,
+ state->m_gpio_cfg);
+ if (status < 0)
+ goto error;
+
+ /* use corresponding bit in io data output registar */
+ status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
+ if (status < 0)
+ goto error;
+ if ((state->m_gpio & 0x0004) == 0)
+ value &= 0xFFFB; /* write zero to 2nd bit - 3rd UIO */
+ else
+ value |= 0x0004; /* write one to 2nd bit - 3rd UIO */
+ /* write back to io data output register */
+ status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
+ if (status < 0)
+ goto error;
+ }
+ }
+ /* Write magic word to disable pdr reg write */
+ status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int switch_antenna_to_qam(struct drxk_state *state)
+{
+ int status = 0;
+ bool gpio_state;
+
+ dprintk(1, "\n");
+
+ if (!state->antenna_gpio)
+ return 0;
+
+ gpio_state = state->m_gpio & state->antenna_gpio;
+
+ if (state->antenna_dvbt ^ gpio_state) {
+ /* Antenna is on DVB-T mode. Switch */
+ if (state->antenna_dvbt)
+ state->m_gpio &= ~state->antenna_gpio;
+ else
+ state->m_gpio |= state->antenna_gpio;
+ status = write_gpio(state);
+ }
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int switch_antenna_to_dvbt(struct drxk_state *state)
+{
+ int status = 0;
+ bool gpio_state;
+
+ dprintk(1, "\n");
+
+ if (!state->antenna_gpio)
+ return 0;
+
+ gpio_state = state->m_gpio & state->antenna_gpio;
+
+ if (!(state->antenna_dvbt ^ gpio_state)) {
+ /* Antenna is on DVB-C mode. Switch */
+ if (state->antenna_dvbt)
+ state->m_gpio |= state->antenna_gpio;
+ else
+ state->m_gpio &= ~state->antenna_gpio;
+ status = write_gpio(state);
+ }
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+ return status;
+}
+
+
+static int power_down_device(struct drxk_state *state)
+{
+ /* Power down to requested mode */
+ /* Backup some register settings */
+ /* Set pins with possible pull-ups connected to them in input mode */
+ /* Analog power down */
+ /* ADC power down */
+ /* Power down device */
+ int status;
+
+ dprintk(1, "\n");
+ if (state->m_b_p_down_open_bridge) {
+ /* Open I2C bridge before power down of DRXK */
+ status = ConfigureI2CBridge(state, true);
+ if (status < 0)
+ goto error;
+ }
+ /* driver 0.9.0 */
+ status = dvbt_enable_ofdm_token_ring(state, false);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SIO_CC_PWD_MODE__A,
+ SIO_CC_PWD_MODE_LEVEL_CLOCK);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+ if (status < 0)
+ goto error;
+ state->m_hi_cfg_ctrl |= SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+ status = hi_cfg_command(state);
+error:
+ if (status < 0)
+ pr_err("Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int init_drxk(struct drxk_state *state)
+{
+ int status = 0, n = 0;
+ enum drx_power_mode power_mode = DRXK_POWER_DOWN_OFDM;
+ u16 driver_version;
+
+ dprintk(1, "\n");
+ if (state->m_drxk_state == DRXK_UNINITIALIZED) {
+ drxk_i2c_lock(state);
+ status = power_up_device(state);
+ if (status < 0)
+ goto error;
+ status = drxx_open(state);
+ if (status < 0)
+ goto error;
+ /* Soft reset of OFDM-, sys- and osc-clockdomain */
+ status = write16(state, SIO_CC_SOFT_RST__A,
+ SIO_CC_SOFT_RST_OFDM__M
+ | SIO_CC_SOFT_RST_SYS__M
+ | SIO_CC_SOFT_RST_OSC__M);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+ if (status < 0)
+ goto error;
+ /*
+ * TODO is this needed? If yes, how much delay in
+ * worst case scenario
+ */
+ usleep_range(1000, 2000);
+ state->m_drxk_a3_patch_code = true;
+ status = get_device_capabilities(state);
+ if (status < 0)
+ goto error;
+
+ /* Bridge delay, uses oscilator clock */
+ /* Delay = (delay (nano seconds) * oscclk (kHz))/ 1000 */
+ /* SDA brdige delay */
+ state->m_hi_cfg_bridge_delay =
+ (u16) ((state->m_osc_clock_freq / 1000) *
+ HI_I2C_BRIDGE_DELAY) / 1000;
+ /* Clipping */
+ if (state->m_hi_cfg_bridge_delay >
+ SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M) {
+ state->m_hi_cfg_bridge_delay =
+ SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M;
+ }
+ /* SCL bridge delay, same as SDA for now */
+ state->m_hi_cfg_bridge_delay +=
+ state->m_hi_cfg_bridge_delay <<
+ SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B;
+
+ status = init_hi(state);
+ if (status < 0)
+ goto error;
+ /* disable various processes */
+#if NOA1ROM
+ if (!(state->m_DRXK_A1_ROM_CODE)
+ && !(state->m_DRXK_A2_ROM_CODE))
+#endif
+ {
+ status = write16(state, SCU_RAM_GPIO__A,
+ SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+ }
+
+ /* disable MPEG port */
+ status = mpegts_disable(state);
+ if (status < 0)
+ goto error;
+
+ /* Stop AUD and SCU */
+ status = write16(state, AUD_COMM_EXEC__A, AUD_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+
+ /* enable token-ring bus through OFDM block for possible ucode upload */
+ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A,
+ SIO_OFDM_SH_OFDM_RING_ENABLE_ON);
+ if (status < 0)
+ goto error;
+
+ /* include boot loader section */
+ status = write16(state, SIO_BL_COMM_EXEC__A,
+ SIO_BL_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = bl_chain_cmd(state, 0, 6, 100);
+ if (status < 0)
+ goto error;
+
+ if (state->fw) {
+ status = download_microcode(state, state->fw->data,
+ state->fw->size);
+ if (status < 0)
+ goto error;
+ }
+
+ /* disable token-ring bus through OFDM block for possible ucode upload */
+ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A,
+ SIO_OFDM_SH_OFDM_RING_ENABLE_OFF);
+ if (status < 0)
+ goto error;
+
+ /* Run SCU for a little while to initialize microcode version numbers */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = drxx_open(state);
+ if (status < 0)
+ goto error;
+ /* added for test */
+ msleep(30);
+
+ power_mode = DRXK_POWER_DOWN_OFDM;
+ status = ctrl_power_mode(state, &power_mode);
+ if (status < 0)
+ goto error;
+
+ /* Stamp driver version number in SCU data RAM in BCD code
+ Done to enable field application engineers to retrieve drxdriver version
+ via I2C from SCU RAM.
+ Not using SCU command interface for SCU register access since no
+ microcode may be present.
+ */
+ driver_version =
+ (((DRXK_VERSION_MAJOR / 100) % 10) << 12) +
+ (((DRXK_VERSION_MAJOR / 10) % 10) << 8) +
+ ((DRXK_VERSION_MAJOR % 10) << 4) +
+ (DRXK_VERSION_MINOR % 10);
+ status = write16(state, SCU_RAM_DRIVER_VER_HI__A,
+ driver_version);
+ if (status < 0)
+ goto error;
+ driver_version =
+ (((DRXK_VERSION_PATCH / 1000) % 10) << 12) +
+ (((DRXK_VERSION_PATCH / 100) % 10) << 8) +
+ (((DRXK_VERSION_PATCH / 10) % 10) << 4) +
+ (DRXK_VERSION_PATCH % 10);
+ status = write16(state, SCU_RAM_DRIVER_VER_LO__A,
+ driver_version);
+ if (status < 0)
+ goto error;
+
+ pr_info("DRXK driver version %d.%d.%d\n",
+ DRXK_VERSION_MAJOR, DRXK_VERSION_MINOR,
+ DRXK_VERSION_PATCH);
+
+ /*
+ * Dirty fix of default values for ROM/PATCH microcode
+ * Dirty because this fix makes it impossible to setup
+ * suitable values before calling DRX_Open. This solution
+ * requires changes to RF AGC speed to be done via the CTRL
+ * function after calling DRX_Open
+ */
+
+ /* m_dvbt_rf_agc_cfg.speed = 3; */
+
+ /* Reset driver debug flags to 0 */
+ status = write16(state, SCU_RAM_DRIVER_DEBUG__A, 0);
+ if (status < 0)
+ goto error;
+ /* driver 0.9.0 */
+ /* Setup FEC OC:
+ NOTE: No more full FEC resets allowed afterwards!! */
+ status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ /* MPEGTS functions are still the same */
+ status = mpegts_dto_init(state);
+ if (status < 0)
+ goto error;
+ status = mpegts_stop(state);
+ if (status < 0)
+ goto error;
+ status = mpegts_configure_polarity(state);
+ if (status < 0)
+ goto error;
+ status = mpegts_configure_pins(state, state->m_enable_mpeg_output);
+ if (status < 0)
+ goto error;
+ /* added: configure GPIO */
+ status = write_gpio(state);
+ if (status < 0)
+ goto error;
+
+ state->m_drxk_state = DRXK_STOPPED;
+
+ if (state->m_b_power_down) {
+ status = power_down_device(state);
+ if (status < 0)
+ goto error;
+ state->m_drxk_state = DRXK_POWERED_DOWN;
+ } else
+ state->m_drxk_state = DRXK_STOPPED;
+
+ /* Initialize the supported delivery systems */
+ n = 0;
+ if (state->m_has_dvbc) {
+ state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A;
+ state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C;
+ strlcat(state->frontend.ops.info.name, " DVB-C",
+ sizeof(state->frontend.ops.info.name));
+ }
+ if (state->m_has_dvbt) {
+ state->frontend.ops.delsys[n++] = SYS_DVBT;
+ strlcat(state->frontend.ops.info.name, " DVB-T",
+ sizeof(state->frontend.ops.info.name));
+ }
+ drxk_i2c_unlock(state);
+ }
+error:
+ if (status < 0) {
+ state->m_drxk_state = DRXK_NO_DEV;
+ drxk_i2c_unlock(state);
+ pr_err("Error %d on %s\n", status, __func__);
+ }
+
+ return status;
+}
+
+static void load_firmware_cb(const struct firmware *fw,
+ void *context)
+{
+ struct drxk_state *state = context;
+
+ dprintk(1, ": %s\n", fw ? "firmware loaded" : "firmware not loaded");
+ if (!fw) {
+ pr_err("Could not load firmware file %s.\n",
+ state->microcode_name);
+ pr_info("Copy %s to your hotplug directory!\n",
+ state->microcode_name);
+ state->microcode_name = NULL;
+
+ /*
+ * As firmware is now load asynchronous, it is not possible
+ * anymore to fail at frontend attach. We might silently
+ * return here, and hope that the driver won't crash.
+ * We might also change all DVB callbacks to return -ENODEV
+ * if the device is not initialized.
+ * As the DRX-K devices have their own internal firmware,
+ * let's just hope that it will match a firmware revision
+ * compatible with this driver and proceed.
+ */
+ }
+ state->fw = fw;
+
+ init_drxk(state);
+}
+
+static void drxk_release(struct dvb_frontend *fe)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+
+ dprintk(1, "\n");
+ release_firmware(state->fw);
+
+ kfree(state);
+}
+
+static int drxk_sleep(struct dvb_frontend *fe)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+
+ dprintk(1, "\n");
+
+ if (state->m_drxk_state == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
+ return 0;
+
+ shut_down(state);
+ return 0;
+}
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+
+ dprintk(1, ": %s\n", enable ? "enable" : "disable");
+
+ if (state->m_drxk_state == DRXK_NO_DEV)
+ return -ENODEV;
+
+ return ConfigureI2CBridge(state, enable ? true : false);
+}
+
+static int drxk_set_parameters(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u32 delsys = p->delivery_system, old_delsys;
+ struct drxk_state *state = fe->demodulator_priv;
+ u32 IF;
+
+ dprintk(1, "\n");
+
+ if (state->m_drxk_state == DRXK_NO_DEV)
+ return -ENODEV;
+
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
+ if (!fe->ops.tuner_ops.get_if_frequency) {
+ pr_err("Error: get_if_frequency() not defined at tuner. Can't work without it!\n");
+ return -EINVAL;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ old_delsys = state->props.delivery_system;
+ state->props = *p;
+
+ if (old_delsys != delsys) {
+ shut_down(state);
+ switch (delsys) {
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ if (!state->m_has_dvbc)
+ return -EINVAL;
+ state->m_itut_annex_c = delsys == SYS_DVBC_ANNEX_C;
+ if (state->m_itut_annex_c)
+ setoperation_mode(state, OM_QAM_ITU_C);
+ else
+ setoperation_mode(state, OM_QAM_ITU_A);
+ break;
+ case SYS_DVBT:
+ if (!state->m_has_dvbt)
+ return -EINVAL;
+ setoperation_mode(state, OM_DVBT);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ fe->ops.tuner_ops.get_if_frequency(fe, &IF);
+ start(state, 0, IF);
+
+ /* After set_frontend, stats aren't available */
+ p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ /* printk(KERN_DEBUG "drxk: %s IF=%d done\n", __func__, IF); */
+
+ return 0;
+}
+
+static int get_strength(struct drxk_state *state, u64 *strength)
+{
+ int status;
+ struct s_cfg_agc rf_agc, if_agc;
+ u32 total_gain = 0;
+ u32 atten = 0;
+ u32 agc_range = 0;
+ u16 scu_lvl = 0;
+ u16 scu_coc = 0;
+ /* FIXME: those are part of the tuner presets */
+ u16 tuner_rf_gain = 50; /* Default value on az6007 driver */
+ u16 tuner_if_gain = 40; /* Default value on az6007 driver */
+
+ *strength = 0;
+
+ if (is_dvbt(state)) {
+ rf_agc = state->m_dvbt_rf_agc_cfg;
+ if_agc = state->m_dvbt_if_agc_cfg;
+ } else if (is_qam(state)) {
+ rf_agc = state->m_qam_rf_agc_cfg;
+ if_agc = state->m_qam_if_agc_cfg;
+ } else {
+ rf_agc = state->m_atv_rf_agc_cfg;
+ if_agc = state->m_atv_if_agc_cfg;
+ }
+
+ if (rf_agc.ctrl_mode == DRXK_AGC_CTRL_AUTO) {
+ /* SCU output_level */
+ status = read16(state, SCU_RAM_AGC_RF_IACCU_HI__A, &scu_lvl);
+ if (status < 0)
+ return status;
+
+ /* SCU c.o.c. */
+ status = read16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, &scu_coc);
+ if (status < 0)
+ return status;
+
+ if (((u32) scu_lvl + (u32) scu_coc) < 0xffff)
+ rf_agc.output_level = scu_lvl + scu_coc;
+ else
+ rf_agc.output_level = 0xffff;
+
+ /* Take RF gain into account */
+ total_gain += tuner_rf_gain;
+
+ /* clip output value */
+ if (rf_agc.output_level < rf_agc.min_output_level)
+ rf_agc.output_level = rf_agc.min_output_level;
+ if (rf_agc.output_level > rf_agc.max_output_level)
+ rf_agc.output_level = rf_agc.max_output_level;
+
+ agc_range = (u32) (rf_agc.max_output_level - rf_agc.min_output_level);
+ if (agc_range > 0) {
+ atten += 100UL *
+ ((u32)(tuner_rf_gain)) *
+ ((u32)(rf_agc.output_level - rf_agc.min_output_level))
+ / agc_range;
+ }
+ }
+
+ if (if_agc.ctrl_mode == DRXK_AGC_CTRL_AUTO) {
+ status = read16(state, SCU_RAM_AGC_IF_IACCU_HI__A,
+ &if_agc.output_level);
+ if (status < 0)
+ return status;
+
+ status = read16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A,
+ &if_agc.top);
+ if (status < 0)
+ return status;
+
+ /* Take IF gain into account */
+ total_gain += (u32) tuner_if_gain;
+
+ /* clip output value */
+ if (if_agc.output_level < if_agc.min_output_level)
+ if_agc.output_level = if_agc.min_output_level;
+ if (if_agc.output_level > if_agc.max_output_level)
+ if_agc.output_level = if_agc.max_output_level;
+
+ agc_range = (u32)(if_agc.max_output_level - if_agc.min_output_level);
+ if (agc_range > 0) {
+ atten += 100UL *
+ ((u32)(tuner_if_gain)) *
+ ((u32)(if_agc.output_level - if_agc.min_output_level))
+ / agc_range;
+ }
+ }
+
+ /*
+ * Convert to 0..65535 scale.
+ * If it can't be measured (AGC is disabled), just show 100%.
+ */
+ if (total_gain > 0)
+ *strength = (65535UL * atten / total_gain / 100);
+ else
+ *strength = 65535;
+
+ return 0;
+}
+
+static int drxk_get_stats(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct drxk_state *state = fe->demodulator_priv;
+ int status;
+ u32 stat;
+ u16 reg16;
+ u32 post_bit_count;
+ u32 post_bit_err_count;
+ u32 post_bit_error_scale;
+ u32 pre_bit_err_count;
+ u32 pre_bit_count;
+ u32 pkt_count;
+ u32 pkt_error_count;
+ s32 cnr;
+
+ if (state->m_drxk_state == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
+ /* get status */
+ state->fe_status = 0;
+ get_lock_status(state, &stat);
+ if (stat == MPEG_LOCK)
+ state->fe_status |= 0x1f;
+ if (stat == FEC_LOCK)
+ state->fe_status |= 0x0f;
+ if (stat == DEMOD_LOCK)
+ state->fe_status |= 0x07;
+
+ /*
+ * Estimate signal strength from AGC
+ */
+ get_strength(state, &c->strength.stat[0].uvalue);
+ c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+
+
+ if (stat >= DEMOD_LOCK) {
+ get_signal_to_noise(state, &cnr);
+ c->cnr.stat[0].svalue = cnr * 100;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ } else {
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ if (stat < FEC_LOCK) {
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return 0;
+ }
+
+ /* Get post BER */
+
+ /* BER measurement is valid if at least FEC lock is achieved */
+
+ /*
+ * OFDM_EC_VD_REQ_SMB_CNT__A and/or OFDM_EC_VD_REQ_BIT_CNT can be
+ * written to set nr of symbols or bits over which to measure
+ * EC_VD_REG_ERR_BIT_CNT__A . See CtrlSetCfg().
+ */
+
+ /* Read registers for post/preViterbi BER calculation */
+ status = read16(state, OFDM_EC_VD_ERR_BIT_CNT__A, &reg16);
+ if (status < 0)
+ goto error;
+ pre_bit_err_count = reg16;
+
+ status = read16(state, OFDM_EC_VD_IN_BIT_CNT__A , &reg16);
+ if (status < 0)
+ goto error;
+ pre_bit_count = reg16;
+
+ /* Number of bit-errors */
+ status = read16(state, FEC_RS_NR_BIT_ERRORS__A, &reg16);
+ if (status < 0)
+ goto error;
+ post_bit_err_count = reg16;
+
+ status = read16(state, FEC_RS_MEASUREMENT_PRESCALE__A, &reg16);
+ if (status < 0)
+ goto error;
+ post_bit_error_scale = reg16;
+
+ status = read16(state, FEC_RS_MEASUREMENT_PERIOD__A, &reg16);
+ if (status < 0)
+ goto error;
+ pkt_count = reg16;
+
+ status = read16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, &reg16);
+ if (status < 0)
+ goto error;
+ pkt_error_count = reg16;
+ write16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0);
+
+ post_bit_err_count *= post_bit_error_scale;
+
+ post_bit_count = pkt_count * 204 * 8;
+
+ /* Store the results */
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue += pkt_error_count;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue += pkt_count;
+
+ c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->pre_bit_error.stat[0].uvalue += pre_bit_err_count;
+ c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->pre_bit_count.stat[0].uvalue += pre_bit_count;
+
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += post_bit_err_count;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue += post_bit_count;
+
+error:
+ return status;
+}
+
+
+static int drxk_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ int rc;
+
+ dprintk(1, "\n");
+
+ rc = drxk_get_stats(fe);
+ if (rc < 0)
+ return rc;
+
+ *status = state->fe_status;
+
+ return 0;
+}
+
+static int drxk_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ dprintk(1, "\n");
+
+ if (state->m_drxk_state == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
+ *strength = c->strength.stat[0].uvalue;
+ return 0;
+}
+
+static int drxk_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ s32 snr2;
+
+ dprintk(1, "\n");
+
+ if (state->m_drxk_state == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
+ get_signal_to_noise(state, &snr2);
+
+ /* No negative SNR, clip to zero */
+ if (snr2 < 0)
+ snr2 = 0;
+ *snr = snr2 & 0xffff;
+ return 0;
+}
+
+static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ u16 err = 0;
+
+ dprintk(1, "\n");
+
+ if (state->m_drxk_state == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
+ dvbtqam_get_acc_pkt_err(state, &err);
+ *ucblocks = (u32) err;
+ return 0;
+}
+
+static int drxk_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *sets)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ dprintk(1, "\n");
+
+ if (state->m_drxk_state == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
+ switch (p->delivery_system) {
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ case SYS_DVBT:
+ sets->min_delay_ms = 3000;
+ sets->max_drift = 0;
+ sets->step_size = 0;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct dvb_frontend_ops drxk_ops = {
+ /* .delsys will be filled dynamically */
+ .info = {
+ .name = "DRXK",
+ .frequency_min_hz = 47 * MHz,
+ .frequency_max_hz = 865 * MHz,
+ /* For DVB-C */
+ .symbol_rate_min = 870000,
+ .symbol_rate_max = 11700000,
+ /* For DVB-T */
+ .frequency_stepsize_hz = 166667,
+
+ .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_MUTE_TS |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER |
+ FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO
+ },
+
+ .release = drxk_release,
+ .sleep = drxk_sleep,
+ .i2c_gate_ctrl = drxk_gate_ctrl,
+
+ .set_frontend = drxk_set_parameters,
+ .get_tune_settings = drxk_get_tune_settings,
+
+ .read_status = drxk_read_status,
+ .read_signal_strength = drxk_read_signal_strength,
+ .read_snr = drxk_read_snr,
+ .read_ucblocks = drxk_read_ucblocks,
+};
+
+struct dvb_frontend *drxk_attach(const struct drxk_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct dtv_frontend_properties *p;
+ struct drxk_state *state = NULL;
+ u8 adr = config->adr;
+ int status;
+
+ dprintk(1, "\n");
+ state = kzalloc(sizeof(struct drxk_state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ state->i2c = i2c;
+ state->demod_address = adr;
+ state->single_master = config->single_master;
+ state->microcode_name = config->microcode_name;
+ state->qam_demod_parameter_count = config->qam_demod_parameter_count;
+ state->no_i2c_bridge = config->no_i2c_bridge;
+ state->antenna_gpio = config->antenna_gpio;
+ state->antenna_dvbt = config->antenna_dvbt;
+ state->m_chunk_size = config->chunk_size;
+ state->enable_merr_cfg = config->enable_merr_cfg;
+
+ if (config->dynamic_clk) {
+ state->m_dvbt_static_clk = false;
+ state->m_dvbc_static_clk = false;
+ } else {
+ state->m_dvbt_static_clk = true;
+ state->m_dvbc_static_clk = true;
+ }
+
+
+ if (config->mpeg_out_clk_strength)
+ state->m_ts_clockk_strength = config->mpeg_out_clk_strength & 0x07;
+ else
+ state->m_ts_clockk_strength = 0x06;
+
+ if (config->parallel_ts)
+ state->m_enable_parallel = true;
+ else
+ state->m_enable_parallel = false;
+
+ /* NOTE: as more UIO bits will be used, add them to the mask */
+ state->uio_mask = config->antenna_gpio;
+
+ /* Default gpio to DVB-C */
+ if (!state->antenna_dvbt && state->antenna_gpio)
+ state->m_gpio |= state->antenna_gpio;
+ else
+ state->m_gpio &= ~state->antenna_gpio;
+
+ mutex_init(&state->mutex);
+
+ memcpy(&state->frontend.ops, &drxk_ops, sizeof(drxk_ops));
+ state->frontend.demodulator_priv = state;
+
+ init_state(state);
+
+ /* Load firmware and initialize DRX-K */
+ if (state->microcode_name) {
+ const struct firmware *fw = NULL;
+
+ status = request_firmware(&fw, state->microcode_name,
+ state->i2c->dev.parent);
+ if (status < 0)
+ fw = NULL;
+ load_firmware_cb(fw, state);
+ } else if (init_drxk(state) < 0)
+ goto error;
+
+
+ /* Initialize stats */
+ p = &state->frontend.dtv_property_cache;
+ p->strength.len = 1;
+ p->cnr.len = 1;
+ p->block_error.len = 1;
+ p->block_count.len = 1;
+ p->pre_bit_error.len = 1;
+ p->pre_bit_count.len = 1;
+ p->post_bit_error.len = 1;
+ p->post_bit_count.len = 1;
+
+ p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ pr_info("frontend initialized.\n");
+ return &state->frontend;
+
+error:
+ pr_err("not found\n");
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(drxk_attach);
+
+MODULE_DESCRIPTION("DRX-K driver");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/drxk_hard.h b/drivers/media/dvb-frontends/drxk_hard.h
new file mode 100644
index 000000000000..a850a876deee
--- /dev/null
+++ b/drivers/media/dvb-frontends/drxk_hard.h
@@ -0,0 +1,368 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include "drxk_map.h"
+
+#define DRXK_VERSION_MAJOR 0
+#define DRXK_VERSION_MINOR 9
+#define DRXK_VERSION_PATCH 4300
+
+#define HI_I2C_DELAY 42
+#define HI_I2C_BRIDGE_DELAY 350
+#define DRXK_MAX_RETRIES 100
+
+#define DRIVER_4400 1
+
+#define DRXX_JTAGID 0x039210D9
+#define DRXX_J_JTAGID 0x239310D9
+#define DRXX_K_JTAGID 0x039210D9
+
+#define DRX_UNKNOWN 254
+#define DRX_AUTO 255
+
+#define DRX_SCU_READY 0
+#define DRXK_MAX_WAITTIME (200)
+#define SCU_RESULT_OK 0
+#define SCU_RESULT_SIZE -4
+#define SCU_RESULT_INVPAR -3
+#define SCU_RESULT_UNKSTD -2
+#define SCU_RESULT_UNKCMD -1
+
+#ifndef DRXK_OFDM_TR_SHUTDOWN_TIMEOUT
+#define DRXK_OFDM_TR_SHUTDOWN_TIMEOUT (200)
+#endif
+
+#define DRXK_8VSB_MPEG_BIT_RATE 19392658UL /*bps*/
+#define DRXK_DVBT_MPEG_BIT_RATE 32000000UL /*bps*/
+#define DRXK_QAM16_MPEG_BIT_RATE 27000000UL /*bps*/
+#define DRXK_QAM32_MPEG_BIT_RATE 33000000UL /*bps*/
+#define DRXK_QAM64_MPEG_BIT_RATE 40000000UL /*bps*/
+#define DRXK_QAM128_MPEG_BIT_RATE 46000000UL /*bps*/
+#define DRXK_QAM256_MPEG_BIT_RATE 52000000UL /*bps*/
+#define DRXK_MAX_MPEG_BIT_RATE 52000000UL /*bps*/
+
+#define IQM_CF_OUT_ENA_OFDM__M 0x4
+#define IQM_FS_ADJ_SEL_B_QAM 0x1
+#define IQM_FS_ADJ_SEL_B_OFF 0x0
+#define IQM_FS_ADJ_SEL_B_VSB 0x2
+#define IQM_RC_ADJ_SEL_B_OFF 0x0
+#define IQM_RC_ADJ_SEL_B_QAM 0x1
+#define IQM_RC_ADJ_SEL_B_VSB 0x2
+
+enum operation_mode {
+ OM_NONE,
+ OM_QAM_ITU_A,
+ OM_QAM_ITU_B,
+ OM_QAM_ITU_C,
+ OM_DVBT
+};
+
+enum drx_power_mode {
+ DRX_POWER_UP = 0,
+ DRX_POWER_MODE_1,
+ DRX_POWER_MODE_2,
+ DRX_POWER_MODE_3,
+ DRX_POWER_MODE_4,
+ DRX_POWER_MODE_5,
+ DRX_POWER_MODE_6,
+ DRX_POWER_MODE_7,
+ DRX_POWER_MODE_8,
+
+ DRX_POWER_MODE_9,
+ DRX_POWER_MODE_10,
+ DRX_POWER_MODE_11,
+ DRX_POWER_MODE_12,
+ DRX_POWER_MODE_13,
+ DRX_POWER_MODE_14,
+ DRX_POWER_MODE_15,
+ DRX_POWER_MODE_16,
+ DRX_POWER_DOWN = 255
+};
+
+
+/* Intermediate power mode for DRXK, power down OFDM clock domain */
+#ifndef DRXK_POWER_DOWN_OFDM
+#define DRXK_POWER_DOWN_OFDM DRX_POWER_MODE_1
+#endif
+
+/* Intermediate power mode for DRXK, power down core (sysclk) */
+#ifndef DRXK_POWER_DOWN_CORE
+#define DRXK_POWER_DOWN_CORE DRX_POWER_MODE_9
+#endif
+
+/* Intermediate power mode for DRXK, power down pll (only osc runs) */
+#ifndef DRXK_POWER_DOWN_PLL
+#define DRXK_POWER_DOWN_PLL DRX_POWER_MODE_10
+#endif
+
+
+enum agc_ctrl_mode {
+ DRXK_AGC_CTRL_AUTO = 0,
+ DRXK_AGC_CTRL_USER,
+ DRXK_AGC_CTRL_OFF
+};
+
+enum e_drxk_state {
+ DRXK_UNINITIALIZED = 0,
+ DRXK_STOPPED,
+ DRXK_DTV_STARTED,
+ DRXK_ATV_STARTED,
+ DRXK_POWERED_DOWN,
+ DRXK_NO_DEV /* If drxk init failed */
+};
+
+enum e_drxk_coef_array_index {
+ DRXK_COEF_IDX_MN = 0,
+ DRXK_COEF_IDX_FM ,
+ DRXK_COEF_IDX_L ,
+ DRXK_COEF_IDX_LP ,
+ DRXK_COEF_IDX_BG ,
+ DRXK_COEF_IDX_DK ,
+ DRXK_COEF_IDX_I ,
+ DRXK_COEF_IDX_MAX
+};
+enum e_drxk_sif_attenuation {
+ DRXK_SIF_ATTENUATION_0DB,
+ DRXK_SIF_ATTENUATION_3DB,
+ DRXK_SIF_ATTENUATION_6DB,
+ DRXK_SIF_ATTENUATION_9DB
+};
+enum e_drxk_constellation {
+ DRX_CONSTELLATION_BPSK = 0,
+ DRX_CONSTELLATION_QPSK,
+ DRX_CONSTELLATION_PSK8,
+ DRX_CONSTELLATION_QAM16,
+ DRX_CONSTELLATION_QAM32,
+ DRX_CONSTELLATION_QAM64,
+ DRX_CONSTELLATION_QAM128,
+ DRX_CONSTELLATION_QAM256,
+ DRX_CONSTELLATION_QAM512,
+ DRX_CONSTELLATION_QAM1024,
+ DRX_CONSTELLATION_UNKNOWN = DRX_UNKNOWN,
+ DRX_CONSTELLATION_AUTO = DRX_AUTO
+};
+enum e_drxk_interleave_mode {
+ DRXK_QAM_I12_J17 = 16,
+ DRXK_QAM_I_UNKNOWN = DRX_UNKNOWN
+};
+enum {
+ DRXK_SPIN_A1 = 0,
+ DRXK_SPIN_A2,
+ DRXK_SPIN_A3,
+ DRXK_SPIN_UNKNOWN
+};
+
+enum drxk_cfg_dvbt_sqi_speed {
+ DRXK_DVBT_SQI_SPEED_FAST = 0,
+ DRXK_DVBT_SQI_SPEED_MEDIUM,
+ DRXK_DVBT_SQI_SPEED_SLOW,
+ DRXK_DVBT_SQI_SPEED_UNKNOWN = DRX_UNKNOWN
+} ;
+
+enum drx_fftmode_t {
+ DRX_FFTMODE_2K = 0,
+ DRX_FFTMODE_4K,
+ DRX_FFTMODE_8K,
+ DRX_FFTMODE_UNKNOWN = DRX_UNKNOWN,
+ DRX_FFTMODE_AUTO = DRX_AUTO
+};
+
+enum drxmpeg_str_width_t {
+ DRX_MPEG_STR_WIDTH_1,
+ DRX_MPEG_STR_WIDTH_8
+};
+
+enum drx_qam_lock_range_t {
+ DRX_QAM_LOCKRANGE_NORMAL,
+ DRX_QAM_LOCKRANGE_EXTENDED
+};
+
+struct drxk_cfg_dvbt_echo_thres_t {
+ u16 threshold;
+ enum drx_fftmode_t fft_mode;
+} ;
+
+struct s_cfg_agc {
+ enum agc_ctrl_mode ctrl_mode; /* off, user, auto */
+ u16 output_level; /* range dependent on AGC */
+ u16 min_output_level; /* range dependent on AGC */
+ u16 max_output_level; /* range dependent on AGC */
+ u16 speed; /* range dependent on AGC */
+ u16 top; /* rf-agc take over point */
+ u16 cut_off_current; /* rf-agc is accelerated if output current
+ is below cut-off current */
+ u16 ingain_tgt_max;
+ u16 fast_clip_ctrl_delay;
+};
+
+struct s_cfg_pre_saw {
+ u16 reference; /* pre SAW reference value, range 0 .. 31 */
+ bool use_pre_saw; /* TRUE algorithms must use pre SAW sense */
+};
+
+struct drxk_ofdm_sc_cmd_t {
+ u16 cmd; /* Command number */
+ u16 subcmd; /* Sub-command parameter*/
+ u16 param0; /* General purpous param */
+ u16 param1; /* General purpous param */
+ u16 param2; /* General purpous param */
+ u16 param3; /* General purpous param */
+ u16 param4; /* General purpous param */
+};
+
+struct drxk_state {
+ struct dvb_frontend frontend;
+ struct dtv_frontend_properties props;
+ struct device *dev;
+
+ struct i2c_adapter *i2c;
+ u8 demod_address;
+ void *priv;
+
+ struct mutex mutex;
+
+ u32 m_instance; /* Channel 1,2,3 or 4 */
+
+ int m_chunk_size;
+ u8 chunk[256];
+
+ bool m_has_lna;
+ bool m_has_dvbt;
+ bool m_has_dvbc;
+ bool m_has_audio;
+ bool m_has_atv;
+ bool m_has_oob;
+ bool m_has_sawsw; /* TRUE if mat_tx is available */
+ bool m_has_gpio1; /* TRUE if mat_rx is available */
+ bool m_has_gpio2; /* TRUE if GPIO is available */
+ bool m_has_irqn; /* TRUE if IRQN is available */
+ u16 m_osc_clock_freq;
+ u16 m_hi_cfg_timing_div;
+ u16 m_hi_cfg_bridge_delay;
+ u16 m_hi_cfg_wake_up_key;
+ u16 m_hi_cfg_timeout;
+ u16 m_hi_cfg_ctrl;
+ s32 m_sys_clock_freq; /* system clock frequency in kHz */
+
+ enum e_drxk_state m_drxk_state; /* State of Drxk (init,stopped,started) */
+ enum operation_mode m_operation_mode; /* digital standards */
+ struct s_cfg_agc m_vsb_rf_agc_cfg; /* settings for VSB RF-AGC */
+ struct s_cfg_agc m_vsb_if_agc_cfg; /* settings for VSB IF-AGC */
+ u16 m_vsb_pga_cfg; /* settings for VSB PGA */
+ struct s_cfg_pre_saw m_vsb_pre_saw_cfg; /* settings for pre SAW sense */
+ s32 m_Quality83percent; /* MER level (*0.1 dB) for 83% quality indication */
+ s32 m_Quality93percent; /* MER level (*0.1 dB) for 93% quality indication */
+ bool m_smart_ant_inverted;
+ bool m_b_debug_enable_bridge;
+ bool m_b_p_down_open_bridge; /* only open DRXK bridge before power-down once it has been accessed */
+ bool m_b_power_down; /* Power down when not used */
+
+ u32 m_iqm_fs_rate_ofs; /* frequency shift as written to DRXK register (28bit fixpoint) */
+
+ bool m_enable_mpeg_output; /* If TRUE, enable MPEG output */
+ bool m_insert_rs_byte; /* If TRUE, insert RS byte */
+ bool m_enable_parallel; /* If TRUE, parallel out otherwise serial */
+ bool m_invert_data; /* If TRUE, invert DATA signals */
+ bool m_invert_err; /* If TRUE, invert ERR signal */
+ bool m_invert_str; /* If TRUE, invert STR signals */
+ bool m_invert_val; /* If TRUE, invert VAL signals */
+ bool m_invert_clk; /* If TRUE, invert CLK signals */
+ bool m_dvbc_static_clk;
+ bool m_dvbt_static_clk; /* If TRUE, static MPEG clockrate will
+ be used, otherwise clockrate will
+ adapt to the bitrate of the TS */
+ u32 m_dvbt_bitrate;
+ u32 m_dvbc_bitrate;
+
+ u8 m_ts_data_strength;
+ u8 m_ts_clockk_strength;
+
+ bool m_itut_annex_c; /* If true, uses ITU-T DVB-C Annex C, instead of Annex A */
+
+ enum drxmpeg_str_width_t m_width_str; /* MPEG start width */
+ u32 m_mpeg_ts_static_bitrate; /* Maximum bitrate in b/s in case
+ static clockrate is selected */
+
+ /* LARGE_INTEGER m_startTime; */ /* Contains the time of the last demod start */
+ s32 m_mpeg_lock_time_out; /* WaitForLockStatus Timeout (counts from start time) */
+ s32 m_demod_lock_time_out; /* WaitForLockStatus Timeout (counts from start time) */
+
+ bool m_disable_te_ihandling;
+
+ bool m_rf_agc_pol;
+ bool m_if_agc_pol;
+
+ struct s_cfg_agc m_atv_rf_agc_cfg; /* settings for ATV RF-AGC */
+ struct s_cfg_agc m_atv_if_agc_cfg; /* settings for ATV IF-AGC */
+ struct s_cfg_pre_saw m_atv_pre_saw_cfg; /* settings for ATV pre SAW sense */
+ bool m_phase_correction_bypass;
+ s16 m_atv_top_vid_peak;
+ u16 m_atv_top_noise_th;
+ enum e_drxk_sif_attenuation m_sif_attenuation;
+ bool m_enable_cvbs_output;
+ bool m_enable_sif_output;
+ bool m_b_mirror_freq_spect;
+ enum e_drxk_constellation m_constellation; /* constellation type of the channel */
+ u32 m_curr_symbol_rate; /* Current QAM symbol rate */
+ struct s_cfg_agc m_qam_rf_agc_cfg; /* settings for QAM RF-AGC */
+ struct s_cfg_agc m_qam_if_agc_cfg; /* settings for QAM IF-AGC */
+ u16 m_qam_pga_cfg; /* settings for QAM PGA */
+ struct s_cfg_pre_saw m_qam_pre_saw_cfg; /* settings for QAM pre SAW sense */
+ enum e_drxk_interleave_mode m_qam_interleave_mode; /* QAM Interleave mode */
+ u16 m_fec_rs_plen;
+ u16 m_fec_rs_prescale;
+
+ enum drxk_cfg_dvbt_sqi_speed m_sqi_speed;
+
+ u16 m_gpio;
+ u16 m_gpio_cfg;
+
+ struct s_cfg_agc m_dvbt_rf_agc_cfg; /* settings for QAM RF-AGC */
+ struct s_cfg_agc m_dvbt_if_agc_cfg; /* settings for QAM IF-AGC */
+ struct s_cfg_pre_saw m_dvbt_pre_saw_cfg; /* settings for QAM pre SAW sense */
+
+ u16 m_agcfast_clip_ctrl_delay;
+ bool m_adc_comp_passed;
+ u16 m_adcCompCoef[64];
+ u16 m_adc_state;
+
+ u8 *m_microcode;
+ int m_microcode_length;
+ bool m_drxk_a3_rom_code;
+ bool m_drxk_a3_patch_code;
+
+ bool m_rfmirror;
+ u8 m_device_spin;
+ u32 m_iqm_rc_rate;
+
+ enum drx_power_mode m_current_power_mode;
+
+ /* when true, avoids other devices to use the I2C bus */
+ bool drxk_i2c_exclusive_lock;
+
+ /*
+ * Configurable parameters at the driver. They stores the values found
+ * at struct drxk_config.
+ */
+
+ u16 uio_mask; /* Bits used by UIO */
+
+ bool enable_merr_cfg;
+ bool single_master;
+ bool no_i2c_bridge;
+ bool antenna_dvbt;
+ u16 antenna_gpio;
+
+ enum fe_status fe_status;
+
+ /* Firmware */
+ const char *microcode_name;
+ struct completion fw_wait_load;
+ const struct firmware *fw;
+ int qam_demod_parameter_count;
+};
+
+#define NEVER_LOCK 0
+#define NOT_LOCKED 1
+#define DEMOD_LOCK 2
+#define FEC_LOCK 3
+#define MPEG_LOCK 4
+
diff --git a/drivers/media/dvb/frontends/drxk_map.h b/drivers/media/dvb-frontends/drxk_map.h
index 23e16c12f234..9234ef4fb68d 100644
--- a/drivers/media/dvb/frontends/drxk_map.h
+++ b/drivers/media/dvb-frontends/drxk_map.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#define AUD_COMM_EXEC__A 0x1000000
#define AUD_COMM_EXEC_STOP 0x0
#define FEC_COMM_EXEC__A 0x1C00000
@@ -10,6 +11,7 @@
#define FEC_RS_COMM_EXEC_STOP 0x0
#define FEC_RS_MEASUREMENT_PERIOD__A 0x1C30012
#define FEC_RS_MEASUREMENT_PRESCALE__A 0x1C30013
+#define FEC_RS_NR_BIT_ERRORS__A 0x1C30014
#define FEC_OC_MODE__A 0x1C40011
#define FEC_OC_MODE_PARITY__M 0x1
#define FEC_OC_DTO_MODE__A 0x1C40014
@@ -129,6 +131,8 @@
#define OFDM_EC_SB_PRIOR__A 0x3410013
#define OFDM_EC_SB_PRIOR_HI 0x0
#define OFDM_EC_SB_PRIOR_LO 0x1
+#define OFDM_EC_VD_ERR_BIT_CNT__A 0x3420017
+#define OFDM_EC_VD_IN_BIT_CNT__A 0x3420018
#define OFDM_EQ_TOP_TD_TPS_CONST__A 0x3010054
#define OFDM_EQ_TOP_TD_TPS_CONST__M 0x3
#define OFDM_EQ_TOP_TD_TPS_CONST_64QAM 0x2
diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c
new file mode 100644
index 000000000000..515aa7c7baf2
--- /dev/null
+++ b/drivers/media/dvb-frontends/ds3000.c
@@ -0,0 +1,1130 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ Montage Technology DS3000 - DVBS/S2 Demodulator driver
+ Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com>
+
+ Copyright (C) 2009-2012 TurboSight.com
+
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include <media/dvb_frontend.h>
+#include "ts2020.h"
+#include "ds3000.h"
+
+static int debug;
+
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(args); \
+ } while (0)
+
+/* as of March 2009 current DS3000 firmware version is 1.78 */
+/* DS3000 FW v1.78 MD5: a32d17910c4f370073f9346e71d34b80 */
+#define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds3000.fw"
+
+#define DS3000_SAMPLE_RATE 96000 /* in kHz */
+
+/* Register values to initialise the demod in DVB-S mode */
+static u8 ds3000_dvbs_init_tab[] = {
+ 0x23, 0x05,
+ 0x08, 0x03,
+ 0x0c, 0x00,
+ 0x21, 0x54,
+ 0x25, 0x82,
+ 0x27, 0x31,
+ 0x30, 0x08,
+ 0x31, 0x40,
+ 0x32, 0x32,
+ 0x33, 0x35,
+ 0x35, 0xff,
+ 0x3a, 0x00,
+ 0x37, 0x10,
+ 0x38, 0x10,
+ 0x39, 0x02,
+ 0x42, 0x60,
+ 0x4a, 0x40,
+ 0x4b, 0x04,
+ 0x4d, 0x91,
+ 0x5d, 0xc8,
+ 0x50, 0x77,
+ 0x51, 0x77,
+ 0x52, 0x36,
+ 0x53, 0x36,
+ 0x56, 0x01,
+ 0x63, 0x43,
+ 0x64, 0x30,
+ 0x65, 0x40,
+ 0x68, 0x26,
+ 0x69, 0x4c,
+ 0x70, 0x20,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x40,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x60,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x80,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0xa0,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x1f,
+ 0x76, 0x00,
+ 0x77, 0xd1,
+ 0x78, 0x0c,
+ 0x79, 0x80,
+ 0x7f, 0x04,
+ 0x7c, 0x00,
+ 0x80, 0x86,
+ 0x81, 0xa6,
+ 0x85, 0x04,
+ 0xcd, 0xf4,
+ 0x90, 0x33,
+ 0xa0, 0x44,
+ 0xc0, 0x18,
+ 0xc3, 0x10,
+ 0xc4, 0x08,
+ 0xc5, 0x80,
+ 0xc6, 0x80,
+ 0xc7, 0x0a,
+ 0xc8, 0x1a,
+ 0xc9, 0x80,
+ 0xfe, 0x92,
+ 0xe0, 0xf8,
+ 0xe6, 0x8b,
+ 0xd0, 0x40,
+ 0xf8, 0x20,
+ 0xfa, 0x0f,
+ 0xfd, 0x20,
+ 0xad, 0x20,
+ 0xae, 0x07,
+ 0xb8, 0x00,
+};
+
+/* Register values to initialise the demod in DVB-S2 mode */
+static u8 ds3000_dvbs2_init_tab[] = {
+ 0x23, 0x0f,
+ 0x08, 0x07,
+ 0x0c, 0x00,
+ 0x21, 0x54,
+ 0x25, 0x82,
+ 0x27, 0x31,
+ 0x30, 0x08,
+ 0x31, 0x32,
+ 0x32, 0x32,
+ 0x33, 0x35,
+ 0x35, 0xff,
+ 0x3a, 0x00,
+ 0x37, 0x10,
+ 0x38, 0x10,
+ 0x39, 0x02,
+ 0x42, 0x60,
+ 0x4a, 0x80,
+ 0x4b, 0x04,
+ 0x4d, 0x81,
+ 0x5d, 0x88,
+ 0x50, 0x36,
+ 0x51, 0x36,
+ 0x52, 0x36,
+ 0x53, 0x36,
+ 0x63, 0x60,
+ 0x64, 0x10,
+ 0x65, 0x10,
+ 0x68, 0x04,
+ 0x69, 0x29,
+ 0x70, 0x20,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x40,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x60,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x80,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0xa0,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x1f,
+ 0xa0, 0x44,
+ 0xc0, 0x08,
+ 0xc1, 0x10,
+ 0xc2, 0x08,
+ 0xc3, 0x10,
+ 0xc4, 0x08,
+ 0xc5, 0xf0,
+ 0xc6, 0xf0,
+ 0xc7, 0x0a,
+ 0xc8, 0x1a,
+ 0xc9, 0x80,
+ 0xca, 0x23,
+ 0xcb, 0x24,
+ 0xce, 0x74,
+ 0x90, 0x03,
+ 0x76, 0x80,
+ 0x77, 0x42,
+ 0x78, 0x0a,
+ 0x79, 0x80,
+ 0xad, 0x40,
+ 0xae, 0x07,
+ 0x7f, 0xd4,
+ 0x7c, 0x00,
+ 0x80, 0xa8,
+ 0x81, 0xda,
+ 0x7c, 0x01,
+ 0x80, 0xda,
+ 0x81, 0xec,
+ 0x7c, 0x02,
+ 0x80, 0xca,
+ 0x81, 0xeb,
+ 0x7c, 0x03,
+ 0x80, 0xba,
+ 0x81, 0xdb,
+ 0x85, 0x08,
+ 0x86, 0x00,
+ 0x87, 0x02,
+ 0x89, 0x80,
+ 0x8b, 0x44,
+ 0x8c, 0xaa,
+ 0x8a, 0x10,
+ 0xba, 0x00,
+ 0xf5, 0x04,
+ 0xfe, 0x44,
+ 0xd2, 0x32,
+ 0xb8, 0x00,
+};
+
+struct ds3000_state {
+ struct i2c_adapter *i2c;
+ const struct ds3000_config *config;
+ struct dvb_frontend frontend;
+ /* previous uncorrected block counter for DVB-S2 */
+ u16 prevUCBS2;
+};
+
+static int ds3000_writereg(struct ds3000_state *state, int reg, int data)
+{
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = { .addr = state->config->demod_address,
+ .flags = 0, .buf = buf, .len = 2 };
+ int err;
+
+ dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data);
+
+ err = i2c_transfer(state->i2c, &msg, 1);
+ if (err != 1) {
+ printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n",
+ __func__, err, reg, data);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static int ds3000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+
+ if (enable)
+ ds3000_writereg(state, 0x03, 0x12);
+ else
+ ds3000_writereg(state, 0x03, 0x02);
+
+ return 0;
+}
+
+/* I2C write for 8k firmware load */
+static int ds3000_writeFW(struct ds3000_state *state, int reg,
+ const u8 *data, u16 len)
+{
+ int i, ret = 0;
+ struct i2c_msg msg;
+ u8 *buf;
+
+ buf = kmalloc(33, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ *(buf) = reg;
+
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.buf = buf;
+ msg.len = 33;
+
+ for (i = 0; i < len; i += 32) {
+ memcpy(buf + 1, data + i, 32);
+
+ dprintk("%s: write reg 0x%02x, len = %d\n", __func__, reg, len);
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+ if (ret != 1) {
+ printk(KERN_ERR "%s: write error(err == %i, reg == 0x%02x\n",
+ __func__, ret, reg);
+ ret = -EREMOTEIO;
+ goto error;
+ }
+ }
+ ret = 0;
+
+error:
+ kfree(buf);
+
+ return ret;
+}
+
+static int ds3000_readreg(struct ds3000_state *state, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ }, {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) {
+ printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret);
+ return ret;
+ }
+
+ dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]);
+
+ return b1[0];
+}
+
+static int ds3000_load_firmware(struct dvb_frontend *fe,
+ const struct firmware *fw);
+
+static int ds3000_firmware_ondemand(struct dvb_frontend *fe)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ const struct firmware *fw;
+ int ret = 0;
+
+ dprintk("%s()\n", __func__);
+
+ ret = ds3000_readreg(state, 0xb2);
+ if (ret < 0)
+ return ret;
+
+ /* Load firmware */
+ /* request the firmware, this will block until someone uploads it */
+ printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__,
+ DS3000_DEFAULT_FIRMWARE);
+ ret = request_firmware(&fw, DS3000_DEFAULT_FIRMWARE,
+ state->i2c->dev.parent);
+ printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", __func__);
+ if (ret) {
+ printk(KERN_ERR "%s: No firmware uploaded (timeout or file not found?)\n",
+ __func__);
+ return ret;
+ }
+
+ ret = ds3000_load_firmware(fe, fw);
+ if (ret)
+ printk("%s: Writing firmware to device failed\n", __func__);
+
+ release_firmware(fw);
+
+ dprintk("%s: Firmware upload %s\n", __func__,
+ ret == 0 ? "complete" : "failed");
+
+ return ret;
+}
+
+static int ds3000_load_firmware(struct dvb_frontend *fe,
+ const struct firmware *fw)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ int ret = 0;
+
+ dprintk("%s\n", __func__);
+ dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n",
+ fw->size,
+ fw->data[0],
+ fw->data[1],
+ fw->data[fw->size - 2],
+ fw->data[fw->size - 1]);
+
+ /* Begin the firmware load process */
+ ds3000_writereg(state, 0xb2, 0x01);
+ /* write the entire firmware */
+ ret = ds3000_writeFW(state, 0xb0, fw->data, fw->size);
+ ds3000_writereg(state, 0xb2, 0x00);
+
+ return ret;
+}
+
+static int ds3000_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ u8 data;
+
+ dprintk("%s(%d)\n", __func__, voltage);
+
+ data = ds3000_readreg(state, 0xa2);
+ data |= 0x03; /* bit0 V/H, bit1 off/on */
+
+ switch (voltage) {
+ case SEC_VOLTAGE_18:
+ data &= ~0x03;
+ break;
+ case SEC_VOLTAGE_13:
+ data &= ~0x03;
+ data |= 0x01;
+ break;
+ case SEC_VOLTAGE_OFF:
+ break;
+ }
+
+ ds3000_writereg(state, 0xa2, data);
+
+ return 0;
+}
+
+static int ds3000_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int lock;
+
+ *status = 0;
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ lock = ds3000_readreg(state, 0xd1);
+ if ((lock & 0x07) == 0x07)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+
+ break;
+ case SYS_DVBS2:
+ lock = ds3000_readreg(state, 0x0d);
+ if ((lock & 0x8f) == 0x8f)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (state->config->set_lock_led)
+ state->config->set_lock_led(fe, *status == 0 ? 0 : 1);
+
+ dprintk("%s: status = 0x%02x\n", __func__, lock);
+
+ return 0;
+}
+
+/* read DS3000 BER value */
+static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 data;
+ u32 ber_reading, lpdc_frames;
+
+ dprintk("%s()\n", __func__);
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ /* set the number of bytes checked during
+ BER estimation */
+ ds3000_writereg(state, 0xf9, 0x04);
+ /* read BER estimation status */
+ data = ds3000_readreg(state, 0xf8);
+ /* check if BER estimation is ready */
+ if ((data & 0x10) == 0) {
+ /* this is the number of error bits,
+ to calculate the bit error rate
+ divide to 8388608 */
+ *ber = (ds3000_readreg(state, 0xf7) << 8) |
+ ds3000_readreg(state, 0xf6);
+ /* start counting error bits */
+ /* need to be set twice
+ otherwise it fails sometimes */
+ data |= 0x10;
+ ds3000_writereg(state, 0xf8, data);
+ ds3000_writereg(state, 0xf8, data);
+ } else
+ /* used to indicate that BER estimation
+ is not ready, i.e. BER is unknown */
+ *ber = 0xffffffff;
+ break;
+ case SYS_DVBS2:
+ /* read the number of LPDC decoded frames */
+ lpdc_frames = (ds3000_readreg(state, 0xd7) << 16) |
+ (ds3000_readreg(state, 0xd6) << 8) |
+ ds3000_readreg(state, 0xd5);
+ /* read the number of packets with bad CRC */
+ ber_reading = (ds3000_readreg(state, 0xf8) << 8) |
+ ds3000_readreg(state, 0xf7);
+ if (lpdc_frames > 750) {
+ /* clear LPDC frame counters */
+ ds3000_writereg(state, 0xd1, 0x01);
+ /* clear bad packets counter */
+ ds3000_writereg(state, 0xf9, 0x01);
+ /* enable bad packets counter */
+ ds3000_writereg(state, 0xf9, 0x00);
+ /* enable LPDC frame counters */
+ ds3000_writereg(state, 0xd1, 0x00);
+ *ber = ber_reading;
+ } else
+ /* used to indicate that BER estimation is not ready,
+ i.e. BER is unknown */
+ *ber = 0xffffffff;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ds3000_read_signal_strength(struct dvb_frontend *fe,
+ u16 *signal_strength)
+{
+ if (fe->ops.tuner_ops.get_rf_strength)
+ fe->ops.tuner_ops.get_rf_strength(fe, signal_strength);
+
+ return 0;
+}
+
+/* calculate DS3000 snr value in dB */
+static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 snr_reading, snr_value;
+ u32 dvbs2_signal_reading, dvbs2_noise_reading, tmp;
+ static const u16 dvbs_snr_tab[] = { /* 20 x Table (rounded up) */
+ 0x0000, 0x1b13, 0x2aea, 0x3627, 0x3ede, 0x45fe, 0x4c03,
+ 0x513a, 0x55d4, 0x59f2, 0x5dab, 0x6111, 0x6431, 0x6717,
+ 0x69c9, 0x6c4e, 0x6eac, 0x70e8, 0x7304, 0x7505
+ };
+ static const u16 dvbs2_snr_tab[] = { /* 80 x Table (rounded up) */
+ 0x0000, 0x0bc2, 0x12a3, 0x1785, 0x1b4e, 0x1e65, 0x2103,
+ 0x2347, 0x2546, 0x2710, 0x28ae, 0x2a28, 0x2b83, 0x2cc5,
+ 0x2df1, 0x2f09, 0x3010, 0x3109, 0x31f4, 0x32d2, 0x33a6,
+ 0x3470, 0x3531, 0x35ea, 0x369b, 0x3746, 0x37ea, 0x3888,
+ 0x3920, 0x39b3, 0x3a42, 0x3acc, 0x3b51, 0x3bd3, 0x3c51,
+ 0x3ccb, 0x3d42, 0x3db6, 0x3e27, 0x3e95, 0x3f00, 0x3f68,
+ 0x3fcf, 0x4033, 0x4094, 0x40f4, 0x4151, 0x41ac, 0x4206,
+ 0x425e, 0x42b4, 0x4308, 0x435b, 0x43ac, 0x43fc, 0x444a,
+ 0x4497, 0x44e2, 0x452d, 0x4576, 0x45bd, 0x4604, 0x4649,
+ 0x468e, 0x46d1, 0x4713, 0x4755, 0x4795, 0x47d4, 0x4813,
+ 0x4851, 0x488d, 0x48c9, 0x4904, 0x493f, 0x4978, 0x49b1,
+ 0x49e9, 0x4a20, 0x4a57
+ };
+
+ dprintk("%s()\n", __func__);
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ snr_reading = ds3000_readreg(state, 0xff);
+ snr_reading /= 8;
+ if (snr_reading == 0)
+ *snr = 0x0000;
+ else {
+ if (snr_reading > 20)
+ snr_reading = 20;
+ snr_value = dvbs_snr_tab[snr_reading - 1] * 10 / 23026;
+ /* cook the value to be suitable for szap-s2
+ human readable output */
+ *snr = snr_value * 8 * 655;
+ }
+ dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
+ snr_reading, *snr);
+ break;
+ case SYS_DVBS2:
+ dvbs2_noise_reading = (ds3000_readreg(state, 0x8c) & 0x3f) +
+ (ds3000_readreg(state, 0x8d) << 4);
+ dvbs2_signal_reading = ds3000_readreg(state, 0x8e);
+ tmp = dvbs2_signal_reading * dvbs2_signal_reading >> 1;
+ if (tmp == 0) {
+ *snr = 0x0000;
+ return 0;
+ }
+ if (dvbs2_noise_reading == 0) {
+ snr_value = 0x0013;
+ /* cook the value to be suitable for szap-s2
+ human readable output */
+ *snr = 0xffff;
+ return 0;
+ }
+ if (tmp > dvbs2_noise_reading) {
+ snr_reading = tmp / dvbs2_noise_reading;
+ if (snr_reading > 80)
+ snr_reading = 80;
+ snr_value = dvbs2_snr_tab[snr_reading - 1] / 1000;
+ /* cook the value to be suitable for szap-s2
+ human readable output */
+ *snr = snr_value * 5 * 655;
+ } else {
+ snr_reading = dvbs2_noise_reading / tmp;
+ if (snr_reading > 80)
+ snr_reading = 80;
+ *snr = -(dvbs2_snr_tab[snr_reading - 1] / 1000);
+ }
+ dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
+ snr_reading, *snr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* read DS3000 uncorrected blocks */
+static int ds3000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 data;
+ u16 _ucblocks;
+
+ dprintk("%s()\n", __func__);
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ *ucblocks = (ds3000_readreg(state, 0xf5) << 8) |
+ ds3000_readreg(state, 0xf4);
+ data = ds3000_readreg(state, 0xf8);
+ /* clear packet counters */
+ data &= ~0x20;
+ ds3000_writereg(state, 0xf8, data);
+ /* enable packet counters */
+ data |= 0x20;
+ ds3000_writereg(state, 0xf8, data);
+ break;
+ case SYS_DVBS2:
+ _ucblocks = (ds3000_readreg(state, 0xe2) << 8) |
+ ds3000_readreg(state, 0xe1);
+ if (_ucblocks > state->prevUCBS2)
+ *ucblocks = _ucblocks - state->prevUCBS2;
+ else
+ *ucblocks = state->prevUCBS2 - _ucblocks;
+ state->prevUCBS2 = _ucblocks;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ds3000_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ u8 data;
+
+ dprintk("%s(%d)\n", __func__, tone);
+ if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
+ printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone);
+ return -EINVAL;
+ }
+
+ data = ds3000_readreg(state, 0xa2);
+ data &= ~0xc0;
+ ds3000_writereg(state, 0xa2, data);
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ dprintk("%s: setting tone on\n", __func__);
+ data = ds3000_readreg(state, 0xa1);
+ data &= ~0x43;
+ data |= 0x04;
+ ds3000_writereg(state, 0xa1, data);
+ break;
+ case SEC_TONE_OFF:
+ dprintk("%s: setting tone off\n", __func__);
+ data = ds3000_readreg(state, 0xa2);
+ data |= 0x80;
+ ds3000_writereg(state, 0xa2, data);
+ break;
+ }
+
+ return 0;
+}
+
+static int ds3000_send_diseqc_msg(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *d)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ int i;
+ u8 data;
+
+ /* Dump DiSEqC message */
+ dprintk("%s(", __func__);
+ for (i = 0 ; i < d->msg_len;) {
+ dprintk("0x%02x", d->msg[i]);
+ if (++i < d->msg_len)
+ dprintk(", ");
+ }
+
+ /* enable DiSEqC message send pin */
+ data = ds3000_readreg(state, 0xa2);
+ data &= ~0xc0;
+ ds3000_writereg(state, 0xa2, data);
+
+ /* DiSEqC message */
+ for (i = 0; i < d->msg_len; i++)
+ ds3000_writereg(state, 0xa3 + i, d->msg[i]);
+
+ data = ds3000_readreg(state, 0xa1);
+ /* clear DiSEqC message length and status,
+ enable DiSEqC message send */
+ data &= ~0xf8;
+ /* set DiSEqC mode, modulation active during 33 pulses,
+ set DiSEqC message length */
+ data |= ((d->msg_len - 1) << 3) | 0x07;
+ ds3000_writereg(state, 0xa1, data);
+
+ /* wait up to 150ms for DiSEqC transmission to complete */
+ for (i = 0; i < 15; i++) {
+ data = ds3000_readreg(state, 0xa1);
+ if ((data & 0x40) == 0)
+ break;
+ msleep(10);
+ }
+
+ /* DiSEqC timeout after 150ms */
+ if (i == 15) {
+ data = ds3000_readreg(state, 0xa1);
+ data &= ~0x80;
+ data |= 0x40;
+ ds3000_writereg(state, 0xa1, data);
+
+ data = ds3000_readreg(state, 0xa2);
+ data &= ~0xc0;
+ data |= 0x80;
+ ds3000_writereg(state, 0xa2, data);
+
+ return -ETIMEDOUT;
+ }
+
+ data = ds3000_readreg(state, 0xa2);
+ data &= ~0xc0;
+ data |= 0x80;
+ ds3000_writereg(state, 0xa2, data);
+
+ return 0;
+}
+
+/* Send DiSEqC burst */
+static int ds3000_diseqc_send_burst(struct dvb_frontend *fe,
+ enum fe_sec_mini_cmd burst)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ int i;
+ u8 data;
+
+ dprintk("%s()\n", __func__);
+
+ data = ds3000_readreg(state, 0xa2);
+ data &= ~0xc0;
+ ds3000_writereg(state, 0xa2, data);
+
+ /* DiSEqC burst */
+ if (burst == SEC_MINI_A)
+ /* Unmodulated tone burst */
+ ds3000_writereg(state, 0xa1, 0x02);
+ else if (burst == SEC_MINI_B)
+ /* Modulated tone burst */
+ ds3000_writereg(state, 0xa1, 0x01);
+ else
+ return -EINVAL;
+
+ msleep(13);
+ for (i = 0; i < 5; i++) {
+ data = ds3000_readreg(state, 0xa1);
+ if ((data & 0x40) == 0)
+ break;
+ msleep(1);
+ }
+
+ if (i == 5) {
+ data = ds3000_readreg(state, 0xa1);
+ data &= ~0x80;
+ data |= 0x40;
+ ds3000_writereg(state, 0xa1, data);
+
+ data = ds3000_readreg(state, 0xa2);
+ data &= ~0xc0;
+ data |= 0x80;
+ ds3000_writereg(state, 0xa2, data);
+
+ return -ETIMEDOUT;
+ }
+
+ data = ds3000_readreg(state, 0xa2);
+ data &= ~0xc0;
+ data |= 0x80;
+ ds3000_writereg(state, 0xa2, data);
+
+ return 0;
+}
+
+static void ds3000_release(struct dvb_frontend *fe)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+
+ if (state->config->set_lock_led)
+ state->config->set_lock_led(fe, 0);
+
+ dprintk("%s\n", __func__);
+ kfree(state);
+}
+
+static const struct dvb_frontend_ops ds3000_ops;
+
+struct dvb_frontend *ds3000_attach(const struct ds3000_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct ds3000_state *state;
+ int ret;
+
+ dprintk("%s\n", __func__);
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ state->config = config;
+ state->i2c = i2c;
+ state->prevUCBS2 = 0;
+
+ /* check if the demod is present */
+ ret = ds3000_readreg(state, 0x00) & 0xfe;
+ if (ret != 0xe0) {
+ kfree(state);
+ printk(KERN_ERR "Invalid probe, probably not a DS3000\n");
+ return NULL;
+ }
+
+ printk(KERN_INFO "DS3000 chip version: %d.%d attached.\n",
+ ds3000_readreg(state, 0x02),
+ ds3000_readreg(state, 0x01));
+
+ memcpy(&state->frontend.ops, &ds3000_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ /*
+ * Some devices like T480 starts with voltage on. Be sure
+ * to turn voltage off during init, as this can otherwise
+ * interfere with Unicable SCR systems.
+ */
+ ds3000_set_voltage(&state->frontend, SEC_VOLTAGE_OFF);
+ return &state->frontend;
+}
+EXPORT_SYMBOL_GPL(ds3000_attach);
+
+static int ds3000_set_carrier_offset(struct dvb_frontend *fe,
+ s32 carrier_offset_khz)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ s32 tmp;
+
+ tmp = carrier_offset_khz;
+ tmp *= 65536;
+ tmp = (2 * tmp + DS3000_SAMPLE_RATE) / (2 * DS3000_SAMPLE_RATE);
+
+ if (tmp < 0)
+ tmp += 65536;
+
+ ds3000_writereg(state, 0x5f, tmp >> 8);
+ ds3000_writereg(state, 0x5e, tmp & 0xff);
+
+ return 0;
+}
+
+static int ds3000_set_frontend(struct dvb_frontend *fe)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ int i;
+ enum fe_status status;
+ s32 offset_khz;
+ u32 frequency;
+ u16 value;
+
+ dprintk("%s() ", __func__);
+
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, 0);
+ /* Tune */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ /* ds3000 global reset */
+ ds3000_writereg(state, 0x07, 0x80);
+ ds3000_writereg(state, 0x07, 0x00);
+ /* ds3000 built-in uC reset */
+ ds3000_writereg(state, 0xb2, 0x01);
+ /* ds3000 software reset */
+ ds3000_writereg(state, 0x00, 0x01);
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ /* initialise the demod in DVB-S mode */
+ for (i = 0; i < sizeof(ds3000_dvbs_init_tab); i += 2)
+ ds3000_writereg(state,
+ ds3000_dvbs_init_tab[i],
+ ds3000_dvbs_init_tab[i + 1]);
+ value = ds3000_readreg(state, 0xfe);
+ value &= 0xc0;
+ value |= 0x1b;
+ ds3000_writereg(state, 0xfe, value);
+ break;
+ case SYS_DVBS2:
+ /* initialise the demod in DVB-S2 mode */
+ for (i = 0; i < sizeof(ds3000_dvbs2_init_tab); i += 2)
+ ds3000_writereg(state,
+ ds3000_dvbs2_init_tab[i],
+ ds3000_dvbs2_init_tab[i + 1]);
+ if (c->symbol_rate >= 30000000)
+ ds3000_writereg(state, 0xfe, 0x54);
+ else
+ ds3000_writereg(state, 0xfe, 0x98);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* enable 27MHz clock output */
+ ds3000_writereg(state, 0x29, 0x80);
+ /* enable ac coupling */
+ ds3000_writereg(state, 0x25, 0x8a);
+
+ if ((c->symbol_rate < ds3000_ops.info.symbol_rate_min) ||
+ (c->symbol_rate > ds3000_ops.info.symbol_rate_max)) {
+ dprintk("%s() symbol_rate %u out of range (%u ... %u)\n",
+ __func__, c->symbol_rate,
+ ds3000_ops.info.symbol_rate_min,
+ ds3000_ops.info.symbol_rate_max);
+ return -EINVAL;
+ }
+
+ /* enhance symbol rate performance */
+ if ((c->symbol_rate / 1000) <= 5000) {
+ value = 29777 / (c->symbol_rate / 1000) + 1;
+ if (value % 2 != 0)
+ value++;
+ ds3000_writereg(state, 0xc3, 0x0d);
+ ds3000_writereg(state, 0xc8, value);
+ ds3000_writereg(state, 0xc4, 0x10);
+ ds3000_writereg(state, 0xc7, 0x0e);
+ } else if ((c->symbol_rate / 1000) <= 10000) {
+ value = 92166 / (c->symbol_rate / 1000) + 1;
+ if (value % 2 != 0)
+ value++;
+ ds3000_writereg(state, 0xc3, 0x07);
+ ds3000_writereg(state, 0xc8, value);
+ ds3000_writereg(state, 0xc4, 0x09);
+ ds3000_writereg(state, 0xc7, 0x12);
+ } else if ((c->symbol_rate / 1000) <= 20000) {
+ value = 64516 / (c->symbol_rate / 1000) + 1;
+ ds3000_writereg(state, 0xc3, value);
+ ds3000_writereg(state, 0xc8, 0x0e);
+ ds3000_writereg(state, 0xc4, 0x07);
+ ds3000_writereg(state, 0xc7, 0x18);
+ } else {
+ value = 129032 / (c->symbol_rate / 1000) + 1;
+ ds3000_writereg(state, 0xc3, value);
+ ds3000_writereg(state, 0xc8, 0x0a);
+ ds3000_writereg(state, 0xc4, 0x05);
+ ds3000_writereg(state, 0xc7, 0x24);
+ }
+
+ /* normalized symbol rate rounded to the closest integer */
+ value = (((c->symbol_rate / 1000) << 16) +
+ (DS3000_SAMPLE_RATE / 2)) / DS3000_SAMPLE_RATE;
+ ds3000_writereg(state, 0x61, value & 0x00ff);
+ ds3000_writereg(state, 0x62, (value & 0xff00) >> 8);
+
+ /* co-channel interference cancellation disabled */
+ ds3000_writereg(state, 0x56, 0x00);
+
+ /* equalizer disabled */
+ ds3000_writereg(state, 0x76, 0x00);
+
+ /*ds3000_writereg(state, 0x08, 0x03);
+ ds3000_writereg(state, 0xfd, 0x22);
+ ds3000_writereg(state, 0x08, 0x07);
+ ds3000_writereg(state, 0xfd, 0x42);
+ ds3000_writereg(state, 0x08, 0x07);*/
+
+ if (state->config->ci_mode) {
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ default:
+ ds3000_writereg(state, 0xfd, 0x80);
+ break;
+ case SYS_DVBS2:
+ ds3000_writereg(state, 0xfd, 0x01);
+ break;
+ }
+ }
+
+ /* ds3000 out of software reset */
+ ds3000_writereg(state, 0x00, 0x00);
+ /* start ds3000 built-in uC */
+ ds3000_writereg(state, 0xb2, 0x00);
+
+ if (fe->ops.tuner_ops.get_frequency) {
+ fe->ops.tuner_ops.get_frequency(fe, &frequency);
+ offset_khz = frequency - c->frequency;
+ ds3000_set_carrier_offset(fe, offset_khz);
+ }
+
+ for (i = 0; i < 30 ; i++) {
+ ds3000_read_status(fe, &status);
+ if (status & FE_HAS_LOCK)
+ break;
+
+ msleep(10);
+ }
+
+ return 0;
+}
+
+static int ds3000_tune(struct dvb_frontend *fe,
+ bool re_tune,
+ unsigned int mode_flags,
+ unsigned int *delay,
+ enum fe_status *status)
+{
+ if (re_tune) {
+ int ret = ds3000_set_frontend(fe);
+ if (ret)
+ return ret;
+ }
+
+ *delay = HZ / 5;
+
+ return ds3000_read_status(fe, status);
+}
+
+static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+
+ if (state->config->set_lock_led)
+ state->config->set_lock_led(fe, 0);
+
+ dprintk("%s()\n", __func__);
+ return DVBFE_ALGO_HW;
+}
+
+/*
+ * Initialise or wake up device
+ *
+ * Power config will reset and load initial firmware if required
+ */
+static int ds3000_initfe(struct dvb_frontend *fe)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ int ret;
+
+ dprintk("%s()\n", __func__);
+ /* hard reset */
+ ds3000_writereg(state, 0x08, 0x01 | ds3000_readreg(state, 0x08));
+ msleep(1);
+
+ /* Load the firmware if required */
+ ret = ds3000_firmware_ondemand(fe);
+ if (ret != 0) {
+ printk(KERN_ERR "%s: Unable initialize firmware\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dvb_frontend_ops ds3000_ops = {
+ .delsys = { SYS_DVBS, SYS_DVBS2 },
+ .info = {
+ .name = "Montage Technology DS3000",
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
+ .frequency_stepsize_hz = 1011 * kHz,
+ .frequency_tolerance_hz = 5 * MHz,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .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_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_2G_MODULATION |
+ FE_CAN_QPSK | FE_CAN_RECOVER
+ },
+
+ .release = ds3000_release,
+
+ .init = ds3000_initfe,
+ .i2c_gate_ctrl = ds3000_i2c_gate_ctrl,
+ .read_status = ds3000_read_status,
+ .read_ber = ds3000_read_ber,
+ .read_signal_strength = ds3000_read_signal_strength,
+ .read_snr = ds3000_read_snr,
+ .read_ucblocks = ds3000_read_ucblocks,
+ .set_voltage = ds3000_set_voltage,
+ .set_tone = ds3000_set_tone,
+ .diseqc_send_master_cmd = ds3000_send_diseqc_msg,
+ .diseqc_send_burst = ds3000_diseqc_send_burst,
+ .get_frontend_algo = ds3000_get_algo,
+
+ .set_frontend = ds3000_set_frontend,
+ .tune = ds3000_tune,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
+
+MODULE_DESCRIPTION("DVB Frontend module for Montage Technology DS3000 hardware");
+MODULE_AUTHOR("Konstantin Dimitrov <kosio.dimitrov@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(DS3000_DEFAULT_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/ds3000.h b/drivers/media/dvb-frontends/ds3000.h
new file mode 100644
index 000000000000..cd24fa4d25d2
--- /dev/null
+++ b/drivers/media/dvb-frontends/ds3000.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Montage Technology DS3000 - DVBS/S2 Demodulator driver
+ Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com>
+
+ Copyright (C) 2009-2012 TurboSight.com
+
+ */
+
+#ifndef DS3000_H
+#define DS3000_H
+
+#include <linux/dvb/frontend.h>
+
+struct ds3000_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+ u8 ci_mode;
+ /* Set device param to start dma */
+ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
+ /* Hook for Lock LED */
+ void (*set_lock_led)(struct dvb_frontend *fe, int offon);
+};
+
+#if IS_REACHABLE(CONFIG_DVB_DS3000)
+extern struct dvb_frontend *ds3000_attach(const struct ds3000_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline
+struct dvb_frontend *ds3000_attach(const struct ds3000_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_DS3000 */
+#endif /* DS3000_H */
diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c
new file mode 100644
index 000000000000..1775a4aa0a18
--- /dev/null
+++ b/drivers/media/dvb-frontends/dvb-pll.c
@@ -0,0 +1,954 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * descriptions + helper functions for simple dvb plls.
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/idr.h>
+#include <linux/dvb/frontend.h>
+#include <asm/types.h>
+
+#include "dvb-pll.h"
+
+#define dprintk(fmt, arg...) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), __func__, ##arg)
+
+struct dvb_pll_priv {
+ /* pll number */
+ int nr;
+
+ /* i2c details */
+ int pll_i2c_address;
+ struct i2c_adapter *i2c;
+
+ /* the PLL descriptor */
+ const struct dvb_pll_desc *pll_desc;
+
+ /* cached frequency/bandwidth */
+ u32 frequency;
+ u32 bandwidth;
+};
+
+#define DVB_PLL_MAX 64
+static DEFINE_IDA(pll_ida);
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable verbose debug messages");
+
+static unsigned int id[DVB_PLL_MAX] =
+ { [ 0 ... (DVB_PLL_MAX-1) ] = DVB_PLL_UNDEFINED };
+module_param_array(id, int, NULL, 0644);
+MODULE_PARM_DESC(id, "force pll id to use (DEBUG ONLY)");
+
+/* ----------------------------------------------------------- */
+
+struct dvb_pll_desc {
+ const char *name;
+ u32 min;
+ u32 max;
+ u32 iffreq;
+ void (*set)(struct dvb_frontend *fe, u8 *buf);
+ u8 *initdata;
+ u8 *initdata2;
+ u8 *sleepdata;
+ int count;
+ struct {
+ u32 limit;
+ u32 stepsize;
+ u8 config;
+ u8 cb;
+ } entries[];
+};
+
+/* ----------------------------------------------------------- */
+/* descriptions */
+
+static const struct dvb_pll_desc dvb_pll_thomson_dtt7579 = {
+ .name = "Thomson dtt7579",
+ .min = 177 * MHz,
+ .max = 858 * MHz,
+ .iffreq= 36166667,
+ .sleepdata = (u8[]){ 2, 0xb4, 0x03 },
+ .count = 4,
+ .entries = {
+ { 443250000, 166667, 0xb4, 0x02 },
+ { 542000000, 166667, 0xb4, 0x08 },
+ { 771000000, 166667, 0xbc, 0x08 },
+ { 999999999, 166667, 0xf4, 0x08 },
+ },
+};
+
+static void thomson_dtt759x_bw(struct dvb_frontend *fe, u8 *buf)
+{
+ u32 bw = fe->dtv_property_cache.bandwidth_hz;
+ if (bw == 7000000)
+ buf[3] |= 0x10;
+}
+
+static const struct dvb_pll_desc dvb_pll_thomson_dtt759x = {
+ .name = "Thomson dtt759x",
+ .min = 177 * MHz,
+ .max = 896 * MHz,
+ .set = thomson_dtt759x_bw,
+ .iffreq= 36166667,
+ .sleepdata = (u8[]){ 2, 0x84, 0x03 },
+ .count = 5,
+ .entries = {
+ { 264000000, 166667, 0xb4, 0x02 },
+ { 470000000, 166667, 0xbc, 0x02 },
+ { 735000000, 166667, 0xbc, 0x08 },
+ { 835000000, 166667, 0xf4, 0x08 },
+ { 999999999, 166667, 0xfc, 0x08 },
+ },
+};
+
+static void thomson_dtt7520x_bw(struct dvb_frontend *fe, u8 *buf)
+{
+ u32 bw = fe->dtv_property_cache.bandwidth_hz;
+ if (bw == 8000000)
+ buf[3] ^= 0x10;
+}
+
+static const struct dvb_pll_desc dvb_pll_thomson_dtt7520x = {
+ .name = "Thomson dtt7520x",
+ .min = 185 * MHz,
+ .max = 900 * MHz,
+ .set = thomson_dtt7520x_bw,
+ .iffreq = 36166667,
+ .count = 7,
+ .entries = {
+ { 305000000, 166667, 0xb4, 0x12 },
+ { 405000000, 166667, 0xbc, 0x12 },
+ { 445000000, 166667, 0xbc, 0x12 },
+ { 465000000, 166667, 0xf4, 0x18 },
+ { 735000000, 166667, 0xfc, 0x18 },
+ { 835000000, 166667, 0xbc, 0x18 },
+ { 999999999, 166667, 0xfc, 0x18 },
+ },
+};
+
+static const struct dvb_pll_desc dvb_pll_lg_z201 = {
+ .name = "LG z201",
+ .min = 174 * MHz,
+ .max = 862 * MHz,
+ .iffreq= 36166667,
+ .sleepdata = (u8[]){ 2, 0xbc, 0x03 },
+ .count = 5,
+ .entries = {
+ { 157500000, 166667, 0xbc, 0x01 },
+ { 443250000, 166667, 0xbc, 0x02 },
+ { 542000000, 166667, 0xbc, 0x04 },
+ { 830000000, 166667, 0xf4, 0x04 },
+ { 999999999, 166667, 0xfc, 0x04 },
+ },
+};
+
+static const struct dvb_pll_desc dvb_pll_unknown_1 = {
+ .name = "unknown 1", /* used by dntv live dvb-t */
+ .min = 174 * MHz,
+ .max = 862 * MHz,
+ .iffreq= 36166667,
+ .count = 9,
+ .entries = {
+ { 150000000, 166667, 0xb4, 0x01 },
+ { 173000000, 166667, 0xbc, 0x01 },
+ { 250000000, 166667, 0xb4, 0x02 },
+ { 400000000, 166667, 0xbc, 0x02 },
+ { 420000000, 166667, 0xf4, 0x02 },
+ { 470000000, 166667, 0xfc, 0x02 },
+ { 600000000, 166667, 0xbc, 0x08 },
+ { 730000000, 166667, 0xf4, 0x08 },
+ { 999999999, 166667, 0xfc, 0x08 },
+ },
+};
+
+/* Infineon TUA6010XS
+ * used in Thomson Cable Tuner
+ */
+static const struct dvb_pll_desc dvb_pll_tua6010xs = {
+ .name = "Infineon TUA6010XS",
+ .min = 44250 * kHz,
+ .max = 858 * MHz,
+ .iffreq= 36125000,
+ .count = 3,
+ .entries = {
+ { 115750000, 62500, 0x8e, 0x03 },
+ { 403250000, 62500, 0x8e, 0x06 },
+ { 999999999, 62500, 0x8e, 0x85 },
+ },
+};
+
+/* Panasonic env57h1xd5 (some Philips PLL ?) */
+static const struct dvb_pll_desc dvb_pll_env57h1xd5 = {
+ .name = "Panasonic ENV57H1XD5",
+ .min = 44250 * kHz,
+ .max = 858 * MHz,
+ .iffreq= 36125000,
+ .count = 4,
+ .entries = {
+ { 153000000, 166667, 0xc2, 0x41 },
+ { 470000000, 166667, 0xc2, 0x42 },
+ { 526000000, 166667, 0xc2, 0x84 },
+ { 999999999, 166667, 0xc2, 0xa4 },
+ },
+};
+
+/* Philips TDA6650/TDA6651
+ * used in Panasonic ENV77H11D5
+ */
+static void tda665x_bw(struct dvb_frontend *fe, u8 *buf)
+{
+ u32 bw = fe->dtv_property_cache.bandwidth_hz;
+ if (bw == 8000000)
+ buf[3] |= 0x08;
+}
+
+static const struct dvb_pll_desc dvb_pll_tda665x = {
+ .name = "Philips TDA6650/TDA6651",
+ .min = 44250 * kHz,
+ .max = 858 * MHz,
+ .set = tda665x_bw,
+ .iffreq= 36166667,
+ .initdata = (u8[]){ 4, 0x0b, 0xf5, 0x85, 0xab },
+ .count = 12,
+ .entries = {
+ { 93834000, 166667, 0xca, 0x61 /* 011 0 0 0 01 */ },
+ { 123834000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ },
+ { 161000000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ },
+ { 163834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ },
+ { 253834000, 166667, 0xca, 0x62 /* 011 0 0 0 10 */ },
+ { 383834000, 166667, 0xca, 0xa2 /* 101 0 0 0 10 */ },
+ { 443834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ },
+ { 444000000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ },
+ { 583834000, 166667, 0xca, 0x64 /* 011 0 0 1 00 */ },
+ { 793834000, 166667, 0xca, 0xa4 /* 101 0 0 1 00 */ },
+ { 444834000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ },
+ { 861000000, 166667, 0xca, 0xe4 /* 111 0 0 1 00 */ },
+ }
+};
+
+/* Infineon TUA6034
+ * used in LG TDTP E102P
+ */
+static void tua6034_bw(struct dvb_frontend *fe, u8 *buf)
+{
+ u32 bw = fe->dtv_property_cache.bandwidth_hz;
+ if (bw == 7000000)
+ buf[3] |= 0x08;
+}
+
+static const struct dvb_pll_desc dvb_pll_tua6034 = {
+ .name = "Infineon TUA6034",
+ .min = 44250 * kHz,
+ .max = 858 * MHz,
+ .iffreq= 36166667,
+ .count = 3,
+ .set = tua6034_bw,
+ .entries = {
+ { 174500000, 62500, 0xce, 0x01 },
+ { 230000000, 62500, 0xce, 0x02 },
+ { 999999999, 62500, 0xce, 0x04 },
+ },
+};
+
+/* ALPS TDED4
+ * used in Nebula-Cards and USB boxes
+ */
+static void tded4_bw(struct dvb_frontend *fe, u8 *buf)
+{
+ u32 bw = fe->dtv_property_cache.bandwidth_hz;
+ if (bw == 8000000)
+ buf[3] |= 0x04;
+}
+
+static const struct dvb_pll_desc dvb_pll_tded4 = {
+ .name = "ALPS TDED4",
+ .min = 47 * MHz,
+ .max = 863 * MHz,
+ .iffreq= 36166667,
+ .set = tded4_bw,
+ .count = 4,
+ .entries = {
+ { 153000000, 166667, 0x85, 0x01 },
+ { 470000000, 166667, 0x85, 0x02 },
+ { 823000000, 166667, 0x85, 0x08 },
+ { 999999999, 166667, 0x85, 0x88 },
+ }
+};
+
+/* ALPS TDHU2
+ * used in AverTVHD MCE A180
+ */
+static const struct dvb_pll_desc dvb_pll_tdhu2 = {
+ .name = "ALPS TDHU2",
+ .min = 54 * MHz,
+ .max = 864 * MHz,
+ .iffreq= 44000000,
+ .count = 4,
+ .entries = {
+ { 162000000, 62500, 0x85, 0x01 },
+ { 426000000, 62500, 0x85, 0x02 },
+ { 782000000, 62500, 0x85, 0x08 },
+ { 999999999, 62500, 0x85, 0x88 },
+ }
+};
+
+/* Samsung TBMV30111IN / TBMV30712IN1
+ * used in Air2PC ATSC - 2nd generation (nxt2002)
+ */
+static const struct dvb_pll_desc dvb_pll_samsung_tbmv = {
+ .name = "Samsung TBMV30111IN / TBMV30712IN1",
+ .min = 54 * MHz,
+ .max = 860 * MHz,
+ .iffreq= 44000000,
+ .count = 6,
+ .entries = {
+ { 172000000, 166667, 0xb4, 0x01 },
+ { 214000000, 166667, 0xb4, 0x02 },
+ { 467000000, 166667, 0xbc, 0x02 },
+ { 721000000, 166667, 0xbc, 0x08 },
+ { 841000000, 166667, 0xf4, 0x08 },
+ { 999999999, 166667, 0xfc, 0x02 },
+ }
+};
+
+/*
+ * Philips SD1878 Tuner.
+ */
+static const struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = {
+ .name = "Philips SD1878",
+ .min = 950 * MHz,
+ .max = 2150 * MHz,
+ .iffreq= 249, /* zero-IF, offset 249 is to round up */
+ .count = 4,
+ .entries = {
+ { 1250000, 500, 0xc4, 0x00},
+ { 1450000, 500, 0xc4, 0x40},
+ { 2050000, 500, 0xc4, 0x80},
+ { 2150000, 500, 0xc4, 0xc0},
+ },
+};
+
+static void opera1_bw(struct dvb_frontend *fe, u8 *buf)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ u32 b_w = (c->symbol_rate * 27) / 32000;
+ struct i2c_msg msg = {
+ .addr = priv->pll_i2c_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 4
+ };
+ int result;
+ u8 lpf;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ result = i2c_transfer(priv->i2c, &msg, 1);
+ if (result != 1)
+ pr_err("%s: i2c_transfer failed:%d",
+ __func__, result);
+
+ if (b_w <= 10000)
+ lpf = 0xc;
+ else if (b_w <= 12000)
+ lpf = 0x2;
+ else if (b_w <= 14000)
+ lpf = 0xa;
+ else if (b_w <= 16000)
+ lpf = 0x6;
+ else if (b_w <= 18000)
+ lpf = 0xe;
+ else if (b_w <= 20000)
+ lpf = 0x1;
+ else if (b_w <= 22000)
+ lpf = 0x9;
+ else if (b_w <= 24000)
+ lpf = 0x5;
+ else if (b_w <= 26000)
+ lpf = 0xd;
+ else if (b_w <= 28000)
+ lpf = 0x3;
+ else
+ lpf = 0xb;
+ buf[2] ^= 0x1c; /* Flip bits 3-5 */
+ /* Set lpf */
+ buf[2] |= ((lpf >> 2) & 0x3) << 3;
+ buf[3] |= (lpf & 0x3) << 2;
+
+ return;
+}
+
+static const struct dvb_pll_desc dvb_pll_opera1 = {
+ .name = "Opera Tuner",
+ .min = 900 * MHz,
+ .max = 2250 * MHz,
+ .initdata = (u8[]){ 4, 0x08, 0xe5, 0xe1, 0x00 },
+ .initdata2 = (u8[]){ 4, 0x08, 0xe5, 0xe5, 0x00 },
+ .iffreq= 0,
+ .set = opera1_bw,
+ .count = 8,
+ .entries = {
+ { 1064000, 500, 0xf9, 0xc2 },
+ { 1169000, 500, 0xf9, 0xe2 },
+ { 1299000, 500, 0xf9, 0x20 },
+ { 1444000, 500, 0xf9, 0x40 },
+ { 1606000, 500, 0xf9, 0x60 },
+ { 1777000, 500, 0xf9, 0x80 },
+ { 1941000, 500, 0xf9, 0xa0 },
+ { 2250000, 500, 0xf9, 0xc0 },
+ }
+};
+
+static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf)
+{
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ struct i2c_msg msg = {
+ .addr = priv->pll_i2c_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 4
+ };
+ int result;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ result = i2c_transfer(priv->i2c, &msg, 1);
+ if (result != 1)
+ pr_err("%s: i2c_transfer failed:%d",
+ __func__, result);
+
+ buf[2] = 0x9e;
+ buf[3] = 0x90;
+
+ return;
+}
+
+/* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */
+static const struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
+ .name = "Samsung DTOS403IH102A",
+ .min = 44250 * kHz,
+ .max = 858 * MHz,
+ .iffreq = 36125000,
+ .count = 8,
+ .set = samsung_dtos403ih102a_set,
+ .entries = {
+ { 135000000, 62500, 0xbe, 0x01 },
+ { 177000000, 62500, 0xf6, 0x01 },
+ { 370000000, 62500, 0xbe, 0x02 },
+ { 450000000, 62500, 0xf6, 0x02 },
+ { 466000000, 62500, 0xfe, 0x02 },
+ { 538000000, 62500, 0xbe, 0x08 },
+ { 826000000, 62500, 0xf6, 0x08 },
+ { 999999999, 62500, 0xfe, 0x08 },
+ }
+};
+
+/* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */
+static const struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = {
+ .name = "Samsung TDTC9251DH0",
+ .min = 48 * MHz,
+ .max = 863 * MHz,
+ .iffreq = 36166667,
+ .count = 3,
+ .entries = {
+ { 157500000, 166667, 0xcc, 0x09 },
+ { 443000000, 166667, 0xcc, 0x0a },
+ { 863000000, 166667, 0xcc, 0x08 },
+ }
+};
+
+/* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */
+static const struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = {
+ .name = "Samsung TBDU18132",
+ .min = 950 * MHz,
+ .max = 2150 * MHz, /* guesses */
+ .iffreq = 0,
+ .count = 2,
+ .entries = {
+ { 1550000, 125, 0x84, 0x82 },
+ { 4095937, 125, 0x84, 0x80 },
+ }
+ /* TSA5059 PLL has a 17 bit divisor rather than the 15 bits supported
+ * by this driver. The two extra bits are 0x60 in the third byte. 15
+ * bits is enough for over 4 GHz, which is enough to cover the range
+ * of this tuner. We could use the additional divisor bits by adding
+ * more entries, e.g.
+ { 0x0ffff * 125 + 125/2, 125, 0x84 | 0x20, },
+ { 0x17fff * 125 + 125/2, 125, 0x84 | 0x40, },
+ { 0x1ffff * 125 + 125/2, 125, 0x84 | 0x60, }, */
+};
+
+/* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */
+static const struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = {
+ .name = "Samsung TBMU24112",
+ .min = 950 * MHz,
+ .max = 2150 * MHz, /* guesses */
+ .iffreq = 0,
+ .count = 2,
+ .entries = {
+ { 1500000, 125, 0x84, 0x18 },
+ { 9999999, 125, 0x84, 0x08 },
+ }
+};
+
+/* Alps TDEE4 DVB-C NIM, used on Cablestar 2 */
+/* byte 4 : 1 * * AGD R3 R2 R1 R0
+ * byte 5 : C1 * RE RTS BS4 BS3 BS2 BS1
+ * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95
+ * Range(MHz) C1 * RE RTS BS4 BS3 BS2 BS1 Byte 5
+ * 47 - 153 0 * 0 0 0 0 0 1 0x01
+ * 153 - 430 0 * 0 0 0 0 1 0 0x02
+ * 430 - 822 0 * 0 0 1 0 0 0 0x08
+ * 822 - 862 1 * 0 0 1 0 0 0 0x88 */
+static const struct dvb_pll_desc dvb_pll_alps_tdee4 = {
+ .name = "ALPS TDEE4",
+ .min = 47 * MHz,
+ .max = 862 * MHz,
+ .iffreq = 36125000,
+ .count = 4,
+ .entries = {
+ { 153000000, 62500, 0x95, 0x01 },
+ { 430000000, 62500, 0x95, 0x02 },
+ { 822000000, 62500, 0x95, 0x08 },
+ { 999999999, 62500, 0x95, 0x88 },
+ }
+};
+
+/* Infineon TUA6034 ISDB-T, used in Friio */
+/* CP cur. 50uA, AGC takeover: 103dBuV, PORT3 on */
+static const struct dvb_pll_desc dvb_pll_tua6034_friio = {
+ .name = "Infineon TUA6034 ISDB-T (Friio)",
+ .min = 90 * MHz,
+ .max = 770 * MHz,
+ .iffreq = 57000000,
+ .initdata = (u8[]){ 4, 0x9a, 0x50, 0xb2, 0x08 },
+ .sleepdata = (u8[]){ 4, 0x9a, 0x70, 0xb3, 0x0b },
+ .count = 3,
+ .entries = {
+ { 170000000, 142857, 0xba, 0x09 },
+ { 470000000, 142857, 0xba, 0x0a },
+ { 770000000, 142857, 0xb2, 0x08 },
+ }
+};
+
+/* Philips TDA6651 ISDB-T, used in Earthsoft PT1 */
+static const struct dvb_pll_desc dvb_pll_tda665x_earth_pt1 = {
+ .name = "Philips TDA6651 ISDB-T (EarthSoft PT1)",
+ .min = 90 * MHz,
+ .max = 770 * MHz,
+ .iffreq = 57000000,
+ .initdata = (u8[]){ 5, 0x0e, 0x7f, 0xc1, 0x80, 0x80 },
+ .count = 10,
+ .entries = {
+ { 140000000, 142857, 0xc1, 0x81 },
+ { 170000000, 142857, 0xc1, 0xa1 },
+ { 220000000, 142857, 0xc1, 0x62 },
+ { 330000000, 142857, 0xc1, 0xa2 },
+ { 402000000, 142857, 0xc1, 0xe2 },
+ { 450000000, 142857, 0xc1, 0x64 },
+ { 550000000, 142857, 0xc1, 0x84 },
+ { 600000000, 142857, 0xc1, 0xa4 },
+ { 700000000, 142857, 0xc1, 0xc4 },
+ { 770000000, 142857, 0xc1, 0xe4 },
+ }
+};
+
+/* ----------------------------------------------------------- */
+
+static const struct dvb_pll_desc *pll_list[] = {
+ [DVB_PLL_UNDEFINED] = NULL,
+ [DVB_PLL_THOMSON_DTT7579] = &dvb_pll_thomson_dtt7579,
+ [DVB_PLL_THOMSON_DTT759X] = &dvb_pll_thomson_dtt759x,
+ [DVB_PLL_THOMSON_DTT7520X] = &dvb_pll_thomson_dtt7520x,
+ [DVB_PLL_LG_Z201] = &dvb_pll_lg_z201,
+ [DVB_PLL_UNKNOWN_1] = &dvb_pll_unknown_1,
+ [DVB_PLL_TUA6010XS] = &dvb_pll_tua6010xs,
+ [DVB_PLL_ENV57H1XD5] = &dvb_pll_env57h1xd5,
+ [DVB_PLL_TUA6034] = &dvb_pll_tua6034,
+ [DVB_PLL_TDA665X] = &dvb_pll_tda665x,
+ [DVB_PLL_TDED4] = &dvb_pll_tded4,
+ [DVB_PLL_TDEE4] = &dvb_pll_alps_tdee4,
+ [DVB_PLL_TDHU2] = &dvb_pll_tdhu2,
+ [DVB_PLL_SAMSUNG_TBMV] = &dvb_pll_samsung_tbmv,
+ [DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261,
+ [DVB_PLL_OPERA1] = &dvb_pll_opera1,
+ [DVB_PLL_SAMSUNG_DTOS403IH102A] = &dvb_pll_samsung_dtos403ih102a,
+ [DVB_PLL_SAMSUNG_TDTC9251DH0] = &dvb_pll_samsung_tdtc9251dh0,
+ [DVB_PLL_SAMSUNG_TBDU18132] = &dvb_pll_samsung_tbdu18132,
+ [DVB_PLL_SAMSUNG_TBMU24112] = &dvb_pll_samsung_tbmu24112,
+ [DVB_PLL_TUA6034_FRIIO] = &dvb_pll_tua6034_friio,
+ [DVB_PLL_TDA665X_EARTH_PT1] = &dvb_pll_tda665x_earth_pt1,
+};
+
+/* ----------------------------------------------------------- */
+/* code */
+
+static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf,
+ const u32 frequency)
+{
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ const struct dvb_pll_desc *desc = priv->pll_desc;
+ u32 div;
+ int i;
+
+ for (i = 0; i < desc->count; i++) {
+ if (frequency > desc->entries[i].limit)
+ continue;
+ break;
+ }
+
+ if (debug)
+ dprintk("pll: %s: freq=%d | i=%d/%d\n", desc->name,
+ frequency, i, desc->count);
+ if (i == desc->count)
+ return -EINVAL;
+
+ div = (frequency + desc->iffreq +
+ desc->entries[i].stepsize/2) / desc->entries[i].stepsize;
+ buf[0] = div >> 8;
+ buf[1] = div & 0xff;
+ buf[2] = desc->entries[i].config;
+ buf[3] = desc->entries[i].cb;
+
+ if (desc->set)
+ desc->set(fe, buf);
+
+ if (debug)
+ dprintk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
+ desc->name, div, buf[0], buf[1], buf[2], buf[3]);
+
+ // calculate the frequency we set it to
+ return (div * desc->entries[i].stepsize) - desc->iffreq;
+}
+
+static void dvb_pll_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+}
+
+static int dvb_pll_sleep(struct dvb_frontend *fe)
+{
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+
+ if (priv->i2c == NULL)
+ return -EINVAL;
+
+ if (priv->pll_desc->sleepdata) {
+ struct i2c_msg msg = { .flags = 0,
+ .addr = priv->pll_i2c_address,
+ .buf = priv->pll_desc->sleepdata + 1,
+ .len = priv->pll_desc->sleepdata[0] };
+
+ int result;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+ return result;
+ }
+ return 0;
+ }
+ /* Shouldn't be called when initdata is NULL, maybe BUG()? */
+ return -EINVAL;
+}
+
+static int dvb_pll_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ u8 buf[4];
+ struct i2c_msg msg =
+ { .addr = priv->pll_i2c_address, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
+ int result;
+ u32 frequency = 0;
+
+ if (priv->i2c == NULL)
+ return -EINVAL;
+
+ result = dvb_pll_configure(fe, buf, c->frequency);
+ if (result < 0)
+ return result;
+ else
+ frequency = result;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+ return result;
+ }
+
+ priv->frequency = frequency;
+ priv->bandwidth = c->bandwidth_hz;
+
+ return 0;
+}
+
+static int dvb_pll_calc_regs(struct dvb_frontend *fe,
+ u8 *buf, int buf_len)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ int result;
+ u32 frequency = 0;
+
+ if (buf_len < 5)
+ return -EINVAL;
+
+ result = dvb_pll_configure(fe, buf + 1, c->frequency);
+ if (result < 0)
+ return result;
+ else
+ frequency = result;
+
+ buf[0] = priv->pll_i2c_address;
+
+ priv->frequency = frequency;
+ priv->bandwidth = c->bandwidth_hz;
+
+ return 5;
+}
+
+static int dvb_pll_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int dvb_pll_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ *bandwidth = priv->bandwidth;
+ return 0;
+}
+
+static int dvb_pll_init(struct dvb_frontend *fe)
+{
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+
+ if (priv->i2c == NULL)
+ return -EINVAL;
+
+ if (priv->pll_desc->initdata) {
+ struct i2c_msg msg = { .flags = 0,
+ .addr = priv->pll_i2c_address,
+ .buf = priv->pll_desc->initdata + 1,
+ .len = priv->pll_desc->initdata[0] };
+
+ int result;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ result = i2c_transfer(priv->i2c, &msg, 1);
+ if (result != 1)
+ return result;
+ if (priv->pll_desc->initdata2) {
+ msg.buf = priv->pll_desc->initdata2 + 1;
+ msg.len = priv->pll_desc->initdata2[0];
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ result = i2c_transfer(priv->i2c, &msg, 1);
+ if (result != 1)
+ return result;
+ }
+ return 0;
+ }
+ /* Shouldn't be called when initdata is NULL, maybe BUG()? */
+ return -EINVAL;
+}
+
+static const struct dvb_tuner_ops dvb_pll_tuner_ops = {
+ .release = dvb_pll_release,
+ .sleep = dvb_pll_sleep,
+ .init = dvb_pll_init,
+ .set_params = dvb_pll_set_params,
+ .calc_regs = dvb_pll_calc_regs,
+ .get_frequency = dvb_pll_get_frequency,
+ .get_bandwidth = dvb_pll_get_bandwidth,
+};
+
+struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr,
+ struct i2c_adapter *i2c,
+ unsigned int pll_desc_id)
+{
+ u8 *b1;
+ struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD, .len = 1 };
+ struct dvb_pll_priv *priv = NULL;
+ int ret;
+ const struct dvb_pll_desc *desc;
+ int nr;
+
+ b1 = kmalloc(1, GFP_KERNEL);
+ if (!b1)
+ return NULL;
+
+ b1[0] = 0;
+ msg.buf = b1;
+
+ nr = ida_alloc_max(&pll_ida, DVB_PLL_MAX - 1, GFP_KERNEL);
+ if (nr < 0) {
+ kfree(b1);
+ return NULL;
+ }
+
+ if (id[nr] > DVB_PLL_UNDEFINED && id[nr] < ARRAY_SIZE(pll_list))
+ pll_desc_id = id[nr];
+
+ BUG_ON(pll_desc_id < 1 || pll_desc_id >= ARRAY_SIZE(pll_list));
+
+ desc = pll_list[pll_desc_id];
+
+ if (i2c != NULL) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ ret = i2c_transfer (i2c, &msg, 1);
+ if (ret != 1)
+ goto out;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ priv = kzalloc(sizeof(struct dvb_pll_priv), GFP_KERNEL);
+ if (!priv)
+ goto out;
+
+ priv->pll_i2c_address = pll_addr;
+ priv->i2c = i2c;
+ priv->pll_desc = desc;
+ priv->nr = nr;
+
+ memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ strscpy(fe->ops.tuner_ops.info.name, desc->name,
+ sizeof(fe->ops.tuner_ops.info.name));
+
+ fe->ops.tuner_ops.info.frequency_min_hz = desc->min;
+ fe->ops.tuner_ops.info.frequency_max_hz = desc->max;
+
+ dprintk("%s tuner, frequency range: %u...%u\n",
+ desc->name, desc->min, desc->max);
+
+ if (!desc->initdata)
+ fe->ops.tuner_ops.init = NULL;
+ if (!desc->sleepdata)
+ fe->ops.tuner_ops.sleep = NULL;
+
+ fe->tuner_priv = priv;
+
+ if ((debug) || (id[priv->nr] == pll_desc_id)) {
+ dprintk("dvb-pll[%d]", priv->nr);
+ if (i2c != NULL)
+ pr_cont(" %d-%04x", i2c_adapter_id(i2c), pll_addr);
+ pr_cont(": id# %d (%s) attached, %s\n", pll_desc_id, desc->name,
+ id[priv->nr] == pll_desc_id ?
+ "insmod option" : "autodetected");
+ }
+
+ kfree(b1);
+
+ return fe;
+out:
+ kfree(b1);
+ ida_free(&pll_ida, nr);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(dvb_pll_attach);
+
+
+static int
+dvb_pll_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct dvb_pll_config *cfg;
+ struct dvb_frontend *fe;
+ unsigned int desc_id;
+
+ cfg = client->dev.platform_data;
+ fe = cfg->fe;
+ i2c_set_clientdata(client, fe);
+ desc_id = (unsigned int) id->driver_data;
+
+ if (!dvb_pll_attach(fe, client->addr, client->adapter, desc_id))
+ return -ENOMEM;
+
+ /*
+ * Unset tuner_ops.release (== dvb_pll_release)
+ * which has been just set in the above dvb_pll_attach(),
+ * because if tuner_ops.release was left defined,
+ * this module would be 'put' twice on exit:
+ * once by dvb_frontend_detach() and another by dvb_module_release().
+ *
+ * dvb_pll_release is instead executed in the i2c driver's .remove(),
+ * keeping dvb_pll_attach untouched for legacy (dvb_attach) drivers.
+ */
+ fe->ops.tuner_ops.release = NULL;
+ dev_info(&client->dev, "DVB Simple Tuner attached.\n");
+ return 0;
+}
+
+static void dvb_pll_remove(struct i2c_client *client)
+{
+ struct dvb_frontend *fe = i2c_get_clientdata(client);
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+
+ ida_free(&pll_ida, priv->nr);
+ dvb_pll_release(fe);
+}
+
+
+static const struct i2c_device_id dvb_pll_id[] = {
+ {"dtt7579", DVB_PLL_THOMSON_DTT7579},
+ {"dtt759x", DVB_PLL_THOMSON_DTT759X},
+ {"z201", DVB_PLL_LG_Z201},
+ {"unknown_1", DVB_PLL_UNKNOWN_1},
+ {"tua6010xs", DVB_PLL_TUA6010XS},
+ {"env57h1xd5", DVB_PLL_ENV57H1XD5},
+ {"tua6034", DVB_PLL_TUA6034},
+ {"tda665x", DVB_PLL_TDA665X},
+ {"tded4", DVB_PLL_TDED4},
+ {"tdhu2", DVB_PLL_TDHU2},
+ {"tbmv", DVB_PLL_SAMSUNG_TBMV},
+ {"sd1878_tda8261", DVB_PLL_PHILIPS_SD1878_TDA8261},
+ {"opera1", DVB_PLL_OPERA1},
+ {"dtos403ih102a", DVB_PLL_SAMSUNG_DTOS403IH102A},
+ {"tdtc9251dh0", DVB_PLL_SAMSUNG_TDTC9251DH0},
+ {"tbdu18132", DVB_PLL_SAMSUNG_TBDU18132},
+ {"tbmu24112", DVB_PLL_SAMSUNG_TBMU24112},
+ {"tdee4", DVB_PLL_TDEE4},
+ {"dtt7520x", DVB_PLL_THOMSON_DTT7520X},
+ {"tua6034_friio", DVB_PLL_TUA6034_FRIIO},
+ {"tda665x_earthpt1", DVB_PLL_TDA665X_EARTH_PT1},
+ {}
+};
+
+
+MODULE_DEVICE_TABLE(i2c, dvb_pll_id);
+
+static struct i2c_driver dvb_pll_driver = {
+ .driver = {
+ .name = "dvb_pll",
+ },
+ .probe = dvb_pll_probe,
+ .remove = dvb_pll_remove,
+ .id_table = dvb_pll_id,
+};
+
+module_i2c_driver(dvb_pll_driver);
+
+MODULE_DESCRIPTION("dvb pll library");
+MODULE_AUTHOR("Gerd Knorr");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/dvb-pll.h b/drivers/media/dvb-frontends/dvb-pll.h
new file mode 100644
index 000000000000..71838888743b
--- /dev/null
+++ b/drivers/media/dvb-frontends/dvb-pll.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * descriptions + helper functions for simple dvb plls.
+ */
+
+#ifndef __DVB_PLL_H__
+#define __DVB_PLL_H__
+
+#include <linux/i2c.h>
+#include <media/dvb_frontend.h>
+
+#define DVB_PLL_UNDEFINED 0
+#define DVB_PLL_THOMSON_DTT7579 1
+#define DVB_PLL_THOMSON_DTT759X 2
+#define DVB_PLL_LG_Z201 3
+#define DVB_PLL_UNKNOWN_1 4
+#define DVB_PLL_TUA6010XS 5
+#define DVB_PLL_ENV57H1XD5 6
+#define DVB_PLL_TUA6034 7
+#define DVB_PLL_TDA665X 8
+#define DVB_PLL_TDED4 9
+#define DVB_PLL_TDHU2 10
+#define DVB_PLL_SAMSUNG_TBMV 11
+#define DVB_PLL_PHILIPS_SD1878_TDA8261 12
+#define DVB_PLL_OPERA1 13
+#define DVB_PLL_SAMSUNG_DTOS403IH102A 14
+#define DVB_PLL_SAMSUNG_TDTC9251DH0 15
+#define DVB_PLL_SAMSUNG_TBDU18132 16
+#define DVB_PLL_SAMSUNG_TBMU24112 17
+#define DVB_PLL_TDEE4 18
+#define DVB_PLL_THOMSON_DTT7520X 19
+#define DVB_PLL_TUA6034_FRIIO 20
+#define DVB_PLL_TDA665X_EARTH_PT1 21
+
+struct dvb_pll_config {
+ struct dvb_frontend *fe;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_PLL)
+/**
+ * dvb_pll_attach - Attach a dvb-pll to the supplied frontend structure.
+ *
+ * @fe: Frontend to attach to.
+ * @pll_addr: i2c address of the PLL (if used).
+ * @i2c: i2c adapter to use (set to NULL if not used).
+ * @pll_desc_id: dvb_pll_desc to use.
+ *
+ * return: Frontend pointer on success, NULL on failure
+ */
+extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe,
+ int pll_addr,
+ struct i2c_adapter *i2c,
+ unsigned int pll_desc_id);
+#else
+static inline struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe,
+ int pll_addr,
+ struct i2c_adapter *i2c,
+ unsigned int pll_desc_id)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb-frontends/dvb_dummy_fe.c b/drivers/media/dvb-frontends/dvb_dummy_fe.c
new file mode 100644
index 000000000000..9ff1ebaa5e04
--- /dev/null
+++ b/drivers/media/dvb-frontends/dvb_dummy_fe.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Dummy Frontend
+ *
+ * Written by Emard <emard@softhome.net>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include <media/dvb_frontend.h>
+#include "dvb_dummy_fe.h"
+
+
+struct dvb_dummy_fe_state {
+ struct dvb_frontend frontend;
+};
+
+
+static int dvb_dummy_fe_read_status(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ *status = FE_HAS_SIGNAL
+ | FE_HAS_CARRIER
+ | FE_HAS_VITERBI
+ | FE_HAS_SYNC
+ | FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int dvb_dummy_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ *ber = 0;
+ return 0;
+}
+
+static int dvb_dummy_fe_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ *strength = 0;
+ return 0;
+}
+
+static int dvb_dummy_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ *snr = 0;
+ return 0;
+}
+
+static int dvb_dummy_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+ return 0;
+}
+
+/*
+ * Should only be implemented if it actually reads something from the hardware.
+ * Also, it should check for the locks, in order to avoid report wrong data
+ * to userspace.
+ */
+static int dvb_dummy_fe_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
+{
+ return 0;
+}
+
+static int dvb_dummy_fe_set_frontend(struct dvb_frontend *fe)
+{
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ return 0;
+}
+
+static int dvb_dummy_fe_sleep(struct dvb_frontend *fe)
+{
+ return 0;
+}
+
+static int dvb_dummy_fe_init(struct dvb_frontend *fe)
+{
+ return 0;
+}
+
+static int dvb_dummy_fe_set_tone(struct dvb_frontend *fe,
+ enum fe_sec_tone_mode tone)
+{
+ return 0;
+}
+
+static int dvb_dummy_fe_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage)
+{
+ return 0;
+}
+
+static void dvb_dummy_fe_release(struct dvb_frontend *fe)
+{
+ struct dvb_dummy_fe_state *state = fe->demodulator_priv;
+
+ kfree(state);
+}
+
+static const struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops;
+
+struct dvb_frontend *dvb_dummy_fe_ofdm_attach(void)
+{
+ struct dvb_dummy_fe_state *state = NULL;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops,
+ &dvb_dummy_fe_ofdm_ops,
+ sizeof(struct dvb_frontend_ops));
+
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+}
+EXPORT_SYMBOL(dvb_dummy_fe_ofdm_attach);
+
+static const struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops;
+
+struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void)
+{
+ struct dvb_dummy_fe_state *state = NULL;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops,
+ &dvb_dummy_fe_qpsk_ops,
+ sizeof(struct dvb_frontend_ops));
+
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+}
+EXPORT_SYMBOL(dvb_dummy_fe_qpsk_attach);
+
+static const struct dvb_frontend_ops dvb_dummy_fe_qam_ops;
+
+struct dvb_frontend *dvb_dummy_fe_qam_attach(void)
+{
+ struct dvb_dummy_fe_state *state = NULL;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops,
+ &dvb_dummy_fe_qam_ops,
+ sizeof(struct dvb_frontend_ops));
+
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+}
+EXPORT_SYMBOL(dvb_dummy_fe_qam_attach);
+
+static const struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Dummy DVB-T",
+ .frequency_min_hz = 0,
+ .frequency_max_hz = 863250 * kHz,
+ .frequency_stepsize_hz = 62500,
+ .caps = 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_6_7 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_8_9 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = dvb_dummy_fe_release,
+
+ .init = dvb_dummy_fe_init,
+ .sleep = dvb_dummy_fe_sleep,
+
+ .set_frontend = dvb_dummy_fe_set_frontend,
+ .get_frontend = dvb_dummy_fe_get_frontend,
+
+ .read_status = dvb_dummy_fe_read_status,
+ .read_ber = dvb_dummy_fe_read_ber,
+ .read_signal_strength = dvb_dummy_fe_read_signal_strength,
+ .read_snr = dvb_dummy_fe_read_snr,
+ .read_ucblocks = dvb_dummy_fe_read_ucblocks,
+};
+
+static const struct dvb_frontend_ops dvb_dummy_fe_qam_ops = {
+ .delsys = { SYS_DVBC_ANNEX_A },
+ .info = {
+ .name = "Dummy DVB-C",
+ .frequency_min_hz = 51 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 62500,
+ /* symbol_rate_min: SACLK/64 == (XIN/2)/64 */
+ .symbol_rate_min = (57840000 / 2) / 64,
+ .symbol_rate_max = (57840000 / 2) / 4, /* SACLK/4 */
+ .caps = FE_CAN_QAM_16 |
+ FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 |
+ FE_CAN_QAM_256 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_INVERSION_AUTO
+ },
+
+ .release = dvb_dummy_fe_release,
+
+ .init = dvb_dummy_fe_init,
+ .sleep = dvb_dummy_fe_sleep,
+
+ .set_frontend = dvb_dummy_fe_set_frontend,
+ .get_frontend = dvb_dummy_fe_get_frontend,
+
+ .read_status = dvb_dummy_fe_read_status,
+ .read_ber = dvb_dummy_fe_read_ber,
+ .read_signal_strength = dvb_dummy_fe_read_signal_strength,
+ .read_snr = dvb_dummy_fe_read_snr,
+ .read_ucblocks = dvb_dummy_fe_read_ucblocks,
+};
+
+static const struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops = {
+ .delsys = { SYS_DVBS },
+ .info = {
+ .name = "Dummy DVB-S",
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
+ .frequency_stepsize_hz = 250 * kHz,
+ .frequency_tolerance_hz = 29500 * kHz,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK
+ },
+
+ .release = dvb_dummy_fe_release,
+
+ .init = dvb_dummy_fe_init,
+ .sleep = dvb_dummy_fe_sleep,
+
+ .set_frontend = dvb_dummy_fe_set_frontend,
+ .get_frontend = dvb_dummy_fe_get_frontend,
+
+ .read_status = dvb_dummy_fe_read_status,
+ .read_ber = dvb_dummy_fe_read_ber,
+ .read_signal_strength = dvb_dummy_fe_read_signal_strength,
+ .read_snr = dvb_dummy_fe_read_snr,
+ .read_ucblocks = dvb_dummy_fe_read_ucblocks,
+
+ .set_voltage = dvb_dummy_fe_set_voltage,
+ .set_tone = dvb_dummy_fe_set_tone,
+};
+
+MODULE_DESCRIPTION("DVB DUMMY Frontend");
+MODULE_AUTHOR("Emard");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/dvb_dummy_fe.h b/drivers/media/dvb-frontends/dvb_dummy_fe.h
new file mode 100644
index 000000000000..463abf5ebd56
--- /dev/null
+++ b/drivers/media/dvb-frontends/dvb_dummy_fe.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Dummy Frontend
+ *
+ * Written by Emard <emard@softhome.net>
+ */
+
+#ifndef DVB_DUMMY_FE_H
+#define DVB_DUMMY_FE_H
+
+#include <linux/dvb/frontend.h>
+#include <media/dvb_frontend.h>
+
+#if IS_REACHABLE(CONFIG_DVB_DUMMY_FE)
+struct dvb_frontend *dvb_dummy_fe_ofdm_attach(void);
+struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void);
+struct dvb_frontend *dvb_dummy_fe_qam_attach(void);
+#else
+static inline struct dvb_frontend *dvb_dummy_fe_ofdm_attach(void)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+static inline struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+static inline struct dvb_frontend *dvb_dummy_fe_qam_attach(void)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_DUMMY_FE */
+
+#endif // DVB_DUMMY_FE_H
diff --git a/drivers/media/dvb/frontends/ec100.c b/drivers/media/dvb-frontends/ec100.c
index c56fddbf53b7..2ad0a3c2f756 100644
--- a/drivers/media/dvb/frontends/ec100.c
+++ b/drivers/media/dvb-frontends/ec100.c
@@ -1,32 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* E3C EC100 demodulator driver
*
* Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
-#include "dvb_frontend.h"
-#include "ec100_priv.h"
+#include <media/dvb_frontend.h>
#include "ec100.h"
-int ec100_debug;
-module_param_named(debug, ec100_debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
struct ec100_state {
struct i2c_adapter *i2c;
struct dvb_frontend frontend;
@@ -38,23 +19,33 @@ struct ec100_state {
/* write single register */
static int ec100_write_reg(struct ec100_state *state, u8 reg, u8 val)
{
+ int ret;
u8 buf[2] = {reg, val};
- struct i2c_msg msg = {
- .addr = state->config.demod_address,
- .flags = 0,
- .len = 2,
- .buf = buf};
-
- if (i2c_transfer(state->i2c, &msg, 1) != 1) {
- warn("I2C write failed reg:%02x", reg);
- return -EREMOTEIO;
+ struct i2c_msg msg[1] = {
+ {
+ .addr = state->config.demod_address,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&state->i2c->dev, "%s: i2c wr failed=%d reg=%02x\n",
+ KBUILD_MODNAME, ret, reg);
+ ret = -EREMOTEIO;
}
- return 0;
+
+ return ret;
}
/* read single register */
static int ec100_read_reg(struct ec100_state *state, u8 reg, u8 *val)
{
+ int ret;
struct i2c_msg msg[2] = {
{
.addr = state->config.demod_address,
@@ -69,11 +60,16 @@ static int ec100_read_reg(struct ec100_state *state, u8 reg, u8 *val)
}
};
- if (i2c_transfer(state->i2c, msg, 2) != 2) {
- warn("I2C read failed reg:%02x", reg);
- return -EREMOTEIO;
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret == 2) {
+ ret = 0;
+ } else {
+ dev_warn(&state->i2c->dev, "%s: i2c rd failed=%d reg=%02x\n",
+ KBUILD_MODNAME, ret, reg);
+ ret = -EREMOTEIO;
}
- return 0;
+
+ return ret;
}
static int ec100_set_frontend(struct dvb_frontend *fe)
@@ -83,8 +79,8 @@ static int ec100_set_frontend(struct dvb_frontend *fe)
int ret;
u8 tmp, tmp2;
- deb_info("%s: freq:%d bw:%d\n", __func__, c->frequency,
- c->bandwidth_hz);
+ dev_dbg(&state->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n",
+ __func__, c->frequency, c->bandwidth_hz);
/* program tuner */
if (fe->ops.tuner_ops.set_params)
@@ -150,7 +146,7 @@ static int ec100_set_frontend(struct dvb_frontend *fe)
return ret;
error:
- deb_info("%s: failed:%d\n", __func__, ret);
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -164,7 +160,7 @@ static int ec100_get_tune_settings(struct dvb_frontend *fe,
return 0;
}
-static int ec100_read_status(struct dvb_frontend *fe, fe_status_t *status)
+static int ec100_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct ec100_state *state = fe->demodulator_priv;
int ret;
@@ -196,7 +192,7 @@ static int ec100_read_status(struct dvb_frontend *fe, fe_status_t *status)
return ret;
error:
- deb_info("%s: failed:%d\n", __func__, ret);
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -228,7 +224,7 @@ static int ec100_read_ber(struct dvb_frontend *fe, u32 *ber)
return ret;
error:
- deb_info("%s: failed:%d\n", __func__, ret);
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -248,7 +244,7 @@ static int ec100_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
return ret;
error:
- deb_info("%s: failed:%d\n", __func__, ret);
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -270,7 +266,7 @@ static void ec100_release(struct dvb_frontend *fe)
kfree(state);
}
-static struct dvb_frontend_ops ec100_ops;
+static const struct dvb_frontend_ops ec100_ops;
struct dvb_frontend *ec100_attach(const struct ec100_config *config,
struct i2c_adapter *i2c)
@@ -303,9 +299,9 @@ error:
kfree(state);
return NULL;
}
-EXPORT_SYMBOL(ec100_attach);
+EXPORT_SYMBOL_GPL(ec100_attach);
-static struct dvb_frontend_ops ec100_ops = {
+static const struct dvb_frontend_ops ec100_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = "E3C EC100 DVB-T",
diff --git a/drivers/media/dvb-frontends/ec100.h b/drivers/media/dvb-frontends/ec100.h
new file mode 100644
index 000000000000..398dba65f6e6
--- /dev/null
+++ b/drivers/media/dvb-frontends/ec100.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * E3C EC100 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ */
+
+#ifndef EC100_H
+#define EC100_H
+
+#include <linux/dvb/frontend.h>
+
+struct ec100_config {
+ /* demodulator's I2C address */
+ u8 demod_address;
+};
+
+
+#if IS_REACHABLE(CONFIG_DVB_EC100)
+extern struct dvb_frontend *ec100_attach(const struct ec100_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *ec100_attach(
+ const struct ec100_config *config, struct i2c_adapter *i2c)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* EC100_H */
diff --git a/drivers/media/dvb/frontends/eds1547.h b/drivers/media/dvb-frontends/eds1547.h
index c983f2f85802..bb85a6e27076 100644
--- a/drivers/media/dvb/frontends/eds1547.h
+++ b/drivers/media/dvb-frontends/eds1547.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/* eds1547.h Earda EDS-1547 tuner support
*
* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
*
-* This program is free software; you can redistribute it and/or modify it
-* under the terms of the GNU General Public License as published by the
-* Free Software Foundation, version 2.
-*
-* see Documentation/dvb/README.dvb-usb for more information
+* see Documentation/driver-api/media/drivers/dvb-usb.rst for more information
*/
#ifndef EDS1547
diff --git a/drivers/media/dvb-frontends/gp8psk-fe.c b/drivers/media/dvb-frontends/gp8psk-fe.c
new file mode 100644
index 000000000000..ed671e951a17
--- /dev/null
+++ b/drivers/media/dvb-frontends/gp8psk-fe.c
@@ -0,0 +1,397 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Frontend driver for the GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module
+ *
+ * Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com)
+ * Copyright (C) 2006,2007 Genpix Electronics (genpix@genpix-electronics.com)
+ *
+ * Thanks to GENPIX for the sample code used to implement this module.
+ *
+ * This module is based off the vp7045 and vp702x modules
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "gp8psk-fe.h"
+#include <media/dvb_frontend.h>
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
+
+struct gp8psk_fe_state {
+ struct dvb_frontend fe;
+ void *priv;
+ const struct gp8psk_fe_ops *ops;
+ bool is_rev1;
+ u8 lock;
+ u16 snr;
+ unsigned long next_status_check;
+ unsigned long status_check_interval;
+};
+
+static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+ u8 status;
+
+ st->ops->in(st->priv, GET_8PSK_CONFIG, 0, 0, &status, 1);
+ return status & bmDCtuned;
+}
+
+static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+
+ return st->ops->out(st->priv, SET_8PSK_CONFIG, mode, 0, NULL, 0);
+}
+
+static int gp8psk_fe_update_status(struct gp8psk_fe_state *st)
+{
+ u8 buf[6];
+ if (time_after(jiffies,st->next_status_check)) {
+ st->ops->in(st->priv, GET_SIGNAL_LOCK, 0, 0, &st->lock, 1);
+ st->ops->in(st->priv, GET_SIGNAL_STRENGTH, 0, 0, buf, 6);
+ st->snr = (buf[1]) << 8 | buf[0];
+ st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000;
+ }
+ return 0;
+}
+
+static int gp8psk_fe_read_status(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+ gp8psk_fe_update_status(st);
+
+ if (st->lock)
+ *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ else
+ *status = 0;
+
+ if (*status & FE_HAS_LOCK)
+ st->status_check_interval = 1000;
+ else
+ st->status_check_interval = 100;
+ return 0;
+}
+
+/* not supported by this Frontend */
+static int gp8psk_fe_read_ber(struct dvb_frontend* fe, u32 *ber)
+{
+ (void) fe;
+ *ber = 0;
+ return 0;
+}
+
+/* not supported by this Frontend */
+static int gp8psk_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
+{
+ (void) fe;
+ *unc = 0;
+ return 0;
+}
+
+static int gp8psk_fe_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+ gp8psk_fe_update_status(st);
+ /* snr is reported in dBu*256 */
+ *snr = st->snr;
+ return 0;
+}
+
+static int gp8psk_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+ gp8psk_fe_update_status(st);
+ /* snr is reported in dBu*256 */
+ /* snr / 38.4 ~= 100% strength */
+ /* snr * 17 returns 100% strength as 65535 */
+ if (st->snr > 0xf00)
+ *strength = 0xffff;
+ else
+ *strength = (st->snr << 4) + st->snr; /* snr*17 */
+ return 0;
+}
+
+static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 800;
+ return 0;
+}
+
+static int gp8psk_fe_set_frontend(struct dvb_frontend *fe)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 cmd[10];
+ u32 freq = c->frequency * 1000;
+
+ dprintk("%s()\n", __func__);
+
+ cmd[4] = freq & 0xff;
+ cmd[5] = (freq >> 8) & 0xff;
+ cmd[6] = (freq >> 16) & 0xff;
+ cmd[7] = (freq >> 24) & 0xff;
+
+ /* backwards compatibility: DVB-S + 8-PSK were used for Turbo-FEC */
+ if (c->delivery_system == SYS_DVBS && c->modulation == PSK_8)
+ c->delivery_system = SYS_TURBO;
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ if (c->modulation != QPSK) {
+ dprintk("%s: unsupported modulation selected (%d)\n",
+ __func__, c->modulation);
+ return -EOPNOTSUPP;
+ }
+ c->fec_inner = FEC_AUTO;
+ break;
+ case SYS_DVBS2: /* kept for backwards compatibility */
+ dprintk("%s: DVB-S2 delivery system selected\n", __func__);
+ break;
+ case SYS_TURBO:
+ dprintk("%s: Turbo-FEC delivery system selected\n", __func__);
+ break;
+
+ default:
+ dprintk("%s: unsupported delivery system selected (%d)\n",
+ __func__, c->delivery_system);
+ return -EOPNOTSUPP;
+ }
+
+ cmd[0] = c->symbol_rate & 0xff;
+ cmd[1] = (c->symbol_rate >> 8) & 0xff;
+ cmd[2] = (c->symbol_rate >> 16) & 0xff;
+ cmd[3] = (c->symbol_rate >> 24) & 0xff;
+ switch (c->modulation) {
+ case QPSK:
+ if (st->is_rev1)
+ if (gp8psk_tuned_to_DCII(fe))
+ st->ops->reload(st->priv);
+ switch (c->fec_inner) {
+ case FEC_1_2:
+ cmd[9] = 0; break;
+ case FEC_2_3:
+ cmd[9] = 1; break;
+ case FEC_3_4:
+ cmd[9] = 2; break;
+ case FEC_5_6:
+ cmd[9] = 3; break;
+ case FEC_7_8:
+ cmd[9] = 4; break;
+ case FEC_AUTO:
+ cmd[9] = 5; break;
+ default:
+ cmd[9] = 5; break;
+ }
+ if (c->delivery_system == SYS_TURBO)
+ cmd[8] = ADV_MOD_TURBO_QPSK;
+ else
+ cmd[8] = ADV_MOD_DVB_QPSK;
+ break;
+ case PSK_8: /* PSK_8 is for compatibility with DN */
+ cmd[8] = ADV_MOD_TURBO_8PSK;
+ switch (c->fec_inner) {
+ case FEC_2_3:
+ cmd[9] = 0; break;
+ case FEC_3_4:
+ cmd[9] = 1; break;
+ case FEC_3_5:
+ cmd[9] = 2; break;
+ case FEC_5_6:
+ cmd[9] = 3; break;
+ case FEC_8_9:
+ cmd[9] = 4; break;
+ default:
+ cmd[9] = 0; break;
+ }
+ break;
+ case QAM_16: /* QAM_16 is for compatibility with DN */
+ cmd[8] = ADV_MOD_TURBO_16QAM;
+ cmd[9] = 0;
+ break;
+ default: /* Unknown modulation */
+ dprintk("%s: unsupported modulation selected (%d)\n",
+ __func__, c->modulation);
+ return -EOPNOTSUPP;
+ }
+
+ if (st->is_rev1)
+ gp8psk_set_tuner_mode(fe, 0);
+ st->ops->out(st->priv, TUNE_8PSK, 0, 0, cmd, 10);
+
+ st->lock = 0;
+ st->next_status_check = jiffies;
+ st->status_check_interval = 200;
+
+ return 0;
+}
+
+static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe,
+ struct dvb_diseqc_master_cmd *m)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ if (st->ops->out(st->priv, SEND_DISEQC_COMMAND, m->msg[0], 0,
+ m->msg, m->msg_len)) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int gp8psk_fe_send_diseqc_burst(struct dvb_frontend *fe,
+ enum fe_sec_mini_cmd burst)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+ u8 cmd;
+
+ dprintk("%s\n", __func__);
+
+ /* These commands are certainly wrong */
+ cmd = (burst == SEC_MINI_A) ? 0x00 : 0x01;
+
+ if (st->ops->out(st->priv, SEND_DISEQC_COMMAND, cmd, 0,
+ &cmd, 0)) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int gp8psk_fe_set_tone(struct dvb_frontend *fe,
+ enum fe_sec_tone_mode tone)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+
+ if (st->ops->out(st->priv, SET_22KHZ_TONE,
+ (tone == SEC_TONE_ON), 0, NULL, 0)) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int gp8psk_fe_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+
+ if (st->ops->out(st->priv, SET_LNB_VOLTAGE,
+ voltage == SEC_VOLTAGE_18, 0, NULL, 0)) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int gp8psk_fe_enable_high_lnb_voltage(struct dvb_frontend* fe, long onoff)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+
+ return st->ops->out(st->priv, USE_EXTRA_VOLT, onoff, 0, NULL, 0);
+}
+
+static int gp8psk_fe_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long sw_cmd)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+ u8 cmd = sw_cmd & 0x7f;
+
+ if (st->ops->out(st->priv, SET_DN_SWITCH, cmd, 0, NULL, 0))
+ return -EINVAL;
+
+ if (st->ops->out(st->priv, SET_LNB_VOLTAGE, !!(sw_cmd & 0x80),
+ 0, NULL, 0))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void gp8psk_fe_release(struct dvb_frontend* fe)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+
+ kfree(st);
+}
+
+static const struct dvb_frontend_ops gp8psk_fe_ops;
+
+struct dvb_frontend *gp8psk_fe_attach(const struct gp8psk_fe_ops *ops,
+ void *priv, bool is_rev1)
+{
+ struct gp8psk_fe_state *st;
+
+ if (!ops || !ops->in || !ops->out || !ops->reload) {
+ pr_err("Error! gp8psk-fe ops not defined.\n");
+ return NULL;
+ }
+
+ st = kzalloc(sizeof(struct gp8psk_fe_state), GFP_KERNEL);
+ if (!st)
+ return NULL;
+
+ memcpy(&st->fe.ops, &gp8psk_fe_ops, sizeof(struct dvb_frontend_ops));
+ st->fe.demodulator_priv = st;
+ st->ops = ops;
+ st->priv = priv;
+ st->is_rev1 = is_rev1;
+
+ pr_info("Frontend %sattached\n", is_rev1 ? "revision 1 " : "");
+
+ return &st->fe;
+}
+EXPORT_SYMBOL_GPL(gp8psk_fe_attach);
+
+static const struct dvb_frontend_ops gp8psk_fe_ops = {
+ .delsys = { SYS_DVBS },
+ .info = {
+ .name = "Genpix DVB-S",
+ .frequency_min_hz = 800 * MHz,
+ .frequency_max_hz = 2250 * MHz,
+ .frequency_stepsize_hz = 100 * kHz,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .symbol_rate_tolerance = 500, /* ppm */
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ /*
+ * FE_CAN_QAM_16 is for compatibility
+ * (Myth incorrectly detects Turbo-QPSK as plain QAM-16)
+ */
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_TURBO_FEC
+ },
+
+ .release = gp8psk_fe_release,
+
+ .init = NULL,
+ .sleep = NULL,
+
+ .set_frontend = gp8psk_fe_set_frontend,
+
+ .get_tune_settings = gp8psk_fe_get_tune_settings,
+
+ .read_status = gp8psk_fe_read_status,
+ .read_ber = gp8psk_fe_read_ber,
+ .read_signal_strength = gp8psk_fe_read_signal_strength,
+ .read_snr = gp8psk_fe_read_snr,
+ .read_ucblocks = gp8psk_fe_read_unc_blocks,
+
+ .diseqc_send_master_cmd = gp8psk_fe_send_diseqc_msg,
+ .diseqc_send_burst = gp8psk_fe_send_diseqc_burst,
+ .set_tone = gp8psk_fe_set_tone,
+ .set_voltage = gp8psk_fe_set_voltage,
+ .dishnetwork_send_legacy_command = gp8psk_fe_send_legacy_dish_cmd,
+ .enable_high_lnb_voltage = gp8psk_fe_enable_high_lnb_voltage
+};
+
+MODULE_AUTHOR("Alan Nisota <alannisota@gamil.com>");
+MODULE_DESCRIPTION("Frontend Driver for Genpix DVB-S");
+MODULE_VERSION("1.1");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/gp8psk-fe.h b/drivers/media/dvb-frontends/gp8psk-fe.h
new file mode 100644
index 000000000000..2805a3b67d62
--- /dev/null
+++ b/drivers/media/dvb-frontends/gp8psk-fe.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * gp8psk_fe driver
+ */
+
+#ifndef GP8PSK_FE_H
+#define GP8PSK_FE_H
+
+#include <linux/types.h>
+
+/* gp8psk commands */
+
+#define GET_8PSK_CONFIG 0x80 /* in */
+#define SET_8PSK_CONFIG 0x81
+#define I2C_WRITE 0x83
+#define I2C_READ 0x84
+#define ARM_TRANSFER 0x85
+#define TUNE_8PSK 0x86
+#define GET_SIGNAL_STRENGTH 0x87 /* in */
+#define LOAD_BCM4500 0x88
+#define BOOT_8PSK 0x89 /* in */
+#define START_INTERSIL 0x8A /* in */
+#define SET_LNB_VOLTAGE 0x8B
+#define SET_22KHZ_TONE 0x8C
+#define SEND_DISEQC_COMMAND 0x8D
+#define SET_DVB_MODE 0x8E
+#define SET_DN_SWITCH 0x8F
+#define GET_SIGNAL_LOCK 0x90 /* in */
+#define GET_FW_VERS 0x92
+#define GET_SERIAL_NUMBER 0x93 /* in */
+#define USE_EXTRA_VOLT 0x94
+#define GET_FPGA_VERS 0x95
+#define CW3K_INIT 0x9d
+
+/* PSK_configuration bits */
+#define bm8pskStarted 0x01
+#define bm8pskFW_Loaded 0x02
+#define bmIntersilOn 0x04
+#define bmDVBmode 0x08
+#define bm22kHz 0x10
+#define bmSEL18V 0x20
+#define bmDCtuned 0x40
+#define bmArmed 0x80
+
+/* Satellite modulation modes */
+#define ADV_MOD_DVB_QPSK 0 /* DVB-S QPSK */
+#define ADV_MOD_TURBO_QPSK 1 /* Turbo QPSK */
+#define ADV_MOD_TURBO_8PSK 2 /* Turbo 8PSK (also used for Trellis 8PSK) */
+#define ADV_MOD_TURBO_16QAM 3 /* Turbo 16QAM (also used for Trellis 8PSK) */
+
+#define ADV_MOD_DCII_C_QPSK 4 /* Digicipher II Combo */
+#define ADV_MOD_DCII_I_QPSK 5 /* Digicipher II I-stream */
+#define ADV_MOD_DCII_Q_QPSK 6 /* Digicipher II Q-stream */
+#define ADV_MOD_DCII_C_OQPSK 7 /* Digicipher II offset QPSK */
+#define ADV_MOD_DSS_QPSK 8 /* DSS (DIRECTV) QPSK */
+#define ADV_MOD_DVB_BPSK 9 /* DVB-S BPSK */
+
+/* firmware revision id's */
+#define GP8PSK_FW_REV1 0x020604
+#define GP8PSK_FW_REV2 0x020704
+#define GP8PSK_FW_VERS(_fw_vers) \
+ ((_fw_vers)[2]<<0x10 | (_fw_vers)[1]<<0x08 | (_fw_vers)[0])
+
+struct gp8psk_fe_ops {
+ int (*in)(void *priv, u8 req, u16 value, u16 index, u8 *b, int blen);
+ int (*out)(void *priv, u8 req, u16 value, u16 index, u8 *b, int blen);
+ int (*reload)(void *priv);
+};
+
+struct dvb_frontend *gp8psk_fe_attach(const struct gp8psk_fe_ops *ops,
+ void *priv, bool is_rev1);
+
+#endif
diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c
new file mode 100644
index 000000000000..f127adee3ebb
--- /dev/null
+++ b/drivers/media/dvb-frontends/helene.c
@@ -0,0 +1,1120 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * helene.c
+ *
+ * Sony HELENE DVB-S/S2 DVB-T/T2 DVB-C/C2 ISDB-T/S tuner driver (CXD2858ER)
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dvb/frontend.h>
+#include <linux/types.h>
+#include "helene.h"
+#include <media/dvb_frontend.h>
+
+#define MAX_WRITE_REGSIZE 20
+
+enum helene_state {
+ STATE_UNKNOWN,
+ STATE_SLEEP,
+ STATE_ACTIVE
+};
+
+struct helene_priv {
+ u32 frequency;
+ u8 i2c_address;
+ struct i2c_adapter *i2c;
+ enum helene_state state;
+ void *set_tuner_data;
+ int (*set_tuner)(void *, int);
+ enum helene_xtal xtal;
+};
+
+#define TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system) \
+ (((tv_system) != SONY_HELENE_DTV_DVBC_6) && \
+ ((tv_system) != SONY_HELENE_DTV_DVBC_8)\
+ && ((tv_system) != SONY_HELENE_DTV_DVBC2_6) && \
+ ((tv_system) != SONY_HELENE_DTV_DVBC2_8))
+
+#define HELENE_AUTO 0xff
+#define HELENE_OFFSET(ofs) ((u8)(ofs) & 0x1F)
+#define HELENE_BW_6 0x00
+#define HELENE_BW_7 0x01
+#define HELENE_BW_8 0x02
+#define HELENE_BW_1_7 0x03
+
+enum helene_tv_system_t {
+ SONY_HELENE_TV_SYSTEM_UNKNOWN,
+ /* Terrestrial Analog */
+ SONY_HELENE_ATV_MN_EIAJ,
+ /**< System-M (Japan) (IF: Fp=5.75MHz in default) */
+ SONY_HELENE_ATV_MN_SAP,
+ /**< System-M (US) (IF: Fp=5.75MHz in default) */
+ SONY_HELENE_ATV_MN_A2,
+ /**< System-M (Korea) (IF: Fp=5.9MHz in default) */
+ SONY_HELENE_ATV_BG,
+ /**< System-B/G (IF: Fp=7.3MHz in default) */
+ SONY_HELENE_ATV_I,
+ /**< System-I (IF: Fp=7.85MHz in default) */
+ SONY_HELENE_ATV_DK,
+ /**< System-D/K (IF: Fp=7.85MHz in default) */
+ SONY_HELENE_ATV_L,
+ /**< System-L (IF: Fp=7.85MHz in default) */
+ SONY_HELENE_ATV_L_DASH,
+ /**< System-L DASH (IF: Fp=2.2MHz in default) */
+ /* Terrestrial/Cable Digital */
+ SONY_HELENE_DTV_8VSB,
+ /**< ATSC 8VSB (IF: Fc=3.7MHz in default) */
+ SONY_HELENE_DTV_QAM,
+ /**< US QAM (IF: Fc=3.7MHz in default) */
+ SONY_HELENE_DTV_ISDBT_6,
+ /**< ISDB-T 6MHzBW (IF: Fc=3.55MHz in default) */
+ SONY_HELENE_DTV_ISDBT_7,
+ /**< ISDB-T 7MHzBW (IF: Fc=4.15MHz in default) */
+ SONY_HELENE_DTV_ISDBT_8,
+ /**< ISDB-T 8MHzBW (IF: Fc=4.75MHz in default) */
+ SONY_HELENE_DTV_DVBT_5,
+ /**< DVB-T 5MHzBW (IF: Fc=3.6MHz in default) */
+ SONY_HELENE_DTV_DVBT_6,
+ /**< DVB-T 6MHzBW (IF: Fc=3.6MHz in default) */
+ SONY_HELENE_DTV_DVBT_7,
+ /**< DVB-T 7MHzBW (IF: Fc=4.2MHz in default) */
+ SONY_HELENE_DTV_DVBT_8,
+ /**< DVB-T 8MHzBW (IF: Fc=4.8MHz in default) */
+ SONY_HELENE_DTV_DVBT2_1_7,
+ /**< DVB-T2 1.7MHzBW (IF: Fc=3.5MHz in default) */
+ SONY_HELENE_DTV_DVBT2_5,
+ /**< DVB-T2 5MHzBW (IF: Fc=3.6MHz in default) */
+ SONY_HELENE_DTV_DVBT2_6,
+ /**< DVB-T2 6MHzBW (IF: Fc=3.6MHz in default) */
+ SONY_HELENE_DTV_DVBT2_7,
+ /**< DVB-T2 7MHzBW (IF: Fc=4.2MHz in default) */
+ SONY_HELENE_DTV_DVBT2_8,
+ /**< DVB-T2 8MHzBW (IF: Fc=4.8MHz in default) */
+ SONY_HELENE_DTV_DVBC_6,
+ /**< DVB-C 6MHzBW (IF: Fc=3.7MHz in default) */
+ SONY_HELENE_DTV_DVBC_8,
+ /**< DVB-C 8MHzBW (IF: Fc=4.9MHz in default) */
+ SONY_HELENE_DTV_DVBC2_6,
+ /**< DVB-C2 6MHzBW (IF: Fc=3.7MHz in default) */
+ SONY_HELENE_DTV_DVBC2_8,
+ /**< DVB-C2 8MHzBW (IF: Fc=4.9MHz in default) */
+ SONY_HELENE_DTV_DTMB,
+ /**< DTMB (IF: Fc=5.1MHz in default) */
+ /* Satellite */
+ SONY_HELENE_STV_ISDBS,
+ /**< ISDB-S */
+ SONY_HELENE_STV_DVBS,
+ /**< DVB-S */
+ SONY_HELENE_STV_DVBS2,
+ /**< DVB-S2 */
+
+ SONY_HELENE_ATV_MIN = SONY_HELENE_ATV_MN_EIAJ,
+ /**< Minimum analog terrestrial system */
+ SONY_HELENE_ATV_MAX = SONY_HELENE_ATV_L_DASH,
+ /**< Maximum analog terrestrial system */
+ SONY_HELENE_DTV_MIN = SONY_HELENE_DTV_8VSB,
+ /**< Minimum digital terrestrial system */
+ SONY_HELENE_DTV_MAX = SONY_HELENE_DTV_DTMB,
+ /**< Maximum digital terrestrial system */
+ SONY_HELENE_TERR_TV_SYSTEM_NUM,
+ /**< Number of supported terrestrial broadcasting system */
+ SONY_HELENE_STV_MIN = SONY_HELENE_STV_ISDBS,
+ /**< Minimum satellite system */
+ SONY_HELENE_STV_MAX = SONY_HELENE_STV_DVBS2
+ /**< Maximum satellite system */
+};
+
+struct helene_terr_adjust_param_t {
+ /* < Addr:0x69 Bit[6:4] : RFVGA gain.
+ * 0xFF means Auto. (RF_GAIN_SEL = 1)
+ */
+ uint8_t RF_GAIN;
+ /* < Addr:0x69 Bit[3:0] : IF_BPF gain.
+ */
+ uint8_t IF_BPF_GC;
+ /* < Addr:0x6B Bit[3:0] : RF overload
+ * RF input detect level. (FRF <= 172MHz)
+ */
+ uint8_t RFOVLD_DET_LV1_VL;
+ /* < Addr:0x6B Bit[3:0] : RF overload
+ * RF input detect level. (172MHz < FRF <= 464MHz)
+ */
+ uint8_t RFOVLD_DET_LV1_VH;
+ /* < Addr:0x6B Bit[3:0] : RF overload
+ * RF input detect level. (FRF > 464MHz)
+ */
+ uint8_t RFOVLD_DET_LV1_U;
+ /* < Addr:0x6C Bit[2:0] :
+ * Internal RFAGC detect level. (FRF <= 172MHz)
+ */
+ uint8_t IFOVLD_DET_LV_VL;
+ /* < Addr:0x6C Bit[2:0] :
+ * Internal RFAGC detect level. (172MHz < FRF <= 464MHz)
+ */
+ uint8_t IFOVLD_DET_LV_VH;
+ /* < Addr:0x6C Bit[2:0] :
+ * Internal RFAGC detect level. (FRF > 464MHz)
+ */
+ uint8_t IFOVLD_DET_LV_U;
+ /* < Addr:0x6D Bit[5:4] :
+ * IF filter center offset.
+ */
+ uint8_t IF_BPF_F0;
+ /* < Addr:0x6D Bit[1:0] :
+ * 6MHzBW(0x00) or 7MHzBW(0x01)
+ * or 8MHzBW(0x02) or 1.7MHzBW(0x03)
+ */
+ uint8_t BW;
+ /* < Addr:0x6E Bit[4:0] :
+ * 5bit signed. IF offset (kHz) = FIF_OFFSET x 50
+ */
+ uint8_t FIF_OFFSET;
+ /* < Addr:0x6F Bit[4:0] :
+ * 5bit signed. BW offset (kHz) =
+ * BW_OFFSET x 50 (BW_OFFSET x 10 in 1.7MHzBW)
+ */
+ uint8_t BW_OFFSET;
+ /* < Addr:0x9C Bit[0] :
+ * Local polarity. (0: Upper Local, 1: Lower Local)
+ */
+ uint8_t IS_LOWERLOCAL;
+};
+
+static const struct helene_terr_adjust_param_t
+terr_params[SONY_HELENE_TERR_TV_SYSTEM_NUM] = {
+ /*< SONY_HELENE_TV_SYSTEM_UNKNOWN */
+ {HELENE_AUTO, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(0), 0x00},
+ /* Analog */
+ /**< SONY_HELENE_ATV_MN_EIAJ (System-M (Japan)) */
+ {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00,
+ HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(1), 0x00},
+ /**< SONY_HELENE_ATV_MN_SAP (System-M (US)) */
+ {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00,
+ HELENE_BW_6, HELENE_OFFSET(0), HELENE_OFFSET(1), 0x00},
+ {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00,
+ HELENE_BW_6, HELENE_OFFSET(3), HELENE_OFFSET(1), 0x00},
+ /**< SONY_HELENE_ATV_MN_A2 (System-M (Korea)) */
+ {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00,
+ HELENE_BW_7, HELENE_OFFSET(11), HELENE_OFFSET(5), 0x00},
+ /**< SONY_HELENE_ATV_BG (System-B/G) */
+ {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00,
+ HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00},
+ /**< SONY_HELENE_ATV_I (System-I) */
+ {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00,
+ HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00},
+ /**< SONY_HELENE_ATV_DK (System-D/K) */
+ {HELENE_AUTO, 0x03, 0x04, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x00,
+ HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(-3), 0x00},
+ /**< SONY_HELENE_ATV_L (System-L) */
+ {HELENE_AUTO, 0x03, 0x04, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x00,
+ HELENE_BW_8, HELENE_OFFSET(-1), HELENE_OFFSET(4), 0x00},
+ /**< SONY_HELENE_ATV_L_DASH (System-L DASH) */
+ /* Digital */
+ {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x03, 0x03, 0x03, 0x00,
+ HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-3), 0x00},
+ /**< SONY_HELENE_DTV_8VSB (ATSC 8VSB) */
+ {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-3), 0x00},
+ /**< SONY_HELENE_DTV_QAM (US QAM) */
+ {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_6, HELENE_OFFSET(-9), HELENE_OFFSET(-5), 0x00},
+ /**< SONY_HELENE_DTV_ISDBT_6 (ISDB-T 6MHzBW) */
+ {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_7, HELENE_OFFSET(-7), HELENE_OFFSET(-6), 0x00},
+ /**< SONY_HELENE_DTV_ISDBT_7 (ISDB-T 7MHzBW) */
+ {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_8, HELENE_OFFSET(-5), HELENE_OFFSET(-7), 0x00},
+ /**< SONY_HELENE_DTV_ISDBT_8 (ISDB-T 8MHzBW) */
+ {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00},
+ /**< SONY_HELENE_DTV_DVBT_5 (DVB-T 5MHzBW) */
+ {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00},
+ /**< SONY_HELENE_DTV_DVBT_6 (DVB-T 6MHzBW) */
+ {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_7, HELENE_OFFSET(-6), HELENE_OFFSET(-5), 0x00},
+ /**< SONY_HELENE_DTV_DVBT_7 (DVB-T 7MHzBW) */
+ {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_8, HELENE_OFFSET(-4), HELENE_OFFSET(-6), 0x00},
+ /**< SONY_HELENE_DTV_DVBT_8 (DVB-T 8MHzBW) */
+ {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_1_7, HELENE_OFFSET(-10), HELENE_OFFSET(-10), 0x00},
+ /**< SONY_HELENE_DTV_DVBT2_1_7 (DVB-T2 1.7MHzBW) */
+ {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00},
+ /**< SONY_HELENE_DTV_DVBT2_5 (DVB-T2 5MHzBW) */
+ {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_6, HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00},
+ /**< SONY_HELENE_DTV_DVBT2_6 (DVB-T2 6MHzBW) */
+ {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_7, HELENE_OFFSET(-6), HELENE_OFFSET(-5), 0x00},
+ /**< SONY_HELENE_DTV_DVBT2_7 (DVB-T2 7MHzBW) */
+ {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_8, HELENE_OFFSET(-4), HELENE_OFFSET(-6), 0x00},
+ /**< SONY_HELENE_DTV_DVBT2_8 (DVB-T2 8MHzBW) */
+ {HELENE_AUTO, 0x05, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00,
+ HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-4), 0x00},
+ /**< SONY_HELENE_DTV_DVBC_6 (DVB-C 6MHzBW) */
+ {HELENE_AUTO, 0x05, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00,
+ HELENE_BW_8, HELENE_OFFSET(-2), HELENE_OFFSET(-3), 0x00},
+ /**< SONY_HELENE_DTV_DVBC_8 (DVB-C 8MHzBW) */
+ {HELENE_AUTO, 0x03, 0x09, 0x09, 0x09, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_6, HELENE_OFFSET(-6), HELENE_OFFSET(-2), 0x00},
+ /**< SONY_HELENE_DTV_DVBC2_6 (DVB-C2 6MHzBW) */
+ {HELENE_AUTO, 0x03, 0x09, 0x09, 0x09, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_8, HELENE_OFFSET(-2), HELENE_OFFSET(0), 0x00},
+ /**< SONY_HELENE_DTV_DVBC2_8 (DVB-C2 8MHzBW) */
+ {HELENE_AUTO, 0x04, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+ HELENE_BW_8, HELENE_OFFSET(2), HELENE_OFFSET(1), 0x00}
+ /**< SONY_HELENE_DTV_DTMB (DTMB) */
+};
+
+static void helene_i2c_debug(struct helene_priv *priv,
+ u8 reg, u8 write, const u8 *data, u32 len)
+{
+ dev_dbg(&priv->i2c->dev, "helene: I2C %s reg 0x%02x size %d\n",
+ (write == 0 ? "read" : "write"), reg, len);
+ print_hex_dump_bytes("helene: I2C data: ",
+ DUMP_PREFIX_OFFSET, data, len);
+}
+
+static int helene_write_regs(struct helene_priv *priv,
+ u8 reg, const u8 *data, u32 len)
+{
+ int ret;
+ u8 buf[MAX_WRITE_REGSIZE + 1];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->i2c_address,
+ .flags = 0,
+ .len = len + 1,
+ .buf = buf,
+ }
+ };
+
+ if (len + 1 > sizeof(buf)) {
+ dev_warn(&priv->i2c->dev,
+ "wr reg=%04x: len=%d vs %zu is too big!\n",
+ reg, len + 1, sizeof(buf));
+ return -E2BIG;
+ }
+
+ helene_i2c_debug(priv, reg, 1, data, len);
+ buf[0] = reg;
+ memcpy(&buf[1], data, len);
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret >= 0 && ret != 1)
+ ret = -EREMOTEIO;
+ if (ret < 0) {
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c wr failed=%d reg=%02x len=%d\n",
+ KBUILD_MODNAME, ret, reg, len);
+ return ret;
+ }
+ return 0;
+}
+
+static int helene_write_reg(struct helene_priv *priv, u8 reg, u8 val)
+{
+ u8 tmp = val; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */
+
+ return helene_write_regs(priv, reg, &tmp, 1);
+}
+
+static int helene_read_regs(struct helene_priv *priv,
+ u8 reg, u8 *val, u32 len)
+{
+ int ret;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->i2c_address,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = priv->i2c_address,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, &msg[0], 1);
+ if (ret >= 0 && ret != 1)
+ ret = -EREMOTEIO;
+ if (ret < 0) {
+ dev_warn(&priv->i2c->dev,
+ "%s: I2C rw failed=%d addr=%02x reg=%02x\n",
+ KBUILD_MODNAME, ret, priv->i2c_address, reg);
+ return ret;
+ }
+ ret = i2c_transfer(priv->i2c, &msg[1], 1);
+ if (ret >= 0 && ret != 1)
+ ret = -EREMOTEIO;
+ if (ret < 0) {
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c rd failed=%d addr=%02x reg=%02x\n",
+ KBUILD_MODNAME, ret, priv->i2c_address, reg);
+ return ret;
+ }
+ helene_i2c_debug(priv, reg, 0, val, len);
+ return 0;
+}
+
+static int helene_read_reg(struct helene_priv *priv, u8 reg, u8 *val)
+{
+ return helene_read_regs(priv, reg, val, 1);
+}
+
+static int helene_set_reg_bits(struct helene_priv *priv,
+ u8 reg, u8 data, u8 mask)
+{
+ int res;
+ u8 rdata;
+
+ if (mask != 0xff) {
+ res = helene_read_reg(priv, reg, &rdata);
+ if (res != 0)
+ return res;
+ data = ((data & mask) | (rdata & (mask ^ 0xFF)));
+ }
+ return helene_write_reg(priv, reg, data);
+}
+
+static int helene_enter_power_save(struct helene_priv *priv)
+{
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state == STATE_SLEEP)
+ return 0;
+
+ /* Standby setting for CPU */
+ helene_write_reg(priv, 0x88, 0x0);
+
+ /* Standby setting for internal logic block */
+ helene_write_reg(priv, 0x87, 0xC0);
+
+ priv->state = STATE_SLEEP;
+ return 0;
+}
+
+static int helene_leave_power_save(struct helene_priv *priv)
+{
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state == STATE_ACTIVE)
+ return 0;
+
+ /* Standby setting for internal logic block */
+ helene_write_reg(priv, 0x87, 0xC4);
+
+ /* Standby setting for CPU */
+ helene_write_reg(priv, 0x88, 0x40);
+
+ priv->state = STATE_ACTIVE;
+ return 0;
+}
+
+static int helene_init(struct dvb_frontend *fe)
+{
+ struct helene_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ return helene_leave_power_save(priv);
+}
+
+static void helene_release(struct dvb_frontend *fe)
+{
+ struct helene_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+}
+
+static int helene_sleep(struct dvb_frontend *fe)
+{
+ struct helene_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ helene_enter_power_save(priv);
+ return 0;
+}
+
+static enum helene_tv_system_t helene_get_tv_system(struct dvb_frontend *fe)
+{
+ enum helene_tv_system_t system = SONY_HELENE_TV_SYSTEM_UNKNOWN;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct helene_priv *priv = fe->tuner_priv;
+
+ if (p->delivery_system == SYS_DVBT) {
+ if (p->bandwidth_hz <= 5000000)
+ system = SONY_HELENE_DTV_DVBT_5;
+ else if (p->bandwidth_hz <= 6000000)
+ system = SONY_HELENE_DTV_DVBT_6;
+ else if (p->bandwidth_hz <= 7000000)
+ system = SONY_HELENE_DTV_DVBT_7;
+ else if (p->bandwidth_hz <= 8000000)
+ system = SONY_HELENE_DTV_DVBT_8;
+ else {
+ system = SONY_HELENE_DTV_DVBT_8;
+ p->bandwidth_hz = 8000000;
+ }
+ } else if (p->delivery_system == SYS_DVBT2) {
+ if (p->bandwidth_hz <= 5000000)
+ system = SONY_HELENE_DTV_DVBT2_5;
+ else if (p->bandwidth_hz <= 6000000)
+ system = SONY_HELENE_DTV_DVBT2_6;
+ else if (p->bandwidth_hz <= 7000000)
+ system = SONY_HELENE_DTV_DVBT2_7;
+ else if (p->bandwidth_hz <= 8000000)
+ system = SONY_HELENE_DTV_DVBT2_8;
+ else {
+ system = SONY_HELENE_DTV_DVBT2_8;
+ p->bandwidth_hz = 8000000;
+ }
+ } else if (p->delivery_system == SYS_DVBS) {
+ system = SONY_HELENE_STV_DVBS;
+ } else if (p->delivery_system == SYS_DVBS2) {
+ system = SONY_HELENE_STV_DVBS2;
+ } else if (p->delivery_system == SYS_ISDBS) {
+ system = SONY_HELENE_STV_ISDBS;
+ } else if (p->delivery_system == SYS_ISDBT) {
+ if (p->bandwidth_hz <= 6000000)
+ system = SONY_HELENE_DTV_ISDBT_6;
+ else if (p->bandwidth_hz <= 7000000)
+ system = SONY_HELENE_DTV_ISDBT_7;
+ else if (p->bandwidth_hz <= 8000000)
+ system = SONY_HELENE_DTV_ISDBT_8;
+ else {
+ system = SONY_HELENE_DTV_ISDBT_8;
+ p->bandwidth_hz = 8000000;
+ }
+ } else if (p->delivery_system == SYS_DVBC_ANNEX_A) {
+ if (p->bandwidth_hz <= 6000000)
+ system = SONY_HELENE_DTV_DVBC_6;
+ else if (p->bandwidth_hz <= 8000000)
+ system = SONY_HELENE_DTV_DVBC_8;
+ }
+ dev_dbg(&priv->i2c->dev,
+ "%s(): HELENE DTV system %d (delsys %d, bandwidth %d)\n",
+ __func__, (int)system, p->delivery_system,
+ p->bandwidth_hz);
+ return system;
+}
+
+static int helene_set_params_s(struct dvb_frontend *fe)
+{
+ u8 data[MAX_WRITE_REGSIZE];
+ u32 frequency;
+ enum helene_tv_system_t tv_system;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct helene_priv *priv = fe->tuner_priv;
+ int frequencykHz = p->frequency;
+ uint32_t frequency4kHz = 0;
+ u32 symbol_rate = p->symbol_rate/1000;
+
+ dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz sr=%uKsps\n",
+ __func__, frequencykHz, symbol_rate);
+ tv_system = helene_get_tv_system(fe);
+
+ if (tv_system == SONY_HELENE_TV_SYSTEM_UNKNOWN) {
+ dev_err(&priv->i2c->dev, "%s(): unknown DTV system\n",
+ __func__);
+ return -EINVAL;
+ }
+ /* RF switch turn to satellite */
+ if (priv->set_tuner)
+ priv->set_tuner(priv->set_tuner_data, 0);
+ frequency = roundup(p->frequency / 1000, 1);
+
+ /* Disable IF signal output */
+ helene_write_reg(priv, 0x15, 0x02);
+
+ /* RFIN matching in power save (Sat) reset */
+ helene_write_reg(priv, 0x43, 0x06);
+
+ /* Analog block setting (0x6A, 0x6B) */
+ data[0] = 0x00;
+ data[1] = 0x00;
+ helene_write_regs(priv, 0x6A, data, 2);
+ helene_write_reg(priv, 0x75, 0x99);
+ helene_write_reg(priv, 0x9D, 0x00);
+
+ /* Tuning setting for CPU (0x61) */
+ helene_write_reg(priv, 0x61, 0x07);
+
+ /* Satellite mode select (0x01) */
+ helene_write_reg(priv, 0x01, 0x01);
+
+ /* Clock enable for internal logic block, CPU wake-up (0x04, 0x05) */
+ data[0] = 0xC4;
+ data[1] = 0x40;
+
+ switch (priv->xtal) {
+ case SONY_HELENE_XTAL_16000:
+ data[2] = 0x02;
+ break;
+ case SONY_HELENE_XTAL_20500:
+ data[2] = 0x02;
+ break;
+ case SONY_HELENE_XTAL_24000:
+ data[2] = 0x03;
+ break;
+ case SONY_HELENE_XTAL_41000:
+ data[2] = 0x05;
+ break;
+ default:
+ dev_err(&priv->i2c->dev, "%s(): unknown xtal %d\n",
+ __func__, priv->xtal);
+ return -EINVAL;
+ }
+
+ /* Setting for analog block (0x07). LOOPFILTER INTERNAL */
+ data[3] = 0x80;
+
+ /* Tuning setting for analog block
+ * (0x08, 0x09, 0x0A, 0x0B). LOOPFILTER INTERNAL
+ */
+ if (priv->xtal == SONY_HELENE_XTAL_20500)
+ data[4] = 0x58;
+ else
+ data[4] = 0x70;
+
+ data[5] = 0x1E;
+ data[6] = 0x02;
+ data[7] = 0x24;
+
+ /* Enable for analog block (0x0C, 0x0D, 0x0E). SAT LNA ON */
+ data[8] = 0x0F;
+ data[8] |= 0xE0; /* POWERSAVE_TERR_RF_ACTIVE */
+ data[9] = 0x02;
+ data[10] = 0x1E;
+
+ /* Setting for LPF cutoff frequency (0x0F) */
+ switch (tv_system) {
+ case SONY_HELENE_STV_ISDBS:
+ data[11] = 0x22; /* 22MHz */
+ break;
+ case SONY_HELENE_STV_DVBS:
+ if (symbol_rate <= 4000)
+ data[11] = 0x05;
+ else if (symbol_rate <= 10000)
+ data[11] = (uint8_t)((symbol_rate * 47
+ + (40000-1)) / 40000);
+ else
+ data[11] = (uint8_t)((symbol_rate * 27
+ + (40000-1)) / 40000 + 5);
+
+ if (data[11] > 36)
+ data[11] = 36; /* 5 <= lpf_cutoff <= 36 is valid */
+ break;
+ case SONY_HELENE_STV_DVBS2:
+ if (symbol_rate <= 4000)
+ data[11] = 0x05;
+ else if (symbol_rate <= 10000)
+ data[11] = (uint8_t)((symbol_rate * 11
+ + (10000-1)) / 10000);
+ else
+ data[11] = (uint8_t)((symbol_rate * 3
+ + (5000-1)) / 5000 + 5);
+
+ if (data[11] > 36)
+ data[11] = 36; /* 5 <= lpf_cutoff <= 36 is valid */
+ break;
+ default:
+ dev_err(&priv->i2c->dev, "%s(): unknown standard %d\n",
+ __func__, tv_system);
+ return -EINVAL;
+ }
+
+ /* RF tuning frequency setting (0x10, 0x11, 0x12) */
+ frequency4kHz = (frequencykHz + 2) / 4;
+ data[12] = (uint8_t)(frequency4kHz & 0xFF); /* FRF_L */
+ data[13] = (uint8_t)((frequency4kHz >> 8) & 0xFF); /* FRF_M */
+ /* FRF_H (bit[3:0]) */
+ data[14] = (uint8_t)((frequency4kHz >> 16) & 0x0F);
+
+ /* Tuning command (0x13) */
+ data[15] = 0xFF;
+
+ /* Setting for IQOUT_LIMIT (0x14) 0.75Vpp */
+ data[16] = 0x00;
+
+ /* Enable IQ output (0x15) */
+ data[17] = 0x01;
+
+ helene_write_regs(priv, 0x04, data, 18);
+
+ dev_dbg(&priv->i2c->dev, "%s(): tune done\n",
+ __func__);
+
+ priv->frequency = frequency;
+ return 0;
+}
+
+static int helene_set_params_t(struct dvb_frontend *fe)
+{
+ u8 data[MAX_WRITE_REGSIZE];
+ u32 frequency;
+ enum helene_tv_system_t tv_system;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct helene_priv *priv = fe->tuner_priv;
+ int frequencykHz = p->frequency / 1000;
+
+ dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz\n",
+ __func__, frequencykHz);
+ tv_system = helene_get_tv_system(fe);
+
+ if (tv_system == SONY_HELENE_TV_SYSTEM_UNKNOWN) {
+ dev_dbg(&priv->i2c->dev, "%s(): unknown DTV system\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (priv->set_tuner)
+ priv->set_tuner(priv->set_tuner_data, 1);
+ frequency = roundup(p->frequency / 1000, 25);
+
+ /* mode select */
+ helene_write_reg(priv, 0x01, 0x00);
+
+ /* Disable IF signal output */
+ helene_write_reg(priv, 0x74, 0x02);
+
+ if (priv->state == STATE_SLEEP)
+ helene_leave_power_save(priv);
+
+ /* Initial setting for internal analog block (0x91, 0x92) */
+ if ((tv_system == SONY_HELENE_DTV_DVBC_6) ||
+ (tv_system == SONY_HELENE_DTV_DVBC_8)) {
+ data[0] = 0x16;
+ data[1] = 0x26;
+ } else {
+ data[0] = 0x10;
+ data[1] = 0x20;
+ }
+ helene_write_regs(priv, 0x91, data, 2);
+
+ /* Setting for analog block */
+ if (TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system))
+ data[0] = 0x90;
+ else
+ data[0] = 0x00;
+
+ /* Setting for local polarity (0x9D) */
+ data[1] = (uint8_t)(terr_params[tv_system].IS_LOWERLOCAL & 0x01);
+ helene_write_regs(priv, 0x9C, data, 2);
+
+ /* Enable for analog block */
+ data[0] = 0xEE;
+ data[1] = 0x02;
+ data[2] = 0x1E;
+ data[3] = 0x67; /* Tuning setting for CPU */
+
+ /* Setting for PLL reference divider for xtal=24MHz */
+ if ((tv_system == SONY_HELENE_DTV_DVBC_6) ||
+ (tv_system == SONY_HELENE_DTV_DVBC_8))
+ data[4] = 0x18;
+ else
+ data[4] = 0x03;
+
+ /* Tuning setting for analog block */
+ if (TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system)) {
+ data[5] = 0x38;
+ data[6] = 0x1E;
+ data[7] = 0x02;
+ data[8] = 0x24;
+ } else if ((tv_system == SONY_HELENE_DTV_DVBC_6) ||
+ (tv_system == SONY_HELENE_DTV_DVBC_8)) {
+ data[5] = 0x1C;
+ data[6] = 0x78;
+ data[7] = 0x08;
+ data[8] = 0x1C;
+ } else {
+ data[5] = 0xB4;
+ data[6] = 0x78;
+ data[7] = 0x08;
+ data[8] = 0x30;
+ }
+ helene_write_regs(priv, 0x5E, data, 9);
+
+ /* LT_AMP_EN should be 0 */
+ helene_set_reg_bits(priv, 0x67, 0x0, 0x02);
+
+ /* Setting for IFOUT_LIMIT */
+ data[0] = 0x00; /* 1.5Vpp */
+
+ /* RF_GAIN setting */
+ if (terr_params[tv_system].RF_GAIN == HELENE_AUTO)
+ data[1] = 0x80; /* RF_GAIN_SEL = 1 */
+ else
+ data[1] = (uint8_t)((terr_params[tv_system].RF_GAIN
+ << 4) & 0x70);
+
+ /* IF_BPF_GC setting */
+ data[1] |= (uint8_t)(terr_params[tv_system].IF_BPF_GC & 0x0F);
+
+ /* Setting for internal RFAGC (0x6A, 0x6B, 0x6C) */
+ data[2] = 0x00;
+ if (frequencykHz <= 172000) {
+ data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_VL
+ & 0x0F);
+ data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_VL
+ & 0x07);
+ } else if (frequencykHz <= 464000) {
+ data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_VH
+ & 0x0F);
+ data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_VH
+ & 0x07);
+ } else {
+ data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_U
+ & 0x0F);
+ data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_U
+ & 0x07);
+ }
+ data[4] |= 0x20;
+
+ /* Setting for IF frequency and bandwidth */
+
+ /* IF filter center frequency offset (IF_BPF_F0) (0x6D) */
+ data[5] = (uint8_t)((terr_params[tv_system].IF_BPF_F0 << 4) & 0x30);
+
+ /* IF filter band width (BW) (0x6D) */
+ data[5] |= (uint8_t)(terr_params[tv_system].BW & 0x03);
+
+ /* IF frequency offset value (FIF_OFFSET) (0x6E) */
+ data[6] = (uint8_t)(terr_params[tv_system].FIF_OFFSET & 0x1F);
+
+ /* IF band width offset value (BW_OFFSET) (0x6F) */
+ data[7] = (uint8_t)(terr_params[tv_system].BW_OFFSET & 0x1F);
+
+ /* RF tuning frequency setting (0x70, 0x71, 0x72) */
+ data[8] = (uint8_t)(frequencykHz & 0xFF); /* FRF_L */
+ data[9] = (uint8_t)((frequencykHz >> 8) & 0xFF); /* FRF_M */
+ data[10] = (uint8_t)((frequencykHz >> 16)
+ & 0x0F); /* FRF_H (bit[3:0]) */
+
+ /* Tuning command */
+ data[11] = 0xFF;
+
+ /* Enable IF output, AGC and IFOUT pin selection (0x74) */
+ data[12] = 0x01;
+
+ if ((tv_system == SONY_HELENE_DTV_DVBC_6) ||
+ (tv_system == SONY_HELENE_DTV_DVBC_8)) {
+ data[13] = 0xD9;
+ data[14] = 0x0F;
+ data[15] = 0x24;
+ data[16] = 0x87;
+ } else {
+ data[13] = 0x99;
+ data[14] = 0x00;
+ data[15] = 0x24;
+ data[16] = 0x87;
+ }
+
+ helene_write_regs(priv, 0x68, data, 17);
+
+ dev_dbg(&priv->i2c->dev, "%s(): tune done\n",
+ __func__);
+
+ priv->frequency = frequency;
+ return 0;
+}
+
+static int helene_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ if (p->delivery_system == SYS_DVBT ||
+ p->delivery_system == SYS_DVBT2 ||
+ p->delivery_system == SYS_ISDBT ||
+ p->delivery_system == SYS_DVBC_ANNEX_A)
+ return helene_set_params_t(fe);
+
+ return helene_set_params_s(fe);
+}
+
+static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct helene_priv *priv = fe->tuner_priv;
+
+ *frequency = priv->frequency * 1000;
+ return 0;
+}
+
+static const struct dvb_tuner_ops helene_tuner_ops_t = {
+ .info = {
+ .name = "Sony HELENE Ter tuner",
+ .frequency_min_hz = 1 * MHz,
+ .frequency_max_hz = 1200 * MHz,
+ .frequency_step_hz = 25 * kHz,
+ },
+ .init = helene_init,
+ .release = helene_release,
+ .sleep = helene_sleep,
+ .set_params = helene_set_params_t,
+ .get_frequency = helene_get_frequency,
+};
+
+static const struct dvb_tuner_ops helene_tuner_ops_s = {
+ .info = {
+ .name = "Sony HELENE Sat tuner",
+ .frequency_min_hz = 500 * MHz,
+ .frequency_max_hz = 2500 * MHz,
+ .frequency_step_hz = 1 * MHz,
+ },
+ .init = helene_init,
+ .release = helene_release,
+ .sleep = helene_sleep,
+ .set_params = helene_set_params_s,
+ .get_frequency = helene_get_frequency,
+};
+
+static const struct dvb_tuner_ops helene_tuner_ops = {
+ .info = {
+ .name = "Sony HELENE Sat/Ter tuner",
+ .frequency_min_hz = 1 * MHz,
+ .frequency_max_hz = 2500 * MHz,
+ .frequency_step_hz = 25 * kHz,
+ },
+ .init = helene_init,
+ .release = helene_release,
+ .sleep = helene_sleep,
+ .set_params = helene_set_params,
+ .get_frequency = helene_get_frequency,
+};
+
+/* power-on tuner
+ * call once after reset
+ */
+static int helene_x_pon(struct helene_priv *priv)
+{
+ /* RFIN matching in power save (terrestrial) = ACTIVE */
+ /* RFIN matching in power save (satellite) = ACTIVE */
+ u8 dataT[] = { 0x06, 0x00, 0x02, 0x00 };
+ /* SAT_RF_ACTIVE = true, lnaOff = false, terrRfActive = true */
+ u8 dataS[] = { 0x05, 0x06 };
+ u8 cdata[] = {0x7A, 0x01};
+ u8 data[20];
+ u8 rdata[2];
+
+ /* mode select */
+ helene_write_reg(priv, 0x01, 0x00);
+
+ helene_write_reg(priv, 0x67, dataT[3]);
+ helene_write_reg(priv, 0x43, dataS[1]);
+ helene_write_regs(priv, 0x5E, dataT, 3);
+ helene_write_reg(priv, 0x0C, dataS[0]);
+
+ /* Initial setting for internal logic block */
+ helene_write_regs(priv, 0x99, cdata, sizeof(cdata));
+
+ /* 0x81 - 0x94 */
+ if (priv->xtal == SONY_HELENE_XTAL_16000)
+ data[0] = 0x10; /* xtal 16 MHz */
+ else
+ data[0] = 0x18; /* xtal 24 MHz */
+ data[1] = (uint8_t)(0x80 | (0x04 & 0x1F)); /* 4 x 25 = 100uA */
+ data[2] = (uint8_t)(0x80 | (0x26 & 0x7F)); /* 38 x 0.25 = 9.5pF */
+ data[3] = 0x80; /* REFOUT signal output 500mVpp */
+ data[4] = 0x00; /* GPIO settings */
+ data[5] = 0x00; /* GPIO settings */
+ data[6] = 0xC4; /* Clock enable for internal logic block */
+ data[7] = 0x40; /* Start CPU boot-up */
+ data[8] = 0x10; /* For burst-write */
+
+ /* Setting for internal RFAGC */
+ data[9] = 0x00;
+ data[10] = 0x45;
+ data[11] = 0x75;
+
+ data[12] = 0x07; /* Setting for analog block */
+
+ /* Initial setting for internal analog block */
+ data[13] = 0x1C;
+ data[14] = 0x3F;
+ data[15] = 0x02;
+ data[16] = 0x10;
+ data[17] = 0x20;
+ data[18] = 0x0A;
+ data[19] = 0x00;
+
+ helene_write_regs(priv, 0x81, data, sizeof(data));
+
+ /* Setting for internal RFAGC */
+ helene_write_reg(priv, 0x9B, 0x00);
+
+ msleep(20);
+
+ /* Check CPU_STT/CPU_ERR */
+ helene_read_regs(priv, 0x1A, rdata, sizeof(rdata));
+
+ if (rdata[0] != 0x00) {
+ dev_err(&priv->i2c->dev,
+ "HELENE tuner CPU error 0x%x\n", rdata[0]);
+ return -EIO;
+ }
+
+ /* VCO current setting */
+ cdata[0] = 0x90;
+ cdata[1] = 0x06;
+ helene_write_regs(priv, 0x17, cdata, sizeof(cdata));
+ msleep(20);
+ helene_read_reg(priv, 0x19, data);
+ helene_write_reg(priv, 0x95, (uint8_t)((data[0] >> 4) & 0x0F));
+
+ /* Disable IF signal output */
+ helene_write_reg(priv, 0x74, 0x02);
+
+ /* Standby setting for CPU */
+ helene_write_reg(priv, 0x88, 0x00);
+
+ /* Standby setting for internal logic block */
+ helene_write_reg(priv, 0x87, 0xC0);
+
+ /* Load capacitance control setting for crystal oscillator */
+ helene_write_reg(priv, 0x80, 0x01);
+
+ /* Satellite initial setting */
+ cdata[0] = 0x07;
+ cdata[1] = 0x00;
+ helene_write_regs(priv, 0x41, cdata, sizeof(cdata));
+
+ dev_info(&priv->i2c->dev,
+ "HELENE tuner x_pon done\n");
+
+ return 0;
+}
+
+struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe,
+ const struct helene_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct helene_priv *priv = NULL;
+
+ priv = kzalloc(sizeof(struct helene_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+ priv->i2c_address = (config->i2c_address >> 1);
+ priv->i2c = i2c;
+ priv->set_tuner_data = config->set_tuner_priv;
+ priv->set_tuner = config->set_tuner_callback;
+ priv->xtal = config->xtal;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ if (helene_x_pon(priv) != 0) {
+ kfree(priv);
+ return NULL;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_s,
+ sizeof(struct dvb_tuner_ops));
+ fe->tuner_priv = priv;
+ dev_info(&priv->i2c->dev,
+ "Sony HELENE Sat attached on addr=%x at I2C adapter %p\n",
+ priv->i2c_address, priv->i2c);
+ return fe;
+}
+EXPORT_SYMBOL_GPL(helene_attach_s);
+
+struct dvb_frontend *helene_attach(struct dvb_frontend *fe,
+ const struct helene_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct helene_priv *priv = NULL;
+
+ priv = kzalloc(sizeof(struct helene_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+ priv->i2c_address = (config->i2c_address >> 1);
+ priv->i2c = i2c;
+ priv->set_tuner_data = config->set_tuner_priv;
+ priv->set_tuner = config->set_tuner_callback;
+ priv->xtal = config->xtal;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ if (helene_x_pon(priv) != 0) {
+ kfree(priv);
+ return NULL;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_t,
+ sizeof(struct dvb_tuner_ops));
+ fe->tuner_priv = priv;
+ dev_info(&priv->i2c->dev,
+ "Sony HELENE Ter attached on addr=%x at I2C adapter %p\n",
+ priv->i2c_address, priv->i2c);
+ return fe;
+}
+EXPORT_SYMBOL_GPL(helene_attach);
+
+static int helene_probe(struct i2c_client *client)
+{
+ struct helene_config *config = client->dev.platform_data;
+ struct dvb_frontend *fe = config->fe;
+ struct device *dev = &client->dev;
+ struct helene_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->i2c_address = client->addr;
+ priv->i2c = client->adapter;
+ priv->set_tuner_data = config->set_tuner_priv;
+ priv->set_tuner = config->set_tuner_callback;
+ priv->xtal = config->xtal;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ if (helene_x_pon(priv) != 0)
+ return -EINVAL;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ memcpy(&fe->ops.tuner_ops, &helene_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ fe->tuner_priv = priv;
+ i2c_set_clientdata(client, priv);
+
+ dev_info(dev, "Sony HELENE attached on addr=%x at I2C adapter %p\n",
+ priv->i2c_address, priv->i2c);
+
+ return 0;
+}
+
+static const struct i2c_device_id helene_id[] = {
+ { "helene", },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, helene_id);
+
+static struct i2c_driver helene_driver = {
+ .driver = {
+ .name = "helene",
+ },
+ .probe = helene_probe,
+ .id_table = helene_id,
+};
+module_i2c_driver(helene_driver);
+
+MODULE_DESCRIPTION("Sony HELENE Sat/Ter tuner driver");
+MODULE_AUTHOR("Abylay Ospan <aospan@netup.ru>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/helene.h b/drivers/media/dvb-frontends/helene.h
new file mode 100644
index 000000000000..32e0b1fb268c
--- /dev/null
+++ b/drivers/media/dvb-frontends/helene.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * helene.h
+ *
+ * Sony HELENE DVB-S/S2/T/T2/C/C2/ISDB-T/S tuner driver (CXD2858ER)
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
+ */
+
+#ifndef __DVB_HELENE_H__
+#define __DVB_HELENE_H__
+
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+enum helene_xtal {
+ SONY_HELENE_XTAL_16000, /* 16 MHz */
+ SONY_HELENE_XTAL_20500, /* 20.5 MHz */
+ SONY_HELENE_XTAL_24000, /* 24 MHz */
+ SONY_HELENE_XTAL_41000 /* 41 MHz */
+};
+
+/**
+ * struct helene_config - the configuration of 'Helene' tuner driver
+ * @i2c_address: I2C address of the tuner
+ * @xtal_freq_mhz: Oscillator frequency, MHz
+ * @set_tuner_priv: Callback function private context
+ * @set_tuner_callback: Callback function that notifies the parent driver
+ * which tuner is active now
+ * @xtal: Cristal frequency as described by &enum helene_xtal
+ * @fe: Frontend for which connects this tuner
+ */
+struct helene_config {
+ u8 i2c_address;
+ u8 xtal_freq_mhz;
+ void *set_tuner_priv;
+ int (*set_tuner_callback)(void *, int);
+ enum helene_xtal xtal;
+
+ struct dvb_frontend *fe;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_HELENE)
+/**
+ * helene_attach - Attach a helene tuner (terrestrial and cable standards)
+ *
+ * @fe: frontend to be attached
+ * @config: pointer to &struct helene_config with tuner configuration.
+ * @i2c: i2c adapter to use.
+ *
+ * return: FE pointer on success, NULL on failure.
+ */
+extern struct dvb_frontend *helene_attach(struct dvb_frontend *fe,
+ const struct helene_config *config,
+ struct i2c_adapter *i2c);
+
+/**
+ * helene_attach_s - Attach a helene tuner (satellite standards)
+ *
+ * @fe: frontend to be attached
+ * @config: pointer to &struct helene_config with tuner configuration.
+ * @i2c: i2c adapter to use.
+ *
+ * return: FE pointer on success, NULL on failure.
+ */
+extern struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe,
+ const struct helene_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *helene_attach(struct dvb_frontend *fe,
+ const struct helene_config *config,
+ struct i2c_adapter *i2c)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+static inline struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe,
+ const struct helene_config *config,
+ struct i2c_adapter *i2c)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb-frontends/horus3a.c b/drivers/media/dvb-frontends/horus3a.c
new file mode 100644
index 000000000000..0330b78a5b3f
--- /dev/null
+++ b/drivers/media/dvb-frontends/horus3a.c
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * horus3a.h
+ *
+ * Sony Horus3A DVB-S/S2 tuner driver
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dvb/frontend.h>
+#include <linux/types.h>
+#include "horus3a.h"
+#include <media/dvb_frontend.h>
+
+#define MAX_WRITE_REGSIZE 5
+
+enum horus3a_state {
+ STATE_UNKNOWN,
+ STATE_SLEEP,
+ STATE_ACTIVE
+};
+
+struct horus3a_priv {
+ u32 frequency;
+ u8 i2c_address;
+ struct i2c_adapter *i2c;
+ enum horus3a_state state;
+ void *set_tuner_data;
+ int (*set_tuner)(void *, int);
+};
+
+static void horus3a_i2c_debug(struct horus3a_priv *priv,
+ u8 reg, u8 write, const u8 *data, u32 len)
+{
+ dev_dbg(&priv->i2c->dev, "horus3a: I2C %s reg 0x%02x size %d\n",
+ (write == 0 ? "read" : "write"), reg, len);
+ print_hex_dump_bytes("horus3a: I2C data: ",
+ DUMP_PREFIX_OFFSET, data, len);
+}
+
+static int horus3a_write_regs(struct horus3a_priv *priv,
+ u8 reg, const u8 *data, u32 len)
+{
+ int ret;
+ u8 buf[MAX_WRITE_REGSIZE + 1];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->i2c_address,
+ .flags = 0,
+ .len = len + 1,
+ .buf = buf,
+ }
+ };
+
+ if (len + 1 > sizeof(buf)) {
+ dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n",
+ reg, len + 1);
+ return -E2BIG;
+ }
+
+ horus3a_i2c_debug(priv, reg, 1, data, len);
+ buf[0] = reg;
+ memcpy(&buf[1], data, len);
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret >= 0 && ret != 1)
+ ret = -EREMOTEIO;
+ if (ret < 0) {
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c wr failed=%d reg=%02x len=%d\n",
+ KBUILD_MODNAME, ret, reg, len);
+ return ret;
+ }
+ return 0;
+}
+
+static int horus3a_write_reg(struct horus3a_priv *priv, u8 reg, u8 val)
+{
+ u8 tmp = val; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */
+
+ return horus3a_write_regs(priv, reg, &tmp, 1);
+}
+
+static int horus3a_enter_power_save(struct horus3a_priv *priv)
+{
+ u8 data[2];
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state == STATE_SLEEP)
+ return 0;
+ /* IQ Generator disable */
+ horus3a_write_reg(priv, 0x2a, 0x79);
+ /* MDIV_EN = 0 */
+ horus3a_write_reg(priv, 0x29, 0x70);
+ /* VCO disable preparation */
+ horus3a_write_reg(priv, 0x28, 0x3e);
+ /* VCO buffer disable */
+ horus3a_write_reg(priv, 0x2a, 0x19);
+ /* VCO calibration disable */
+ horus3a_write_reg(priv, 0x1c, 0x00);
+ /* Power save setting (xtal is not stopped) */
+ data[0] = 0xC0;
+ /* LNA is Disabled */
+ data[1] = 0xA7;
+ /* 0x11 - 0x12 */
+ horus3a_write_regs(priv, 0x11, data, sizeof(data));
+ priv->state = STATE_SLEEP;
+ return 0;
+}
+
+static int horus3a_leave_power_save(struct horus3a_priv *priv)
+{
+ u8 data[2];
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ if (priv->state == STATE_ACTIVE)
+ return 0;
+ /* Leave power save */
+ data[0] = 0x00;
+ /* LNA is Disabled */
+ data[1] = 0xa7;
+ /* 0x11 - 0x12 */
+ horus3a_write_regs(priv, 0x11, data, sizeof(data));
+ /* VCO buffer enable */
+ horus3a_write_reg(priv, 0x2a, 0x79);
+ /* VCO calibration enable */
+ horus3a_write_reg(priv, 0x1c, 0xc0);
+ /* MDIV_EN = 1 */
+ horus3a_write_reg(priv, 0x29, 0x71);
+ usleep_range(5000, 7000);
+ priv->state = STATE_ACTIVE;
+ return 0;
+}
+
+static int horus3a_init(struct dvb_frontend *fe)
+{
+ struct horus3a_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ return 0;
+}
+
+static void horus3a_release(struct dvb_frontend *fe)
+{
+ struct horus3a_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+}
+
+static int horus3a_sleep(struct dvb_frontend *fe)
+{
+ struct horus3a_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ horus3a_enter_power_save(priv);
+ return 0;
+}
+
+static int horus3a_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct horus3a_priv *priv = fe->tuner_priv;
+ u32 frequency = p->frequency;
+ u32 symbol_rate = p->symbol_rate/1000;
+ u8 mixdiv = 0;
+ u8 mdiv = 0;
+ u32 ms = 0;
+ u8 f_ctl = 0;
+ u8 g_ctl = 0;
+ u8 fc_lpf = 0;
+ u8 data[5];
+
+ dev_dbg(&priv->i2c->dev, "%s(): frequency %dkHz symbol_rate %dksps\n",
+ __func__, frequency, symbol_rate);
+ if (priv->set_tuner)
+ priv->set_tuner(priv->set_tuner_data, 0);
+ if (priv->state == STATE_SLEEP)
+ horus3a_leave_power_save(priv);
+
+ /* frequency should be X MHz (X : integer) */
+ frequency = DIV_ROUND_CLOSEST(frequency, 1000) * 1000;
+ if (frequency <= 1155000) {
+ mixdiv = 4;
+ mdiv = 1;
+ } else {
+ mixdiv = 2;
+ mdiv = 0;
+ }
+ /* Assumed that fREF == 1MHz (1000kHz) */
+ ms = DIV_ROUND_CLOSEST((frequency * mixdiv) / 2, 1000);
+ if (ms > 0x7FFF) { /* 15 bit */
+ dev_err(&priv->i2c->dev, "horus3a: invalid frequency %d\n",
+ frequency);
+ return -EINVAL;
+ }
+ if (frequency < 975000) {
+ /* F_CTL=11100 G_CTL=001 */
+ f_ctl = 0x1C;
+ g_ctl = 0x01;
+ } else if (frequency < 1050000) {
+ /* F_CTL=11000 G_CTL=010 */
+ f_ctl = 0x18;
+ g_ctl = 0x02;
+ } else if (frequency < 1150000) {
+ /* F_CTL=10100 G_CTL=010 */
+ f_ctl = 0x14;
+ g_ctl = 0x02;
+ } else if (frequency < 1250000) {
+ /* F_CTL=10000 G_CTL=011 */
+ f_ctl = 0x10;
+ g_ctl = 0x03;
+ } else if (frequency < 1350000) {
+ /* F_CTL=01100 G_CTL=100 */
+ f_ctl = 0x0C;
+ g_ctl = 0x04;
+ } else if (frequency < 1450000) {
+ /* F_CTL=01010 G_CTL=100 */
+ f_ctl = 0x0A;
+ g_ctl = 0x04;
+ } else if (frequency < 1600000) {
+ /* F_CTL=00111 G_CTL=101 */
+ f_ctl = 0x07;
+ g_ctl = 0x05;
+ } else if (frequency < 1800000) {
+ /* F_CTL=00100 G_CTL=010 */
+ f_ctl = 0x04;
+ g_ctl = 0x02;
+ } else if (frequency < 2000000) {
+ /* F_CTL=00010 G_CTL=001 */
+ f_ctl = 0x02;
+ g_ctl = 0x01;
+ } else {
+ /* F_CTL=00000 G_CTL=000 */
+ f_ctl = 0x00;
+ g_ctl = 0x00;
+ }
+ /* LPF cutoff frequency setting */
+ if (p->delivery_system == SYS_DVBS) {
+ /*
+ * rolloff = 0.35
+ * SR <= 4.3
+ * fc_lpf = 5
+ * 4.3 < SR <= 10
+ * fc_lpf = SR * (1 + rolloff) / 2 + SR / 2 =
+ * SR * 1.175 = SR * (47/40)
+ * 10 < SR
+ * fc_lpf = SR * (1 + rolloff) / 2 + 5 =
+ * SR * 0.675 + 5 = SR * (27/40) + 5
+ * NOTE: The result should be round up.
+ */
+ if (symbol_rate <= 4300)
+ fc_lpf = 5;
+ else if (symbol_rate <= 10000)
+ fc_lpf = (u8)DIV_ROUND_UP(symbol_rate * 47, 40000);
+ else
+ fc_lpf = (u8)DIV_ROUND_UP(symbol_rate * 27, 40000) + 5;
+ /* 5 <= fc_lpf <= 36 */
+ if (fc_lpf > 36)
+ fc_lpf = 36;
+ } else if (p->delivery_system == SYS_DVBS2) {
+ /*
+ * SR <= 4.5:
+ * fc_lpf = 5
+ * 4.5 < SR <= 10:
+ * fc_lpf = SR * (1 + rolloff) / 2 + SR / 2
+ * 10 < SR:
+ * fc_lpf = SR * (1 + rolloff) / 2 + 5
+ * NOTE: The result should be round up.
+ */
+ if (symbol_rate <= 4500)
+ fc_lpf = 5;
+ else if (symbol_rate <= 10000)
+ fc_lpf = (u8)((symbol_rate * 11 + (10000-1)) / 10000);
+ else
+ fc_lpf = (u8)((symbol_rate * 3 + (5000-1)) / 5000 + 5);
+ /* 5 <= fc_lpf <= 36 is valid */
+ if (fc_lpf > 36)
+ fc_lpf = 36;
+ } else {
+ dev_err(&priv->i2c->dev,
+ "horus3a: invalid delivery system %d\n",
+ p->delivery_system);
+ return -EINVAL;
+ }
+ /* 0x00 - 0x04 */
+ data[0] = (u8)((ms >> 7) & 0xFF);
+ data[1] = (u8)((ms << 1) & 0xFF);
+ data[2] = 0x00;
+ data[3] = 0x00;
+ data[4] = (u8)(mdiv << 7);
+ horus3a_write_regs(priv, 0x00, data, sizeof(data));
+ /* Write G_CTL, F_CTL */
+ horus3a_write_reg(priv, 0x09, (u8)((g_ctl << 5) | f_ctl));
+ /* Write LPF cutoff frequency */
+ horus3a_write_reg(priv, 0x37, (u8)(0x80 | (fc_lpf << 1)));
+ /* Start Calibration */
+ horus3a_write_reg(priv, 0x05, 0x80);
+ /* IQ Generator enable */
+ horus3a_write_reg(priv, 0x2a, 0x7b);
+ /* tuner stabilization time */
+ msleep(60);
+ /* Store tuned frequency to the struct */
+ priv->frequency = ms * 2 * 1000 / mixdiv;
+ return 0;
+}
+
+static int horus3a_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct horus3a_priv *priv = fe->tuner_priv;
+
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static const struct dvb_tuner_ops horus3a_tuner_ops = {
+ .info = {
+ .name = "Sony Horus3a",
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
+ .frequency_step_hz = 1 * MHz,
+ },
+ .init = horus3a_init,
+ .release = horus3a_release,
+ .sleep = horus3a_sleep,
+ .set_params = horus3a_set_params,
+ .get_frequency = horus3a_get_frequency,
+};
+
+struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe,
+ const struct horus3a_config *config,
+ struct i2c_adapter *i2c)
+{
+ u8 buf[3], val;
+ struct horus3a_priv *priv = NULL;
+
+ priv = kzalloc(sizeof(struct horus3a_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+ priv->i2c_address = (config->i2c_address >> 1);
+ priv->i2c = i2c;
+ priv->set_tuner_data = config->set_tuner_priv;
+ priv->set_tuner = config->set_tuner_callback;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ /* wait 4ms after power on */
+ usleep_range(4000, 6000);
+ /* IQ Generator disable */
+ horus3a_write_reg(priv, 0x2a, 0x79);
+ /* REF_R = Xtal Frequency */
+ buf[0] = config->xtal_freq_mhz;
+ buf[1] = config->xtal_freq_mhz;
+ buf[2] = 0;
+ /* 0x6 - 0x8 */
+ horus3a_write_regs(priv, 0x6, buf, 3);
+ /* IQ Out = Single Ended */
+ horus3a_write_reg(priv, 0x0a, 0x40);
+ switch (config->xtal_freq_mhz) {
+ case 27:
+ val = 0x1f;
+ break;
+ case 24:
+ val = 0x10;
+ break;
+ case 16:
+ val = 0xc;
+ break;
+ default:
+ val = 0;
+ dev_warn(&priv->i2c->dev,
+ "horus3a: invalid xtal frequency %dMHz\n",
+ config->xtal_freq_mhz);
+ break;
+ }
+ val <<= 2;
+ horus3a_write_reg(priv, 0x0e, val);
+ horus3a_enter_power_save(priv);
+ usleep_range(3000, 5000);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ memcpy(&fe->ops.tuner_ops, &horus3a_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ fe->tuner_priv = priv;
+ dev_info(&priv->i2c->dev,
+ "Sony HORUS3A attached on addr=%x at I2C adapter %p\n",
+ priv->i2c_address, priv->i2c);
+ return fe;
+}
+EXPORT_SYMBOL_GPL(horus3a_attach);
+
+MODULE_DESCRIPTION("Sony HORUS3A satellite tuner driver");
+MODULE_AUTHOR("Sergey Kozlov <serjk@netup.ru>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/horus3a.h b/drivers/media/dvb-frontends/horus3a.h
new file mode 100644
index 000000000000..91dbe20169cd
--- /dev/null
+++ b/drivers/media/dvb-frontends/horus3a.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * horus3a.h
+ *
+ * Sony Horus3A DVB-S/S2 tuner driver
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
+ */
+
+#ifndef __DVB_HORUS3A_H__
+#define __DVB_HORUS3A_H__
+
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+/**
+ * struct horus3a_config - the configuration of Horus3A tuner driver
+ * @i2c_address: I2C address of the tuner
+ * @xtal_freq_mhz: Oscillator frequency, MHz
+ * @set_tuner_priv: Callback function private context
+ * @set_tuner_callback: Callback function that notifies the parent driver
+ * which tuner is active now
+ */
+struct horus3a_config {
+ u8 i2c_address;
+ u8 xtal_freq_mhz;
+ void *set_tuner_priv;
+ int (*set_tuner_callback)(void *, int);
+};
+
+#if IS_REACHABLE(CONFIG_DVB_HORUS3A)
+/**
+ * horus3a_attach - Attach a horus3a tuner
+ *
+ * @fe: frontend to be attached
+ * @config: pointer to &struct helene_config with tuner configuration.
+ * @i2c: i2c adapter to use.
+ *
+ * return: FE pointer on success, NULL on failure.
+ */
+extern struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe,
+ const struct horus3a_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe,
+ const struct horus3a_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/frontends/isl6405.c b/drivers/media/dvb-frontends/isl6405.c
index 33d33f4d8867..7d28a743f97e 100644
--- a/drivers/media/dvb/frontends/isl6405.c
+++ b/drivers/media/dvb-frontends/isl6405.c
@@ -1,28 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* isl6405.c - driver for dual lnb supply and control ic ISL6405
*
* Copyright (C) 2008 Hartmut Hackmann
* Copyright (C) 2006 Oliver Endriss
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- *
- *
- * the project's page is at http://www.linuxtv.org
+ * the project's page is at https://linuxtv.org
*/
#include <linux/delay.h>
#include <linux/errno.h>
@@ -32,7 +15,7 @@
#include <linux/string.h>
#include <linux/slab.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "isl6405.h"
struct isl6405 {
@@ -43,7 +26,8 @@ struct isl6405 {
u8 i2c_addr;
};
-static int isl6405_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+static int isl6405_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage)
{
struct isl6405 *isl6405 = (struct isl6405 *) fe->sec_priv;
struct i2c_msg msg = { .addr = isl6405->i2c_addr, .flags = 0,
@@ -77,7 +61,7 @@ static int isl6405_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage
break;
default:
return -EINVAL;
- };
+ }
}
isl6405->config |= isl6405->override_or;
isl6405->config &= isl6405->override_and;
@@ -157,7 +141,7 @@ struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe, struct i2c_adapter
return fe;
}
-EXPORT_SYMBOL(isl6405_attach);
+EXPORT_SYMBOL_GPL(isl6405_attach);
MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6405");
MODULE_AUTHOR("Hartmut Hackmann & Oliver Endriss");
diff --git a/drivers/media/dvb-frontends/isl6405.h b/drivers/media/dvb-frontends/isl6405.h
new file mode 100644
index 000000000000..c1cb131d2482
--- /dev/null
+++ b/drivers/media/dvb-frontends/isl6405.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * isl6405.h - driver for dual lnb supply and control ic ISL6405
+ *
+ * Copyright (C) 2008 Hartmut Hackmann
+ * Copyright (C) 2006 Oliver Endriss
+ *
+ * the project's page is at https://linuxtv.org
+ */
+
+#ifndef _ISL6405_H
+#define _ISL6405_H
+
+#include <linux/dvb/frontend.h>
+
+/* system register bits */
+
+/* this bit selects register (control) 1 or 2
+ note that the bit maps are different */
+
+#define ISL6405_SR 0x80
+
+/* SR = 0 */
+#define ISL6405_OLF1 0x01
+#define ISL6405_EN1 0x02
+#define ISL6405_VSEL1 0x04
+#define ISL6405_LLC1 0x08
+#define ISL6405_ENT1 0x10
+#define ISL6405_ISEL1 0x20
+#define ISL6405_DCL 0x40
+
+/* SR = 1 */
+#define ISL6405_OLF2 0x01
+#define ISL6405_OTF 0x02
+#define ISL6405_EN2 0x04
+#define ISL6405_VSEL2 0x08
+#define ISL6405_LLC2 0x10
+#define ISL6405_ENT2 0x20
+#define ISL6405_ISEL2 0x40
+
+#if IS_REACHABLE(CONFIG_DVB_ISL6405)
+/* override_set and override_clear control which system register bits (above)
+ * to always set & clear
+ */
+extern struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c,
+ u8 i2c_addr, u8 override_set, u8 override_clear);
+#else
+static inline struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, u8 i2c_addr,
+ u8 override_set, u8 override_clear)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_ISL6405 */
+
+#endif
diff --git a/drivers/media/dvb-frontends/isl6421.c b/drivers/media/dvb-frontends/isl6421.c
new file mode 100644
index 000000000000..2e9f6f12f849
--- /dev/null
+++ b/drivers/media/dvb-frontends/isl6421.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * isl6421.h - driver for lnb supply and control ic ISL6421
+ *
+ * Copyright (C) 2006 Andrew de Quincey
+ * Copyright (C) 2006 Oliver Endriss
+ *
+ * the project's page is at https://linuxtv.org
+ */
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include <media/dvb_frontend.h>
+#include "isl6421.h"
+
+struct isl6421 {
+ u8 config;
+ u8 override_or;
+ u8 override_and;
+ struct i2c_adapter *i2c;
+ u8 i2c_addr;
+ bool is_off;
+};
+
+static int isl6421_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage)
+{
+ int ret;
+ u8 buf;
+ bool is_off;
+ struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = isl6421->i2c_addr,
+ .flags = 0,
+ .buf = &isl6421->config,
+ .len = 1,
+ }, {
+ .addr = isl6421->i2c_addr,
+ .flags = I2C_M_RD,
+ .buf = &buf,
+ .len = 1,
+ }
+
+ };
+
+ isl6421->config &= ~(ISL6421_VSEL1 | ISL6421_EN1);
+
+ switch(voltage) {
+ case SEC_VOLTAGE_OFF:
+ is_off = true;
+ break;
+ case SEC_VOLTAGE_13:
+ is_off = false;
+ isl6421->config |= ISL6421_EN1;
+ break;
+ case SEC_VOLTAGE_18:
+ is_off = false;
+ isl6421->config |= (ISL6421_EN1 | ISL6421_VSEL1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * If LNBf were not powered on, disable dynamic current limit, as,
+ * according with datasheet, highly capacitive load on the output may
+ * cause a difficult start-up.
+ */
+ if (isl6421->is_off && !is_off)
+ isl6421->config |= ISL6421_DCL;
+
+ isl6421->config |= isl6421->override_or;
+ isl6421->config &= isl6421->override_and;
+
+ ret = i2c_transfer(isl6421->i2c, msg, 2);
+ if (ret < 0)
+ return ret;
+ if (ret != 2)
+ return -EIO;
+
+ /* Store off status now in case future commands fail */
+ isl6421->is_off = is_off;
+
+ /* On overflow, the device will try again after 900 ms (typically) */
+ if (!is_off && (buf & ISL6421_OLF1))
+ msleep(1000);
+
+ /* Re-enable dynamic current limit */
+ if ((isl6421->config & ISL6421_DCL) &&
+ !(isl6421->override_or & ISL6421_DCL)) {
+ isl6421->config &= ~ISL6421_DCL;
+
+ ret = i2c_transfer(isl6421->i2c, msg, 2);
+ if (ret < 0)
+ return ret;
+ if (ret != 2)
+ return -EIO;
+ }
+
+ /* Check if overload flag is active. If so, disable power */
+ if (!is_off && (buf & ISL6421_OLF1)) {
+ isl6421->config &= ~(ISL6421_VSEL1 | ISL6421_EN1);
+ ret = i2c_transfer(isl6421->i2c, msg, 1);
+ if (ret < 0)
+ return ret;
+ if (ret != 1)
+ return -EIO;
+ isl6421->is_off = true;
+
+ dev_warn(&isl6421->i2c->dev,
+ "Overload current detected. disabling LNBf power\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
+{
+ struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
+ struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0,
+ .buf = &isl6421->config,
+ .len = sizeof(isl6421->config) };
+
+ if (arg)
+ isl6421->config |= ISL6421_LLC1;
+ else
+ isl6421->config &= ~ISL6421_LLC1;
+
+ isl6421->config |= isl6421->override_or;
+ isl6421->config &= isl6421->override_and;
+
+ return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO;
+}
+
+static int isl6421_set_tone(struct dvb_frontend *fe,
+ enum fe_sec_tone_mode tone)
+{
+ struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
+ struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0,
+ .buf = &isl6421->config,
+ .len = sizeof(isl6421->config) };
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ isl6421->config |= ISL6421_ENT1;
+ break;
+ case SEC_TONE_OFF:
+ isl6421->config &= ~ISL6421_ENT1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ isl6421->config |= isl6421->override_or;
+ isl6421->config &= isl6421->override_and;
+
+ return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO;
+}
+
+static void isl6421_release(struct dvb_frontend *fe)
+{
+ /* power off */
+ isl6421_set_voltage(fe, SEC_VOLTAGE_OFF);
+
+ /* free */
+ kfree(fe->sec_priv);
+ fe->sec_priv = NULL;
+}
+
+struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
+ u8 override_set, u8 override_clear, bool override_tone)
+{
+ struct isl6421 *isl6421 = kmalloc(sizeof(struct isl6421), GFP_KERNEL);
+ if (!isl6421)
+ return NULL;
+
+ /* default configuration */
+ isl6421->config = ISL6421_ISEL1;
+ isl6421->i2c = i2c;
+ isl6421->i2c_addr = i2c_addr;
+ fe->sec_priv = isl6421;
+
+ /* bits which should be forced to '1' */
+ isl6421->override_or = override_set;
+
+ /* bits which should be forced to '0' */
+ isl6421->override_and = ~override_clear;
+
+ /* detect if it is present or not */
+ if (isl6421_set_voltage(fe, SEC_VOLTAGE_OFF)) {
+ kfree(isl6421);
+ fe->sec_priv = NULL;
+ return NULL;
+ }
+
+ isl6421->is_off = true;
+
+ /* install release callback */
+ fe->ops.release_sec = isl6421_release;
+
+ /* override frontend ops */
+ fe->ops.set_voltage = isl6421_set_voltage;
+ fe->ops.enable_high_lnb_voltage = isl6421_enable_high_lnb_voltage;
+ if (override_tone)
+ fe->ops.set_tone = isl6421_set_tone;
+
+ return fe;
+}
+EXPORT_SYMBOL_GPL(isl6421_attach);
+
+MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6421");
+MODULE_AUTHOR("Andrew de Quincey & Oliver Endriss");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/isl6421.h b/drivers/media/dvb-frontends/isl6421.h
new file mode 100644
index 000000000000..e594877849de
--- /dev/null
+++ b/drivers/media/dvb-frontends/isl6421.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * isl6421.h - driver for lnb supply and control ic ISL6421
+ *
+ * Copyright (C) 2006 Andrew de Quincey
+ * Copyright (C) 2006 Oliver Endriss
+ *
+ * the project's page is at https://linuxtv.org
+ */
+
+#ifndef _ISL6421_H
+#define _ISL6421_H
+
+#include <linux/dvb/frontend.h>
+
+/* system register bits */
+#define ISL6421_OLF1 0x01
+#define ISL6421_EN1 0x02
+#define ISL6421_VSEL1 0x04
+#define ISL6421_LLC1 0x08
+#define ISL6421_ENT1 0x10
+#define ISL6421_ISEL1 0x20
+#define ISL6421_DCL 0x40
+
+#if IS_REACHABLE(CONFIG_DVB_ISL6421)
+/* override_set and override_clear control which system register bits (above) to always set & clear */
+extern struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
+ u8 override_set, u8 override_clear, bool override_tone);
+#else
+static inline struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
+ u8 override_set, u8 override_clear, bool override_tone)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_ISL6421
+
+#endif
diff --git a/drivers/media/dvb/frontends/isl6423.c b/drivers/media/dvb-frontends/isl6423.c
index dca5bebfeeb5..a0d0a3834057 100644
--- a/drivers/media/dvb/frontends/isl6423.c
+++ b/drivers/media/dvb-frontends/isl6423.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Intersil ISL6423 SEC and LNB Power supply controller
Copyright (C) Manu Abraham <abraham.manu@gmail.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/delay.h>
@@ -26,7 +14,7 @@
#include <linux/string.h>
#include <linux/slab.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "isl6423.h"
static unsigned int verbose;
@@ -301,7 +289,7 @@ exit:
fe->sec_priv = NULL;
return NULL;
}
-EXPORT_SYMBOL(isl6423_attach);
+EXPORT_SYMBOL_GPL(isl6423_attach);
MODULE_DESCRIPTION("ISL6423 SEC");
MODULE_AUTHOR("Manu Abraham");
diff --git a/drivers/media/dvb-frontends/isl6423.h b/drivers/media/dvb-frontends/isl6423.h
new file mode 100644
index 000000000000..9fa87ceffd24
--- /dev/null
+++ b/drivers/media/dvb-frontends/isl6423.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Intersil ISL6423 SEC and LNB Power supply controller
+
+ Copyright (C) Manu Abraham <abraham.manu@gmail.com>
+
+*/
+
+#ifndef __ISL_6423_H
+#define __ISL_6423_H
+
+#include <linux/dvb/frontend.h>
+
+enum isl6423_current {
+ SEC_CURRENT_275m = 0,
+ SEC_CURRENT_515m,
+ SEC_CURRENT_635m,
+ SEC_CURRENT_800m,
+};
+
+enum isl6423_curlim {
+ SEC_CURRENT_LIM_ON = 1,
+ SEC_CURRENT_LIM_OFF
+};
+
+struct isl6423_config {
+ enum isl6423_current current_max;
+ enum isl6423_curlim curlim;
+ u8 addr;
+ u8 mod_extern;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_ISL6423)
+
+
+extern struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ const struct isl6423_config *config);
+
+#else
+static inline struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ const struct isl6423_config *config)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+#endif /* CONFIG_DVB_ISL6423 */
+
+#endif /* __ISL_6423_H */
diff --git a/drivers/media/dvb/frontends/itd1000.c b/drivers/media/dvb-frontends/itd1000.c
index 316457584fe7..f8f362f50e78 100644
--- a/drivers/media/dvb/frontends/itd1000.c
+++ b/drivers/media/dvb-frontends/itd1000.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite"
*
* Copyright (c) 2007-8 Patrick Boettcher <pb@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
*/
#include <linux/module.h>
@@ -26,11 +12,14 @@
#include <linux/i2c.h>
#include <linux/slab.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "itd1000.h"
#include "itd1000_priv.h"
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE 64
+
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
@@ -52,10 +41,18 @@ MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
/* don't write more than one byte with flexcop behind */
static int itd1000_write_regs(struct itd1000_state *state, u8 reg, u8 v[], u8 len)
{
- u8 buf[1+len];
+ u8 buf[MAX_XFER_SIZE];
struct i2c_msg msg = {
.addr = state->cfg->i2c_address, .flags = 0, .buf = buf, .len = len+1
};
+
+ if (1 + len > sizeof(buf)) {
+ printk(KERN_WARNING
+ "itd1000: i2c wr reg=%04x: len=%d is too big!\n",
+ reg, len);
+ return -EINVAL;
+ }
+
buf[0] = reg;
memcpy(&buf[1], v, len);
@@ -88,8 +85,9 @@ static int itd1000_read_reg(struct itd1000_state *state, u8 reg)
static inline int itd1000_write_reg(struct itd1000_state *state, u8 r, u8 v)
{
- int ret = itd1000_write_regs(state, r, &v, 1);
- state->shadow[r] = v;
+ u8 tmp = v; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */
+ int ret = itd1000_write_regs(state, r, &tmp, 1);
+ state->shadow[r] = tmp;
return ret;
}
@@ -231,7 +229,7 @@ static void itd1000_set_lo(struct itd1000_state *state, u32 freq_khz)
state->frequency = ((plln * 1000) + (pllf * 1000)/1048576) * 2*FREF;
itd_dbg("frequency: %dkHz (wanted) %dkHz (set), PLLF = %d, PLLN = %d\n", freq_khz, state->frequency, pllf, plln);
- itd1000_write_reg(state, PLLNH, 0x80); /* PLLNH */;
+ itd1000_write_reg(state, PLLNH, 0x80); /* PLLNH */
itd1000_write_reg(state, PLLNL, plln & 0xff);
itd1000_write_reg(state, PLLFH, (itd1000_read_reg(state, PLLFH) & 0xf0) | ((pllf >> 16) & 0x0f));
itd1000_write_reg(state, PLLFM, (pllf >> 8) & 0xff);
@@ -337,19 +335,18 @@ static int itd1000_sleep(struct dvb_frontend *fe)
return 0;
}
-static int itd1000_release(struct dvb_frontend *fe)
+static void itd1000_release(struct dvb_frontend *fe)
{
kfree(fe->tuner_priv);
fe->tuner_priv = NULL;
- return 0;
}
static const struct dvb_tuner_ops itd1000_tuner_ops = {
.info = {
- .name = "Integrant ITD1000",
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .frequency_step = 125, /* kHz for QPSK frontends */
+ .name = "Integrant ITD1000",
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
+ .frequency_step_hz = 125 * kHz,
},
.release = itd1000_release,
@@ -392,7 +389,7 @@ struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter
return fe;
}
-EXPORT_SYMBOL(itd1000_attach);
+EXPORT_SYMBOL_GPL(itd1000_attach);
MODULE_AUTHOR("Patrick Boettcher <pb@linuxtv.org>");
MODULE_DESCRIPTION("Integrant ITD1000 driver");
diff --git a/drivers/media/dvb-frontends/itd1000.h b/drivers/media/dvb-frontends/itd1000.h
new file mode 100644
index 000000000000..3539c69ce20a
--- /dev/null
+++ b/drivers/media/dvb-frontends/itd1000.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite"
+ *
+ * Copyright (c) 2007 Patrick Boettcher <pb@linuxtv.org>
+ */
+
+#ifndef ITD1000_H
+#define ITD1000_H
+
+struct dvb_frontend;
+struct i2c_adapter;
+
+struct itd1000_config {
+ u8 i2c_address;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_TUNER_ITD1000)
+extern struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg);
+#else
+static inline struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb-frontends/itd1000_priv.h b/drivers/media/dvb-frontends/itd1000_priv.h
new file mode 100644
index 000000000000..f33157d2ec23
--- /dev/null
+++ b/drivers/media/dvb-frontends/itd1000_priv.h
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite"
+ *
+ * Copyright (c) 2007 Patrick Boettcher <pb@linuxtv.org>
+ */
+
+#ifndef ITD1000_PRIV_H
+#define ITD1000_PRIV_H
+
+struct itd1000_state {
+ struct itd1000_config *cfg;
+ struct i2c_adapter *i2c;
+
+ u32 frequency; /* contains the value resulting from the LO-setting */
+
+ /* ugly workaround for flexcop's incapable i2c-controller
+ * FIXME, if possible
+ */
+ u8 shadow[256];
+};
+
+enum itd1000_register {
+ VCO_CHP1 = 0x65,
+ VCO_CHP2,
+ PLLCON1,
+ PLLNH,
+ PLLNL,
+ PLLFH,
+ PLLFM,
+ PLLFL,
+ RESERVED_0X6D,
+ PLLLOCK,
+ VCO_CHP2_I2C,
+ VCO_CHP1_I2C,
+ BW,
+ RESERVED_0X73 = 0x73,
+ RESERVED_0X74,
+ RESERVED_0X75,
+ GVBB,
+ GVRF,
+ GVBB_I2C,
+ EXTGVBBRF,
+ DIVAGCCK,
+ BBTR,
+ RFTR,
+ BBGVMIN,
+ RESERVED_0X7E,
+ RESERVED_0X85 = 0x85,
+ RESERVED_0X86,
+ CON1,
+ RESERVED_0X88,
+ RESERVED_0X89,
+ RFST0,
+ RFST1,
+ RFST2,
+ RFST3,
+ RFST4,
+ RFST5,
+ RFST6,
+ RFST7,
+ RFST8,
+ RFST9,
+ RESERVED_0X94,
+ RESERVED_0X95,
+ RESERVED_0X96,
+ RESERVED_0X97,
+ RESERVED_0X98,
+ RESERVED_0X99,
+ RESERVED_0X9A,
+ RESERVED_0X9B,
+};
+
+#endif
diff --git a/drivers/media/dvb/frontends/ix2505v.c b/drivers/media/dvb-frontends/ix2505v.c
index bc5a82082aaa..3212e333d472 100644
--- a/drivers/media/dvb/frontends/ix2505v.c
+++ b/drivers/media/dvb-frontends/ix2505v.c
@@ -1,21 +1,8 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Driver for Sharp IX2505V (marked B0017) DVB-S silicon tuner
*
* Copyright (C) 2010 Malcolm Priestley
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2, as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
#include <linux/module.h>
@@ -40,7 +27,7 @@ struct ix2505v_state {
u32 frequency;
};
-/**
+/*
* Data read format of the Sharp IX2505V B0017
*
* byte1: 1 | 1 | 0 | 0 | 0 | MA1 | MA0 | 1
@@ -94,17 +81,16 @@ static int ix2505v_write(struct ix2505v_state *state, u8 buf[], u8 count)
return 0;
}
-static int ix2505v_release(struct dvb_frontend *fe)
+static void ix2505v_release(struct dvb_frontend *fe)
{
struct ix2505v_state *state = fe->tuner_priv;
fe->tuner_priv = NULL;
kfree(state);
- return 0;
}
-/**
+/*
* Data write format of the Sharp IX2505V B0017
*
* byte1: 1 | 1 | 0 | 0 | 0 | 0(MA1)| 0(MA0)| 0
@@ -140,8 +126,8 @@ static int ix2505v_set_params(struct dvb_frontend *fe)
u8 gain, cc, ref, psc, local_osc, lpf;
u8 data[4] = {0};
- if ((frequency < fe->ops.info.frequency_min)
- || (frequency > fe->ops.info.frequency_max))
+ if ((frequency < fe->ops.info.frequency_min_hz / kHz)
+ || (frequency > fe->ops.info.frequency_max_hz / kHz))
return -EINVAL;
if (state->config->tuner_gain)
@@ -212,7 +198,7 @@ static int ix2505v_set_params(struct dvb_frontend *fe)
lpf = 0xb;
deb_info("Osc=%x b_w=%x lpf=%x\n", local_osc, b_w, lpf);
- deb_info("Data 0=[%x%x%x%x]\n", data[0], data[1], data[2], data[3]);
+ deb_info("Data 0=[%4phN]\n", data);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
@@ -258,11 +244,11 @@ static int ix2505v_get_frequency(struct dvb_frontend *fe, u32 *frequency)
return 0;
}
-static struct dvb_tuner_ops ix2505v_tuner_ops = {
+static const struct dvb_tuner_ops ix2505v_tuner_ops = {
.info = {
.name = "Sharp IX2505V (B0017)",
- .frequency_min = 950000,
- .frequency_max = 2175000
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2175 * MHz
},
.release = ix2505v_release,
.set_params = ix2505v_set_params,
@@ -316,7 +302,7 @@ error:
kfree(state);
return NULL;
}
-EXPORT_SYMBOL(ix2505v_attach);
+EXPORT_SYMBOL_GPL(ix2505v_attach);
module_param_named(debug, ix2505v_debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
diff --git a/drivers/media/dvb-frontends/ix2505v.h b/drivers/media/dvb-frontends/ix2505v.h
new file mode 100644
index 000000000000..175569131365
--- /dev/null
+++ b/drivers/media/dvb-frontends/ix2505v.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Driver for Sharp IX2505V (marked B0017) DVB-S silicon tuner
+ *
+ * Copyright (C) 2010 Malcolm Priestley
+ */
+
+#ifndef DVB_IX2505V_H
+#define DVB_IX2505V_H
+
+#include <linux/i2c.h>
+#include <media/dvb_frontend.h>
+
+/**
+ * struct ix2505v_config - ix2505 attachment configuration
+ *
+ * @tuner_address: tuner address
+ * @tuner_gain: Baseband AMP gain control 0/1=0dB(default) 2=-2bB 3=-4dB
+ * @tuner_chargepump: Charge pump output +/- 0=120 1=260 2=555 3=1200(default)
+ * @min_delay_ms: delay after tune
+ * @tuner_write_only: disables reads
+ */
+struct ix2505v_config {
+ u8 tuner_address;
+ u8 tuner_gain;
+ u8 tuner_chargepump;
+ int min_delay_ms;
+ u8 tuner_write_only;
+
+};
+
+#if IS_REACHABLE(CONFIG_DVB_IX2505V)
+/**
+ * ix2505v_attach - Attach a ix2505v tuner to the supplied frontend structure.
+ *
+ * @fe: Frontend to attach to.
+ * @config: pointer to &struct ix2505v_config
+ * @i2c: pointer to &struct i2c_adapter.
+ *
+ * return: FE pointer on success, NULL on failure.
+ */
+extern struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe,
+ const struct ix2505v_config *config, struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe,
+ const struct ix2505v_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* DVB_IX2505V_H */
diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb-frontends/l64781.c
index 36fcf559e361..fe5af2453d55 100644
--- a/drivers/media/dvb/frontends/l64781.c
+++ b/drivers/media/dvb-frontends/l64781.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
driver for LSI L64781 COFDM demodulator
Copyright (C) 2001 Holger Waechtler for Convergence Integrated Media GmbH
Marko Kohtala <marko.kohtala@luukku.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
@@ -25,7 +13,7 @@
#include <linux/module.h>
#include <linux/string.h>
#include <linux/slab.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "l64781.h"
@@ -180,11 +168,11 @@ static int apply_frontend_param(struct dvb_frontend *fe)
p->transmission_mode != TRANSMISSION_MODE_8K)
return -EINVAL;
- if (p->guard_interval < GUARD_INTERVAL_1_32 ||
+ if ((int)p->guard_interval < GUARD_INTERVAL_1_32 ||
p->guard_interval > GUARD_INTERVAL_1_4)
return -EINVAL;
- if (p->hierarchy < HIERARCHY_NONE ||
+ if ((int)p->hierarchy < HIERARCHY_NONE ||
p->hierarchy > HIERARCHY_4)
return -EINVAL;
@@ -243,9 +231,9 @@ static int apply_frontend_param(struct dvb_frontend *fe)
return 0;
}
-static int get_frontend(struct dvb_frontend *fe)
+static int get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct l64781_state* state = fe->demodulator_priv;
int tmp;
@@ -359,7 +347,7 @@ static int get_frontend(struct dvb_frontend *fe)
return 0;
}
-static int l64781_read_status(struct dvb_frontend* fe, fe_status_t* status)
+static int l64781_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct l64781_state* state = fe->demodulator_priv;
int sync = l64781_readreg (state, 0x32);
@@ -496,7 +484,7 @@ static void l64781_release(struct dvb_frontend* fe)
kfree(state);
}
-static struct dvb_frontend_ops l64781_ops;
+static const struct dvb_frontend_ops l64781_ops;
struct dvb_frontend* l64781_attach(const struct l64781_config* config,
struct i2c_adapter* i2c)
@@ -517,7 +505,7 @@ struct dvb_frontend* l64781_attach(const struct l64781_config* config,
state->i2c = i2c;
state->first = 1;
- /**
+ /*
* the L64781 won't show up before we send the reset_and_configure()
* broadcast. If nothing responds there is no L64781 on the bus...
*/
@@ -546,7 +534,7 @@ struct dvb_frontend* l64781_attach(const struct l64781_config* config,
/* Responds to all reads with 0 */
if (l64781_readreg(state, 0x1a) != 0) {
- dprintk("Read 1 returned unexpcted value\n");
+ dprintk("Read 1 returned unexpected value\n");
goto error;
}
@@ -555,7 +543,7 @@ struct dvb_frontend* l64781_attach(const struct l64781_config* config,
/* Responds with register default value */
if (l64781_readreg(state, 0x1a) != 0xa1) {
- dprintk("Read 2 returned unexpcted value\n");
+ dprintk("Read 2 returned unexpected value\n");
goto error;
}
@@ -571,14 +559,13 @@ error:
return NULL;
}
-static struct dvb_frontend_ops l64781_ops = {
+static const struct dvb_frontend_ops l64781_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = "LSI L64781 DVB-T",
- /* .frequency_min = ???,*/
- /* .frequency_max = ???,*/
- .frequency_stepsize = 166666,
- /* .frequency_tolerance = ???,*/
+ /* .frequency_min_hz = ???,*/
+ /* .frequency_max_hz = ???,*/
+ .frequency_stepsize_hz = 166666,
/* .symbol_rate_tolerance = ???,*/
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
@@ -606,4 +593,4 @@ MODULE_DESCRIPTION("LSI L64781 DVB-T Demodulator driver");
MODULE_AUTHOR("Holger Waechtler, Marko Kohtala");
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(l64781_attach);
+EXPORT_SYMBOL_GPL(l64781_attach);
diff --git a/drivers/media/dvb-frontends/l64781.h b/drivers/media/dvb-frontends/l64781.h
new file mode 100644
index 000000000000..41d55f627fd2
--- /dev/null
+++ b/drivers/media/dvb-frontends/l64781.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ driver for LSI L64781 COFDM demodulator
+
+ Copyright (C) 2001 Holger Waechtler for Convergence Integrated Media GmbH
+ Marko Kohtala <marko.kohtala@luukku.com>
+
+
+*/
+
+#ifndef L64781_H
+#define L64781_H
+
+#include <linux/dvb/frontend.h>
+
+struct l64781_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_L64781)
+extern struct dvb_frontend* l64781_attach(const struct l64781_config* config,
+ struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* l64781_attach(const struct l64781_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_L64781
+
+#endif // L64781_H
diff --git a/drivers/media/dvb/frontends/lg2160.c b/drivers/media/dvb-frontends/lg2160.c
index cc11260e99df..fe700aa56bff 100644
--- a/drivers/media/dvb/frontends/lg2160.c
+++ b/drivers/media/dvb-frontends/lg2160.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Support for LG2160 - ATSC/MH
*
* Copyright (C) 2010 Michael Krufky <mkrufky@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
#include <linux/jiffies.h>
@@ -942,101 +928,102 @@ static int lg216x_read_rs_err_count(struct lg216x_state *state, u16 *err)
/* ------------------------------------------------------------------------ */
-static int lg216x_get_frontend(struct dvb_frontend *fe)
+static int lg216x_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
{
struct lg216x_state *state = fe->demodulator_priv;
int ret;
lg_dbg("\n");
- fe->dtv_property_cache.modulation = VSB_8;
- fe->dtv_property_cache.frequency = state->current_frequency;
- fe->dtv_property_cache.delivery_system = SYS_ATSCMH;
+ c->modulation = VSB_8;
+ c->frequency = state->current_frequency;
+ c->delivery_system = SYS_ATSCMH;
ret = lg216x_get_fic_version(state,
- &fe->dtv_property_cache.atscmh_fic_ver);
+ &c->atscmh_fic_ver);
if (lg_fail(ret))
goto fail;
- if (state->fic_ver != fe->dtv_property_cache.atscmh_fic_ver) {
- state->fic_ver = fe->dtv_property_cache.atscmh_fic_ver;
+ if (state->fic_ver != c->atscmh_fic_ver) {
+ state->fic_ver = c->atscmh_fic_ver;
#if 0
ret = lg2160_get_parade_id(state,
- &fe->dtv_property_cache.atscmh_parade_id);
+ &c->atscmh_parade_id);
if (lg_fail(ret))
goto fail;
/* #else */
- fe->dtv_property_cache.atscmh_parade_id = state->parade_id;
+ c->atscmh_parade_id = state->parade_id;
#endif
ret = lg216x_get_nog(state,
- &fe->dtv_property_cache.atscmh_nog);
+ &c->atscmh_nog);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_tnog(state,
- &fe->dtv_property_cache.atscmh_tnog);
+ &c->atscmh_tnog);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_sgn(state,
- &fe->dtv_property_cache.atscmh_sgn);
+ &c->atscmh_sgn);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_prc(state,
- &fe->dtv_property_cache.atscmh_prc);
+ &c->atscmh_prc);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_rs_frame_mode(state,
(enum atscmh_rs_frame_mode *)
- &fe->dtv_property_cache.atscmh_rs_frame_mode);
+ &c->atscmh_rs_frame_mode);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_rs_frame_ensemble(state,
(enum atscmh_rs_frame_ensemble *)
- &fe->dtv_property_cache.atscmh_rs_frame_ensemble);
+ &c->atscmh_rs_frame_ensemble);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_rs_code_mode(state,
(enum atscmh_rs_code_mode *)
- &fe->dtv_property_cache.atscmh_rs_code_mode_pri,
+ &c->atscmh_rs_code_mode_pri,
(enum atscmh_rs_code_mode *)
- &fe->dtv_property_cache.atscmh_rs_code_mode_sec);
+ &c->atscmh_rs_code_mode_sec);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_sccc_block_mode(state,
(enum atscmh_sccc_block_mode *)
- &fe->dtv_property_cache.atscmh_sccc_block_mode);
+ &c->atscmh_sccc_block_mode);
if (lg_fail(ret))
goto fail;
ret = lg216x_get_sccc_code_mode(state,
(enum atscmh_sccc_code_mode *)
- &fe->dtv_property_cache.atscmh_sccc_code_mode_a,
+ &c->atscmh_sccc_code_mode_a,
(enum atscmh_sccc_code_mode *)
- &fe->dtv_property_cache.atscmh_sccc_code_mode_b,
+ &c->atscmh_sccc_code_mode_b,
(enum atscmh_sccc_code_mode *)
- &fe->dtv_property_cache.atscmh_sccc_code_mode_c,
+ &c->atscmh_sccc_code_mode_c,
(enum atscmh_sccc_code_mode *)
- &fe->dtv_property_cache.atscmh_sccc_code_mode_d);
+ &c->atscmh_sccc_code_mode_d);
if (lg_fail(ret))
goto fail;
}
#if 0
ret = lg216x_read_fic_err_count(state,
- (u8 *)&fe->dtv_property_cache.atscmh_fic_err);
+ (u8 *)&c->atscmh_fic_err);
if (lg_fail(ret))
goto fail;
ret = lg216x_read_crc_err_count(state,
- &fe->dtv_property_cache.atscmh_crc_err);
+ &c->atscmh_crc_err);
if (lg_fail(ret))
goto fail;
ret = lg216x_read_rs_err_count(state,
- &fe->dtv_property_cache.atscmh_rs_err);
+ &c->atscmh_rs_err);
if (lg_fail(ret))
goto fail;
switch (state->cfg->lg_chip) {
case LG2160:
- if (((fe->dtv_property_cache.atscmh_rs_err >= 240) &&
- (fe->dtv_property_cache.atscmh_crc_err >= 240)) &&
+ if (((c->atscmh_rs_err >= 240) &&
+ (c->atscmh_crc_err >= 240)) &&
((jiffies_to_msecs(jiffies) - state->last_reset) > 6000))
ret = lg216x_soft_reset(state);
break;
@@ -1051,17 +1038,10 @@ fail:
return ret;
}
-static int lg216x_get_property(struct dvb_frontend *fe,
- struct dtv_property *tvp)
-{
- return (DTV_ATSCMH_FIC_VER == tvp->cmd) ?
- lg216x_get_frontend(fe) : 0;
-}
-
-
static int lg2160_set_frontend(struct dvb_frontend *fe)
{
struct lg216x_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret;
lg_dbg("(%d)\n", fe->dtv_property_cache.frequency);
@@ -1129,7 +1109,7 @@ static int lg2160_set_frontend(struct dvb_frontend *fe)
ret = lg216x_enable_fic(state, 1);
lg_fail(ret);
- lg216x_get_frontend(fe);
+ lg216x_get_frontend(fe, c);
fail:
return ret;
}
@@ -1203,7 +1183,7 @@ static int lg216x_read_lock_status(struct lg216x_state *state,
#endif
}
-static int lg216x_read_status(struct dvb_frontend *fe, fe_status_t *status)
+static int lg216x_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct lg216x_state *state = fe->demodulator_priv;
int ret, acq_lock, sync_lock;
@@ -1355,21 +1335,19 @@ static void lg216x_release(struct dvb_frontend *fe)
kfree(state);
}
-static struct dvb_frontend_ops lg2160_ops = {
+static const struct dvb_frontend_ops lg2160_ops = {
.delsys = { SYS_ATSCMH },
.info = {
.name = "LG Electronics LG2160 ATSC/MH Frontend",
- .frequency_min = 54000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 62500,
+ .frequency_min_hz = 54 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 62500,
},
.i2c_gate_ctrl = lg216x_i2c_gate_ctrl,
#if 0
.init = lg216x_init,
.sleep = lg216x_sleep,
#endif
- .get_property = lg216x_get_property,
-
.set_frontend = lg2160_set_frontend,
.get_frontend = lg216x_get_frontend,
.get_tune_settings = lg216x_get_tune_settings,
@@ -1383,21 +1361,19 @@ static struct dvb_frontend_ops lg2160_ops = {
.release = lg216x_release,
};
-static struct dvb_frontend_ops lg2161_ops = {
+static const struct dvb_frontend_ops lg2161_ops = {
.delsys = { SYS_ATSCMH },
.info = {
.name = "LG Electronics LG2161 ATSC/MH Frontend",
- .frequency_min = 54000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 62500,
+ .frequency_min_hz = 54 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 62500,
},
.i2c_gate_ctrl = lg216x_i2c_gate_ctrl,
#if 0
.init = lg216x_init,
.sleep = lg216x_sleep,
#endif
- .get_property = lg216x_get_property,
-
.set_frontend = lg2160_set_frontend,
.get_frontend = lg216x_get_frontend,
.get_tune_settings = lg216x_get_tune_settings,
@@ -1421,8 +1397,8 @@ struct dvb_frontend *lg2160_attach(const struct lg2160_config *config,
config ? config->i2c_addr : 0);
state = kzalloc(sizeof(struct lg216x_state), GFP_KERNEL);
- if (state == NULL)
- goto fail;
+ if (!state)
+ return NULL;
state->cfg = config;
state->i2c_adap = i2c_adap;
@@ -1432,7 +1408,7 @@ struct dvb_frontend *lg2160_attach(const struct lg2160_config *config,
switch (config->lg_chip) {
default:
lg_warn("invalid chip requested, defaulting to LG2160");
- /* fall-thru */
+ fallthrough;
case LG2160:
memcpy(&state->frontend.ops, &lg2160_ops,
sizeof(struct dvb_frontend_ops));
@@ -1449,20 +1425,10 @@ struct dvb_frontend *lg2160_attach(const struct lg2160_config *config,
state->frontend.dtv_property_cache.atscmh_parade_id = 1;
return &state->frontend;
-fail:
- lg_warn("unable to detect LG216x hardware\n");
- kfree(state);
- return NULL;
}
-EXPORT_SYMBOL(lg2160_attach);
+EXPORT_SYMBOL_GPL(lg2160_attach);
MODULE_DESCRIPTION("LG Electronics LG216x ATSC/MH Demodulator Driver");
MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.3");
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb-frontends/lg2160.h b/drivers/media/dvb-frontends/lg2160.h
new file mode 100644
index 000000000000..17ddf6822654
--- /dev/null
+++ b/drivers/media/dvb-frontends/lg2160.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for LG2160 - ATSC/MH
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@linuxtv.org>
+ */
+
+#ifndef _LG2160_H_
+#define _LG2160_H_
+
+#include <linux/i2c.h>
+#include <media/dvb_frontend.h>
+
+enum lg_chip_type {
+ LG2160 = 0,
+ LG2161 = 1,
+};
+
+#define LG2161_1019 LG2161
+#define LG2161_1040 LG2161
+
+enum lg2160_spi_clock {
+ LG2160_SPI_3_125_MHZ = 0,
+ LG2160_SPI_6_25_MHZ = 1,
+ LG2160_SPI_12_5_MHZ = 2,
+};
+
+#if 0
+enum lg2161_oif {
+ LG2161_OIF_EBI2_SLA = 1,
+ LG2161_OIF_SDIO_SLA = 2,
+ LG2161_OIF_SPI_SLA = 3,
+ LG2161_OIF_SPI_MAS = 4,
+ LG2161_OIF_SERIAL_TS = 7,
+};
+#endif
+
+struct lg2160_config {
+ u8 i2c_addr;
+
+ /* user defined IF frequency in KHz */
+ u16 if_khz;
+
+ /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */
+ unsigned int deny_i2c_rptr:1;
+
+ /* spectral inversion - 0:disabled 1:enabled */
+ unsigned int spectral_inversion:1;
+
+ unsigned int output_if;
+ enum lg2160_spi_clock spi_clock;
+ enum lg_chip_type lg_chip;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_LG2160)
+extern
+struct dvb_frontend *lg2160_attach(const struct lg2160_config *config,
+ struct i2c_adapter *i2c_adap);
+#else
+static inline
+struct dvb_frontend *lg2160_attach(const struct lg2160_config *config,
+ struct i2c_adapter *i2c_adap)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_LG2160 */
+
+#endif /* _LG2160_H_ */
diff --git a/drivers/media/dvb/frontends/lgdt3305.c b/drivers/media/dvb-frontends/lgdt3305.c
index 1d2c47378cf8..bdc8311e1c0b 100644
--- a/drivers/media/dvb/frontends/lgdt3305.c
+++ b/drivers/media/dvb-frontends/lgdt3305.c
@@ -1,30 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM
*
* Copyright (C) 2008, 2009, 2010 Michael Krufky <mkrufky@linuxtv.org>
*
* LGDT3304 support by Jarod Wilson <jarod@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
#include <asm/div64.h>
#include <linux/dvb/frontend.h>
#include <linux/slab.h>
-#include "dvb_math.h"
+#include <linux/int_log.h>
#include "lgdt3305.h"
static int debug;
@@ -60,7 +46,7 @@ struct lgdt3305_state {
struct dvb_frontend frontend;
- fe_modulation_t current_modulation;
+ enum fe_modulation current_modulation;
u32 current_frequency;
u32 snr;
};
@@ -236,12 +222,13 @@ static inline int lgdt3305_mpeg_mode(struct lgdt3305_state *state,
return lgdt3305_set_reg_bit(state, LGDT3305_TP_CTRL_1, 5, mode);
}
-static int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state,
- enum lgdt3305_tp_clock_edge edge,
- enum lgdt3305_tp_valid_polarity valid)
+static int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state)
{
u8 val;
int ret;
+ enum lgdt3305_tp_clock_edge edge = state->cfg->tpclk_edge;
+ enum lgdt3305_tp_clock_mode mode = state->cfg->tpclk_mode;
+ enum lgdt3305_tp_valid_polarity valid = state->cfg->tpvalid_polarity;
lg_dbg("edge = %d, valid = %d\n", edge, valid);
@@ -253,6 +240,8 @@ static int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state,
if (edge)
val |= 0x08;
+ if (mode)
+ val |= 0x40;
if (valid)
val |= 0x01;
@@ -740,9 +729,7 @@ static int lgdt3304_set_parameters(struct dvb_frontend *fe)
goto fail;
/* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */
- ret = lgdt3305_mpeg_mode_polarity(state,
- state->cfg->tpclk_edge,
- state->cfg->tpvalid_polarity);
+ ret = lgdt3305_mpeg_mode_polarity(state);
fail:
return ret;
}
@@ -806,16 +793,14 @@ static int lgdt3305_set_parameters(struct dvb_frontend *fe)
goto fail;
/* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */
- ret = lgdt3305_mpeg_mode_polarity(state,
- state->cfg->tpclk_edge,
- state->cfg->tpvalid_polarity);
+ ret = lgdt3305_mpeg_mode_polarity(state);
fail:
return ret;
}
-static int lgdt3305_get_frontend(struct dvb_frontend *fe)
+static int lgdt3305_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct lgdt3305_state *state = fe->demodulator_priv;
lg_dbg("\n");
@@ -913,7 +898,7 @@ fail:
return ret;
}
-static int lgdt3305_read_status(struct dvb_frontend *fe, fe_status_t *status)
+static int lgdt3305_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct lgdt3305_state *state = fe->demodulator_priv;
u8 val;
@@ -1104,8 +1089,8 @@ static void lgdt3305_release(struct dvb_frontend *fe)
kfree(state);
}
-static struct dvb_frontend_ops lgdt3304_ops;
-static struct dvb_frontend_ops lgdt3305_ops;
+static const struct dvb_frontend_ops lgdt3304_ops;
+static const struct dvb_frontend_ops lgdt3305_ops;
struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config,
struct i2c_adapter *i2c_adap)
@@ -1163,19 +1148,20 @@ fail:
kfree(state);
return NULL;
}
-EXPORT_SYMBOL(lgdt3305_attach);
+EXPORT_SYMBOL_GPL(lgdt3305_attach);
-static struct dvb_frontend_ops lgdt3304_ops = {
+static const struct dvb_frontend_ops lgdt3304_ops = {
.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
.info = {
.name = "LG Electronics LGDT3304 VSB/QAM Frontend",
- .frequency_min = 54000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 62500,
+ .frequency_min_hz = 54 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 62500,
.caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
},
.i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl,
.init = lgdt3305_init,
+ .sleep = lgdt3305_sleep,
.set_frontend = lgdt3304_set_parameters,
.get_frontend = lgdt3305_get_frontend,
.get_tune_settings = lgdt3305_get_tune_settings,
@@ -1187,13 +1173,13 @@ static struct dvb_frontend_ops lgdt3304_ops = {
.release = lgdt3305_release,
};
-static struct dvb_frontend_ops lgdt3305_ops = {
+static const struct dvb_frontend_ops lgdt3305_ops = {
.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
.info = {
.name = "LG Electronics LGDT3305 VSB/QAM Frontend",
- .frequency_min = 54000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 62500,
+ .frequency_min_hz = 54 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 62500,
.caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
},
.i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl,
@@ -1214,9 +1200,3 @@ MODULE_DESCRIPTION("LG Electronics LGDT3304/5 ATSC/QAM-B Demodulator Driver");
MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.2");
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb-frontends/lgdt3305.h b/drivers/media/dvb-frontends/lgdt3305.h
new file mode 100644
index 000000000000..3718cf823560
--- /dev/null
+++ b/drivers/media/dvb-frontends/lgdt3305.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM
+ *
+ * Copyright (C) 2008, 2009, 2010 Michael Krufky <mkrufky@linuxtv.org>
+ */
+
+#ifndef _LGDT3305_H_
+#define _LGDT3305_H_
+
+#include <linux/i2c.h>
+#include <media/dvb_frontend.h>
+
+
+enum lgdt3305_mpeg_mode {
+ LGDT3305_MPEG_PARALLEL = 0,
+ LGDT3305_MPEG_SERIAL = 1,
+};
+
+enum lgdt3305_tp_clock_edge {
+ LGDT3305_TPCLK_RISING_EDGE = 0,
+ LGDT3305_TPCLK_FALLING_EDGE = 1,
+};
+
+enum lgdt3305_tp_clock_mode {
+ LGDT3305_TPCLK_GATED = 0,
+ LGDT3305_TPCLK_FIXED = 1,
+};
+
+enum lgdt3305_tp_valid_polarity {
+ LGDT3305_TP_VALID_LOW = 0,
+ LGDT3305_TP_VALID_HIGH = 1,
+};
+
+enum lgdt_demod_chip_type {
+ LGDT3305 = 0,
+ LGDT3304 = 1,
+};
+
+struct lgdt3305_config {
+ u8 i2c_addr;
+
+ /* user defined IF frequency in KHz */
+ u16 qam_if_khz;
+ u16 vsb_if_khz;
+
+ /* AGC Power reference - defaults are used if left unset */
+ u16 usref_8vsb; /* default: 0x32c4 */
+ u16 usref_qam64; /* default: 0x5400 */
+ u16 usref_qam256; /* default: 0x2a80 */
+
+ /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */
+ unsigned int deny_i2c_rptr:1;
+
+ /* spectral inversion - 0:disabled 1:enabled */
+ unsigned int spectral_inversion:1;
+
+ /* use RF AGC loop - 0:disabled 1:enabled */
+ unsigned int rf_agc_loop:1;
+
+ enum lgdt3305_mpeg_mode mpeg_mode;
+ enum lgdt3305_tp_clock_edge tpclk_edge;
+ enum lgdt3305_tp_clock_mode tpclk_mode;
+ enum lgdt3305_tp_valid_polarity tpvalid_polarity;
+ enum lgdt_demod_chip_type demod_chip;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_LGDT3305)
+extern
+struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config,
+ struct i2c_adapter *i2c_adap);
+#else
+static inline
+struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config,
+ struct i2c_adapter *i2c_adap)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_LGDT3305 */
+
+#endif /* _LGDT3305_H_ */
diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c
new file mode 100644
index 000000000000..6ab9d4de65ce
--- /dev/null
+++ b/drivers/media/dvb-frontends/lgdt3306a.c
@@ -0,0 +1,2267 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for LGDT3306A - 8VSB/QAM-B
+ *
+ * Copyright (C) 2013 Fred Richter <frichter@hauppauge.com>
+ * - driver structure based on lgdt3305.[ch] by Michael Krufky
+ * - code based on LG3306_V0.35 API by LG Electronics Inc.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <asm/div64.h>
+#include <linux/kernel.h>
+#include <linux/dvb/frontend.h>
+#include <linux/int_log.h>
+#include "lgdt3306a.h"
+#include <linux/i2c-mux.h>
+
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))");
+
+/*
+ * Older drivers treated QAM64 and QAM256 the same; that is the HW always
+ * used "Auto" mode during detection. Setting "forced_manual"=1 allows
+ * the user to treat these modes as separate. For backwards compatibility,
+ * it's off by default. QAM_AUTO can now be specified to achive that
+ * effect even if "forced_manual"=1
+ */
+static int forced_manual;
+module_param(forced_manual, int, 0644);
+MODULE_PARM_DESC(forced_manual, "if set, QAM64 and QAM256 will only lock to modulation specified");
+
+#define DBG_INFO 1
+#define DBG_REG 2
+#define DBG_DUMP 4 /* FGR - comment out to remove dump code */
+
+#define lg_debug(fmt, arg...) \
+ printk(KERN_DEBUG pr_fmt(fmt), ## arg)
+
+#define dbg_info(fmt, arg...) \
+ do { \
+ if (debug & DBG_INFO) \
+ lg_debug(fmt, ## arg); \
+ } while (0)
+
+#define dbg_reg(fmt, arg...) \
+ do { \
+ if (debug & DBG_REG) \
+ lg_debug(fmt, ## arg); \
+ } while (0)
+
+#define lg_chkerr(ret) \
+({ \
+ int __ret; \
+ __ret = (ret < 0); \
+ if (__ret) \
+ pr_err("error %d on line %d\n", ret, __LINE__); \
+ __ret; \
+})
+
+struct lgdt3306a_state {
+ struct i2c_adapter *i2c_adap;
+ const struct lgdt3306a_config *cfg;
+
+ struct dvb_frontend frontend;
+
+ enum fe_modulation current_modulation;
+ u32 current_frequency;
+ u32 snr;
+
+ struct i2c_mux_core *muxc;
+};
+
+/*
+ * LG3306A Register Usage
+ * (LG does not really name the registers, so this code does not either)
+ *
+ * 0000 -> 00FF Common control and status
+ * 1000 -> 10FF Synchronizer control and status
+ * 1F00 -> 1FFF Smart Antenna control and status
+ * 2100 -> 21FF VSB Equalizer control and status
+ * 2800 -> 28FF QAM Equalizer control and status
+ * 3000 -> 30FF FEC control and status
+ */
+
+enum lgdt3306a_lock_status {
+ LG3306_UNLOCK = 0x00,
+ LG3306_LOCK = 0x01,
+ LG3306_UNKNOWN_LOCK = 0xff
+};
+
+enum lgdt3306a_neverlock_status {
+ LG3306_NL_INIT = 0x00,
+ LG3306_NL_PROCESS = 0x01,
+ LG3306_NL_LOCK = 0x02,
+ LG3306_NL_FAIL = 0x03,
+ LG3306_NL_UNKNOWN = 0xff
+};
+
+enum lgdt3306a_modulation {
+ LG3306_VSB = 0x00,
+ LG3306_QAM64 = 0x01,
+ LG3306_QAM256 = 0x02,
+ LG3306_UNKNOWN_MODE = 0xff
+};
+
+enum lgdt3306a_lock_check {
+ LG3306_SYNC_LOCK,
+ LG3306_FEC_LOCK,
+ LG3306_TR_LOCK,
+ LG3306_AGC_LOCK,
+};
+
+
+#ifdef DBG_DUMP
+static void lgdt3306a_DumpAllRegs(struct lgdt3306a_state *state);
+static void lgdt3306a_DumpRegs(struct lgdt3306a_state *state);
+#endif
+
+
+static int lgdt3306a_write_reg(struct lgdt3306a_state *state, u16 reg, u8 val)
+{
+ int ret;
+ u8 buf[] = { reg >> 8, reg & 0xff, val };
+ struct i2c_msg msg = {
+ .addr = state->cfg->i2c_addr, .flags = 0,
+ .buf = buf, .len = 3,
+ };
+
+ dbg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val);
+
+ ret = i2c_transfer(state->i2c_adap, &msg, 1);
+
+ if (ret != 1) {
+ pr_err("error (addr %02x %02x <- %02x, err = %i)\n",
+ msg.buf[0], msg.buf[1], msg.buf[2], ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int lgdt3306a_read_reg(struct lgdt3306a_state *state, u16 reg, u8 *val)
+{
+ int ret;
+ u8 reg_buf[] = { reg >> 8, reg & 0xff };
+ struct i2c_msg msg[] = {
+ { .addr = state->cfg->i2c_addr,
+ .flags = 0, .buf = reg_buf, .len = 2 },
+ { .addr = state->cfg->i2c_addr,
+ .flags = I2C_M_RD, .buf = val, .len = 1 },
+ };
+
+ ret = i2c_transfer(state->i2c_adap, msg, 2);
+
+ if (ret != 2) {
+ pr_err("error (addr %02x reg %04x error (ret == %i)\n",
+ state->cfg->i2c_addr, reg, ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EREMOTEIO;
+ }
+ dbg_reg("reg: 0x%04x, val: 0x%02x\n", reg, *val);
+
+ return 0;
+}
+
+#define read_reg(state, reg) \
+({ \
+ u8 __val; \
+ int ret = lgdt3306a_read_reg(state, reg, &__val); \
+ if (lg_chkerr(ret)) \
+ __val = 0; \
+ __val; \
+})
+
+static int lgdt3306a_set_reg_bit(struct lgdt3306a_state *state,
+ u16 reg, int bit, int onoff)
+{
+ u8 val;
+ int ret;
+
+ dbg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff);
+
+ ret = lgdt3306a_read_reg(state, reg, &val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ val &= ~(1 << bit);
+ val |= (onoff & 1) << bit;
+
+ ret = lgdt3306a_write_reg(state, reg, val);
+ lg_chkerr(ret);
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lgdt3306a_soft_reset(struct lgdt3306a_state *state)
+{
+ int ret;
+
+ dbg_info("\n");
+
+ ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 0);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ msleep(20);
+ ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 1);
+ lg_chkerr(ret);
+
+fail:
+ return ret;
+}
+
+static int lgdt3306a_mpeg_mode(struct lgdt3306a_state *state,
+ enum lgdt3306a_mpeg_mode mode)
+{
+ u8 val;
+ int ret;
+
+ dbg_info("(%d)\n", mode);
+ /* transport packet format - TPSENB=0x80 */
+ ret = lgdt3306a_set_reg_bit(state, 0x0071, 7,
+ mode == LGDT3306A_MPEG_PARALLEL ? 1 : 0);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /*
+ * start of packet signal duration
+ * TPSSOPBITEN=0x40; 0=byte duration, 1=bit duration
+ */
+ ret = lgdt3306a_set_reg_bit(state, 0x0071, 6, 0);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ ret = lgdt3306a_read_reg(state, 0x0070, &val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ val |= 0x10; /* TPCLKSUPB=0x10 */
+
+ if (mode == LGDT3306A_MPEG_PARALLEL)
+ val &= ~0x10;
+
+ ret = lgdt3306a_write_reg(state, 0x0070, val);
+ lg_chkerr(ret);
+
+fail:
+ return ret;
+}
+
+static int lgdt3306a_mpeg_mode_polarity(struct lgdt3306a_state *state,
+ enum lgdt3306a_tp_clock_edge edge,
+ enum lgdt3306a_tp_valid_polarity valid)
+{
+ u8 val;
+ int ret;
+
+ dbg_info("edge=%d, valid=%d\n", edge, valid);
+
+ ret = lgdt3306a_read_reg(state, 0x0070, &val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ val &= ~0x06; /* TPCLKPOL=0x04, TPVALPOL=0x02 */
+
+ if (edge == LGDT3306A_TPCLK_RISING_EDGE)
+ val |= 0x04;
+ if (valid == LGDT3306A_TP_VALID_HIGH)
+ val |= 0x02;
+
+ ret = lgdt3306a_write_reg(state, 0x0070, val);
+ lg_chkerr(ret);
+
+fail:
+ return ret;
+}
+
+static int lgdt3306a_mpeg_tristate(struct lgdt3306a_state *state,
+ int mode)
+{
+ u8 val;
+ int ret;
+
+ dbg_info("(%d)\n", mode);
+
+ if (mode) {
+ ret = lgdt3306a_read_reg(state, 0x0070, &val);
+ if (lg_chkerr(ret))
+ goto fail;
+ /*
+ * Tristate bus; TPOUTEN=0x80, TPCLKOUTEN=0x20,
+ * TPDATAOUTEN=0x08
+ */
+ val &= ~0xa8;
+ ret = lgdt3306a_write_reg(state, 0x0070, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* AGCIFOUTENB=0x40; 1=Disable IFAGC pin */
+ ret = lgdt3306a_set_reg_bit(state, 0x0003, 6, 1);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ } else {
+ /* enable IFAGC pin */
+ ret = lgdt3306a_set_reg_bit(state, 0x0003, 6, 0);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ ret = lgdt3306a_read_reg(state, 0x0070, &val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ val |= 0xa8; /* enable bus */
+ ret = lgdt3306a_write_reg(state, 0x0070, val);
+ if (lg_chkerr(ret))
+ goto fail;
+ }
+
+fail:
+ return ret;
+}
+
+static int lgdt3306a_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+ struct lgdt3306a_state *state = fe->demodulator_priv;
+
+ dbg_info("acquire=%d\n", acquire);
+
+ return lgdt3306a_mpeg_tristate(state, acquire ? 0 : 1);
+
+}
+
+static int lgdt3306a_power(struct lgdt3306a_state *state,
+ int mode)
+{
+ int ret;
+
+ dbg_info("(%d)\n", mode);
+
+ if (mode == 0) {
+ /* into reset */
+ ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 0);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* power down */
+ ret = lgdt3306a_set_reg_bit(state, 0x0000, 0, 0);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ } else {
+ /* out of reset */
+ ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 1);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* power up */
+ ret = lgdt3306a_set_reg_bit(state, 0x0000, 0, 1);
+ if (lg_chkerr(ret))
+ goto fail;
+ }
+
+#ifdef DBG_DUMP
+ lgdt3306a_DumpAllRegs(state);
+#endif
+fail:
+ return ret;
+}
+
+
+static int lgdt3306a_set_vsb(struct lgdt3306a_state *state)
+{
+ u8 val;
+ int ret;
+
+ dbg_info("\n");
+
+ /* 0. Spectrum inversion detection manual; spectrum inverted */
+ ret = lgdt3306a_read_reg(state, 0x0002, &val);
+ val &= 0xf7; /* SPECINVAUTO Off */
+ val |= 0x04; /* SPECINV On */
+ ret = lgdt3306a_write_reg(state, 0x0002, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 1. Selection of standard mode(0x08=QAM, 0x80=VSB) */
+ ret = lgdt3306a_write_reg(state, 0x0008, 0x80);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 2. Bandwidth mode for VSB(6MHz) */
+ ret = lgdt3306a_read_reg(state, 0x0009, &val);
+ val &= 0xe3;
+ val |= 0x0c; /* STDOPDETTMODE[2:0]=3 */
+ ret = lgdt3306a_write_reg(state, 0x0009, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 3. QAM mode detection mode(None) */
+ ret = lgdt3306a_read_reg(state, 0x0009, &val);
+ val &= 0xfc; /* STDOPDETCMODE[1:0]=0 */
+ ret = lgdt3306a_write_reg(state, 0x0009, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 4. ADC sampling frequency rate(2x sampling) */
+ ret = lgdt3306a_read_reg(state, 0x000d, &val);
+ val &= 0xbf; /* SAMPLING4XFEN=0 */
+ ret = lgdt3306a_write_reg(state, 0x000d, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+#if 0
+ /* FGR - disable any AICC filtering, testing only */
+
+ ret = lgdt3306a_write_reg(state, 0x0024, 0x00);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* AICCFIXFREQ0 NT N-1(Video rejection) */
+ ret = lgdt3306a_write_reg(state, 0x002e, 0x00);
+ ret = lgdt3306a_write_reg(state, 0x002f, 0x00);
+ ret = lgdt3306a_write_reg(state, 0x0030, 0x00);
+
+ /* AICCFIXFREQ1 NT N-1(Audio rejection) */
+ ret = lgdt3306a_write_reg(state, 0x002b, 0x00);
+ ret = lgdt3306a_write_reg(state, 0x002c, 0x00);
+ ret = lgdt3306a_write_reg(state, 0x002d, 0x00);
+
+ /* AICCFIXFREQ2 NT Co-Channel(Video rejection) */
+ ret = lgdt3306a_write_reg(state, 0x0028, 0x00);
+ ret = lgdt3306a_write_reg(state, 0x0029, 0x00);
+ ret = lgdt3306a_write_reg(state, 0x002a, 0x00);
+
+ /* AICCFIXFREQ3 NT Co-Channel(Audio rejection) */
+ ret = lgdt3306a_write_reg(state, 0x0025, 0x00);
+ ret = lgdt3306a_write_reg(state, 0x0026, 0x00);
+ ret = lgdt3306a_write_reg(state, 0x0027, 0x00);
+
+#else
+ /* FGR - this works well for HVR-1955,1975 */
+
+ /* 5. AICCOPMODE NT N-1 Adj. */
+ ret = lgdt3306a_write_reg(state, 0x0024, 0x5A);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* AICCFIXFREQ0 NT N-1(Video rejection) */
+ ret = lgdt3306a_write_reg(state, 0x002e, 0x5A);
+ ret = lgdt3306a_write_reg(state, 0x002f, 0x00);
+ ret = lgdt3306a_write_reg(state, 0x0030, 0x00);
+
+ /* AICCFIXFREQ1 NT N-1(Audio rejection) */
+ ret = lgdt3306a_write_reg(state, 0x002b, 0x36);
+ ret = lgdt3306a_write_reg(state, 0x002c, 0x00);
+ ret = lgdt3306a_write_reg(state, 0x002d, 0x00);
+
+ /* AICCFIXFREQ2 NT Co-Channel(Video rejection) */
+ ret = lgdt3306a_write_reg(state, 0x0028, 0x2A);
+ ret = lgdt3306a_write_reg(state, 0x0029, 0x00);
+ ret = lgdt3306a_write_reg(state, 0x002a, 0x00);
+
+ /* AICCFIXFREQ3 NT Co-Channel(Audio rejection) */
+ ret = lgdt3306a_write_reg(state, 0x0025, 0x06);
+ ret = lgdt3306a_write_reg(state, 0x0026, 0x00);
+ ret = lgdt3306a_write_reg(state, 0x0027, 0x00);
+#endif
+
+ ret = lgdt3306a_read_reg(state, 0x001e, &val);
+ val &= 0x0f;
+ val |= 0xa0;
+ ret = lgdt3306a_write_reg(state, 0x001e, val);
+
+ ret = lgdt3306a_write_reg(state, 0x0022, 0x08);
+
+ ret = lgdt3306a_write_reg(state, 0x0023, 0xFF);
+
+ ret = lgdt3306a_read_reg(state, 0x211f, &val);
+ val &= 0xef;
+ ret = lgdt3306a_write_reg(state, 0x211f, val);
+
+ ret = lgdt3306a_write_reg(state, 0x2173, 0x01);
+
+ ret = lgdt3306a_read_reg(state, 0x1061, &val);
+ val &= 0xf8;
+ val |= 0x04;
+ ret = lgdt3306a_write_reg(state, 0x1061, val);
+
+ ret = lgdt3306a_read_reg(state, 0x103d, &val);
+ val &= 0xcf;
+ ret = lgdt3306a_write_reg(state, 0x103d, val);
+
+ ret = lgdt3306a_write_reg(state, 0x2122, 0x40);
+
+ ret = lgdt3306a_read_reg(state, 0x2141, &val);
+ val &= 0x3f;
+ ret = lgdt3306a_write_reg(state, 0x2141, val);
+
+ ret = lgdt3306a_read_reg(state, 0x2135, &val);
+ val &= 0x0f;
+ val |= 0x70;
+ ret = lgdt3306a_write_reg(state, 0x2135, val);
+
+ ret = lgdt3306a_read_reg(state, 0x0003, &val);
+ val &= 0xf7;
+ ret = lgdt3306a_write_reg(state, 0x0003, val);
+
+ ret = lgdt3306a_read_reg(state, 0x001c, &val);
+ val &= 0x7f;
+ ret = lgdt3306a_write_reg(state, 0x001c, val);
+
+ /* 6. EQ step size */
+ ret = lgdt3306a_read_reg(state, 0x2179, &val);
+ val &= 0xf8;
+ ret = lgdt3306a_write_reg(state, 0x2179, val);
+
+ ret = lgdt3306a_read_reg(state, 0x217a, &val);
+ val &= 0xf8;
+ ret = lgdt3306a_write_reg(state, 0x217a, val);
+
+ /* 7. Reset */
+ ret = lgdt3306a_soft_reset(state);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ dbg_info("complete\n");
+fail:
+ return ret;
+}
+
+static int lgdt3306a_set_qam(struct lgdt3306a_state *state, int modulation)
+{
+ u8 val;
+ int ret;
+
+ dbg_info("modulation=%d\n", modulation);
+
+ /* 1. Selection of standard mode(0x08=QAM, 0x80=VSB) */
+ ret = lgdt3306a_write_reg(state, 0x0008, 0x08);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 1a. Spectrum inversion detection to Auto */
+ ret = lgdt3306a_read_reg(state, 0x0002, &val);
+ val &= 0xfb; /* SPECINV Off */
+ val |= 0x08; /* SPECINVAUTO On */
+ ret = lgdt3306a_write_reg(state, 0x0002, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 2. Bandwidth mode for QAM */
+ ret = lgdt3306a_read_reg(state, 0x0009, &val);
+ val &= 0xe3; /* STDOPDETTMODE[2:0]=0 VSB Off */
+ ret = lgdt3306a_write_reg(state, 0x0009, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 3. : 64QAM/256QAM detection(manual, auto) */
+ ret = lgdt3306a_read_reg(state, 0x0009, &val);
+ val &= 0xfc;
+ /* Check for forced Manual modulation modes; otherwise always "auto" */
+ if(forced_manual && (modulation != QAM_AUTO)){
+ val |= 0x01; /* STDOPDETCMODE[1:0]= 1=Manual */
+ } else {
+ val |= 0x02; /* STDOPDETCMODE[1:0]= 2=Auto */
+ }
+ ret = lgdt3306a_write_reg(state, 0x0009, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 3a. : 64QAM/256QAM selection for manual */
+ ret = lgdt3306a_read_reg(state, 0x101a, &val);
+ val &= 0xf8;
+ if (modulation == QAM_64)
+ val |= 0x02; /* QMDQMODE[2:0]=2=QAM64 */
+ else
+ val |= 0x04; /* QMDQMODE[2:0]=4=QAM256 */
+
+ ret = lgdt3306a_write_reg(state, 0x101a, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 4. ADC sampling frequency rate(4x sampling) */
+ ret = lgdt3306a_read_reg(state, 0x000d, &val);
+ val &= 0xbf;
+ val |= 0x40; /* SAMPLING4XFEN=1 */
+ ret = lgdt3306a_write_reg(state, 0x000d, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 5. No AICC operation in QAM mode */
+ ret = lgdt3306a_read_reg(state, 0x0024, &val);
+ val &= 0x00;
+ ret = lgdt3306a_write_reg(state, 0x0024, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 5.1 V0.36 SRDCHKALWAYS : For better QAM detection */
+ ret = lgdt3306a_read_reg(state, 0x000a, &val);
+ val &= 0xfd;
+ val |= 0x02;
+ ret = lgdt3306a_write_reg(state, 0x000a, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 5.2 V0.36 Control of "no signal" detector function */
+ ret = lgdt3306a_read_reg(state, 0x2849, &val);
+ val &= 0xdf;
+ ret = lgdt3306a_write_reg(state, 0x2849, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 5.3 Fix for Blonder Tongue HDE-2H-QAM and AQM modulators */
+ ret = lgdt3306a_read_reg(state, 0x302b, &val);
+ val &= 0x7f; /* SELFSYNCFINDEN_CQS=0; disable auto reset */
+ ret = lgdt3306a_write_reg(state, 0x302b, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 6. Reset */
+ ret = lgdt3306a_soft_reset(state);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ dbg_info("complete\n");
+fail:
+ return ret;
+}
+
+static int lgdt3306a_set_modulation(struct lgdt3306a_state *state,
+ struct dtv_frontend_properties *p)
+{
+ int ret;
+
+ dbg_info("\n");
+
+ switch (p->modulation) {
+ case VSB_8:
+ ret = lgdt3306a_set_vsb(state);
+ break;
+ case QAM_64:
+ case QAM_256:
+ case QAM_AUTO:
+ ret = lgdt3306a_set_qam(state, p->modulation);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (lg_chkerr(ret))
+ goto fail;
+
+ state->current_modulation = p->modulation;
+
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lgdt3306a_agc_setup(struct lgdt3306a_state *state,
+ struct dtv_frontend_properties *p)
+{
+ /* TODO: anything we want to do here??? */
+ dbg_info("\n");
+
+ switch (p->modulation) {
+ case VSB_8:
+ break;
+ case QAM_64:
+ case QAM_256:
+ case QAM_AUTO:
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lgdt3306a_set_inversion(struct lgdt3306a_state *state,
+ int inversion)
+{
+ int ret;
+
+ dbg_info("(%d)\n", inversion);
+
+ ret = lgdt3306a_set_reg_bit(state, 0x0002, 2, inversion ? 1 : 0);
+ return ret;
+}
+
+static int lgdt3306a_set_inversion_auto(struct lgdt3306a_state *state,
+ int enabled)
+{
+ int ret;
+
+ dbg_info("(%d)\n", enabled);
+
+ /* 0=Manual 1=Auto(QAM only) - SPECINVAUTO=0x04 */
+ ret = lgdt3306a_set_reg_bit(state, 0x0002, 3, enabled);
+ return ret;
+}
+
+static int lgdt3306a_set_if(struct lgdt3306a_state *state,
+ struct dtv_frontend_properties *p)
+{
+ int ret;
+ u16 if_freq_khz;
+ u8 nco1, nco2;
+
+ switch (p->modulation) {
+ case VSB_8:
+ if_freq_khz = state->cfg->vsb_if_khz;
+ break;
+ case QAM_64:
+ case QAM_256:
+ case QAM_AUTO:
+ if_freq_khz = state->cfg->qam_if_khz;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (if_freq_khz) {
+ default:
+ pr_warn("IF=%d KHz is not supported, 3250 assumed\n",
+ if_freq_khz);
+ fallthrough;
+ case 3250: /* 3.25Mhz */
+ nco1 = 0x34;
+ nco2 = 0x00;
+ break;
+ case 3500: /* 3.50Mhz */
+ nco1 = 0x38;
+ nco2 = 0x00;
+ break;
+ case 4000: /* 4.00Mhz */
+ nco1 = 0x40;
+ nco2 = 0x00;
+ break;
+ case 5000: /* 5.00Mhz */
+ nco1 = 0x50;
+ nco2 = 0x00;
+ break;
+ case 5380: /* 5.38Mhz */
+ nco1 = 0x56;
+ nco2 = 0x14;
+ break;
+ }
+ ret = lgdt3306a_write_reg(state, 0x0010, nco1);
+ if (ret)
+ return ret;
+ ret = lgdt3306a_write_reg(state, 0x0011, nco2);
+ if (ret)
+ return ret;
+
+ dbg_info("if_freq=%d KHz->[%04x]\n", if_freq_khz, nco1<<8 | nco2);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lgdt3306a_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct lgdt3306a_state *state = fe->demodulator_priv;
+
+ if (state->cfg->deny_i2c_rptr) {
+ dbg_info("deny_i2c_rptr=%d\n", state->cfg->deny_i2c_rptr);
+ return 0;
+ }
+ dbg_info("(%d)\n", enable);
+
+ /* NI2CRPTEN=0x80 */
+ return lgdt3306a_set_reg_bit(state, 0x0002, 7, enable ? 0 : 1);
+}
+
+static int lgdt3306a_sleep(struct lgdt3306a_state *state)
+{
+ int ret;
+
+ dbg_info("\n");
+ state->current_frequency = -1; /* force re-tune, when we wake */
+
+ ret = lgdt3306a_mpeg_tristate(state, 1); /* disable data bus */
+ if (lg_chkerr(ret))
+ goto fail;
+
+ ret = lgdt3306a_power(state, 0); /* power down */
+ lg_chkerr(ret);
+
+fail:
+ return 0;
+}
+
+static int lgdt3306a_fe_sleep(struct dvb_frontend *fe)
+{
+ struct lgdt3306a_state *state = fe->demodulator_priv;
+
+ return lgdt3306a_sleep(state);
+}
+
+static int lgdt3306a_init(struct dvb_frontend *fe)
+{
+ struct lgdt3306a_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 val;
+ int ret;
+
+ dbg_info("\n");
+
+ /* 1. Normal operation mode */
+ ret = lgdt3306a_set_reg_bit(state, 0x0001, 0, 1); /* SIMFASTENB=0x01 */
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 2. Spectrum inversion auto detection (Not valid for VSB) */
+ ret = lgdt3306a_set_inversion_auto(state, 0);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 3. Spectrum inversion(According to the tuner configuration) */
+ ret = lgdt3306a_set_inversion(state, 1);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 4. Peak-to-peak voltage of ADC input signal */
+
+ /* ADCSEL1V=0x80=1Vpp; 0x00=2Vpp */
+ ret = lgdt3306a_set_reg_bit(state, 0x0004, 7, 1);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 5. ADC output data capture clock phase */
+
+ /* 0=same phase as ADC clock */
+ ret = lgdt3306a_set_reg_bit(state, 0x0004, 2, 0);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 5a. ADC sampling clock source */
+
+ /* ADCCLKPLLSEL=0x08; 0=use ext clock, not PLL */
+ ret = lgdt3306a_set_reg_bit(state, 0x0004, 3, 0);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 6. Automatic PLL set */
+
+ /* PLLSETAUTO=0x40; 0=off */
+ ret = lgdt3306a_set_reg_bit(state, 0x0005, 6, 0);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ if (state->cfg->xtalMHz == 24) { /* 24MHz */
+ /* 7. Frequency for PLL output(0x2564 for 192MHz for 24MHz) */
+ ret = lgdt3306a_read_reg(state, 0x0005, &val);
+ if (lg_chkerr(ret))
+ goto fail;
+ val &= 0xc0;
+ val |= 0x25;
+ ret = lgdt3306a_write_reg(state, 0x0005, val);
+ if (lg_chkerr(ret))
+ goto fail;
+ ret = lgdt3306a_write_reg(state, 0x0006, 0x64);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 8. ADC sampling frequency(0x180000 for 24MHz sampling) */
+ ret = lgdt3306a_read_reg(state, 0x000d, &val);
+ if (lg_chkerr(ret))
+ goto fail;
+ val &= 0xc0;
+ val |= 0x18;
+ ret = lgdt3306a_write_reg(state, 0x000d, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ } else if (state->cfg->xtalMHz == 25) { /* 25MHz */
+ /* 7. Frequency for PLL output */
+ ret = lgdt3306a_read_reg(state, 0x0005, &val);
+ if (lg_chkerr(ret))
+ goto fail;
+ val &= 0xc0;
+ val |= 0x25;
+ ret = lgdt3306a_write_reg(state, 0x0005, val);
+ if (lg_chkerr(ret))
+ goto fail;
+ ret = lgdt3306a_write_reg(state, 0x0006, 0x64);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 8. ADC sampling frequency(0x190000 for 25MHz sampling) */
+ ret = lgdt3306a_read_reg(state, 0x000d, &val);
+ if (lg_chkerr(ret))
+ goto fail;
+ val &= 0xc0;
+ val |= 0x19;
+ ret = lgdt3306a_write_reg(state, 0x000d, val);
+ if (lg_chkerr(ret))
+ goto fail;
+ } else {
+ pr_err("Bad xtalMHz=%d\n", state->cfg->xtalMHz);
+ }
+#if 0
+ ret = lgdt3306a_write_reg(state, 0x000e, 0x00);
+ ret = lgdt3306a_write_reg(state, 0x000f, 0x00);
+#endif
+
+ /* 9. Center frequency of input signal of ADC */
+ ret = lgdt3306a_write_reg(state, 0x0010, 0x34); /* 3.25MHz */
+ ret = lgdt3306a_write_reg(state, 0x0011, 0x00);
+
+ /* 10. Fixed gain error value */
+ ret = lgdt3306a_write_reg(state, 0x0014, 0); /* gain error=0 */
+
+ /* 10a. VSB TR BW gear shift initial step */
+ ret = lgdt3306a_read_reg(state, 0x103c, &val);
+ val &= 0x0f;
+ val |= 0x20; /* SAMGSAUTOSTL_V[3:0] = 2 */
+ ret = lgdt3306a_write_reg(state, 0x103c, val);
+
+ /* 10b. Timing offset calibration in low temperature for VSB */
+ ret = lgdt3306a_read_reg(state, 0x103d, &val);
+ val &= 0xfc;
+ val |= 0x03;
+ ret = lgdt3306a_write_reg(state, 0x103d, val);
+
+ /* 10c. Timing offset calibration in low temperature for QAM */
+ ret = lgdt3306a_read_reg(state, 0x1036, &val);
+ val &= 0xf0;
+ val |= 0x0c;
+ ret = lgdt3306a_write_reg(state, 0x1036, val);
+
+ /* 11. Using the imaginary part of CIR in CIR loading */
+ ret = lgdt3306a_read_reg(state, 0x211f, &val);
+ val &= 0xef; /* do not use imaginary of CIR */
+ ret = lgdt3306a_write_reg(state, 0x211f, val);
+
+ /* 12. Control of no signal detector function */
+ ret = lgdt3306a_read_reg(state, 0x2849, &val);
+ val &= 0xef; /* NOUSENOSIGDET=0, enable no signal detector */
+ ret = lgdt3306a_write_reg(state, 0x2849, val);
+
+ /* FGR - put demod in some known mode */
+ ret = lgdt3306a_set_vsb(state);
+
+ /* 13. TP stream format */
+ ret = lgdt3306a_mpeg_mode(state, state->cfg->mpeg_mode);
+
+ /* 14. disable output buses */
+ ret = lgdt3306a_mpeg_tristate(state, 1);
+
+ /* 15. Sleep (in reset) */
+ ret = lgdt3306a_sleep(state);
+ lg_chkerr(ret);
+
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+fail:
+ return ret;
+}
+
+static int lgdt3306a_set_parameters(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct lgdt3306a_state *state = fe->demodulator_priv;
+ int ret;
+
+ dbg_info("(%d, %d)\n", p->frequency, p->modulation);
+
+ if (state->current_frequency == p->frequency &&
+ state->current_modulation == p->modulation) {
+ dbg_info(" (already set, skipping ...)\n");
+ return 0;
+ }
+ state->current_frequency = -1;
+ state->current_modulation = -1;
+
+ ret = lgdt3306a_power(state, 1); /* power up */
+ if (lg_chkerr(ret))
+ goto fail;
+
+ if (fe->ops.tuner_ops.set_params) {
+ ret = fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+#if 0
+ if (lg_chkerr(ret))
+ goto fail;
+ state->current_frequency = p->frequency;
+#endif
+ }
+
+ ret = lgdt3306a_set_modulation(state, p);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ ret = lgdt3306a_agc_setup(state, p);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ ret = lgdt3306a_set_if(state, p);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* spectral_inversion defaults already set for VSB and QAM */
+
+ ret = lgdt3306a_mpeg_mode(state, state->cfg->mpeg_mode);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ ret = lgdt3306a_mpeg_mode_polarity(state,
+ state->cfg->tpclk_edge,
+ state->cfg->tpvalid_polarity);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ ret = lgdt3306a_mpeg_tristate(state, 0); /* enable data bus */
+ if (lg_chkerr(ret))
+ goto fail;
+
+ ret = lgdt3306a_soft_reset(state);
+ if (lg_chkerr(ret))
+ goto fail;
+
+#ifdef DBG_DUMP
+ lgdt3306a_DumpAllRegs(state);
+#endif
+ state->current_frequency = p->frequency;
+fail:
+ return ret;
+}
+
+static int lgdt3306a_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
+{
+ struct lgdt3306a_state *state = fe->demodulator_priv;
+
+ dbg_info("(%u, %d)\n",
+ state->current_frequency, state->current_modulation);
+
+ p->modulation = state->current_modulation;
+ p->frequency = state->current_frequency;
+ return 0;
+}
+
+static enum dvbfe_algo lgdt3306a_get_frontend_algo(struct dvb_frontend *fe)
+{
+#if 1
+ return DVBFE_ALGO_CUSTOM;
+#else
+ return DVBFE_ALGO_HW;
+#endif
+}
+
+/* ------------------------------------------------------------------------ */
+static int lgdt3306a_monitor_vsb(struct lgdt3306a_state *state)
+{
+ u8 val;
+ int ret;
+ u8 snrRef, maxPowerMan, nCombDet;
+ u16 fbDlyCir;
+
+ ret = lgdt3306a_read_reg(state, 0x21a1, &val);
+ if (ret)
+ return ret;
+ snrRef = val & 0x3f;
+
+ ret = lgdt3306a_read_reg(state, 0x2185, &maxPowerMan);
+ if (ret)
+ return ret;
+
+ ret = lgdt3306a_read_reg(state, 0x2191, &val);
+ if (ret)
+ return ret;
+ nCombDet = (val & 0x80) >> 7;
+
+ ret = lgdt3306a_read_reg(state, 0x2180, &val);
+ if (ret)
+ return ret;
+ fbDlyCir = (val & 0x03) << 8;
+
+ ret = lgdt3306a_read_reg(state, 0x2181, &val);
+ if (ret)
+ return ret;
+ fbDlyCir |= val;
+
+ dbg_info("snrRef=%d maxPowerMan=0x%x nCombDet=%d fbDlyCir=0x%x\n",
+ snrRef, maxPowerMan, nCombDet, fbDlyCir);
+
+ /* Carrier offset sub loop bandwidth */
+ ret = lgdt3306a_read_reg(state, 0x1061, &val);
+ if (ret)
+ return ret;
+ val &= 0xf8;
+ if ((snrRef > 18) && (maxPowerMan > 0x68)
+ && (nCombDet == 0x01)
+ && ((fbDlyCir == 0x03FF) || (fbDlyCir < 0x6C))) {
+ /* SNR is over 18dB and no ghosting */
+ val |= 0x00; /* final bandwidth = 0 */
+ } else {
+ val |= 0x04; /* final bandwidth = 4 */
+ }
+ ret = lgdt3306a_write_reg(state, 0x1061, val);
+ if (ret)
+ return ret;
+
+ /* Adjust Notch Filter */
+ ret = lgdt3306a_read_reg(state, 0x0024, &val);
+ if (ret)
+ return ret;
+ val &= 0x0f;
+ if (nCombDet == 0) { /* Turn on the Notch Filter */
+ val |= 0x50;
+ }
+ ret = lgdt3306a_write_reg(state, 0x0024, val);
+ if (ret)
+ return ret;
+
+ /* VSB Timing Recovery output normalization */
+ ret = lgdt3306a_read_reg(state, 0x103d, &val);
+ if (ret)
+ return ret;
+ val &= 0xcf;
+ val |= 0x20;
+ ret = lgdt3306a_write_reg(state, 0x103d, val);
+
+ return ret;
+}
+
+static enum lgdt3306a_modulation
+lgdt3306a_check_oper_mode(struct lgdt3306a_state *state)
+{
+ u8 val = 0;
+ int ret;
+
+ ret = lgdt3306a_read_reg(state, 0x0081, &val);
+ if (ret)
+ goto err;
+
+ if (val & 0x80) {
+ dbg_info("VSB\n");
+ return LG3306_VSB;
+ }
+ if (val & 0x08) {
+ ret = lgdt3306a_read_reg(state, 0x00a6, &val);
+ if (ret)
+ goto err;
+ val = val >> 2;
+ if (val & 0x01) {
+ dbg_info("QAM256\n");
+ return LG3306_QAM256;
+ }
+ dbg_info("QAM64\n");
+ return LG3306_QAM64;
+ }
+err:
+ pr_warn("UNKNOWN\n");
+ return LG3306_UNKNOWN_MODE;
+}
+
+static enum lgdt3306a_lock_status
+lgdt3306a_check_lock_status(struct lgdt3306a_state *state,
+ enum lgdt3306a_lock_check whatLock)
+{
+ u8 val = 0;
+ int ret;
+ enum lgdt3306a_modulation modeOper;
+ enum lgdt3306a_lock_status lockStatus;
+
+ modeOper = LG3306_UNKNOWN_MODE;
+
+ switch (whatLock) {
+ case LG3306_SYNC_LOCK:
+ {
+ ret = lgdt3306a_read_reg(state, 0x00a6, &val);
+ if (ret)
+ return ret;
+
+ if ((val & 0x80) == 0x80)
+ lockStatus = LG3306_LOCK;
+ else
+ lockStatus = LG3306_UNLOCK;
+
+ dbg_info("SYNC_LOCK=%x\n", lockStatus);
+ break;
+ }
+ case LG3306_AGC_LOCK:
+ {
+ ret = lgdt3306a_read_reg(state, 0x0080, &val);
+ if (ret)
+ return ret;
+
+ if ((val & 0x40) == 0x40)
+ lockStatus = LG3306_LOCK;
+ else
+ lockStatus = LG3306_UNLOCK;
+
+ dbg_info("AGC_LOCK=%x\n", lockStatus);
+ break;
+ }
+ case LG3306_TR_LOCK:
+ {
+ modeOper = lgdt3306a_check_oper_mode(state);
+ if ((modeOper == LG3306_QAM64) || (modeOper == LG3306_QAM256)) {
+ ret = lgdt3306a_read_reg(state, 0x1094, &val);
+ if (ret)
+ return ret;
+
+ if ((val & 0x80) == 0x80)
+ lockStatus = LG3306_LOCK;
+ else
+ lockStatus = LG3306_UNLOCK;
+ } else
+ lockStatus = LG3306_UNKNOWN_LOCK;
+
+ dbg_info("TR_LOCK=%x\n", lockStatus);
+ break;
+ }
+ case LG3306_FEC_LOCK:
+ {
+ modeOper = lgdt3306a_check_oper_mode(state);
+ if ((modeOper == LG3306_QAM64) || (modeOper == LG3306_QAM256)) {
+ ret = lgdt3306a_read_reg(state, 0x0080, &val);
+ if (ret)
+ return ret;
+
+ if ((val & 0x10) == 0x10)
+ lockStatus = LG3306_LOCK;
+ else
+ lockStatus = LG3306_UNLOCK;
+ } else
+ lockStatus = LG3306_UNKNOWN_LOCK;
+
+ dbg_info("FEC_LOCK=%x\n", lockStatus);
+ break;
+ }
+
+ default:
+ lockStatus = LG3306_UNKNOWN_LOCK;
+ pr_warn("UNKNOWN whatLock=%d\n", whatLock);
+ break;
+ }
+
+ return lockStatus;
+}
+
+static enum lgdt3306a_neverlock_status
+lgdt3306a_check_neverlock_status(struct lgdt3306a_state *state)
+{
+ u8 val = 0;
+ int ret;
+ enum lgdt3306a_neverlock_status lockStatus;
+
+ ret = lgdt3306a_read_reg(state, 0x0080, &val);
+ if (ret)
+ return ret;
+ lockStatus = (enum lgdt3306a_neverlock_status)(val & 0x03);
+
+ dbg_info("NeverLock=%d", lockStatus);
+
+ return lockStatus;
+}
+
+static int lgdt3306a_pre_monitoring(struct lgdt3306a_state *state)
+{
+ u8 val = 0;
+ int ret;
+ u8 currChDiffACQ, snrRef, mainStrong, aiccrejStatus;
+
+ /* Channel variation */
+ ret = lgdt3306a_read_reg(state, 0x21bc, &currChDiffACQ);
+ if (ret)
+ return ret;
+
+ /* SNR of Frame sync */
+ ret = lgdt3306a_read_reg(state, 0x21a1, &val);
+ if (ret)
+ return ret;
+ snrRef = val & 0x3f;
+
+ /* Strong Main CIR */
+ ret = lgdt3306a_read_reg(state, 0x2199, &val);
+ if (ret)
+ return ret;
+ mainStrong = (val & 0x40) >> 6;
+
+ ret = lgdt3306a_read_reg(state, 0x0090, &val);
+ if (ret)
+ return ret;
+ aiccrejStatus = (val & 0xf0) >> 4;
+
+ dbg_info("snrRef=%d mainStrong=%d aiccrejStatus=%d currChDiffACQ=0x%x\n",
+ snrRef, mainStrong, aiccrejStatus, currChDiffACQ);
+
+#if 0
+ /* Dynamic ghost exists */
+ if ((mainStrong == 0) && (currChDiffACQ > 0x70))
+#endif
+ if (mainStrong == 0) {
+ ret = lgdt3306a_read_reg(state, 0x2135, &val);
+ if (ret)
+ return ret;
+ val &= 0x0f;
+ val |= 0xa0;
+ ret = lgdt3306a_write_reg(state, 0x2135, val);
+ if (ret)
+ return ret;
+
+ ret = lgdt3306a_read_reg(state, 0x2141, &val);
+ if (ret)
+ return ret;
+ val &= 0x3f;
+ val |= 0x80;
+ ret = lgdt3306a_write_reg(state, 0x2141, val);
+ if (ret)
+ return ret;
+
+ ret = lgdt3306a_write_reg(state, 0x2122, 0x70);
+ if (ret)
+ return ret;
+ } else { /* Weak ghost or static channel */
+ ret = lgdt3306a_read_reg(state, 0x2135, &val);
+ if (ret)
+ return ret;
+ val &= 0x0f;
+ val |= 0x70;
+ ret = lgdt3306a_write_reg(state, 0x2135, val);
+ if (ret)
+ return ret;
+
+ ret = lgdt3306a_read_reg(state, 0x2141, &val);
+ if (ret)
+ return ret;
+ val &= 0x3f;
+ val |= 0x40;
+ ret = lgdt3306a_write_reg(state, 0x2141, val);
+ if (ret)
+ return ret;
+
+ ret = lgdt3306a_write_reg(state, 0x2122, 0x40);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static enum lgdt3306a_lock_status
+lgdt3306a_sync_lock_poll(struct lgdt3306a_state *state)
+{
+ enum lgdt3306a_lock_status syncLockStatus = LG3306_UNLOCK;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ msleep(30);
+
+ syncLockStatus = lgdt3306a_check_lock_status(state,
+ LG3306_SYNC_LOCK);
+
+ if (syncLockStatus == LG3306_LOCK) {
+ dbg_info("locked(%d)\n", i);
+ return LG3306_LOCK;
+ }
+ }
+ dbg_info("not locked\n");
+ return LG3306_UNLOCK;
+}
+
+static enum lgdt3306a_lock_status
+lgdt3306a_fec_lock_poll(struct lgdt3306a_state *state)
+{
+ enum lgdt3306a_lock_status FECLockStatus = LG3306_UNLOCK;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ msleep(30);
+
+ FECLockStatus = lgdt3306a_check_lock_status(state,
+ LG3306_FEC_LOCK);
+
+ if (FECLockStatus == LG3306_LOCK) {
+ dbg_info("locked(%d)\n", i);
+ return FECLockStatus;
+ }
+ }
+ dbg_info("not locked\n");
+ return FECLockStatus;
+}
+
+static enum lgdt3306a_neverlock_status
+lgdt3306a_neverlock_poll(struct lgdt3306a_state *state)
+{
+ enum lgdt3306a_neverlock_status NLLockStatus = LG3306_NL_FAIL;
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ msleep(30);
+
+ NLLockStatus = lgdt3306a_check_neverlock_status(state);
+
+ if (NLLockStatus == LG3306_NL_LOCK) {
+ dbg_info("NL_LOCK(%d)\n", i);
+ return NLLockStatus;
+ }
+ }
+ dbg_info("NLLockStatus=%d\n", NLLockStatus);
+ return NLLockStatus;
+}
+
+static u8 lgdt3306a_get_packet_error(struct lgdt3306a_state *state)
+{
+ u8 val;
+ int ret;
+
+ ret = lgdt3306a_read_reg(state, 0x00fa, &val);
+ if (ret)
+ return ret;
+
+ return val;
+}
+
+static const u32 valx_x10[] = {
+ 10, 11, 13, 15, 17, 20, 25, 33, 41, 50, 59, 73, 87, 100
+};
+static const u32 log10x_x1000[] = {
+ 0, 41, 114, 176, 230, 301, 398, 518, 613, 699, 771, 863, 939, 1000
+};
+
+static u32 log10_x1000(u32 x)
+{
+ u32 diff_val, step_val, step_log10;
+ u32 log_val = 0;
+ u32 i;
+
+ if (x <= 0)
+ return -1000000; /* signal error */
+
+ if (x == 10)
+ return 0; /* log(1)=0 */
+
+ if (x < 10) {
+ while (x < 10) {
+ x = x * 10;
+ log_val--;
+ }
+ } else { /* x > 10 */
+ while (x >= 100) {
+ x = x / 10;
+ log_val++;
+ }
+ }
+ log_val *= 1000;
+
+ if (x == 10) /* was our input an exact multiple of 10 */
+ return log_val; /* don't need to interpolate */
+
+ /* find our place on the log curve */
+ for (i = 1; i < ARRAY_SIZE(valx_x10); i++) {
+ if (valx_x10[i] >= x)
+ break;
+ }
+ if (i == ARRAY_SIZE(valx_x10))
+ return log_val + log10x_x1000[i - 1];
+
+ diff_val = x - valx_x10[i-1];
+ step_val = valx_x10[i] - valx_x10[i - 1];
+ step_log10 = log10x_x1000[i] - log10x_x1000[i - 1];
+
+ /* do a linear interpolation to get in-between values */
+ return log_val + log10x_x1000[i - 1] +
+ ((diff_val*step_log10) / step_val);
+}
+
+static u32 lgdt3306a_calculate_snr_x100(struct lgdt3306a_state *state)
+{
+ u32 mse; /* Mean-Square Error */
+ u32 pwr; /* Constelation power */
+ u32 snr_x100;
+
+ mse = (read_reg(state, 0x00ec) << 8) |
+ (read_reg(state, 0x00ed));
+ pwr = (read_reg(state, 0x00e8) << 8) |
+ (read_reg(state, 0x00e9));
+
+ if (mse == 0) /* no signal */
+ return 0;
+
+ snr_x100 = log10_x1000((pwr * 10000) / mse) - 3000;
+ dbg_info("mse=%u, pwr=%u, snr_x100=%d\n", mse, pwr, snr_x100);
+
+ return snr_x100;
+}
+
+static enum lgdt3306a_lock_status
+lgdt3306a_vsb_lock_poll(struct lgdt3306a_state *state)
+{
+ int ret;
+ u8 cnt = 0;
+ u8 packet_error;
+ u32 snr;
+
+ for (cnt = 0; cnt < 10; cnt++) {
+ if (lgdt3306a_sync_lock_poll(state) == LG3306_UNLOCK) {
+ dbg_info("no sync lock!\n");
+ return LG3306_UNLOCK;
+ }
+
+ msleep(20);
+ ret = lgdt3306a_pre_monitoring(state);
+ if (ret)
+ break;
+
+ packet_error = lgdt3306a_get_packet_error(state);
+ snr = lgdt3306a_calculate_snr_x100(state);
+ dbg_info("cnt=%d errors=%d snr=%d\n", cnt, packet_error, snr);
+
+ if ((snr >= 1500) && (packet_error < 0xff))
+ return LG3306_LOCK;
+ }
+
+ dbg_info("not locked!\n");
+ return LG3306_UNLOCK;
+}
+
+static enum lgdt3306a_lock_status
+lgdt3306a_qam_lock_poll(struct lgdt3306a_state *state)
+{
+ u8 cnt;
+ u8 packet_error;
+ u32 snr;
+
+ for (cnt = 0; cnt < 10; cnt++) {
+ if (lgdt3306a_fec_lock_poll(state) == LG3306_UNLOCK) {
+ dbg_info("no fec lock!\n");
+ return LG3306_UNLOCK;
+ }
+
+ msleep(20);
+
+ packet_error = lgdt3306a_get_packet_error(state);
+ snr = lgdt3306a_calculate_snr_x100(state);
+ dbg_info("cnt=%d errors=%d snr=%d\n", cnt, packet_error, snr);
+
+ if ((snr >= 1500) && (packet_error < 0xff))
+ return LG3306_LOCK;
+ }
+
+ dbg_info("not locked!\n");
+ return LG3306_UNLOCK;
+}
+
+static int lgdt3306a_read_status(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ struct lgdt3306a_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u16 strength = 0;
+ int ret = 0;
+
+ if (fe->ops.tuner_ops.get_rf_strength) {
+ ret = fe->ops.tuner_ops.get_rf_strength(fe, &strength);
+ if (ret == 0)
+ dbg_info("strength=%d\n", strength);
+ else
+ dbg_info("fe->ops.tuner_ops.get_rf_strength() failed\n");
+ }
+
+ *status = 0;
+ if (lgdt3306a_neverlock_poll(state) == LG3306_NL_LOCK) {
+ *status |= FE_HAS_SIGNAL;
+ *status |= FE_HAS_CARRIER;
+
+ switch (state->current_modulation) {
+ case QAM_256:
+ case QAM_64:
+ case QAM_AUTO:
+ if (lgdt3306a_qam_lock_poll(state) == LG3306_LOCK) {
+ *status |= FE_HAS_VITERBI;
+ *status |= FE_HAS_SYNC;
+
+ *status |= FE_HAS_LOCK;
+ }
+ break;
+ case VSB_8:
+ if (lgdt3306a_vsb_lock_poll(state) == LG3306_LOCK) {
+ *status |= FE_HAS_VITERBI;
+ *status |= FE_HAS_SYNC;
+
+ *status |= FE_HAS_LOCK;
+
+ ret = lgdt3306a_monitor_vsb(state);
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (*status & FE_HAS_SYNC) {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = lgdt3306a_calculate_snr_x100(state) * 10;
+ } else {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+ }
+ return ret;
+}
+
+
+static int lgdt3306a_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct lgdt3306a_state *state = fe->demodulator_priv;
+
+ state->snr = lgdt3306a_calculate_snr_x100(state);
+ /* report SNR in dB * 10 */
+ *snr = state->snr/10;
+
+ return 0;
+}
+
+static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ /*
+ * Calculate some sort of "strength" from SNR
+ */
+ struct lgdt3306a_state *state = fe->demodulator_priv;
+ u8 val;
+ u16 snr; /* snr_x10 */
+ int ret;
+ u32 ref_snr; /* snr*100 */
+ u32 str;
+
+ *strength = 0;
+
+ switch (state->current_modulation) {
+ case VSB_8:
+ ref_snr = 1600; /* 16dB */
+ break;
+ case QAM_64:
+ case QAM_256:
+ case QAM_AUTO:
+ /* need to know actual modulation to set proper SNR baseline */
+ ret = lgdt3306a_read_reg(state, 0x00a6, &val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ if(val & 0x04)
+ ref_snr = 2800; /* QAM-256 28dB */
+ else
+ ref_snr = 2200; /* QAM-64 22dB */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = fe->ops.read_snr(fe, &snr);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ if (state->snr <= (ref_snr - 100))
+ str = 0;
+ else if (state->snr <= ref_snr)
+ str = (0xffff * 65) / 100; /* 65% */
+ else {
+ str = state->snr - ref_snr;
+ str /= 50;
+ str += 78; /* 78%-100% */
+ if (str > 100)
+ str = 100;
+ str = (0xffff * str) / 100;
+ }
+ *strength = (u16)str;
+ dbg_info("strength=%u\n", *strength);
+
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lgdt3306a_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct lgdt3306a_state *state = fe->demodulator_priv;
+ u32 tmp;
+
+ *ber = 0;
+#if 1
+ /* FGR - FIXME - I don't know what value is expected by dvb_core
+ * what is the scale of the value?? */
+ tmp = read_reg(state, 0x00fc); /* NBERVALUE[24-31] */
+ tmp = (tmp << 8) | read_reg(state, 0x00fd); /* NBERVALUE[16-23] */
+ tmp = (tmp << 8) | read_reg(state, 0x00fe); /* NBERVALUE[8-15] */
+ tmp = (tmp << 8) | read_reg(state, 0x00ff); /* NBERVALUE[0-7] */
+ *ber = tmp;
+ dbg_info("ber=%u\n", tmp);
+#endif
+ return 0;
+}
+
+static int lgdt3306a_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct lgdt3306a_state *state = fe->demodulator_priv;
+
+ *ucblocks = 0;
+#if 1
+ /* FGR - FIXME - I don't know what value is expected by dvb_core
+ * what happens when value wraps? */
+ *ucblocks = read_reg(state, 0x00f4); /* TPIFTPERRCNT[0-7] */
+ dbg_info("ucblocks=%u\n", *ucblocks);
+#endif
+
+ return 0;
+}
+
+static int lgdt3306a_tune(struct dvb_frontend *fe, bool re_tune,
+ unsigned int mode_flags, unsigned int *delay,
+ enum fe_status *status)
+{
+ int ret = 0;
+ struct lgdt3306a_state *state = fe->demodulator_priv;
+
+ dbg_info("re_tune=%u\n", re_tune);
+
+ if (re_tune) {
+ state->current_frequency = -1; /* force re-tune */
+ ret = lgdt3306a_set_parameters(fe);
+ if (ret != 0)
+ return ret;
+ }
+ *delay = 125;
+ ret = lgdt3306a_read_status(fe, status);
+
+ return ret;
+}
+
+static int lgdt3306a_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings
+ *fe_tune_settings)
+{
+ fe_tune_settings->min_delay_ms = 100;
+ dbg_info("\n");
+ return 0;
+}
+
+static enum dvbfe_search lgdt3306a_search(struct dvb_frontend *fe)
+{
+ enum fe_status status = 0;
+ int ret;
+
+ /* set frontend */
+ ret = lgdt3306a_set_parameters(fe);
+ if (ret)
+ goto error;
+
+ ret = lgdt3306a_read_status(fe, &status);
+ if (ret)
+ goto error;
+
+ /* check if we have a valid signal */
+ if (status & FE_HAS_LOCK)
+ return DVBFE_ALGO_SEARCH_SUCCESS;
+ else
+ return DVBFE_ALGO_SEARCH_AGAIN;
+
+error:
+ dbg_info("failed (%d)\n", ret);
+ return DVBFE_ALGO_SEARCH_ERROR;
+}
+
+static void lgdt3306a_release(struct dvb_frontend *fe)
+{
+ struct lgdt3306a_state *state = fe->demodulator_priv;
+
+ dbg_info("\n");
+ kfree(state);
+}
+
+static const struct dvb_frontend_ops lgdt3306a_ops;
+
+struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config,
+ struct i2c_adapter *i2c_adap)
+{
+ struct lgdt3306a_state *state = NULL;
+ int ret;
+ u8 val;
+
+ dbg_info("(%d-%04x)\n",
+ i2c_adap ? i2c_adapter_id(i2c_adap) : 0,
+ config ? config->i2c_addr : 0);
+
+ state = kzalloc(sizeof(struct lgdt3306a_state), GFP_KERNEL);
+ if (state == NULL)
+ goto fail;
+
+ state->cfg = config;
+ state->i2c_adap = i2c_adap;
+
+ memcpy(&state->frontend.ops, &lgdt3306a_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ /* verify that we're talking to a lg3306a */
+ /* FGR - NOTE - there is no obvious ChipId to check; we check
+ * some "known" bits after reset, but it's still just a guess */
+ ret = lgdt3306a_read_reg(state, 0x0000, &val);
+ if (lg_chkerr(ret))
+ goto fail;
+ if ((val & 0x74) != 0x74) {
+ pr_warn("expected 0x74, got 0x%x\n", (val & 0x74));
+#if 0
+ /* FIXME - re-enable when we know this is right */
+ goto fail;
+#endif
+ }
+ ret = lgdt3306a_read_reg(state, 0x0001, &val);
+ if (lg_chkerr(ret))
+ goto fail;
+ if ((val & 0xf6) != 0xc6) {
+ pr_warn("expected 0xc6, got 0x%x\n", (val & 0xf6));
+#if 0
+ /* FIXME - re-enable when we know this is right */
+ goto fail;
+#endif
+ }
+ ret = lgdt3306a_read_reg(state, 0x0002, &val);
+ if (lg_chkerr(ret))
+ goto fail;
+ if ((val & 0x73) != 0x03) {
+ pr_warn("expected 0x03, got 0x%x\n", (val & 0x73));
+#if 0
+ /* FIXME - re-enable when we know this is right */
+ goto fail;
+#endif
+ }
+
+ state->current_frequency = -1;
+ state->current_modulation = -1;
+
+ lgdt3306a_sleep(state);
+
+ return &state->frontend;
+
+fail:
+ pr_warn("unable to detect LGDT3306A hardware\n");
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(lgdt3306a_attach);
+
+#ifdef DBG_DUMP
+
+static const short regtab[] = {
+ 0x0000, /* SOFTRSTB 1'b1 1'b1 1'b1 ADCPDB 1'b1 PLLPDB GBBPDB 11111111 */
+ 0x0001, /* 1'b1 1'b1 1'b0 1'b0 AUTORPTRS */
+ 0x0002, /* NI2CRPTEN 1'b0 1'b0 1'b0 SPECINVAUT */
+ 0x0003, /* AGCRFOUT */
+ 0x0004, /* ADCSEL1V ADCCNT ADCCNF ADCCNS ADCCLKPLL */
+ 0x0005, /* PLLINDIVSE */
+ 0x0006, /* PLLCTRL[7:0] 11100001 */
+ 0x0007, /* SYSINITWAITTIME[7:0] (msec) 00001000 */
+ 0x0008, /* STDOPMODE[7:0] 10000000 */
+ 0x0009, /* 1'b0 1'b0 1'b0 STDOPDETTMODE[2:0] STDOPDETCMODE[1:0] 00011110 */
+ 0x000a, /* DAFTEN 1'b1 x x SCSYSLOCK */
+ 0x000b, /* SCSYSLOCKCHKTIME[7:0] (10msec) 01100100 */
+ 0x000d, /* x SAMPLING4 */
+ 0x000e, /* SAMFREQ[15:8] 00000000 */
+ 0x000f, /* SAMFREQ[7:0] 00000000 */
+ 0x0010, /* IFFREQ[15:8] 01100000 */
+ 0x0011, /* IFFREQ[7:0] 00000000 */
+ 0x0012, /* AGCEN AGCREFMO */
+ 0x0013, /* AGCRFFIXB AGCIFFIXB AGCLOCKDETRNGSEL[1:0] 1'b1 1'b0 1'b0 1'b0 11101000 */
+ 0x0014, /* AGCFIXVALUE[7:0] 01111111 */
+ 0x0015, /* AGCREF[15:8] 00001010 */
+ 0x0016, /* AGCREF[7:0] 11100100 */
+ 0x0017, /* AGCDELAY[7:0] 00100000 */
+ 0x0018, /* AGCRFBW[3:0] AGCIFBW[3:0] 10001000 */
+ 0x0019, /* AGCUDOUTMODE[1:0] AGCUDCTRLLEN[1:0] AGCUDCTRL */
+ 0x001c, /* 1'b1 PFEN MFEN AICCVSYNC */
+ 0x001d, /* 1'b0 1'b1 1'b0 1'b1 AICCVSYNC */
+ 0x001e, /* AICCALPHA[3:0] 1'b1 1'b0 1'b1 1'b0 01111010 */
+ 0x001f, /* AICCDETTH[19:16] AICCOFFTH[19:16] 00000000 */
+ 0x0020, /* AICCDETTH[15:8] 01111100 */
+ 0x0021, /* AICCDETTH[7:0] 00000000 */
+ 0x0022, /* AICCOFFTH[15:8] 00000101 */
+ 0x0023, /* AICCOFFTH[7:0] 11100000 */
+ 0x0024, /* AICCOPMODE3[1:0] AICCOPMODE2[1:0] AICCOPMODE1[1:0] AICCOPMODE0[1:0] 00000000 */
+ 0x0025, /* AICCFIXFREQ3[23:16] 00000000 */
+ 0x0026, /* AICCFIXFREQ3[15:8] 00000000 */
+ 0x0027, /* AICCFIXFREQ3[7:0] 00000000 */
+ 0x0028, /* AICCFIXFREQ2[23:16] 00000000 */
+ 0x0029, /* AICCFIXFREQ2[15:8] 00000000 */
+ 0x002a, /* AICCFIXFREQ2[7:0] 00000000 */
+ 0x002b, /* AICCFIXFREQ1[23:16] 00000000 */
+ 0x002c, /* AICCFIXFREQ1[15:8] 00000000 */
+ 0x002d, /* AICCFIXFREQ1[7:0] 00000000 */
+ 0x002e, /* AICCFIXFREQ0[23:16] 00000000 */
+ 0x002f, /* AICCFIXFREQ0[15:8] 00000000 */
+ 0x0030, /* AICCFIXFREQ0[7:0] 00000000 */
+ 0x0031, /* 1'b0 1'b1 1'b0 1'b0 x DAGC1STER */
+ 0x0032, /* DAGC1STEN DAGC1STER */
+ 0x0033, /* DAGC1STREF[15:8] 00001010 */
+ 0x0034, /* DAGC1STREF[7:0] 11100100 */
+ 0x0035, /* DAGC2NDE */
+ 0x0036, /* DAGC2NDREF[15:8] 00001010 */
+ 0x0037, /* DAGC2NDREF[7:0] 10000000 */
+ 0x0038, /* DAGC2NDLOCKDETRNGSEL[1:0] */
+ 0x003d, /* 1'b1 SAMGEARS */
+ 0x0040, /* SAMLFGMA */
+ 0x0041, /* SAMLFBWM */
+ 0x0044, /* 1'b1 CRGEARSHE */
+ 0x0045, /* CRLFGMAN */
+ 0x0046, /* CFLFBWMA */
+ 0x0047, /* CRLFGMAN */
+ 0x0048, /* x x x x CRLFGSTEP_VS[3:0] xxxx1001 */
+ 0x0049, /* CRLFBWMA */
+ 0x004a, /* CRLFBWMA */
+ 0x0050, /* 1'b0 1'b1 1'b1 1'b0 MSECALCDA */
+ 0x0070, /* TPOUTEN TPIFEN TPCLKOUTE */
+ 0x0071, /* TPSENB TPSSOPBITE */
+ 0x0073, /* TP47HINS x x CHBERINT PERMODE[1:0] PERINT[1:0] 1xx11100 */
+ 0x0075, /* x x x x x IQSWAPCTRL[2:0] xxxxx000 */
+ 0x0076, /* NBERCON NBERST NBERPOL NBERWSYN */
+ 0x0077, /* x NBERLOSTTH[2:0] NBERACQTH[3:0] x0000000 */
+ 0x0078, /* NBERPOLY[31:24] 00000000 */
+ 0x0079, /* NBERPOLY[23:16] 00000000 */
+ 0x007a, /* NBERPOLY[15:8] 00000000 */
+ 0x007b, /* NBERPOLY[7:0] 00000000 */
+ 0x007c, /* NBERPED[31:24] 00000000 */
+ 0x007d, /* NBERPED[23:16] 00000000 */
+ 0x007e, /* NBERPED[15:8] 00000000 */
+ 0x007f, /* NBERPED[7:0] 00000000 */
+ 0x0080, /* x AGCLOCK DAGCLOCK SYSLOCK x x NEVERLOCK[1:0] */
+ 0x0085, /* SPECINVST */
+ 0x0088, /* SYSLOCKTIME[15:8] */
+ 0x0089, /* SYSLOCKTIME[7:0] */
+ 0x008c, /* FECLOCKTIME[15:8] */
+ 0x008d, /* FECLOCKTIME[7:0] */
+ 0x008e, /* AGCACCOUT[15:8] */
+ 0x008f, /* AGCACCOUT[7:0] */
+ 0x0090, /* AICCREJSTATUS[3:0] AICCREJBUSY[3:0] */
+ 0x0091, /* AICCVSYNC */
+ 0x009c, /* CARRFREQOFFSET[15:8] */
+ 0x009d, /* CARRFREQOFFSET[7:0] */
+ 0x00a1, /* SAMFREQOFFSET[23:16] */
+ 0x00a2, /* SAMFREQOFFSET[15:8] */
+ 0x00a3, /* SAMFREQOFFSET[7:0] */
+ 0x00a6, /* SYNCLOCK SYNCLOCKH */
+#if 0 /* covered elsewhere */
+ 0x00e8, /* CONSTPWR[15:8] */
+ 0x00e9, /* CONSTPWR[7:0] */
+ 0x00ea, /* BMSE[15:8] */
+ 0x00eb, /* BMSE[7:0] */
+ 0x00ec, /* MSE[15:8] */
+ 0x00ed, /* MSE[7:0] */
+ 0x00ee, /* CONSTI[7:0] */
+ 0x00ef, /* CONSTQ[7:0] */
+#endif
+ 0x00f4, /* TPIFTPERRCNT[7:0] */
+ 0x00f5, /* TPCORREC */
+ 0x00f6, /* VBBER[15:8] */
+ 0x00f7, /* VBBER[7:0] */
+ 0x00f8, /* VABER[15:8] */
+ 0x00f9, /* VABER[7:0] */
+ 0x00fa, /* TPERRCNT[7:0] */
+ 0x00fb, /* NBERLOCK x x x x x x x */
+ 0x00fc, /* NBERVALUE[31:24] */
+ 0x00fd, /* NBERVALUE[23:16] */
+ 0x00fe, /* NBERVALUE[15:8] */
+ 0x00ff, /* NBERVALUE[7:0] */
+ 0x1000, /* 1'b0 WODAGCOU */
+ 0x1005, /* x x 1'b1 1'b1 x SRD_Q_QM */
+ 0x1009, /* SRDWAITTIME[7:0] (10msec) 00100011 */
+ 0x100a, /* SRDWAITTIME_CQS[7:0] (msec) 01100100 */
+ 0x101a, /* x 1'b1 1'b0 1'b0 x QMDQAMMODE[2:0] x100x010 */
+ 0x1036, /* 1'b0 1'b1 1'b0 1'b0 SAMGSEND_CQS[3:0] 01001110 */
+ 0x103c, /* SAMGSAUTOSTL_V[3:0] SAMGSAUTOEDL_V[3:0] 01000110 */
+ 0x103d, /* 1'b1 1'b1 SAMCNORMBP_V[1:0] 1'b0 1'b0 SAMMODESEL_V[1:0] 11100001 */
+ 0x103f, /* SAMZTEDSE */
+ 0x105d, /* EQSTATUSE */
+ 0x105f, /* x PMAPG2_V[2:0] x DMAPG2_V[2:0] x001x011 */
+ 0x1060, /* 1'b1 EQSTATUSE */
+ 0x1061, /* CRMAPBWSTL_V[3:0] CRMAPBWEDL_V[3:0] 00000100 */
+ 0x1065, /* 1'b0 x CRMODE_V[1:0] 1'b1 x 1'b1 x 0x111x1x */
+ 0x1066, /* 1'b0 1'b0 1'b1 1'b0 1'b1 PNBOOSTSE */
+ 0x1068, /* CREPHNGAIN2_V[3:0] CREPHNPBW_V[3:0] 10010001 */
+ 0x106e, /* x x x x x CREPHNEN_ */
+ 0x106f, /* CREPHNTH_V[7:0] 00010101 */
+ 0x1072, /* CRSWEEPN */
+ 0x1073, /* CRPGAIN_V[3:0] x x 1'b1 1'b1 1001xx11 */
+ 0x1074, /* CRPBW_V[3:0] x x 1'b1 1'b1 0001xx11 */
+ 0x1080, /* DAFTSTATUS[1:0] x x x x x x */
+ 0x1081, /* SRDSTATUS[1:0] x x x x x SRDLOCK */
+ 0x10a9, /* EQSTATUS_CQS[1:0] x x x x x x */
+ 0x10b7, /* EQSTATUS_V[1:0] x x x x x x */
+#if 0 /* SMART_ANT */
+ 0x1f00, /* MODEDETE */
+ 0x1f01, /* x x x x x x x SFNRST xxxxxxx0 */
+ 0x1f03, /* NUMOFANT[7:0] 10000000 */
+ 0x1f04, /* x SELMASK[6:0] x0000000 */
+ 0x1f05, /* x SETMASK[6:0] x0000000 */
+ 0x1f06, /* x TXDATA[6:0] x0000000 */
+ 0x1f07, /* x CHNUMBER[6:0] x0000000 */
+ 0x1f09, /* AGCTIME[23:16] 10011000 */
+ 0x1f0a, /* AGCTIME[15:8] 10010110 */
+ 0x1f0b, /* AGCTIME[7:0] 10000000 */
+ 0x1f0c, /* ANTTIME[31:24] 00000000 */
+ 0x1f0d, /* ANTTIME[23:16] 00000011 */
+ 0x1f0e, /* ANTTIME[15:8] 10010000 */
+ 0x1f0f, /* ANTTIME[7:0] 10010000 */
+ 0x1f11, /* SYNCTIME[23:16] 10011000 */
+ 0x1f12, /* SYNCTIME[15:8] 10010110 */
+ 0x1f13, /* SYNCTIME[7:0] 10000000 */
+ 0x1f14, /* SNRTIME[31:24] 00000001 */
+ 0x1f15, /* SNRTIME[23:16] 01111101 */
+ 0x1f16, /* SNRTIME[15:8] 01111000 */
+ 0x1f17, /* SNRTIME[7:0] 01000000 */
+ 0x1f19, /* FECTIME[23:16] 00000000 */
+ 0x1f1a, /* FECTIME[15:8] 01110010 */
+ 0x1f1b, /* FECTIME[7:0] 01110000 */
+ 0x1f1d, /* FECTHD[7:0] 00000011 */
+ 0x1f1f, /* SNRTHD[23:16] 00001000 */
+ 0x1f20, /* SNRTHD[15:8] 01111111 */
+ 0x1f21, /* SNRTHD[7:0] 10000101 */
+ 0x1f80, /* IRQFLG x x SFSDRFLG MODEBFLG SAVEFLG SCANFLG TRACKFLG */
+ 0x1f81, /* x SYNCCON SNRCON FECCON x STDBUSY SYNCRST AGCFZCO */
+ 0x1f82, /* x x x SCANOPCD[4:0] */
+ 0x1f83, /* x x x x MAINOPCD[3:0] */
+ 0x1f84, /* x x RXDATA[13:8] */
+ 0x1f85, /* RXDATA[7:0] */
+ 0x1f86, /* x x SDTDATA[13:8] */
+ 0x1f87, /* SDTDATA[7:0] */
+ 0x1f89, /* ANTSNR[23:16] */
+ 0x1f8a, /* ANTSNR[15:8] */
+ 0x1f8b, /* ANTSNR[7:0] */
+ 0x1f8c, /* x x x x ANTFEC[13:8] */
+ 0x1f8d, /* ANTFEC[7:0] */
+ 0x1f8e, /* MAXCNT[7:0] */
+ 0x1f8f, /* SCANCNT[7:0] */
+ 0x1f91, /* MAXPW[23:16] */
+ 0x1f92, /* MAXPW[15:8] */
+ 0x1f93, /* MAXPW[7:0] */
+ 0x1f95, /* CURPWMSE[23:16] */
+ 0x1f96, /* CURPWMSE[15:8] */
+ 0x1f97, /* CURPWMSE[7:0] */
+#endif /* SMART_ANT */
+ 0x211f, /* 1'b1 1'b1 1'b1 CIRQEN x x 1'b0 1'b0 1111xx00 */
+ 0x212a, /* EQAUTOST */
+ 0x2122, /* CHFAST[7:0] 01100000 */
+ 0x212b, /* FFFSTEP_V[3:0] x FBFSTEP_V[2:0] 0001x001 */
+ 0x212c, /* PHDEROTBWSEL[3:0] 1'b1 1'b1 1'b1 1'b0 10001110 */
+ 0x212d, /* 1'b1 1'b1 1'b1 1'b1 x x TPIFLOCKS */
+ 0x2135, /* DYNTRACKFDEQ[3:0] x 1'b0 1'b0 1'b0 1010x000 */
+ 0x2141, /* TRMODE[1:0] 1'b1 1'b1 1'b0 1'b1 1'b1 1'b1 01110111 */
+ 0x2162, /* AICCCTRLE */
+ 0x2173, /* PHNCNFCNT[7:0] 00000100 */
+ 0x2179, /* 1'b0 1'b0 1'b0 1'b1 x BADSINGLEDYNTRACKFBF[2:0] 0001x001 */
+ 0x217a, /* 1'b0 1'b0 1'b0 1'b1 x BADSLOWSINGLEDYNTRACKFBF[2:0] 0001x001 */
+ 0x217e, /* CNFCNTTPIF[7:0] 00001000 */
+ 0x217f, /* TPERRCNTTPIF[7:0] 00000001 */
+ 0x2180, /* x x x x x x FBDLYCIR[9:8] */
+ 0x2181, /* FBDLYCIR[7:0] */
+ 0x2185, /* MAXPWRMAIN[7:0] */
+ 0x2191, /* NCOMBDET x x x x x x x */
+ 0x2199, /* x MAINSTRON */
+ 0x219a, /* FFFEQSTEPOUT_V[3:0] FBFSTEPOUT_V[2:0] */
+ 0x21a1, /* x x SNRREF[5:0] */
+ 0x2845, /* 1'b0 1'b1 x x FFFSTEP_CQS[1:0] FFFCENTERTAP[1:0] 01xx1110 */
+ 0x2846, /* 1'b0 x 1'b0 1'b1 FBFSTEP_CQS[1:0] 1'b1 1'b0 0x011110 */
+ 0x2847, /* ENNOSIGDE */
+ 0x2849, /* 1'b1 1'b1 NOUSENOSI */
+ 0x284a, /* EQINITWAITTIME[7:0] 01100100 */
+ 0x3000, /* 1'b1 1'b1 1'b1 x x x 1'b0 RPTRSTM */
+ 0x3001, /* RPTRSTWAITTIME[7:0] (100msec) 00110010 */
+ 0x3031, /* FRAMELOC */
+ 0x3032, /* 1'b1 1'b0 1'b0 1'b0 x x FRAMELOCKMODE_CQS[1:0] 1000xx11 */
+ 0x30a9, /* VDLOCK_Q FRAMELOCK */
+ 0x30aa, /* MPEGLOCK */
+};
+
+#define numDumpRegs (ARRAY_SIZE(regtab))
+static u8 regval1[numDumpRegs] = {0, };
+static u8 regval2[numDumpRegs] = {0, };
+
+static void lgdt3306a_DumpAllRegs(struct lgdt3306a_state *state)
+{
+ memset(regval2, 0xff, sizeof(regval2));
+ lgdt3306a_DumpRegs(state);
+}
+
+static void lgdt3306a_DumpRegs(struct lgdt3306a_state *state)
+{
+ int i;
+ int sav_debug = debug;
+
+ if ((debug & DBG_DUMP) == 0)
+ return;
+ debug &= ~DBG_REG; /* suppress DBG_REG during reg dump */
+
+ lg_debug("\n");
+
+ for (i = 0; i < numDumpRegs; i++) {
+ lgdt3306a_read_reg(state, regtab[i], &regval1[i]);
+ if (regval1[i] != regval2[i]) {
+ lg_debug(" %04X = %02X\n", regtab[i], regval1[i]);
+ regval2[i] = regval1[i];
+ }
+ }
+ debug = sav_debug;
+}
+#endif /* DBG_DUMP */
+
+
+
+static const struct dvb_frontend_ops lgdt3306a_ops = {
+ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
+ .info = {
+ .name = "LG Electronics LGDT3306A VSB/QAM Frontend",
+ .frequency_min_hz = 54 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 62500,
+ .caps = FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+ },
+ .i2c_gate_ctrl = lgdt3306a_i2c_gate_ctrl,
+ .init = lgdt3306a_init,
+ .sleep = lgdt3306a_fe_sleep,
+ /* if this is set, it overrides the default swzigzag */
+ .tune = lgdt3306a_tune,
+ .set_frontend = lgdt3306a_set_parameters,
+ .get_frontend = lgdt3306a_get_frontend,
+ .get_frontend_algo = lgdt3306a_get_frontend_algo,
+ .get_tune_settings = lgdt3306a_get_tune_settings,
+ .read_status = lgdt3306a_read_status,
+ .read_ber = lgdt3306a_read_ber,
+ .read_signal_strength = lgdt3306a_read_signal_strength,
+ .read_snr = lgdt3306a_read_snr,
+ .read_ucblocks = lgdt3306a_read_ucblocks,
+ .release = lgdt3306a_release,
+ .ts_bus_ctrl = lgdt3306a_ts_bus_ctrl,
+ .search = lgdt3306a_search,
+};
+
+static int lgdt3306a_select(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct i2c_client *client = i2c_mux_priv(muxc);
+ struct lgdt3306a_state *state = i2c_get_clientdata(client);
+
+ return lgdt3306a_i2c_gate_ctrl(&state->frontend, 1);
+}
+
+static int lgdt3306a_deselect(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct i2c_client *client = i2c_mux_priv(muxc);
+ struct lgdt3306a_state *state = i2c_get_clientdata(client);
+
+ return lgdt3306a_i2c_gate_ctrl(&state->frontend, 0);
+}
+
+static int lgdt3306a_probe(struct i2c_client *client)
+{
+ struct lgdt3306a_config *config;
+ struct lgdt3306a_state *state;
+ struct dvb_frontend *fe;
+ int ret;
+
+ if (!client->dev.platform_data) {
+ dev_err(&client->dev, "platform data is mandatory\n");
+ return -EINVAL;
+ }
+
+ config = kmemdup(client->dev.platform_data,
+ sizeof(struct lgdt3306a_config), GFP_KERNEL);
+ if (config == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ config->i2c_addr = client->addr;
+ fe = lgdt3306a_attach(config, client->adapter);
+ if (fe == NULL) {
+ ret = -ENODEV;
+ goto err_fe;
+ }
+
+ i2c_set_clientdata(client, fe->demodulator_priv);
+ state = fe->demodulator_priv;
+ state->frontend.ops.release = NULL;
+
+ /* create mux i2c adapter for tuner */
+ state->muxc = i2c_mux_alloc(client->adapter, &client->dev,
+ 1, 0, I2C_MUX_LOCKED,
+ lgdt3306a_select, lgdt3306a_deselect);
+ if (!state->muxc) {
+ ret = -ENOMEM;
+ goto err_kfree;
+ }
+ state->muxc->priv = client;
+ ret = i2c_mux_add_adapter(state->muxc, 0, 0);
+ if (ret)
+ goto err_kfree;
+
+ /* create dvb_frontend */
+ fe->ops.i2c_gate_ctrl = NULL;
+ *config->i2c_adapter = state->muxc->adapter[0];
+ *config->fe = fe;
+
+ dev_info(&client->dev, "LG Electronics LGDT3306A successfully identified\n");
+
+ return 0;
+
+err_kfree:
+ kfree(state);
+err_fe:
+ kfree(config);
+fail:
+ dev_warn(&client->dev, "probe failed = %d\n", ret);
+ return ret;
+}
+
+static void lgdt3306a_remove(struct i2c_client *client)
+{
+ struct lgdt3306a_state *state = i2c_get_clientdata(client);
+
+ i2c_mux_del_adapters(state->muxc);
+
+ state->frontend.ops.release = NULL;
+ state->frontend.demodulator_priv = NULL;
+
+ kfree(state->cfg);
+ kfree(state);
+}
+
+static const struct i2c_device_id lgdt3306a_id_table[] = {
+ { "lgdt3306a" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, lgdt3306a_id_table);
+
+static struct i2c_driver lgdt3306a_driver = {
+ .driver = {
+ .name = "lgdt3306a",
+ .suppress_bind_attrs = true,
+ },
+ .probe = lgdt3306a_probe,
+ .remove = lgdt3306a_remove,
+ .id_table = lgdt3306a_id_table,
+};
+
+module_i2c_driver(lgdt3306a_driver);
+
+MODULE_DESCRIPTION("LG Electronics LGDT3306A ATSC/QAM-B Demodulator Driver");
+MODULE_AUTHOR("Fred Richter <frichter@hauppauge.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.2");
diff --git a/drivers/media/dvb-frontends/lgdt3306a.h b/drivers/media/dvb-frontends/lgdt3306a.h
new file mode 100644
index 000000000000..407e74b5d195
--- /dev/null
+++ b/drivers/media/dvb-frontends/lgdt3306a.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for LGDT3306A - 8VSB/QAM-B
+ *
+ * Copyright (C) 2013,2014 Fred Richter <frichter@hauppauge.com>
+ * based on lgdt3305.[ch] by Michael Krufky
+ */
+
+#ifndef _LGDT3306A_H_
+#define _LGDT3306A_H_
+
+#include <linux/i2c.h>
+#include <media/dvb_frontend.h>
+
+
+enum lgdt3306a_mpeg_mode {
+ LGDT3306A_MPEG_PARALLEL = 0,
+ LGDT3306A_MPEG_SERIAL = 1,
+};
+
+enum lgdt3306a_tp_clock_edge {
+ LGDT3306A_TPCLK_RISING_EDGE = 0,
+ LGDT3306A_TPCLK_FALLING_EDGE = 1,
+};
+
+enum lgdt3306a_tp_valid_polarity {
+ LGDT3306A_TP_VALID_LOW = 0,
+ LGDT3306A_TP_VALID_HIGH = 1,
+};
+
+struct lgdt3306a_config {
+ u8 i2c_addr;
+
+ /* user defined IF frequency in KHz */
+ u16 qam_if_khz;
+ u16 vsb_if_khz;
+
+ /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */
+ unsigned int deny_i2c_rptr:1;
+
+ /* spectral inversion - 0:disabled 1:enabled */
+ unsigned int spectral_inversion:1;
+
+ enum lgdt3306a_mpeg_mode mpeg_mode;
+ enum lgdt3306a_tp_clock_edge tpclk_edge;
+ enum lgdt3306a_tp_valid_polarity tpvalid_polarity;
+
+ /* demod clock freq in MHz; 24 or 25 supported */
+ int xtalMHz;
+
+ /* returned by driver if using i2c bus multiplexing */
+ struct dvb_frontend **fe;
+ struct i2c_adapter **i2c_adapter;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_LGDT3306A)
+struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config,
+ struct i2c_adapter *i2c_adap);
+#else
+static inline
+struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config,
+ struct i2c_adapter *i2c_adap)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_LGDT3306A */
+
+#endif /* _LGDT3306A_H_ */
diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c
new file mode 100644
index 000000000000..8c34a5b850bc
--- /dev/null
+++ b/drivers/media/dvb-frontends/lgdt330x.c
@@ -0,0 +1,1006 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for LGDT3302 and LGDT3303 - VSB/QAM
+ *
+ * Copyright (C) 2005 Wilson Michaels <wilsonmichaels@earthlink.net>
+ */
+
+/*
+ * NOTES ABOUT THIS DRIVER
+ *
+ * This Linux driver supports:
+ * DViCO FusionHDTV 3 Gold-Q
+ * DViCO FusionHDTV 3 Gold-T
+ * DViCO FusionHDTV 5 Gold
+ * DViCO FusionHDTV 5 Lite
+ * DViCO FusionHDTV 5 USB Gold
+ * Air2PC/AirStar 2 ATSC 3rd generation (HD5000)
+ * pcHDTV HD5500
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+
+#include <media/dvb_frontend.h>
+#include <linux/int_log.h>
+#include "lgdt330x_priv.h"
+#include "lgdt330x.h"
+
+/* Use Equalizer Mean Squared Error instead of Phaser Tracker MSE */
+/* #define USE_EQMSE */
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off lgdt330x frontend debugging (default:off).");
+
+#define dprintk(state, fmt, arg...) do { \
+ if (debug) \
+ dev_printk(KERN_DEBUG, &state->client->dev, fmt, ##arg);\
+} while (0)
+
+struct lgdt330x_state {
+ struct i2c_client *client;
+
+ /* Configuration settings */
+ struct lgdt330x_config config;
+
+ struct dvb_frontend frontend;
+
+ /* Demodulator private data */
+ enum fe_modulation current_modulation;
+ u32 snr; /* Result of last SNR calculation */
+ u16 ucblocks;
+ unsigned long last_stats_time;
+
+ /* Tuner private data */
+ u32 current_frequency;
+};
+
+static int i2c_write_demod_bytes(struct lgdt330x_state *state,
+ const u8 *buf, /* data bytes to send */
+ int len /* number of bytes to send */)
+{
+ int i;
+ int err;
+
+ for (i = 0; i < len - 1; i += 2) {
+ err = i2c_master_send(state->client, buf, 2);
+ if (err != 2) {
+ dev_warn(&state->client->dev,
+ "%s: error (addr %02x <- %02x, err = %i)\n",
+ __func__, buf[0], buf[1], err);
+ if (err < 0)
+ return err;
+ else
+ return -EREMOTEIO;
+ }
+ buf += 2;
+ }
+ return 0;
+}
+
+/*
+ * This routine writes the register (reg) to the demod bus
+ * then reads the data returned for (len) bytes.
+ */
+static int i2c_read_demod_bytes(struct lgdt330x_state *state,
+ enum I2C_REG reg, u8 *buf, int len)
+{
+ u8 wr[] = { reg };
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->client->addr,
+ .flags = 0,
+ .buf = wr,
+ .len = 1
+ }, {
+ .addr = state->client->addr,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ .len = len
+ },
+ };
+ int ret;
+
+ ret = i2c_transfer(state->client->adapter, msg, 2);
+ if (ret != 2) {
+ dev_warn(&state->client->dev,
+ "%s: addr 0x%02x select 0x%02x error (ret == %i)\n",
+ __func__, state->client->addr, reg, ret);
+ if (ret >= 0)
+ ret = -EIO;
+ } else {
+ ret = 0;
+ }
+ return ret;
+}
+
+/* Software reset */
+static int lgdt3302_sw_reset(struct lgdt330x_state *state)
+{
+ u8 reset[] = {
+ IRQ_MASK,
+ /*
+ * bit 6 is active low software reset
+ * bits 5-0 are 1 to mask interrupts
+ */
+ 0x00
+ };
+ int ret;
+
+ ret = i2c_write_demod_bytes(state,
+ reset, sizeof(reset));
+ if (ret == 0) {
+ /* force reset high (inactive) and unmask interrupts */
+ reset[1] = 0x7f;
+ ret = i2c_write_demod_bytes(state,
+ reset, sizeof(reset));
+ }
+ return ret;
+}
+
+static int lgdt3303_sw_reset(struct lgdt330x_state *state)
+{
+ u8 reset[] = {
+ 0x02,
+ 0x00 /* bit 0 is active low software reset */
+ };
+ int ret;
+
+ ret = i2c_write_demod_bytes(state,
+ reset, sizeof(reset));
+ if (ret == 0) {
+ /* force reset high (inactive) */
+ reset[1] = 0x01;
+ ret = i2c_write_demod_bytes(state,
+ reset, sizeof(reset));
+ }
+ return ret;
+}
+
+static int lgdt330x_sw_reset(struct lgdt330x_state *state)
+{
+ switch (state->config.demod_chip) {
+ case LGDT3302:
+ return lgdt3302_sw_reset(state);
+ case LGDT3303:
+ return lgdt3303_sw_reset(state);
+ default:
+ return -ENODEV;
+ }
+}
+
+static int lgdt330x_init(struct dvb_frontend *fe)
+{
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ char *chip_name;
+ int err;
+ /*
+ * Array of byte pairs <address, value>
+ * to initialize each different chip
+ */
+ static const u8 lgdt3302_init_data[] = {
+ /* Use 50MHz param values from spec sheet since xtal is 50 */
+ /*
+ * Change the value of NCOCTFV[25:0] of carrier
+ * recovery center frequency register
+ */
+ VSB_CARRIER_FREQ0, 0x00,
+ VSB_CARRIER_FREQ1, 0x87,
+ VSB_CARRIER_FREQ2, 0x8e,
+ VSB_CARRIER_FREQ3, 0x01,
+ /*
+ * Change the TPCLK pin polarity
+ * data is valid on falling clock
+ */
+ DEMUX_CONTROL, 0xfb,
+ /*
+ * Change the value of IFBW[11:0] of
+ * AGC IF/RF loop filter bandwidth register
+ */
+ AGC_RF_BANDWIDTH0, 0x40,
+ AGC_RF_BANDWIDTH1, 0x93,
+ AGC_RF_BANDWIDTH2, 0x00,
+ /*
+ * Change the value of bit 6, 'nINAGCBY' and
+ * 'NSSEL[1:0] of ACG function control register 2
+ */
+ AGC_FUNC_CTRL2, 0xc6,
+ /*
+ * Change the value of bit 6 'RFFIX'
+ * of AGC function control register 3
+ */
+ AGC_FUNC_CTRL3, 0x40,
+ /*
+ * Set the value of 'INLVTHD' register 0x2a/0x2c
+ * to 0x7fe
+ */
+ AGC_DELAY0, 0x07,
+ AGC_DELAY2, 0xfe,
+ /*
+ * Change the value of IAGCBW[15:8]
+ * of inner AGC loop filter bandwidth
+ */
+ AGC_LOOP_BANDWIDTH0, 0x08,
+ AGC_LOOP_BANDWIDTH1, 0x9a
+ };
+ static const u8 lgdt3303_init_data[] = {
+ 0x4c, 0x14
+ };
+ static const u8 flip_1_lgdt3303_init_data[] = {
+ 0x4c, 0x14,
+ 0x87, 0xf3
+ };
+ static const u8 flip_2_lgdt3303_init_data[] = {
+ 0x4c, 0x14,
+ 0x87, 0xda
+ };
+
+ /*
+ * Hardware reset is done using gpio[0] of cx23880x chip.
+ * I'd like to do it here, but don't know how to find chip address.
+ * cx88-cards.c arranges for the reset bit to be inactive (high).
+ * Maybe there needs to be a callable function in cx88-core or
+ * the caller of this function needs to do it.
+ */
+
+ switch (state->config.demod_chip) {
+ case LGDT3302:
+ chip_name = "LGDT3302";
+ err = i2c_write_demod_bytes(state, lgdt3302_init_data,
+ sizeof(lgdt3302_init_data));
+ break;
+ case LGDT3303:
+ chip_name = "LGDT3303";
+ switch (state->config.clock_polarity_flip) {
+ case 2:
+ err = i2c_write_demod_bytes(state,
+ flip_2_lgdt3303_init_data,
+ sizeof(flip_2_lgdt3303_init_data));
+ break;
+ case 1:
+ err = i2c_write_demod_bytes(state,
+ flip_1_lgdt3303_init_data,
+ sizeof(flip_1_lgdt3303_init_data));
+ break;
+ case 0:
+ default:
+ err = i2c_write_demod_bytes(state, lgdt3303_init_data,
+ sizeof(lgdt3303_init_data));
+ }
+ break;
+ default:
+ chip_name = "undefined";
+ dev_warn(&state->client->dev,
+ "Only LGDT3302 and LGDT3303 are supported chips.\n");
+ err = -ENODEV;
+ }
+ dprintk(state, "Initialized the %s chip\n", chip_name);
+ if (err < 0)
+ return err;
+
+ p->cnr.len = 1;
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.len = 1;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.len = 1;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ state->last_stats_time = 0;
+
+ return lgdt330x_sw_reset(state);
+}
+
+static int lgdt330x_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct lgdt330x_state *state = fe->demodulator_priv;
+
+ *ucblocks = state->ucblocks;
+
+ return 0;
+}
+
+static int lgdt330x_set_parameters(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ /*
+ * Array of byte pairs <address, value>
+ * to initialize 8VSB for lgdt3303 chip 50 MHz IF
+ */
+ static const u8 lgdt3303_8vsb_44_data[] = {
+ 0x04, 0x00,
+ 0x0d, 0x40,
+ 0x0e, 0x87,
+ 0x0f, 0x8e,
+ 0x10, 0x01,
+ 0x47, 0x8b
+ };
+ /*
+ * Array of byte pairs <address, value>
+ * to initialize QAM for lgdt3303 chip
+ */
+ static const u8 lgdt3303_qam_data[] = {
+ 0x04, 0x00,
+ 0x0d, 0x00,
+ 0x0e, 0x00,
+ 0x0f, 0x00,
+ 0x10, 0x00,
+ 0x51, 0x63,
+ 0x47, 0x66,
+ 0x48, 0x66,
+ 0x4d, 0x1a,
+ 0x49, 0x08,
+ 0x4a, 0x9b
+ };
+ u8 top_ctrl_cfg[] = { TOP_CONTROL, 0x03 };
+
+ int err = 0;
+ /* Change only if we are actually changing the modulation */
+ if (state->current_modulation != p->modulation) {
+ switch (p->modulation) {
+ case VSB_8:
+ dprintk(state, "VSB_8 MODE\n");
+
+ /* Select VSB mode */
+ top_ctrl_cfg[1] = 0x03;
+
+ /* Select ANT connector if supported by card */
+ if (state->config.pll_rf_set)
+ state->config.pll_rf_set(fe, 1);
+
+ if (state->config.demod_chip == LGDT3303) {
+ err = i2c_write_demod_bytes(state,
+ lgdt3303_8vsb_44_data,
+ sizeof(lgdt3303_8vsb_44_data));
+ }
+ break;
+
+ case QAM_64:
+ dprintk(state, "QAM_64 MODE\n");
+
+ /* Select QAM_64 mode */
+ top_ctrl_cfg[1] = 0x00;
+
+ /* Select CABLE connector if supported by card */
+ if (state->config.pll_rf_set)
+ state->config.pll_rf_set(fe, 0);
+
+ if (state->config.demod_chip == LGDT3303) {
+ err = i2c_write_demod_bytes(state,
+ lgdt3303_qam_data,
+ sizeof(lgdt3303_qam_data));
+ }
+ break;
+
+ case QAM_256:
+ dprintk(state, "QAM_256 MODE\n");
+
+ /* Select QAM_256 mode */
+ top_ctrl_cfg[1] = 0x01;
+
+ /* Select CABLE connector if supported by card */
+ if (state->config.pll_rf_set)
+ state->config.pll_rf_set(fe, 0);
+
+ if (state->config.demod_chip == LGDT3303) {
+ err = i2c_write_demod_bytes(state,
+ lgdt3303_qam_data,
+ sizeof(lgdt3303_qam_data));
+ }
+ break;
+ default:
+ dev_warn(&state->client->dev,
+ "%s: Modulation type(%d) UNSUPPORTED\n",
+ __func__, p->modulation);
+ return -1;
+ }
+ if (err < 0)
+ dev_warn(&state->client->dev,
+ "%s: error blasting bytes to lgdt3303 for modulation type(%d)\n",
+ __func__, p->modulation);
+
+ /*
+ * select serial or parallel MPEG hardware interface
+ * Serial: 0x04 for LGDT3302 or 0x40 for LGDT3303
+ * Parallel: 0x00
+ */
+ top_ctrl_cfg[1] |= state->config.serial_mpeg;
+
+ /* Select the requested mode */
+ i2c_write_demod_bytes(state, top_ctrl_cfg,
+ sizeof(top_ctrl_cfg));
+ if (state->config.set_ts_params)
+ state->config.set_ts_params(fe, 0);
+ state->current_modulation = p->modulation;
+ }
+
+ /* Tune to the specified frequency */
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ /* Keep track of the new frequency */
+ /*
+ * FIXME this is the wrong way to do this...
+ * The tuner is shared with the video4linux analog API
+ */
+ state->current_frequency = p->frequency;
+
+ lgdt330x_sw_reset(state);
+ return 0;
+}
+
+static int lgdt330x_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
+{
+ struct lgdt330x_state *state = fe->demodulator_priv;
+
+ p->frequency = state->current_frequency;
+ return 0;
+}
+
+/*
+ * Calculate SNR estimation (scaled by 2^24)
+ *
+ * 8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM
+ * equations from LGDT3303 datasheet. VSB is the same between the '02
+ * and '03, so maybe QAM is too? Perhaps someone with a newer datasheet
+ * that has QAM information could verify?
+ *
+ * For 8-VSB: (two ways, take your pick)
+ * LGDT3302:
+ * SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE)
+ * LGDT3303:
+ * SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE)
+ * LGDT3302 & LGDT3303:
+ * SNR_PT = 10 * log10(25 * 32^2 / PT_MSE) (we use this one)
+ * For 64-QAM:
+ * SNR = 10 * log10( 688128 / MSEQAM)
+ * For 256-QAM:
+ * SNR = 10 * log10( 696320 / MSEQAM)
+ *
+ * We re-write the snr equation as:
+ * SNR * 2^24 = 10*(c - intlog10(MSE))
+ * Where for 256-QAM, c = log10(696320) * 2^24, and so on.
+ */
+static u32 calculate_snr(u32 mse, u32 c)
+{
+ if (mse == 0) /* No signal */
+ return 0;
+
+ mse = intlog10(mse);
+ if (mse > c) {
+ /*
+ * Negative SNR, which is possible, but realisticly the
+ * demod will lose lock before the signal gets this bad.
+ * The API only allows for unsigned values, so just return 0
+ */
+ return 0;
+ }
+ return 10 * (c - mse);
+}
+
+static int lgdt3302_read_snr(struct dvb_frontend *fe)
+{
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ u8 buf[5]; /* read data buffer */
+ u32 noise; /* noise value */
+ u32 c; /* per-modulation SNR calculation constant */
+
+ switch (state->current_modulation) {
+ case VSB_8:
+ i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5);
+#ifdef USE_EQMSE
+ /* Use Equalizer Mean-Square Error Register */
+ /* SNR for ranges from -15.61 to +41.58 */
+ noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2];
+ c = 69765745; /* log10(25*24^2)*2^24 */
+#else
+ /* Use Phase Tracker Mean-Square Error Register */
+ /* SNR for ranges from -13.11 to +44.08 */
+ noise = ((buf[0] & 7 << 3) << 13) | (buf[3] << 8) | buf[4];
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#endif
+ break;
+ case QAM_64:
+ case QAM_256:
+ i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2);
+ noise = ((buf[0] & 3) << 8) | buf[1];
+ c = state->current_modulation == QAM_64 ? 97939837 : 98026066;
+ /* log10(688128)*2^24 and log10(696320)*2^24 */
+ break;
+ default:
+ dev_err(&state->client->dev,
+ "%s: Modulation set to unsupported value\n",
+ __func__);
+
+ state->snr = 0;
+
+ return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */
+ }
+
+ state->snr = calculate_snr(noise, c);
+
+ dprintk(state, "noise = 0x%08x, snr = %d.%02d dB\n", noise,
+ state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16);
+
+ return 0;
+}
+
+static int lgdt3303_read_snr(struct dvb_frontend *fe)
+{
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ u8 buf[5]; /* read data buffer */
+ u32 noise; /* noise value */
+ u32 c; /* per-modulation SNR calculation constant */
+
+ switch (state->current_modulation) {
+ case VSB_8:
+ i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5);
+#ifdef USE_EQMSE
+ /* Use Equalizer Mean-Square Error Register */
+ /* SNR for ranges from -16.12 to +44.08 */
+ noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2];
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#else
+ /* Use Phase Tracker Mean-Square Error Register */
+ /* SNR for ranges from -13.11 to +44.08 */
+ noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4];
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#endif
+ break;
+ case QAM_64:
+ case QAM_256:
+ i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2);
+ noise = (buf[0] << 8) | buf[1];
+ c = state->current_modulation == QAM_64 ? 97939837 : 98026066;
+ /* log10(688128)*2^24 and log10(696320)*2^24 */
+ break;
+ default:
+ dev_err(&state->client->dev,
+ "%s: Modulation set to unsupported value\n",
+ __func__);
+ state->snr = 0;
+ return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */
+ }
+
+ state->snr = calculate_snr(noise, c);
+
+ dprintk(state, "noise = 0x%08x, snr = %d.%02d dB\n", noise,
+ state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16);
+
+ return 0;
+}
+
+static int lgdt330x_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct lgdt330x_state *state = fe->demodulator_priv;
+
+ *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */
+
+ return 0;
+}
+
+static int lgdt330x_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ /* Calculate Strength from SNR up to 35dB */
+ /*
+ * Even though the SNR can go higher than 35dB, there is some comfort
+ * factor in having a range of strong signals that can show at 100%
+ */
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ u16 snr;
+ int ret;
+
+ ret = fe->ops.read_snr(fe, &snr);
+ if (ret != 0)
+ return ret;
+ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
+ /* scale the range 0 - 35*2^24 into 0 - 65535 */
+ if (state->snr >= 8960 * 0x10000)
+ *strength = 0xffff;
+ else
+ *strength = state->snr / 8960;
+
+ return 0;
+}
+
+
+static int lgdt3302_read_status(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u8 buf[3];
+ int err;
+
+ *status = 0; /* Reset status result */
+
+ /* AGC status register */
+ i2c_read_demod_bytes(state, AGC_STATUS, buf, 1);
+ dprintk(state, "AGC_STATUS = 0x%02x\n", buf[0]);
+ if ((buf[0] & 0x0c) == 0x8) {
+ /*
+ * Test signal does not exist flag
+ * as well as the AGC lock flag.
+ */
+ *status |= FE_HAS_SIGNAL;
+ }
+
+ /*
+ * You must set the Mask bits to 1 in the IRQ_MASK in order
+ * to see that status bit in the IRQ_STATUS register.
+ * This is done in SwReset();
+ */
+
+ /* signal status */
+ i2c_read_demod_bytes(state, TOP_CONTROL, buf, sizeof(buf));
+ dprintk(state,
+ "TOP_CONTROL = 0x%02x, IRO_MASK = 0x%02x, IRQ_STATUS = 0x%02x\n",
+ buf[0], buf[1], buf[2]);
+
+ /* sync status */
+ if ((buf[2] & 0x03) == 0x01)
+ *status |= FE_HAS_SYNC;
+
+ /* FEC error status */
+ if ((buf[2] & 0x0c) == 0x08)
+ *status |= FE_HAS_LOCK | FE_HAS_VITERBI;
+
+ /* Carrier Recovery Lock Status Register */
+ i2c_read_demod_bytes(state, CARRIER_LOCK, buf, 1);
+ dprintk(state, "CARRIER_LOCK = 0x%02x\n", buf[0]);
+ switch (state->current_modulation) {
+ case QAM_256:
+ case QAM_64:
+ /* Need to understand why there are 3 lock levels here */
+ if ((buf[0] & 0x07) == 0x07)
+ *status |= FE_HAS_CARRIER;
+ break;
+ case VSB_8:
+ if ((buf[0] & 0x80) == 0x80)
+ *status |= FE_HAS_CARRIER;
+ break;
+ default:
+ dev_warn(&state->client->dev,
+ "%s: Modulation set to unsupported value\n",
+ __func__);
+ }
+
+ if (!(*status & FE_HAS_LOCK)) {
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return 0;
+ }
+
+ if (state->last_stats_time &&
+ time_is_after_jiffies(state->last_stats_time))
+ return 0;
+
+ state->last_stats_time = jiffies + msecs_to_jiffies(1000);
+
+ err = lgdt3302_read_snr(fe);
+ if (!err) {
+ p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ p->cnr.stat[0].svalue = (((u64)state->snr) * 1000) >> 24;
+ } else {
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ err = i2c_read_demod_bytes(state, LGDT3302_PACKET_ERR_COUNTER1,
+ buf, sizeof(buf));
+ if (!err) {
+ state->ucblocks = (buf[0] << 8) | buf[1];
+
+ dprintk(state, "UCB = 0x%02x\n", state->ucblocks);
+
+ p->block_error.stat[0].uvalue += state->ucblocks;
+ /* FIXME: what's the basis for block count */
+ p->block_count.stat[0].uvalue += 10000;
+
+ p->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ } else {
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ return 0;
+}
+
+static int lgdt3303_read_status(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u8 buf[3];
+ int err;
+
+ *status = 0; /* Reset status result */
+
+ /* lgdt3303 AGC status register */
+ err = i2c_read_demod_bytes(state, 0x58, buf, 1);
+ if (err < 0)
+ return err;
+
+ dprintk(state, "AGC_STATUS = 0x%02x\n", buf[0]);
+ if ((buf[0] & 0x21) == 0x01) {
+ /*
+ * Test input signal does not exist flag
+ * as well as the AGC lock flag.
+ */
+ *status |= FE_HAS_SIGNAL;
+ }
+
+ /* Carrier Recovery Lock Status Register */
+ i2c_read_demod_bytes(state, CARRIER_LOCK, buf, 1);
+ dprintk(state, "CARRIER_LOCK = 0x%02x\n", buf[0]);
+ switch (state->current_modulation) {
+ case QAM_256:
+ case QAM_64:
+ /* Need to understand why there are 3 lock levels here */
+ if ((buf[0] & 0x07) == 0x07)
+ *status |= FE_HAS_CARRIER;
+ else
+ break;
+ i2c_read_demod_bytes(state, 0x8a, buf, 1);
+ dprintk(state, "QAM LOCK = 0x%02x\n", buf[0]);
+
+ if ((buf[0] & 0x04) == 0x04)
+ *status |= FE_HAS_SYNC;
+ if ((buf[0] & 0x01) == 0x01)
+ *status |= FE_HAS_LOCK;
+ if ((buf[0] & 0x08) == 0x08)
+ *status |= FE_HAS_VITERBI;
+ break;
+ case VSB_8:
+ if ((buf[0] & 0x80) == 0x80)
+ *status |= FE_HAS_CARRIER;
+ else
+ break;
+ i2c_read_demod_bytes(state, 0x38, buf, 1);
+ dprintk(state, "8-VSB LOCK = 0x%02x\n", buf[0]);
+
+ if ((buf[0] & 0x02) == 0x00)
+ *status |= FE_HAS_SYNC;
+ if ((buf[0] & 0x01) == 0x01)
+ *status |= FE_HAS_VITERBI | FE_HAS_LOCK;
+ break;
+ default:
+ dev_warn(&state->client->dev,
+ "%s: Modulation set to unsupported value\n",
+ __func__);
+ }
+
+ if (!(*status & FE_HAS_LOCK)) {
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return 0;
+ }
+
+ if (state->last_stats_time &&
+ time_is_after_jiffies(state->last_stats_time))
+ return 0;
+
+ state->last_stats_time = jiffies + msecs_to_jiffies(1000);
+
+ err = lgdt3303_read_snr(fe);
+ if (!err) {
+ p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ p->cnr.stat[0].svalue = (((u64)state->snr) * 1000) >> 24;
+ } else {
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ err = i2c_read_demod_bytes(state, LGDT3303_PACKET_ERR_COUNTER1,
+ buf, sizeof(buf));
+ if (!err) {
+ state->ucblocks = (buf[0] << 8) | buf[1];
+
+ dprintk(state, "UCB = 0x%02x\n", state->ucblocks);
+
+ p->block_error.stat[0].uvalue += state->ucblocks;
+ /* FIXME: what's the basis for block count */
+ p->block_count.stat[0].uvalue += 10000;
+
+ p->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ } else {
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ return 0;
+}
+
+static int
+lgdt330x_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fe_tune_settings)
+{
+ /* I have no idea about this - it may not be needed */
+ fe_tune_settings->min_delay_ms = 500;
+ fe_tune_settings->step_size = 0;
+ fe_tune_settings->max_drift = 0;
+ return 0;
+}
+
+static void lgdt330x_release(struct dvb_frontend *fe)
+{
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
+
+ dev_dbg(&client->dev, "\n");
+
+ i2c_unregister_device(client);
+}
+
+static struct dvb_frontend *lgdt330x_get_dvb_frontend(struct i2c_client *client)
+{
+ struct lgdt330x_state *state = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ return &state->frontend;
+}
+
+static const struct dvb_frontend_ops lgdt3302_ops;
+static const struct dvb_frontend_ops lgdt3303_ops;
+
+static int lgdt330x_probe(struct i2c_client *client)
+{
+ struct lgdt330x_state *state = NULL;
+ u8 buf[1];
+
+ /* Allocate memory for the internal state */
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ goto error;
+
+ /* Setup the state */
+ memcpy(&state->config, client->dev.platform_data,
+ sizeof(state->config));
+ i2c_set_clientdata(client, state);
+ state->client = client;
+
+ /* Create dvb_frontend */
+ switch (state->config.demod_chip) {
+ case LGDT3302:
+ memcpy(&state->frontend.ops, &lgdt3302_ops,
+ sizeof(struct dvb_frontend_ops));
+ break;
+ case LGDT3303:
+ memcpy(&state->frontend.ops, &lgdt3303_ops,
+ sizeof(struct dvb_frontend_ops));
+ break;
+ default:
+ goto error;
+ }
+ state->frontend.demodulator_priv = state;
+
+ /* Setup get frontend callback */
+ state->config.get_dvb_frontend = lgdt330x_get_dvb_frontend;
+
+ /* Verify communication with demod chip */
+ if (i2c_read_demod_bytes(state, 2, buf, 1))
+ goto error;
+
+ state->current_frequency = -1;
+ state->current_modulation = -1;
+
+ dev_info(&state->client->dev,
+ "Demod loaded for LGDT330%s chip\n",
+ state->config.demod_chip == LGDT3302 ? "2" : "3");
+
+ return 0;
+
+error:
+ kfree(state);
+ if (debug)
+ dev_printk(KERN_DEBUG, &client->dev, "Error loading lgdt330x driver\n");
+ return -ENODEV;
+}
+struct dvb_frontend *lgdt330x_attach(const struct lgdt330x_config *_config,
+ u8 demod_address,
+ struct i2c_adapter *i2c)
+{
+ struct i2c_client *client;
+ struct i2c_board_info board_info = {};
+ struct lgdt330x_config config = *_config;
+
+ strscpy(board_info.type, "lgdt330x", sizeof(board_info.type));
+ board_info.addr = demod_address;
+ board_info.platform_data = &config;
+ client = i2c_new_client_device(i2c, &board_info);
+ if (!i2c_client_has_driver(client))
+ return NULL;
+
+ return lgdt330x_get_dvb_frontend(client);
+}
+EXPORT_SYMBOL_GPL(lgdt330x_attach);
+
+static const struct dvb_frontend_ops lgdt3302_ops = {
+ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
+ .info = {
+ .name = "LG Electronics LGDT3302 VSB/QAM Frontend",
+ .frequency_min_hz = 54 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 62500,
+ .symbol_rate_min = 5056941, /* QAM 64 */
+ .symbol_rate_max = 10762000, /* VSB 8 */
+ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+ },
+ .init = lgdt330x_init,
+ .set_frontend = lgdt330x_set_parameters,
+ .get_frontend = lgdt330x_get_frontend,
+ .get_tune_settings = lgdt330x_get_tune_settings,
+ .read_status = lgdt3302_read_status,
+ .read_signal_strength = lgdt330x_read_signal_strength,
+ .read_snr = lgdt330x_read_snr,
+ .read_ucblocks = lgdt330x_read_ucblocks,
+ .release = lgdt330x_release,
+};
+
+static const struct dvb_frontend_ops lgdt3303_ops = {
+ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
+ .info = {
+ .name = "LG Electronics LGDT3303 VSB/QAM Frontend",
+ .frequency_min_hz = 54 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 62500,
+ .symbol_rate_min = 5056941, /* QAM 64 */
+ .symbol_rate_max = 10762000, /* VSB 8 */
+ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+ },
+ .init = lgdt330x_init,
+ .set_frontend = lgdt330x_set_parameters,
+ .get_frontend = lgdt330x_get_frontend,
+ .get_tune_settings = lgdt330x_get_tune_settings,
+ .read_status = lgdt3303_read_status,
+ .read_signal_strength = lgdt330x_read_signal_strength,
+ .read_snr = lgdt330x_read_snr,
+ .read_ucblocks = lgdt330x_read_ucblocks,
+ .release = lgdt330x_release,
+};
+
+static void lgdt330x_remove(struct i2c_client *client)
+{
+ struct lgdt330x_state *state = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ kfree(state);
+}
+
+static const struct i2c_device_id lgdt330x_id_table[] = {
+ { "lgdt330x" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, lgdt330x_id_table);
+
+static struct i2c_driver lgdt330x_driver = {
+ .driver = {
+ .name = "lgdt330x",
+ .suppress_bind_attrs = true,
+ },
+ .probe = lgdt330x_probe,
+ .remove = lgdt330x_remove,
+ .id_table = lgdt330x_id_table,
+};
+
+module_i2c_driver(lgdt330x_driver);
+
+
+MODULE_DESCRIPTION("LGDT330X (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulator Driver");
+MODULE_AUTHOR("Wilson Michaels");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/lgdt330x.h b/drivers/media/dvb-frontends/lgdt330x.h
new file mode 100644
index 000000000000..99834aa60824
--- /dev/null
+++ b/drivers/media/dvb-frontends/lgdt330x.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for LGDT3302 and LGDT3303 - VSB/QAM
+ *
+ * Copyright (C) 2005 Wilson Michaels <wilsonmichaels@earthlink.net>
+ */
+
+#ifndef LGDT330X_H
+#define LGDT330X_H
+
+#include <linux/dvb/frontend.h>
+
+typedef enum lg_chip_t {
+ UNDEFINED,
+ LGDT3302,
+ LGDT3303
+}lg_chip_type;
+
+/**
+ * struct lgdt330x_config - contains lgdt330x configuration
+ *
+ * @demod_chip: LG demodulator chip LGDT3302 or LGDT3303
+ * @serial_mpeg: MPEG hardware interface - 0:parallel 1:serial
+ * @pll_rf_set: Callback function to set PLL interface
+ * @set_ts_params: Callback function to set device param for start_dma
+ * @clock_polarity_flip:
+ * Flip the polarity of the mpeg data transfer clock using alternate
+ * init data.
+ * This option applies ONLY to LGDT3303 - 0:disabled (default) 1:enabled
+ * @get_dvb_frontend:
+ * returns the frontend associated with this I2C client.
+ * Filled by the driver.
+ */
+struct lgdt330x_config
+{
+ lg_chip_type demod_chip;
+ int serial_mpeg;
+ int (*pll_rf_set) (struct dvb_frontend* fe, int index);
+ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
+ int clock_polarity_flip;
+
+ struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
+};
+
+#if IS_REACHABLE(CONFIG_DVB_LGDT330X)
+struct dvb_frontend *lgdt330x_attach(const struct lgdt330x_config *config,
+ u8 demod_address,
+ struct i2c_adapter *i2c);
+#else
+static inline
+struct dvb_frontend *lgdt330x_attach(const struct lgdt330x_config *config,
+ u8 demod_address,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_LGDT330X
+
+#endif /* LGDT330X_H */
diff --git a/drivers/media/dvb-frontends/lgdt330x_priv.h b/drivers/media/dvb-frontends/lgdt330x_priv.h
new file mode 100644
index 000000000000..eb3758dd712c
--- /dev/null
+++ b/drivers/media/dvb-frontends/lgdt330x_priv.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for LGDT3302 and LGDT3303 - VSB/QAM
+ *
+ * Copyright (C) 2005 Wilson Michaels <wilsonmichaels@earthlink.net>
+ */
+
+#ifndef _LGDT330X_PRIV_
+#define _LGDT330X_PRIV_
+
+/* i2c control register addresses */
+enum I2C_REG {
+ TOP_CONTROL= 0x00,
+ IRQ_MASK= 0x01,
+ IRQ_STATUS= 0x02,
+ VSB_CARRIER_FREQ0= 0x16,
+ VSB_CARRIER_FREQ1= 0x17,
+ VSB_CARRIER_FREQ2= 0x18,
+ VSB_CARRIER_FREQ3= 0x19,
+ CARRIER_MSEQAM1= 0x1a,
+ CARRIER_MSEQAM2= 0x1b,
+ CARRIER_LOCK= 0x1c,
+ TIMING_RECOVERY= 0x1d,
+ AGC_DELAY0= 0x2a,
+ AGC_DELAY1= 0x2b,
+ AGC_DELAY2= 0x2c,
+ AGC_RF_BANDWIDTH0= 0x2d,
+ AGC_RF_BANDWIDTH1= 0x2e,
+ AGC_RF_BANDWIDTH2= 0x2f,
+ AGC_LOOP_BANDWIDTH0= 0x30,
+ AGC_LOOP_BANDWIDTH1= 0x31,
+ AGC_FUNC_CTRL1= 0x32,
+ AGC_FUNC_CTRL2= 0x33,
+ AGC_FUNC_CTRL3= 0x34,
+ AGC_RFIF_ACC0= 0x39,
+ AGC_RFIF_ACC1= 0x3a,
+ AGC_RFIF_ACC2= 0x3b,
+ AGC_STATUS= 0x3f,
+ SYNC_STATUS_VSB= 0x43,
+ DEMUX_CONTROL= 0x66,
+ LGDT3302_EQPH_ERR0= 0x47,
+ LGDT3302_EQ_ERR1= 0x48,
+ LGDT3302_EQ_ERR2= 0x49,
+ LGDT3302_PH_ERR1= 0x4a,
+ LGDT3302_PH_ERR2= 0x4b,
+ LGDT3302_PACKET_ERR_COUNTER1= 0x6a,
+ LGDT3302_PACKET_ERR_COUNTER2= 0x6b,
+ LGDT3303_EQPH_ERR0= 0x6e,
+ LGDT3303_EQ_ERR1= 0x6f,
+ LGDT3303_EQ_ERR2= 0x70,
+ LGDT3303_PH_ERR1= 0x71,
+ LGDT3303_PH_ERR2= 0x72,
+ LGDT3303_PACKET_ERR_COUNTER1= 0x8b,
+ LGDT3303_PACKET_ERR_COUNTER2= 0x8c,
+};
+
+#endif /* _LGDT330X_PRIV_ */
diff --git a/drivers/media/dvb/frontends/lgs8gl5.c b/drivers/media/dvb-frontends/lgs8gl5.c
index 2cec8041a106..872abb70d1b6 100644
--- a/drivers/media/dvb/frontends/lgs8gl5.c
+++ b/drivers/media/dvb-frontends/lgs8gl5.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver
Copyright (C) 2008 Sirius International (Hong Kong) Limited
Timothy Lee <timothy.lee@siriushk.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
@@ -25,7 +13,7 @@
#include <linux/module.h>
#include <linux/string.h>
#include <linux/slab.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "lgs8gl5.h"
@@ -249,7 +237,7 @@ lgs8gl5_init(struct dvb_frontend *fe)
static int
-lgs8gl5_read_status(struct dvb_frontend *fe, fe_status_t *status)
+lgs8gl5_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct lgs8gl5_state *state = fe->demodulator_priv;
u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
@@ -336,10 +324,11 @@ lgs8gl5_set_frontend(struct dvb_frontend *fe)
static int
-lgs8gl5_get_frontend(struct dvb_frontend *fe)
+lgs8gl5_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct lgs8gl5_state *state = fe->demodulator_priv;
+
u8 inv = lgs8gl5_read_reg(state, REG_INVERSION);
p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF;
@@ -375,7 +364,7 @@ lgs8gl5_release(struct dvb_frontend *fe)
}
-static struct dvb_frontend_ops lgs8gl5_ops;
+static const struct dvb_frontend_ops lgs8gl5_ops;
struct dvb_frontend*
@@ -411,14 +400,13 @@ error:
EXPORT_SYMBOL(lgs8gl5_attach);
-static struct dvb_frontend_ops lgs8gl5_ops = {
- .delsys = { SYS_DMBTH },
+static const struct dvb_frontend_ops lgs8gl5_ops = {
+ .delsys = { SYS_DTMB },
.info = {
.name = "Legend Silicon LGS-8GL5 DMB-TH",
- .frequency_min = 474000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 10000,
- .frequency_tolerance = 0,
+ .frequency_min_hz = 474 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 10 * kHz,
.caps = FE_CAN_FEC_AUTO |
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 |
FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
diff --git a/drivers/media/dvb-frontends/lgs8gl5.h b/drivers/media/dvb-frontends/lgs8gl5.h
new file mode 100644
index 000000000000..1ea9c4b5232c
--- /dev/null
+++ b/drivers/media/dvb-frontends/lgs8gl5.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver
+
+ Copyright (C) 2008 Sirius International (Hong Kong) Limited
+ Timothy Lee <timothy.lee@siriushk.com>
+
+
+*/
+
+#ifndef LGS8GL5_H
+#define LGS8GL5_H
+
+#include <linux/dvb/frontend.h>
+
+struct lgs8gl5_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_LGS8GL5)
+extern struct dvb_frontend *lgs8gl5_attach(
+ const struct lgs8gl5_config *config, struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *lgs8gl5_attach(
+ const struct lgs8gl5_config *config, struct i2c_adapter *i2c) {
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_LGS8GL5 */
+
+#endif /* LGS8GL5_H */
diff --git a/drivers/media/dvb/frontends/lgs8gxx.c b/drivers/media/dvb-frontends/lgs8gxx.c
index 568363a10a31..ffaf60e16ecd 100644
--- a/drivers/media/dvb/frontends/lgs8gxx.c
+++ b/drivers/media/dvb-frontends/lgs8gxx.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator
* LGS8913, LGS8GL5, LGS8G75
@@ -6,27 +7,12 @@
* Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com>
* Copyright (C) 2008 Sirius International (Hong Kong) Limited
* Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
#include <asm/div64.h>
#include <linux/firmware.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "lgs8gxx.h"
#include "lgs8gxx_priv.h"
@@ -40,6 +26,8 @@
static int debug;
static int fake_signal_str = 1;
+#define LGS8GXX_FIRMWARE "lgs8g75.fw"
+
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
@@ -592,7 +580,7 @@ static int lgs8g75_init_data(struct lgs8gxx_state *priv)
int rc;
int i;
- rc = request_firmware(&fw, "lgs8g75.fw", &priv->i2c->dev);
+ rc = request_firmware(&fw, LGS8GXX_FIRMWARE, &priv->i2c->dev);
if (rc)
return rc;
@@ -670,7 +658,7 @@ static int lgs8gxx_write(struct dvb_frontend *fe, const u8 buf[], int len)
static int lgs8gxx_set_fe(struct dvb_frontend *fe)
{
-
+ struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
struct lgs8gxx_state *priv = fe->demodulator_priv;
dprintk("%s\n", __func__);
@@ -687,17 +675,7 @@ static int lgs8gxx_set_fe(struct dvb_frontend *fe)
msleep(10);
- return 0;
-}
-
-static int lgs8gxx_get_fe(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
- dprintk("%s\n", __func__);
-
/* TODO: get real readings from device */
- /* inversion status */
- fe_params->inversion = INVERSION_OFF;
/* bandwidth */
fe_params->bandwidth_hz = 8000000;
@@ -730,7 +708,8 @@ int lgs8gxx_get_tune_settings(struct dvb_frontend *fe,
return 0;
}
-static int lgs8gxx_read_status(struct dvb_frontend *fe, fe_status_t *fe_status)
+static int lgs8gxx_read_status(struct dvb_frontend *fe,
+ enum fe_status *fe_status)
{
struct lgs8gxx_state *priv = fe->demodulator_priv;
s8 ret;
@@ -992,13 +971,13 @@ static int lgs8gxx_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
return lgs8gxx_write_reg(priv, 0x01, 0);
}
-static struct dvb_frontend_ops lgs8gxx_ops = {
- .delsys = { SYS_DMBTH },
+static const struct dvb_frontend_ops lgs8gxx_ops = {
+ .delsys = { SYS_DTMB },
.info = {
.name = "Legend Silicon LGS8913/LGS8GXX DMB-TH",
- .frequency_min = 474000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 10000,
+ .frequency_min_hz = 474 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 10 * kHz,
.caps =
FE_CAN_FEC_AUTO |
FE_CAN_QAM_AUTO |
@@ -1013,7 +992,6 @@ static struct dvb_frontend_ops lgs8gxx_ops = {
.i2c_gate_ctrl = lgs8gxx_i2c_gate_ctrl,
.set_frontend = lgs8gxx_set_fe,
- .get_frontend = lgs8gxx_get_fe,
.get_tune_settings = lgs8gxx_get_tune_settings,
.read_status = lgs8gxx_read_status,
@@ -1065,8 +1043,9 @@ error_out:
return NULL;
}
-EXPORT_SYMBOL(lgs8gxx_attach);
+EXPORT_SYMBOL_GPL(lgs8gxx_attach);
MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver");
MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(LGS8GXX_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/lgs8gxx.h b/drivers/media/dvb-frontends/lgs8gxx.h
new file mode 100644
index 000000000000..5b2f13a8be21
--- /dev/null
+++ b/drivers/media/dvb-frontends/lgs8gxx.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator
+ * LGS8913, LGS8GL5, LGS8G75
+ * experimental support LGS8G42, LGS8G52
+ *
+ * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com>
+ * Copyright (C) 2008 Sirius International (Hong Kong) Limited
+ * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
+ */
+
+#ifndef __LGS8GXX_H__
+#define __LGS8GXX_H__
+
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+#define LGS8GXX_PROD_LGS8913 0
+#define LGS8GXX_PROD_LGS8GL5 1
+#define LGS8GXX_PROD_LGS8G42 3
+#define LGS8GXX_PROD_LGS8G52 4
+#define LGS8GXX_PROD_LGS8G54 5
+#define LGS8GXX_PROD_LGS8G75 6
+
+struct lgs8gxx_config {
+
+ /* product type */
+ u8 prod;
+
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* parallel or serial transport stream */
+ u8 serial_ts;
+
+ /* transport stream polarity*/
+ u8 ts_clk_pol;
+
+ /* transport stream clock gated by ts_valid */
+ u8 ts_clk_gated;
+
+ /* A/D Clock frequency */
+ u32 if_clk_freq; /* in kHz */
+
+ /* IF frequency */
+ u32 if_freq; /* in kHz */
+
+ /*Use External ADC*/
+ u8 ext_adc;
+
+ /*External ADC output two's complement*/
+ u8 adc_signed;
+
+ /*Sample IF data at falling edge of IF_CLK*/
+ u8 if_neg_edge;
+
+ /*IF use Negative center frequency*/
+ u8 if_neg_center;
+
+ /*8G75 internal ADC input range selection*/
+ /*0: 0.8Vpp, 1: 1.0Vpp, 2: 1.6Vpp, 3: 2.0Vpp*/
+ u8 adc_vpp;
+
+ /* slave address and configuration of the tuner */
+ u8 tuner_address;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_LGS8GXX)
+extern struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline
+struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config,
+ struct i2c_adapter *i2c) {
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_LGS8GXX */
+
+#endif /* __LGS8GXX_H__ */
diff --git a/drivers/media/dvb-frontends/lgs8gxx_priv.h b/drivers/media/dvb-frontends/lgs8gxx_priv.h
new file mode 100644
index 000000000000..4c295a7586b5
--- /dev/null
+++ b/drivers/media/dvb-frontends/lgs8gxx_priv.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator
+ * LGS8913, LGS8GL5, LGS8G75
+ * experimental support LGS8G42, LGS8G52
+ *
+ * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com>
+ * Copyright (C) 2008 Sirius International (Hong Kong) Limited
+ * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
+ */
+
+#ifndef LGS8913_PRIV_H
+#define LGS8913_PRIV_H
+
+struct lgs8gxx_state {
+ struct i2c_adapter *i2c;
+ /* configuration settings */
+ const struct lgs8gxx_config *config;
+ struct dvb_frontend frontend;
+ u16 curr_gi; /* current guard interval */
+};
+
+#define SC_MASK 0x1C /* Sub-Carrier Modulation Mask */
+#define SC_QAM64 0x10 /* 64QAM modulation */
+#define SC_QAM32 0x0C /* 32QAM modulation */
+#define SC_QAM16 0x08 /* 16QAM modulation */
+#define SC_QAM4NR 0x04 /* 4QAM-NR modulation */
+#define SC_QAM4 0x00 /* 4QAM modulation */
+
+#define LGS_FEC_MASK 0x03 /* FEC Rate Mask */
+#define LGS_FEC_0_4 0x00 /* FEC Rate 0.4 */
+#define LGS_FEC_0_6 0x01 /* FEC Rate 0.6 */
+#define LGS_FEC_0_8 0x02 /* FEC Rate 0.8 */
+
+#define TIM_MASK 0x20 /* Time Interleave Length Mask */
+#define TIM_LONG 0x20 /* Time Interleave Length = 720 */
+#define TIM_MIDDLE 0x00 /* Time Interleave Length = 240 */
+
+#define CF_MASK 0x80 /* Control Frame Mask */
+#define CF_EN 0x80 /* Control Frame On */
+
+#define GI_MASK 0x03 /* Guard Interval Mask */
+#define GI_420 0x00 /* 1/9 Guard Interval */
+#define GI_595 0x01 /* */
+#define GI_945 0x02 /* 1/4 Guard Interval */
+
+
+#define TS_PARALLEL 0x00 /* Parallel TS Output a.k.a. SPI */
+#define TS_SERIAL 0x01 /* Serial TS Output a.k.a. SSI */
+#define TS_CLK_NORMAL 0x00 /* MPEG Clock Normal */
+#define TS_CLK_INVERTED 0x02 /* MPEG Clock Inverted */
+#define TS_CLK_GATED 0x00 /* MPEG clock gated */
+#define TS_CLK_FREERUN 0x04 /* MPEG clock free running*/
+
+
+#endif
diff --git a/drivers/media/dvb-frontends/lnbh24.h b/drivers/media/dvb-frontends/lnbh24.h
new file mode 100644
index 000000000000..d8d0303c6e00
--- /dev/null
+++ b/drivers/media/dvb-frontends/lnbh24.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * lnbh24.h - driver for lnb supply and control ic lnbh24
+ *
+ * Copyright (C) 2009 NetUP Inc.
+ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
+ */
+
+#ifndef _LNBH24_H
+#define _LNBH24_H
+
+/* system register bits */
+#define LNBH24_OLF 0x01
+#define LNBH24_OTF 0x02
+#define LNBH24_EN 0x04
+#define LNBH24_VSEL 0x08
+#define LNBH24_LLC 0x10
+#define LNBH24_TEN 0x20
+#define LNBH24_TTX 0x40
+#define LNBH24_PCL 0x80
+
+#include <linux/dvb/frontend.h>
+
+#if IS_REACHABLE(CONFIG_DVB_LNBP21)
+/* override_set and override_clear control which
+ system register bits (above) to always set & clear */
+extern struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, u8 override_set,
+ u8 override_clear, u8 i2c_addr);
+#else
+static inline struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, u8 override_set,
+ u8 override_clear, u8 i2c_addr)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb-frontends/lnbh25.c b/drivers/media/dvb-frontends/lnbh25.c
new file mode 100644
index 000000000000..41bec050642b
--- /dev/null
+++ b/drivers/media/dvb-frontends/lnbh25.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lnbh25.c
+ *
+ * Driver for LNB supply and control IC LNBH25
+ *
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include <media/dvb_frontend.h>
+#include "lnbh25.h"
+
+/**
+ * struct lnbh25_priv - LNBH25 driver private data
+ * @i2c: pointer to the I2C adapter structure
+ * @i2c_address: I2C address of LNBH25 SEC chip
+ * @config: Registers configuration:
+ * offset 0: 1st register address, always 0x02 (DATA1)
+ * offset 1: DATA1 register value
+ * offset 2: DATA2 register value
+ */
+struct lnbh25_priv {
+ struct i2c_adapter *i2c;
+ u8 i2c_address;
+ u8 config[3];
+};
+
+#define LNBH25_STATUS_OFL 0x1
+#define LNBH25_STATUS_VMON 0x4
+#define LNBH25_VSEL_13 0x03
+#define LNBH25_VSEL_18 0x0a
+
+static int lnbh25_read_vmon(struct lnbh25_priv *priv)
+{
+ int i, ret;
+ u8 addr = 0x00;
+ u8 status[6];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->i2c_address,
+ .flags = 0,
+ .len = 1,
+ .buf = &addr
+ }, {
+ .addr = priv->i2c_address,
+ .flags = I2C_M_RD,
+ .len = sizeof(status),
+ .buf = status
+ }
+ };
+
+ for (i = 0; i < 2; i++) {
+ ret = i2c_transfer(priv->i2c, &msg[i], 1);
+ if (ret >= 0 && ret != 1)
+ ret = -EIO;
+ if (ret < 0) {
+ dev_dbg(&priv->i2c->dev,
+ "%s(): I2C transfer %d failed (%d)\n",
+ __func__, i, ret);
+ return ret;
+ }
+ }
+ dev_dbg(&priv->i2c->dev, "%s(): %*ph\n",
+ __func__, (int) sizeof(status), status);
+ if ((status[0] & (LNBH25_STATUS_OFL | LNBH25_STATUS_VMON)) != 0) {
+ dev_err(&priv->i2c->dev,
+ "%s(): voltage in failure state, status reg 0x%x\n",
+ __func__, status[0]);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int lnbh25_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage)
+{
+ int ret;
+ u8 data1_reg;
+ const char *vsel;
+ struct lnbh25_priv *priv = fe->sec_priv;
+ struct i2c_msg msg = {
+ .addr = priv->i2c_address,
+ .flags = 0,
+ .len = sizeof(priv->config),
+ .buf = priv->config
+ };
+
+ switch (voltage) {
+ case SEC_VOLTAGE_OFF:
+ data1_reg = 0x00;
+ vsel = "Off";
+ break;
+ case SEC_VOLTAGE_13:
+ data1_reg = LNBH25_VSEL_13;
+ vsel = "13V";
+ break;
+ case SEC_VOLTAGE_18:
+ data1_reg = LNBH25_VSEL_18;
+ vsel = "18V";
+ break;
+ default:
+ return -EINVAL;
+ }
+ priv->config[1] = data1_reg;
+ dev_dbg(&priv->i2c->dev,
+ "%s(): %s, I2C 0x%x write [ %02x %02x %02x ]\n",
+ __func__, vsel, priv->i2c_address,
+ priv->config[0], priv->config[1], priv->config[2]);
+ ret = i2c_transfer(priv->i2c, &msg, 1);
+ if (ret >= 0 && ret != 1)
+ ret = -EIO;
+ if (ret < 0) {
+ dev_err(&priv->i2c->dev, "%s(): I2C transfer error (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ if (voltage != SEC_VOLTAGE_OFF) {
+ msleep(120);
+ ret = lnbh25_read_vmon(priv);
+ } else {
+ msleep(20);
+ ret = 0;
+ }
+ return ret;
+}
+
+static void lnbh25_release(struct dvb_frontend *fe)
+{
+ struct lnbh25_priv *priv = fe->sec_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+ lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF);
+ kfree(fe->sec_priv);
+ fe->sec_priv = NULL;
+}
+
+struct dvb_frontend *lnbh25_attach(struct dvb_frontend *fe,
+ struct lnbh25_config *cfg,
+ struct i2c_adapter *i2c)
+{
+ struct lnbh25_priv *priv;
+
+ dev_dbg(&i2c->dev, "%s()\n", __func__);
+ priv = kzalloc(sizeof(struct lnbh25_priv), GFP_KERNEL);
+ if (!priv)
+ return NULL;
+ priv->i2c_address = (cfg->i2c_address >> 1);
+ priv->i2c = i2c;
+ priv->config[0] = 0x02;
+ priv->config[1] = 0x00;
+ priv->config[2] = cfg->data2_config;
+ fe->sec_priv = priv;
+ if (lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF)) {
+ dev_err(&i2c->dev,
+ "%s(): no LNBH25 found at I2C addr 0x%02x\n",
+ __func__, priv->i2c_address);
+ kfree(priv);
+ fe->sec_priv = NULL;
+ return NULL;
+ }
+
+ fe->ops.release_sec = lnbh25_release;
+ fe->ops.set_voltage = lnbh25_set_voltage;
+
+ dev_info(&i2c->dev, "%s(): attached at I2C addr 0x%02x\n",
+ __func__, priv->i2c_address);
+ return fe;
+}
+EXPORT_SYMBOL_GPL(lnbh25_attach);
+
+MODULE_DESCRIPTION("ST LNBH25 driver");
+MODULE_AUTHOR("info@netup.ru");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/lnbh25.h b/drivers/media/dvb-frontends/lnbh25.h
new file mode 100644
index 000000000000..4f68f0c444b6
--- /dev/null
+++ b/drivers/media/dvb-frontends/lnbh25.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * lnbh25.c
+ *
+ * Driver for LNB supply and control IC LNBH25
+ *
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
+ */
+
+#ifndef LNBH25_H
+#define LNBH25_H
+
+#include <linux/i2c.h>
+#include <linux/dvb/frontend.h>
+
+/* 22 kHz tone enabled. Tone output controlled by DSQIN pin */
+#define LNBH25_TEN 0x01
+/* Low power mode activated (used only with 22 kHz tone output disabled) */
+#define LNBH25_LPM 0x02
+/* DSQIN input pin is set to receive external 22 kHz TTL signal source */
+#define LNBH25_EXTM 0x04
+
+struct lnbh25_config {
+ u8 i2c_address;
+ u8 data2_config;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_LNBH25)
+struct dvb_frontend *lnbh25_attach(
+ struct dvb_frontend *fe,
+ struct lnbh25_config *cfg,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *lnbh25_attach(
+ struct dvb_frontend *fe,
+ struct lnbh25_config *cfg,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb-frontends/lnbh29.c b/drivers/media/dvb-frontends/lnbh29.c
new file mode 100644
index 000000000000..410bae099c32
--- /dev/null
+++ b/drivers/media/dvb-frontends/lnbh29.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for LNB supply and control IC STMicroelectronics LNBH29
+//
+// Copyright (c) 2018 Socionext Inc.
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#include <media/dvb_frontend.h>
+#include "lnbh29.h"
+
+/**
+ * struct lnbh29_priv - LNBH29 driver private data
+ * @i2c: Pointer to the I2C adapter structure
+ * @i2c_address: I2C address of LNBH29 chip
+ * @config: Registers configuration
+ * offset 0: 1st register address, always 0x01 (DATA)
+ * offset 1: DATA register value
+ */
+struct lnbh29_priv {
+ struct i2c_adapter *i2c;
+ u8 i2c_address;
+ u8 config[2];
+};
+
+#define LNBH29_STATUS_OLF BIT(0)
+#define LNBH29_STATUS_OTF BIT(1)
+#define LNBH29_STATUS_VMON BIT(2)
+#define LNBH29_STATUS_PNG BIT(3)
+#define LNBH29_STATUS_PDO BIT(4)
+#define LNBH29_VSEL_MASK GENMASK(2, 0)
+#define LNBH29_VSEL_0 0x00
+/* Min: 13.188V, Typ: 13.667V, Max:14V */
+#define LNBH29_VSEL_13 0x03
+/* Min: 18.158V, Typ: 18.817V, Max:19.475V */
+#define LNBH29_VSEL_18 0x07
+
+static int lnbh29_read_vmon(struct lnbh29_priv *priv)
+{
+ u8 addr = 0x00;
+ u8 status[2];
+ int ret;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->i2c_address,
+ .flags = 0,
+ .len = 1,
+ .buf = &addr
+ }, {
+ .addr = priv->i2c_address,
+ .flags = I2C_M_RD,
+ .len = sizeof(status),
+ .buf = status
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret >= 0 && ret != 2)
+ ret = -EIO;
+ if (ret < 0) {
+ dev_dbg(&priv->i2c->dev, "LNBH29 I2C transfer failed (%d)\n",
+ ret);
+ return ret;
+ }
+
+ if (status[0] & (LNBH29_STATUS_OLF | LNBH29_STATUS_VMON)) {
+ dev_err(&priv->i2c->dev,
+ "LNBH29 voltage in failure state, status reg 0x%x\n",
+ status[0]);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int lnbh29_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage)
+{
+ struct lnbh29_priv *priv = fe->sec_priv;
+ u8 data_reg;
+ int ret;
+ struct i2c_msg msg = {
+ .addr = priv->i2c_address,
+ .flags = 0,
+ .len = sizeof(priv->config),
+ .buf = priv->config
+ };
+
+ switch (voltage) {
+ case SEC_VOLTAGE_OFF:
+ data_reg = LNBH29_VSEL_0;
+ break;
+ case SEC_VOLTAGE_13:
+ data_reg = LNBH29_VSEL_13;
+ break;
+ case SEC_VOLTAGE_18:
+ data_reg = LNBH29_VSEL_18;
+ break;
+ default:
+ return -EINVAL;
+ }
+ priv->config[1] &= ~LNBH29_VSEL_MASK;
+ priv->config[1] |= data_reg;
+
+ ret = i2c_transfer(priv->i2c, &msg, 1);
+ if (ret >= 0 && ret != 1)
+ ret = -EIO;
+ if (ret < 0) {
+ dev_err(&priv->i2c->dev, "LNBH29 I2C transfer error (%d)\n",
+ ret);
+ return ret;
+ }
+
+ /* Soft-start time (Vout 0V to 18V) is Typ. 6ms. */
+ usleep_range(6000, 20000);
+
+ if (voltage == SEC_VOLTAGE_OFF)
+ return 0;
+
+ return lnbh29_read_vmon(priv);
+}
+
+static void lnbh29_release(struct dvb_frontend *fe)
+{
+ lnbh29_set_voltage(fe, SEC_VOLTAGE_OFF);
+ kfree(fe->sec_priv);
+ fe->sec_priv = NULL;
+}
+
+struct dvb_frontend *lnbh29_attach(struct dvb_frontend *fe,
+ struct lnbh29_config *cfg,
+ struct i2c_adapter *i2c)
+{
+ struct lnbh29_priv *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return NULL;
+
+ priv->i2c_address = (cfg->i2c_address >> 1);
+ priv->i2c = i2c;
+ priv->config[0] = 0x01;
+ priv->config[1] = cfg->data_config;
+ fe->sec_priv = priv;
+
+ if (lnbh29_set_voltage(fe, SEC_VOLTAGE_OFF)) {
+ dev_err(&i2c->dev, "no LNBH29 found at I2C addr 0x%02x\n",
+ priv->i2c_address);
+ kfree(priv);
+ fe->sec_priv = NULL;
+ return NULL;
+ }
+
+ fe->ops.release_sec = lnbh29_release;
+ fe->ops.set_voltage = lnbh29_set_voltage;
+
+ dev_info(&i2c->dev, "LNBH29 attached at I2C addr 0x%02x\n",
+ priv->i2c_address);
+
+ return fe;
+}
+EXPORT_SYMBOL(lnbh29_attach);
+
+MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
+MODULE_DESCRIPTION("STMicroelectronics LNBH29 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/dvb-frontends/lnbh29.h b/drivers/media/dvb-frontends/lnbh29.h
new file mode 100644
index 000000000000..6179921520d9
--- /dev/null
+++ b/drivers/media/dvb-frontends/lnbh29.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver for LNB supply and control IC STMicroelectronics LNBH29
+ *
+ * Copyright (c) 2018 Socionext Inc.
+ */
+
+#ifndef LNBH29_H
+#define LNBH29_H
+
+#include <linux/i2c.h>
+#include <linux/dvb/frontend.h>
+
+/* Using very low E.S.R. capacitors or ceramic caps */
+#define LNBH29_DATA_COMP BIT(3)
+
+struct lnbh29_config {
+ u8 i2c_address;
+ u8 data_config;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_LNBH29)
+struct dvb_frontend *lnbh29_attach(struct dvb_frontend *fe,
+ struct lnbh29_config *cfg,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *lnbh29_attach(struct dvb_frontend *fe,
+ struct lnbh29_config *cfg,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/frontends/lnbp21.c b/drivers/media/dvb-frontends/lnbp21.c
index 13437259eeac..32593b1f75a3 100644
--- a/drivers/media/dvb/frontends/lnbp21.c
+++ b/drivers/media/dvb-frontends/lnbp21.c
@@ -1,28 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* lnbp21.c - driver for lnb supply and control ic lnbp21
*
* Copyright (C) 2006, 2009 Oliver Endriss <o.endriss@gmx.de>
* Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- *
- *
- * the project's page is at http://www.linuxtv.org
+ * the project's page is at https://linuxtv.org
*/
#include <linux/delay.h>
#include <linux/errno.h>
@@ -32,7 +15,7 @@
#include <linux/string.h>
#include <linux/slab.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "lnbp21.h"
#include "lnbh24.h"
@@ -45,7 +28,7 @@ struct lnbp21 {
};
static int lnbp21_set_voltage(struct dvb_frontend *fe,
- fe_sec_voltage_t voltage)
+ enum fe_sec_voltage voltage)
{
struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv;
struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0,
@@ -65,7 +48,7 @@ static int lnbp21_set_voltage(struct dvb_frontend *fe,
break;
default:
return -EINVAL;
- };
+ }
lnbp21->config |= lnbp21->override_or;
lnbp21->config &= lnbp21->override_and;
@@ -92,7 +75,7 @@ static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
}
static int lnbp21_set_tone(struct dvb_frontend *fe,
- fe_sec_tone_mode_t tone)
+ enum fe_sec_tone_mode tone)
{
struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv;
struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0,
@@ -108,7 +91,7 @@ static int lnbp21_set_tone(struct dvb_frontend *fe,
break;
default:
return -EINVAL;
- };
+ }
lnbp21->config |= lnbp21->override_or;
lnbp21->config &= lnbp21->override_and;
@@ -172,7 +155,7 @@ struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe,
return lnbx2x_attach(fe, i2c, override_set, override_clear,
i2c_addr, LNBH24_TTX);
}
-EXPORT_SYMBOL(lnbh24_attach);
+EXPORT_SYMBOL_GPL(lnbh24_attach);
struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c, u8 override_set,
@@ -181,7 +164,7 @@ struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe,
return lnbx2x_attach(fe, i2c, override_set, override_clear,
0x08, LNBP21_ISEL);
}
-EXPORT_SYMBOL(lnbp21_attach);
+EXPORT_SYMBOL_GPL(lnbp21_attach);
MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp21, lnbh24");
MODULE_AUTHOR("Oliver Endriss, Igor M. Liplianin");
diff --git a/drivers/media/dvb-frontends/lnbp21.h b/drivers/media/dvb-frontends/lnbp21.h
new file mode 100644
index 000000000000..f17cf2b7318c
--- /dev/null
+++ b/drivers/media/dvb-frontends/lnbp21.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * lnbp21.h - driver for lnb supply and control ic lnbp21
+ *
+ * Copyright (C) 2006 Oliver Endriss
+ *
+ * the project's page is at https://linuxtv.org
+ */
+
+#ifndef _LNBP21_H
+#define _LNBP21_H
+
+/* system register bits */
+/* [RO] 0=OK; 1=over current limit flag */
+#define LNBP21_OLF 0x01
+/* [RO] 0=OK; 1=over temperature flag (150 C) */
+#define LNBP21_OTF 0x02
+/* [RW] 0=disable LNB power, enable loopthrough
+ 1=enable LNB power, disable loopthrough */
+#define LNBP21_EN 0x04
+/* [RW] 0=low voltage (13/14V, vert pol)
+ 1=high voltage (18/19V,horiz pol) */
+#define LNBP21_VSEL 0x08
+/* [RW] increase LNB voltage by 1V:
+ 0=13/18V; 1=14/19V */
+#define LNBP21_LLC 0x10
+/* [RW] 0=tone controlled by DSQIN pin
+ 1=tone enable, disable DSQIN */
+#define LNBP21_TEN 0x20
+/* [RW] current limit select:
+ 0:Iout=500-650mA Isc=300mA
+ 1:Iout=400-550mA Isc=200mA */
+#define LNBP21_ISEL 0x40
+/* [RW] short-circuit protect:
+ 0=pulsed (dynamic) curr limiting
+ 1=static curr limiting */
+#define LNBP21_PCL 0x80
+
+#include <linux/dvb/frontend.h>
+
+#if IS_REACHABLE(CONFIG_DVB_LNBP21)
+/* override_set and override_clear control which
+ system register bits (above) to always set & clear */
+extern struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, u8 override_set,
+ u8 override_clear);
+#else
+static inline struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, u8 override_set,
+ u8 override_clear)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb-frontends/lnbp22.c b/drivers/media/dvb-frontends/lnbp22.c
new file mode 100644
index 000000000000..cb4ea5d3fad4
--- /dev/null
+++ b/drivers/media/dvb-frontends/lnbp22.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lnbp22.h - driver for lnb supply and control ic lnbp22
+ *
+ * Copyright (C) 2006 Dominik Kuhlen
+ * Based on lnbp21 driver
+ *
+ * the project's page is at https://linuxtv.org
+ */
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include <media/dvb_frontend.h>
+#include "lnbp22.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+
+#define dprintk(lvl, arg...) if (debug >= (lvl)) printk(arg)
+
+struct lnbp22 {
+ u8 config[4];
+ struct i2c_adapter *i2c;
+};
+
+static int lnbp22_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage)
+{
+ struct lnbp22 *lnbp22 = (struct lnbp22 *)fe->sec_priv;
+ struct i2c_msg msg = {
+ .addr = 0x08,
+ .flags = 0,
+ .buf = (char *)&lnbp22->config,
+ .len = sizeof(lnbp22->config),
+ };
+
+ dprintk(1, "%s: %d (18V=%d 13V=%d)\n", __func__, voltage,
+ SEC_VOLTAGE_18, SEC_VOLTAGE_13);
+
+ lnbp22->config[3] = 0x60; /* Power down */
+ switch (voltage) {
+ case SEC_VOLTAGE_OFF:
+ break;
+ case SEC_VOLTAGE_13:
+ lnbp22->config[3] |= LNBP22_EN;
+ break;
+ case SEC_VOLTAGE_18:
+ lnbp22->config[3] |= (LNBP22_EN | LNBP22_VSEL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dprintk(1, "%s: 0x%02x)\n", __func__, lnbp22->config[3]);
+ return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO;
+}
+
+static int lnbp22_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
+{
+ struct lnbp22 *lnbp22 = (struct lnbp22 *) fe->sec_priv;
+ struct i2c_msg msg = {
+ .addr = 0x08,
+ .flags = 0,
+ .buf = (char *)&lnbp22->config,
+ .len = sizeof(lnbp22->config),
+ };
+
+ dprintk(1, "%s: %d\n", __func__, (int)arg);
+ if (arg)
+ lnbp22->config[3] |= LNBP22_LLC;
+ else
+ lnbp22->config[3] &= ~LNBP22_LLC;
+
+ return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO;
+}
+
+static void lnbp22_release(struct dvb_frontend *fe)
+{
+ dprintk(1, "%s\n", __func__);
+ /* LNBP power off */
+ lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF);
+
+ /* free data */
+ kfree(fe->sec_priv);
+ fe->sec_priv = NULL;
+}
+
+struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c)
+{
+ struct lnbp22 *lnbp22 = kmalloc(sizeof(struct lnbp22), GFP_KERNEL);
+ if (!lnbp22)
+ return NULL;
+
+ /* default configuration */
+ lnbp22->config[0] = 0x00; /* ? */
+ lnbp22->config[1] = 0x28; /* ? */
+ lnbp22->config[2] = 0x48; /* ? */
+ lnbp22->config[3] = 0x60; /* Power down */
+ lnbp22->i2c = i2c;
+ fe->sec_priv = lnbp22;
+
+ /* detect if it is present or not */
+ if (lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF)) {
+ dprintk(0, "%s LNBP22 not found\n", __func__);
+ kfree(lnbp22);
+ fe->sec_priv = NULL;
+ return NULL;
+ }
+
+ /* install release callback */
+ fe->ops.release_sec = lnbp22_release;
+
+ /* override frontend ops */
+ fe->ops.set_voltage = lnbp22_set_voltage;
+ fe->ops.enable_high_lnb_voltage = lnbp22_enable_high_lnb_voltage;
+
+ return fe;
+}
+EXPORT_SYMBOL_GPL(lnbp22_attach);
+
+MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp22");
+MODULE_AUTHOR("Dominik Kuhlen");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/lnbp22.h b/drivers/media/dvb-frontends/lnbp22.h
new file mode 100644
index 000000000000..35810d9d09de
--- /dev/null
+++ b/drivers/media/dvb-frontends/lnbp22.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * lnbp22.h - driver for lnb supply and control ic lnbp22
+ *
+ * Copyright (C) 2006 Dominik Kuhlen
+ * Based on lnbp21.h
+ *
+ * the project's page is at https://linuxtv.org
+ */
+
+#ifndef _LNBP22_H
+#define _LNBP22_H
+
+/* Enable */
+#define LNBP22_EN 0x10
+/* Voltage selection */
+#define LNBP22_VSEL 0x02
+/* Plus 1 Volt Bit */
+#define LNBP22_LLC 0x01
+
+#include <linux/dvb/frontend.h>
+
+#if IS_REACHABLE(CONFIG_DVB_LNBP22)
+/*
+ * override_set and override_clear control which system register bits (above)
+ * to always set & clear
+ */
+extern struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_LNBP22 */
+
+#endif /* _LNBP22_H */
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
new file mode 100644
index 000000000000..5a03485686d9
--- /dev/null
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -0,0 +1,1965 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Montage Technology M88DS3103/M88RS6000 demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ */
+
+#include "m88ds3103_priv.h"
+
+static const struct dvb_frontend_ops m88ds3103_ops;
+
+/* write single register with mask */
+static int m88ds3103_update_bits(struct m88ds3103_dev *dev,
+ u8 reg, u8 mask, u8 val)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = regmap_bulk_read(dev->regmap, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return regmap_bulk_write(dev->regmap, reg, &val, 1);
+}
+
+/* write reg val table using reg addr auto increment */
+static int m88ds3103_wr_reg_val_tab(struct m88ds3103_dev *dev,
+ const struct m88ds3103_reg_val *tab, int tab_len)
+{
+ struct i2c_client *client = dev->client;
+ int ret, i, j;
+ u8 buf[83];
+
+ dev_dbg(&client->dev, "tab_len=%d\n", tab_len);
+
+ if (tab_len > 86) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (i = 0, j = 0; i < tab_len; i++, j++) {
+ buf[j] = tab[i].val;
+
+ if (i == tab_len - 1 || tab[i].reg != tab[i + 1].reg - 1 ||
+ !((j + 1) % (dev->cfg->i2c_wr_max - 1))) {
+ ret = regmap_bulk_write(dev->regmap, tab[i].reg - j, buf, j + 1);
+ if (ret)
+ goto err;
+
+ j = -1;
+ }
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+/*
+ * m88ds3103b demod has an internal device related to clocking. First the i2c
+ * gate must be opened, for one transaction, then writes will be allowed.
+ */
+static int m88ds3103b_dt_write(struct m88ds3103_dev *dev, int reg, int data)
+{
+ struct i2c_client *client = dev->client;
+ u8 buf[] = {reg, data};
+ u8 val;
+ int ret;
+ struct i2c_msg msg = {
+ .addr = dev->dt_addr, .flags = 0, .buf = buf, .len = 2
+ };
+
+ m88ds3103_update_bits(dev, 0x11, 0x01, 0x00);
+
+ val = 0x11;
+ ret = regmap_write(dev->regmap, 0x03, val);
+ if (ret)
+ dev_dbg(&client->dev, "fail=%d\n", ret);
+
+ ret = i2c_transfer(dev->dt_client->adapter, &msg, 1);
+ if (ret != 1) {
+ dev_err(&client->dev, "0x%02x (ret=%i, reg=0x%02x, value=0x%02x)\n",
+ dev->dt_addr, ret, reg, data);
+
+ m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+ return -EREMOTEIO;
+ }
+ m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+
+ dev_dbg(&client->dev, "0x%02x reg 0x%02x, value 0x%02x\n",
+ dev->dt_addr, reg, data);
+
+ return 0;
+}
+
+/*
+ * m88ds3103b demod has an internal device related to clocking. First the i2c
+ * gate must be opened, for two transactions, then reads will be allowed.
+ */
+static int m88ds3103b_dt_read(struct m88ds3103_dev *dev, u8 reg)
+{
+ struct i2c_client *client = dev->client;
+ int ret;
+ u8 val;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ {
+ .addr = dev->dt_addr,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ },
+ {
+ .addr = dev->dt_addr,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+
+ m88ds3103_update_bits(dev, 0x11, 0x01, 0x00);
+
+ val = 0x12;
+ ret = regmap_write(dev->regmap, 0x03, val);
+ if (ret)
+ dev_dbg(&client->dev, "fail=%d\n", ret);
+
+ ret = i2c_transfer(dev->dt_client->adapter, msg, 2);
+ if (ret != 2) {
+ dev_err(&client->dev, "0x%02x (ret=%d, reg=0x%02x)\n",
+ dev->dt_addr, ret, reg);
+
+ m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+ return -EREMOTEIO;
+ }
+ m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+
+ dev_dbg(&client->dev, "0x%02x reg 0x%02x, value 0x%02x\n",
+ dev->dt_addr, reg, b1[0]);
+
+ return b1[0];
+}
+
+/*
+ * Get the demodulator AGC PWM voltage setting supplied to the tuner.
+ */
+int m88ds3103_get_agc_pwm(struct dvb_frontend *fe, u8 *_agc_pwm)
+{
+ struct m88ds3103_dev *dev = fe->demodulator_priv;
+ unsigned tmp;
+ int ret;
+
+ ret = regmap_read(dev->regmap, 0x3f, &tmp);
+ if (ret == 0)
+ *_agc_pwm = tmp;
+ return ret;
+}
+EXPORT_SYMBOL(m88ds3103_get_agc_pwm);
+
+static int m88ds3103_read_status(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ struct m88ds3103_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, itmp;
+ unsigned int utmp;
+ u8 buf[3];
+
+ *status = 0;
+
+ if (!dev->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ ret = regmap_read(dev->regmap, 0xd1, &utmp);
+ if (ret)
+ goto err;
+
+ if ((utmp & 0x07) == 0x07)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ break;
+ case SYS_DVBS2:
+ ret = regmap_read(dev->regmap, 0x0d, &utmp);
+ if (ret)
+ goto err;
+
+ if ((utmp & 0x8f) == 0x8f)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid delivery_system\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ dev->fe_status = *status;
+ dev_dbg(&client->dev, "lock=%02x status=%02x\n", utmp, *status);
+
+ /* CNR */
+ if (dev->fe_status & FE_HAS_VITERBI) {
+ unsigned int cnr, noise, signal, noise_tot, signal_tot;
+
+ cnr = 0;
+ /* more iterations for more accurate estimation */
+ #define M88DS3103_SNR_ITERATIONS 3
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ itmp = 0;
+
+ for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) {
+ ret = regmap_read(dev->regmap, 0xff, &utmp);
+ if (ret)
+ goto err;
+
+ itmp += utmp;
+ }
+
+ /* use of single register limits max value to 15 dB */
+ /* SNR(X) dB = 10 * ln(X) / ln(10) dB */
+ itmp = DIV_ROUND_CLOSEST(itmp, 8 * M88DS3103_SNR_ITERATIONS);
+ if (itmp)
+ cnr = div_u64((u64) 10000 * intlog2(itmp), intlog2(10));
+ break;
+ case SYS_DVBS2:
+ noise_tot = 0;
+ signal_tot = 0;
+
+ for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) {
+ ret = regmap_bulk_read(dev->regmap, 0x8c, buf, 3);
+ if (ret)
+ goto err;
+
+ noise = buf[1] << 6; /* [13:6] */
+ noise |= buf[0] & 0x3f; /* [5:0] */
+ noise >>= 2;
+ signal = buf[2] * buf[2];
+ signal >>= 1;
+
+ noise_tot += noise;
+ signal_tot += signal;
+ }
+
+ noise = noise_tot / M88DS3103_SNR_ITERATIONS;
+ signal = signal_tot / M88DS3103_SNR_ITERATIONS;
+
+ /* SNR(X) dB = 10 * log10(X) dB */
+ if (signal > noise) {
+ itmp = signal / noise;
+ cnr = div_u64((u64) 10000 * intlog10(itmp), (1 << 24));
+ }
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid delivery_system\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (cnr) {
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = cnr;
+ } else {
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+ } else {
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* BER */
+ if (dev->fe_status & FE_HAS_LOCK) {
+ unsigned int utmp, post_bit_error, post_bit_count;
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ ret = regmap_write(dev->regmap, 0xf9, 0x04);
+ if (ret)
+ goto err;
+
+ ret = regmap_read(dev->regmap, 0xf8, &utmp);
+ if (ret)
+ goto err;
+
+ /* measurement ready? */
+ if (!(utmp & 0x10)) {
+ ret = regmap_bulk_read(dev->regmap, 0xf6, buf, 2);
+ if (ret)
+ goto err;
+
+ post_bit_error = buf[1] << 8 | buf[0] << 0;
+ post_bit_count = 0x800000;
+ dev->post_bit_error += post_bit_error;
+ dev->post_bit_count += post_bit_count;
+ dev->dvbv3_ber = post_bit_error;
+
+ /* restart measurement */
+ utmp |= 0x10;
+ ret = regmap_write(dev->regmap, 0xf8, utmp);
+ if (ret)
+ goto err;
+ }
+ break;
+ case SYS_DVBS2:
+ ret = regmap_bulk_read(dev->regmap, 0xd5, buf, 3);
+ if (ret)
+ goto err;
+
+ utmp = buf[2] << 16 | buf[1] << 8 | buf[0] << 0;
+
+ /* enough data? */
+ if (utmp > 4000) {
+ ret = regmap_bulk_read(dev->regmap, 0xf7, buf, 2);
+ if (ret)
+ goto err;
+
+ post_bit_error = buf[1] << 8 | buf[0] << 0;
+ post_bit_count = 32 * utmp; /* TODO: FEC */
+ dev->post_bit_error += post_bit_error;
+ dev->post_bit_count += post_bit_count;
+ dev->dvbv3_ber = post_bit_error;
+
+ /* restart measurement */
+ ret = regmap_write(dev->regmap, 0xd1, 0x01);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap, 0xf9, 0x01);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap, 0xf9, 0x00);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap, 0xd1, 0x00);
+ if (ret)
+ goto err;
+ }
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid delivery_system\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
+ } else {
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int m88ds3103b_select_mclk(struct m88ds3103_dev *dev)
+{
+ struct i2c_client *client = dev->client;
+ struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
+ u32 adc_Freq_MHz[3] = {96, 93, 99};
+ u8 reg16_list[3] = {96, 92, 100}, reg16, reg15;
+ u32 offset_MHz[3];
+ u32 max_offset = 0;
+ u32 old_setting = dev->mclk;
+ u32 tuner_freq_MHz = c->frequency / 1000;
+ u8 i;
+ char big_symbol = 0;
+
+ big_symbol = (c->symbol_rate > 45010000) ? 1 : 0;
+
+ if (big_symbol) {
+ reg16 = 115;
+ } else {
+ reg16 = 96;
+
+ /* TODO: IS THIS NECESSARY ? */
+ for (i = 0; i < 3; i++) {
+ offset_MHz[i] = tuner_freq_MHz % adc_Freq_MHz[i];
+
+ if (offset_MHz[i] > (adc_Freq_MHz[i] / 2))
+ offset_MHz[i] = adc_Freq_MHz[i] - offset_MHz[i];
+
+ if (offset_MHz[i] > max_offset) {
+ max_offset = offset_MHz[i];
+ reg16 = reg16_list[i];
+ dev->mclk = adc_Freq_MHz[i] * 1000 * 1000;
+
+ if (big_symbol)
+ dev->mclk /= 2;
+
+ dev_dbg(&client->dev, "modifying mclk %u -> %u\n",
+ old_setting, dev->mclk);
+ }
+ }
+ }
+
+ if (dev->mclk == 93000000)
+ regmap_write(dev->regmap, 0xA0, 0x42);
+ else if (dev->mclk == 96000000)
+ regmap_write(dev->regmap, 0xA0, 0x44);
+ else if (dev->mclk == 99000000)
+ regmap_write(dev->regmap, 0xA0, 0x46);
+ else if (dev->mclk == 110250000)
+ regmap_write(dev->regmap, 0xA0, 0x4E);
+ else
+ regmap_write(dev->regmap, 0xA0, 0x44);
+
+ reg15 = m88ds3103b_dt_read(dev, 0x15);
+
+ m88ds3103b_dt_write(dev, 0x05, 0x40);
+ m88ds3103b_dt_write(dev, 0x11, 0x08);
+
+ if (big_symbol)
+ reg15 |= 0x02;
+ else
+ reg15 &= ~0x02;
+
+ m88ds3103b_dt_write(dev, 0x15, reg15);
+ m88ds3103b_dt_write(dev, 0x16, reg16);
+
+ usleep_range(5000, 5500);
+
+ m88ds3103b_dt_write(dev, 0x05, 0x00);
+ m88ds3103b_dt_write(dev, 0x11, (u8)(big_symbol ? 0x0E : 0x0A));
+
+ usleep_range(5000, 5500);
+
+ return 0;
+}
+
+static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz)
+{
+ u8 reg15, reg16, reg1D, reg1E, reg1F, tmp;
+ u8 sm, f0 = 0, f1 = 0, f2 = 0, f3 = 0;
+ u16 pll_div_fb, N;
+ u32 div;
+
+ reg15 = m88ds3103b_dt_read(dev, 0x15);
+ reg16 = m88ds3103b_dt_read(dev, 0x16);
+ reg1D = m88ds3103b_dt_read(dev, 0x1D);
+
+ if (dev->cfg->ts_mode != M88DS3103_TS_SERIAL) {
+ if (reg16 == 92)
+ tmp = 93;
+ else if (reg16 == 100)
+ tmp = 99;
+ else
+ tmp = 96;
+
+ mclk_khz *= tmp;
+ mclk_khz /= 96;
+ }
+
+ pll_div_fb = (reg15 & 0x01) << 8;
+ pll_div_fb += reg16;
+ pll_div_fb += 32;
+
+ div = 9000 * pll_div_fb * 4;
+ div /= mclk_khz;
+
+ if (dev->cfg->ts_mode == M88DS3103_TS_SERIAL) {
+ if (div <= 32) {
+ N = 2;
+
+ f0 = 0;
+ f1 = div / N;
+ f2 = div - f1;
+ f3 = 0;
+ } else if (div <= 34) {
+ N = 3;
+
+ f0 = div / N;
+ f1 = (div - f0) / (N - 1);
+ f2 = div - f0 - f1;
+ f3 = 0;
+ } else if (div <= 64) {
+ N = 4;
+
+ f0 = div / N;
+ f1 = (div - f0) / (N - 1);
+ f2 = (div - f0 - f1) / (N - 2);
+ f3 = div - f0 - f1 - f2;
+ } else {
+ N = 4;
+
+ f0 = 16;
+ f1 = 16;
+ f2 = 16;
+ f3 = 16;
+ }
+
+ if (f0 == 16)
+ f0 = 0;
+ else if ((f0 < 8) && (f0 != 0))
+ f0 = 8;
+
+ if (f1 == 16)
+ f1 = 0;
+ else if ((f1 < 8) && (f1 != 0))
+ f1 = 8;
+
+ if (f2 == 16)
+ f2 = 0;
+ else if ((f2 < 8) && (f2 != 0))
+ f2 = 8;
+
+ if (f3 == 16)
+ f3 = 0;
+ else if ((f3 < 8) && (f3 != 0))
+ f3 = 8;
+ } else {
+ if (div <= 32) {
+ N = 2;
+
+ f0 = 0;
+ f1 = div / N;
+ f2 = div - f1;
+ f3 = 0;
+ } else if (div <= 48) {
+ N = 3;
+
+ f0 = div / N;
+ f1 = (div - f0) / (N - 1);
+ f2 = div - f0 - f1;
+ f3 = 0;
+ } else if (div <= 64) {
+ N = 4;
+
+ f0 = div / N;
+ f1 = (div - f0) / (N - 1);
+ f2 = (div - f0 - f1) / (N - 2);
+ f3 = div - f0 - f1 - f2;
+ } else {
+ N = 4;
+
+ f0 = 16;
+ f1 = 16;
+ f2 = 16;
+ f3 = 16;
+ }
+
+ if (f0 == 16)
+ f0 = 0;
+ else if ((f0 < 9) && (f0 != 0))
+ f0 = 9;
+
+ if (f1 == 16)
+ f1 = 0;
+ else if ((f1 < 9) && (f1 != 0))
+ f1 = 9;
+
+ if (f2 == 16)
+ f2 = 0;
+ else if ((f2 < 9) && (f2 != 0))
+ f2 = 9;
+
+ if (f3 == 16)
+ f3 = 0;
+ else if ((f3 < 9) && (f3 != 0))
+ f3 = 9;
+ }
+
+ sm = N - 1;
+
+ /* Write to registers */
+ //reg15 &= 0x01;
+ //reg15 |= (pll_div_fb >> 8) & 0x01;
+
+ //reg16 = pll_div_fb & 0xFF;
+
+ reg1D &= ~0x03;
+ reg1D |= sm;
+ reg1D |= 0x80;
+
+ reg1E = ((f3 << 4) + f2) & 0xFF;
+ reg1F = ((f1 << 4) + f0) & 0xFF;
+
+ m88ds3103b_dt_write(dev, 0x05, 0x40);
+ m88ds3103b_dt_write(dev, 0x11, 0x08);
+ m88ds3103b_dt_write(dev, 0x1D, reg1D);
+ m88ds3103b_dt_write(dev, 0x1E, reg1E);
+ m88ds3103b_dt_write(dev, 0x1F, reg1F);
+
+ m88ds3103b_dt_write(dev, 0x17, 0xc1);
+ m88ds3103b_dt_write(dev, 0x17, 0x81);
+
+ usleep_range(5000, 5500);
+
+ m88ds3103b_dt_write(dev, 0x05, 0x00);
+ m88ds3103b_dt_write(dev, 0x11, 0x0A);
+
+ usleep_range(5000, 5500);
+
+ return 0;
+}
+
+static int m88ds3103_set_frontend(struct dvb_frontend *fe)
+{
+ struct m88ds3103_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, len;
+ const struct m88ds3103_reg_val *init;
+ u8 u8tmp, u8tmp1 = 0, u8tmp2 = 0; /* silence compiler warning */
+ u8 buf[3];
+ u16 u16tmp;
+ u32 tuner_frequency_khz, target_mclk, u32tmp;
+ s32 s32tmp;
+ static const struct reg_sequence reset_buf[] = {
+ {0x07, 0x80}, {0x07, 0x00}
+ };
+
+ dev_dbg(&client->dev,
+ "delivery_system=%d modulation=%d frequency=%u symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n",
+ c->delivery_system, c->modulation, c->frequency, c->symbol_rate,
+ c->inversion, c->pilot, c->rolloff);
+
+ if (!dev->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ /* reset */
+ ret = regmap_multi_reg_write(dev->regmap, reset_buf, 2);
+ if (ret)
+ goto err;
+
+ /* Disable demod clock path */
+ if (dev->chip_id == M88RS6000_CHIP_ID) {
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ ret = regmap_read(dev->regmap, 0xb2, &u32tmp);
+ if (ret)
+ goto err;
+ if (u32tmp == 0x01) {
+ ret = regmap_write(dev->regmap, 0x00, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0xb2, 0x00);
+ if (ret)
+ goto err;
+ }
+ }
+
+ ret = regmap_write(dev->regmap, 0x06, 0xe0);
+ if (ret)
+ goto err;
+ }
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params) {
+ ret = fe->ops.tuner_ops.set_params(fe);
+ if (ret)
+ goto err;
+ }
+
+ if (fe->ops.tuner_ops.get_frequency) {
+ ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency_khz);
+ if (ret)
+ goto err;
+ } else {
+ /*
+ * Use nominal target frequency as tuner driver does not provide
+ * actual frequency used. Carrier offset calculation is not
+ * valid.
+ */
+ tuner_frequency_khz = c->frequency;
+ }
+
+ /* set M88RS6000/DS3103B demod main mclk and ts mclk from tuner die */
+ if (dev->chip_id == M88RS6000_CHIP_ID) {
+ if (c->symbol_rate > 45010000)
+ dev->mclk = 110250000;
+ else
+ dev->mclk = 96000000;
+
+ if (c->delivery_system == SYS_DVBS)
+ target_mclk = 96000000;
+ else
+ target_mclk = 144000000;
+
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ m88ds3103b_select_mclk(dev);
+ m88ds3103b_set_mclk(dev, target_mclk / 1000);
+ }
+
+ /* Enable demod clock path */
+ ret = regmap_write(dev->regmap, 0x06, 0x00);
+ if (ret)
+ goto err;
+ usleep_range(10000, 20000);
+ } else {
+ /* set M88DS3103 mclk and ts mclk. */
+ dev->mclk = 96000000;
+
+ switch (dev->cfg->ts_mode) {
+ case M88DS3103_TS_SERIAL:
+ case M88DS3103_TS_SERIAL_D7:
+ target_mclk = dev->cfg->ts_clk;
+ break;
+ case M88DS3103_TS_PARALLEL:
+ case M88DS3103_TS_CI:
+ if (c->delivery_system == SYS_DVBS)
+ target_mclk = 96000000;
+ else {
+ if (c->symbol_rate < 18000000)
+ target_mclk = 96000000;
+ else if (c->symbol_rate < 28000000)
+ target_mclk = 144000000;
+ else
+ target_mclk = 192000000;
+ }
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid ts_mode\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ switch (target_mclk) {
+ case 96000000:
+ u8tmp1 = 0x02; /* 0b10 */
+ u8tmp2 = 0x01; /* 0b01 */
+ break;
+ case 144000000:
+ u8tmp1 = 0x00; /* 0b00 */
+ u8tmp2 = 0x01; /* 0b01 */
+ break;
+ case 192000000:
+ u8tmp1 = 0x03; /* 0b11 */
+ u8tmp2 = 0x00; /* 0b00 */
+ break;
+ }
+ ret = m88ds3103_update_bits(dev, 0x22, 0xc0, u8tmp1 << 6);
+ if (ret)
+ goto err;
+ ret = m88ds3103_update_bits(dev, 0x24, 0xc0, u8tmp2 << 6);
+ if (ret)
+ goto err;
+ }
+
+ ret = regmap_write(dev->regmap, 0xb2, 0x01);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap, 0x00, 0x01);
+ if (ret)
+ goto err;
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ if (dev->chip_id == M88RS6000_CHIP_ID) {
+ len = ARRAY_SIZE(m88rs6000_dvbs_init_reg_vals);
+ init = m88rs6000_dvbs_init_reg_vals;
+ } else {
+ len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals);
+ init = m88ds3103_dvbs_init_reg_vals;
+ }
+ break;
+ case SYS_DVBS2:
+ if (dev->chip_id == M88RS6000_CHIP_ID) {
+ len = ARRAY_SIZE(m88rs6000_dvbs2_init_reg_vals);
+ init = m88rs6000_dvbs2_init_reg_vals;
+ } else {
+ len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals);
+ init = m88ds3103_dvbs2_init_reg_vals;
+ }
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid delivery_system\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* program init table */
+ if (c->delivery_system != dev->delivery_system) {
+ ret = m88ds3103_wr_reg_val_tab(dev, init, len);
+ if (ret)
+ goto err;
+ }
+
+ if (dev->chip_id == M88RS6000_CHIP_ID) {
+ if (c->delivery_system == SYS_DVBS2 &&
+ c->symbol_rate <= 5000000) {
+ ret = regmap_write(dev->regmap, 0xc0, 0x04);
+ if (ret)
+ goto err;
+ buf[0] = 0x09;
+ buf[1] = 0x22;
+ buf[2] = 0x88;
+ ret = regmap_bulk_write(dev->regmap, 0x8a, buf, 3);
+ if (ret)
+ goto err;
+ }
+ ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08);
+ if (ret)
+ goto err;
+
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ buf[0] = m88ds3103b_dt_read(dev, 0x15);
+ buf[1] = m88ds3103b_dt_read(dev, 0x16);
+
+ if (c->symbol_rate > 45010000) {
+ buf[0] &= ~0x03;
+ buf[0] |= 0x02;
+ buf[0] |= ((147 - 32) >> 8) & 0x01;
+ buf[1] = (147 - 32) & 0xFF;
+
+ dev->mclk = 110250 * 1000;
+ } else {
+ buf[0] &= ~0x03;
+ buf[0] |= ((128 - 32) >> 8) & 0x01;
+ buf[1] = (128 - 32) & 0xFF;
+
+ dev->mclk = 96000 * 1000;
+ }
+ m88ds3103b_dt_write(dev, 0x15, buf[0]);
+ m88ds3103b_dt_write(dev, 0x16, buf[1]);
+
+ regmap_read(dev->regmap, 0x30, &u32tmp);
+ u32tmp &= ~0x80;
+ regmap_write(dev->regmap, 0x30, u32tmp & 0xff);
+ }
+
+ ret = regmap_write(dev->regmap, 0xf1, 0x01);
+ if (ret)
+ goto err;
+
+ if (dev->chiptype != M88DS3103_CHIPTYPE_3103B) {
+ ret = m88ds3103_update_bits(dev, 0x30, 0x80, 0x80);
+ if (ret)
+ goto err;
+ }
+ }
+
+ switch (dev->cfg->ts_mode) {
+ case M88DS3103_TS_SERIAL:
+ u8tmp1 = 0x00;
+ u8tmp = 0x06;
+ break;
+ case M88DS3103_TS_SERIAL_D7:
+ u8tmp1 = 0x20;
+ u8tmp = 0x06;
+ break;
+ case M88DS3103_TS_PARALLEL:
+ u8tmp = 0x02;
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ u8tmp = 0x01;
+ u8tmp1 = 0x01;
+ }
+ break;
+ case M88DS3103_TS_CI:
+ u8tmp = 0x03;
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid ts_mode\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (dev->cfg->ts_clk_pol)
+ u8tmp |= 0x40;
+
+ /* TS mode */
+ ret = regmap_write(dev->regmap, 0xfd, u8tmp);
+ if (ret)
+ goto err;
+
+ switch (dev->cfg->ts_mode) {
+ case M88DS3103_TS_SERIAL:
+ case M88DS3103_TS_SERIAL_D7:
+ ret = m88ds3103_update_bits(dev, 0x29, 0x20, u8tmp1);
+ if (ret)
+ goto err;
+ u16tmp = 0;
+ u8tmp1 = 0x3f;
+ u8tmp2 = 0x3f;
+ break;
+ case M88DS3103_TS_PARALLEL:
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ ret = m88ds3103_update_bits(dev, 0x29, 0x01, u8tmp1);
+ if (ret)
+ goto err;
+ }
+ fallthrough;
+ default:
+ u16tmp = DIV_ROUND_UP(target_mclk, dev->cfg->ts_clk);
+ u8tmp1 = u16tmp / 2 - 1;
+ u8tmp2 = DIV_ROUND_UP(u16tmp, 2) - 1;
+ }
+
+ dev_dbg(&client->dev, "target_mclk=%u ts_clk=%u ts_clk_divide_ratio=%u\n",
+ target_mclk, dev->cfg->ts_clk, u16tmp);
+
+ /* u8tmp1[5:2] => fe[3:0], u8tmp1[1:0] => ea[7:6] */
+ /* u8tmp2[5:0] => ea[5:0] */
+ u8tmp = (u8tmp1 >> 2) & 0x0f;
+ ret = regmap_update_bits(dev->regmap, 0xfe, 0x0f, u8tmp);
+ if (ret)
+ goto err;
+ u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0;
+ ret = regmap_write(dev->regmap, 0xea, u8tmp);
+ if (ret)
+ goto err;
+
+ if (c->symbol_rate <= 3000000)
+ u8tmp = 0x20;
+ else if (c->symbol_rate <= 10000000)
+ u8tmp = 0x10;
+ else
+ u8tmp = 0x06;
+
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B)
+ m88ds3103b_set_mclk(dev, target_mclk / 1000);
+
+ ret = regmap_write(dev->regmap, 0xc3, 0x08);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap, 0xc8, u8tmp);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap, 0xc4, 0x08);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap, 0xc7, 0x00);
+ if (ret)
+ goto err;
+
+ u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk);
+ buf[0] = (u16tmp >> 0) & 0xff;
+ buf[1] = (u16tmp >> 8) & 0xff;
+ ret = regmap_bulk_write(dev->regmap, 0x61, buf, 2);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_update_bits(dev, 0x4d, 0x02, dev->cfg->spec_inv << 1);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap, 0x33, dev->cfg->agc);
+ if (ret)
+ goto err;
+
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ /* enable/disable 192M LDPC clock */
+ ret = m88ds3103_update_bits(dev, 0x29, 0x10,
+ (c->delivery_system == SYS_DVBS) ? 0x10 : 0x0);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_update_bits(dev, 0xc9, 0x08, 0x08);
+ if (ret)
+ goto err;
+ }
+
+ dev_dbg(&client->dev, "carrier offset=%d\n",
+ (tuner_frequency_khz - c->frequency));
+
+ /* Use 32-bit calc as there is no s64 version of DIV_ROUND_CLOSEST() */
+ s32tmp = 0x10000 * (tuner_frequency_khz - c->frequency);
+ s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk / 1000);
+ buf[0] = (s32tmp >> 0) & 0xff;
+ buf[1] = (s32tmp >> 8) & 0xff;
+ ret = regmap_bulk_write(dev->regmap, 0x5e, buf, 2);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap, 0x00, 0x00);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap, 0xb2, 0x00);
+ if (ret)
+ goto err;
+
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ /* to light up the LOCK led */
+ ret = m88ds3103_update_bits(dev, 0x11, 0x80, 0x00);
+ if (ret)
+ goto err;
+ }
+
+ dev->delivery_system = c->delivery_system;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int m88ds3103_init(struct dvb_frontend *fe)
+{
+ struct m88ds3103_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, len, rem;
+ unsigned int utmp;
+ const struct firmware *firmware;
+ const char *name;
+
+ dev_dbg(&client->dev, "\n");
+
+ /* set cold state by default */
+ dev->warm = false;
+
+ /* wake up device from sleep */
+ ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01);
+ if (ret)
+ goto err;
+ ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00);
+ if (ret)
+ goto err;
+ ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x00);
+ if (ret)
+ goto err;
+
+ /* firmware status */
+ ret = regmap_read(dev->regmap, 0xb9, &utmp);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "firmware=%02x\n", utmp);
+
+ if (utmp)
+ goto warm;
+
+ /* global reset, global diseqc reset, global fec reset */
+ ret = regmap_write(dev->regmap, 0x07, 0xe0);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x07, 0x00);
+ if (ret)
+ goto err;
+
+ /* cold state - try to download firmware */
+ dev_info(&client->dev, "found a '%s' in cold state\n",
+ dev->fe.ops.info.name);
+
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B)
+ name = M88DS3103B_FIRMWARE;
+ else if (dev->chip_id == M88RS6000_CHIP_ID)
+ name = M88RS6000_FIRMWARE;
+ else
+ name = M88DS3103_FIRMWARE;
+
+ /* request the firmware, this will block and timeout */
+ ret = request_firmware(&firmware, name, &client->dev);
+ if (ret) {
+ dev_err(&client->dev, "firmware file '%s' not found\n", name);
+ goto err;
+ }
+
+ dev_info(&client->dev, "downloading firmware from file '%s'\n", name);
+
+ ret = regmap_write(dev->regmap, 0xb2, 0x01);
+ if (ret)
+ goto err_release_firmware;
+
+ for (rem = firmware->size; rem > 0; rem -= (dev->cfg->i2c_wr_max - 1)) {
+ len = min(dev->cfg->i2c_wr_max - 1, rem);
+ ret = regmap_bulk_write(dev->regmap, 0xb0,
+ &firmware->data[firmware->size - rem],
+ len);
+ if (ret) {
+ dev_err(&client->dev, "firmware download failed %d\n",
+ ret);
+ goto err_release_firmware;
+ }
+ }
+
+ ret = regmap_write(dev->regmap, 0xb2, 0x00);
+ if (ret)
+ goto err_release_firmware;
+
+ release_firmware(firmware);
+
+ ret = regmap_read(dev->regmap, 0xb9, &utmp);
+ if (ret)
+ goto err;
+
+ if (!utmp) {
+ ret = -EINVAL;
+ dev_info(&client->dev, "firmware did not run\n");
+ goto err;
+ }
+
+ dev_info(&client->dev, "found a '%s' in warm state\n",
+ dev->fe.ops.info.name);
+ dev_info(&client->dev, "firmware version: %X.%X\n",
+ (utmp >> 4) & 0xf, (utmp >> 0 & 0xf));
+
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ m88ds3103b_dt_write(dev, 0x21, 0x92);
+ m88ds3103b_dt_write(dev, 0x15, 0x6C);
+ m88ds3103b_dt_write(dev, 0x17, 0xC1);
+ m88ds3103b_dt_write(dev, 0x17, 0x81);
+ }
+warm:
+ /* warm state */
+ dev->warm = true;
+
+ /* init stats here in order signal app which stats are supported */
+ c->cnr.len = 1;
+ c->cnr.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;
+
+ return 0;
+err_release_firmware:
+ release_firmware(firmware);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int m88ds3103_sleep(struct dvb_frontend *fe)
+{
+ struct m88ds3103_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+ unsigned int utmp;
+
+ dev_dbg(&client->dev, "\n");
+
+ dev->fe_status = 0;
+ dev->delivery_system = SYS_UNDEFINED;
+
+ /* TS Hi-Z */
+ if (dev->chip_id == M88RS6000_CHIP_ID)
+ utmp = 0x29;
+ else
+ utmp = 0x27;
+ ret = m88ds3103_update_bits(dev, utmp, 0x01, 0x00);
+ if (ret)
+ goto err;
+
+ /* sleep */
+ ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00);
+ if (ret)
+ goto err;
+ ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01);
+ if (ret)
+ goto err;
+ ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x10);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int m88ds3103_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ struct m88ds3103_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+ u8 buf[3];
+
+ dev_dbg(&client->dev, "\n");
+
+ if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) {
+ ret = 0;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ ret = regmap_bulk_read(dev->regmap, 0xe0, &buf[0], 1);
+ if (ret)
+ goto err;
+
+ ret = regmap_bulk_read(dev->regmap, 0xe6, &buf[1], 1);
+ if (ret)
+ goto err;
+
+ switch ((buf[0] >> 2) & 0x01) {
+ case 0:
+ c->inversion = INVERSION_OFF;
+ break;
+ case 1:
+ c->inversion = INVERSION_ON;
+ break;
+ }
+
+ switch ((buf[1] >> 5) & 0x07) {
+ case 0:
+ c->fec_inner = FEC_7_8;
+ break;
+ case 1:
+ c->fec_inner = FEC_5_6;
+ break;
+ case 2:
+ c->fec_inner = FEC_3_4;
+ break;
+ case 3:
+ c->fec_inner = FEC_2_3;
+ break;
+ case 4:
+ c->fec_inner = FEC_1_2;
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid fec_inner\n");
+ }
+
+ c->modulation = QPSK;
+
+ break;
+ case SYS_DVBS2:
+ ret = regmap_bulk_read(dev->regmap, 0x7e, &buf[0], 1);
+ if (ret)
+ goto err;
+
+ ret = regmap_bulk_read(dev->regmap, 0x89, &buf[1], 1);
+ if (ret)
+ goto err;
+
+ ret = regmap_bulk_read(dev->regmap, 0xf2, &buf[2], 1);
+ if (ret)
+ goto err;
+
+ switch ((buf[0] >> 0) & 0x0f) {
+ case 2:
+ c->fec_inner = FEC_2_5;
+ break;
+ case 3:
+ c->fec_inner = FEC_1_2;
+ break;
+ case 4:
+ c->fec_inner = FEC_3_5;
+ break;
+ case 5:
+ c->fec_inner = FEC_2_3;
+ break;
+ case 6:
+ c->fec_inner = FEC_3_4;
+ break;
+ case 7:
+ c->fec_inner = FEC_4_5;
+ break;
+ case 8:
+ c->fec_inner = FEC_5_6;
+ break;
+ case 9:
+ c->fec_inner = FEC_8_9;
+ break;
+ case 10:
+ c->fec_inner = FEC_9_10;
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid fec_inner\n");
+ }
+
+ switch ((buf[0] >> 5) & 0x01) {
+ case 0:
+ c->pilot = PILOT_OFF;
+ break;
+ case 1:
+ c->pilot = PILOT_ON;
+ break;
+ }
+
+ switch ((buf[0] >> 6) & 0x07) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = PSK_8;
+ break;
+ case 2:
+ c->modulation = APSK_16;
+ break;
+ case 3:
+ c->modulation = APSK_32;
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid modulation\n");
+ }
+
+ switch ((buf[1] >> 7) & 0x01) {
+ case 0:
+ c->inversion = INVERSION_OFF;
+ break;
+ case 1:
+ c->inversion = INVERSION_ON;
+ break;
+ }
+
+ switch ((buf[2] >> 0) & 0x03) {
+ case 0:
+ c->rolloff = ROLLOFF_35;
+ break;
+ case 1:
+ c->rolloff = ROLLOFF_25;
+ break;
+ case 2:
+ c->rolloff = ROLLOFF_20;
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid rolloff\n");
+ }
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid delivery_system\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = regmap_bulk_read(dev->regmap, 0x6d, buf, 2);
+ if (ret)
+ goto err;
+
+ c->symbol_rate = DIV_ROUND_CLOSEST_ULL((u64)(buf[1] << 8 | buf[0] << 0) * dev->mclk, 0x10000);
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL)
+ *snr = div_s64(c->cnr.stat[0].svalue, 100);
+ else
+ *snr = 0;
+
+ return 0;
+}
+
+static int m88ds3103_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct m88ds3103_dev *dev = fe->demodulator_priv;
+
+ *ber = dev->dvbv3_ber;
+
+ return 0;
+}
+
+static int m88ds3103_set_tone(struct dvb_frontend *fe,
+ enum fe_sec_tone_mode fe_sec_tone_mode)
+{
+ struct m88ds3103_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+ unsigned int utmp, tone, reg_a1_mask;
+
+ dev_dbg(&client->dev, "fe_sec_tone_mode=%d\n", fe_sec_tone_mode);
+
+ if (!dev->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (fe_sec_tone_mode) {
+ case SEC_TONE_ON:
+ tone = 0;
+ reg_a1_mask = 0x47;
+ break;
+ case SEC_TONE_OFF:
+ tone = 1;
+ reg_a1_mask = 0x00;
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid fe_sec_tone_mode\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ utmp = tone << 7 | dev->cfg->envelope_mode << 5;
+ ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp);
+ if (ret)
+ goto err;
+
+ utmp = 1 << 2;
+ ret = m88ds3103_update_bits(dev, 0xa1, reg_a1_mask, utmp);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int m88ds3103_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage fe_sec_voltage)
+{
+ struct m88ds3103_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+ unsigned int utmp;
+ bool voltage_sel, voltage_dis;
+
+ dev_dbg(&client->dev, "fe_sec_voltage=%d\n", fe_sec_voltage);
+
+ if (!dev->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (fe_sec_voltage) {
+ case SEC_VOLTAGE_18:
+ voltage_sel = true;
+ voltage_dis = false;
+ break;
+ case SEC_VOLTAGE_13:
+ voltage_sel = false;
+ voltage_dis = false;
+ break;
+ case SEC_VOLTAGE_OFF:
+ voltage_sel = false;
+ voltage_dis = true;
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid fe_sec_voltage\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* output pin polarity */
+ voltage_sel ^= dev->cfg->lnb_hv_pol;
+ voltage_dis ^= dev->cfg->lnb_en_pol;
+
+ utmp = voltage_dis << 1 | voltage_sel << 0;
+ ret = m88ds3103_update_bits(dev, 0xa2, 0x03, utmp);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *diseqc_cmd)
+{
+ struct m88ds3103_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+ unsigned int utmp;
+ unsigned long timeout;
+
+ dev_dbg(&client->dev, "msg=%*ph\n",
+ diseqc_cmd->msg_len, diseqc_cmd->msg);
+
+ if (!dev->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ utmp = dev->cfg->envelope_mode << 5;
+ ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp);
+ if (ret)
+ goto err;
+
+ ret = regmap_bulk_write(dev->regmap, 0xa3, diseqc_cmd->msg,
+ diseqc_cmd->msg_len);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap, 0xa1,
+ (diseqc_cmd->msg_len - 1) << 3 | 0x07);
+ if (ret)
+ goto err;
+
+ /* wait DiSEqC TX ready */
+ #define SEND_MASTER_CMD_TIMEOUT 120
+ timeout = jiffies + msecs_to_jiffies(SEND_MASTER_CMD_TIMEOUT);
+
+ /* DiSEqC message period is 13.5 ms per byte */
+ utmp = diseqc_cmd->msg_len * 13500;
+ usleep_range(utmp - 4000, utmp);
+
+ for (utmp = 1; !time_after(jiffies, timeout) && utmp;) {
+ ret = regmap_read(dev->regmap, 0xa1, &utmp);
+ if (ret)
+ goto err;
+ utmp = (utmp >> 6) & 0x1;
+ }
+
+ if (utmp == 0) {
+ dev_dbg(&client->dev, "diseqc tx took %u ms\n",
+ jiffies_to_msecs(jiffies) -
+ (jiffies_to_msecs(timeout) - SEND_MASTER_CMD_TIMEOUT));
+ } else {
+ dev_dbg(&client->dev, "diseqc tx timeout\n");
+
+ ret = m88ds3103_update_bits(dev, 0xa1, 0xc0, 0x40);
+ if (ret)
+ goto err;
+ }
+
+ ret = m88ds3103_update_bits(dev, 0xa2, 0xc0, 0x80);
+ if (ret)
+ goto err;
+
+ if (utmp == 1) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe,
+ enum fe_sec_mini_cmd fe_sec_mini_cmd)
+{
+ struct m88ds3103_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+ unsigned int utmp, burst;
+ unsigned long timeout;
+
+ dev_dbg(&client->dev, "fe_sec_mini_cmd=%d\n", fe_sec_mini_cmd);
+
+ if (!dev->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ utmp = dev->cfg->envelope_mode << 5;
+ ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp);
+ if (ret)
+ goto err;
+
+ switch (fe_sec_mini_cmd) {
+ case SEC_MINI_A:
+ burst = 0x02;
+ break;
+ case SEC_MINI_B:
+ burst = 0x01;
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid fe_sec_mini_cmd\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = regmap_write(dev->regmap, 0xa1, burst);
+ if (ret)
+ goto err;
+
+ /* wait DiSEqC TX ready */
+ #define SEND_BURST_TIMEOUT 40
+ timeout = jiffies + msecs_to_jiffies(SEND_BURST_TIMEOUT);
+
+ /* DiSEqC ToneBurst period is 12.5 ms */
+ usleep_range(8500, 12500);
+
+ for (utmp = 1; !time_after(jiffies, timeout) && utmp;) {
+ ret = regmap_read(dev->regmap, 0xa1, &utmp);
+ if (ret)
+ goto err;
+ utmp = (utmp >> 6) & 0x1;
+ }
+
+ if (utmp == 0) {
+ dev_dbg(&client->dev, "diseqc tx took %u ms\n",
+ jiffies_to_msecs(jiffies) -
+ (jiffies_to_msecs(timeout) - SEND_BURST_TIMEOUT));
+ } else {
+ dev_dbg(&client->dev, "diseqc tx timeout\n");
+
+ ret = m88ds3103_update_bits(dev, 0xa1, 0xc0, 0x40);
+ if (ret)
+ goto err;
+ }
+
+ ret = m88ds3103_update_bits(dev, 0xa2, 0xc0, 0x80);
+ if (ret)
+ goto err;
+
+ if (utmp == 1) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int m88ds3103_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 3000;
+
+ return 0;
+}
+
+static void m88ds3103_release(struct dvb_frontend *fe)
+{
+ struct m88ds3103_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+
+ i2c_unregister_device(client);
+}
+
+static int m88ds3103_select(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct m88ds3103_dev *dev = i2c_mux_priv(muxc);
+ struct i2c_client *client = dev->client;
+ int ret;
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = "\x03\x11",
+ };
+
+ /* Open tuner I2C repeater for 1 xfer, closes automatically */
+ ret = __i2c_transfer(client->adapter, &msg, 1);
+ if (ret != 1) {
+ dev_warn(&client->dev, "i2c wr failed=%d\n", ret);
+ if (ret >= 0)
+ ret = -EREMOTEIO;
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * XXX: That is wrapper to m88ds3103_probe() via driver core in order to provide
+ * proper I2C client for legacy media attach binding.
+ * New users must use I2C client binding directly!
+ */
+struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
+ struct i2c_adapter *i2c,
+ struct i2c_adapter **tuner_i2c_adapter)
+{
+ struct i2c_client *client;
+ struct i2c_board_info board_info;
+ struct m88ds3103_platform_data pdata = {};
+
+ pdata.clk = cfg->clock;
+ pdata.i2c_wr_max = cfg->i2c_wr_max;
+ pdata.ts_mode = cfg->ts_mode;
+ pdata.ts_clk = cfg->ts_clk;
+ pdata.ts_clk_pol = cfg->ts_clk_pol;
+ pdata.spec_inv = cfg->spec_inv;
+ pdata.agc = cfg->agc;
+ pdata.agc_inv = cfg->agc_inv;
+ pdata.clk_out = cfg->clock_out;
+ pdata.envelope_mode = cfg->envelope_mode;
+ pdata.lnb_hv_pol = cfg->lnb_hv_pol;
+ pdata.lnb_en_pol = cfg->lnb_en_pol;
+ pdata.attach_in_use = true;
+
+ memset(&board_info, 0, sizeof(board_info));
+ strscpy(board_info.type, "m88ds3103", I2C_NAME_SIZE);
+ board_info.addr = cfg->i2c_addr;
+ board_info.platform_data = &pdata;
+ client = i2c_new_client_device(i2c, &board_info);
+ if (!i2c_client_has_driver(client))
+ return NULL;
+
+ *tuner_i2c_adapter = pdata.get_i2c_adapter(client);
+ return pdata.get_dvb_frontend(client);
+}
+EXPORT_SYMBOL_GPL(m88ds3103_attach);
+
+static const struct dvb_frontend_ops m88ds3103_ops = {
+ .delsys = {SYS_DVBS, SYS_DVBS2},
+ .info = {
+ .name = "Montage Technology M88DS3103",
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
+ .frequency_tolerance_hz = 5 * MHz,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .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_6_7 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_8_9 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_RECOVER |
+ FE_CAN_2G_MODULATION
+ },
+
+ .release = m88ds3103_release,
+
+ .get_tune_settings = m88ds3103_get_tune_settings,
+
+ .init = m88ds3103_init,
+ .sleep = m88ds3103_sleep,
+
+ .set_frontend = m88ds3103_set_frontend,
+ .get_frontend = m88ds3103_get_frontend,
+
+ .read_status = m88ds3103_read_status,
+ .read_snr = m88ds3103_read_snr,
+ .read_ber = m88ds3103_read_ber,
+
+ .diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd,
+ .diseqc_send_burst = m88ds3103_diseqc_send_burst,
+
+ .set_tone = m88ds3103_set_tone,
+ .set_voltage = m88ds3103_set_voltage,
+};
+
+static struct dvb_frontend *m88ds3103_get_dvb_frontend(struct i2c_client *client)
+{
+ struct m88ds3103_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ return &dev->fe;
+}
+
+static struct i2c_adapter *m88ds3103_get_i2c_adapter(struct i2c_client *client)
+{
+ struct m88ds3103_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ return dev->muxc->adapter[0];
+}
+
+static int m88ds3103_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct m88ds3103_dev *dev;
+ struct m88ds3103_platform_data *pdata = client->dev.platform_data;
+ int ret;
+ unsigned int utmp;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev->client = client;
+ dev->config.clock = pdata->clk;
+ dev->config.i2c_wr_max = pdata->i2c_wr_max;
+ dev->config.ts_mode = pdata->ts_mode;
+ dev->config.ts_clk = pdata->ts_clk * 1000;
+ dev->config.ts_clk_pol = pdata->ts_clk_pol;
+ dev->config.spec_inv = pdata->spec_inv;
+ dev->config.agc_inv = pdata->agc_inv;
+ dev->config.clock_out = pdata->clk_out;
+ dev->config.envelope_mode = pdata->envelope_mode;
+ dev->config.agc = pdata->agc;
+ dev->config.lnb_hv_pol = pdata->lnb_hv_pol;
+ dev->config.lnb_en_pol = pdata->lnb_en_pol;
+ dev->cfg = &dev->config;
+ /* create regmap */
+ dev->regmap_config.reg_bits = 8;
+ dev->regmap_config.val_bits = 8;
+ dev->regmap_config.lock_arg = dev;
+ dev->regmap = devm_regmap_init_i2c(client, &dev->regmap_config);
+ if (IS_ERR(dev->regmap)) {
+ ret = PTR_ERR(dev->regmap);
+ goto err_kfree;
+ }
+
+ /* 0x00: chip id[6:0], 0x01: chip ver[7:0], 0x02: chip ver[15:8] */
+ ret = regmap_read(dev->regmap, 0x00, &utmp);
+ if (ret)
+ goto err_kfree;
+
+ dev->chip_id = utmp >> 1;
+ dev->chiptype = (u8)id->driver_data;
+
+ dev_dbg(&client->dev, "chip_id=%02x\n", dev->chip_id);
+
+ switch (dev->chip_id) {
+ case M88RS6000_CHIP_ID:
+ case M88DS3103_CHIP_ID:
+ break;
+ default:
+ ret = -ENODEV;
+ dev_err(&client->dev, "Unknown device. Chip_id=%02x\n", dev->chip_id);
+ goto err_kfree;
+ }
+
+ switch (dev->cfg->clock_out) {
+ case M88DS3103_CLOCK_OUT_DISABLED:
+ utmp = 0x80;
+ break;
+ case M88DS3103_CLOCK_OUT_ENABLED:
+ utmp = 0x00;
+ break;
+ case M88DS3103_CLOCK_OUT_ENABLED_DIV2:
+ utmp = 0x10;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_kfree;
+ }
+
+ if (!pdata->ts_clk) {
+ ret = -EINVAL;
+ goto err_kfree;
+ }
+
+ /* 0x29 register is defined differently for m88rs6000. */
+ /* set internal tuner address to 0x21 */
+ if (dev->chip_id == M88RS6000_CHIP_ID)
+ utmp = 0x00;
+
+ ret = regmap_write(dev->regmap, 0x29, utmp);
+ if (ret)
+ goto err_kfree;
+
+ /* sleep */
+ ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00);
+ if (ret)
+ goto err_kfree;
+ ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01);
+ if (ret)
+ goto err_kfree;
+ ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x10);
+ if (ret)
+ goto err_kfree;
+
+ /* create mux i2c adapter for tuner */
+ dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0,
+ m88ds3103_select, NULL);
+ if (!dev->muxc) {
+ ret = -ENOMEM;
+ goto err_kfree;
+ }
+ dev->muxc->priv = dev;
+ ret = i2c_mux_add_adapter(dev->muxc, 0, 0);
+ if (ret)
+ goto err_kfree;
+
+ /* create dvb_frontend */
+ memcpy(&dev->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops));
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B)
+ strscpy(dev->fe.ops.info.name, "Montage Technology M88DS3103B",
+ sizeof(dev->fe.ops.info.name));
+ else if (dev->chip_id == M88RS6000_CHIP_ID)
+ strscpy(dev->fe.ops.info.name, "Montage Technology M88RS6000",
+ sizeof(dev->fe.ops.info.name));
+ if (!pdata->attach_in_use)
+ dev->fe.ops.release = NULL;
+ dev->fe.demodulator_priv = dev;
+ i2c_set_clientdata(client, dev);
+
+ /* setup callbacks */
+ pdata->get_dvb_frontend = m88ds3103_get_dvb_frontend;
+ pdata->get_i2c_adapter = m88ds3103_get_i2c_adapter;
+
+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
+ /* enable i2c repeater for tuner */
+ m88ds3103_update_bits(dev, 0x11, 0x01, 0x01);
+
+ /* get frontend address */
+ ret = regmap_read(dev->regmap, 0x29, &utmp);
+ if (ret)
+ goto err_del_adapters;
+ dev->dt_addr = ((utmp & 0x80) == 0) ? 0x42 >> 1 : 0x40 >> 1;
+ dev_dbg(&client->dev, "dt addr is 0x%02x\n", dev->dt_addr);
+
+ dev->dt_client = i2c_new_dummy_device(client->adapter,
+ dev->dt_addr);
+ if (IS_ERR(dev->dt_client)) {
+ ret = PTR_ERR(dev->dt_client);
+ goto err_del_adapters;
+ }
+ }
+
+ return 0;
+
+err_del_adapters:
+ i2c_mux_del_adapters(dev->muxc);
+err_kfree:
+ kfree(dev);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static void m88ds3103_remove(struct i2c_client *client)
+{
+ struct m88ds3103_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ i2c_unregister_device(dev->dt_client);
+
+ i2c_mux_del_adapters(dev->muxc);
+
+ kfree(dev);
+}
+
+static const struct i2c_device_id m88ds3103_id_table[] = {
+ {"m88ds3103", M88DS3103_CHIPTYPE_3103},
+ {"m88rs6000", M88DS3103_CHIPTYPE_RS6000},
+ {"m88ds3103b", M88DS3103_CHIPTYPE_3103B},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, m88ds3103_id_table);
+
+static struct i2c_driver m88ds3103_driver = {
+ .driver = {
+ .name = "m88ds3103",
+ .suppress_bind_attrs = true,
+ },
+ .probe = m88ds3103_probe,
+ .remove = m88ds3103_remove,
+ .id_table = m88ds3103_id_table,
+};
+
+module_i2c_driver(m88ds3103_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Montage Technology M88DS3103 DVB-S/S2 demodulator driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(M88DS3103_FIRMWARE);
+MODULE_FIRMWARE(M88RS6000_FIRMWARE);
+MODULE_FIRMWARE(M88DS3103B_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/m88ds3103.h b/drivers/media/dvb-frontends/m88ds3103.h
new file mode 100644
index 000000000000..e32b68c0df70
--- /dev/null
+++ b/drivers/media/dvb-frontends/m88ds3103.h
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Montage Technology M88DS3103/M88RS6000 demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ */
+
+#ifndef M88DS3103_H
+#define M88DS3103_H
+
+#include <linux/dvb/frontend.h>
+
+/*
+ * I2C address
+ * 0x68,
+ */
+
+/**
+ * enum m88ds3103_ts_mode - TS connection mode
+ * @M88DS3103_TS_SERIAL: TS output pin D0, normal
+ * @M88DS3103_TS_SERIAL_D7: TS output pin D7
+ * @M88DS3103_TS_PARALLEL: TS Parallel mode
+ * @M88DS3103_TS_CI: TS CI Mode
+ */
+enum m88ds3103_ts_mode {
+ M88DS3103_TS_SERIAL,
+ M88DS3103_TS_SERIAL_D7,
+ M88DS3103_TS_PARALLEL,
+ M88DS3103_TS_CI
+};
+
+/**
+ * enum m88ds3103_clock_out
+ * @M88DS3103_CLOCK_OUT_DISABLED: Clock output is disabled
+ * @M88DS3103_CLOCK_OUT_ENABLED: Clock output is enabled with crystal
+ * clock.
+ * @M88DS3103_CLOCK_OUT_ENABLED_DIV2: Clock output is enabled with half
+ * crystal clock.
+ */
+enum m88ds3103_clock_out {
+ M88DS3103_CLOCK_OUT_DISABLED,
+ M88DS3103_CLOCK_OUT_ENABLED,
+ M88DS3103_CLOCK_OUT_ENABLED_DIV2
+};
+
+/**
+ * struct m88ds3103_platform_data - Platform data for the m88ds3103 driver
+ * @clk: Clock frequency.
+ * @i2c_wr_max: Max bytes I2C adapter can write at once.
+ * @ts_mode: TS mode.
+ * @ts_clk: TS clock (KHz).
+ * @ts_clk_pol: TS clk polarity. 1-active at falling edge; 0-active at rising
+ * edge.
+ * @spec_inv: Input spectrum inversion.
+ * @agc: AGC configuration.
+ * @agc_inv: AGC polarity.
+ * @clk_out: Clock output.
+ * @envelope_mode: DiSEqC envelope mode.
+ * @lnb_hv_pol: LNB H/V pin polarity. 0: pin high set to VOLTAGE_18, pin low to
+ * set VOLTAGE_13. 1: pin high set to VOLTAGE_13, pin low to set VOLTAGE_18.
+ * @lnb_en_pol: LNB enable pin polarity. 0: pin high to disable, pin low to
+ * enable. 1: pin high to enable, pin low to disable.
+ * @get_dvb_frontend: Get DVB frontend.
+ * @get_i2c_adapter: Get I2C adapter.
+ */
+struct m88ds3103_platform_data {
+ u32 clk;
+ u16 i2c_wr_max;
+ enum m88ds3103_ts_mode ts_mode;
+ u32 ts_clk;
+ enum m88ds3103_clock_out clk_out;
+ u8 ts_clk_pol:1;
+ u8 spec_inv:1;
+ u8 agc;
+ u8 agc_inv:1;
+ u8 envelope_mode:1;
+ u8 lnb_hv_pol:1;
+ u8 lnb_en_pol:1;
+
+ struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
+ struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *);
+
+/* private: For legacy media attach wrapper. Do not set value. */
+ u8 attach_in_use:1;
+};
+
+/**
+ * struct m88ds3103_config - m88ds3102 configuration
+ *
+ * @i2c_addr: I2C address. Default: none, must set. Example: 0x68, ...
+ * @clock: Device's clock. Default: none, must set. Example: 27000000
+ * @i2c_wr_max: Max bytes I2C provider is asked to write at once.
+ * Default: none, must set. Example: 33, 65, ...
+ * @ts_mode: TS output mode, as defined by &enum m88ds3103_ts_mode.
+ * Default: M88DS3103_TS_SERIAL.
+ * @ts_clk: TS clk in KHz. Default: 0.
+ * @ts_clk_pol: TS clk polarity.Default: 0.
+ * 1-active at falling edge; 0-active at rising edge.
+ * @spec_inv: Spectrum inversion. Default: 0.
+ * @agc_inv: AGC polarity. Default: 0.
+ * @clock_out: Clock output, as defined by &enum m88ds3103_clock_out.
+ * Default: M88DS3103_CLOCK_OUT_DISABLED.
+ * @envelope_mode: DiSEqC envelope mode. Default: 0.
+ * @agc: AGC configuration. Default: none, must set.
+ * @lnb_hv_pol: LNB H/V pin polarity. Default: 0. Values:
+ * 1: pin high set to VOLTAGE_13, pin low to set VOLTAGE_18;
+ * 0: pin high set to VOLTAGE_18, pin low to set VOLTAGE_13.
+ * @lnb_en_pol: LNB enable pin polarity. Default: 0. Values:
+ * 1: pin high to enable, pin low to disable;
+ * 0: pin high to disable, pin low to enable.
+ */
+struct m88ds3103_config {
+ u8 i2c_addr;
+ u32 clock;
+ u16 i2c_wr_max;
+ u8 ts_mode;
+ u32 ts_clk;
+ u8 ts_clk_pol:1;
+ u8 spec_inv:1;
+ u8 agc_inv:1;
+ u8 clock_out;
+ u8 envelope_mode:1;
+ u8 agc;
+ u8 lnb_hv_pol:1;
+ u8 lnb_en_pol:1;
+};
+
+#if defined(CONFIG_DVB_M88DS3103) || \
+ (defined(CONFIG_DVB_M88DS3103_MODULE) && defined(MODULE))
+/**
+ * m88ds3103_attach - Attach a m88ds3103 demod
+ *
+ * @config: pointer to &struct m88ds3103_config with demod configuration.
+ * @i2c: i2c adapter to use.
+ * @tuner_i2c: on success, returns the I2C adapter associated with
+ * m88ds3103 tuner.
+ *
+ * return: FE pointer on success, NULL on failure.
+ * Note: Do not add new m88ds3103_attach() users! Use I2C bindings instead.
+ */
+extern struct dvb_frontend *m88ds3103_attach(
+ const struct m88ds3103_config *config,
+ struct i2c_adapter *i2c,
+ struct i2c_adapter **tuner_i2c);
+extern int m88ds3103_get_agc_pwm(struct dvb_frontend *fe, u8 *_agc_pwm);
+#else
+static inline struct dvb_frontend *m88ds3103_attach(
+ const struct m88ds3103_config *config,
+ struct i2c_adapter *i2c,
+ struct i2c_adapter **tuner_i2c)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#define m88ds3103_get_agc_pwm NULL
+#endif
+
+#endif
diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h
new file mode 100644
index 000000000000..594ad9cbc2cc
--- /dev/null
+++ b/drivers/media/dvb-frontends/m88ds3103_priv.h
@@ -0,0 +1,402 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Montage Technology M88DS3103/M88RS6000 demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ */
+
+#ifndef M88DS3103_PRIV_H
+#define M88DS3103_PRIV_H
+
+#include <media/dvb_frontend.h>
+#include "m88ds3103.h"
+#include <linux/int_log.h>
+#include <linux/firmware.h>
+#include <linux/i2c-mux.h>
+#include <linux/regmap.h>
+#include <linux/math64.h>
+
+#define M88DS3103B_FIRMWARE "dvb-demod-m88ds3103b.fw"
+#define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw"
+#define M88RS6000_FIRMWARE "dvb-demod-m88rs6000.fw"
+
+#define M88RS6000_CHIP_ID 0x74
+#define M88DS3103_CHIP_ID 0x70
+
+#define M88DS3103_CHIPTYPE_3103 0
+#define M88DS3103_CHIPTYPE_RS6000 1
+#define M88DS3103_CHIPTYPE_3103B 2
+
+struct m88ds3103_dev {
+ struct i2c_client *client;
+ struct i2c_client *dt_client;
+ struct regmap_config regmap_config;
+ struct regmap *regmap;
+ struct m88ds3103_config config;
+ const struct m88ds3103_config *cfg;
+ struct dvb_frontend fe;
+ enum fe_delivery_system delivery_system;
+ enum fe_status fe_status;
+ u32 dvbv3_ber; /* for old DVBv3 API read_ber */
+ bool warm; /* FW running */
+ struct i2c_mux_core *muxc;
+ /* auto detect chip id to do different config */
+ u8 chip_id;
+ /* chip type to differentiate m88rs6000 from m88ds3103b */
+ u8 chiptype;
+ /* main mclk is calculated for M88RS6000 dynamically */
+ s32 mclk;
+ u64 post_bit_error;
+ u64 post_bit_count;
+ u8 dt_addr;
+};
+
+struct m88ds3103_reg_val {
+ u8 reg;
+ u8 val;
+};
+
+static const struct m88ds3103_reg_val m88ds3103_dvbs_init_reg_vals[] = {
+ {0x23, 0x07},
+ {0x08, 0x03},
+ {0x0c, 0x02},
+ {0x21, 0x54},
+ {0x25, 0x8a},
+ {0x27, 0x31},
+ {0x30, 0x08},
+ {0x31, 0x40},
+ {0x32, 0x32},
+ {0x35, 0xff},
+ {0x3a, 0x00},
+ {0x37, 0x10},
+ {0x38, 0x10},
+ {0x39, 0x02},
+ {0x42, 0x60},
+ {0x4a, 0x80},
+ {0x4b, 0x04},
+ {0x4d, 0x91},
+ {0x5d, 0xc8},
+ {0x50, 0x36},
+ {0x51, 0x36},
+ {0x52, 0x36},
+ {0x53, 0x36},
+ {0x56, 0x01},
+ {0x63, 0x0f},
+ {0x64, 0x30},
+ {0x65, 0x40},
+ {0x68, 0x26},
+ {0x69, 0x4c},
+ {0x70, 0x20},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x40},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x60},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x80},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0xa0},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x1f},
+ {0x76, 0x38},
+ {0x77, 0xa6},
+ {0x78, 0x0c},
+ {0x79, 0x80},
+ {0x7f, 0x14},
+ {0x7c, 0x00},
+ {0xae, 0x82},
+ {0x80, 0x64},
+ {0x81, 0x66},
+ {0x82, 0x44},
+ {0x85, 0x04},
+ {0xcd, 0xf4},
+ {0x90, 0x33},
+ {0xa0, 0x44},
+ {0xc0, 0x08},
+ {0xc3, 0x10},
+ {0xc4, 0x08},
+ {0xc5, 0xf0},
+ {0xc6, 0xff},
+ {0xc7, 0x00},
+ {0xc8, 0x1a},
+ {0xc9, 0x80},
+ {0xe0, 0xf8},
+ {0xe6, 0x8b},
+ {0xd0, 0x40},
+ {0xf8, 0x20},
+ {0xfa, 0x0f},
+ {0x00, 0x00},
+ {0xbd, 0x01},
+ {0xb8, 0x00},
+};
+
+static const struct m88ds3103_reg_val m88ds3103_dvbs2_init_reg_vals[] = {
+ {0x23, 0x07},
+ {0x08, 0x07},
+ {0x0c, 0x02},
+ {0x21, 0x54},
+ {0x25, 0x8a},
+ {0x27, 0x31},
+ {0x30, 0x08},
+ {0x32, 0x32},
+ {0x35, 0xff},
+ {0x3a, 0x00},
+ {0x37, 0x10},
+ {0x38, 0x10},
+ {0x39, 0x02},
+ {0x42, 0x60},
+ {0x4a, 0x80},
+ {0x4b, 0x04},
+ {0x4d, 0x91},
+ {0x5d, 0xc8},
+ {0x50, 0x36},
+ {0x51, 0x36},
+ {0x52, 0x36},
+ {0x53, 0x36},
+ {0x56, 0x01},
+ {0x63, 0x0f},
+ {0x64, 0x10},
+ {0x65, 0x20},
+ {0x68, 0x46},
+ {0x69, 0xcd},
+ {0x70, 0x20},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x40},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x60},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x80},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0xa0},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x1f},
+ {0x76, 0x38},
+ {0x77, 0xa6},
+ {0x78, 0x0c},
+ {0x79, 0x80},
+ {0x7f, 0x14},
+ {0x85, 0x08},
+ {0xcd, 0xf4},
+ {0x90, 0x33},
+ {0x86, 0x00},
+ {0x87, 0x0f},
+ {0x89, 0x00},
+ {0x8b, 0x44},
+ {0x8c, 0x66},
+ {0x9d, 0xc1},
+ {0x8a, 0x10},
+ {0xad, 0x40},
+ {0xa0, 0x44},
+ {0xc0, 0x08},
+ {0xc1, 0x10},
+ {0xc2, 0x08},
+ {0xc3, 0x10},
+ {0xc4, 0x08},
+ {0xc5, 0xf0},
+ {0xc6, 0xff},
+ {0xc7, 0x00},
+ {0xc8, 0x1a},
+ {0xc9, 0x80},
+ {0xca, 0x23},
+ {0xcb, 0x24},
+ {0xcc, 0xf4},
+ {0xce, 0x74},
+ {0x00, 0x00},
+ {0xbd, 0x01},
+ {0xb8, 0x00},
+};
+
+static const struct m88ds3103_reg_val m88rs6000_dvbs_init_reg_vals[] = {
+ {0x23, 0x07},
+ {0x08, 0x03},
+ {0x0c, 0x02},
+ {0x20, 0x00},
+ {0x21, 0x54},
+ {0x25, 0x82},
+ {0x27, 0x31},
+ {0x30, 0x08},
+ {0x31, 0x40},
+ {0x32, 0x32},
+ {0x33, 0x35},
+ {0x35, 0xff},
+ {0x3a, 0x00},
+ {0x37, 0x10},
+ {0x38, 0x10},
+ {0x39, 0x02},
+ {0x42, 0x60},
+ {0x4a, 0x80},
+ {0x4b, 0x04},
+ {0x4d, 0x91},
+ {0x5d, 0xc8},
+ {0x50, 0x36},
+ {0x51, 0x36},
+ {0x52, 0x36},
+ {0x53, 0x36},
+ {0x63, 0x0f},
+ {0x64, 0x30},
+ {0x65, 0x40},
+ {0x68, 0x26},
+ {0x69, 0x4c},
+ {0x70, 0x20},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x40},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x60},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x80},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0xa0},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x1f},
+ {0x76, 0x38},
+ {0x77, 0xa6},
+ {0x78, 0x0c},
+ {0x79, 0x80},
+ {0x7f, 0x14},
+ {0x7c, 0x00},
+ {0xae, 0x82},
+ {0x80, 0x64},
+ {0x81, 0x66},
+ {0x82, 0x44},
+ {0x85, 0x04},
+ {0xcd, 0xf4},
+ {0x90, 0x33},
+ {0xa0, 0x44},
+ {0xbe, 0x00},
+ {0xc0, 0x08},
+ {0xc3, 0x10},
+ {0xc4, 0x08},
+ {0xc5, 0xf0},
+ {0xc6, 0xff},
+ {0xc7, 0x00},
+ {0xc8, 0x1a},
+ {0xc9, 0x80},
+ {0xe0, 0xf8},
+ {0xe6, 0x8b},
+ {0xd0, 0x40},
+ {0xf8, 0x20},
+ {0xfa, 0x0f},
+ {0x00, 0x00},
+ {0xbd, 0x01},
+ {0xb8, 0x00},
+ {0x29, 0x11},
+};
+
+static const struct m88ds3103_reg_val m88rs6000_dvbs2_init_reg_vals[] = {
+ {0x23, 0x07},
+ {0x08, 0x07},
+ {0x0c, 0x02},
+ {0x20, 0x00},
+ {0x21, 0x54},
+ {0x25, 0x82},
+ {0x27, 0x31},
+ {0x30, 0x08},
+ {0x32, 0x32},
+ {0x33, 0x35},
+ {0x35, 0xff},
+ {0x3a, 0x00},
+ {0x37, 0x10},
+ {0x38, 0x10},
+ {0x39, 0x02},
+ {0x42, 0x60},
+ {0x4a, 0x80},
+ {0x4b, 0x04},
+ {0x4d, 0x91},
+ {0x5d, 0xc8},
+ {0x50, 0x36},
+ {0x51, 0x36},
+ {0x52, 0x36},
+ {0x53, 0x36},
+ {0x63, 0x0f},
+ {0x64, 0x10},
+ {0x65, 0x20},
+ {0x68, 0x46},
+ {0x69, 0xcd},
+ {0x70, 0x20},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x40},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x60},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x80},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0xa0},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x1f},
+ {0x76, 0x38},
+ {0x77, 0xa6},
+ {0x78, 0x0c},
+ {0x79, 0x80},
+ {0x7f, 0x14},
+ {0x85, 0x08},
+ {0xcd, 0xf4},
+ {0x90, 0x33},
+ {0x86, 0x00},
+ {0x87, 0x0f},
+ {0x89, 0x00},
+ {0x8b, 0x44},
+ {0x8c, 0x66},
+ {0x9d, 0xc1},
+ {0x8a, 0x10},
+ {0xad, 0x40},
+ {0xa0, 0x44},
+ {0xbe, 0x00},
+ {0xc0, 0x08},
+ {0xc1, 0x10},
+ {0xc2, 0x08},
+ {0xc3, 0x10},
+ {0xc4, 0x08},
+ {0xc5, 0xf0},
+ {0xc6, 0xff},
+ {0xc7, 0x00},
+ {0xc8, 0x1a},
+ {0xc9, 0x80},
+ {0xca, 0x23},
+ {0xcb, 0x24},
+ {0xcc, 0xf4},
+ {0xce, 0x74},
+ {0x00, 0x00},
+ {0xbd, 0x01},
+ {0xb8, 0x00},
+ {0x29, 0x01},
+};
+#endif
diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c
new file mode 100644
index 000000000000..2aa98203cd65
--- /dev/null
+++ b/drivers/media/dvb-frontends/m88rs2000.c
@@ -0,0 +1,817 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ Driver for M88RS2000 demodulator and tuner
+
+ Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
+ Beta Driver
+
+ Include various calculation code from DS3000 driver.
+ Copyright (C) 2009 Konstantin Dimitrov.
+
+
+*/
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+
+#include <media/dvb_frontend.h>
+#include "m88rs2000.h"
+
+struct m88rs2000_state {
+ struct i2c_adapter *i2c;
+ const struct m88rs2000_config *config;
+ struct dvb_frontend frontend;
+ u8 no_lock_count;
+ u32 tuner_frequency;
+ u32 symbol_rate;
+ enum fe_code_rate fec_inner;
+ u8 tuner_level;
+ int errmode;
+};
+
+static int m88rs2000_debug;
+
+module_param_named(debug, m88rs2000_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+#define dprintk(level, args...) do { \
+ if (level & m88rs2000_debug) \
+ printk(KERN_DEBUG "m88rs2000-fe: " args); \
+} while (0)
+
+#define deb_info(args...) dprintk(0x01, args)
+#define info(format, arg...) \
+ printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg)
+
+static int m88rs2000_writereg(struct m88rs2000_state *state,
+ u8 reg, u8 data)
+{
+ int ret;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = {
+ .addr = state->config->demod_addr,
+ .flags = 0,
+ .buf = buf,
+ .len = 2
+ };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ deb_info("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+ __func__, reg, data, ret);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->demod_addr,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ }, {
+ .addr = state->config->demod_addr,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ deb_info("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+ __func__, reg, ret);
+
+ return b1[0];
+}
+
+static u32 m88rs2000_get_mclk(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u32 mclk;
+ u8 reg;
+ /* Must not be 0x00 or 0xff */
+ reg = m88rs2000_readreg(state, 0x86);
+ if (!reg || reg == 0xff)
+ return 0;
+
+ reg /= 2;
+ reg += 1;
+
+ mclk = (u32)(reg * RS2000_FE_CRYSTAL_KHZ + 28 / 2) / 28;
+
+ return mclk;
+}
+
+static int m88rs2000_set_carrieroffset(struct dvb_frontend *fe, s16 offset)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u32 mclk;
+ s32 tmp;
+ u8 reg;
+ int ret;
+
+ mclk = m88rs2000_get_mclk(fe);
+ if (!mclk)
+ return -EINVAL;
+
+ tmp = (offset * 4096 + (s32)mclk / 2) / (s32)mclk;
+ if (tmp < 0)
+ tmp += 4096;
+
+ /* Carrier Offset */
+ ret = m88rs2000_writereg(state, 0x9c, (u8)(tmp >> 4));
+
+ reg = m88rs2000_readreg(state, 0x9d);
+ reg &= 0xf;
+ reg |= (u8)(tmp & 0xf) << 4;
+
+ ret |= m88rs2000_writereg(state, 0x9d, reg);
+
+ return ret;
+}
+
+static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+ u64 temp;
+ u32 mclk;
+ u8 b[3];
+
+ if ((srate < 1000000) || (srate > 45000000))
+ return -EINVAL;
+
+ mclk = m88rs2000_get_mclk(fe);
+ if (!mclk)
+ return -EINVAL;
+
+ temp = srate / 1000;
+ temp *= 1 << 24;
+
+ do_div(temp, mclk);
+
+ b[0] = (u8) (temp >> 16) & 0xff;
+ b[1] = (u8) (temp >> 8) & 0xff;
+ b[2] = (u8) temp & 0xff;
+
+ ret = m88rs2000_writereg(state, 0x93, b[2]);
+ ret |= m88rs2000_writereg(state, 0x94, b[1]);
+ ret |= m88rs2000_writereg(state, 0x95, b[0]);
+
+ if (srate > 10000000)
+ ret |= m88rs2000_writereg(state, 0xa0, 0x20);
+ else
+ ret |= m88rs2000_writereg(state, 0xa0, 0x60);
+
+ ret |= m88rs2000_writereg(state, 0xa1, 0xe0);
+
+ if (srate > 12000000)
+ ret |= m88rs2000_writereg(state, 0xa3, 0x20);
+ else if (srate > 2800000)
+ ret |= m88rs2000_writereg(state, 0xa3, 0x98);
+ else
+ ret |= m88rs2000_writereg(state, 0xa3, 0x90);
+
+ deb_info("m88rs2000: m88rs2000_set_symbolrate\n");
+ return ret;
+}
+
+static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *m)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+
+ int i;
+ u8 reg;
+ deb_info("%s\n", __func__);
+ m88rs2000_writereg(state, 0x9a, 0x30);
+ reg = m88rs2000_readreg(state, 0xb2);
+ reg &= 0x3f;
+ m88rs2000_writereg(state, 0xb2, reg);
+ for (i = 0; i < m->msg_len; i++)
+ m88rs2000_writereg(state, 0xb3 + i, m->msg[i]);
+
+ reg = m88rs2000_readreg(state, 0xb1);
+ reg &= 0x87;
+ reg |= ((m->msg_len - 1) << 3) | 0x07;
+ reg &= 0x7f;
+ m88rs2000_writereg(state, 0xb1, reg);
+
+ for (i = 0; i < 15; i++) {
+ if ((m88rs2000_readreg(state, 0xb1) & 0x40) == 0x0)
+ break;
+ msleep(20);
+ }
+
+ reg = m88rs2000_readreg(state, 0xb1);
+ if ((reg & 0x40) > 0x0) {
+ reg &= 0x7f;
+ reg |= 0x40;
+ m88rs2000_writereg(state, 0xb1, reg);
+ }
+
+ reg = m88rs2000_readreg(state, 0xb2);
+ reg &= 0x3f;
+ reg |= 0x80;
+ m88rs2000_writereg(state, 0xb2, reg);
+ m88rs2000_writereg(state, 0x9a, 0xb0);
+
+
+ return 0;
+}
+
+static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe,
+ enum fe_sec_mini_cmd burst)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 reg0, reg1;
+ deb_info("%s\n", __func__);
+ m88rs2000_writereg(state, 0x9a, 0x30);
+ msleep(50);
+ reg0 = m88rs2000_readreg(state, 0xb1);
+ reg1 = m88rs2000_readreg(state, 0xb2);
+ /* TODO complete this section */
+ m88rs2000_writereg(state, 0xb2, reg1);
+ m88rs2000_writereg(state, 0xb1, reg0);
+ m88rs2000_writereg(state, 0x9a, 0xb0);
+
+ return 0;
+}
+
+static int m88rs2000_set_tone(struct dvb_frontend *fe,
+ enum fe_sec_tone_mode tone)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 reg0, reg1;
+ m88rs2000_writereg(state, 0x9a, 0x30);
+ reg0 = m88rs2000_readreg(state, 0xb1);
+ reg1 = m88rs2000_readreg(state, 0xb2);
+
+ reg1 &= 0x3f;
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ reg0 |= 0x4;
+ reg0 &= 0xbc;
+ break;
+ case SEC_TONE_OFF:
+ reg1 |= 0x80;
+ break;
+ default:
+ break;
+ }
+ m88rs2000_writereg(state, 0xb2, reg1);
+ m88rs2000_writereg(state, 0xb1, reg0);
+ m88rs2000_writereg(state, 0x9a, 0xb0);
+ return 0;
+}
+
+struct inittab {
+ u8 cmd;
+ u8 reg;
+ u8 val;
+};
+
+static struct inittab m88rs2000_setup[] = {
+ {DEMOD_WRITE, 0x9a, 0x30},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {WRITE_DELAY, 0x19, 0x00},
+ {DEMOD_WRITE, 0x00, 0x00},
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {DEMOD_WRITE, 0x81, 0xc1},
+ {DEMOD_WRITE, 0x81, 0x81},
+ {DEMOD_WRITE, 0x86, 0xc6},
+ {DEMOD_WRITE, 0x9a, 0x30},
+ {DEMOD_WRITE, 0xf0, 0x22},
+ {DEMOD_WRITE, 0xf1, 0xbf},
+ {DEMOD_WRITE, 0xb0, 0x45},
+ {DEMOD_WRITE, 0xb2, 0x01}, /* set voltage pin always set 1*/
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {0xff, 0xaa, 0xff}
+};
+
+static struct inittab m88rs2000_shutdown[] = {
+ {DEMOD_WRITE, 0x9a, 0x30},
+ {DEMOD_WRITE, 0xb0, 0x00},
+ {DEMOD_WRITE, 0xf1, 0x89},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {DEMOD_WRITE, 0x81, 0x81},
+ {0xff, 0xaa, 0xff}
+};
+
+static struct inittab fe_reset[] = {
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0x20, 0x81},
+ {DEMOD_WRITE, 0x21, 0x80},
+ {DEMOD_WRITE, 0x10, 0x33},
+ {DEMOD_WRITE, 0x11, 0x44},
+ {DEMOD_WRITE, 0x12, 0x07},
+ {DEMOD_WRITE, 0x18, 0x20},
+ {DEMOD_WRITE, 0x28, 0x04},
+ {DEMOD_WRITE, 0x29, 0x8e},
+ {DEMOD_WRITE, 0x3b, 0xff},
+ {DEMOD_WRITE, 0x32, 0x10},
+ {DEMOD_WRITE, 0x33, 0x02},
+ {DEMOD_WRITE, 0x34, 0x30},
+ {DEMOD_WRITE, 0x35, 0xff},
+ {DEMOD_WRITE, 0x38, 0x50},
+ {DEMOD_WRITE, 0x39, 0x68},
+ {DEMOD_WRITE, 0x3c, 0x7f},
+ {DEMOD_WRITE, 0x3d, 0x0f},
+ {DEMOD_WRITE, 0x45, 0x20},
+ {DEMOD_WRITE, 0x46, 0x24},
+ {DEMOD_WRITE, 0x47, 0x7c},
+ {DEMOD_WRITE, 0x48, 0x16},
+ {DEMOD_WRITE, 0x49, 0x04},
+ {DEMOD_WRITE, 0x4a, 0x01},
+ {DEMOD_WRITE, 0x4b, 0x78},
+ {DEMOD_WRITE, 0X4d, 0xd2},
+ {DEMOD_WRITE, 0x4e, 0x6d},
+ {DEMOD_WRITE, 0x50, 0x30},
+ {DEMOD_WRITE, 0x51, 0x30},
+ {DEMOD_WRITE, 0x54, 0x7b},
+ {DEMOD_WRITE, 0x56, 0x09},
+ {DEMOD_WRITE, 0x58, 0x59},
+ {DEMOD_WRITE, 0x59, 0x37},
+ {DEMOD_WRITE, 0x63, 0xfa},
+ {0xff, 0xaa, 0xff}
+};
+
+static struct inittab fe_trigger[] = {
+ {DEMOD_WRITE, 0x97, 0x04},
+ {DEMOD_WRITE, 0x99, 0x77},
+ {DEMOD_WRITE, 0x9b, 0x64},
+ {DEMOD_WRITE, 0x9e, 0x00},
+ {DEMOD_WRITE, 0x9f, 0xf8},
+ {DEMOD_WRITE, 0x98, 0xff},
+ {DEMOD_WRITE, 0xc0, 0x0f},
+ {DEMOD_WRITE, 0x89, 0x01},
+ {DEMOD_WRITE, 0x00, 0x00},
+ {WRITE_DELAY, 0x0a, 0x00},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0x00, 0x00},
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {0xff, 0xaa, 0xff}
+};
+
+static int m88rs2000_tab_set(struct m88rs2000_state *state,
+ struct inittab *tab)
+{
+ int ret = 0;
+ u8 i;
+ if (tab == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < 255; i++) {
+ switch (tab[i].cmd) {
+ case 0x01:
+ ret = m88rs2000_writereg(state, tab[i].reg,
+ tab[i].val);
+ break;
+ case 0x10:
+ if (tab[i].reg > 0)
+ mdelay(tab[i].reg);
+ break;
+ case 0xff:
+ if (tab[i].reg == 0xaa && tab[i].val == 0xff)
+ return 0;
+ break;
+ case 0x00:
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (ret < 0)
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int m88rs2000_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage volt)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 data;
+
+ data = m88rs2000_readreg(state, 0xb2);
+ data |= 0x03; /* bit0 V/H, bit1 off/on */
+
+ switch (volt) {
+ case SEC_VOLTAGE_18:
+ data &= ~0x03;
+ break;
+ case SEC_VOLTAGE_13:
+ data &= ~0x03;
+ data |= 0x01;
+ break;
+ case SEC_VOLTAGE_OFF:
+ break;
+ }
+
+ m88rs2000_writereg(state, 0xb2, data);
+
+ return 0;
+}
+
+static int m88rs2000_init(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+
+ deb_info("m88rs2000: init chip\n");
+ /* Setup frontend from shutdown/cold */
+ if (state->config->inittab)
+ ret = m88rs2000_tab_set(state,
+ (struct inittab *)state->config->inittab);
+ else
+ ret = m88rs2000_tab_set(state, m88rs2000_setup);
+
+ return ret;
+}
+
+static int m88rs2000_sleep(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+ /* Shutdown the frondend */
+ ret = m88rs2000_tab_set(state, m88rs2000_shutdown);
+ return ret;
+}
+
+static int m88rs2000_read_status(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 reg = m88rs2000_readreg(state, 0x8c);
+
+ *status = 0;
+
+ if ((reg & 0xee) == 0xee) {
+ *status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI
+ | FE_HAS_SYNC | FE_HAS_LOCK;
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, CALL_IS_READ);
+ }
+ return 0;
+}
+
+static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 tmp0, tmp1;
+
+ m88rs2000_writereg(state, 0x9a, 0x30);
+ tmp0 = m88rs2000_readreg(state, 0xd8);
+ if ((tmp0 & 0x10) != 0) {
+ m88rs2000_writereg(state, 0x9a, 0xb0);
+ *ber = 0xffffffff;
+ return 0;
+ }
+
+ *ber = (m88rs2000_readreg(state, 0xd7) << 8) |
+ m88rs2000_readreg(state, 0xd6);
+
+ tmp1 = m88rs2000_readreg(state, 0xd9);
+ m88rs2000_writereg(state, 0xd9, (tmp1 & ~7) | 4);
+ /* needs twice */
+ m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30);
+ m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30);
+ m88rs2000_writereg(state, 0x9a, 0xb0);
+
+ return 0;
+}
+
+static int m88rs2000_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ if (fe->ops.tuner_ops.get_rf_strength)
+ fe->ops.tuner_ops.get_rf_strength(fe, strength);
+
+ return 0;
+}
+
+static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+
+ *snr = 512 * m88rs2000_readreg(state, 0x65);
+
+ return 0;
+}
+
+static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 tmp;
+
+ *ucblocks = (m88rs2000_readreg(state, 0xd5) << 8) |
+ m88rs2000_readreg(state, 0xd4);
+ tmp = m88rs2000_readreg(state, 0xd8);
+ m88rs2000_writereg(state, 0xd8, tmp & ~0x20);
+ /* needs two times */
+ m88rs2000_writereg(state, 0xd8, tmp | 0x20);
+ m88rs2000_writereg(state, 0xd8, tmp | 0x20);
+
+ return 0;
+}
+
+static int m88rs2000_set_fec(struct m88rs2000_state *state,
+ enum fe_code_rate fec)
+{
+ u8 fec_set, reg;
+ int ret;
+
+ switch (fec) {
+ case FEC_1_2:
+ fec_set = 0x8;
+ break;
+ case FEC_2_3:
+ fec_set = 0x10;
+ break;
+ case FEC_3_4:
+ fec_set = 0x20;
+ break;
+ case FEC_5_6:
+ fec_set = 0x40;
+ break;
+ case FEC_7_8:
+ fec_set = 0x80;
+ break;
+ case FEC_AUTO:
+ default:
+ fec_set = 0x0;
+ }
+
+ reg = m88rs2000_readreg(state, 0x70);
+ reg &= 0x7;
+ ret = m88rs2000_writereg(state, 0x70, reg | fec_set);
+
+ ret |= m88rs2000_writereg(state, 0x76, 0x8);
+
+ return ret;
+}
+
+static enum fe_code_rate m88rs2000_get_fec(struct m88rs2000_state *state)
+{
+ u8 reg;
+ m88rs2000_writereg(state, 0x9a, 0x30);
+ reg = m88rs2000_readreg(state, 0x76);
+ m88rs2000_writereg(state, 0x9a, 0xb0);
+
+ reg &= 0xf0;
+ reg >>= 5;
+
+ switch (reg) {
+ case 0x4:
+ return FEC_1_2;
+ case 0x3:
+ return FEC_2_3;
+ case 0x2:
+ return FEC_3_4;
+ case 0x1:
+ return FEC_5_6;
+ case 0x0:
+ return FEC_7_8;
+ default:
+ break;
+ }
+
+ return FEC_AUTO;
+}
+
+static int m88rs2000_set_frontend(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ enum fe_status status = 0;
+ int i, ret = 0;
+ u32 tuner_freq;
+ s16 offset = 0;
+ u8 reg;
+
+ state->no_lock_count = 0;
+
+ if (c->delivery_system != SYS_DVBS) {
+ deb_info("%s: unsupported delivery system selected (%d)\n",
+ __func__, c->delivery_system);
+ return -EOPNOTSUPP;
+ }
+
+ /* Set Tuner */
+ if (fe->ops.tuner_ops.set_params)
+ ret = fe->ops.tuner_ops.set_params(fe);
+
+ if (ret < 0)
+ return -ENODEV;
+
+ if (fe->ops.tuner_ops.get_frequency) {
+ ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_freq);
+
+ if (ret < 0)
+ return -ENODEV;
+
+ offset = (s16)((s32)tuner_freq - c->frequency);
+ } else {
+ offset = 0;
+ }
+
+ /* default mclk value 96.4285 * 2 * 1000 = 192857 */
+ if (((c->frequency % 192857) >= (192857 - 3000)) ||
+ (c->frequency % 192857) <= 3000)
+ ret = m88rs2000_writereg(state, 0x86, 0xc2);
+ else
+ ret = m88rs2000_writereg(state, 0x86, 0xc6);
+
+ ret |= m88rs2000_set_carrieroffset(fe, offset);
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Reset demod by symbol rate */
+ if (c->symbol_rate > 27500000)
+ ret = m88rs2000_writereg(state, 0xf1, 0xa4);
+ else
+ ret = m88rs2000_writereg(state, 0xf1, 0xbf);
+
+ ret |= m88rs2000_tab_set(state, fe_reset);
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Set FEC */
+ ret = m88rs2000_set_fec(state, c->fec_inner);
+ ret |= m88rs2000_writereg(state, 0x85, 0x1);
+ ret |= m88rs2000_writereg(state, 0x8a, 0xbf);
+ ret |= m88rs2000_writereg(state, 0x8d, 0x1e);
+ ret |= m88rs2000_writereg(state, 0x90, 0xf1);
+ ret |= m88rs2000_writereg(state, 0x91, 0x08);
+
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Set Symbol Rate */
+ ret = m88rs2000_set_symbolrate(fe, c->symbol_rate);
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Set up Demod */
+ ret = m88rs2000_tab_set(state, fe_trigger);
+ if (ret < 0)
+ return -ENODEV;
+
+ for (i = 0; i < 25; i++) {
+ reg = m88rs2000_readreg(state, 0x8c);
+ if ((reg & 0xee) == 0xee) {
+ status = FE_HAS_LOCK;
+ break;
+ }
+ state->no_lock_count++;
+ if (state->no_lock_count == 15) {
+ reg = m88rs2000_readreg(state, 0x70);
+ reg ^= 0x4;
+ m88rs2000_writereg(state, 0x70, reg);
+ state->no_lock_count = 0;
+ }
+ msleep(20);
+ }
+
+ if (status & FE_HAS_LOCK) {
+ state->fec_inner = m88rs2000_get_fec(state);
+ /* Unknown suspect SNR level */
+ reg = m88rs2000_readreg(state, 0x65);
+ }
+
+ state->tuner_frequency = c->frequency;
+ state->symbol_rate = c->symbol_rate;
+ return 0;
+}
+
+static int m88rs2000_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+
+ c->fec_inner = state->fec_inner;
+ c->frequency = state->tuner_frequency;
+ c->symbol_rate = state->symbol_rate;
+ return 0;
+}
+
+static int m88rs2000_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *tune)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (c->symbol_rate > 3000000)
+ tune->min_delay_ms = 2000;
+ else
+ tune->min_delay_ms = 3000;
+
+ tune->step_size = c->symbol_rate / 16000;
+ tune->max_drift = c->symbol_rate / 2000;
+
+ return 0;
+}
+
+static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+
+ if (enable)
+ m88rs2000_writereg(state, 0x81, 0x84);
+ else
+ m88rs2000_writereg(state, 0x81, 0x81);
+ udelay(10);
+ return 0;
+}
+
+static void m88rs2000_release(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static const struct dvb_frontend_ops m88rs2000_ops = {
+ .delsys = { SYS_DVBS },
+ .info = {
+ .name = "M88RS2000 DVB-S",
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
+ .frequency_stepsize_hz = 1 * MHz,
+ .frequency_tolerance_hz = 5 * MHz,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .symbol_rate_tolerance = 500, /* ppm */
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+ FE_CAN_QPSK | FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_AUTO
+ },
+
+ .release = m88rs2000_release,
+ .init = m88rs2000_init,
+ .sleep = m88rs2000_sleep,
+ .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl,
+ .read_status = m88rs2000_read_status,
+ .read_ber = m88rs2000_read_ber,
+ .read_signal_strength = m88rs2000_read_signal_strength,
+ .read_snr = m88rs2000_read_snr,
+ .read_ucblocks = m88rs2000_read_ucblocks,
+ .diseqc_send_master_cmd = m88rs2000_send_diseqc_msg,
+ .diseqc_send_burst = m88rs2000_send_diseqc_burst,
+ .set_tone = m88rs2000_set_tone,
+ .set_voltage = m88rs2000_set_voltage,
+
+ .set_frontend = m88rs2000_set_frontend,
+ .get_frontend = m88rs2000_get_frontend,
+ .get_tune_settings = m88rs2000_get_tune_settings,
+};
+
+struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct m88rs2000_state *state = NULL;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ state->tuner_frequency = 0;
+ state->symbol_rate = 0;
+ state->fec_inner = 0;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &m88rs2000_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(m88rs2000_attach);
+
+MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.13");
+
diff --git a/drivers/media/dvb-frontends/m88rs2000.h b/drivers/media/dvb-frontends/m88rs2000.h
new file mode 100644
index 000000000000..3a141b02d5d9
--- /dev/null
+++ b/drivers/media/dvb-frontends/m88rs2000.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Driver for M88RS2000 demodulator
+
+
+*/
+
+#ifndef M88RS2000_H
+#define M88RS2000_H
+
+#include <linux/dvb/frontend.h>
+#include <media/dvb_frontend.h>
+
+struct m88rs2000_config {
+ /* Demodulator i2c address */
+ u8 demod_addr;
+
+ u8 *inittab;
+
+ /* minimum delay before retuning */
+ int min_delay_ms;
+
+ int (*set_ts_params)(struct dvb_frontend *, int);
+};
+
+enum {
+ CALL_IS_SET_FRONTEND = 0x0,
+ CALL_IS_READ,
+};
+
+#if IS_REACHABLE(CONFIG_DVB_M88RS2000)
+extern struct dvb_frontend *m88rs2000_attach(
+ const struct m88rs2000_config *config, struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *m88rs2000_attach(
+ const struct m88rs2000_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_M88RS2000 */
+
+#define RS2000_FE_CRYSTAL_KHZ 27000
+
+enum {
+ DEMOD_WRITE = 0x1,
+ WRITE_DELAY = 0x10,
+};
+#endif /* M88RS2000_H */
diff --git a/drivers/media/dvb/frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c
index 9ae40abfd71a..9033e39d75f4 100644
--- a/drivers/media/dvb/frontends/mb86a16.c
+++ b/drivers/media/dvb-frontends/mb86a16.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Fujitsu MB86A16 DVB-S/DSS DC Receiver driver
Copyright (C) Manu Abraham (abraham.manu@gmail.com)
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
@@ -24,15 +12,13 @@
#include <linux/moduleparam.h>
#include <linux/slab.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "mb86a16.h"
#include "mb86a16_priv.h"
-unsigned int verbose = 5;
+static unsigned int verbose = 5;
module_param(verbose, int, 0644);
-#define ABS(x) ((x) < 0 ? (-x) : (x))
-
struct mb86a16_state {
struct i2c_adapter *i2c_adap;
const struct mb86a16_config *config;
@@ -115,9 +101,11 @@ static int mb86a16_read(struct mb86a16_state *state, u8 reg, u8 *val)
};
ret = i2c_transfer(state->i2c_adap, msg, 2);
if (ret != 2) {
- dprintk(verbose, MB86A16_ERROR, 1, "read error(reg=0x%02x, ret=0x%i)",
+ dprintk(verbose, MB86A16_ERROR, 1, "read error(reg=0x%02x, ret=%i)",
reg, ret);
+ if (ret < 0)
+ return ret;
return -EREMOTEIO;
}
*val = b1[0];
@@ -413,27 +401,21 @@ static int signal_det(struct mb86a16_state *state,
int smrt,
unsigned char *SIG)
{
-
- int ret ;
- int smrtd ;
- int wait_sym ;
-
- u32 wait_t;
- unsigned char S[3] ;
- int i ;
+ int ret;
+ int smrtd;
+ unsigned char S[3];
+ int i;
if (*SIG > 45) {
if (CNTM_set(state, 2, 1, 2) < 0) {
dprintk(verbose, MB86A16_ERROR, 1, "CNTM set Error");
return -1;
}
- wait_sym = 40000;
} else {
if (CNTM_set(state, 3, 1, 2) < 0) {
dprintk(verbose, MB86A16_ERROR, 1, "CNTM set Error");
return -1;
}
- wait_sym = 80000;
}
for (i = 0; i < 3; i++) {
if (i == 0)
@@ -445,22 +427,17 @@ static int signal_det(struct mb86a16_state *state,
smrt_info_get(state, smrtd);
smrt_set(state, smrtd);
srst(state);
- wait_t = (wait_sym + 99 * smrtd / 100) / smrtd;
- if (wait_t == 0)
- wait_t = 1;
msleep_interruptible(10);
if (mb86a16_read(state, 0x37, &(S[i])) != 2) {
dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error");
return -EREMOTEIO;
}
}
- if ((S[1] > S[0] * 112 / 100) &&
- (S[1] > S[2] * 112 / 100)) {
-
+ if ((S[1] > S[0] * 112 / 100) && (S[1] > S[2] * 112 / 100))
ret = 1;
- } else {
+ else
ret = 0;
- }
+
*SIG = S[1];
if (CNTM_set(state, 0, 1, 2) < 0) {
@@ -591,7 +568,7 @@ err:
return -EREMOTEIO;
}
-static int mb86a16_read_status(struct dvb_frontend *fe, fe_status_t *status)
+static int mb86a16_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
u8 stat, stat2;
struct mb86a16_state *state = fe->demodulator_priv;
@@ -644,6 +621,7 @@ static int sync_chk(struct mb86a16_state *state,
return sync;
err:
dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error");
+ *VIRM = 0;
return -EREMOTEIO;
}
@@ -1210,12 +1188,12 @@ static int mb86a16_set_fe(struct mb86a16_state *state)
signal_dupl = 0;
for (j = 0; j < prev_freq_num; j++) {
- if ((ABS(prev_swp_freq[j] - swp_freq)) < (swp_ofs * 3 / 2)) {
+ if ((abs(prev_swp_freq[j] - swp_freq)) < (swp_ofs * 3 / 2)) {
signal_dupl = 1;
dprintk(verbose, MB86A16_INFO, 1, "Probably Duplicate Signal, j = %d", j);
}
}
- if ((signal_dupl == 0) && (swp_freq > 0) && (ABS(swp_freq - state->frequency * 1000) < fcp + state->srate / 6)) {
+ if ((signal_dupl == 0) && (swp_freq > 0) && (abs(swp_freq - state->frequency * 1000) < fcp + state->srate / 6)) {
dprintk(verbose, MB86A16_DEBUG, 1, "------ Signal detect ------ [swp_freq=[%07d, srate=%05d]]", swp_freq, state->srate);
prev_swp_freq[prev_freq_num] = swp_freq;
prev_freq_num++;
@@ -1389,7 +1367,7 @@ static int mb86a16_set_fe(struct mb86a16_state *state)
dprintk(verbose, MB86A16_INFO, 1, "SWEEP Frequency = %d", swp_freq);
swp_freq += delta_freq;
dprintk(verbose, MB86A16_INFO, 1, "Adjusting .., DELTA Freq = %d, SWEEP Freq=%d", delta_freq, swp_freq);
- if (ABS(state->frequency * 1000 - swp_freq) > 3800) {
+ if (abs(state->frequency * 1000 - swp_freq) > 3800) {
dprintk(verbose, MB86A16_INFO, 1, "NO -- SIGNAL !");
} else {
@@ -1474,11 +1452,8 @@ static int mb86a16_set_fe(struct mb86a16_state *state)
wait_t = (786432 + state->srate / 2) / state->srate;
else
wait_t = (1572864 + state->srate / 2) / state->srate;
- if (state->srate < 5000)
- /* FIXME ! , should be a long wait ! */
- msleep_interruptible(wait_t);
- else
- msleep_interruptible(wait_t);
+
+ msleep_interruptible(wait_t);
if (sync_chk(state, &junk) == 0) {
iq_vt_set(state, 1);
@@ -1512,10 +1487,12 @@ static int mb86a16_set_fe(struct mb86a16_state *state)
}
}
- mb86a16_read(state, 0x15, &agcval);
- mb86a16_read(state, 0x26, &cnmval);
- dprintk(verbose, MB86A16_INFO, 1, "AGC = %02x CNM = %02x", agcval, cnmval);
-
+ if (mb86a16_read(state, 0x15, &agcval) != 2 || mb86a16_read(state, 0x26, &cnmval) != 2) {
+ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error");
+ ret = -EREMOTEIO;
+ } else {
+ dprintk(verbose, MB86A16_INFO, 1, "AGC = %02x CNM = %02x", agcval, cnmval);
+ }
return ret;
}
@@ -1523,6 +1500,7 @@ static int mb86a16_send_diseqc_msg(struct dvb_frontend *fe,
struct dvb_diseqc_master_cmd *cmd)
{
struct mb86a16_state *state = fe->demodulator_priv;
+ int ret = -EREMOTEIO;
int i;
u8 regs;
@@ -1535,8 +1513,10 @@ static int mb86a16_send_diseqc_msg(struct dvb_frontend *fe,
regs = 0x18;
- if (cmd->msg_len > 5 || cmd->msg_len < 4)
- return -EINVAL;
+ if (cmd->msg_len > 5 || cmd->msg_len < 4) {
+ ret = -EINVAL;
+ goto err;
+ }
for (i = 0; i < cmd->msg_len; i++) {
if (mb86a16_write(state, regs, cmd->msg[i]) < 0)
@@ -1557,10 +1537,11 @@ static int mb86a16_send_diseqc_msg(struct dvb_frontend *fe,
err:
dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error");
- return -EREMOTEIO;
+ return ret;
}
-static int mb86a16_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst)
+static int mb86a16_send_diseqc_burst(struct dvb_frontend *fe,
+ enum fe_sec_mini_cmd burst)
{
struct mb86a16_state *state = fe->demodulator_priv;
@@ -1588,7 +1569,7 @@ err:
return -EREMOTEIO;
}
-static int mb86a16_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+static int mb86a16_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
{
struct mb86a16_state *state = fe->demodulator_priv;
@@ -1684,15 +1665,15 @@ static int mb86a16_read_ber(struct dvb_frontend *fe, u32 *ber)
* the deinterleaver output.
* monitored BER is expressed as a 20 bit output in total
*/
- ber_rst = ber_mon >> 3;
+ ber_rst = (ber_mon >> 3) & 0x03;
*ber = (((ber_msb << 8) | ber_mid) << 8) | ber_lsb;
if (ber_rst == 0)
timer = 12500000;
- if (ber_rst == 1)
+ else if (ber_rst == 1)
timer = 25000000;
- if (ber_rst == 2)
+ else if (ber_rst == 2)
timer = 50000000;
- if (ber_rst == 3)
+ else /* ber_rst == 3 */
timer = 100000000;
*ber /= timer;
@@ -1704,11 +1685,11 @@ static int mb86a16_read_ber(struct dvb_frontend *fe, u32 *ber)
* QPSK demodulator output.
* monitored BER is expressed as a 24 bit output in total
*/
- ber_tim = ber_mon >> 1;
+ ber_tim = (ber_mon >> 1) & 0x01;
*ber = (((ber_msb << 8) | ber_mid) << 8) | ber_lsb;
if (ber_tim == 0)
timer = 16;
- if (ber_tim == 1)
+ else /* ber_tim == 1 */
timer = 24;
*ber /= 2 ^ timer;
@@ -1813,14 +1794,13 @@ static enum dvbfe_algo mb86a16_frontend_algo(struct dvb_frontend *fe)
return DVBFE_ALGO_CUSTOM;
}
-static struct dvb_frontend_ops mb86a16_ops = {
+static const struct dvb_frontend_ops mb86a16_ops = {
.delsys = { SYS_DVBS },
.info = {
.name = "Fujitsu MB86A16 DVB-S",
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .frequency_stepsize = 3000,
- .frequency_tolerance = 0,
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
+ .frequency_stepsize_hz = 3 * MHz,
.symbol_rate_min = 1000000,
.symbol_rate_max = 45000000,
.symbol_rate_tolerance = 500,
@@ -1873,6 +1853,7 @@ error:
kfree(state);
return NULL;
}
-EXPORT_SYMBOL(mb86a16_attach);
+EXPORT_SYMBOL_GPL(mb86a16_attach);
+MODULE_DESCRIPTION("Fujitsu MB86A16 DVB-S/DSS DC Receiver driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Manu Abraham");
diff --git a/drivers/media/dvb-frontends/mb86a16.h b/drivers/media/dvb-frontends/mb86a16.h
new file mode 100644
index 000000000000..c0c5419280ab
--- /dev/null
+++ b/drivers/media/dvb-frontends/mb86a16.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Fujitsu MB86A16 DVB-S/DSS DC Receiver driver
+
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+*/
+
+#ifndef __MB86A16_H
+#define __MB86A16_H
+
+#include <linux/dvb/frontend.h>
+#include <media/dvb_frontend.h>
+
+
+struct mb86a16_config {
+ u8 demod_address;
+
+ int (*set_voltage)(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage);
+};
+
+
+
+#if IS_REACHABLE(CONFIG_DVB_MB86A16)
+
+extern struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config,
+ struct i2c_adapter *i2c_adap);
+
+#else
+
+static inline struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config,
+ struct i2c_adapter *i2c_adap)
+{
+ printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+#endif /* CONFIG_DVB_MB86A16 */
+
+#endif /* __MB86A16_H */
diff --git a/drivers/media/dvb/frontends/mb86a16_priv.h b/drivers/media/dvb-frontends/mb86a16_priv.h
index 360a35acfe84..1670e4b02c70 100644
--- a/drivers/media/dvb/frontends/mb86a16_priv.h
+++ b/drivers/media/dvb-frontends/mb86a16_priv.h
@@ -1,21 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
Fujitsu MB86A16 DVB-S/DSS DC Receiver driver
Copyright (C) Manu Abraham (abraham.manu@gmail.com)
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __MB86A16_PRIV_H
diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
new file mode 100644
index 000000000000..f8e4bbee5bd5
--- /dev/null
+++ b/drivers/media/dvb-frontends/mb86a20s.c
@@ -0,0 +1,2112 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Fujitu mb86a20s ISDB-T/ISDB-Tsb Module driver
+ *
+ * Copyright (C) 2010-2013 Mauro Carvalho Chehab
+ * Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com>
+ */
+
+#include <linux/kernel.h>
+#include <asm/div64.h>
+
+#include <media/dvb_frontend.h>
+#include "mb86a20s.h"
+
+#define NUM_LAYERS 3
+
+enum mb86a20s_bandwidth {
+ MB86A20S_13SEG = 0,
+ MB86A20S_13SEG_PARTIAL = 1,
+ MB86A20S_1SEG = 2,
+ MB86A20S_3SEG = 3,
+};
+
+static u8 mb86a20s_subchannel[] = {
+ 0xb0, 0xc0, 0xd0, 0xe0,
+ 0xf0, 0x00, 0x10, 0x20,
+};
+
+struct mb86a20s_state {
+ struct i2c_adapter *i2c;
+ const struct mb86a20s_config *config;
+ u32 last_frequency;
+
+ struct dvb_frontend frontend;
+
+ u32 if_freq;
+ enum mb86a20s_bandwidth bw;
+ bool inversion;
+ u32 subchannel;
+
+ u32 estimated_rate[NUM_LAYERS];
+ unsigned long get_strength_time;
+
+ bool need_init;
+};
+
+struct regdata {
+ u8 reg;
+ u8 data;
+};
+
+#define BER_SAMPLING_RATE 1 /* Seconds */
+
+/*
+ * Initialization sequence: Use whatevere default values that PV SBTVD
+ * does on its initialisation, obtained via USB snoop
+ */
+static struct regdata mb86a20s_init1[] = {
+ { 0x70, 0x0f },
+ { 0x70, 0xff },
+ { 0x08, 0x01 },
+ { 0x50, 0xd1 }, { 0x51, 0x20 },
+};
+
+static struct regdata mb86a20s_init2[] = {
+ { 0x50, 0xd1 }, { 0x51, 0x22 },
+ { 0x39, 0x01 },
+ { 0x71, 0x00 },
+ { 0x3b, 0x21 },
+ { 0x3c, 0x3a },
+ { 0x01, 0x0d },
+ { 0x04, 0x08 }, { 0x05, 0x05 },
+ { 0x04, 0x0e }, { 0x05, 0x00 },
+ { 0x04, 0x0f }, { 0x05, 0x14 },
+ { 0x04, 0x0b }, { 0x05, 0x8c },
+ { 0x04, 0x00 }, { 0x05, 0x00 },
+ { 0x04, 0x01 }, { 0x05, 0x07 },
+ { 0x04, 0x02 }, { 0x05, 0x0f },
+ { 0x04, 0x03 }, { 0x05, 0xa0 },
+ { 0x04, 0x09 }, { 0x05, 0x00 },
+ { 0x04, 0x0a }, { 0x05, 0xff },
+ { 0x04, 0x27 }, { 0x05, 0x64 },
+ { 0x04, 0x28 }, { 0x05, 0x00 },
+ { 0x04, 0x1e }, { 0x05, 0xff },
+ { 0x04, 0x29 }, { 0x05, 0x0a },
+ { 0x04, 0x32 }, { 0x05, 0x0a },
+ { 0x04, 0x14 }, { 0x05, 0x02 },
+ { 0x04, 0x04 }, { 0x05, 0x00 },
+ { 0x04, 0x05 }, { 0x05, 0x22 },
+ { 0x04, 0x06 }, { 0x05, 0x0e },
+ { 0x04, 0x07 }, { 0x05, 0xd8 },
+ { 0x04, 0x12 }, { 0x05, 0x00 },
+ { 0x04, 0x13 }, { 0x05, 0xff },
+
+ /*
+ * On this demod, when the bit count reaches the count below,
+ * it collects the bit error count. The bit counters are initialized
+ * to 65535 here. This warrants that all of them will be quickly
+ * calculated when device gets locked. As TMCC is parsed, the values
+ * will be adjusted later in the driver's code.
+ */
+ { 0x52, 0x01 }, /* Turn on BER before Viterbi */
+ { 0x50, 0xa7 }, { 0x51, 0x00 },
+ { 0x50, 0xa8 }, { 0x51, 0xff },
+ { 0x50, 0xa9 }, { 0x51, 0xff },
+ { 0x50, 0xaa }, { 0x51, 0x00 },
+ { 0x50, 0xab }, { 0x51, 0xff },
+ { 0x50, 0xac }, { 0x51, 0xff },
+ { 0x50, 0xad }, { 0x51, 0x00 },
+ { 0x50, 0xae }, { 0x51, 0xff },
+ { 0x50, 0xaf }, { 0x51, 0xff },
+
+ /*
+ * On this demod, post BER counts blocks. When the count reaches the
+ * value below, it collects the block error count. The block counters
+ * are initialized to 127 here. This warrants that all of them will be
+ * quickly calculated when device gets locked. As TMCC is parsed, the
+ * values will be adjusted later in the driver's code.
+ */
+ { 0x5e, 0x07 }, /* Turn on BER after Viterbi */
+ { 0x50, 0xdc }, { 0x51, 0x00 },
+ { 0x50, 0xdd }, { 0x51, 0x7f },
+ { 0x50, 0xde }, { 0x51, 0x00 },
+ { 0x50, 0xdf }, { 0x51, 0x7f },
+ { 0x50, 0xe0 }, { 0x51, 0x00 },
+ { 0x50, 0xe1 }, { 0x51, 0x7f },
+
+ /*
+ * On this demod, when the block count reaches the count below,
+ * it collects the block error count. The block counters are initialized
+ * to 127 here. This warrants that all of them will be quickly
+ * calculated when device gets locked. As TMCC is parsed, the values
+ * will be adjusted later in the driver's code.
+ */
+ { 0x50, 0xb0 }, { 0x51, 0x07 }, /* Enable PER */
+ { 0x50, 0xb2 }, { 0x51, 0x00 },
+ { 0x50, 0xb3 }, { 0x51, 0x7f },
+ { 0x50, 0xb4 }, { 0x51, 0x00 },
+ { 0x50, 0xb5 }, { 0x51, 0x7f },
+ { 0x50, 0xb6 }, { 0x51, 0x00 },
+ { 0x50, 0xb7 }, { 0x51, 0x7f },
+
+ { 0x50, 0x50 }, { 0x51, 0x02 }, /* MER manual mode */
+ { 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */
+ { 0x45, 0x04 }, /* CN symbol 4 */
+ { 0x48, 0x04 }, /* CN manual mode */
+ { 0x50, 0xd5 }, { 0x51, 0x01 },
+ { 0x50, 0xd6 }, { 0x51, 0x1f },
+ { 0x50, 0xd2 }, { 0x51, 0x03 },
+ { 0x50, 0xd7 }, { 0x51, 0x3f },
+ { 0x1c, 0x01 },
+ { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x03 },
+ { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0d },
+ { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 },
+ { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x01 },
+ { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x21 },
+ { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x29 },
+ { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 },
+ { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x31 },
+ { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0e },
+ { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x4e },
+ { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x46 },
+ { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f },
+ { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x56 },
+ { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x35 },
+ { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbe },
+ { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0x84 },
+ { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x03 }, { 0x2b, 0xee },
+ { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x98 },
+ { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x9f },
+ { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xb2 },
+ { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0xc2 },
+ { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0x4a },
+ { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbc },
+ { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x04 }, { 0x2b, 0xba },
+ { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0x14 },
+ { 0x50, 0x1e }, { 0x51, 0x5d },
+ { 0x50, 0x22 }, { 0x51, 0x00 },
+ { 0x50, 0x23 }, { 0x51, 0xc8 },
+ { 0x50, 0x24 }, { 0x51, 0x00 },
+ { 0x50, 0x25 }, { 0x51, 0xf0 },
+ { 0x50, 0x26 }, { 0x51, 0x00 },
+ { 0x50, 0x27 }, { 0x51, 0xc3 },
+ { 0x50, 0x39 }, { 0x51, 0x02 },
+ { 0x50, 0xd5 }, { 0x51, 0x01 },
+ { 0xd0, 0x00 },
+};
+
+static struct regdata mb86a20s_reset_reception[] = {
+ { 0x70, 0xf0 },
+ { 0x70, 0xff },
+ { 0x08, 0x01 },
+ { 0x08, 0x00 },
+};
+
+static struct regdata mb86a20s_per_ber_reset[] = {
+ { 0x53, 0x00 }, /* pre BER Counter reset */
+ { 0x53, 0x07 },
+
+ { 0x5f, 0x00 }, /* post BER Counter reset */
+ { 0x5f, 0x07 },
+
+ { 0x50, 0xb1 }, /* PER Counter reset */
+ { 0x51, 0x07 },
+ { 0x51, 0x00 },
+};
+
+/*
+ * I2C read/write functions and macros
+ */
+
+static int mb86a20s_i2c_writereg(struct mb86a20s_state *state,
+ u8 i2c_addr, u8 reg, u8 data)
+{
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = {
+ .addr = i2c_addr, .flags = 0, .buf = buf, .len = 2
+ };
+ int rc;
+
+ rc = i2c_transfer(state->i2c, &msg, 1);
+ if (rc != 1) {
+ dev_err(&state->i2c->dev,
+ "%s: writereg error (rc == %i, reg == 0x%02x, data == 0x%02x)\n",
+ __func__, rc, reg, data);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int mb86a20s_i2c_writeregdata(struct mb86a20s_state *state,
+ u8 i2c_addr, struct regdata *rd, int size)
+{
+ int i, rc;
+
+ for (i = 0; i < size; i++) {
+ rc = mb86a20s_i2c_writereg(state, i2c_addr, rd[i].reg,
+ rd[i].data);
+ if (rc < 0)
+ return rc;
+ }
+ return 0;
+}
+
+static int mb86a20s_i2c_readreg(struct mb86a20s_state *state,
+ u8 i2c_addr, u8 reg)
+{
+ u8 val;
+ int rc;
+ struct i2c_msg msg[] = {
+ { .addr = i2c_addr, .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = i2c_addr, .flags = I2C_M_RD, .buf = &val, .len = 1 }
+ };
+
+ rc = i2c_transfer(state->i2c, msg, 2);
+
+ if (rc != 2) {
+ dev_err(&state->i2c->dev, "%s: reg=0x%x (error=%d)\n",
+ __func__, reg, rc);
+ return (rc < 0) ? rc : -EIO;
+ }
+
+ return val;
+}
+
+#define mb86a20s_readreg(state, reg) \
+ mb86a20s_i2c_readreg(state, state->config->demod_address, reg)
+#define mb86a20s_writereg(state, reg, val) \
+ mb86a20s_i2c_writereg(state, state->config->demod_address, reg, val)
+#define mb86a20s_writeregdata(state, regdata) \
+ mb86a20s_i2c_writeregdata(state, state->config->demod_address, \
+ regdata, ARRAY_SIZE(regdata))
+
+/*
+ * Ancillary internal routines (likely compiled inlined)
+ *
+ * The functions below assume that gateway lock has already obtained
+ */
+
+static int mb86a20s_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ int val;
+
+ *status = 0;
+
+ val = mb86a20s_readreg(state, 0x0a);
+ if (val < 0)
+ return val;
+
+ val &= 0xf;
+ if (val >= 2)
+ *status |= FE_HAS_SIGNAL;
+
+ if (val >= 4)
+ *status |= FE_HAS_CARRIER;
+
+ if (val >= 5)
+ *status |= FE_HAS_VITERBI;
+
+ if (val >= 7)
+ *status |= FE_HAS_SYNC;
+
+ /*
+ * Actually, on state S8, it starts receiving TS, but the TS
+ * output is only on normal state after the transition to S9.
+ */
+ if (val >= 9)
+ *status |= FE_HAS_LOCK;
+
+ dev_dbg(&state->i2c->dev, "%s: Status = 0x%02x (state = %d)\n",
+ __func__, *status, val);
+
+ return val;
+}
+
+static int mb86a20s_read_signal_strength(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int rc;
+ unsigned rf_max, rf_min, rf;
+
+ if (state->get_strength_time &&
+ (!time_after(jiffies, state->get_strength_time)))
+ return c->strength.stat[0].uvalue;
+
+ /* Reset its value if an error happen */
+ c->strength.stat[0].uvalue = 0;
+
+ /* Does a binary search to get RF strength */
+ rf_max = 0xfff;
+ rf_min = 0;
+ do {
+ rf = (rf_max + rf_min) / 2;
+ rc = mb86a20s_writereg(state, 0x04, 0x1f);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x05, rf >> 8);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x04, 0x20);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x05, rf);
+ if (rc < 0)
+ return rc;
+
+ rc = mb86a20s_readreg(state, 0x02);
+ if (rc < 0)
+ return rc;
+ if (rc & 0x08)
+ rf_min = (rf_max + rf_min) / 2;
+ else
+ rf_max = (rf_max + rf_min) / 2;
+ if (rf_max - rf_min < 4) {
+ rf = (rf_max + rf_min) / 2;
+
+ /* Rescale it from 2^12 (4096) to 2^16 */
+ rf = rf << (16 - 12);
+ if (rf)
+ rf |= (1 << 12) - 1;
+
+ dev_dbg(&state->i2c->dev,
+ "%s: signal strength = %d (%d < RF=%d < %d)\n",
+ __func__, rf, rf_min, rf >> 4, rf_max);
+ c->strength.stat[0].uvalue = rf;
+ state->get_strength_time = jiffies +
+ msecs_to_jiffies(1000);
+ return 0;
+ }
+ } while (1);
+}
+
+static int mb86a20s_get_modulation(struct mb86a20s_state *state,
+ unsigned layer)
+{
+ int rc;
+ static unsigned char reg[] = {
+ [0] = 0x86, /* Layer A */
+ [1] = 0x8a, /* Layer B */
+ [2] = 0x8e, /* Layer C */
+ };
+
+ if (layer >= ARRAY_SIZE(reg))
+ return -EINVAL;
+ rc = mb86a20s_writereg(state, 0x6d, reg[layer]);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x6e);
+ if (rc < 0)
+ return rc;
+ switch ((rc >> 4) & 0x07) {
+ case 0:
+ return DQPSK;
+ case 1:
+ return QPSK;
+ case 2:
+ return QAM_16;
+ case 3:
+ return QAM_64;
+ default:
+ return QAM_AUTO;
+ }
+}
+
+static int mb86a20s_get_fec(struct mb86a20s_state *state,
+ unsigned layer)
+{
+ int rc;
+
+ static unsigned char reg[] = {
+ [0] = 0x87, /* Layer A */
+ [1] = 0x8b, /* Layer B */
+ [2] = 0x8f, /* Layer C */
+ };
+
+ if (layer >= ARRAY_SIZE(reg))
+ return -EINVAL;
+ rc = mb86a20s_writereg(state, 0x6d, reg[layer]);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x6e);
+ if (rc < 0)
+ return rc;
+ switch ((rc >> 4) & 0x07) {
+ case 0:
+ return FEC_1_2;
+ case 1:
+ return FEC_2_3;
+ case 2:
+ return FEC_3_4;
+ case 3:
+ return FEC_5_6;
+ case 4:
+ return FEC_7_8;
+ default:
+ return FEC_AUTO;
+ }
+}
+
+static int mb86a20s_get_interleaving(struct mb86a20s_state *state,
+ unsigned layer)
+{
+ int rc;
+ static const int interleaving[] = {
+ 0, 1, 2, 4, 8
+ };
+
+ static const unsigned char reg[] = {
+ [0] = 0x88, /* Layer A */
+ [1] = 0x8c, /* Layer B */
+ [2] = 0x90, /* Layer C */
+ };
+
+ if (layer >= ARRAY_SIZE(reg))
+ return -EINVAL;
+ rc = mb86a20s_writereg(state, 0x6d, reg[layer]);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x6e);
+ if (rc < 0)
+ return rc;
+
+ return interleaving[(rc >> 4) & 0x07];
+}
+
+static int mb86a20s_get_segment_count(struct mb86a20s_state *state,
+ unsigned layer)
+{
+ int rc, count;
+ static unsigned char reg[] = {
+ [0] = 0x89, /* Layer A */
+ [1] = 0x8d, /* Layer B */
+ [2] = 0x91, /* Layer C */
+ };
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ if (layer >= ARRAY_SIZE(reg))
+ return -EINVAL;
+
+ rc = mb86a20s_writereg(state, 0x6d, reg[layer]);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x6e);
+ if (rc < 0)
+ return rc;
+ count = (rc >> 4) & 0x0f;
+
+ dev_dbg(&state->i2c->dev, "%s: segments: %d.\n", __func__, count);
+
+ return count;
+}
+
+static void mb86a20s_reset_frontend_cache(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ /* Fixed parameters */
+ c->delivery_system = SYS_ISDBT;
+ c->bandwidth_hz = 6000000;
+
+ /* Initialize values that will be later autodetected */
+ c->isdbt_layer_enabled = 0;
+ c->transmission_mode = TRANSMISSION_MODE_AUTO;
+ c->guard_interval = GUARD_INTERVAL_AUTO;
+ c->isdbt_sb_mode = 0;
+ c->isdbt_sb_segment_count = 0;
+}
+
+/*
+ * Estimates the bit rate using the per-segment bit rate given by
+ * ABNT/NBR 15601 spec (table 4).
+ */
+static const u32 isdbt_rate[3][5][4] = {
+ { /* DQPSK/QPSK */
+ { 280850, 312060, 330420, 340430 }, /* 1/2 */
+ { 374470, 416080, 440560, 453910 }, /* 2/3 */
+ { 421280, 468090, 495630, 510650 }, /* 3/4 */
+ { 468090, 520100, 550700, 567390 }, /* 5/6 */
+ { 491500, 546110, 578230, 595760 }, /* 7/8 */
+ }, { /* QAM16 */
+ { 561710, 624130, 660840, 680870 }, /* 1/2 */
+ { 748950, 832170, 881120, 907820 }, /* 2/3 */
+ { 842570, 936190, 991260, 1021300 }, /* 3/4 */
+ { 936190, 1040210, 1101400, 1134780 }, /* 5/6 */
+ { 983000, 1092220, 1156470, 1191520 }, /* 7/8 */
+ }, { /* QAM64 */
+ { 842570, 936190, 991260, 1021300 }, /* 1/2 */
+ { 1123430, 1248260, 1321680, 1361740 }, /* 2/3 */
+ { 1263860, 1404290, 1486900, 1531950 }, /* 3/4 */
+ { 1404290, 1560320, 1652110, 1702170 }, /* 5/6 */
+ { 1474500, 1638340, 1734710, 1787280 }, /* 7/8 */
+ }
+};
+
+static u32 isdbt_layer_min_bitrate(struct dtv_frontend_properties *c,
+ u32 layer)
+{
+ int mod, fec, guard;
+
+ /*
+ * If modulation/fec/guard is not detected, the default is
+ * to consider the lowest bit rate, to avoid taking too long time
+ * to get BER.
+ */
+ switch (c->layer[layer].modulation) {
+ case DQPSK:
+ case QPSK:
+ default:
+ mod = 0;
+ break;
+ case QAM_16:
+ mod = 1;
+ break;
+ case QAM_64:
+ mod = 2;
+ break;
+ }
+
+ switch (c->layer[layer].fec) {
+ default:
+ case FEC_1_2:
+ case FEC_AUTO:
+ fec = 0;
+ break;
+ case FEC_2_3:
+ fec = 1;
+ break;
+ case FEC_3_4:
+ fec = 2;
+ break;
+ case FEC_5_6:
+ fec = 3;
+ break;
+ case FEC_7_8:
+ fec = 4;
+ break;
+ }
+
+ switch (c->guard_interval) {
+ default:
+ case GUARD_INTERVAL_1_4:
+ guard = 0;
+ break;
+ case GUARD_INTERVAL_1_8:
+ guard = 1;
+ break;
+ case GUARD_INTERVAL_1_16:
+ guard = 2;
+ break;
+ case GUARD_INTERVAL_1_32:
+ guard = 3;
+ break;
+ }
+
+ return isdbt_rate[mod][fec][guard] * c->layer[layer].segment_count;
+}
+
+static int mb86a20s_get_frontend(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int layer, rc, rate, counter;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ /* Reset frontend cache to default values */
+ mb86a20s_reset_frontend_cache(fe);
+
+ /* Check for partial reception */
+ rc = mb86a20s_writereg(state, 0x6d, 0x85);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x6e);
+ if (rc < 0)
+ return rc;
+ c->isdbt_partial_reception = (rc & 0x10) ? 1 : 0;
+
+ /* Get per-layer data */
+
+ for (layer = 0; layer < NUM_LAYERS; layer++) {
+ dev_dbg(&state->i2c->dev, "%s: getting data for layer %c.\n",
+ __func__, 'A' + layer);
+
+ rc = mb86a20s_get_segment_count(state, layer);
+ if (rc < 0)
+ goto noperlayer_error;
+ if (rc >= 0 && rc < 14) {
+ c->layer[layer].segment_count = rc;
+ } else {
+ c->layer[layer].segment_count = 0;
+ state->estimated_rate[layer] = 0;
+ continue;
+ }
+ c->isdbt_layer_enabled |= 1 << layer;
+ rc = mb86a20s_get_modulation(state, layer);
+ if (rc < 0)
+ goto noperlayer_error;
+ dev_dbg(&state->i2c->dev, "%s: modulation %d.\n",
+ __func__, rc);
+ c->layer[layer].modulation = rc;
+ rc = mb86a20s_get_fec(state, layer);
+ if (rc < 0)
+ goto noperlayer_error;
+ dev_dbg(&state->i2c->dev, "%s: FEC %d.\n",
+ __func__, rc);
+ c->layer[layer].fec = rc;
+ rc = mb86a20s_get_interleaving(state, layer);
+ if (rc < 0)
+ goto noperlayer_error;
+ dev_dbg(&state->i2c->dev, "%s: interleaving %d.\n",
+ __func__, rc);
+ c->layer[layer].interleaving = rc;
+
+ rate = isdbt_layer_min_bitrate(c, layer);
+ counter = rate * BER_SAMPLING_RATE;
+
+ /* Avoids sampling too quickly or to overflow the register */
+ if (counter < 256)
+ counter = 256;
+ else if (counter > (1 << 24) - 1)
+ counter = (1 << 24) - 1;
+
+ dev_dbg(&state->i2c->dev,
+ "%s: layer %c bitrate: %d kbps; counter = %d (0x%06x)\n",
+ __func__, 'A' + layer, rate / 1000, counter, counter);
+
+ state->estimated_rate[layer] = counter;
+ }
+
+ rc = mb86a20s_writereg(state, 0x6d, 0x84);
+ if (rc < 0)
+ return rc;
+ if ((rc & 0x60) == 0x20) {
+ c->isdbt_sb_mode = 1;
+ /* At least, one segment should exist */
+ if (!c->isdbt_sb_segment_count)
+ c->isdbt_sb_segment_count = 1;
+ }
+
+ /* Get transmission mode and guard interval */
+ rc = mb86a20s_readreg(state, 0x07);
+ if (rc < 0)
+ return rc;
+ c->transmission_mode = TRANSMISSION_MODE_AUTO;
+ if ((rc & 0x60) == 0x20) {
+ /* Only modes 2 and 3 are supported */
+ switch ((rc >> 2) & 0x03) {
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_4K;
+ break;
+ case 2:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ }
+ }
+ c->guard_interval = GUARD_INTERVAL_AUTO;
+ if (!(rc & 0x10)) {
+ /* Guard interval 1/32 is not supported */
+ switch (rc & 0x3) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ }
+ }
+ return 0;
+
+noperlayer_error:
+
+ /* per-layer info is incomplete; discard all per-layer */
+ c->isdbt_layer_enabled = 0;
+
+ return rc;
+}
+
+static int mb86a20s_reset_counters(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int rc, val;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ /* Reset the counters, if the channel changed */
+ if (state->last_frequency != c->frequency) {
+ memset(&c->cnr, 0, sizeof(c->cnr));
+ memset(&c->pre_bit_error, 0, sizeof(c->pre_bit_error));
+ memset(&c->pre_bit_count, 0, sizeof(c->pre_bit_count));
+ memset(&c->post_bit_error, 0, sizeof(c->post_bit_error));
+ memset(&c->post_bit_count, 0, sizeof(c->post_bit_count));
+ memset(&c->block_error, 0, sizeof(c->block_error));
+ memset(&c->block_count, 0, sizeof(c->block_count));
+
+ state->last_frequency = c->frequency;
+ }
+
+ /* Clear status for most stats */
+
+ /* BER/PER counter reset */
+ rc = mb86a20s_writeregdata(state, mb86a20s_per_ber_reset);
+ if (rc < 0)
+ goto err;
+
+ /* CNR counter reset */
+ rc = mb86a20s_readreg(state, 0x45);
+ if (rc < 0)
+ goto err;
+ val = rc;
+ rc = mb86a20s_writereg(state, 0x45, val | 0x10);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_writereg(state, 0x45, val & 0x6f);
+ if (rc < 0)
+ goto err;
+
+ /* MER counter reset */
+ rc = mb86a20s_writereg(state, 0x50, 0x50);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ goto err;
+ val = rc;
+ rc = mb86a20s_writereg(state, 0x51, val | 0x01);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_writereg(state, 0x51, val & 0x06);
+ if (rc < 0)
+ goto err;
+
+ goto ok;
+err:
+ dev_err(&state->i2c->dev,
+ "%s: Can't reset FE statistics (error %d).\n",
+ __func__, rc);
+ok:
+ return rc;
+}
+
+static int mb86a20s_get_pre_ber(struct dvb_frontend *fe,
+ unsigned layer,
+ u32 *error, u32 *count)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ int rc, val;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ if (layer >= NUM_LAYERS)
+ return -EINVAL;
+
+ /* Check if the BER measures are already available */
+ rc = mb86a20s_readreg(state, 0x54);
+ if (rc < 0)
+ return rc;
+
+ /* Check if data is available for that layer */
+ if (!(rc & (1 << layer))) {
+ dev_dbg(&state->i2c->dev,
+ "%s: preBER for layer %c is not available yet.\n",
+ __func__, 'A' + layer);
+ return -EBUSY;
+ }
+
+ /* Read Bit Error Count */
+ rc = mb86a20s_readreg(state, 0x55 + layer * 3);
+ if (rc < 0)
+ return rc;
+ *error = rc << 16;
+ rc = mb86a20s_readreg(state, 0x56 + layer * 3);
+ if (rc < 0)
+ return rc;
+ *error |= rc << 8;
+ rc = mb86a20s_readreg(state, 0x57 + layer * 3);
+ if (rc < 0)
+ return rc;
+ *error |= rc;
+
+ dev_dbg(&state->i2c->dev,
+ "%s: bit error before Viterbi for layer %c: %d.\n",
+ __func__, 'A' + layer, *error);
+
+ /* Read Bit Count */
+ rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ *count = rc << 16;
+ rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ *count |= rc << 8;
+ rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ *count |= rc;
+
+ dev_dbg(&state->i2c->dev,
+ "%s: bit count before Viterbi for layer %c: %d.\n",
+ __func__, 'A' + layer, *count);
+
+
+ /*
+ * As we get TMCC data from the frontend, we can better estimate the
+ * BER bit counters, in order to do the BER measure during a longer
+ * time. Use those data, if available, to update the bit count
+ * measure.
+ */
+
+ if (state->estimated_rate[layer]
+ && state->estimated_rate[layer] != *count) {
+ dev_dbg(&state->i2c->dev,
+ "%s: updating layer %c preBER counter to %d.\n",
+ __func__, 'A' + layer, state->estimated_rate[layer]);
+
+ /* Turn off BER before Viterbi */
+ rc = mb86a20s_writereg(state, 0x52, 0x00);
+
+ /* Update counter for this layer */
+ rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51,
+ state->estimated_rate[layer] >> 16);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51,
+ state->estimated_rate[layer] >> 8);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51,
+ state->estimated_rate[layer]);
+ if (rc < 0)
+ return rc;
+
+ /* Turn on BER before Viterbi */
+ rc = mb86a20s_writereg(state, 0x52, 0x01);
+
+ /* Reset all preBER counters */
+ rc = mb86a20s_writereg(state, 0x53, 0x00);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x53, 0x07);
+ } else {
+ /* Reset counter to collect new data */
+ rc = mb86a20s_readreg(state, 0x53);
+ if (rc < 0)
+ return rc;
+ val = rc;
+ rc = mb86a20s_writereg(state, 0x53, val & ~(1 << layer));
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x53, val | (1 << layer));
+ }
+
+ return rc;
+}
+
+static int mb86a20s_get_post_ber(struct dvb_frontend *fe,
+ unsigned layer,
+ u32 *error, u32 *count)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ u32 counter, collect_rate;
+ int rc, val;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ if (layer >= NUM_LAYERS)
+ return -EINVAL;
+
+ /* Check if the BER measures are already available */
+ rc = mb86a20s_readreg(state, 0x60);
+ if (rc < 0)
+ return rc;
+
+ /* Check if data is available for that layer */
+ if (!(rc & (1 << layer))) {
+ dev_dbg(&state->i2c->dev,
+ "%s: post BER for layer %c is not available yet.\n",
+ __func__, 'A' + layer);
+ return -EBUSY;
+ }
+
+ /* Read Bit Error Count */
+ rc = mb86a20s_readreg(state, 0x64 + layer * 3);
+ if (rc < 0)
+ return rc;
+ *error = rc << 16;
+ rc = mb86a20s_readreg(state, 0x65 + layer * 3);
+ if (rc < 0)
+ return rc;
+ *error |= rc << 8;
+ rc = mb86a20s_readreg(state, 0x66 + layer * 3);
+ if (rc < 0)
+ return rc;
+ *error |= rc;
+
+ dev_dbg(&state->i2c->dev,
+ "%s: post bit error for layer %c: %d.\n",
+ __func__, 'A' + layer, *error);
+
+ /* Read Bit Count */
+ rc = mb86a20s_writereg(state, 0x50, 0xdc + layer * 2);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ counter = rc << 8;
+ rc = mb86a20s_writereg(state, 0x50, 0xdd + layer * 2);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ counter |= rc;
+ *count = counter * 204 * 8;
+
+ dev_dbg(&state->i2c->dev,
+ "%s: post bit count for layer %c: %d.\n",
+ __func__, 'A' + layer, *count);
+
+ /*
+ * As we get TMCC data from the frontend, we can better estimate the
+ * BER bit counters, in order to do the BER measure during a longer
+ * time. Use those data, if available, to update the bit count
+ * measure.
+ */
+
+ if (!state->estimated_rate[layer])
+ goto reset_measurement;
+
+ collect_rate = state->estimated_rate[layer] / 204 / 8;
+ if (collect_rate < 32)
+ collect_rate = 32;
+ if (collect_rate > 65535)
+ collect_rate = 65535;
+ if (collect_rate != counter) {
+ dev_dbg(&state->i2c->dev,
+ "%s: updating postBER counter on layer %c to %d.\n",
+ __func__, 'A' + layer, collect_rate);
+
+ /* Turn off BER after Viterbi */
+ rc = mb86a20s_writereg(state, 0x5e, 0x00);
+
+ /* Update counter for this layer */
+ rc = mb86a20s_writereg(state, 0x50, 0xdc + layer * 2);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51, collect_rate >> 8);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x50, 0xdd + layer * 2);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51, collect_rate & 0xff);
+ if (rc < 0)
+ return rc;
+
+ /* Turn on BER after Viterbi */
+ rc = mb86a20s_writereg(state, 0x5e, 0x07);
+
+ /* Reset all preBER counters */
+ rc = mb86a20s_writereg(state, 0x5f, 0x00);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x5f, 0x07);
+
+ return rc;
+ }
+
+reset_measurement:
+ /* Reset counter to collect new data */
+ rc = mb86a20s_readreg(state, 0x5f);
+ if (rc < 0)
+ return rc;
+ val = rc;
+ rc = mb86a20s_writereg(state, 0x5f, val & ~(1 << layer));
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x5f, val | (1 << layer));
+
+ return rc;
+}
+
+static int mb86a20s_get_blk_error(struct dvb_frontend *fe,
+ unsigned layer,
+ u32 *error, u32 *count)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ int rc, val;
+ u32 collect_rate;
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ if (layer >= NUM_LAYERS)
+ return -EINVAL;
+
+ /* Check if the PER measures are already available */
+ rc = mb86a20s_writereg(state, 0x50, 0xb8);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+
+ /* Check if data is available for that layer */
+
+ if (!(rc & (1 << layer))) {
+ dev_dbg(&state->i2c->dev,
+ "%s: block counts for layer %c aren't available yet.\n",
+ __func__, 'A' + layer);
+ return -EBUSY;
+ }
+
+ /* Read Packet error Count */
+ rc = mb86a20s_writereg(state, 0x50, 0xb9 + layer * 2);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ *error = rc << 8;
+ rc = mb86a20s_writereg(state, 0x50, 0xba + layer * 2);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ *error |= rc;
+ dev_dbg(&state->i2c->dev, "%s: block error for layer %c: %d.\n",
+ __func__, 'A' + layer, *error);
+
+ /* Read Bit Count */
+ rc = mb86a20s_writereg(state, 0x50, 0xb2 + layer * 2);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ *count = rc << 8;
+ rc = mb86a20s_writereg(state, 0x50, 0xb3 + layer * 2);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ *count |= rc;
+
+ dev_dbg(&state->i2c->dev,
+ "%s: block count for layer %c: %d.\n",
+ __func__, 'A' + layer, *count);
+
+ /*
+ * As we get TMCC data from the frontend, we can better estimate the
+ * BER bit counters, in order to do the BER measure during a longer
+ * time. Use those data, if available, to update the bit count
+ * measure.
+ */
+
+ if (!state->estimated_rate[layer])
+ goto reset_measurement;
+
+ collect_rate = state->estimated_rate[layer] / 204 / 8;
+ if (collect_rate < 32)
+ collect_rate = 32;
+ if (collect_rate > 65535)
+ collect_rate = 65535;
+
+ if (collect_rate != *count) {
+ dev_dbg(&state->i2c->dev,
+ "%s: updating PER counter on layer %c to %d.\n",
+ __func__, 'A' + layer, collect_rate);
+
+ /* Stop PER measurement */
+ rc = mb86a20s_writereg(state, 0x50, 0xb0);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51, 0x00);
+ if (rc < 0)
+ return rc;
+
+ /* Update this layer's counter */
+ rc = mb86a20s_writereg(state, 0x50, 0xb2 + layer * 2);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51, collect_rate >> 8);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x50, 0xb3 + layer * 2);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51, collect_rate & 0xff);
+ if (rc < 0)
+ return rc;
+
+ /* start PER measurement */
+ rc = mb86a20s_writereg(state, 0x50, 0xb0);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51, 0x07);
+ if (rc < 0)
+ return rc;
+
+ /* Reset all counters to collect new data */
+ rc = mb86a20s_writereg(state, 0x50, 0xb1);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51, 0x07);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51, 0x00);
+
+ return rc;
+ }
+
+reset_measurement:
+ /* Reset counter to collect new data */
+ rc = mb86a20s_writereg(state, 0x50, 0xb1);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ val = rc;
+ rc = mb86a20s_writereg(state, 0x51, val | (1 << layer));
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51, val & ~(1 << layer));
+
+ return rc;
+}
+
+struct linear_segments {
+ unsigned x, y;
+};
+
+/*
+ * All tables below return a dB/1000 measurement
+ */
+
+static const struct linear_segments cnr_to_db_table[] = {
+ { 19648, 0},
+ { 18187, 1000},
+ { 16534, 2000},
+ { 14823, 3000},
+ { 13161, 4000},
+ { 11622, 5000},
+ { 10279, 6000},
+ { 9089, 7000},
+ { 8042, 8000},
+ { 7137, 9000},
+ { 6342, 10000},
+ { 5641, 11000},
+ { 5030, 12000},
+ { 4474, 13000},
+ { 3988, 14000},
+ { 3556, 15000},
+ { 3180, 16000},
+ { 2841, 17000},
+ { 2541, 18000},
+ { 2276, 19000},
+ { 2038, 20000},
+ { 1800, 21000},
+ { 1625, 22000},
+ { 1462, 23000},
+ { 1324, 24000},
+ { 1175, 25000},
+ { 1063, 26000},
+ { 980, 27000},
+ { 907, 28000},
+ { 840, 29000},
+ { 788, 30000},
+};
+
+static const struct linear_segments cnr_64qam_table[] = {
+ { 3922688, 0},
+ { 3920384, 1000},
+ { 3902720, 2000},
+ { 3894784, 3000},
+ { 3882496, 4000},
+ { 3872768, 5000},
+ { 3858944, 6000},
+ { 3851520, 7000},
+ { 3838976, 8000},
+ { 3829248, 9000},
+ { 3818240, 10000},
+ { 3806976, 11000},
+ { 3791872, 12000},
+ { 3767040, 13000},
+ { 3720960, 14000},
+ { 3637504, 15000},
+ { 3498496, 16000},
+ { 3296000, 17000},
+ { 3031040, 18000},
+ { 2715392, 19000},
+ { 2362624, 20000},
+ { 1963264, 21000},
+ { 1649664, 22000},
+ { 1366784, 23000},
+ { 1120768, 24000},
+ { 890880, 25000},
+ { 723456, 26000},
+ { 612096, 27000},
+ { 518912, 28000},
+ { 448256, 29000},
+ { 388864, 30000},
+};
+
+static const struct linear_segments cnr_16qam_table[] = {
+ { 5314816, 0},
+ { 5219072, 1000},
+ { 5118720, 2000},
+ { 4998912, 3000},
+ { 4875520, 4000},
+ { 4736000, 5000},
+ { 4604160, 6000},
+ { 4458752, 7000},
+ { 4300288, 8000},
+ { 4092928, 9000},
+ { 3836160, 10000},
+ { 3521024, 11000},
+ { 3155968, 12000},
+ { 2756864, 13000},
+ { 2347008, 14000},
+ { 1955072, 15000},
+ { 1593600, 16000},
+ { 1297920, 17000},
+ { 1043968, 18000},
+ { 839680, 19000},
+ { 672256, 20000},
+ { 523008, 21000},
+ { 424704, 22000},
+ { 345088, 23000},
+ { 280064, 24000},
+ { 221440, 25000},
+ { 179712, 26000},
+ { 151040, 27000},
+ { 128512, 28000},
+ { 110080, 29000},
+ { 95744, 30000},
+};
+
+static const struct linear_segments cnr_qpsk_table[] = {
+ { 2834176, 0},
+ { 2683648, 1000},
+ { 2536960, 2000},
+ { 2391808, 3000},
+ { 2133248, 4000},
+ { 1906176, 5000},
+ { 1666560, 6000},
+ { 1422080, 7000},
+ { 1189632, 8000},
+ { 976384, 9000},
+ { 790272, 10000},
+ { 633344, 11000},
+ { 505600, 12000},
+ { 402944, 13000},
+ { 320768, 14000},
+ { 255488, 15000},
+ { 204032, 16000},
+ { 163072, 17000},
+ { 130304, 18000},
+ { 105216, 19000},
+ { 83456, 20000},
+ { 65024, 21000},
+ { 52480, 22000},
+ { 42752, 23000},
+ { 34560, 24000},
+ { 27136, 25000},
+ { 22016, 26000},
+ { 18432, 27000},
+ { 15616, 28000},
+ { 13312, 29000},
+ { 11520, 30000},
+};
+
+static u32 interpolate_value(u32 value, const struct linear_segments *segments,
+ unsigned len)
+{
+ u64 tmp64;
+ u32 dx, dy;
+ int i, ret;
+
+ if (value >= segments[0].x)
+ return segments[0].y;
+ if (value < segments[len-1].x)
+ return segments[len-1].y;
+
+ for (i = 1; i < len - 1; i++) {
+ /* If value is identical, no need to interpolate */
+ if (value == segments[i].x)
+ return segments[i].y;
+ if (value > segments[i].x)
+ break;
+ }
+
+ /* Linear interpolation between the two (x,y) points */
+ dy = segments[i].y - segments[i - 1].y;
+ dx = segments[i - 1].x - segments[i].x;
+ tmp64 = value - segments[i].x;
+ tmp64 *= dy;
+ do_div(tmp64, dx);
+ ret = segments[i].y - tmp64;
+
+ return ret;
+}
+
+static int mb86a20s_get_main_CNR(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 cnr_linear, cnr;
+ int rc, val;
+
+ /* Check if CNR is available */
+ rc = mb86a20s_readreg(state, 0x45);
+ if (rc < 0)
+ return rc;
+
+ if (!(rc & 0x40)) {
+ dev_dbg(&state->i2c->dev, "%s: CNR is not available yet.\n",
+ __func__);
+ return -EBUSY;
+ }
+ val = rc;
+
+ rc = mb86a20s_readreg(state, 0x46);
+ if (rc < 0)
+ return rc;
+ cnr_linear = rc << 8;
+
+ rc = mb86a20s_readreg(state, 0x46);
+ if (rc < 0)
+ return rc;
+ cnr_linear |= rc;
+
+ cnr = interpolate_value(cnr_linear,
+ cnr_to_db_table, ARRAY_SIZE(cnr_to_db_table));
+
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = cnr;
+
+ dev_dbg(&state->i2c->dev, "%s: CNR is %d.%03d dB (%d)\n",
+ __func__, cnr / 1000, cnr % 1000, cnr_linear);
+
+ /* CNR counter reset */
+ rc = mb86a20s_writereg(state, 0x45, val | 0x10);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x45, val & 0x6f);
+
+ return rc;
+}
+
+static int mb86a20s_get_blk_error_layer_CNR(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 mer, cnr;
+ int rc, val, layer;
+ const struct linear_segments *segs;
+ unsigned segs_len;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ /* Check if the measures are already available */
+ rc = mb86a20s_writereg(state, 0x50, 0x5b);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+
+ /* Check if data is available */
+ if (!(rc & 0x01)) {
+ dev_dbg(&state->i2c->dev,
+ "%s: MER measures aren't available yet.\n", __func__);
+ return -EBUSY;
+ }
+
+ /* Read all layers */
+ for (layer = 0; layer < NUM_LAYERS; layer++) {
+ if (!(c->isdbt_layer_enabled & (1 << layer))) {
+ c->cnr.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE;
+ continue;
+ }
+
+ rc = mb86a20s_writereg(state, 0x50, 0x52 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ mer = rc << 16;
+ rc = mb86a20s_writereg(state, 0x50, 0x53 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ mer |= rc << 8;
+ rc = mb86a20s_writereg(state, 0x50, 0x54 + layer * 3);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ mer |= rc;
+
+ switch (c->layer[layer].modulation) {
+ case DQPSK:
+ case QPSK:
+ segs = cnr_qpsk_table;
+ segs_len = ARRAY_SIZE(cnr_qpsk_table);
+ break;
+ case QAM_16:
+ segs = cnr_16qam_table;
+ segs_len = ARRAY_SIZE(cnr_16qam_table);
+ break;
+ default:
+ case QAM_64:
+ segs = cnr_64qam_table;
+ segs_len = ARRAY_SIZE(cnr_64qam_table);
+ break;
+ }
+ cnr = interpolate_value(mer, segs, segs_len);
+
+ c->cnr.stat[1 + layer].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[1 + layer].svalue = cnr;
+
+ dev_dbg(&state->i2c->dev,
+ "%s: CNR for layer %c is %d.%03d dB (MER = %d).\n",
+ __func__, 'A' + layer, cnr / 1000, cnr % 1000, mer);
+
+ }
+
+ /* Start a new MER measurement */
+ /* MER counter reset */
+ rc = mb86a20s_writereg(state, 0x50, 0x50);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_readreg(state, 0x51);
+ if (rc < 0)
+ return rc;
+ val = rc;
+
+ rc = mb86a20s_writereg(state, 0x51, val | 0x01);
+ if (rc < 0)
+ return rc;
+ rc = mb86a20s_writereg(state, 0x51, val & 0x06);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static void mb86a20s_stats_not_ready(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int layer;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ /* Fill the length of each status counter */
+
+ /* Only global stats */
+ c->strength.len = 1;
+
+ /* Per-layer stats - 3 layers + global */
+ c->cnr.len = NUM_LAYERS + 1;
+ c->pre_bit_error.len = NUM_LAYERS + 1;
+ c->pre_bit_count.len = NUM_LAYERS + 1;
+ c->post_bit_error.len = NUM_LAYERS + 1;
+ c->post_bit_count.len = NUM_LAYERS + 1;
+ c->block_error.len = NUM_LAYERS + 1;
+ c->block_count.len = NUM_LAYERS + 1;
+
+ /* Signal is always available */
+ c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ c->strength.stat[0].uvalue = 0;
+
+ /* Put all of them at FE_SCALE_NOT_AVAILABLE */
+ for (layer = 0; layer < NUM_LAYERS + 1; layer++) {
+ c->cnr.stat[layer].scale = FE_SCALE_NOT_AVAILABLE;
+ c->pre_bit_error.stat[layer].scale = FE_SCALE_NOT_AVAILABLE;
+ c->pre_bit_count.stat[layer].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_error.stat[layer].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[layer].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[layer].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[layer].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+}
+
+static int mb86a20s_get_stats(struct dvb_frontend *fe, int status_nr)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int rc = 0, layer;
+ u32 bit_error = 0, bit_count = 0;
+ u32 t_pre_bit_error = 0, t_pre_bit_count = 0;
+ u32 t_post_bit_error = 0, t_post_bit_count = 0;
+ u32 block_error = 0, block_count = 0;
+ u32 t_block_error = 0, t_block_count = 0;
+ int pre_ber_layers = 0, post_ber_layers = 0;
+ int per_layers = 0;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ mb86a20s_get_main_CNR(fe);
+
+ /* Get per-layer stats */
+ mb86a20s_get_blk_error_layer_CNR(fe);
+
+ /*
+ * At state 7, only CNR is available
+ * For BER measures, state=9 is required
+ * FIXME: we may get MER measures with state=8
+ */
+ if (status_nr < 9)
+ return 0;
+
+ for (layer = 0; layer < NUM_LAYERS; layer++) {
+ if (c->isdbt_layer_enabled & (1 << layer)) {
+ /* Handle BER before vterbi */
+ rc = mb86a20s_get_pre_ber(fe, layer,
+ &bit_error, &bit_count);
+ if (rc >= 0) {
+ c->pre_bit_error.stat[1 + layer].scale = FE_SCALE_COUNTER;
+ c->pre_bit_error.stat[1 + layer].uvalue += bit_error;
+ c->pre_bit_count.stat[1 + layer].scale = FE_SCALE_COUNTER;
+ c->pre_bit_count.stat[1 + layer].uvalue += bit_count;
+ } else if (rc != -EBUSY) {
+ /*
+ * If an I/O error happened,
+ * measures are now unavailable
+ */
+ c->pre_bit_error.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE;
+ c->pre_bit_count.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE;
+ dev_err(&state->i2c->dev,
+ "%s: Can't get BER for layer %c (error %d).\n",
+ __func__, 'A' + layer, rc);
+ }
+ if (c->block_error.stat[1 + layer].scale != FE_SCALE_NOT_AVAILABLE)
+ pre_ber_layers++;
+
+ /* Handle BER post vterbi */
+ rc = mb86a20s_get_post_ber(fe, layer,
+ &bit_error, &bit_count);
+ if (rc >= 0) {
+ c->post_bit_error.stat[1 + layer].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[1 + layer].uvalue += bit_error;
+ c->post_bit_count.stat[1 + layer].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[1 + layer].uvalue += bit_count;
+ } else if (rc != -EBUSY) {
+ /*
+ * If an I/O error happened,
+ * measures are now unavailable
+ */
+ c->post_bit_error.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE;
+ dev_err(&state->i2c->dev,
+ "%s: Can't get BER for layer %c (error %d).\n",
+ __func__, 'A' + layer, rc);
+ }
+ if (c->block_error.stat[1 + layer].scale != FE_SCALE_NOT_AVAILABLE)
+ post_ber_layers++;
+
+ /* Handle Block errors for PER/UCB reports */
+ rc = mb86a20s_get_blk_error(fe, layer,
+ &block_error,
+ &block_count);
+ if (rc >= 0) {
+ c->block_error.stat[1 + layer].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[1 + layer].uvalue += block_error;
+ c->block_count.stat[1 + layer].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[1 + layer].uvalue += block_count;
+ } else if (rc != -EBUSY) {
+ /*
+ * If an I/O error happened,
+ * measures are now unavailable
+ */
+ c->block_error.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[1 + layer].scale = FE_SCALE_NOT_AVAILABLE;
+ dev_err(&state->i2c->dev,
+ "%s: Can't get PER for layer %c (error %d).\n",
+ __func__, 'A' + layer, rc);
+
+ }
+ if (c->block_error.stat[1 + layer].scale != FE_SCALE_NOT_AVAILABLE)
+ per_layers++;
+
+ /* Update total preBER */
+ t_pre_bit_error += c->pre_bit_error.stat[1 + layer].uvalue;
+ t_pre_bit_count += c->pre_bit_count.stat[1 + layer].uvalue;
+
+ /* Update total postBER */
+ t_post_bit_error += c->post_bit_error.stat[1 + layer].uvalue;
+ t_post_bit_count += c->post_bit_count.stat[1 + layer].uvalue;
+
+ /* Update total PER */
+ t_block_error += c->block_error.stat[1 + layer].uvalue;
+ t_block_count += c->block_count.stat[1 + layer].uvalue;
+ }
+ }
+
+ /*
+ * Start showing global count if at least one error count is
+ * available.
+ */
+ if (pre_ber_layers) {
+ /*
+ * At least one per-layer BER measure was read. We can now
+ * calculate the total BER
+ *
+ * Total Bit Error/Count is calculated as the sum of the
+ * bit errors on all active layers.
+ */
+ c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->pre_bit_error.stat[0].uvalue = t_pre_bit_error;
+ c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->pre_bit_count.stat[0].uvalue = t_pre_bit_count;
+ } else {
+ c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ }
+
+ /*
+ * Start showing global count if at least one error count is
+ * available.
+ */
+ if (post_ber_layers) {
+ /*
+ * At least one per-layer BER measure was read. We can now
+ * calculate the total BER
+ *
+ * Total Bit Error/Count is calculated as the sum of the
+ * bit errors on all active layers.
+ */
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = t_post_bit_error;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue = t_post_bit_count;
+ } else {
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ }
+
+ if (per_layers) {
+ /*
+ * At least one per-layer UCB measure was read. We can now
+ * calculate the total UCB
+ *
+ * Total block Error/Count is calculated as the sum of the
+ * block errors on all active layers.
+ */
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue = t_block_error;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue = t_block_count;
+ } else {
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ }
+
+ return rc;
+}
+
+/*
+ * The functions below are called via DVB callbacks, so they need to
+ * properly use the I2C gate control
+ */
+
+static int mb86a20s_initfe(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ u64 pll;
+ u32 fclk;
+ int rc;
+ u8 regD5 = 1, reg71, reg09 = 0x3a;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ /* Initialize the frontend */
+ rc = mb86a20s_writeregdata(state, mb86a20s_init1);
+ if (rc < 0)
+ goto err;
+
+ if (!state->inversion)
+ reg09 |= 0x04;
+ rc = mb86a20s_writereg(state, 0x09, reg09);
+ if (rc < 0)
+ goto err;
+ if (!state->bw)
+ reg71 = 1;
+ else
+ reg71 = 0;
+ rc = mb86a20s_writereg(state, 0x39, reg71);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_writereg(state, 0x71, state->bw);
+ if (rc < 0)
+ goto err;
+ if (state->subchannel) {
+ rc = mb86a20s_writereg(state, 0x44, state->subchannel);
+ if (rc < 0)
+ goto err;
+ }
+
+ fclk = state->config->fclk;
+ if (!fclk)
+ fclk = 32571428;
+
+ /* Adjust IF frequency to match tuner */
+ if (fe->ops.tuner_ops.get_if_frequency)
+ fe->ops.tuner_ops.get_if_frequency(fe, &state->if_freq);
+
+ if (!state->if_freq)
+ state->if_freq = 3300000;
+
+ pll = (((u64)1) << 34) * state->if_freq;
+ do_div(pll, 63 * fclk);
+ pll = (1 << 25) - pll;
+ rc = mb86a20s_writereg(state, 0x28, 0x2a);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_writereg(state, 0x29, (pll >> 16) & 0xff);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_writereg(state, 0x2a, (pll >> 8) & 0xff);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_writereg(state, 0x2b, pll & 0xff);
+ if (rc < 0)
+ goto err;
+ dev_dbg(&state->i2c->dev, "%s: fclk=%d, IF=%d, clock reg=0x%06llx\n",
+ __func__, fclk, state->if_freq, (long long)pll);
+
+ /* pll = freq[Hz] * 2^24/10^6 / 16.285714286 */
+ pll = state->if_freq * 1677721600L;
+ do_div(pll, 1628571429L);
+ rc = mb86a20s_writereg(state, 0x28, 0x20);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_writereg(state, 0x29, (pll >> 16) & 0xff);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_writereg(state, 0x2a, (pll >> 8) & 0xff);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_writereg(state, 0x2b, pll & 0xff);
+ if (rc < 0)
+ goto err;
+ dev_dbg(&state->i2c->dev, "%s: IF=%d, IF reg=0x%06llx\n",
+ __func__, state->if_freq, (long long)pll);
+
+ if (!state->config->is_serial)
+ regD5 &= ~1;
+
+ rc = mb86a20s_writereg(state, 0x50, 0xd5);
+ if (rc < 0)
+ goto err;
+ rc = mb86a20s_writereg(state, 0x51, regD5);
+ if (rc < 0)
+ goto err;
+
+ rc = mb86a20s_writeregdata(state, mb86a20s_init2);
+ if (rc < 0)
+ goto err;
+
+
+err:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ if (rc < 0) {
+ state->need_init = true;
+ dev_info(&state->i2c->dev,
+ "mb86a20s: Init failed. Will try again later\n");
+ } else {
+ state->need_init = false;
+ dev_dbg(&state->i2c->dev, "Initialization succeeded.\n");
+ }
+ return rc;
+}
+
+static int mb86a20s_set_frontend(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int rc, if_freq;
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ if (!c->isdbt_layer_enabled)
+ c->isdbt_layer_enabled = 7;
+
+ if (c->isdbt_layer_enabled == 1)
+ state->bw = MB86A20S_1SEG;
+ else if (c->isdbt_partial_reception)
+ state->bw = MB86A20S_13SEG_PARTIAL;
+ else
+ state->bw = MB86A20S_13SEG;
+
+ if (c->inversion == INVERSION_ON)
+ state->inversion = true;
+ else
+ state->inversion = false;
+
+ if (!c->isdbt_sb_mode) {
+ state->subchannel = 0;
+ } else {
+ if (c->isdbt_sb_subchannel >= ARRAY_SIZE(mb86a20s_subchannel))
+ c->isdbt_sb_subchannel = 0;
+
+ state->subchannel = mb86a20s_subchannel[c->isdbt_sb_subchannel];
+ }
+
+ /*
+ * Gate should already be opened, but it doesn't hurt to
+ * double-check
+ */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ fe->ops.tuner_ops.set_params(fe);
+
+ if (fe->ops.tuner_ops.get_if_frequency)
+ fe->ops.tuner_ops.get_if_frequency(fe, &if_freq);
+
+ /*
+ * Make it more reliable: if, for some reason, the initial
+ * device initialization doesn't happen, initialize it when
+ * a SBTVD parameters are adjusted.
+ *
+ * Unfortunately, due to a hard to track bug at tda829x/tda18271,
+ * the agc callback logic is not called during DVB attach time,
+ * causing mb86a20s to not be initialized with Kworld SBTVD.
+ * So, this hack is needed, in order to make Kworld SBTVD to work.
+ *
+ * It is also needed to change the IF after the initial init.
+ *
+ * HACK: Always init the frontend when set_frontend is called:
+ * it was noticed that, on some devices, it fails to lock on a
+ * different channel. So, it is better to reset everything, even
+ * wasting some time, than to loose channel lock.
+ */
+ mb86a20s_initfe(fe);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception);
+ mb86a20s_reset_counters(fe);
+ mb86a20s_stats_not_ready(fe);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ return rc;
+}
+
+static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ int rc, status_nr;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ /* Get lock */
+ status_nr = mb86a20s_read_status(fe, status);
+ if (status_nr < 7) {
+ mb86a20s_stats_not_ready(fe);
+ mb86a20s_reset_frontend_cache(fe);
+ }
+ if (status_nr < 0) {
+ dev_err(&state->i2c->dev,
+ "%s: Can't read frontend lock status\n", __func__);
+ rc = status_nr;
+ goto error;
+ }
+
+ /* Get signal strength */
+ rc = mb86a20s_read_signal_strength(fe);
+ if (rc < 0) {
+ dev_err(&state->i2c->dev,
+ "%s: Can't reset VBER registers.\n", __func__);
+ mb86a20s_stats_not_ready(fe);
+ mb86a20s_reset_frontend_cache(fe);
+
+ rc = 0; /* Status is OK */
+ goto error;
+ }
+
+ if (status_nr >= 7) {
+ /* Get TMCC info*/
+ rc = mb86a20s_get_frontend(fe);
+ if (rc < 0) {
+ dev_err(&state->i2c->dev,
+ "%s: Can't get FE TMCC data.\n", __func__);
+ rc = 0; /* Status is OK */
+ goto error;
+ }
+
+ /* Get statistics */
+ rc = mb86a20s_get_stats(fe, status_nr);
+ if (rc < 0 && rc != -EBUSY) {
+ dev_err(&state->i2c->dev,
+ "%s: Can't get FE statistics.\n", __func__);
+ rc = 0;
+ goto error;
+ }
+ rc = 0; /* Don't return EBUSY to userspace */
+ }
+ goto ok;
+
+error:
+ mb86a20s_stats_not_ready(fe);
+
+ok:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ return rc;
+}
+
+static int mb86a20s_read_signal_strength_from_cache(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+
+ *strength = c->strength.stat[0].uvalue;
+
+ return 0;
+}
+
+static int mb86a20s_tune(struct dvb_frontend *fe,
+ bool re_tune,
+ unsigned int mode_flags,
+ unsigned int *delay,
+ enum fe_status *status)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+ int rc = 0;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ if (re_tune)
+ rc = mb86a20s_set_frontend(fe);
+
+ if (!(mode_flags & FE_TUNE_MODE_ONESHOT))
+ mb86a20s_read_status_and_stats(fe, status);
+
+ return rc;
+}
+
+static void mb86a20s_release(struct dvb_frontend *fe)
+{
+ struct mb86a20s_state *state = fe->demodulator_priv;
+
+ dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
+
+ kfree(state);
+}
+
+static enum dvbfe_algo mb86a20s_get_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
+static const struct dvb_frontend_ops mb86a20s_ops;
+
+struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct mb86a20s_state *state;
+ u8 rev;
+
+ dev_dbg(&i2c->dev, "%s called.\n", __func__);
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &mb86a20s_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ /* Check if it is a mb86a20s frontend */
+ rev = mb86a20s_readreg(state, 0);
+ if (rev != 0x13) {
+ kfree(state);
+ dev_dbg(&i2c->dev,
+ "Frontend revision %d is unknown - aborting.\n",
+ rev);
+ return NULL;
+ }
+
+ dev_info(&i2c->dev, "Detected a Fujitsu mb86a20s frontend\n");
+ return &state->frontend;
+}
+EXPORT_SYMBOL_GPL(mb86a20s_attach);
+
+static const struct dvb_frontend_ops mb86a20s_ops = {
+ .delsys = { SYS_ISDBT },
+ /* Use dib8000 values per default */
+ .info = {
+ .name = "Fujitsu mb86A20s",
+ .caps = FE_CAN_RECOVER |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_QAM_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
+ /* Actually, those values depend on the used tuner */
+ .frequency_min_hz = 45 * MHz,
+ .frequency_max_hz = 864 * MHz,
+ .frequency_stepsize_hz = 62500,
+ },
+
+ .release = mb86a20s_release,
+
+ .init = mb86a20s_initfe,
+ .set_frontend = mb86a20s_set_frontend,
+ .read_status = mb86a20s_read_status_and_stats,
+ .read_signal_strength = mb86a20s_read_signal_strength_from_cache,
+ .tune = mb86a20s_tune,
+ .get_frontend_algo = mb86a20s_get_frontend_algo,
+};
+
+MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/mb86a20s.h b/drivers/media/dvb-frontends/mb86a20s.h
new file mode 100644
index 000000000000..d20d22bf7580
--- /dev/null
+++ b/drivers/media/dvb-frontends/mb86a20s.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Fujitsu mb86a20s driver
+ *
+ * Copyright (C) 2010 Mauro Carvalho Chehab
+ */
+
+#ifndef MB86A20S_H
+#define MB86A20S_H
+
+#include <linux/dvb/frontend.h>
+
+/**
+ * struct mb86a20s_config - Define the per-device attributes of the frontend
+ *
+ * @fclk: Clock frequency. If zero, assumes the default
+ * (32.57142 Mhz)
+ * @demod_address: the demodulator's i2c address
+ * @is_serial: if true, TS is serial. Otherwise, TS is parallel
+ */
+struct mb86a20s_config {
+ u32 fclk;
+ u8 demod_address;
+ bool is_serial;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_MB86A20S)
+/**
+ * mb86a20s_attach - Attach a mb86a20s demod
+ *
+ * @config: pointer to &struct mb86a20s_config with demod configuration.
+ * @i2c: i2c adapter to use.
+ *
+ * return: FE pointer on success, NULL on failure.
+ */
+extern struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config,
+ struct i2c_adapter *i2c);
+
+#else
+static inline struct dvb_frontend *mb86a20s_attach(
+ const struct mb86a20s_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* MB86A20S */
diff --git a/drivers/media/dvb-frontends/mn88443x.c b/drivers/media/dvb-frontends/mn88443x.c
new file mode 100644
index 000000000000..818c4e67364c
--- /dev/null
+++ b/drivers/media/dvb-frontends/mn88443x.c
@@ -0,0 +1,811 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Socionext MN88443x series demodulator driver for ISDB-S/ISDB-T.
+//
+// Copyright (c) 2018 Socionext Inc.
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/int_log.h>
+
+#include "mn88443x.h"
+
+/* ISDB-S registers */
+#define ATSIDU_S 0x2f
+#define ATSIDL_S 0x30
+#define TSSET_S 0x31
+#define AGCREAD_S 0x5a
+#define CPMON1_S 0x5e
+#define CPMON1_S_FSYNC BIT(5)
+#define CPMON1_S_ERRMON BIT(4)
+#define CPMON1_S_SIGOFF BIT(3)
+#define CPMON1_S_W2LOCK BIT(2)
+#define CPMON1_S_W1LOCK BIT(1)
+#define CPMON1_S_DW1LOCK BIT(0)
+#define TRMON_S 0x60
+#define BERCNFLG_S 0x68
+#define BERCNFLG_S_BERVRDY BIT(5)
+#define BERCNFLG_S_BERVCHK BIT(4)
+#define BERCNFLG_S_BERDRDY BIT(3)
+#define BERCNFLG_S_BERDCHK BIT(2)
+#define CNRDXU_S 0x69
+#define CNRDXL_S 0x6a
+#define CNRDYU_S 0x6b
+#define CNRDYL_S 0x6c
+#define BERVRDU_S 0x71
+#define BERVRDL_S 0x72
+#define DOSET1_S 0x73
+
+/* Primary ISDB-T */
+#define PLLASET1 0x00
+#define PLLASET2 0x01
+#define PLLBSET1 0x02
+#define PLLBSET2 0x03
+#define PLLSET 0x04
+#define OUTCSET 0x08
+#define OUTCSET_CHDRV_8MA 0xff
+#define OUTCSET_CHDRV_4MA 0x00
+#define PLDWSET 0x09
+#define PLDWSET_NORMAL 0x00
+#define PLDWSET_PULLDOWN 0xff
+#define HIZSET1 0x0a
+#define HIZSET2 0x0b
+
+/* Secondary ISDB-T (for MN884434 only) */
+#define RCVSET 0x00
+#define TSSET1_M 0x01
+#define TSSET2_M 0x02
+#define TSSET3_M 0x03
+#define INTACSET 0x08
+#define HIZSET3 0x0b
+
+/* ISDB-T registers */
+#define TSSET1 0x05
+#define TSSET1_TSASEL_MASK GENMASK(4, 3)
+#define TSSET1_TSASEL_ISDBT (0x0 << 3)
+#define TSSET1_TSASEL_ISDBS (0x1 << 3)
+#define TSSET1_TSASEL_NONE (0x2 << 3)
+#define TSSET1_TSBSEL_MASK GENMASK(2, 1)
+#define TSSET1_TSBSEL_ISDBS (0x0 << 1)
+#define TSSET1_TSBSEL_ISDBT (0x1 << 1)
+#define TSSET1_TSBSEL_NONE (0x2 << 1)
+#define TSSET2 0x06
+#define TSSET3 0x07
+#define TSSET3_INTASEL_MASK GENMASK(7, 6)
+#define TSSET3_INTASEL_T (0x0 << 6)
+#define TSSET3_INTASEL_S (0x1 << 6)
+#define TSSET3_INTASEL_NONE (0x2 << 6)
+#define TSSET3_INTBSEL_MASK GENMASK(5, 4)
+#define TSSET3_INTBSEL_S (0x0 << 4)
+#define TSSET3_INTBSEL_T (0x1 << 4)
+#define TSSET3_INTBSEL_NONE (0x2 << 4)
+#define OUTSET2 0x0d
+#define PWDSET 0x0f
+#define PWDSET_OFDMPD_MASK GENMASK(3, 2)
+#define PWDSET_OFDMPD_DOWN BIT(3)
+#define PWDSET_PSKPD_MASK GENMASK(1, 0)
+#define PWDSET_PSKPD_DOWN BIT(1)
+#define CLKSET1_T 0x11
+#define MDSET_T 0x13
+#define MDSET_T_MDAUTO_MASK GENMASK(7, 4)
+#define MDSET_T_MDAUTO_AUTO (0xf << 4)
+#define MDSET_T_MDAUTO_MANUAL (0x0 << 4)
+#define MDSET_T_FFTS_MASK GENMASK(3, 2)
+#define MDSET_T_FFTS_MODE1 (0x0 << 2)
+#define MDSET_T_FFTS_MODE2 (0x1 << 2)
+#define MDSET_T_FFTS_MODE3 (0x2 << 2)
+#define MDSET_T_GI_MASK GENMASK(1, 0)
+#define MDSET_T_GI_1_32 (0x0 << 0)
+#define MDSET_T_GI_1_16 (0x1 << 0)
+#define MDSET_T_GI_1_8 (0x2 << 0)
+#define MDSET_T_GI_1_4 (0x3 << 0)
+#define MDASET_T 0x14
+#define ADCSET1_T 0x20
+#define ADCSET1_T_REFSEL_MASK GENMASK(1, 0)
+#define ADCSET1_T_REFSEL_2V (0x3 << 0)
+#define ADCSET1_T_REFSEL_1_5V (0x2 << 0)
+#define ADCSET1_T_REFSEL_1V (0x1 << 0)
+#define NCOFREQU_T 0x24
+#define NCOFREQM_T 0x25
+#define NCOFREQL_T 0x26
+#define FADU_T 0x27
+#define FADM_T 0x28
+#define FADL_T 0x29
+#define AGCSET2_T 0x2c
+#define AGCSET2_T_IFPOLINV_INC BIT(0)
+#define AGCSET2_T_RFPOLINV_INC BIT(1)
+#define AGCV3_T 0x3e
+#define MDRD_T 0xa2
+#define MDRD_T_SEGID_MASK GENMASK(5, 4)
+#define MDRD_T_SEGID_13 (0x0 << 4)
+#define MDRD_T_SEGID_1 (0x1 << 4)
+#define MDRD_T_SEGID_3 (0x2 << 4)
+#define MDRD_T_FFTS_MASK GENMASK(3, 2)
+#define MDRD_T_FFTS_MODE1 (0x0 << 2)
+#define MDRD_T_FFTS_MODE2 (0x1 << 2)
+#define MDRD_T_FFTS_MODE3 (0x2 << 2)
+#define MDRD_T_GI_MASK GENMASK(1, 0)
+#define MDRD_T_GI_1_32 (0x0 << 0)
+#define MDRD_T_GI_1_16 (0x1 << 0)
+#define MDRD_T_GI_1_8 (0x2 << 0)
+#define MDRD_T_GI_1_4 (0x3 << 0)
+#define SSEQRD_T 0xa3
+#define SSEQRD_T_SSEQSTRD_MASK GENMASK(3, 0)
+#define SSEQRD_T_SSEQSTRD_RESET (0x0 << 0)
+#define SSEQRD_T_SSEQSTRD_TUNING (0x1 << 0)
+#define SSEQRD_T_SSEQSTRD_AGC (0x2 << 0)
+#define SSEQRD_T_SSEQSTRD_SEARCH (0x3 << 0)
+#define SSEQRD_T_SSEQSTRD_CLOCK_SYNC (0x4 << 0)
+#define SSEQRD_T_SSEQSTRD_FREQ_SYNC (0x8 << 0)
+#define SSEQRD_T_SSEQSTRD_FRAME_SYNC (0x9 << 0)
+#define SSEQRD_T_SSEQSTRD_SYNC (0xa << 0)
+#define SSEQRD_T_SSEQSTRD_LOCK (0xb << 0)
+#define AGCRDU_T 0xa8
+#define AGCRDL_T 0xa9
+#define CNRDU_T 0xbe
+#define CNRDL_T 0xbf
+#define BERFLG_T 0xc0
+#define BERFLG_T_BERDRDY BIT(7)
+#define BERFLG_T_BERDCHK BIT(6)
+#define BERFLG_T_BERVRDYA BIT(5)
+#define BERFLG_T_BERVCHKA BIT(4)
+#define BERFLG_T_BERVRDYB BIT(3)
+#define BERFLG_T_BERVCHKB BIT(2)
+#define BERFLG_T_BERVRDYC BIT(1)
+#define BERFLG_T_BERVCHKC BIT(0)
+#define BERRDU_T 0xc1
+#define BERRDM_T 0xc2
+#define BERRDL_T 0xc3
+#define BERLENRDU_T 0xc4
+#define BERLENRDL_T 0xc5
+#define ERRFLG_T 0xc6
+#define ERRFLG_T_BERDOVF BIT(7)
+#define ERRFLG_T_BERVOVFA BIT(6)
+#define ERRFLG_T_BERVOVFB BIT(5)
+#define ERRFLG_T_BERVOVFC BIT(4)
+#define ERRFLG_T_NERRFA BIT(3)
+#define ERRFLG_T_NERRFB BIT(2)
+#define ERRFLG_T_NERRFC BIT(1)
+#define ERRFLG_T_NERRF BIT(0)
+#define DOSET1_T 0xcf
+
+#define CLK_LOW 4000000
+#define CLK_DIRECT 20200000
+#define CLK_MAX 25410000
+
+#define S_T_FREQ 8126984 /* 512 / 63 MHz */
+
+struct mn88443x_spec {
+ bool primary;
+};
+
+struct mn88443x_priv {
+ const struct mn88443x_spec *spec;
+
+ struct dvb_frontend fe;
+ struct clk *mclk;
+ struct gpio_desc *reset_gpio;
+ u32 clk_freq;
+ u32 if_freq;
+
+ /* Common */
+ bool use_clkbuf;
+
+ /* ISDB-S */
+ struct i2c_client *client_s;
+ struct regmap *regmap_s;
+
+ /* ISDB-T */
+ struct i2c_client *client_t;
+ struct regmap *regmap_t;
+};
+
+static int mn88443x_cmn_power_on(struct mn88443x_priv *chip)
+{
+ struct device *dev = &chip->client_s->dev;
+ struct regmap *r_t = chip->regmap_t;
+ int ret;
+
+ ret = clk_prepare_enable(chip->mclk);
+ if (ret) {
+ dev_err(dev, "Failed to prepare and enable mclk: %d\n",
+ ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(chip->reset_gpio, 1);
+ usleep_range(100, 1000);
+ gpiod_set_value_cansleep(chip->reset_gpio, 0);
+
+ if (chip->spec->primary) {
+ regmap_write(r_t, OUTCSET, OUTCSET_CHDRV_8MA);
+ regmap_write(r_t, PLDWSET, PLDWSET_NORMAL);
+ regmap_write(r_t, HIZSET1, 0x80);
+ regmap_write(r_t, HIZSET2, 0xe0);
+ } else {
+ regmap_write(r_t, HIZSET3, 0x8f);
+ }
+
+ return 0;
+}
+
+static void mn88443x_cmn_power_off(struct mn88443x_priv *chip)
+{
+ gpiod_set_value_cansleep(chip->reset_gpio, 1);
+
+ clk_disable_unprepare(chip->mclk);
+}
+
+static void mn88443x_s_sleep(struct mn88443x_priv *chip)
+{
+ struct regmap *r_t = chip->regmap_t;
+
+ regmap_update_bits(r_t, PWDSET, PWDSET_PSKPD_MASK,
+ PWDSET_PSKPD_DOWN);
+}
+
+static void mn88443x_s_wake(struct mn88443x_priv *chip)
+{
+ struct regmap *r_t = chip->regmap_t;
+
+ regmap_update_bits(r_t, PWDSET, PWDSET_PSKPD_MASK, 0);
+}
+
+static void mn88443x_s_tune(struct mn88443x_priv *chip,
+ struct dtv_frontend_properties *c)
+{
+ struct regmap *r_s = chip->regmap_s;
+
+ regmap_write(r_s, ATSIDU_S, c->stream_id >> 8);
+ regmap_write(r_s, ATSIDL_S, c->stream_id);
+ regmap_write(r_s, TSSET_S, 0);
+}
+
+static int mn88443x_s_read_status(struct mn88443x_priv *chip,
+ struct dtv_frontend_properties *c,
+ enum fe_status *status)
+{
+ struct regmap *r_s = chip->regmap_s;
+ u32 cpmon, tmpu, tmpl, flg;
+ u64 tmp;
+
+ /* Sync detection */
+ regmap_read(r_s, CPMON1_S, &cpmon);
+
+ *status = 0;
+ if (cpmon & CPMON1_S_FSYNC)
+ *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ if (cpmon & CPMON1_S_W2LOCK)
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
+
+ /* Signal strength */
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ if (*status & FE_HAS_SIGNAL) {
+ u32 agc;
+
+ regmap_read(r_s, AGCREAD_S, &tmpu);
+ agc = tmpu << 8;
+
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ c->strength.stat[0].uvalue = agc;
+ }
+
+ /* C/N rate */
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ if (*status & FE_HAS_VITERBI) {
+ u32 cnr = 0, x, y, d;
+ u64 d_3 = 0;
+
+ regmap_read(r_s, CNRDXU_S, &tmpu);
+ regmap_read(r_s, CNRDXL_S, &tmpl);
+ x = (tmpu << 8) | tmpl;
+ regmap_read(r_s, CNRDYU_S, &tmpu);
+ regmap_read(r_s, CNRDYL_S, &tmpl);
+ y = (tmpu << 8) | tmpl;
+
+ /* CNR[dB]: 10 * log10(D) - 30.74 / D^3 - 3 */
+ /* D = x^2 / (2^15 * y - x^2) */
+ d = (y << 15) - x * x;
+ if (d > 0) {
+ /* (2^4 * D)^3 = 2^12 * D^3 */
+ /* 3.074 * 2^(12 + 24) = 211243671486 */
+ d_3 = div_u64(16 * x * x, d);
+ d_3 = d_3 * d_3 * d_3;
+ if (d_3)
+ d_3 = div_u64(211243671486ULL, d_3);
+ }
+
+ if (d_3) {
+ /* 0.3 * 2^24 = 5033164 */
+ tmp = (s64)2 * intlog10(x) - intlog10(abs(d)) - d_3
+ - 5033164;
+ cnr = div_u64(tmp * 10000, 1 << 24);
+ }
+
+ if (cnr) {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].uvalue = cnr;
+ }
+ }
+
+ /* BER */
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ regmap_read(r_s, BERCNFLG_S, &flg);
+
+ if ((*status & FE_HAS_VITERBI) && (flg & BERCNFLG_S_BERVRDY)) {
+ u32 bit_err, bit_cnt;
+
+ regmap_read(r_s, BERVRDU_S, &tmpu);
+ regmap_read(r_s, BERVRDL_S, &tmpl);
+ bit_err = (tmpu << 8) | tmpl;
+ bit_cnt = (1 << 13) * 204;
+
+ if (bit_cnt) {
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = 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 = bit_cnt;
+ }
+ }
+
+ return 0;
+}
+
+static void mn88443x_t_sleep(struct mn88443x_priv *chip)
+{
+ struct regmap *r_t = chip->regmap_t;
+
+ regmap_update_bits(r_t, PWDSET, PWDSET_OFDMPD_MASK,
+ PWDSET_OFDMPD_DOWN);
+}
+
+static void mn88443x_t_wake(struct mn88443x_priv *chip)
+{
+ struct regmap *r_t = chip->regmap_t;
+
+ regmap_update_bits(r_t, PWDSET, PWDSET_OFDMPD_MASK, 0);
+}
+
+static bool mn88443x_t_is_valid_clk(u32 adckt, u32 if_freq)
+{
+ if (if_freq == DIRECT_IF_57MHZ) {
+ if (adckt >= CLK_DIRECT && adckt <= 21000000)
+ return true;
+ if (adckt >= 25300000 && adckt <= CLK_MAX)
+ return true;
+ } else if (if_freq == DIRECT_IF_44MHZ) {
+ if (adckt >= 25000000 && adckt <= CLK_MAX)
+ return true;
+ } else if (if_freq >= LOW_IF_4MHZ && if_freq < DIRECT_IF_44MHZ) {
+ if (adckt >= CLK_DIRECT && adckt <= CLK_MAX)
+ return true;
+ }
+
+ return false;
+}
+
+static int mn88443x_t_set_freq(struct mn88443x_priv *chip)
+{
+ struct device *dev = &chip->client_s->dev;
+ struct regmap *r_t = chip->regmap_t;
+ s64 adckt, nco, ad_t;
+ u32 m, v;
+
+ /* Clock buffer (but not supported) or XTAL */
+ if (chip->clk_freq >= CLK_LOW && chip->clk_freq < CLK_DIRECT) {
+ chip->use_clkbuf = true;
+ regmap_write(r_t, CLKSET1_T, 0x07);
+
+ adckt = 0;
+ } else {
+ chip->use_clkbuf = false;
+ regmap_write(r_t, CLKSET1_T, 0x00);
+
+ adckt = chip->clk_freq;
+ }
+ if (!mn88443x_t_is_valid_clk(adckt, chip->if_freq)) {
+ dev_err(dev, "Invalid clock, CLK:%d, ADCKT:%lld, IF:%d\n",
+ chip->clk_freq, adckt, chip->if_freq);
+ return -EINVAL;
+ }
+
+ /* Direct IF or Low IF */
+ if (chip->if_freq == DIRECT_IF_57MHZ ||
+ chip->if_freq == DIRECT_IF_44MHZ)
+ nco = adckt * 2 - chip->if_freq;
+ else
+ nco = -((s64)chip->if_freq);
+ nco = div_s64(nco << 24, adckt);
+ ad_t = div_s64(adckt << 22, S_T_FREQ);
+
+ regmap_write(r_t, NCOFREQU_T, nco >> 16);
+ regmap_write(r_t, NCOFREQM_T, nco >> 8);
+ regmap_write(r_t, NCOFREQL_T, nco);
+ regmap_write(r_t, FADU_T, ad_t >> 16);
+ regmap_write(r_t, FADM_T, ad_t >> 8);
+ regmap_write(r_t, FADL_T, ad_t);
+
+ /* Level of IF */
+ m = ADCSET1_T_REFSEL_MASK;
+ v = ADCSET1_T_REFSEL_1_5V;
+ regmap_update_bits(r_t, ADCSET1_T, m, v);
+
+ /* Polarity of AGC */
+ v = AGCSET2_T_IFPOLINV_INC | AGCSET2_T_RFPOLINV_INC;
+ regmap_update_bits(r_t, AGCSET2_T, v, v);
+
+ /* Lower output level of AGC */
+ regmap_write(r_t, AGCV3_T, 0x00);
+
+ regmap_write(r_t, MDSET_T, 0xfa);
+
+ return 0;
+}
+
+static void mn88443x_t_tune(struct mn88443x_priv *chip,
+ struct dtv_frontend_properties *c)
+{
+ struct regmap *r_t = chip->regmap_t;
+ u32 m, v;
+
+ m = MDSET_T_MDAUTO_MASK | MDSET_T_FFTS_MASK | MDSET_T_GI_MASK;
+ v = MDSET_T_MDAUTO_AUTO | MDSET_T_FFTS_MODE3 | MDSET_T_GI_1_8;
+ regmap_update_bits(r_t, MDSET_T, m, v);
+
+ regmap_write(r_t, MDASET_T, 0);
+}
+
+static int mn88443x_t_read_status(struct mn88443x_priv *chip,
+ struct dtv_frontend_properties *c,
+ enum fe_status *status)
+{
+ struct regmap *r_t = chip->regmap_t;
+ u32 seqrd, st, flg, tmpu, tmpm, tmpl;
+ u64 tmp;
+
+ /* Sync detection */
+ regmap_read(r_t, SSEQRD_T, &seqrd);
+ st = seqrd & SSEQRD_T_SSEQSTRD_MASK;
+
+ *status = 0;
+ if (st >= SSEQRD_T_SSEQSTRD_SYNC)
+ *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ if (st >= SSEQRD_T_SSEQSTRD_FRAME_SYNC)
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
+
+ /* Signal strength */
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ if (*status & FE_HAS_SIGNAL) {
+ u32 agc;
+
+ regmap_read(r_t, AGCRDU_T, &tmpu);
+ regmap_read(r_t, AGCRDL_T, &tmpl);
+ agc = (tmpu << 8) | tmpl;
+
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ c->strength.stat[0].uvalue = agc;
+ }
+
+ /* C/N rate */
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ if (*status & FE_HAS_VITERBI) {
+ u32 cnr;
+
+ regmap_read(r_t, CNRDU_T, &tmpu);
+ regmap_read(r_t, CNRDL_T, &tmpl);
+
+ if (tmpu || tmpl) {
+ /* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */
+ /* intlog10(65536) = 80807124, 0.2 * 2^24 = 3355443 */
+ tmp = (u64)80807124 - intlog10((tmpu << 8) | tmpl)
+ + 3355443;
+ cnr = div_u64(tmp * 10000, 1 << 24);
+ } else {
+ cnr = 0;
+ }
+
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].uvalue = cnr;
+ }
+
+ /* BER */
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ regmap_read(r_t, BERFLG_T, &flg);
+
+ if ((*status & FE_HAS_VITERBI) && (flg & BERFLG_T_BERVRDYA)) {
+ u32 bit_err, bit_cnt;
+
+ regmap_read(r_t, BERRDU_T, &tmpu);
+ regmap_read(r_t, BERRDM_T, &tmpm);
+ regmap_read(r_t, BERRDL_T, &tmpl);
+ bit_err = (tmpu << 16) | (tmpm << 8) | tmpl;
+
+ regmap_read(r_t, BERLENRDU_T, &tmpu);
+ regmap_read(r_t, BERLENRDL_T, &tmpl);
+ bit_cnt = ((tmpu << 8) | tmpl) * 203 * 8;
+
+ if (bit_cnt) {
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = 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 = bit_cnt;
+ }
+ }
+
+ return 0;
+}
+
+static int mn88443x_sleep(struct dvb_frontend *fe)
+{
+ struct mn88443x_priv *chip = fe->demodulator_priv;
+
+ mn88443x_s_sleep(chip);
+ mn88443x_t_sleep(chip);
+
+ return 0;
+}
+
+static int mn88443x_set_frontend(struct dvb_frontend *fe)
+{
+ struct mn88443x_priv *chip = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct regmap *r_s = chip->regmap_s;
+ struct regmap *r_t = chip->regmap_t;
+ u8 tssel = 0, intsel = 0;
+
+ if (c->delivery_system == SYS_ISDBS) {
+ mn88443x_s_wake(chip);
+ mn88443x_t_sleep(chip);
+
+ tssel = TSSET1_TSASEL_ISDBS;
+ intsel = TSSET3_INTASEL_S;
+ } else if (c->delivery_system == SYS_ISDBT) {
+ mn88443x_s_sleep(chip);
+ mn88443x_t_wake(chip);
+
+ mn88443x_t_set_freq(chip);
+
+ tssel = TSSET1_TSASEL_ISDBT;
+ intsel = TSSET3_INTASEL_T;
+ }
+
+ regmap_update_bits(r_t, TSSET1,
+ TSSET1_TSASEL_MASK | TSSET1_TSBSEL_MASK,
+ tssel | TSSET1_TSBSEL_NONE);
+ regmap_write(r_t, TSSET2, 0);
+ regmap_update_bits(r_t, TSSET3,
+ TSSET3_INTASEL_MASK | TSSET3_INTBSEL_MASK,
+ intsel | TSSET3_INTBSEL_NONE);
+
+ regmap_write(r_t, DOSET1_T, 0x95);
+ regmap_write(r_s, DOSET1_S, 0x80);
+
+ if (c->delivery_system == SYS_ISDBS)
+ mn88443x_s_tune(chip, c);
+ else if (c->delivery_system == SYS_ISDBT)
+ mn88443x_t_tune(chip, c);
+
+ if (fe->ops.tuner_ops.set_params) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ return 0;
+}
+
+static int mn88443x_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ s->min_delay_ms = 850;
+
+ if (c->delivery_system == SYS_ISDBS) {
+ s->max_drift = 30000 * 2 + 1;
+ s->step_size = 30000;
+ } else if (c->delivery_system == SYS_ISDBT) {
+ s->max_drift = 142857 * 2 + 1;
+ s->step_size = 142857 * 2;
+ }
+
+ return 0;
+}
+
+static int mn88443x_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct mn88443x_priv *chip = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (c->delivery_system == SYS_ISDBS)
+ return mn88443x_s_read_status(chip, c, status);
+
+ if (c->delivery_system == SYS_ISDBT)
+ return mn88443x_t_read_status(chip, c, status);
+
+ return -EINVAL;
+}
+
+static const struct dvb_frontend_ops mn88443x_ops = {
+ .delsys = { SYS_ISDBS, SYS_ISDBT },
+ .info = {
+ .name = "Socionext MN88443x",
+ .frequency_min_hz = 470 * MHz,
+ .frequency_max_hz = 2071 * MHz,
+ .symbol_rate_min = 28860000,
+ .symbol_rate_max = 28860000,
+ .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .sleep = mn88443x_sleep,
+ .set_frontend = mn88443x_set_frontend,
+ .get_tune_settings = mn88443x_get_tune_settings,
+ .read_status = mn88443x_read_status,
+};
+
+static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_NONE,
+};
+
+static int mn88443x_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct mn88443x_config *conf = client->dev.platform_data;
+ struct mn88443x_priv *chip;
+ struct device *dev = &client->dev;
+ int ret;
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ if (dev->of_node)
+ chip->spec = of_device_get_match_data(dev);
+ else
+ chip->spec = (struct mn88443x_spec *)id->driver_data;
+ if (!chip->spec)
+ return -EINVAL;
+
+ chip->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(chip->mclk) && !conf) {
+ dev_err(dev, "Failed to request mclk: %pe\n", chip->mclk);
+ return PTR_ERR(chip->mclk);
+ }
+
+ ret = of_property_read_u32(dev->of_node, "if-frequency",
+ &chip->if_freq);
+ if (ret && !conf) {
+ dev_err(dev, "Failed to load IF frequency: %d.\n", ret);
+ return ret;
+ }
+
+ chip->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(chip->reset_gpio)) {
+ dev_err(dev, "Failed to request reset_gpio: %pe\n",
+ chip->reset_gpio);
+ return PTR_ERR(chip->reset_gpio);
+ }
+
+ if (conf) {
+ chip->mclk = conf->mclk;
+ chip->if_freq = conf->if_freq;
+ chip->reset_gpio = conf->reset_gpio;
+
+ *conf->fe = &chip->fe;
+ }
+
+ chip->client_s = client;
+ chip->regmap_s = devm_regmap_init_i2c(chip->client_s, &regmap_config);
+ if (IS_ERR(chip->regmap_s))
+ return PTR_ERR(chip->regmap_s);
+
+ /*
+ * Chip has two I2C addresses for each satellite/terrestrial system.
+ * ISDB-T uses address ISDB-S + 4, so we register a dummy client.
+ */
+ chip->client_t = i2c_new_dummy_device(client->adapter, client->addr + 4);
+ if (IS_ERR(chip->client_t))
+ return PTR_ERR(chip->client_t);
+
+ chip->regmap_t = devm_regmap_init_i2c(chip->client_t, &regmap_config);
+ if (IS_ERR(chip->regmap_t)) {
+ ret = PTR_ERR(chip->regmap_t);
+ goto err_i2c_t;
+ }
+
+ chip->clk_freq = clk_get_rate(chip->mclk);
+
+ memcpy(&chip->fe.ops, &mn88443x_ops, sizeof(mn88443x_ops));
+ chip->fe.demodulator_priv = chip;
+ i2c_set_clientdata(client, chip);
+
+ ret = mn88443x_cmn_power_on(chip);
+ if (ret)
+ goto err_i2c_t;
+
+ mn88443x_s_sleep(chip);
+ mn88443x_t_sleep(chip);
+
+ return 0;
+
+err_i2c_t:
+ i2c_unregister_device(chip->client_t);
+
+ return ret;
+}
+
+static void mn88443x_remove(struct i2c_client *client)
+{
+ struct mn88443x_priv *chip = i2c_get_clientdata(client);
+
+ mn88443x_cmn_power_off(chip);
+
+ i2c_unregister_device(chip->client_t);
+}
+
+static const struct mn88443x_spec mn88443x_spec_pri = {
+ .primary = true,
+};
+
+static const struct mn88443x_spec mn88443x_spec_sec = {
+ .primary = false,
+};
+
+static const struct of_device_id mn88443x_of_match[] = {
+ { .compatible = "socionext,mn884433", .data = &mn88443x_spec_pri, },
+ { .compatible = "socionext,mn884434-0", .data = &mn88443x_spec_pri, },
+ { .compatible = "socionext,mn884434-1", .data = &mn88443x_spec_sec, },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mn88443x_of_match);
+
+static const struct i2c_device_id mn88443x_i2c_id[] = {
+ { "mn884433", (kernel_ulong_t)&mn88443x_spec_pri },
+ { "mn884434-0", (kernel_ulong_t)&mn88443x_spec_pri },
+ { "mn884434-1", (kernel_ulong_t)&mn88443x_spec_sec },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mn88443x_i2c_id);
+
+static struct i2c_driver mn88443x_driver = {
+ .driver = {
+ .name = "mn88443x",
+ .of_match_table = mn88443x_of_match,
+ },
+ .probe = mn88443x_probe,
+ .remove = mn88443x_remove,
+ .id_table = mn88443x_i2c_id,
+};
+
+module_i2c_driver(mn88443x_driver);
+
+MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
+MODULE_DESCRIPTION("Socionext MN88443x series demodulator driver.");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/dvb-frontends/mn88443x.h b/drivers/media/dvb-frontends/mn88443x.h
new file mode 100644
index 000000000000..b19aaf6a1ea3
--- /dev/null
+++ b/drivers/media/dvb-frontends/mn88443x.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Socionext MN88443x series demodulator driver for ISDB-S/ISDB-T.
+ *
+ * Copyright (c) 2018 Socionext Inc.
+ */
+
+#ifndef MN88443X_H
+#define MN88443X_H
+
+#include <media/dvb_frontend.h>
+
+/* ISDB-T IF frequency */
+#define DIRECT_IF_57MHZ 57000000
+#define DIRECT_IF_44MHZ 44000000
+#define LOW_IF_4MHZ 4000000
+
+struct mn88443x_config {
+ struct clk *mclk;
+ u32 if_freq;
+ struct gpio_desc *reset_gpio;
+
+ /* Everything after that is returned by the driver. */
+ struct dvb_frontend **fe;
+};
+
+#endif /* MN88443X_H */
diff --git a/drivers/media/dvb-frontends/mn88472.c b/drivers/media/dvb-frontends/mn88472.c
new file mode 100644
index 000000000000..729751671c3d
--- /dev/null
+++ b/drivers/media/dvb-frontends/mn88472.c
@@ -0,0 +1,731 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Panasonic MN88472 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ */
+
+#include "mn88472_priv.h"
+
+static int mn88472_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 1000;
+ return 0;
+}
+
+static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88472_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, stmp;
+ unsigned int utmp, utmp1, utmp2;
+ u8 buf[5];
+
+ if (!dev->active) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ ret = regmap_read(dev->regmap[0], 0x7f, &utmp);
+ if (ret)
+ goto err;
+ if ((utmp & 0x0f) >= 0x09)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ else
+ *status = 0;
+ break;
+ case SYS_DVBT2:
+ ret = regmap_read(dev->regmap[2], 0x92, &utmp);
+ if (ret)
+ goto err;
+ if ((utmp & 0x0f) >= 0x0d)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ else if ((utmp & 0x0f) >= 0x0a)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+ else if ((utmp & 0x0f) >= 0x07)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ else
+ *status = 0;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = regmap_read(dev->regmap[1], 0x84, &utmp);
+ if (ret)
+ goto err;
+ if ((utmp & 0x0f) >= 0x08)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ else
+ *status = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Signal strength */
+ if (*status & FE_HAS_SIGNAL) {
+ for (i = 0; i < 2; i++) {
+ ret = regmap_bulk_read(dev->regmap[2], 0x8e + i,
+ &buf[i], 1);
+ if (ret)
+ goto err;
+ }
+
+ utmp1 = buf[0] << 8 | buf[1] << 0 | buf[0] >> 2;
+ dev_dbg(&client->dev, "strength=%u\n", utmp1);
+
+ c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ c->strength.stat[0].uvalue = utmp1;
+ } else {
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* CNR */
+ if (*status & FE_HAS_VITERBI && c->delivery_system == SYS_DVBT) {
+ /* DVB-T CNR */
+ ret = regmap_bulk_read(dev->regmap[0], 0x9c, buf, 2);
+ if (ret)
+ goto err;
+
+ utmp = buf[0] << 8 | buf[1] << 0;
+ if (utmp) {
+ /* CNR[dB]: 10 * log10(65536 / value) + 2 */
+ /* log10(65536) = 80807124, 0.2 = 3355443 */
+ stmp = ((u64)80807124 - intlog10(utmp) + 3355443)
+ * 10000 >> 24;
+
+ dev_dbg(&client->dev, "cnr=%d value=%u\n", stmp, utmp);
+ } else {
+ stmp = 0;
+ }
+
+ c->cnr.stat[0].svalue = stmp;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ } else if (*status & FE_HAS_VITERBI &&
+ c->delivery_system == SYS_DVBT2) {
+ /* DVB-T2 CNR */
+ for (i = 0; i < 3; i++) {
+ ret = regmap_bulk_read(dev->regmap[2], 0xbc + i,
+ &buf[i], 1);
+ if (ret)
+ goto err;
+ }
+
+ utmp = buf[1] << 8 | buf[2] << 0;
+ utmp1 = (buf[0] >> 2) & 0x01; /* 0=SISO, 1=MISO */
+ if (utmp) {
+ if (utmp1) {
+ /* CNR[dB]: 10 * log10(16384 / value) - 6 */
+ /* log10(16384) = 70706234, 0.6 = 10066330 */
+ stmp = ((u64)70706234 - intlog10(utmp)
+ - 10066330) * 10000 >> 24;
+ dev_dbg(&client->dev, "cnr=%d value=%u MISO\n",
+ stmp, utmp);
+ } else {
+ /* CNR[dB]: 10 * log10(65536 / value) + 2 */
+ /* log10(65536) = 80807124, 0.2 = 3355443 */
+ stmp = ((u64)80807124 - intlog10(utmp)
+ + 3355443) * 10000 >> 24;
+
+ dev_dbg(&client->dev, "cnr=%d value=%u SISO\n",
+ stmp, utmp);
+ }
+ } else {
+ stmp = 0;
+ }
+
+ c->cnr.stat[0].svalue = stmp;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ } else if (*status & FE_HAS_VITERBI &&
+ c->delivery_system == SYS_DVBC_ANNEX_A) {
+ /* DVB-C CNR */
+ ret = regmap_bulk_read(dev->regmap[1], 0xa1, buf, 4);
+ if (ret)
+ goto err;
+
+ utmp1 = buf[0] << 8 | buf[1] << 0; /* signal */
+ utmp2 = buf[2] << 8 | buf[3] << 0; /* noise */
+ if (utmp1 && utmp2) {
+ /* CNR[dB]: 10 * log10(8 * (signal / noise)) */
+ /* log10(8) = 15151336 */
+ stmp = ((u64)15151336 + intlog10(utmp1)
+ - intlog10(utmp2)) * 10000 >> 24;
+
+ dev_dbg(&client->dev, "cnr=%d signal=%u noise=%u\n",
+ stmp, utmp1, utmp2);
+ } else {
+ stmp = 0;
+ }
+
+ c->cnr.stat[0].svalue = stmp;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ } else {
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* PER */
+ if (*status & FE_HAS_SYNC) {
+ ret = regmap_bulk_read(dev->regmap[0], 0xe1, buf, 4);
+ if (ret)
+ goto err;
+
+ utmp1 = buf[0] << 8 | buf[1] << 0;
+ utmp2 = buf[2] << 8 | buf[3] << 0;
+ dev_dbg(&client->dev, "block_error=%u block_count=%u\n",
+ utmp1, utmp2);
+
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue += utmp1;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue += utmp2;
+ } else {
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88472_set_frontend(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88472_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ unsigned int utmp;
+ u32 if_frequency;
+ u8 buf[3], delivery_system_val, bandwidth_val, *bandwidth_vals_ptr;
+ u8 reg_bank0_b4_val, reg_bank0_cd_val, reg_bank0_d4_val;
+ u8 reg_bank0_d6_val;
+
+ dev_dbg(&client->dev,
+ "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n",
+ c->delivery_system, c->modulation, c->frequency,
+ c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id);
+
+ if (!dev->active) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ delivery_system_val = 0x02;
+ reg_bank0_b4_val = 0x00;
+ reg_bank0_cd_val = 0x1f;
+ reg_bank0_d4_val = 0x0a;
+ reg_bank0_d6_val = 0x48;
+ break;
+ case SYS_DVBT2:
+ delivery_system_val = 0x03;
+ reg_bank0_b4_val = 0xf6;
+ reg_bank0_cd_val = 0x01;
+ reg_bank0_d4_val = 0x09;
+ reg_bank0_d6_val = 0x46;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ delivery_system_val = 0x04;
+ reg_bank0_b4_val = 0x00;
+ reg_bank0_cd_val = 0x17;
+ reg_bank0_d4_val = 0x09;
+ reg_bank0_d6_val = 0x48;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ switch (c->bandwidth_hz) {
+ case 5000000:
+ bandwidth_vals_ptr = "\xe5\x99\x9a\x1b\xa9\x1b\xa9";
+ bandwidth_val = 0x03;
+ break;
+ case 6000000:
+ bandwidth_vals_ptr = "\xbf\x55\x55\x15\x6b\x15\x6b";
+ bandwidth_val = 0x02;
+ break;
+ case 7000000:
+ bandwidth_vals_ptr = "\xa4\x00\x00\x0f\x2c\x0f\x2c";
+ bandwidth_val = 0x01;
+ break;
+ case 8000000:
+ bandwidth_vals_ptr = "\x8f\x80\x00\x08\xee\x08\xee";
+ bandwidth_val = 0x00;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+ break;
+ case SYS_DVBC_ANNEX_A:
+ bandwidth_vals_ptr = NULL;
+ bandwidth_val = 0x00;
+ break;
+ default:
+ break;
+ }
+
+ /* Program tuner */
+ if (fe->ops.tuner_ops.set_params) {
+ ret = fe->ops.tuner_ops.set_params(fe);
+ if (ret)
+ goto err;
+ }
+
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency);
+ } else {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = regmap_write(dev->regmap[2], 0x00, 0x66);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x01, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x02, 0x01);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x04, bandwidth_val);
+ if (ret)
+ goto err;
+
+ /* IF */
+ utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, dev->clk);
+ buf[0] = (utmp >> 16) & 0xff;
+ buf[1] = (utmp >> 8) & 0xff;
+ buf[2] = (utmp >> 0) & 0xff;
+ for (i = 0; i < 3; i++) {
+ ret = regmap_write(dev->regmap[2], 0x10 + i, buf[i]);
+ if (ret)
+ goto err;
+ }
+
+ /* Bandwidth */
+ if (bandwidth_vals_ptr) {
+ for (i = 0; i < 7; i++) {
+ ret = regmap_write(dev->regmap[2], 0x13 + i,
+ bandwidth_vals_ptr[i]);
+ if (ret)
+ goto err;
+ }
+ }
+
+ ret = regmap_write(dev->regmap[0], 0xb4, reg_bank0_b4_val);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0xcd, reg_bank0_cd_val);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0xd4, reg_bank0_d4_val);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0xd6, reg_bank0_d6_val);
+ if (ret)
+ goto err;
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ ret = regmap_write(dev->regmap[0], 0x07, 0x26);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x00, 0xba);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x01, 0x13);
+ if (ret)
+ goto err;
+ break;
+ case SYS_DVBT2:
+ ret = regmap_write(dev->regmap[2], 0x2b, 0x13);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x4f, 0x05);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[1], 0xf6, 0x05);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x32,
+ (c->stream_id == NO_STREAM_ID_FILTER) ? 0 :
+ c->stream_id );
+ if (ret)
+ goto err;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ break;
+ default:
+ break;
+ }
+
+ /* Reset FSM */
+ ret = regmap_write(dev->regmap[2], 0xf8, 0x9f);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88472_init(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88472_dev *dev = i2c_get_clientdata(client);
+ int ret, len, rem;
+ unsigned int utmp;
+ const struct firmware *firmware;
+ const char *name = MN88472_FIRMWARE;
+
+ dev_dbg(&client->dev, "\n");
+
+ /* Power up */
+ ret = regmap_write(dev->regmap[2], 0x05, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x0b, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x0c, 0x00);
+ if (ret)
+ goto err;
+
+ /* Check if firmware is already running */
+ ret = regmap_read(dev->regmap[0], 0xf5, &utmp);
+ if (ret)
+ goto err;
+ if (!(utmp & 0x01))
+ goto warm;
+
+ ret = request_firmware(&firmware, name, &client->dev);
+ if (ret) {
+ dev_err(&client->dev, "firmware file '%s' not found\n", name);
+ goto err;
+ }
+
+ dev_info(&client->dev, "downloading firmware from file '%s'\n", name);
+
+ ret = regmap_write(dev->regmap[0], 0xf5, 0x03);
+ if (ret)
+ goto err_release_firmware;
+
+ for (rem = firmware->size; rem > 0; rem -= (dev->i2c_write_max - 1)) {
+ len = min(dev->i2c_write_max - 1, rem);
+ ret = regmap_bulk_write(dev->regmap[0], 0xf6,
+ &firmware->data[firmware->size - rem],
+ len);
+ if (ret) {
+ dev_err(&client->dev, "firmware download failed %d\n",
+ ret);
+ goto err_release_firmware;
+ }
+ }
+
+ /* Parity check of firmware */
+ ret = regmap_read(dev->regmap[0], 0xf8, &utmp);
+ if (ret)
+ goto err_release_firmware;
+ if (utmp & 0x10) {
+ ret = -EINVAL;
+ dev_err(&client->dev, "firmware did not run\n");
+ goto err_release_firmware;
+ }
+
+ ret = regmap_write(dev->regmap[0], 0xf5, 0x00);
+ if (ret)
+ goto err_release_firmware;
+
+ release_firmware(firmware);
+warm:
+ /* TS config */
+ switch (dev->ts_mode) {
+ case SERIAL_TS_MODE:
+ utmp = 0x1d;
+ break;
+ case PARALLEL_TS_MODE:
+ utmp = 0x00;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+ ret = regmap_write(dev->regmap[2], 0x08, utmp);
+ if (ret)
+ goto err;
+
+ switch (dev->ts_clk) {
+ case VARIABLE_TS_CLOCK:
+ utmp = 0xe3;
+ break;
+ case FIXED_TS_CLOCK:
+ utmp = 0xe1;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+ ret = regmap_write(dev->regmap[0], 0xd9, utmp);
+ if (ret)
+ goto err;
+
+ dev->active = true;
+
+ return 0;
+err_release_firmware:
+ release_firmware(firmware);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88472_sleep(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88472_dev *dev = i2c_get_clientdata(client);
+ int ret;
+
+ dev_dbg(&client->dev, "\n");
+
+ /* Power down */
+ ret = regmap_write(dev->regmap[2], 0x0c, 0x30);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x0b, 0x30);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static const struct dvb_frontend_ops mn88472_ops = {
+ .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A},
+ .info = {
+ .name = "Panasonic MN88472",
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 7200000,
+ .caps = FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ 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_HIERARCHY_AUTO |
+ FE_CAN_MUTE_TS |
+ FE_CAN_2G_MODULATION |
+ FE_CAN_MULTISTREAM
+ },
+
+ .get_tune_settings = mn88472_get_tune_settings,
+
+ .init = mn88472_init,
+ .sleep = mn88472_sleep,
+
+ .set_frontend = mn88472_set_frontend,
+
+ .read_status = mn88472_read_status,
+};
+
+static struct dvb_frontend *mn88472_get_dvb_frontend(struct i2c_client *client)
+{
+ struct mn88472_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ return &dev->fe;
+}
+
+static int mn88472_probe(struct i2c_client *client)
+{
+ struct mn88472_config *pdata = client->dev.platform_data;
+ struct mn88472_dev *dev;
+ struct dtv_frontend_properties *c;
+ int ret;
+ unsigned int utmp;
+ static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ };
+
+ dev_dbg(&client->dev, "\n");
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev->i2c_write_max = pdata->i2c_wr_max ? pdata->i2c_wr_max : ~0;
+ dev->clk = pdata->xtal;
+ dev->ts_mode = pdata->ts_mode;
+ dev->ts_clk = pdata->ts_clock;
+ dev->client[0] = client;
+ dev->regmap[0] = regmap_init_i2c(dev->client[0], &regmap_config);
+ if (IS_ERR(dev->regmap[0])) {
+ ret = PTR_ERR(dev->regmap[0]);
+ goto err_kfree;
+ }
+
+ /*
+ * Chip has three I2C addresses for different register banks. Used
+ * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients,
+ * 0x1a and 0x1c, in order to get own I2C client for each register bank.
+ *
+ * Also, register bank 2 do not support sequential I/O. Only single
+ * register write or read is allowed to that bank.
+ */
+ dev->client[1] = i2c_new_dummy_device(client->adapter, 0x1a);
+ if (IS_ERR(dev->client[1])) {
+ ret = PTR_ERR(dev->client[1]);
+ dev_err(&client->dev, "I2C registration failed\n");
+ goto err_regmap_0_regmap_exit;
+ }
+ dev->regmap[1] = regmap_init_i2c(dev->client[1], &regmap_config);
+ if (IS_ERR(dev->regmap[1])) {
+ ret = PTR_ERR(dev->regmap[1]);
+ goto err_client_1_i2c_unregister_device;
+ }
+ i2c_set_clientdata(dev->client[1], dev);
+
+ dev->client[2] = i2c_new_dummy_device(client->adapter, 0x1c);
+ if (IS_ERR(dev->client[2])) {
+ ret = PTR_ERR(dev->client[2]);
+ dev_err(&client->dev, "2nd I2C registration failed\n");
+ goto err_regmap_1_regmap_exit;
+ }
+ dev->regmap[2] = regmap_init_i2c(dev->client[2], &regmap_config);
+ if (IS_ERR(dev->regmap[2])) {
+ ret = PTR_ERR(dev->regmap[2]);
+ goto err_client_2_i2c_unregister_device;
+ }
+ i2c_set_clientdata(dev->client[2], dev);
+
+ /* Check demod answers with correct chip id */
+ ret = regmap_read(dev->regmap[2], 0xff, &utmp);
+ if (ret)
+ goto err_regmap_2_regmap_exit;
+
+ dev_dbg(&client->dev, "chip id=%02x\n", utmp);
+
+ if (utmp != 0x02) {
+ ret = -ENODEV;
+ goto err_regmap_2_regmap_exit;
+ }
+
+ /* Sleep because chip is active by default */
+ ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
+ if (ret)
+ goto err_regmap_2_regmap_exit;
+
+ /* Create dvb frontend */
+ memcpy(&dev->fe.ops, &mn88472_ops, sizeof(struct dvb_frontend_ops));
+ dev->fe.demodulator_priv = client;
+ *pdata->fe = &dev->fe;
+ i2c_set_clientdata(client, dev);
+
+ /* Init stats to indicate which stats are supported */
+ c = &dev->fe.dtv_property_cache;
+ c->strength.len = 1;
+ c->cnr.len = 1;
+ c->block_error.len = 1;
+ c->block_count.len = 1;
+
+ /* Setup callbacks */
+ pdata->get_dvb_frontend = mn88472_get_dvb_frontend;
+
+ dev_info(&client->dev, "Panasonic MN88472 successfully identified\n");
+
+ return 0;
+err_regmap_2_regmap_exit:
+ regmap_exit(dev->regmap[2]);
+err_client_2_i2c_unregister_device:
+ i2c_unregister_device(dev->client[2]);
+err_regmap_1_regmap_exit:
+ regmap_exit(dev->regmap[1]);
+err_client_1_i2c_unregister_device:
+ i2c_unregister_device(dev->client[1]);
+err_regmap_0_regmap_exit:
+ regmap_exit(dev->regmap[0]);
+err_kfree:
+ kfree(dev);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static void mn88472_remove(struct i2c_client *client)
+{
+ struct mn88472_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ regmap_exit(dev->regmap[2]);
+ i2c_unregister_device(dev->client[2]);
+
+ regmap_exit(dev->regmap[1]);
+ i2c_unregister_device(dev->client[1]);
+
+ regmap_exit(dev->regmap[0]);
+
+ kfree(dev);
+}
+
+static const struct i2c_device_id mn88472_id_table[] = {
+ { "mn88472" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mn88472_id_table);
+
+static struct i2c_driver mn88472_driver = {
+ .driver = {
+ .name = "mn88472",
+ .suppress_bind_attrs = true,
+ },
+ .probe = mn88472_probe,
+ .remove = mn88472_remove,
+ .id_table = mn88472_id_table,
+};
+
+module_i2c_driver(mn88472_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Panasonic MN88472 DVB-T/T2/C demodulator driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(MN88472_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/mn88472.h b/drivers/media/dvb-frontends/mn88472.h
new file mode 100644
index 000000000000..e4f217747873
--- /dev/null
+++ b/drivers/media/dvb-frontends/mn88472.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Panasonic MN88472 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ */
+
+#ifndef MN88472_H
+#define MN88472_H
+
+#include <linux/dvb/frontend.h>
+
+/* Define old names for backward compatibility */
+#define VARIABLE_TS_CLOCK MN88472_TS_CLK_VARIABLE
+#define FIXED_TS_CLOCK MN88472_TS_CLK_FIXED
+#define SERIAL_TS_MODE MN88472_TS_MODE_SERIAL
+#define PARALLEL_TS_MODE MN88472_TS_MODE_PARALLEL
+
+/**
+ * struct mn88472_config - Platform data for the mn88472 driver
+ * @xtal: Clock frequency.
+ * @ts_mode: TS mode.
+ * @ts_clock: TS clock config.
+ * @i2c_wr_max: Max number of bytes driver writes to I2C at once.
+ * @fe: pointer to a frontend pointer
+ * @get_dvb_frontend: Get DVB frontend callback.
+ */
+struct mn88472_config {
+ unsigned int xtal;
+
+#define MN88472_TS_MODE_SERIAL 0
+#define MN88472_TS_MODE_PARALLEL 1
+ int ts_mode;
+
+#define MN88472_TS_CLK_FIXED 0
+#define MN88472_TS_CLK_VARIABLE 1
+ int ts_clock;
+
+ u16 i2c_wr_max;
+
+ /* Everything after that is returned by the driver. */
+
+ /*
+ * DVB frontend.
+ */
+ struct dvb_frontend **fe;
+ struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/mn88472_priv.h b/drivers/media/dvb-frontends/mn88472_priv.h
new file mode 100644
index 000000000000..41f14bd67bfd
--- /dev/null
+++ b/drivers/media/dvb-frontends/mn88472_priv.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Panasonic MN88472 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ */
+
+#ifndef MN88472_PRIV_H
+#define MN88472_PRIV_H
+
+#include <media/dvb_frontend.h>
+#include <linux/int_log.h>
+#include "mn88472.h"
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+
+#define MN88472_FIRMWARE "dvb-demod-mn88472-02.fw"
+
+struct mn88472_dev {
+ struct i2c_client *client[3];
+ struct regmap *regmap[3];
+ struct dvb_frontend fe;
+ u16 i2c_write_max;
+ unsigned int clk;
+ unsigned int active:1;
+ unsigned int ts_mode:1;
+ unsigned int ts_clk:1;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/mn88473.c b/drivers/media/dvb-frontends/mn88473.c
new file mode 100644
index 000000000000..fefc640d8afb
--- /dev/null
+++ b/drivers/media/dvb-frontends/mn88473.c
@@ -0,0 +1,766 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Panasonic MN88473 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ */
+
+#include "mn88473_priv.h"
+
+static int mn88473_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 1000;
+ return 0;
+}
+
+static int mn88473_set_frontend(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ unsigned int uitmp;
+ u32 if_frequency;
+ u8 delivery_system_val, if_val[3], *conf_val_ptr;
+ u8 reg_bank2_2d_val, reg_bank0_d2_val;
+
+ dev_dbg(&client->dev,
+ "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n",
+ c->delivery_system, c->modulation, c->frequency,
+ c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id);
+
+ if (!dev->active) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ delivery_system_val = 0x02;
+ reg_bank2_2d_val = 0x23;
+ reg_bank0_d2_val = 0x2a;
+ break;
+ case SYS_DVBT2:
+ delivery_system_val = 0x03;
+ reg_bank2_2d_val = 0x3b;
+ reg_bank0_d2_val = 0x29;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ delivery_system_val = 0x04;
+ reg_bank2_2d_val = 0x3b;
+ reg_bank0_d2_val = 0x29;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ conf_val_ptr = "\xe9\x55\x55\x1c\x29\x1c\x29";
+ break;
+ case 7000000:
+ conf_val_ptr = "\xc8\x00\x00\x17\x0a\x17\x0a";
+ break;
+ case 8000000:
+ conf_val_ptr = "\xaf\x00\x00\x11\xec\x11\xec";
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+ break;
+ case SYS_DVBC_ANNEX_A:
+ conf_val_ptr = "\x10\xab\x0d\xae\x1d\x9d";
+ break;
+ default:
+ break;
+ }
+
+ /* Program tuner */
+ if (fe->ops.tuner_ops.set_params) {
+ ret = fe->ops.tuner_ops.set_params(fe);
+ if (ret)
+ goto err;
+ }
+
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "get_if_frequency=%u\n", if_frequency);
+ } else {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Calculate IF registers */
+ uitmp = DIV_ROUND_CLOSEST_ULL((u64) if_frequency * 0x1000000, dev->clk);
+ if_val[0] = (uitmp >> 16) & 0xff;
+ if_val[1] = (uitmp >> 8) & 0xff;
+ if_val[2] = (uitmp >> 0) & 0xff;
+
+ ret = regmap_write(dev->regmap[2], 0x05, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0xfb, 0x13);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0xef, 0x13);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0xf9, 0x13);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x00, 0x18);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x01, 0x01);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x02, 0x21);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x0b, 0x00);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < sizeof(if_val); i++) {
+ ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]);
+ if (ret)
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ for (i = 0; i < 7; i++) {
+ ret = regmap_write(dev->regmap[2], 0x13 + i,
+ conf_val_ptr[i]);
+ if (ret)
+ goto err;
+ }
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = regmap_bulk_write(dev->regmap[1], 0x10, conf_val_ptr, 6);
+ if (ret)
+ goto err;
+ break;
+ default:
+ break;
+ }
+
+ ret = regmap_write(dev->regmap[2], 0x2d, reg_bank2_2d_val);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x2e, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x56, 0x0d);
+ if (ret)
+ goto err;
+ ret = regmap_bulk_write(dev->regmap[0], 0x01,
+ "\xba\x13\x80\xba\x91\xdd\xe7\x28", 8);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x0a, 0x1a);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x13, 0x1f);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x19, 0x03);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x1d, 0xb0);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x2a, 0x72);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x2d, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x3c, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0x3f, 0xf8);
+ if (ret)
+ goto err;
+ ret = regmap_bulk_write(dev->regmap[0], 0x40, "\xf4\x08", 2);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0xd2, reg_bank0_d2_val);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0xd4, 0x55);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[1], 0xbe, 0x08);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0xb2, 0x37);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[0], 0xd7, 0x04);
+ if (ret)
+ goto err;
+
+ /* PLP */
+ if (c->delivery_system == SYS_DVBT2) {
+ ret = regmap_write(dev->regmap[2], 0x36,
+ (c->stream_id == NO_STREAM_ID_FILTER) ? 0 :
+ c->stream_id );
+ if (ret)
+ goto err;
+ }
+
+ /* Reset FSM */
+ ret = regmap_write(dev->regmap[2], 0xf8, 0x9f);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88473_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, stmp;
+ unsigned int utmp, utmp1, utmp2;
+ u8 buf[5];
+
+ if (!dev->active) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ /* Lock detection */
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ ret = regmap_read(dev->regmap[0], 0x62, &utmp);
+ if (ret)
+ goto err;
+
+ if (!(utmp & 0xa0)) {
+ if ((utmp & 0x0f) >= 0x09)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ else if ((utmp & 0x0f) >= 0x03)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ } else {
+ *status = 0;
+ }
+ break;
+ case SYS_DVBT2:
+ ret = regmap_read(dev->regmap[2], 0x8b, &utmp);
+ if (ret)
+ goto err;
+
+ if (!(utmp & 0x40)) {
+ if ((utmp & 0x0f) >= 0x0d)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ else if ((utmp & 0x0f) >= 0x0a)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+ else if ((utmp & 0x0f) >= 0x07)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ } else {
+ *status = 0;
+ }
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = regmap_read(dev->regmap[1], 0x85, &utmp);
+ if (ret)
+ goto err;
+
+ if (!(utmp & 0x40)) {
+ ret = regmap_read(dev->regmap[1], 0x89, &utmp);
+ if (ret)
+ goto err;
+
+ if (utmp & 0x01)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ } else {
+ *status = 0;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Signal strength */
+ if (*status & FE_HAS_SIGNAL) {
+ for (i = 0; i < 2; i++) {
+ ret = regmap_bulk_read(dev->regmap[2], 0x86 + i,
+ &buf[i], 1);
+ if (ret)
+ goto err;
+ }
+
+ /* AGCRD[15:6] gives us a 10bit value ([5:0] are always 0) */
+ utmp1 = buf[0] << 8 | buf[1] << 0 | buf[0] >> 2;
+ dev_dbg(&client->dev, "strength=%u\n", utmp1);
+
+ c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ c->strength.stat[0].uvalue = utmp1;
+ } else {
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* CNR */
+ if (*status & FE_HAS_VITERBI && c->delivery_system == SYS_DVBT) {
+ /* DVB-T CNR */
+ ret = regmap_bulk_read(dev->regmap[0], 0x8f, buf, 2);
+ if (ret)
+ goto err;
+
+ utmp = buf[0] << 8 | buf[1] << 0;
+ if (utmp) {
+ /* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */
+ /* log10(65536) = 80807124, 0.2 = 3355443 */
+ stmp = div_u64(((u64)80807124 - intlog10(utmp)
+ + 3355443) * 10000, 1 << 24);
+ dev_dbg(&client->dev, "cnr=%d value=%u\n", stmp, utmp);
+ } else {
+ stmp = 0;
+ }
+
+ c->cnr.stat[0].svalue = stmp;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ } else if (*status & FE_HAS_VITERBI &&
+ c->delivery_system == SYS_DVBT2) {
+ /* DVB-T2 CNR */
+ for (i = 0; i < 3; i++) {
+ ret = regmap_bulk_read(dev->regmap[2], 0xb7 + i,
+ &buf[i], 1);
+ if (ret)
+ goto err;
+ }
+
+ utmp = buf[1] << 8 | buf[2] << 0;
+ utmp1 = (buf[0] >> 2) & 0x01; /* 0=SISO, 1=MISO */
+ if (utmp) {
+ if (utmp1) {
+ /* CNR[dB]: 10 * (log10(16384 / value) - 0.6) */
+ /* log10(16384) = 70706234, 0.6 = 10066330 */
+ stmp = div_u64(((u64)70706234 - intlog10(utmp)
+ - 10066330) * 10000, 1 << 24);
+ dev_dbg(&client->dev, "cnr=%d value=%u MISO\n",
+ stmp, utmp);
+ } else {
+ /* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */
+ /* log10(65536) = 80807124, 0.2 = 3355443 */
+ stmp = div_u64(((u64)80807124 - intlog10(utmp)
+ + 3355443) * 10000, 1 << 24);
+ dev_dbg(&client->dev, "cnr=%d value=%u SISO\n",
+ stmp, utmp);
+ }
+ } else {
+ stmp = 0;
+ }
+
+ c->cnr.stat[0].svalue = stmp;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ } else if (*status & FE_HAS_VITERBI &&
+ c->delivery_system == SYS_DVBC_ANNEX_A) {
+ /* DVB-C CNR */
+ ret = regmap_bulk_read(dev->regmap[1], 0xa1, buf, 4);
+ if (ret)
+ goto err;
+
+ utmp1 = buf[0] << 8 | buf[1] << 0; /* signal */
+ utmp2 = buf[2] << 8 | buf[3] << 0; /* noise */
+ if (utmp1 && utmp2) {
+ /* CNR[dB]: 10 * log10(8 * (signal / noise)) */
+ /* log10(8) = 15151336 */
+ stmp = div_u64(((u64)15151336 + intlog10(utmp1)
+ - intlog10(utmp2)) * 10000, 1 << 24);
+ dev_dbg(&client->dev, "cnr=%d signal=%u noise=%u\n",
+ stmp, utmp1, utmp2);
+ } else {
+ stmp = 0;
+ }
+
+ c->cnr.stat[0].svalue = stmp;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ } else {
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* BER */
+ if (*status & FE_HAS_LOCK && (c->delivery_system == SYS_DVBT ||
+ c->delivery_system == SYS_DVBC_ANNEX_A)) {
+ /* DVB-T & DVB-C BER */
+ ret = regmap_bulk_read(dev->regmap[0], 0x92, buf, 5);
+ if (ret)
+ goto err;
+
+ utmp1 = buf[0] << 16 | buf[1] << 8 | buf[2] << 0;
+ utmp2 = buf[3] << 8 | buf[4] << 0;
+ utmp2 = utmp2 * 8 * 204;
+ dev_dbg(&client->dev, "post_bit_error=%u post_bit_count=%u\n",
+ utmp1, utmp2);
+
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += utmp1;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue += utmp2;
+ } else {
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* PER */
+ if (*status & FE_HAS_LOCK) {
+ ret = regmap_bulk_read(dev->regmap[0], 0xdd, buf, 4);
+ if (ret)
+ goto err;
+
+ utmp1 = buf[0] << 8 | buf[1] << 0;
+ utmp2 = buf[2] << 8 | buf[3] << 0;
+ dev_dbg(&client->dev, "block_error=%u block_count=%u\n",
+ utmp1, utmp2);
+
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue += utmp1;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue += utmp2;
+ } else {
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88473_init(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, len, remain;
+ unsigned int uitmp;
+ const struct firmware *fw;
+ const char *name = MN88473_FIRMWARE;
+
+ dev_dbg(&client->dev, "\n");
+
+ /* Check if firmware is already running */
+ ret = regmap_read(dev->regmap[0], 0xf5, &uitmp);
+ if (ret)
+ goto err;
+
+ if (!(uitmp & 0x01))
+ goto warm;
+
+ /* Request the firmware, this will block and timeout */
+ ret = request_firmware(&fw, name, &client->dev);
+ if (ret) {
+ dev_err(&client->dev, "firmware file '%s' not found\n", name);
+ goto err;
+ }
+
+ dev_info(&client->dev, "downloading firmware from file '%s'\n", name);
+
+ ret = regmap_write(dev->regmap[0], 0xf5, 0x03);
+ if (ret)
+ goto err_release_firmware;
+
+ for (remain = fw->size; remain > 0; remain -= (dev->i2c_wr_max - 1)) {
+ len = min(dev->i2c_wr_max - 1, remain);
+ ret = regmap_bulk_write(dev->regmap[0], 0xf6,
+ &fw->data[fw->size - remain], len);
+ if (ret) {
+ dev_err(&client->dev, "firmware download failed %d\n",
+ ret);
+ goto err_release_firmware;
+ }
+ }
+
+ release_firmware(fw);
+
+ /* Parity check of firmware */
+ ret = regmap_read(dev->regmap[0], 0xf8, &uitmp);
+ if (ret)
+ goto err;
+
+ if (uitmp & 0x10) {
+ dev_err(&client->dev, "firmware parity check failed\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = regmap_write(dev->regmap[0], 0xf5, 0x00);
+ if (ret)
+ goto err;
+warm:
+ /* TS config */
+ ret = regmap_write(dev->regmap[2], 0x09, 0x08);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x08, 0x1d);
+ if (ret)
+ goto err;
+
+ dev->active = true;
+
+ /* init stats here to indicate which stats are supported */
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->cnr.len = 1;
+ c->cnr.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;
+err_release_firmware:
+ release_firmware(fw);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88473_sleep(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+ int ret;
+
+ dev_dbg(&client->dev, "\n");
+
+ dev->active = false;
+
+ ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static const struct dvb_frontend_ops mn88473_ops = {
+ .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A},
+ .info = {
+ .name = "Panasonic MN88473",
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 7200000,
+ .caps = FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ 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_HIERARCHY_AUTO |
+ FE_CAN_MUTE_TS |
+ FE_CAN_2G_MODULATION |
+ FE_CAN_MULTISTREAM
+ },
+
+ .get_tune_settings = mn88473_get_tune_settings,
+
+ .init = mn88473_init,
+ .sleep = mn88473_sleep,
+
+ .set_frontend = mn88473_set_frontend,
+
+ .read_status = mn88473_read_status,
+};
+
+static int mn88473_probe(struct i2c_client *client)
+{
+ struct mn88473_config *config = client->dev.platform_data;
+ struct mn88473_dev *dev;
+ int ret;
+ unsigned int uitmp;
+ static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ };
+
+ dev_dbg(&client->dev, "\n");
+
+ /* Caller really need to provide pointer for frontend we create */
+ if (config->fe == NULL) {
+ dev_err(&client->dev, "frontend pointer not defined\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (config->i2c_wr_max)
+ dev->i2c_wr_max = config->i2c_wr_max;
+ else
+ dev->i2c_wr_max = ~0;
+
+ if (config->xtal)
+ dev->clk = config->xtal;
+ else
+ dev->clk = 25000000;
+ dev->client[0] = client;
+ dev->regmap[0] = regmap_init_i2c(dev->client[0], &regmap_config);
+ if (IS_ERR(dev->regmap[0])) {
+ ret = PTR_ERR(dev->regmap[0]);
+ goto err_kfree;
+ }
+
+ /*
+ * Chip has three I2C addresses for different register banks. Used
+ * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients,
+ * 0x1a and 0x1c, in order to get own I2C client for each register bank.
+ *
+ * Also, register bank 2 do not support sequential I/O. Only single
+ * register write or read is allowed to that bank.
+ */
+ dev->client[1] = i2c_new_dummy_device(client->adapter, 0x1a);
+ if (IS_ERR(dev->client[1])) {
+ ret = PTR_ERR(dev->client[1]);
+ dev_err(&client->dev, "I2C registration failed\n");
+ goto err_regmap_0_regmap_exit;
+ }
+ dev->regmap[1] = regmap_init_i2c(dev->client[1], &regmap_config);
+ if (IS_ERR(dev->regmap[1])) {
+ ret = PTR_ERR(dev->regmap[1]);
+ goto err_client_1_i2c_unregister_device;
+ }
+ i2c_set_clientdata(dev->client[1], dev);
+
+ dev->client[2] = i2c_new_dummy_device(client->adapter, 0x1c);
+ if (IS_ERR(dev->client[2])) {
+ ret = PTR_ERR(dev->client[2]);
+ dev_err(&client->dev, "2nd I2C registration failed\n");
+ goto err_regmap_1_regmap_exit;
+ }
+ dev->regmap[2] = regmap_init_i2c(dev->client[2], &regmap_config);
+ if (IS_ERR(dev->regmap[2])) {
+ ret = PTR_ERR(dev->regmap[2]);
+ goto err_client_2_i2c_unregister_device;
+ }
+ i2c_set_clientdata(dev->client[2], dev);
+
+ /* Check demod answers with correct chip id */
+ ret = regmap_read(dev->regmap[2], 0xff, &uitmp);
+ if (ret)
+ goto err_regmap_2_regmap_exit;
+
+ dev_dbg(&client->dev, "chip id=%02x\n", uitmp);
+
+ if (uitmp != 0x03) {
+ ret = -ENODEV;
+ goto err_regmap_2_regmap_exit;
+ }
+
+ /* Sleep because chip is active by default */
+ ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
+ if (ret)
+ goto err_regmap_2_regmap_exit;
+
+ /* Create dvb frontend */
+ memcpy(&dev->frontend.ops, &mn88473_ops, sizeof(dev->frontend.ops));
+ dev->frontend.demodulator_priv = client;
+ *config->fe = &dev->frontend;
+ i2c_set_clientdata(client, dev);
+
+ dev_info(&client->dev, "Panasonic MN88473 successfully identified\n");
+
+ return 0;
+err_regmap_2_regmap_exit:
+ regmap_exit(dev->regmap[2]);
+err_client_2_i2c_unregister_device:
+ i2c_unregister_device(dev->client[2]);
+err_regmap_1_regmap_exit:
+ regmap_exit(dev->regmap[1]);
+err_client_1_i2c_unregister_device:
+ i2c_unregister_device(dev->client[1]);
+err_regmap_0_regmap_exit:
+ regmap_exit(dev->regmap[0]);
+err_kfree:
+ kfree(dev);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static void mn88473_remove(struct i2c_client *client)
+{
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ regmap_exit(dev->regmap[2]);
+ i2c_unregister_device(dev->client[2]);
+
+ regmap_exit(dev->regmap[1]);
+ i2c_unregister_device(dev->client[1]);
+
+ regmap_exit(dev->regmap[0]);
+
+ kfree(dev);
+}
+
+static const struct i2c_device_id mn88473_id_table[] = {
+ { "mn88473" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mn88473_id_table);
+
+static struct i2c_driver mn88473_driver = {
+ .driver = {
+ .name = "mn88473",
+ .suppress_bind_attrs = true,
+ },
+ .probe = mn88473_probe,
+ .remove = mn88473_remove,
+ .id_table = mn88473_id_table,
+};
+
+module_i2c_driver(mn88473_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Panasonic MN88473 DVB-T/T2/C demodulator driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(MN88473_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/mn88473.h b/drivers/media/dvb-frontends/mn88473.h
new file mode 100644
index 000000000000..b8b75c8cfda0
--- /dev/null
+++ b/drivers/media/dvb-frontends/mn88473.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Panasonic MN88473 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ */
+
+#ifndef MN88473_H
+#define MN88473_H
+
+#include <linux/dvb/frontend.h>
+
+struct mn88473_config {
+ /*
+ * Max num of bytes given I2C adapter could write at once.
+ * Default: unlimited
+ */
+ u16 i2c_wr_max;
+
+ /*
+ * Xtal frequency Hz.
+ * Default: 25000000
+ */
+ u32 xtal;
+
+
+ /* Everything after that is returned by the driver. */
+
+ /*
+ * DVB frontend.
+ */
+ struct dvb_frontend **fe;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/mn88473_priv.h b/drivers/media/dvb-frontends/mn88473_priv.h
new file mode 100644
index 000000000000..e9daaacfa22f
--- /dev/null
+++ b/drivers/media/dvb-frontends/mn88473_priv.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Panasonic MN88473 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ */
+
+#ifndef MN88473_PRIV_H
+#define MN88473_PRIV_H
+
+#include <media/dvb_frontend.h>
+#include <linux/int_log.h>
+#include "mn88473.h"
+#include <linux/math64.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+
+#define MN88473_FIRMWARE "dvb-demod-mn88473-01.fw"
+
+struct mn88473_dev {
+ struct i2c_client *client[3];
+ struct regmap *regmap[3];
+ struct dvb_frontend frontend;
+ u16 i2c_wr_max;
+ bool active;
+ u32 clk;
+};
+
+#endif
diff --git a/drivers/media/dvb/frontends/mt312.c b/drivers/media/dvb-frontends/mt312.c
index e20bf13aa860..fb867dd8a26b 100644
--- a/drivers/media/dvb/frontends/mt312.c
+++ b/drivers/media/dvb-frontends/mt312.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Driver for Zarlink VP310/MT312/ZL10313 Satellite Channel Decoder
Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org>
Copyright (C) 2008 Matthias Schwarzott <zzam@gentoo.org>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
References:
http://products.zarlink.com/product_profiles/MT312.htm
@@ -32,10 +19,12 @@
#include <linux/string.h>
#include <linux/slab.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "mt312_priv.h"
#include "mt312.h"
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE 64
struct mt312_state {
struct i2c_adapter *i2c;
@@ -96,9 +85,15 @@ static int mt312_write(struct mt312_state *state, const enum mt312_reg_addr reg,
const u8 *src, const size_t count)
{
int ret;
- u8 buf[count + 1];
+ u8 buf[MAX_XFER_SIZE];
struct i2c_msg msg;
+ if (1 + count > sizeof(buf)) {
+ printk(KERN_WARNING
+ "mt312: write: len=%zu is too big!\n", count);
+ return -EINVAL;
+ }
+
if (debug) {
int i;
dprintk("W(%d):", reg & 0x7f);
@@ -134,12 +129,10 @@ static inline int mt312_readreg(struct mt312_state *state,
static inline int mt312_writereg(struct mt312_state *state,
const enum mt312_reg_addr reg, const u8 val)
{
- return mt312_write(state, reg, &val, 1);
-}
+ u8 tmp = val; /* see gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 */
-static inline u32 mt312_div(u32 a, u32 b)
-{
- return (a + (b / 2)) / b;
+
+ return mt312_write(state, reg, &tmp, 1);
}
static int mt312_reset(struct mt312_state *state, const u8 full)
@@ -148,7 +141,7 @@ static int mt312_reset(struct mt312_state *state, const u8 full)
}
static int mt312_get_inversion(struct mt312_state *state,
- fe_spectral_inversion_t *i)
+ enum fe_spectral_inversion *i)
{
int ret;
u8 vit_mode;
@@ -189,7 +182,7 @@ static int mt312_get_symbol_rate(struct mt312_state *state, u32 *sr)
monitor = (buf[0] << 8) | buf[1];
dprintk("sr(auto) = %u\n",
- mt312_div(monitor * 15625, 4));
+ DIV_ROUND_CLOSEST(monitor * 15625, 4));
} else {
ret = mt312_writereg(state, MON_CTRL, 0x05);
if (ret < 0)
@@ -217,9 +210,9 @@ static int mt312_get_symbol_rate(struct mt312_state *state, u32 *sr)
return 0;
}
-static int mt312_get_code_rate(struct mt312_state *state, fe_code_rate_t *cr)
+static int mt312_get_code_rate(struct mt312_state *state, enum fe_code_rate *cr)
{
- const fe_code_rate_t fec_tab[8] =
+ const enum fe_code_rate fec_tab[8] =
{ FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8,
FEC_AUTO, FEC_AUTO };
@@ -293,10 +286,10 @@ static int mt312_initfe(struct dvb_frontend *fe)
}
/* SYS_CLK */
- buf[0] = mt312_div(state->xtal * state->freq_mult * 2, 1000000);
+ buf[0] = DIV_ROUND_CLOSEST(state->xtal * state->freq_mult * 2, 1000000);
/* DISEQC_RATIO */
- buf[1] = mt312_div(state->xtal, 22000 * 4);
+ buf[1] = DIV_ROUND_CLOSEST(state->xtal, 22000 * 4);
ret = mt312_write(state, SYS_CLK, buf, sizeof(buf));
if (ret < 0)
@@ -372,7 +365,8 @@ static int mt312_send_master_cmd(struct dvb_frontend *fe,
return 0;
}
-static int mt312_send_burst(struct dvb_frontend *fe, const fe_sec_mini_cmd_t c)
+static int mt312_send_burst(struct dvb_frontend *fe,
+ const enum fe_sec_mini_cmd c)
{
struct mt312_state *state = fe->demodulator_priv;
const u8 mini_tab[2] = { 0x02, 0x03 };
@@ -395,7 +389,8 @@ static int mt312_send_burst(struct dvb_frontend *fe, const fe_sec_mini_cmd_t c)
return 0;
}
-static int mt312_set_tone(struct dvb_frontend *fe, const fe_sec_tone_mode_t t)
+static int mt312_set_tone(struct dvb_frontend *fe,
+ const enum fe_sec_tone_mode t)
{
struct mt312_state *state = fe->demodulator_priv;
const u8 tone_tab[2] = { 0x01, 0x00 };
@@ -418,7 +413,8 @@ static int mt312_set_tone(struct dvb_frontend *fe, const fe_sec_tone_mode_t t)
return 0;
}
-static int mt312_set_voltage(struct dvb_frontend *fe, const fe_sec_voltage_t v)
+static int mt312_set_voltage(struct dvb_frontend *fe,
+ const enum fe_sec_voltage v)
{
struct mt312_state *state = fe->demodulator_priv;
const u8 volt_tab[3] = { 0x00, 0x40, 0x00 };
@@ -434,7 +430,7 @@ static int mt312_set_voltage(struct dvb_frontend *fe, const fe_sec_voltage_t v)
return mt312_writereg(state, DISEQC_MODE, val);
}
-static int mt312_read_status(struct dvb_frontend *fe, fe_status_t *s)
+static int mt312_read_status(struct dvb_frontend *fe, enum fe_status *s)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
@@ -446,8 +442,8 @@ static int mt312_read_status(struct dvb_frontend *fe, fe_status_t *s)
if (ret < 0)
return ret;
- dprintk("QPSK_STAT_H: 0x%02x, QPSK_STAT_L: 0x%02x,"
- " FEC_STATUS: 0x%02x\n", status[0], status[1], status[2]);
+ dprintk("QPSK_STAT_H: 0x%02x, QPSK_STAT_L: 0x%02x, FEC_STATUS: 0x%02x\n",
+ status[0], status[1], status[2]);
if (status[0] & 0xc0)
*s |= FE_HAS_SIGNAL; /* signal noise ratio */
@@ -545,11 +541,11 @@ static int mt312_set_frontend(struct dvb_frontend *fe)
dprintk("%s: Freq %d\n", __func__, p->frequency);
- if ((p->frequency < fe->ops.info.frequency_min)
- || (p->frequency > fe->ops.info.frequency_max))
+ if ((p->frequency < fe->ops.info.frequency_min_hz / kHz)
+ || (p->frequency > fe->ops.info.frequency_max_hz / kHz))
return -EINVAL;
- if ((p->inversion < INVERSION_OFF)
+ if (((int)p->inversion < INVERSION_OFF)
|| (p->inversion > INVERSION_ON))
return -EINVAL;
@@ -557,7 +553,7 @@ static int mt312_set_frontend(struct dvb_frontend *fe)
|| (p->symbol_rate > fe->ops.info.symbol_rate_max))
return -EINVAL;
- if ((p->fec_inner < FEC_NONE)
+ if (((int)p->fec_inner < FEC_NONE)
|| (p->fec_inner > FEC_AUTO))
return -EINVAL;
@@ -609,7 +605,7 @@ static int mt312_set_frontend(struct dvb_frontend *fe)
}
/* sr = (u16)(sr * 256.0 / 1000000.0) */
- sr = mt312_div(p->symbol_rate * 4, 15625);
+ sr = DIV_ROUND_CLOSEST(p->symbol_rate * 4, 15625);
/* SYM_RATE */
buf[0] = (sr >> 8) & 0x3f;
@@ -631,14 +627,16 @@ static int mt312_set_frontend(struct dvb_frontend *fe)
if (ret < 0)
return ret;
- mt312_reset(state, 0);
+ ret = mt312_reset(state, 0);
+ if (ret < 0)
+ return ret;
return 0;
}
-static int mt312_get_frontend(struct dvb_frontend *fe)
+static int mt312_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct mt312_state *state = fe->demodulator_priv;
int ret;
@@ -737,14 +735,14 @@ static void mt312_release(struct dvb_frontend *fe)
}
#define MT312_SYS_CLK 90000000UL /* 90 MHz */
-static struct dvb_frontend_ops mt312_ops = {
+static const struct dvb_frontend_ops mt312_ops = {
.delsys = { SYS_DVBS },
.info = {
.name = "Zarlink ???? DVB-S",
- .frequency_min = 950000,
- .frequency_max = 2150000,
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
/* FIXME: adjust freq to real used xtal */
- .frequency_stepsize = (MT312_PLL_CLK / 1000) / 128,
+ .frequency_stepsize_hz = MT312_PLL_CLK / 128,
.symbol_rate_min = MT312_SYS_CLK / 128, /* FIXME as above */
.symbol_rate_max = MT312_SYS_CLK / 2,
.caps =
@@ -801,23 +799,25 @@ struct dvb_frontend *mt312_attach(const struct mt312_config *config,
switch (state->id) {
case ID_VP310:
- strcpy(state->frontend.ops.info.name, "Zarlink VP310 DVB-S");
+ strscpy(state->frontend.ops.info.name, "Zarlink VP310 DVB-S",
+ sizeof(state->frontend.ops.info.name));
state->xtal = MT312_PLL_CLK;
state->freq_mult = 9;
break;
case ID_MT312:
- strcpy(state->frontend.ops.info.name, "Zarlink MT312 DVB-S");
+ strscpy(state->frontend.ops.info.name, "Zarlink MT312 DVB-S",
+ sizeof(state->frontend.ops.info.name));
state->xtal = MT312_PLL_CLK;
state->freq_mult = 6;
break;
case ID_ZL10313:
- strcpy(state->frontend.ops.info.name, "Zarlink ZL10313 DVB-S");
+ strscpy(state->frontend.ops.info.name, "Zarlink ZL10313 DVB-S",
+ sizeof(state->frontend.ops.info.name));
state->xtal = MT312_PLL_CLK_10_111;
state->freq_mult = 9;
break;
default:
- printk(KERN_WARNING "Only Zarlink VP310/MT312/ZL10313"
- " are supported chips.\n");
+ printk(KERN_WARNING "Only Zarlink VP310/MT312/ZL10313 are supported chips.\n");
goto error;
}
@@ -827,7 +827,7 @@ error:
kfree(state);
return NULL;
}
-EXPORT_SYMBOL(mt312_attach);
+EXPORT_SYMBOL_GPL(mt312_attach);
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
diff --git a/drivers/media/dvb-frontends/mt312.h b/drivers/media/dvb-frontends/mt312.h
new file mode 100644
index 000000000000..21828513f84e
--- /dev/null
+++ b/drivers/media/dvb-frontends/mt312.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Driver for Zarlink MT312 Satellite Channel Decoder
+
+ Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org>
+
+
+ References:
+ http://products.zarlink.com/product_profiles/MT312.htm
+ http://products.zarlink.com/product_profiles/SL1935.htm
+*/
+
+#ifndef MT312_H
+#define MT312_H
+
+#include <linux/dvb/frontend.h>
+
+struct mt312_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* inverted voltage setting */
+ unsigned int voltage_inverted:1;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_MT312)
+struct dvb_frontend *mt312_attach(const struct mt312_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *mt312_attach(
+ const struct mt312_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_MT312 */
+
+#endif /* MT312_H */
diff --git a/drivers/media/dvb/frontends/mt312_priv.h b/drivers/media/dvb-frontends/mt312_priv.h
index a3959f94d639..4582b15c9931 100644
--- a/drivers/media/dvb/frontends/mt312_priv.h
+++ b/drivers/media/dvb-frontends/mt312_priv.h
@@ -1,22 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
Driver for Zarlink MT312 QPSK Frontend
Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
diff --git a/drivers/media/dvb/frontends/mt352.c b/drivers/media/dvb-frontends/mt352.c
index 2c3b50e828d7..1b2889f5cf67 100644
--- a/drivers/media/dvb/frontends/mt352.c
+++ b/drivers/media/dvb-frontends/mt352.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Zarlink DVB-T MT352 demodulator
*
@@ -13,21 +14,6 @@
*
* DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by
* Christopher Pascoe <c.pascoe@itee.uq.edu.au>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
*/
#include <linux/kernel.h>
@@ -37,7 +23,7 @@
#include <linux/string.h>
#include <linux/slab.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "mt352_priv.h"
#include "mt352.h"
@@ -215,6 +201,7 @@ static int mt352_set_parameters(struct dvb_frontend *fe)
if (op->hierarchy == HIERARCHY_AUTO ||
op->hierarchy == HIERARCHY_NONE)
break;
+ fallthrough;
default:
return -EINVAL;
}
@@ -311,9 +298,9 @@ static int mt352_set_parameters(struct dvb_frontend *fe)
return 0;
}
-static int mt352_get_parameters(struct dvb_frontend* fe)
+static int mt352_get_parameters(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *op)
{
- struct dtv_frontend_properties *op = &fe->dtv_property_cache;
struct mt352_state* state = fe->demodulator_priv;
u16 tps;
u16 div;
@@ -417,7 +404,7 @@ static int mt352_get_parameters(struct dvb_frontend* fe)
return 0;
}
-static int mt352_read_status(struct dvb_frontend* fe, fe_status_t* status)
+static int mt352_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct mt352_state* state = fe->demodulator_priv;
int s0, s1, s3;
@@ -538,7 +525,7 @@ static void mt352_release(struct dvb_frontend* fe)
kfree(state);
}
-static struct dvb_frontend_ops mt352_ops;
+static const struct dvb_frontend_ops mt352_ops;
struct dvb_frontend* mt352_attach(const struct mt352_config* config,
struct i2c_adapter* i2c)
@@ -566,14 +553,13 @@ error:
return NULL;
}
-static struct dvb_frontend_ops mt352_ops = {
+static const struct dvb_frontend_ops mt352_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = "Zarlink MT352 DVB-T",
- .frequency_min = 174000000,
- .frequency_max = 862000000,
- .frequency_stepsize = 166667,
- .frequency_tolerance = 0,
+ .frequency_min_hz = 174 * MHz,
+ .frequency_max_hz = 862 * MHz,
+ .frequency_stepsize_hz = 166667,
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
FE_CAN_FEC_AUTO |
@@ -607,4 +593,4 @@ MODULE_DESCRIPTION("Zarlink MT352 DVB-T Demodulator driver");
MODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso");
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(mt352_attach);
+EXPORT_SYMBOL_GPL(mt352_attach);
diff --git a/drivers/media/dvb-frontends/mt352.h b/drivers/media/dvb-frontends/mt352.h
new file mode 100644
index 000000000000..acb1c33748f8
--- /dev/null
+++ b/drivers/media/dvb-frontends/mt352.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Zarlink DVB-T MT352 demodulator
+ *
+ * Written by Holger Waechtler <holger@qanu.de>
+ * and Daniel Mack <daniel@qanu.de>
+ *
+ * AVerMedia AVerTV DVB-T 771 support by
+ * Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ * Support for Samsung TDTC9251DH01C(M) tuner
+ * Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it>
+ * Amauri Celani <acelani@essegi.net>
+ *
+ * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by
+ * Christopher Pascoe <c.pascoe@itee.uq.edu.au>
+ */
+
+#ifndef MT352_H
+#define MT352_H
+
+#include <linux/dvb/frontend.h>
+
+struct mt352_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* frequencies in kHz */
+ int adc_clock; // default: 20480
+ int if2; // default: 36166
+
+ /* set if no pll is connected to the secondary i2c bus */
+ int no_tuner;
+
+ /* Initialise the demodulator and PLL. Cannot be NULL */
+ int (*demod_init)(struct dvb_frontend* fe);
+};
+
+#if IS_REACHABLE(CONFIG_DVB_MT352)
+extern struct dvb_frontend* mt352_attach(const struct mt352_config* config,
+ struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* mt352_attach(const struct mt352_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_MT352
+
+static inline int mt352_write(struct dvb_frontend *fe, const u8 buf[], int len) {
+ int r = 0;
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, len);
+ return r;
+}
+
+#endif // MT352_H
diff --git a/drivers/media/dvb/frontends/mt352_priv.h b/drivers/media/dvb-frontends/mt352_priv.h
index 44ad0d4c8f12..3ec4552efa8f 100644
--- a/drivers/media/dvb/frontends/mt352_priv.h
+++ b/drivers/media/dvb-frontends/mt352_priv.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Zarlink DVB-T MT352 demodulator
*
@@ -13,21 +14,6 @@
*
* DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by
* Christopher Pascoe <c.pascoe@itee.uq.edu.au>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
*/
#ifndef _MT352_PRIV_
diff --git a/drivers/media/dvb-frontends/mxl5xx.c b/drivers/media/dvb-frontends/mxl5xx.c
new file mode 100644
index 000000000000..930da176e5bf
--- /dev/null
+++ b/drivers/media/dvb-frontends/mxl5xx.c
@@ -0,0 +1,1885 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the MaxLinear MxL5xx family of tuners/demods
+ *
+ * Copyright (C) 2014-2015 Ralph Metzler <rjkm@metzlerbros.de>
+ * Marcus Metzler <mocm@metzlerbros.de>
+ * developed for Digital Devices GmbH
+ *
+ * based on code:
+ * Copyright (c) 2011-2013 MaxLinear, Inc. All rights reserved
+ * which was released under GPL V2
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/vmalloc.h>
+#include <asm/div64.h>
+#include <linux/unaligned.h>
+
+#include <media/dvb_frontend.h>
+#include "mxl5xx.h"
+#include "mxl5xx_regs.h"
+#include "mxl5xx_defs.h"
+
+#define BYTE0(v) ((v >> 0) & 0xff)
+#define BYTE1(v) ((v >> 8) & 0xff)
+#define BYTE2(v) ((v >> 16) & 0xff)
+#define BYTE3(v) ((v >> 24) & 0xff)
+
+static LIST_HEAD(mxllist);
+
+struct mxl_base {
+ struct list_head mxllist;
+ struct list_head mxls;
+
+ u8 adr;
+ struct i2c_adapter *i2c;
+
+ u32 count;
+ u32 type;
+ u32 sku_type;
+ u32 chipversion;
+ u32 clock;
+ u32 fwversion;
+
+ u8 *ts_map;
+ u8 can_clkout;
+ u8 chan_bond;
+ u8 demod_num;
+ u8 tuner_num;
+
+ unsigned long next_tune;
+
+ struct mutex i2c_lock;
+ struct mutex status_lock;
+ struct mutex tune_lock;
+
+ u8 buf[MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN];
+
+ u32 cmd_size;
+ u8 cmd_data[MAX_CMD_DATA];
+};
+
+struct mxl {
+ struct list_head mxl;
+
+ struct mxl_base *base;
+ struct dvb_frontend fe;
+ struct device *i2cdev;
+ u32 demod;
+ u32 tuner;
+ u32 tuner_in_use;
+ u8 xbar[3];
+
+ unsigned long tune_time;
+};
+
+static void convert_endian(u8 flag, u32 size, u8 *d)
+{
+ u32 i;
+
+ if (!flag)
+ return;
+ for (i = 0; i < (size & ~3); i += 4) {
+ d[i + 0] ^= d[i + 3];
+ d[i + 3] ^= d[i + 0];
+ d[i + 0] ^= d[i + 3];
+
+ d[i + 1] ^= d[i + 2];
+ d[i + 2] ^= d[i + 1];
+ d[i + 1] ^= d[i + 2];
+ }
+
+ switch (size & 3) {
+ case 0:
+ case 1:
+ /* do nothing */
+ break;
+ case 2:
+ d[i + 0] ^= d[i + 1];
+ d[i + 1] ^= d[i + 0];
+ d[i + 0] ^= d[i + 1];
+ break;
+
+ case 3:
+ d[i + 0] ^= d[i + 2];
+ d[i + 2] ^= d[i + 0];
+ d[i + 0] ^= d[i + 2];
+ break;
+ }
+
+}
+
+static int i2c_write(struct i2c_adapter *adap, u8 adr,
+ u8 *data, u32 len)
+{
+ struct i2c_msg msg = {.addr = adr, .flags = 0,
+ .buf = data, .len = len};
+
+ return (i2c_transfer(adap, &msg, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_read(struct i2c_adapter *adap, u8 adr,
+ u8 *data, u32 len)
+{
+ struct i2c_msg msg = {.addr = adr, .flags = I2C_M_RD,
+ .buf = data, .len = len};
+
+ return (i2c_transfer(adap, &msg, 1) == 1) ? 0 : -1;
+}
+
+static int i2cread(struct mxl *state, u8 *data, int len)
+{
+ return i2c_read(state->base->i2c, state->base->adr, data, len);
+}
+
+static int i2cwrite(struct mxl *state, u8 *data, int len)
+{
+ return i2c_write(state->base->i2c, state->base->adr, data, len);
+}
+
+static int read_register_unlocked(struct mxl *state, u32 reg, u32 *val)
+{
+ int stat;
+ u8 data[MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE] = {
+ MXL_HYDRA_PLID_REG_READ, 0x04,
+ GET_BYTE(reg, 0), GET_BYTE(reg, 1),
+ GET_BYTE(reg, 2), GET_BYTE(reg, 3),
+ };
+
+ stat = i2cwrite(state, data,
+ MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE);
+ if (stat)
+ dev_err(state->i2cdev, "i2c read error 1\n");
+ if (!stat)
+ stat = i2cread(state, (u8 *) val,
+ MXL_HYDRA_REG_SIZE_IN_BYTES);
+ le32_to_cpus(val);
+ if (stat)
+ dev_err(state->i2cdev, "i2c read error 2\n");
+ return stat;
+}
+
+#define DMA_I2C_INTERRUPT_ADDR 0x8000011C
+#define DMA_INTR_PROT_WR_CMP 0x08
+
+static int send_command(struct mxl *state, u32 size, u8 *buf)
+{
+ int stat;
+ u32 val, count = 10;
+
+ mutex_lock(&state->base->i2c_lock);
+ if (state->base->fwversion > 0x02010109) {
+ read_register_unlocked(state, DMA_I2C_INTERRUPT_ADDR, &val);
+ if (DMA_INTR_PROT_WR_CMP & val)
+ dev_info(state->i2cdev, "%s busy\n", __func__);
+ while ((DMA_INTR_PROT_WR_CMP & val) && --count) {
+ mutex_unlock(&state->base->i2c_lock);
+ usleep_range(1000, 2000);
+ mutex_lock(&state->base->i2c_lock);
+ read_register_unlocked(state, DMA_I2C_INTERRUPT_ADDR,
+ &val);
+ }
+ if (!count) {
+ dev_info(state->i2cdev, "%s busy\n", __func__);
+ mutex_unlock(&state->base->i2c_lock);
+ return -EBUSY;
+ }
+ }
+ stat = i2cwrite(state, buf, size);
+ mutex_unlock(&state->base->i2c_lock);
+ return stat;
+}
+
+static int write_register(struct mxl *state, u32 reg, u32 val)
+{
+ int stat;
+ u8 data[MXL_HYDRA_REG_WRITE_LEN] = {
+ MXL_HYDRA_PLID_REG_WRITE, 0x08,
+ BYTE0(reg), BYTE1(reg), BYTE2(reg), BYTE3(reg),
+ BYTE0(val), BYTE1(val), BYTE2(val), BYTE3(val),
+ };
+ mutex_lock(&state->base->i2c_lock);
+ stat = i2cwrite(state, data, sizeof(data));
+ mutex_unlock(&state->base->i2c_lock);
+ if (stat)
+ dev_err(state->i2cdev, "i2c write error\n");
+ return stat;
+}
+
+static int write_firmware_block(struct mxl *state,
+ u32 reg, u32 size, u8 *reg_data_ptr)
+{
+ int stat;
+ u8 *buf = state->base->buf;
+
+ mutex_lock(&state->base->i2c_lock);
+ buf[0] = MXL_HYDRA_PLID_REG_WRITE;
+ buf[1] = size + 4;
+ buf[2] = GET_BYTE(reg, 0);
+ buf[3] = GET_BYTE(reg, 1);
+ buf[4] = GET_BYTE(reg, 2);
+ buf[5] = GET_BYTE(reg, 3);
+ memcpy(&buf[6], reg_data_ptr, size);
+ stat = i2cwrite(state, buf,
+ MXL_HYDRA_I2C_HDR_SIZE +
+ MXL_HYDRA_REG_SIZE_IN_BYTES + size);
+ mutex_unlock(&state->base->i2c_lock);
+ if (stat)
+ dev_err(state->i2cdev, "fw block write failed\n");
+ return stat;
+}
+
+static int read_register(struct mxl *state, u32 reg, u32 *val)
+{
+ int stat;
+ u8 data[MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE] = {
+ MXL_HYDRA_PLID_REG_READ, 0x04,
+ GET_BYTE(reg, 0), GET_BYTE(reg, 1),
+ GET_BYTE(reg, 2), GET_BYTE(reg, 3),
+ };
+
+ mutex_lock(&state->base->i2c_lock);
+ stat = i2cwrite(state, data,
+ MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE);
+ if (stat)
+ dev_err(state->i2cdev, "i2c read error 1\n");
+ if (!stat)
+ stat = i2cread(state, (u8 *) val,
+ MXL_HYDRA_REG_SIZE_IN_BYTES);
+ mutex_unlock(&state->base->i2c_lock);
+ le32_to_cpus(val);
+ if (stat)
+ dev_err(state->i2cdev, "i2c read error 2\n");
+ return stat;
+}
+
+static int read_register_block(struct mxl *state, u32 reg, u32 size, u8 *data)
+{
+ int stat;
+ u8 *buf = state->base->buf;
+
+ mutex_lock(&state->base->i2c_lock);
+
+ buf[0] = MXL_HYDRA_PLID_REG_READ;
+ buf[1] = size + 4;
+ buf[2] = GET_BYTE(reg, 0);
+ buf[3] = GET_BYTE(reg, 1);
+ buf[4] = GET_BYTE(reg, 2);
+ buf[5] = GET_BYTE(reg, 3);
+ stat = i2cwrite(state, buf,
+ MXL_HYDRA_I2C_HDR_SIZE + MXL_HYDRA_REG_SIZE_IN_BYTES);
+ if (!stat) {
+ stat = i2cread(state, data, size);
+ convert_endian(MXL_ENABLE_BIG_ENDIAN, size, data);
+ }
+ mutex_unlock(&state->base->i2c_lock);
+ return stat;
+}
+
+static int read_by_mnemonic(struct mxl *state,
+ u32 reg, u8 lsbloc, u8 numofbits, u32 *val)
+{
+ u32 data = 0, mask = 0;
+ int stat;
+
+ stat = read_register(state, reg, &data);
+ if (stat)
+ return stat;
+ mask = MXL_GET_REG_MASK_32(lsbloc, numofbits);
+ data &= mask;
+ data >>= lsbloc;
+ *val = data;
+ return 0;
+}
+
+
+static int update_by_mnemonic(struct mxl *state,
+ u32 reg, u8 lsbloc, u8 numofbits, u32 val)
+{
+ u32 data, mask;
+ int stat;
+
+ stat = read_register(state, reg, &data);
+ if (stat)
+ return stat;
+ mask = MXL_GET_REG_MASK_32(lsbloc, numofbits);
+ data = (data & ~mask) | ((val << lsbloc) & mask);
+ stat = write_register(state, reg, data);
+ return stat;
+}
+
+static int firmware_is_alive(struct mxl *state)
+{
+ u32 hb0, hb1;
+
+ if (read_register(state, HYDRA_HEAR_BEAT, &hb0))
+ return 0;
+ msleep(20);
+ if (read_register(state, HYDRA_HEAR_BEAT, &hb1))
+ return 0;
+ if (hb1 == hb0)
+ return 0;
+ return 1;
+}
+
+static int init(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ /* init fe stats */
+ p->strength.len = 1;
+ p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->cnr.len = 1;
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_error.len = 1;
+ p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_count.len = 1;
+ p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.len = 1;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.len = 1;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ return 0;
+}
+
+static void release(struct dvb_frontend *fe)
+{
+ struct mxl *state = fe->demodulator_priv;
+
+ list_del(&state->mxl);
+ /* Release one frontend, two more shall take its place! */
+ state->base->count--;
+ if (state->base->count == 0) {
+ list_del(&state->base->mxllist);
+ kfree(state->base);
+ }
+ kfree(state);
+}
+
+static enum dvbfe_algo get_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
+static u32 gold2root(u32 gold)
+{
+ u32 x, g, tmp = gold;
+
+ if (tmp >= 0x3ffff)
+ tmp = 0;
+ for (g = 0, x = 1; g < tmp; g++)
+ x = (((x ^ (x >> 7)) & 1) << 17) | (x >> 1);
+ return x;
+}
+
+static int cfg_scrambler(struct mxl *state, u32 gold)
+{
+ u32 root;
+ u8 buf[26] = {
+ MXL_HYDRA_PLID_CMD_WRITE, 24,
+ 0, MXL_HYDRA_DEMOD_SCRAMBLE_CODE_CMD, 0, 0,
+ state->demod, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0, 0, 0,
+ };
+
+ root = gold2root(gold);
+
+ buf[25] = (root >> 24) & 0xff;
+ buf[24] = (root >> 16) & 0xff;
+ buf[23] = (root >> 8) & 0xff;
+ buf[22] = root & 0xff;
+
+ return send_command(state, sizeof(buf), buf);
+}
+
+static int cfg_demod_abort_tune(struct mxl *state)
+{
+ struct MXL_HYDRA_DEMOD_ABORT_TUNE_T abort_tune_cmd;
+ u8 cmd_size = sizeof(abort_tune_cmd);
+ u8 cmd_buff[MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN];
+
+ abort_tune_cmd.demod_id = state->demod;
+ BUILD_HYDRA_CMD(MXL_HYDRA_ABORT_TUNE_CMD, MXL_CMD_WRITE,
+ cmd_size, &abort_tune_cmd, cmd_buff);
+ return send_command(state, cmd_size + MXL_HYDRA_CMD_HEADER_SIZE,
+ &cmd_buff[0]);
+}
+
+static int send_master_cmd(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *cmd)
+{
+ /*struct mxl *state = fe->demodulator_priv;*/
+
+ return 0; /*CfgDemodAbortTune(state);*/
+}
+
+static int set_parameters(struct dvb_frontend *fe)
+{
+ struct mxl *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct MXL_HYDRA_DEMOD_PARAM_T demod_chan_cfg;
+ u8 cmd_size = sizeof(demod_chan_cfg);
+ u8 cmd_buff[MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN];
+ u32 srange = 10;
+ int stat;
+
+ if (p->frequency < 950000 || p->frequency > 2150000)
+ return -EINVAL;
+ if (p->symbol_rate < 1000000 || p->symbol_rate > 45000000)
+ return -EINVAL;
+
+ /* CfgDemodAbortTune(state); */
+
+ switch (p->delivery_system) {
+ case SYS_DSS:
+ demod_chan_cfg.standard = MXL_HYDRA_DSS;
+ demod_chan_cfg.roll_off = MXL_HYDRA_ROLLOFF_AUTO;
+ break;
+ case SYS_DVBS:
+ srange = p->symbol_rate / 1000000;
+ if (srange > 10)
+ srange = 10;
+ demod_chan_cfg.standard = MXL_HYDRA_DVBS;
+ demod_chan_cfg.roll_off = MXL_HYDRA_ROLLOFF_0_35;
+ demod_chan_cfg.modulation_scheme = MXL_HYDRA_MOD_QPSK;
+ demod_chan_cfg.pilots = MXL_HYDRA_PILOTS_OFF;
+ break;
+ case SYS_DVBS2:
+ demod_chan_cfg.standard = MXL_HYDRA_DVBS2;
+ demod_chan_cfg.roll_off = MXL_HYDRA_ROLLOFF_AUTO;
+ demod_chan_cfg.modulation_scheme = MXL_HYDRA_MOD_AUTO;
+ demod_chan_cfg.pilots = MXL_HYDRA_PILOTS_AUTO;
+ cfg_scrambler(state, p->scrambling_sequence_index);
+ break;
+ default:
+ return -EINVAL;
+ }
+ demod_chan_cfg.tuner_index = state->tuner;
+ demod_chan_cfg.demod_index = state->demod;
+ demod_chan_cfg.frequency_in_hz = p->frequency * 1000;
+ demod_chan_cfg.symbol_rate_in_hz = p->symbol_rate;
+ demod_chan_cfg.max_carrier_offset_in_mhz = srange;
+ demod_chan_cfg.spectrum_inversion = MXL_HYDRA_SPECTRUM_AUTO;
+ demod_chan_cfg.fec_code_rate = MXL_HYDRA_FEC_AUTO;
+
+ mutex_lock(&state->base->tune_lock);
+ if (time_after(jiffies + msecs_to_jiffies(200),
+ state->base->next_tune))
+ while (time_before(jiffies, state->base->next_tune))
+ usleep_range(10000, 11000);
+ state->base->next_tune = jiffies + msecs_to_jiffies(100);
+ state->tuner_in_use = state->tuner;
+ BUILD_HYDRA_CMD(MXL_HYDRA_DEMOD_SET_PARAM_CMD, MXL_CMD_WRITE,
+ cmd_size, &demod_chan_cfg, cmd_buff);
+ stat = send_command(state, cmd_size + MXL_HYDRA_CMD_HEADER_SIZE,
+ &cmd_buff[0]);
+ mutex_unlock(&state->base->tune_lock);
+ return stat;
+}
+
+static int enable_tuner(struct mxl *state, u32 tuner, u32 enable);
+
+static int sleep(struct dvb_frontend *fe)
+{
+ struct mxl *state = fe->demodulator_priv;
+ struct mxl *p;
+
+ cfg_demod_abort_tune(state);
+ if (state->tuner_in_use != 0xffffffff) {
+ mutex_lock(&state->base->tune_lock);
+ state->tuner_in_use = 0xffffffff;
+ list_for_each_entry(p, &state->base->mxls, mxl) {
+ if (p->tuner_in_use == state->tuner)
+ break;
+ }
+ if (&p->mxl == &state->base->mxls)
+ enable_tuner(state, state->tuner, 0);
+ mutex_unlock(&state->base->tune_lock);
+ }
+ return 0;
+}
+
+static int read_snr(struct dvb_frontend *fe)
+{
+ struct mxl *state = fe->demodulator_priv;
+ int stat;
+ u32 reg_data = 0;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ mutex_lock(&state->base->status_lock);
+ HYDRA_DEMOD_STATUS_LOCK(state, state->demod);
+ stat = read_register(state, (HYDRA_DMD_SNR_ADDR_OFFSET +
+ HYDRA_DMD_STATUS_OFFSET(state->demod)),
+ &reg_data);
+ HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod);
+ mutex_unlock(&state->base->status_lock);
+
+ p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ p->cnr.stat[0].svalue = (s16)reg_data * 10;
+
+ return stat;
+}
+
+static int read_ber(struct dvb_frontend *fe)
+{
+ struct mxl *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u32 reg[8];
+
+ mutex_lock(&state->base->status_lock);
+ HYDRA_DEMOD_STATUS_LOCK(state, state->demod);
+ read_register_block(state,
+ (HYDRA_DMD_DVBS_1ST_CORR_RS_ERRORS_ADDR_OFFSET +
+ HYDRA_DMD_STATUS_OFFSET(state->demod)),
+ (4 * sizeof(u32)),
+ (u8 *) &reg[0]);
+ HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod);
+
+ switch (p->delivery_system) {
+ case SYS_DSS:
+ case SYS_DVBS:
+ p->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->pre_bit_error.stat[0].uvalue = reg[2];
+ p->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->pre_bit_count.stat[0].uvalue = reg[3];
+ break;
+ default:
+ break;
+ }
+
+ read_register_block(state,
+ (HYDRA_DMD_DVBS2_CRC_ERRORS_ADDR_OFFSET +
+ HYDRA_DMD_STATUS_OFFSET(state->demod)),
+ (7 * sizeof(u32)),
+ (u8 *) &reg[0]);
+
+ switch (p->delivery_system) {
+ case SYS_DSS:
+ case SYS_DVBS:
+ p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_error.stat[0].uvalue = reg[5];
+ p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_count.stat[0].uvalue = reg[6];
+ break;
+ case SYS_DVBS2:
+ p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_error.stat[0].uvalue = reg[1];
+ p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_count.stat[0].uvalue = reg[2];
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&state->base->status_lock);
+
+ return 0;
+}
+
+static int read_signal_strength(struct dvb_frontend *fe)
+{
+ struct mxl *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ int stat;
+ u32 reg_data = 0;
+
+ mutex_lock(&state->base->status_lock);
+ HYDRA_DEMOD_STATUS_LOCK(state, state->demod);
+ stat = read_register(state, (HYDRA_DMD_STATUS_INPUT_POWER_ADDR +
+ HYDRA_DMD_STATUS_OFFSET(state->demod)),
+ &reg_data);
+ HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod);
+ mutex_unlock(&state->base->status_lock);
+
+ p->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ p->strength.stat[0].svalue = (s16) reg_data * 10; /* fix scale */
+
+ return stat;
+}
+
+static int read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct mxl *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u32 reg_data = 0;
+
+ mutex_lock(&state->base->status_lock);
+ HYDRA_DEMOD_STATUS_LOCK(state, state->demod);
+ read_register(state, (HYDRA_DMD_LOCK_STATUS_ADDR_OFFSET +
+ HYDRA_DMD_STATUS_OFFSET(state->demod)),
+ &reg_data);
+ HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod);
+ mutex_unlock(&state->base->status_lock);
+
+ *status = (reg_data == 1) ? 0x1f : 0;
+
+ /* signal statistics */
+
+ /* signal strength is always available */
+ read_signal_strength(fe);
+
+ if (*status & FE_HAS_CARRIER)
+ read_snr(fe);
+ else
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ if (*status & FE_HAS_SYNC)
+ read_ber(fe);
+ else {
+ p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ return 0;
+}
+
+static int tune(struct dvb_frontend *fe, bool re_tune,
+ unsigned int mode_flags,
+ unsigned int *delay, enum fe_status *status)
+{
+ struct mxl *state = fe->demodulator_priv;
+ int r = 0;
+
+ *delay = HZ / 2;
+ if (re_tune) {
+ r = set_parameters(fe);
+ if (r)
+ return r;
+ state->tune_time = jiffies;
+ }
+
+ return read_status(fe, status);
+}
+
+static enum fe_code_rate conv_fec(enum MXL_HYDRA_FEC_E fec)
+{
+ enum fe_code_rate fec2fec[11] = {
+ FEC_NONE, FEC_1_2, FEC_3_5, FEC_2_3,
+ FEC_3_4, FEC_4_5, FEC_5_6, FEC_6_7,
+ FEC_7_8, FEC_8_9, FEC_9_10
+ };
+
+ if (fec > MXL_HYDRA_FEC_9_10)
+ return FEC_NONE;
+ return fec2fec[fec];
+}
+
+static int get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
+{
+ struct mxl *state = fe->demodulator_priv;
+ u32 reg_data[MXL_DEMOD_CHAN_PARAMS_BUFF_SIZE];
+ u32 freq;
+
+ mutex_lock(&state->base->status_lock);
+ HYDRA_DEMOD_STATUS_LOCK(state, state->demod);
+ read_register_block(state,
+ (HYDRA_DMD_STANDARD_ADDR_OFFSET +
+ HYDRA_DMD_STATUS_OFFSET(state->demod)),
+ (MXL_DEMOD_CHAN_PARAMS_BUFF_SIZE * 4), /* 25 * 4 bytes */
+ (u8 *) &reg_data[0]);
+ /* read demod channel parameters */
+ read_register_block(state,
+ (HYDRA_DMD_STATUS_CENTER_FREQ_IN_KHZ_ADDR +
+ HYDRA_DMD_STATUS_OFFSET(state->demod)),
+ (4), /* 4 bytes */
+ (u8 *) &freq);
+ HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod);
+ mutex_unlock(&state->base->status_lock);
+
+ dev_dbg(state->i2cdev, "freq=%u delsys=%u srate=%u\n",
+ freq * 1000, reg_data[DMD_STANDARD_ADDR],
+ reg_data[DMD_SYMBOL_RATE_ADDR]);
+ p->symbol_rate = reg_data[DMD_SYMBOL_RATE_ADDR];
+ p->frequency = freq;
+ /*
+ * p->delivery_system =
+ * (MXL_HYDRA_BCAST_STD_E) regData[DMD_STANDARD_ADDR];
+ * p->inversion =
+ * (MXL_HYDRA_SPECTRUM_E) regData[DMD_SPECTRUM_INVERSION_ADDR];
+ * freqSearchRangeKHz =
+ * (regData[DMD_FREQ_SEARCH_RANGE_IN_KHZ_ADDR]);
+ */
+
+ p->fec_inner = conv_fec(reg_data[DMD_FEC_CODE_RATE_ADDR]);
+ switch (p->delivery_system) {
+ case SYS_DSS:
+ break;
+ case SYS_DVBS2:
+ switch ((enum MXL_HYDRA_PILOTS_E)
+ reg_data[DMD_DVBS2_PILOT_ON_OFF_ADDR]) {
+ case MXL_HYDRA_PILOTS_OFF:
+ p->pilot = PILOT_OFF;
+ break;
+ case MXL_HYDRA_PILOTS_ON:
+ p->pilot = PILOT_ON;
+ break;
+ default:
+ break;
+ }
+ fallthrough;
+ case SYS_DVBS:
+ switch ((enum MXL_HYDRA_MODULATION_E)
+ reg_data[DMD_MODULATION_SCHEME_ADDR]) {
+ case MXL_HYDRA_MOD_QPSK:
+ p->modulation = QPSK;
+ break;
+ case MXL_HYDRA_MOD_8PSK:
+ p->modulation = PSK_8;
+ break;
+ default:
+ break;
+ }
+ switch ((enum MXL_HYDRA_ROLLOFF_E)
+ reg_data[DMD_SPECTRUM_ROLL_OFF_ADDR]) {
+ case MXL_HYDRA_ROLLOFF_0_20:
+ p->rolloff = ROLLOFF_20;
+ break;
+ case MXL_HYDRA_ROLLOFF_0_35:
+ p->rolloff = ROLLOFF_35;
+ break;
+ case MXL_HYDRA_ROLLOFF_0_25:
+ p->rolloff = ROLLOFF_25;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int set_input(struct dvb_frontend *fe, int input)
+{
+ struct mxl *state = fe->demodulator_priv;
+
+ state->tuner = input;
+ return 0;
+}
+
+static const struct dvb_frontend_ops mxl_ops = {
+ .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS },
+ .info = {
+ .name = "MaxLinear MxL5xx DVB-S/S2 tuner-demodulator",
+ .frequency_min_hz = 300 * MHz,
+ .frequency_max_hz = 2350 * MHz,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_2G_MODULATION
+ },
+ .init = init,
+ .release = release,
+ .get_frontend_algo = get_algo,
+ .tune = tune,
+ .read_status = read_status,
+ .sleep = sleep,
+ .get_frontend = get_frontend,
+ .diseqc_send_master_cmd = send_master_cmd,
+};
+
+static struct mxl_base *match_base(struct i2c_adapter *i2c, u8 adr)
+{
+ struct mxl_base *p;
+
+ list_for_each_entry(p, &mxllist, mxllist)
+ if (p->i2c == i2c && p->adr == adr)
+ return p;
+ return NULL;
+}
+
+static void cfg_dev_xtal(struct mxl *state, u32 freq, u32 cap, u32 enable)
+{
+ if (state->base->can_clkout || !enable)
+ update_by_mnemonic(state, 0x90200054, 23, 1, enable);
+
+ if (freq == 24000000)
+ write_register(state, HYDRA_CRYSTAL_SETTING, 0);
+ else
+ write_register(state, HYDRA_CRYSTAL_SETTING, 1);
+
+ write_register(state, HYDRA_CRYSTAL_CAP, cap);
+}
+
+static u32 get_big_endian(u8 num_of_bits, const u8 buf[])
+{
+ u32 ret_value = 0;
+
+ switch (num_of_bits) {
+ case 24:
+ ret_value = (((u32) buf[0]) << 16) |
+ (((u32) buf[1]) << 8) | buf[2];
+ break;
+ case 32:
+ ret_value = (((u32) buf[0]) << 24) |
+ (((u32) buf[1]) << 16) |
+ (((u32) buf[2]) << 8) | buf[3];
+ break;
+ default:
+ break;
+ }
+
+ return ret_value;
+}
+
+static int write_fw_segment(struct mxl *state,
+ u32 mem_addr, u32 total_size, u8 *data_ptr)
+{
+ int status;
+ u32 data_count = 0;
+ u32 size = 0;
+ u32 orig_size = 0;
+ u8 *w_buf_ptr = NULL;
+ u32 block_size = ((MXL_HYDRA_OEM_MAX_BLOCK_WRITE_LENGTH -
+ (MXL_HYDRA_I2C_HDR_SIZE +
+ MXL_HYDRA_REG_SIZE_IN_BYTES)) / 4) * 4;
+ u8 w_msg_buffer[MXL_HYDRA_OEM_MAX_BLOCK_WRITE_LENGTH -
+ (MXL_HYDRA_I2C_HDR_SIZE + MXL_HYDRA_REG_SIZE_IN_BYTES)];
+
+ do {
+ size = orig_size = (((u32)(data_count + block_size)) > total_size) ?
+ (total_size - data_count) : block_size;
+
+ if (orig_size & 3)
+ size = (orig_size + 4) & ~3;
+ w_buf_ptr = &w_msg_buffer[0];
+ memset((void *) w_buf_ptr, 0, size);
+ memcpy((void *) w_buf_ptr, (void *) data_ptr, orig_size);
+ convert_endian(1, size, w_buf_ptr);
+ status = write_firmware_block(state, mem_addr, size, w_buf_ptr);
+ if (status)
+ return status;
+ data_count += size;
+ mem_addr += size;
+ data_ptr += size;
+ } while (data_count < total_size);
+
+ return status;
+}
+
+static int do_firmware_download(struct mxl *state, u8 *mbin_buffer_ptr,
+ u32 mbin_buffer_size)
+
+{
+ int status;
+ u32 index = 0;
+ u32 seg_length = 0;
+ u32 seg_address = 0;
+ struct MBIN_FILE_T *mbin_ptr = (struct MBIN_FILE_T *)mbin_buffer_ptr;
+ struct MBIN_SEGMENT_T *segment_ptr;
+ enum MXL_BOOL_E xcpu_fw_flag = MXL_FALSE;
+
+ if (mbin_ptr->header.id != MBIN_FILE_HEADER_ID) {
+ dev_err(state->i2cdev, "%s: Invalid file header ID (%c)\n",
+ __func__, mbin_ptr->header.id);
+ return -EINVAL;
+ }
+ status = write_register(state, FW_DL_SIGN_ADDR, 0);
+ if (status)
+ return status;
+ segment_ptr = (struct MBIN_SEGMENT_T *) (&mbin_ptr->data[0]);
+ for (index = 0; index < mbin_ptr->header.num_segments; index++) {
+ if (segment_ptr->header.id != MBIN_SEGMENT_HEADER_ID) {
+ dev_err(state->i2cdev, "%s: Invalid segment header ID (%c)\n",
+ __func__, segment_ptr->header.id);
+ return -EINVAL;
+ }
+ seg_length = get_big_endian(24,
+ &(segment_ptr->header.len24[0]));
+ seg_address = get_big_endian(32,
+ &(segment_ptr->header.address[0]));
+
+ if (state->base->type == MXL_HYDRA_DEVICE_568) {
+ if ((((seg_address & 0x90760000) == 0x90760000) ||
+ ((seg_address & 0x90740000) == 0x90740000)) &&
+ (xcpu_fw_flag == MXL_FALSE)) {
+ update_by_mnemonic(state, 0x8003003C, 0, 1, 1);
+ msleep(200);
+ write_register(state, 0x90720000, 0);
+ usleep_range(10000, 11000);
+ xcpu_fw_flag = MXL_TRUE;
+ }
+ status = write_fw_segment(state, seg_address,
+ seg_length,
+ (u8 *) segment_ptr->data);
+ } else {
+ if (((seg_address & 0x90760000) != 0x90760000) &&
+ ((seg_address & 0x90740000) != 0x90740000))
+ status = write_fw_segment(state, seg_address,
+ seg_length, (u8 *) segment_ptr->data);
+ }
+ if (status)
+ return status;
+ segment_ptr = (struct MBIN_SEGMENT_T *)
+ &(segment_ptr->data[((seg_length + 3) / 4) * 4]);
+ }
+ return status;
+}
+
+static int check_fw(struct mxl *state, u8 *mbin, u32 mbin_len)
+{
+ struct MBIN_FILE_HEADER_T *fh = (struct MBIN_FILE_HEADER_T *) mbin;
+ u32 flen = (fh->image_size24[0] << 16) |
+ (fh->image_size24[1] << 8) | fh->image_size24[2];
+ u8 *fw, cs = 0;
+ u32 i;
+
+ if (fh->id != 'M' || fh->fmt_version != '1' || flen > 0x3FFF0) {
+ dev_info(state->i2cdev, "Invalid FW Header\n");
+ return -1;
+ }
+ fw = mbin + sizeof(struct MBIN_FILE_HEADER_T);
+ for (i = 0; i < flen; i += 1)
+ cs += fw[i];
+ if (cs != fh->image_checksum) {
+ dev_info(state->i2cdev, "Invalid FW Checksum\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int firmware_download(struct mxl *state, u8 *mbin, u32 mbin_len)
+{
+ int status;
+ u32 reg_data = 0;
+ struct MXL_HYDRA_SKU_COMMAND_T dev_sku_cfg;
+ u8 cmd_size = sizeof(struct MXL_HYDRA_SKU_COMMAND_T);
+ u8 cmd_buff[sizeof(struct MXL_HYDRA_SKU_COMMAND_T) + 6];
+
+ if (check_fw(state, mbin, mbin_len))
+ return -1;
+
+ /* put CPU into reset */
+ status = update_by_mnemonic(state, 0x8003003C, 0, 1, 0);
+ if (status)
+ return status;
+ usleep_range(1000, 2000);
+
+ /* Reset TX FIFO's, BBAND, XBAR */
+ status = write_register(state, HYDRA_RESET_TRANSPORT_FIFO_REG,
+ HYDRA_RESET_TRANSPORT_FIFO_DATA);
+ if (status)
+ return status;
+ status = write_register(state, HYDRA_RESET_BBAND_REG,
+ HYDRA_RESET_BBAND_DATA);
+ if (status)
+ return status;
+ status = write_register(state, HYDRA_RESET_XBAR_REG,
+ HYDRA_RESET_XBAR_DATA);
+ if (status)
+ return status;
+
+ /* Disable clock to Baseband, Wideband, SerDes,
+ * Alias ext & Transport modules
+ */
+ status = write_register(state, HYDRA_MODULES_CLK_2_REG,
+ HYDRA_DISABLE_CLK_2);
+ if (status)
+ return status;
+ /* Clear Software & Host interrupt status - (Clear on read) */
+ status = read_register(state, HYDRA_PRCM_ROOT_CLK_REG, &reg_data);
+ if (status)
+ return status;
+ status = do_firmware_download(state, mbin, mbin_len);
+ if (status)
+ return status;
+
+ if (state->base->type == MXL_HYDRA_DEVICE_568) {
+ usleep_range(10000, 11000);
+
+ /* bring XCPU out of reset */
+ status = write_register(state, 0x90720000, 1);
+ if (status)
+ return status;
+ msleep(500);
+
+ /* Enable XCPU UART message processing in MCPU */
+ status = write_register(state, 0x9076B510, 1);
+ if (status)
+ return status;
+ } else {
+ /* Bring CPU out of reset */
+ status = update_by_mnemonic(state, 0x8003003C, 0, 1, 1);
+ if (status)
+ return status;
+ /* Wait until FW boots */
+ msleep(150);
+ }
+
+ /* Initialize XPT XBAR */
+ status = write_register(state, XPT_DMD0_BASEADDR, 0x76543210);
+ if (status)
+ return status;
+
+ if (!firmware_is_alive(state))
+ return -1;
+
+ dev_info(state->i2cdev, "Hydra FW alive. Hail!\n");
+
+ /* sometimes register values are wrong shortly
+ * after first heart beats
+ */
+ msleep(50);
+
+ dev_sku_cfg.sku_type = state->base->sku_type;
+ BUILD_HYDRA_CMD(MXL_HYDRA_DEV_CFG_SKU_CMD, MXL_CMD_WRITE,
+ cmd_size, &dev_sku_cfg, cmd_buff);
+ status = send_command(state, cmd_size + MXL_HYDRA_CMD_HEADER_SIZE,
+ &cmd_buff[0]);
+
+ return status;
+}
+
+static int cfg_ts_pad_mux(struct mxl *state, enum MXL_BOOL_E enable_serial_ts)
+{
+ int status = 0;
+ u32 pad_mux_value = 0;
+
+ if (enable_serial_ts == MXL_TRUE) {
+ pad_mux_value = 0;
+ if ((state->base->type == MXL_HYDRA_DEVICE_541) ||
+ (state->base->type == MXL_HYDRA_DEVICE_541S))
+ pad_mux_value = 2;
+ } else {
+ if ((state->base->type == MXL_HYDRA_DEVICE_581) ||
+ (state->base->type == MXL_HYDRA_DEVICE_581S))
+ pad_mux_value = 2;
+ else
+ pad_mux_value = 3;
+ }
+
+ switch (state->base->type) {
+ case MXL_HYDRA_DEVICE_561:
+ case MXL_HYDRA_DEVICE_581:
+ case MXL_HYDRA_DEVICE_541:
+ case MXL_HYDRA_DEVICE_541S:
+ case MXL_HYDRA_DEVICE_561S:
+ case MXL_HYDRA_DEVICE_581S:
+ status |= update_by_mnemonic(state, 0x90000170, 24, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000170, 28, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 0, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 4, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 8, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 12, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 16, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 20, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 24, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 28, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000178, 0, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000178, 4, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000178, 8, 3,
+ pad_mux_value);
+ break;
+
+ case MXL_HYDRA_DEVICE_544:
+ case MXL_HYDRA_DEVICE_542:
+ status |= update_by_mnemonic(state, 0x9000016C, 4, 3, 1);
+ status |= update_by_mnemonic(state, 0x9000016C, 8, 3, 0);
+ status |= update_by_mnemonic(state, 0x9000016C, 12, 3, 0);
+ status |= update_by_mnemonic(state, 0x9000016C, 16, 3, 0);
+ status |= update_by_mnemonic(state, 0x90000170, 0, 3, 0);
+ status |= update_by_mnemonic(state, 0x90000178, 12, 3, 1);
+ status |= update_by_mnemonic(state, 0x90000178, 16, 3, 1);
+ status |= update_by_mnemonic(state, 0x90000178, 20, 3, 1);
+ status |= update_by_mnemonic(state, 0x90000178, 24, 3, 1);
+ status |= update_by_mnemonic(state, 0x9000017C, 0, 3, 1);
+ status |= update_by_mnemonic(state, 0x9000017C, 4, 3, 1);
+ if (enable_serial_ts == MXL_ENABLE) {
+ status |= update_by_mnemonic(state,
+ 0x90000170, 4, 3, 0);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 8, 3, 0);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 12, 3, 0);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 16, 3, 0);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 20, 3, 1);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 24, 3, 1);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 28, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 0, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 4, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 8, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 12, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 16, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 20, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 24, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 28, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 0, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 4, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 8, 3, 2);
+ } else {
+ status |= update_by_mnemonic(state,
+ 0x90000170, 4, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 8, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 12, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 16, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 20, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 24, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 28, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 0, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 4, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 8, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 12, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 16, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 20, 3, 1);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 24, 3, 1);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 28, 3, 1);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 0, 3, 1);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 4, 3, 1);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 8, 3, 1);
+ }
+ break;
+
+ case MXL_HYDRA_DEVICE_568:
+ if (enable_serial_ts == MXL_FALSE) {
+ status |= update_by_mnemonic(state,
+ 0x9000016C, 8, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x9000016C, 12, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x9000016C, 16, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x9000016C, 20, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x9000016C, 24, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x9000016C, 28, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 0, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 4, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 8, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 12, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 16, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 20, 3, 5);
+
+ status |= update_by_mnemonic(state,
+ 0x90000170, 24, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 0, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 4, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 8, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 12, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 16, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 20, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 24, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 28, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 0, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 4, 3, pad_mux_value);
+
+ status |= update_by_mnemonic(state,
+ 0x90000178, 8, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 12, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 16, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 20, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 24, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 28, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x9000017C, 0, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x9000017C, 4, 3, 5);
+ } else {
+ status |= update_by_mnemonic(state,
+ 0x90000170, 4, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 8, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 12, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 16, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 20, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 24, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 28, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 0, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 4, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 8, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 12, 3, pad_mux_value);
+ }
+ break;
+
+
+ case MXL_HYDRA_DEVICE_584:
+ default:
+ status |= update_by_mnemonic(state,
+ 0x90000170, 4, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 8, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 12, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 16, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 20, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 24, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 28, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 0, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 4, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 8, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 12, 3, pad_mux_value);
+ break;
+ }
+ return status;
+}
+
+static int set_drive_strength(struct mxl *state,
+ enum MXL_HYDRA_TS_DRIVE_STRENGTH_E ts_drive_strength)
+{
+ int stat = 0;
+ u32 val;
+
+ read_register(state, 0x90000194, &val);
+ dev_info(state->i2cdev, "DIGIO = %08x\n", val);
+ dev_info(state->i2cdev, "set drive_strength = %u\n", ts_drive_strength);
+
+
+ stat |= update_by_mnemonic(state, 0x90000194, 0, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x90000194, 20, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x90000194, 24, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x90000198, 12, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x90000198, 16, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x90000198, 20, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x90000198, 24, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x9000019C, 0, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x9000019C, 4, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x9000019C, 8, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x9000019C, 24, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x9000019C, 28, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x900001A0, 0, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x900001A0, 4, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x900001A0, 20, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x900001A0, 24, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x900001A0, 28, 3, ts_drive_strength);
+
+ return stat;
+}
+
+static int enable_tuner(struct mxl *state, u32 tuner, u32 enable)
+{
+ int stat = 0;
+ struct MXL_HYDRA_TUNER_CMD ctrl_tuner_cmd;
+ u8 cmd_size = sizeof(ctrl_tuner_cmd);
+ u8 cmd_buff[MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN];
+ u32 val, count = 10;
+
+ ctrl_tuner_cmd.tuner_id = tuner;
+ ctrl_tuner_cmd.enable = enable;
+ BUILD_HYDRA_CMD(MXL_HYDRA_TUNER_ACTIVATE_CMD, MXL_CMD_WRITE,
+ cmd_size, &ctrl_tuner_cmd, cmd_buff);
+ stat = send_command(state, cmd_size + MXL_HYDRA_CMD_HEADER_SIZE,
+ &cmd_buff[0]);
+ if (stat)
+ return stat;
+ read_register(state, HYDRA_TUNER_ENABLE_COMPLETE, &val);
+ while (--count && ((val >> tuner) & 1) != enable) {
+ msleep(20);
+ read_register(state, HYDRA_TUNER_ENABLE_COMPLETE, &val);
+ }
+ if (!count)
+ return -1;
+ read_register(state, HYDRA_TUNER_ENABLE_COMPLETE, &val);
+ dev_dbg(state->i2cdev, "tuner %u ready = %u\n",
+ tuner, (val >> tuner) & 1);
+
+ return 0;
+}
+
+
+static int config_ts(struct mxl *state, enum MXL_HYDRA_DEMOD_ID_E demod_id,
+ struct MXL_HYDRA_MPEGOUT_PARAM_T *mpeg_out_param_ptr)
+{
+ int status = 0;
+ u32 nco_count_min = 0;
+ u32 clk_type = 0;
+
+ static const struct MXL_REG_FIELD_T xpt_sync_polarity[MXL_HYDRA_DEMOD_MAX] = {
+ {0x90700010, 8, 1}, {0x90700010, 9, 1},
+ {0x90700010, 10, 1}, {0x90700010, 11, 1},
+ {0x90700010, 12, 1}, {0x90700010, 13, 1},
+ {0x90700010, 14, 1}, {0x90700010, 15, 1} };
+ static const struct MXL_REG_FIELD_T xpt_clock_polarity[MXL_HYDRA_DEMOD_MAX] = {
+ {0x90700010, 16, 1}, {0x90700010, 17, 1},
+ {0x90700010, 18, 1}, {0x90700010, 19, 1},
+ {0x90700010, 20, 1}, {0x90700010, 21, 1},
+ {0x90700010, 22, 1}, {0x90700010, 23, 1} };
+ static const struct MXL_REG_FIELD_T xpt_valid_polarity[MXL_HYDRA_DEMOD_MAX] = {
+ {0x90700014, 0, 1}, {0x90700014, 1, 1},
+ {0x90700014, 2, 1}, {0x90700014, 3, 1},
+ {0x90700014, 4, 1}, {0x90700014, 5, 1},
+ {0x90700014, 6, 1}, {0x90700014, 7, 1} };
+ static const struct MXL_REG_FIELD_T xpt_ts_clock_phase[MXL_HYDRA_DEMOD_MAX] = {
+ {0x90700018, 0, 3}, {0x90700018, 4, 3},
+ {0x90700018, 8, 3}, {0x90700018, 12, 3},
+ {0x90700018, 16, 3}, {0x90700018, 20, 3},
+ {0x90700018, 24, 3}, {0x90700018, 28, 3} };
+ static const struct MXL_REG_FIELD_T xpt_lsb_first[MXL_HYDRA_DEMOD_MAX] = {
+ {0x9070000C, 16, 1}, {0x9070000C, 17, 1},
+ {0x9070000C, 18, 1}, {0x9070000C, 19, 1},
+ {0x9070000C, 20, 1}, {0x9070000C, 21, 1},
+ {0x9070000C, 22, 1}, {0x9070000C, 23, 1} };
+ static const struct MXL_REG_FIELD_T xpt_sync_byte[MXL_HYDRA_DEMOD_MAX] = {
+ {0x90700010, 0, 1}, {0x90700010, 1, 1},
+ {0x90700010, 2, 1}, {0x90700010, 3, 1},
+ {0x90700010, 4, 1}, {0x90700010, 5, 1},
+ {0x90700010, 6, 1}, {0x90700010, 7, 1} };
+ static const struct MXL_REG_FIELD_T xpt_enable_output[MXL_HYDRA_DEMOD_MAX] = {
+ {0x9070000C, 0, 1}, {0x9070000C, 1, 1},
+ {0x9070000C, 2, 1}, {0x9070000C, 3, 1},
+ {0x9070000C, 4, 1}, {0x9070000C, 5, 1},
+ {0x9070000C, 6, 1}, {0x9070000C, 7, 1} };
+ static const struct MXL_REG_FIELD_T xpt_err_replace_sync[MXL_HYDRA_DEMOD_MAX] = {
+ {0x9070000C, 24, 1}, {0x9070000C, 25, 1},
+ {0x9070000C, 26, 1}, {0x9070000C, 27, 1},
+ {0x9070000C, 28, 1}, {0x9070000C, 29, 1},
+ {0x9070000C, 30, 1}, {0x9070000C, 31, 1} };
+ static const struct MXL_REG_FIELD_T xpt_err_replace_valid[MXL_HYDRA_DEMOD_MAX] = {
+ {0x90700014, 8, 1}, {0x90700014, 9, 1},
+ {0x90700014, 10, 1}, {0x90700014, 11, 1},
+ {0x90700014, 12, 1}, {0x90700014, 13, 1},
+ {0x90700014, 14, 1}, {0x90700014, 15, 1} };
+ static const struct MXL_REG_FIELD_T xpt_continuous_clock[MXL_HYDRA_DEMOD_MAX] = {
+ {0x907001D4, 0, 1}, {0x907001D4, 1, 1},
+ {0x907001D4, 2, 1}, {0x907001D4, 3, 1},
+ {0x907001D4, 4, 1}, {0x907001D4, 5, 1},
+ {0x907001D4, 6, 1}, {0x907001D4, 7, 1} };
+ static const struct MXL_REG_FIELD_T xpt_nco_clock_rate[MXL_HYDRA_DEMOD_MAX] = {
+ {0x90700044, 16, 80}, {0x90700044, 16, 81},
+ {0x90700044, 16, 82}, {0x90700044, 16, 83},
+ {0x90700044, 16, 84}, {0x90700044, 16, 85},
+ {0x90700044, 16, 86}, {0x90700044, 16, 87} };
+
+ demod_id = state->base->ts_map[demod_id];
+
+ if (mpeg_out_param_ptr->enable == MXL_ENABLE) {
+ if (mpeg_out_param_ptr->mpeg_mode ==
+ MXL_HYDRA_MPEG_MODE_PARALLEL) {
+ } else {
+ cfg_ts_pad_mux(state, MXL_TRUE);
+ update_by_mnemonic(state,
+ 0x90700010, 27, 1, MXL_FALSE);
+ }
+ }
+
+ nco_count_min =
+ (u32)(MXL_HYDRA_NCO_CLK / mpeg_out_param_ptr->max_mpeg_clk_rate);
+
+ if (state->base->chipversion >= 2) {
+ status |= update_by_mnemonic(state,
+ xpt_nco_clock_rate[demod_id].reg_addr, /* Reg Addr */
+ xpt_nco_clock_rate[demod_id].lsb_pos, /* LSB pos */
+ xpt_nco_clock_rate[demod_id].num_of_bits, /* Num of bits */
+ nco_count_min); /* Data */
+ } else
+ update_by_mnemonic(state, 0x90700044, 16, 8, nco_count_min);
+
+ if (mpeg_out_param_ptr->mpeg_clk_type == MXL_HYDRA_MPEG_CLK_CONTINUOUS)
+ clk_type = 1;
+
+ if (mpeg_out_param_ptr->mpeg_mode < MXL_HYDRA_MPEG_MODE_PARALLEL) {
+ status |= update_by_mnemonic(state,
+ xpt_continuous_clock[demod_id].reg_addr,
+ xpt_continuous_clock[demod_id].lsb_pos,
+ xpt_continuous_clock[demod_id].num_of_bits,
+ clk_type);
+ } else
+ update_by_mnemonic(state, 0x907001D4, 8, 1, clk_type);
+
+ status |= update_by_mnemonic(state,
+ xpt_sync_polarity[demod_id].reg_addr,
+ xpt_sync_polarity[demod_id].lsb_pos,
+ xpt_sync_polarity[demod_id].num_of_bits,
+ mpeg_out_param_ptr->mpeg_sync_pol);
+
+ status |= update_by_mnemonic(state,
+ xpt_valid_polarity[demod_id].reg_addr,
+ xpt_valid_polarity[demod_id].lsb_pos,
+ xpt_valid_polarity[demod_id].num_of_bits,
+ mpeg_out_param_ptr->mpeg_valid_pol);
+
+ status |= update_by_mnemonic(state,
+ xpt_clock_polarity[demod_id].reg_addr,
+ xpt_clock_polarity[demod_id].lsb_pos,
+ xpt_clock_polarity[demod_id].num_of_bits,
+ mpeg_out_param_ptr->mpeg_clk_pol);
+
+ status |= update_by_mnemonic(state,
+ xpt_sync_byte[demod_id].reg_addr,
+ xpt_sync_byte[demod_id].lsb_pos,
+ xpt_sync_byte[demod_id].num_of_bits,
+ mpeg_out_param_ptr->mpeg_sync_pulse_width);
+
+ status |= update_by_mnemonic(state,
+ xpt_ts_clock_phase[demod_id].reg_addr,
+ xpt_ts_clock_phase[demod_id].lsb_pos,
+ xpt_ts_clock_phase[demod_id].num_of_bits,
+ mpeg_out_param_ptr->mpeg_clk_phase);
+
+ status |= update_by_mnemonic(state,
+ xpt_lsb_first[demod_id].reg_addr,
+ xpt_lsb_first[demod_id].lsb_pos,
+ xpt_lsb_first[demod_id].num_of_bits,
+ mpeg_out_param_ptr->lsb_or_msb_first);
+
+ switch (mpeg_out_param_ptr->mpeg_error_indication) {
+ case MXL_HYDRA_MPEG_ERR_REPLACE_SYNC:
+ status |= update_by_mnemonic(state,
+ xpt_err_replace_sync[demod_id].reg_addr,
+ xpt_err_replace_sync[demod_id].lsb_pos,
+ xpt_err_replace_sync[demod_id].num_of_bits,
+ MXL_TRUE);
+ status |= update_by_mnemonic(state,
+ xpt_err_replace_valid[demod_id].reg_addr,
+ xpt_err_replace_valid[demod_id].lsb_pos,
+ xpt_err_replace_valid[demod_id].num_of_bits,
+ MXL_FALSE);
+ break;
+
+ case MXL_HYDRA_MPEG_ERR_REPLACE_VALID:
+ status |= update_by_mnemonic(state,
+ xpt_err_replace_sync[demod_id].reg_addr,
+ xpt_err_replace_sync[demod_id].lsb_pos,
+ xpt_err_replace_sync[demod_id].num_of_bits,
+ MXL_FALSE);
+
+ status |= update_by_mnemonic(state,
+ xpt_err_replace_valid[demod_id].reg_addr,
+ xpt_err_replace_valid[demod_id].lsb_pos,
+ xpt_err_replace_valid[demod_id].num_of_bits,
+ MXL_TRUE);
+ break;
+
+ case MXL_HYDRA_MPEG_ERR_INDICATION_DISABLED:
+ default:
+ status |= update_by_mnemonic(state,
+ xpt_err_replace_sync[demod_id].reg_addr,
+ xpt_err_replace_sync[demod_id].lsb_pos,
+ xpt_err_replace_sync[demod_id].num_of_bits,
+ MXL_FALSE);
+
+ status |= update_by_mnemonic(state,
+ xpt_err_replace_valid[demod_id].reg_addr,
+ xpt_err_replace_valid[demod_id].lsb_pos,
+ xpt_err_replace_valid[demod_id].num_of_bits,
+ MXL_FALSE);
+
+ break;
+
+ }
+
+ if (mpeg_out_param_ptr->mpeg_mode != MXL_HYDRA_MPEG_MODE_PARALLEL) {
+ status |= update_by_mnemonic(state,
+ xpt_enable_output[demod_id].reg_addr,
+ xpt_enable_output[demod_id].lsb_pos,
+ xpt_enable_output[demod_id].num_of_bits,
+ mpeg_out_param_ptr->enable);
+ }
+ return status;
+}
+
+static int config_mux(struct mxl *state)
+{
+ update_by_mnemonic(state, 0x9070000C, 0, 1, 0);
+ update_by_mnemonic(state, 0x9070000C, 1, 1, 0);
+ update_by_mnemonic(state, 0x9070000C, 2, 1, 0);
+ update_by_mnemonic(state, 0x9070000C, 3, 1, 0);
+ update_by_mnemonic(state, 0x9070000C, 4, 1, 0);
+ update_by_mnemonic(state, 0x9070000C, 5, 1, 0);
+ update_by_mnemonic(state, 0x9070000C, 6, 1, 0);
+ update_by_mnemonic(state, 0x9070000C, 7, 1, 0);
+ update_by_mnemonic(state, 0x90700008, 0, 2, 1);
+ update_by_mnemonic(state, 0x90700008, 2, 2, 1);
+ return 0;
+}
+
+static int load_fw(struct mxl *state, struct mxl5xx_cfg *cfg)
+{
+ int stat = 0;
+ u8 *buf;
+
+ if (cfg->fw)
+ return firmware_download(state, cfg->fw, cfg->fw_len);
+
+ if (!cfg->fw_read)
+ return -1;
+
+ buf = vmalloc(0x40000);
+ if (!buf)
+ return -ENOMEM;
+
+ cfg->fw_read(cfg->fw_priv, buf, 0x40000);
+ stat = firmware_download(state, buf, 0x40000);
+ vfree(buf);
+
+ return stat;
+}
+
+static int validate_sku(struct mxl *state)
+{
+ u32 pad_mux_bond = 0, prcm_chip_id = 0, prcm_so_cid = 0;
+ int status;
+ u32 type = state->base->type;
+
+ status = read_by_mnemonic(state, 0x90000190, 0, 3, &pad_mux_bond);
+ status |= read_by_mnemonic(state, 0x80030000, 0, 12, &prcm_chip_id);
+ status |= read_by_mnemonic(state, 0x80030004, 24, 8, &prcm_so_cid);
+ if (status)
+ return -1;
+
+ dev_info(state->i2cdev, "padMuxBond=%08x, prcmChipId=%08x, prcmSoCId=%08x\n",
+ pad_mux_bond, prcm_chip_id, prcm_so_cid);
+
+ if (prcm_chip_id != 0x560) {
+ switch (pad_mux_bond) {
+ case MXL_HYDRA_SKU_ID_581:
+ if (type == MXL_HYDRA_DEVICE_581)
+ return 0;
+ if (type == MXL_HYDRA_DEVICE_581S) {
+ state->base->type = MXL_HYDRA_DEVICE_581;
+ return 0;
+ }
+ break;
+ case MXL_HYDRA_SKU_ID_584:
+ if (type == MXL_HYDRA_DEVICE_584)
+ return 0;
+ break;
+ case MXL_HYDRA_SKU_ID_544:
+ if (type == MXL_HYDRA_DEVICE_544)
+ return 0;
+ if (type == MXL_HYDRA_DEVICE_542)
+ return 0;
+ break;
+ case MXL_HYDRA_SKU_ID_582:
+ if (type == MXL_HYDRA_DEVICE_582)
+ return 0;
+ break;
+ default:
+ return -1;
+ }
+ }
+ return -1;
+}
+
+static int get_fwinfo(struct mxl *state)
+{
+ int status;
+ u32 val = 0;
+
+ status = read_by_mnemonic(state, 0x90000190, 0, 3, &val);
+ if (status)
+ return status;
+ dev_info(state->i2cdev, "chipID=%08x\n", val);
+
+ status = read_by_mnemonic(state, 0x80030004, 8, 8, &val);
+ if (status)
+ return status;
+ dev_info(state->i2cdev, "chipVer=%08x\n", val);
+
+ status = read_register(state, HYDRA_FIRMWARE_VERSION, &val);
+ if (status)
+ return status;
+ dev_info(state->i2cdev, "FWVer=%08x\n", val);
+
+ state->base->fwversion = val;
+ return status;
+}
+
+
+static u8 ts_map1_to_1[MXL_HYDRA_DEMOD_MAX] = {
+ MXL_HYDRA_DEMOD_ID_0,
+ MXL_HYDRA_DEMOD_ID_1,
+ MXL_HYDRA_DEMOD_ID_2,
+ MXL_HYDRA_DEMOD_ID_3,
+ MXL_HYDRA_DEMOD_ID_4,
+ MXL_HYDRA_DEMOD_ID_5,
+ MXL_HYDRA_DEMOD_ID_6,
+ MXL_HYDRA_DEMOD_ID_7,
+};
+
+static u8 ts_map54x[MXL_HYDRA_DEMOD_MAX] = {
+ MXL_HYDRA_DEMOD_ID_2,
+ MXL_HYDRA_DEMOD_ID_3,
+ MXL_HYDRA_DEMOD_ID_4,
+ MXL_HYDRA_DEMOD_ID_5,
+ MXL_HYDRA_DEMOD_MAX,
+ MXL_HYDRA_DEMOD_MAX,
+ MXL_HYDRA_DEMOD_MAX,
+ MXL_HYDRA_DEMOD_MAX,
+};
+
+static int probe(struct mxl *state, struct mxl5xx_cfg *cfg)
+{
+ u32 chipver;
+ int fw, status, j;
+ struct MXL_HYDRA_MPEGOUT_PARAM_T mpeg_interface_cfg;
+
+ state->base->ts_map = ts_map1_to_1;
+
+ switch (state->base->type) {
+ case MXL_HYDRA_DEVICE_581:
+ case MXL_HYDRA_DEVICE_581S:
+ state->base->can_clkout = 1;
+ state->base->demod_num = 8;
+ state->base->tuner_num = 1;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_581;
+ break;
+ case MXL_HYDRA_DEVICE_582:
+ state->base->can_clkout = 1;
+ state->base->demod_num = 8;
+ state->base->tuner_num = 3;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_582;
+ break;
+ case MXL_HYDRA_DEVICE_585:
+ state->base->can_clkout = 0;
+ state->base->demod_num = 8;
+ state->base->tuner_num = 4;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_585;
+ break;
+ case MXL_HYDRA_DEVICE_544:
+ state->base->can_clkout = 0;
+ state->base->demod_num = 4;
+ state->base->tuner_num = 4;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_544;
+ state->base->ts_map = ts_map54x;
+ break;
+ case MXL_HYDRA_DEVICE_541:
+ case MXL_HYDRA_DEVICE_541S:
+ state->base->can_clkout = 0;
+ state->base->demod_num = 4;
+ state->base->tuner_num = 1;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_541;
+ state->base->ts_map = ts_map54x;
+ break;
+ case MXL_HYDRA_DEVICE_561:
+ case MXL_HYDRA_DEVICE_561S:
+ state->base->can_clkout = 0;
+ state->base->demod_num = 6;
+ state->base->tuner_num = 1;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_561;
+ break;
+ case MXL_HYDRA_DEVICE_568:
+ state->base->can_clkout = 0;
+ state->base->demod_num = 8;
+ state->base->tuner_num = 1;
+ state->base->chan_bond = 1;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_568;
+ break;
+ case MXL_HYDRA_DEVICE_542:
+ state->base->can_clkout = 1;
+ state->base->demod_num = 4;
+ state->base->tuner_num = 3;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_542;
+ state->base->ts_map = ts_map54x;
+ break;
+ case MXL_HYDRA_DEVICE_TEST:
+ case MXL_HYDRA_DEVICE_584:
+ default:
+ state->base->can_clkout = 0;
+ state->base->demod_num = 8;
+ state->base->tuner_num = 4;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_584;
+ break;
+ }
+
+ status = validate_sku(state);
+ if (status)
+ return status;
+
+ update_by_mnemonic(state, 0x80030014, 9, 1, 1);
+ update_by_mnemonic(state, 0x8003003C, 12, 1, 1);
+ status = read_by_mnemonic(state, 0x80030000, 12, 4, &chipver);
+ if (status)
+ state->base->chipversion = 0;
+ else
+ state->base->chipversion = (chipver == 2) ? 2 : 1;
+ dev_info(state->i2cdev, "Hydra chip version %u\n",
+ state->base->chipversion);
+
+ cfg_dev_xtal(state, cfg->clk, cfg->cap, 0);
+
+ fw = firmware_is_alive(state);
+ if (!fw) {
+ status = load_fw(state, cfg);
+ if (status)
+ return status;
+ }
+ get_fwinfo(state);
+
+ config_mux(state);
+ mpeg_interface_cfg.enable = MXL_ENABLE;
+ mpeg_interface_cfg.lsb_or_msb_first = MXL_HYDRA_MPEG_SERIAL_MSB_1ST;
+ /* supports only (0-104&139)MHz */
+ if (cfg->ts_clk)
+ mpeg_interface_cfg.max_mpeg_clk_rate = cfg->ts_clk;
+ else
+ mpeg_interface_cfg.max_mpeg_clk_rate = 69; /* 139; */
+ mpeg_interface_cfg.mpeg_clk_phase = MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_0_DEG;
+ mpeg_interface_cfg.mpeg_clk_pol = MXL_HYDRA_MPEG_CLK_IN_PHASE;
+ /* MXL_HYDRA_MPEG_CLK_GAPPED; */
+ mpeg_interface_cfg.mpeg_clk_type = MXL_HYDRA_MPEG_CLK_CONTINUOUS;
+ mpeg_interface_cfg.mpeg_error_indication =
+ MXL_HYDRA_MPEG_ERR_INDICATION_DISABLED;
+ mpeg_interface_cfg.mpeg_mode = MXL_HYDRA_MPEG_MODE_SERIAL_3_WIRE;
+ mpeg_interface_cfg.mpeg_sync_pol = MXL_HYDRA_MPEG_ACTIVE_HIGH;
+ mpeg_interface_cfg.mpeg_sync_pulse_width = MXL_HYDRA_MPEG_SYNC_WIDTH_BIT;
+ mpeg_interface_cfg.mpeg_valid_pol = MXL_HYDRA_MPEG_ACTIVE_HIGH;
+
+ for (j = 0; j < state->base->demod_num; j++) {
+ status = config_ts(state, (enum MXL_HYDRA_DEMOD_ID_E) j,
+ &mpeg_interface_cfg);
+ if (status)
+ return status;
+ }
+ set_drive_strength(state, 1);
+ return 0;
+}
+
+struct dvb_frontend *mxl5xx_attach(struct i2c_adapter *i2c,
+ struct mxl5xx_cfg *cfg, u32 demod, u32 tuner,
+ int (**fn_set_input)(struct dvb_frontend *, int))
+{
+ struct mxl *state;
+ struct mxl_base *base;
+
+ state = kzalloc(sizeof(struct mxl), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ state->demod = demod;
+ state->tuner = tuner;
+ state->tuner_in_use = 0xffffffff;
+ state->i2cdev = &i2c->dev;
+
+ base = match_base(i2c, cfg->adr);
+ if (base) {
+ base->count++;
+ if (base->count > base->demod_num)
+ goto fail;
+ state->base = base;
+ } else {
+ base = kzalloc(sizeof(struct mxl_base), GFP_KERNEL);
+ if (!base)
+ goto fail;
+ base->i2c = i2c;
+ base->adr = cfg->adr;
+ base->type = cfg->type;
+ base->count = 1;
+ mutex_init(&base->i2c_lock);
+ mutex_init(&base->status_lock);
+ mutex_init(&base->tune_lock);
+ INIT_LIST_HEAD(&base->mxls);
+
+ state->base = base;
+ if (probe(state, cfg) < 0) {
+ kfree(base);
+ goto fail;
+ }
+ list_add(&base->mxllist, &mxllist);
+ }
+ state->fe.ops = mxl_ops;
+ state->xbar[0] = 4;
+ state->xbar[1] = demod;
+ state->xbar[2] = 8;
+ state->fe.demodulator_priv = state;
+ *fn_set_input = set_input;
+
+ list_add(&state->mxl, &base->mxls);
+ return &state->fe;
+
+fail:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(mxl5xx_attach);
+
+MODULE_DESCRIPTION("MaxLinear MxL5xx DVB-S/S2 tuner-demodulator driver");
+MODULE_AUTHOR("Ralph and Marcus Metzler, Metzler Brothers Systementwicklung GbR");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/dvb-frontends/mxl5xx.h b/drivers/media/dvb-frontends/mxl5xx.h
new file mode 100644
index 000000000000..139e16b2ecfc
--- /dev/null
+++ b/drivers/media/dvb-frontends/mxl5xx.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver for the MaxLinear MxL5xx family of tuners/demods
+ *
+ * Copyright (C) 2014-2015 Ralph Metzler <rjkm@metzlerbros.de>
+ * Marcus Metzler <mocm@metzlerbros.de>
+ * developed for Digital Devices GmbH
+ *
+ * based on code:
+ * Copyright (c) 2011-2013 MaxLinear, Inc. All rights reserved
+ * which was released under GPL V2
+ */
+
+#ifndef _MXL5XX_H_
+#define _MXL5XX_H_
+
+#include <linux/types.h>
+#include <linux/i2c.h>
+
+#include <media/dvb_frontend.h>
+
+struct mxl5xx_cfg {
+ u8 adr;
+ u8 type;
+ u32 cap;
+ u32 clk;
+ u32 ts_clk;
+
+ u8 *fw;
+ u32 fw_len;
+
+ int (*fw_read)(void *priv, u8 *buf, u32 len);
+ void *fw_priv;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_MXL5XX)
+
+extern struct dvb_frontend *mxl5xx_attach(struct i2c_adapter *i2c,
+ struct mxl5xx_cfg *cfg, u32 demod, u32 tuner,
+ int (**fn_set_input)(struct dvb_frontend *, int));
+
+#else
+
+static inline struct dvb_frontend *mxl5xx_attach(struct i2c_adapter *i2c,
+ struct mxl5xx_cfg *cfg, u32 demod, u32 tuner,
+ int (**fn_set_input)(struct dvb_frontend *, int))
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+#endif /* CONFIG_DVB_MXL5XX */
+
+#endif /* _MXL5XX_H_ */
diff --git a/drivers/media/dvb-frontends/mxl5xx_defs.h b/drivers/media/dvb-frontends/mxl5xx_defs.h
new file mode 100644
index 000000000000..bf08d82ba2cc
--- /dev/null
+++ b/drivers/media/dvb-frontends/mxl5xx_defs.h
@@ -0,0 +1,728 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Defines for the Maxlinear MX58x family of tuners/demods
+ *
+ * Copyright (C) 2014 Digital Devices GmbH
+ *
+ * based on code:
+ * Copyright (c) 2011-2013 MaxLinear, Inc. All rights reserved
+ * which was released under GPL V2
+ */
+
+enum MXL_BOOL_E {
+ MXL_DISABLE = 0,
+ MXL_ENABLE = 1,
+
+ MXL_FALSE = 0,
+ MXL_TRUE = 1,
+
+ MXL_INVALID = 0,
+ MXL_VALID = 1,
+
+ MXL_NO = 0,
+ MXL_YES = 1,
+
+ MXL_OFF = 0,
+ MXL_ON = 1
+};
+
+/* Firmware-Host Command IDs */
+enum MXL_HYDRA_HOST_CMD_ID_E {
+ /* --Device command IDs-- */
+ MXL_HYDRA_DEV_NO_OP_CMD = 0, /* No OP */
+
+ MXL_HYDRA_DEV_SET_POWER_MODE_CMD = 1,
+ MXL_HYDRA_DEV_SET_OVERWRITE_DEF_CMD = 2,
+
+ /* Host-used CMD, not used by firmware */
+ MXL_HYDRA_DEV_FIRMWARE_DOWNLOAD_CMD = 3,
+
+ /* Additional CONTROL types from DTV */
+ MXL_HYDRA_DEV_SET_BROADCAST_PID_STB_ID_CMD = 4,
+ MXL_HYDRA_DEV_GET_PMM_SLEEP_CMD = 5,
+
+ /* --Tuner command IDs-- */
+ MXL_HYDRA_TUNER_TUNE_CMD = 6,
+ MXL_HYDRA_TUNER_GET_STATUS_CMD = 7,
+
+ /* --Demod command IDs-- */
+ MXL_HYDRA_DEMOD_SET_PARAM_CMD = 8,
+ MXL_HYDRA_DEMOD_GET_STATUS_CMD = 9,
+
+ MXL_HYDRA_DEMOD_RESET_FEC_COUNTER_CMD = 10,
+
+ MXL_HYDRA_DEMOD_SET_PKT_NUM_CMD = 11,
+
+ MXL_HYDRA_DEMOD_SET_IQ_SOURCE_CMD = 12,
+ MXL_HYDRA_DEMOD_GET_IQ_DATA_CMD = 13,
+
+ MXL_HYDRA_DEMOD_GET_M68HC05_VER_CMD = 14,
+
+ MXL_HYDRA_DEMOD_SET_ERROR_COUNTER_MODE_CMD = 15,
+
+ /* --- ABORT channel tune */
+ MXL_HYDRA_ABORT_TUNE_CMD = 16, /* Abort current tune command. */
+
+ /* --SWM/FSK command IDs-- */
+ MXL_HYDRA_FSK_RESET_CMD = 17,
+ MXL_HYDRA_FSK_MSG_CMD = 18,
+ MXL_HYDRA_FSK_SET_OP_MODE_CMD = 19,
+
+ /* --DiSeqC command IDs-- */
+ MXL_HYDRA_DISEQC_MSG_CMD = 20,
+ MXL_HYDRA_DISEQC_COPY_MSG_TO_MAILBOX = 21,
+ MXL_HYDRA_DISEQC_CFG_MSG_CMD = 22,
+
+ /* --- FFT Debug Command IDs-- */
+ MXL_HYDRA_REQ_FFT_SPECTRUM_CMD = 23,
+
+ /* -- Demod scramblle code */
+ MXL_HYDRA_DEMOD_SCRAMBLE_CODE_CMD = 24,
+
+ /* ---For host to know how many commands in total */
+ MXL_HYDRA_LAST_HOST_CMD = 25,
+
+ MXL_HYDRA_DEMOD_INTR_TYPE_CMD = 47,
+ MXL_HYDRA_DEV_INTR_CLEAR_CMD = 48,
+ MXL_HYDRA_TUNER_SPECTRUM_REQ_CMD = 53,
+ MXL_HYDRA_TUNER_ACTIVATE_CMD = 55,
+ MXL_HYDRA_DEV_CFG_POWER_MODE_CMD = 56,
+ MXL_HYDRA_DEV_XTAL_CAP_CMD = 57,
+ MXL_HYDRA_DEV_CFG_SKU_CMD = 58,
+ MXL_HYDRA_TUNER_SPECTRUM_MIN_GAIN_CMD = 59,
+ MXL_HYDRA_DISEQC_CONT_TONE_CFG = 60,
+ MXL_HYDRA_DEV_RF_WAKE_UP_CMD = 61,
+ MXL_HYDRA_DEMOD_CFG_EQ_CTRL_PARAM_CMD = 62,
+ MXL_HYDRA_DEMOD_FREQ_OFFSET_SEARCH_RANGE_CMD = 63,
+ MXL_HYDRA_DEV_REQ_PWR_FROM_ADCRSSI_CMD = 64,
+
+ MXL_XCPU_PID_FLT_CFG_CMD = 65,
+ MXL_XCPU_SHMEM_TEST_CMD = 66,
+ MXL_XCPU_ABORT_TUNE_CMD = 67,
+ MXL_XCPU_CHAN_TUNE_CMD = 68,
+ MXL_XCPU_FLT_BOND_HDRS_CMD = 69,
+
+ MXL_HYDRA_DEV_BROADCAST_WAKE_UP_CMD = 70,
+ MXL_HYDRA_FSK_CFG_FSK_FREQ_CMD = 71,
+ MXL_HYDRA_FSK_POWER_DOWN_CMD = 72,
+ MXL_XCPU_CLEAR_CB_STATS_CMD = 73,
+ MXL_XCPU_CHAN_BOND_RESTART_CMD = 74
+};
+
+#define MXL_ENABLE_BIG_ENDIAN (0)
+
+#define MXL_HYDRA_OEM_MAX_BLOCK_WRITE_LENGTH 248
+
+#define MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN (248)
+
+#define MXL_HYDRA_CAP_MIN 10
+#define MXL_HYDRA_CAP_MAX 33
+
+#define MXL_HYDRA_PLID_REG_READ 0xFB /* Read register PLID */
+#define MXL_HYDRA_PLID_REG_WRITE 0xFC /* Write register PLID */
+
+#define MXL_HYDRA_PLID_CMD_READ 0xFD /* Command Read PLID */
+#define MXL_HYDRA_PLID_CMD_WRITE 0xFE /* Command Write PLID */
+
+#define MXL_HYDRA_REG_SIZE_IN_BYTES 4 /* Hydra register size in bytes */
+#define MXL_HYDRA_I2C_HDR_SIZE (2 * sizeof(u8)) /* PLID + LEN(0xFF) */
+#define MXL_HYDRA_CMD_HEADER_SIZE (MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE)
+
+#define MXL_HYDRA_SKU_ID_581 0
+#define MXL_HYDRA_SKU_ID_584 1
+#define MXL_HYDRA_SKU_ID_585 2
+#define MXL_HYDRA_SKU_ID_544 3
+#define MXL_HYDRA_SKU_ID_561 4
+#define MXL_HYDRA_SKU_ID_582 5
+#define MXL_HYDRA_SKU_ID_568 6
+
+/* macro for register write data buffer size
+ * (PLID + LEN (0xFF) + RegAddr + RegData)
+ */
+#define MXL_HYDRA_REG_WRITE_LEN (MXL_HYDRA_I2C_HDR_SIZE + (2 * MXL_HYDRA_REG_SIZE_IN_BYTES))
+
+/* macro to extract a single byte from 4-byte(32-bit) data */
+#define GET_BYTE(x, n) (((x) >> (8*(n))) & 0xFF)
+
+#define MAX_CMD_DATA 512
+
+#define MXL_GET_REG_MASK_32(lsb_loc, num_of_bits) ((0xFFFFFFFF >> (32 - (num_of_bits))) << (lsb_loc))
+
+#define FW_DL_SIGN (0xDEADBEEF)
+
+#define MBIN_FORMAT_VERSION '1'
+#define MBIN_FILE_HEADER_ID 'M'
+#define MBIN_SEGMENT_HEADER_ID 'S'
+#define MBIN_MAX_FILE_LENGTH (1<<23)
+
+struct MBIN_FILE_HEADER_T {
+ u8 id;
+ u8 fmt_version;
+ u8 header_len;
+ u8 num_segments;
+ u8 entry_address[4];
+ u8 image_size24[3];
+ u8 image_checksum;
+ u8 reserved[4];
+};
+
+struct MBIN_FILE_T {
+ struct MBIN_FILE_HEADER_T header;
+ u8 data[];
+};
+
+struct MBIN_SEGMENT_HEADER_T {
+ u8 id;
+ u8 len24[3];
+ u8 address[4];
+};
+
+struct MBIN_SEGMENT_T {
+ struct MBIN_SEGMENT_HEADER_T header;
+ u8 data[];
+};
+
+enum MXL_CMD_TYPE_E { MXL_CMD_WRITE = 0, MXL_CMD_READ };
+
+#define BUILD_HYDRA_CMD(cmd_id, req_type, size, data_ptr, cmd_buff) \
+ do { \
+ cmd_buff[0] = ((req_type == MXL_CMD_WRITE) ? MXL_HYDRA_PLID_CMD_WRITE : MXL_HYDRA_PLID_CMD_READ); \
+ cmd_buff[1] = (size > 251) ? 0xff : (u8) (size + 4); \
+ cmd_buff[2] = size; \
+ cmd_buff[3] = cmd_id; \
+ cmd_buff[4] = 0x00; \
+ cmd_buff[5] = 0x00; \
+ convert_endian(MXL_ENABLE_BIG_ENDIAN, size, (u8 *)data_ptr); \
+ memcpy((void *)&cmd_buff[6], data_ptr, size); \
+ } while (0)
+
+struct MXL_REG_FIELD_T {
+ u32 reg_addr;
+ u8 lsb_pos;
+ u8 num_of_bits;
+};
+
+struct MXL_DEV_CMD_DATA_T {
+ u32 data_size;
+ u8 data[MAX_CMD_DATA];
+};
+
+enum MXL_HYDRA_SKU_TYPE_E {
+ MXL_HYDRA_SKU_TYPE_MIN = 0x00,
+ MXL_HYDRA_SKU_TYPE_581 = 0x00,
+ MXL_HYDRA_SKU_TYPE_584 = 0x01,
+ MXL_HYDRA_SKU_TYPE_585 = 0x02,
+ MXL_HYDRA_SKU_TYPE_544 = 0x03,
+ MXL_HYDRA_SKU_TYPE_561 = 0x04,
+ MXL_HYDRA_SKU_TYPE_5XX = 0x05,
+ MXL_HYDRA_SKU_TYPE_5YY = 0x06,
+ MXL_HYDRA_SKU_TYPE_511 = 0x07,
+ MXL_HYDRA_SKU_TYPE_561_DE = 0x08,
+ MXL_HYDRA_SKU_TYPE_582 = 0x09,
+ MXL_HYDRA_SKU_TYPE_541 = 0x0A,
+ MXL_HYDRA_SKU_TYPE_568 = 0x0B,
+ MXL_HYDRA_SKU_TYPE_542 = 0x0C,
+ MXL_HYDRA_SKU_TYPE_MAX = 0x0D,
+};
+
+struct MXL_HYDRA_SKU_COMMAND_T {
+ enum MXL_HYDRA_SKU_TYPE_E sku_type;
+};
+
+enum MXL_HYDRA_DEMOD_ID_E {
+ MXL_HYDRA_DEMOD_ID_0 = 0,
+ MXL_HYDRA_DEMOD_ID_1,
+ MXL_HYDRA_DEMOD_ID_2,
+ MXL_HYDRA_DEMOD_ID_3,
+ MXL_HYDRA_DEMOD_ID_4,
+ MXL_HYDRA_DEMOD_ID_5,
+ MXL_HYDRA_DEMOD_ID_6,
+ MXL_HYDRA_DEMOD_ID_7,
+ MXL_HYDRA_DEMOD_MAX
+};
+
+#define MXL_DEMOD_SCRAMBLE_SEQ_LEN 12
+
+#define MAX_STEP_SIZE_24_XTAL_102_05_KHZ 195
+#define MAX_STEP_SIZE_24_XTAL_204_10_KHZ 215
+#define MAX_STEP_SIZE_24_XTAL_306_15_KHZ 203
+#define MAX_STEP_SIZE_24_XTAL_408_20_KHZ 177
+
+#define MAX_STEP_SIZE_27_XTAL_102_05_KHZ 195
+#define MAX_STEP_SIZE_27_XTAL_204_10_KHZ 215
+#define MAX_STEP_SIZE_27_XTAL_306_15_KHZ 203
+#define MAX_STEP_SIZE_27_XTAL_408_20_KHZ 177
+
+#define MXL_HYDRA_SPECTRUM_MIN_FREQ_KHZ 300000
+#define MXL_HYDRA_SPECTRUM_MAX_FREQ_KHZ 2350000
+
+enum MXL_DEMOD_CHAN_PARAMS_OFFSET_E {
+ DMD_STANDARD_ADDR = 0,
+ DMD_SPECTRUM_INVERSION_ADDR,
+ DMD_SPECTRUM_ROLL_OFF_ADDR,
+ DMD_SYMBOL_RATE_ADDR,
+ DMD_MODULATION_SCHEME_ADDR,
+ DMD_FEC_CODE_RATE_ADDR,
+ DMD_SNR_ADDR,
+ DMD_FREQ_OFFSET_ADDR,
+ DMD_CTL_FREQ_OFFSET_ADDR,
+ DMD_STR_FREQ_OFFSET_ADDR,
+ DMD_FTL_FREQ_OFFSET_ADDR,
+ DMD_STR_NBC_SYNC_LOCK_ADDR,
+ DMD_CYCLE_SLIP_COUNT_ADDR,
+ DMD_DISPLAY_IQ_ADDR,
+ DMD_DVBS2_CRC_ERRORS_ADDR,
+ DMD_DVBS2_PER_COUNT_ADDR,
+ DMD_DVBS2_PER_WINDOW_ADDR,
+ DMD_DVBS_CORR_RS_ERRORS_ADDR,
+ DMD_DVBS_UNCORR_RS_ERRORS_ADDR,
+ DMD_DVBS_BER_COUNT_ADDR,
+ DMD_DVBS_BER_WINDOW_ADDR,
+ DMD_TUNER_ID_ADDR,
+ DMD_DVBS2_PILOT_ON_OFF_ADDR,
+ DMD_FREQ_SEARCH_RANGE_IN_KHZ_ADDR,
+
+ MXL_DEMOD_CHAN_PARAMS_BUFF_SIZE,
+};
+
+enum MXL_HYDRA_TUNER_ID_E {
+ MXL_HYDRA_TUNER_ID_0 = 0,
+ MXL_HYDRA_TUNER_ID_1,
+ MXL_HYDRA_TUNER_ID_2,
+ MXL_HYDRA_TUNER_ID_3,
+ MXL_HYDRA_TUNER_MAX
+};
+
+enum MXL_HYDRA_BCAST_STD_E {
+ MXL_HYDRA_DSS = 0,
+ MXL_HYDRA_DVBS,
+ MXL_HYDRA_DVBS2,
+};
+
+enum MXL_HYDRA_FEC_E {
+ MXL_HYDRA_FEC_AUTO = 0,
+ MXL_HYDRA_FEC_1_2,
+ MXL_HYDRA_FEC_3_5,
+ MXL_HYDRA_FEC_2_3,
+ MXL_HYDRA_FEC_3_4,
+ MXL_HYDRA_FEC_4_5,
+ MXL_HYDRA_FEC_5_6,
+ MXL_HYDRA_FEC_6_7,
+ MXL_HYDRA_FEC_7_8,
+ MXL_HYDRA_FEC_8_9,
+ MXL_HYDRA_FEC_9_10,
+};
+
+enum MXL_HYDRA_MODULATION_E {
+ MXL_HYDRA_MOD_AUTO = 0,
+ MXL_HYDRA_MOD_QPSK,
+ MXL_HYDRA_MOD_8PSK
+};
+
+enum MXL_HYDRA_SPECTRUM_E {
+ MXL_HYDRA_SPECTRUM_AUTO = 0,
+ MXL_HYDRA_SPECTRUM_INVERTED,
+ MXL_HYDRA_SPECTRUM_NON_INVERTED,
+};
+
+enum MXL_HYDRA_ROLLOFF_E {
+ MXL_HYDRA_ROLLOFF_AUTO = 0,
+ MXL_HYDRA_ROLLOFF_0_20,
+ MXL_HYDRA_ROLLOFF_0_25,
+ MXL_HYDRA_ROLLOFF_0_35
+};
+
+enum MXL_HYDRA_PILOTS_E {
+ MXL_HYDRA_PILOTS_OFF = 0,
+ MXL_HYDRA_PILOTS_ON,
+ MXL_HYDRA_PILOTS_AUTO
+};
+
+enum MXL_HYDRA_CONSTELLATION_SRC_E {
+ MXL_HYDRA_FORMATTER = 0,
+ MXL_HYDRA_LEGACY_FEC,
+ MXL_HYDRA_FREQ_RECOVERY,
+ MXL_HYDRA_NBC,
+ MXL_HYDRA_CTL,
+ MXL_HYDRA_EQ,
+};
+
+struct MXL_HYDRA_DEMOD_LOCK_T {
+ int agc_lock; /* AGC lock info */
+ int fec_lock; /* Demod FEC block lock info */
+};
+
+struct MXL_HYDRA_DEMOD_STATUS_DVBS_T {
+ u32 rs_errors; /* RS decoder err counter */
+ u32 ber_window; /* Ber Windows */
+ u32 ber_count; /* BER count */
+ u32 ber_window_iter1; /* Ber Windows - post viterbi */
+ u32 ber_count_iter1; /* BER count - post viterbi */
+};
+
+struct MXL_HYDRA_DEMOD_STATUS_DSS_T {
+ u32 rs_errors; /* RS decoder err counter */
+ u32 ber_window; /* Ber Windows */
+ u32 ber_count; /* BER count */
+};
+
+struct MXL_HYDRA_DEMOD_STATUS_DVBS2_T {
+ u32 crc_errors; /* CRC error counter */
+ u32 packet_error_count; /* Number of packet errors */
+ u32 total_packets; /* Total packets */
+};
+
+struct MXL_HYDRA_DEMOD_STATUS_T {
+ enum MXL_HYDRA_BCAST_STD_E standard_mask; /* Standard DVB-S, DVB-S2 or DSS */
+
+ union {
+ struct MXL_HYDRA_DEMOD_STATUS_DVBS_T demod_status_dvbs; /* DVB-S demod status */
+ struct MXL_HYDRA_DEMOD_STATUS_DVBS2_T demod_status_dvbs2; /* DVB-S2 demod status */
+ struct MXL_HYDRA_DEMOD_STATUS_DSS_T demod_status_dss; /* DSS demod status */
+ } u;
+};
+
+struct MXL_HYDRA_DEMOD_SIG_OFFSET_INFO_T {
+ s32 carrier_offset_in_hz; /* CRL offset info */
+ s32 symbol_offset_in_symbol; /* SRL offset info */
+};
+
+struct MXL_HYDRA_DEMOD_SCRAMBLE_INFO_T {
+ u8 scramble_sequence[MXL_DEMOD_SCRAMBLE_SEQ_LEN]; /* scramble sequence */
+ u32 scramble_code; /* scramble gold code */
+};
+
+enum MXL_HYDRA_SPECTRUM_STEP_SIZE_E {
+ MXL_HYDRA_STEP_SIZE_24_XTAL_102_05KHZ, /* 102.05 KHz for 24 MHz XTAL */
+ MXL_HYDRA_STEP_SIZE_24_XTAL_204_10KHZ, /* 204.10 KHz for 24 MHz XTAL */
+ MXL_HYDRA_STEP_SIZE_24_XTAL_306_15KHZ, /* 306.15 KHz for 24 MHz XTAL */
+ MXL_HYDRA_STEP_SIZE_24_XTAL_408_20KHZ, /* 408.20 KHz for 24 MHz XTAL */
+
+ MXL_HYDRA_STEP_SIZE_27_XTAL_102_05KHZ, /* 102.05 KHz for 27 MHz XTAL */
+ MXL_HYDRA_STEP_SIZE_27_XTAL_204_35KHZ, /* 204.35 KHz for 27 MHz XTAL */
+ MXL_HYDRA_STEP_SIZE_27_XTAL_306_52KHZ, /* 306.52 KHz for 27 MHz XTAL */
+ MXL_HYDRA_STEP_SIZE_27_XTAL_408_69KHZ, /* 408.69 KHz for 27 MHz XTAL */
+};
+
+enum MXL_HYDRA_SPECTRUM_RESOLUTION_E {
+ MXL_HYDRA_SPECTRUM_RESOLUTION_00_1_DB, /* 0.1 dB */
+ MXL_HYDRA_SPECTRUM_RESOLUTION_01_0_DB, /* 1.0 dB */
+ MXL_HYDRA_SPECTRUM_RESOLUTION_05_0_DB, /* 5.0 dB */
+ MXL_HYDRA_SPECTRUM_RESOLUTION_10_0_DB, /* 10 dB */
+};
+
+enum MXL_HYDRA_SPECTRUM_ERROR_CODE_E {
+ MXL_SPECTRUM_NO_ERROR,
+ MXL_SPECTRUM_INVALID_PARAMETER,
+ MXL_SPECTRUM_INVALID_STEP_SIZE,
+ MXL_SPECTRUM_BW_CANNOT_BE_COVERED,
+ MXL_SPECTRUM_DEMOD_BUSY,
+ MXL_SPECTRUM_TUNER_NOT_ENABLED,
+};
+
+struct MXL_HYDRA_SPECTRUM_REQ_T {
+ u32 tuner_index; /* TUNER Ctrl: one of MXL58x_TUNER_ID_E */
+ u32 demod_index; /* DEMOD Ctrl: one of MXL58x_DEMOD_ID_E */
+ enum MXL_HYDRA_SPECTRUM_STEP_SIZE_E step_size_in_khz;
+ u32 starting_freq_ink_hz;
+ u32 total_steps;
+ enum MXL_HYDRA_SPECTRUM_RESOLUTION_E spectrum_division;
+};
+
+enum MXL_HYDRA_SEARCH_FREQ_OFFSET_TYPE_E {
+ MXL_HYDRA_SEARCH_MAX_OFFSET = 0, /* DMD searches for max freq offset (i.e. 5MHz) */
+ MXL_HYDRA_SEARCH_BW_PLUS_ROLLOFF, /* DMD searches for BW + ROLLOFF/2 */
+};
+
+struct MXL58X_CFG_FREQ_OFF_SEARCH_RANGE_T {
+ u32 demod_index;
+ enum MXL_HYDRA_SEARCH_FREQ_OFFSET_TYPE_E search_type;
+};
+
+/* there are two slices
+ * slice0 - TS0, TS1, TS2 & TS3
+ * slice1 - TS4, TS5, TS6 & TS7
+ */
+#define MXL_HYDRA_TS_SLICE_MAX 2
+
+#define MAX_FIXED_PID_NUM 32
+
+#define MXL_HYDRA_NCO_CLK 418 /* 418 MHz */
+
+#define MXL_HYDRA_MAX_TS_CLOCK 139 /* 139 MHz */
+
+#define MXL_HYDRA_TS_FIXED_PID_FILT_SIZE 32
+
+#define MXL_HYDRA_SHARED_PID_FILT_SIZE_DEFAULT 33 /* Shared PID filter size in 1-1 mux mode */
+#define MXL_HYDRA_SHARED_PID_FILT_SIZE_2_TO_1 66 /* Shared PID filter size in 2-1 mux mode */
+#define MXL_HYDRA_SHARED_PID_FILT_SIZE_4_TO_1 132 /* Shared PID filter size in 4-1 mux mode */
+
+enum MXL_HYDRA_PID_BANK_TYPE_E {
+ MXL_HYDRA_SOFTWARE_PID_BANK = 0,
+ MXL_HYDRA_HARDWARE_PID_BANK,
+};
+
+enum MXL_HYDRA_TS_MUX_MODE_E {
+ MXL_HYDRA_TS_MUX_PID_REMAP = 0,
+ MXL_HYDRA_TS_MUX_PREFIX_EXTRA_HEADER = 1,
+};
+
+enum MXL_HYDRA_TS_MUX_TYPE_E {
+ MXL_HYDRA_TS_MUX_DISABLE = 0, /* No Mux ( 1 TSIF to 1 TSIF) */
+ MXL_HYDRA_TS_MUX_2_TO_1, /* Mux 2 TSIF to 1 TSIF */
+ MXL_HYDRA_TS_MUX_4_TO_1, /* Mux 4 TSIF to 1 TSIF */
+};
+
+enum MXL_HYDRA_TS_GROUP_E {
+ MXL_HYDRA_TS_GROUP_0_3 = 0, /* TS group 0 to 3 (TS0, TS1, TS2 & TS3) */
+ MXL_HYDRA_TS_GROUP_4_7, /* TS group 0 to 3 (TS4, TS5, TS6 & TS7) */
+};
+
+enum MXL_HYDRA_TS_PID_FLT_CTRL_E {
+ MXL_HYDRA_TS_PIDS_ALLOW_ALL = 0, /* Allow all pids */
+ MXL_HYDRA_TS_PIDS_DROP_ALL, /* Drop all pids */
+ MXL_HYDRA_TS_INVALIDATE_PID_FILTER, /* Delete current PD filter in the device */
+};
+
+enum MXL_HYDRA_TS_PID_TYPE_E {
+ MXL_HYDRA_TS_PID_FIXED = 0,
+ MXL_HYDRA_TS_PID_REGULAR,
+};
+
+struct MXL_HYDRA_TS_PID_T {
+ u16 original_pid; /* pid from TS */
+ u16 remapped_pid; /* remapped pid */
+ enum MXL_BOOL_E enable; /* enable or disable pid */
+ enum MXL_BOOL_E allow_or_drop; /* allow or drop pid */
+ enum MXL_BOOL_E enable_pid_remap; /* enable or disable pid remap */
+ u8 bond_id; /* Bond ID in A0 always 0 - Only for 568 Sku */
+ u8 dest_id; /* Output port ID for the PID - Only for 568 Sku */
+};
+
+struct MXL_HYDRA_TS_MUX_PREFIX_HEADER_T {
+ enum MXL_BOOL_E enable;
+ u8 num_byte;
+ u8 header[12];
+};
+
+enum MXL_HYDRA_PID_FILTER_BANK_E {
+ MXL_HYDRA_PID_BANK_A = 0,
+ MXL_HYDRA_PID_BANK_B,
+};
+
+enum MXL_HYDRA_MPEG_DATA_FMT_E {
+ MXL_HYDRA_MPEG_SERIAL_MSB_1ST = 0,
+ MXL_HYDRA_MPEG_SERIAL_LSB_1ST,
+
+ MXL_HYDRA_MPEG_SYNC_WIDTH_BIT = 0,
+ MXL_HYDRA_MPEG_SYNC_WIDTH_BYTE
+};
+
+enum MXL_HYDRA_MPEG_MODE_E {
+ MXL_HYDRA_MPEG_MODE_SERIAL_4_WIRE = 0, /* MPEG 4 Wire serial mode */
+ MXL_HYDRA_MPEG_MODE_SERIAL_3_WIRE, /* MPEG 3 Wire serial mode */
+ MXL_HYDRA_MPEG_MODE_SERIAL_2_WIRE, /* MPEG 2 Wire serial mode */
+ MXL_HYDRA_MPEG_MODE_PARALLEL /* MPEG parallel mode - valid only for MxL581 */
+};
+
+enum MXL_HYDRA_MPEG_CLK_TYPE_E {
+ MXL_HYDRA_MPEG_CLK_CONTINUOUS = 0, /* Continuous MPEG clock */
+ MXL_HYDRA_MPEG_CLK_GAPPED, /* Gapped (gated) MPEG clock */
+};
+
+enum MXL_HYDRA_MPEG_CLK_FMT_E {
+ MXL_HYDRA_MPEG_ACTIVE_LOW = 0,
+ MXL_HYDRA_MPEG_ACTIVE_HIGH,
+
+ MXL_HYDRA_MPEG_CLK_NEGATIVE = 0,
+ MXL_HYDRA_MPEG_CLK_POSITIVE,
+
+ MXL_HYDRA_MPEG_CLK_IN_PHASE = 0,
+ MXL_HYDRA_MPEG_CLK_INVERTED,
+};
+
+enum MXL_HYDRA_MPEG_CLK_PHASE_E {
+ MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_0_DEG = 0,
+ MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_90_DEG,
+ MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_180_DEG,
+ MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_270_DEG
+};
+
+enum MXL_HYDRA_MPEG_ERR_INDICATION_E {
+ MXL_HYDRA_MPEG_ERR_REPLACE_SYNC = 0,
+ MXL_HYDRA_MPEG_ERR_REPLACE_VALID,
+ MXL_HYDRA_MPEG_ERR_INDICATION_DISABLED
+};
+
+struct MXL_HYDRA_MPEGOUT_PARAM_T {
+ int enable; /* Enable or Disable MPEG OUT */
+ enum MXL_HYDRA_MPEG_CLK_TYPE_E mpeg_clk_type; /* Continuous or gapped */
+ enum MXL_HYDRA_MPEG_CLK_FMT_E mpeg_clk_pol; /* MPEG Clk polarity */
+ u8 max_mpeg_clk_rate; /* Max MPEG Clk rate (0 - 104 MHz, 139 MHz) */
+ enum MXL_HYDRA_MPEG_CLK_PHASE_E mpeg_clk_phase; /* MPEG Clk phase */
+ enum MXL_HYDRA_MPEG_DATA_FMT_E lsb_or_msb_first; /* LSB first or MSB first in TS transmission */
+ enum MXL_HYDRA_MPEG_DATA_FMT_E mpeg_sync_pulse_width; /* MPEG SYNC pulse width (1-bit or 1-byte) */
+ enum MXL_HYDRA_MPEG_CLK_FMT_E mpeg_valid_pol; /* MPEG VALID polarity */
+ enum MXL_HYDRA_MPEG_CLK_FMT_E mpeg_sync_pol; /* MPEG SYNC polarity */
+ enum MXL_HYDRA_MPEG_MODE_E mpeg_mode; /* config 4/3/2-wire serial or parallel TS out */
+ enum MXL_HYDRA_MPEG_ERR_INDICATION_E mpeg_error_indication; /* Enable or Disable MPEG error indication */
+};
+
+enum MXL_HYDRA_EXT_TS_IN_ID_E {
+ MXL_HYDRA_EXT_TS_IN_0 = 0,
+ MXL_HYDRA_EXT_TS_IN_1,
+ MXL_HYDRA_EXT_TS_IN_2,
+ MXL_HYDRA_EXT_TS_IN_3,
+ MXL_HYDRA_EXT_TS_IN_MAX
+};
+
+enum MXL_HYDRA_TS_OUT_ID_E {
+ MXL_HYDRA_TS_OUT_0 = 0,
+ MXL_HYDRA_TS_OUT_1,
+ MXL_HYDRA_TS_OUT_2,
+ MXL_HYDRA_TS_OUT_3,
+ MXL_HYDRA_TS_OUT_4,
+ MXL_HYDRA_TS_OUT_5,
+ MXL_HYDRA_TS_OUT_6,
+ MXL_HYDRA_TS_OUT_7,
+ MXL_HYDRA_TS_OUT_MAX
+};
+
+enum MXL_HYDRA_TS_DRIVE_STRENGTH_E {
+ MXL_HYDRA_TS_DRIVE_STRENGTH_1X = 0,
+ MXL_HYDRA_TS_DRIVE_STRENGTH_2X,
+ MXL_HYDRA_TS_DRIVE_STRENGTH_3X,
+ MXL_HYDRA_TS_DRIVE_STRENGTH_4X,
+ MXL_HYDRA_TS_DRIVE_STRENGTH_5X,
+ MXL_HYDRA_TS_DRIVE_STRENGTH_6X,
+ MXL_HYDRA_TS_DRIVE_STRENGTH_7X,
+ MXL_HYDRA_TS_DRIVE_STRENGTH_8X
+};
+
+enum MXL_HYDRA_DEVICE_E {
+ MXL_HYDRA_DEVICE_581 = 0,
+ MXL_HYDRA_DEVICE_584,
+ MXL_HYDRA_DEVICE_585,
+ MXL_HYDRA_DEVICE_544,
+ MXL_HYDRA_DEVICE_561,
+ MXL_HYDRA_DEVICE_TEST,
+ MXL_HYDRA_DEVICE_582,
+ MXL_HYDRA_DEVICE_541,
+ MXL_HYDRA_DEVICE_568,
+ MXL_HYDRA_DEVICE_542,
+ MXL_HYDRA_DEVICE_541S,
+ MXL_HYDRA_DEVICE_561S,
+ MXL_HYDRA_DEVICE_581S,
+ MXL_HYDRA_DEVICE_MAX
+};
+
+/* Demod IQ data */
+struct MXL_HYDRA_DEMOD_IQ_SRC_T {
+ u32 demod_id;
+ u32 source_of_iq; /* == 0, it means I/Q comes from Formatter
+ * == 1, Legacy FEC
+ * == 2, Frequency Recovery
+ * == 3, NBC
+ * == 4, CTL
+ * == 5, EQ
+ * == 6, FPGA
+ */
+};
+
+struct MXL_HYDRA_DEMOD_ABORT_TUNE_T {
+ u32 demod_id;
+};
+
+struct MXL_HYDRA_TUNER_CMD {
+ u8 tuner_id;
+ u8 enable;
+};
+
+/* Demod Para for Channel Tune */
+struct MXL_HYDRA_DEMOD_PARAM_T {
+ u32 tuner_index;
+ u32 demod_index;
+ u32 frequency_in_hz; /* Frequency */
+ u32 standard; /* one of MXL_HYDRA_BCAST_STD_E */
+ u32 spectrum_inversion; /* Input : Spectrum inversion. */
+ u32 roll_off; /* rollOff (alpha) factor */
+ u32 symbol_rate_in_hz; /* Symbol rate */
+ u32 pilots; /* TRUE = pilots enabled */
+ u32 modulation_scheme; /* Input : Modulation Scheme is one of MXL_HYDRA_MODULATION_E */
+ u32 fec_code_rate; /* Input : Forward error correction rate. Is one of MXL_HYDRA_FEC_E */
+ u32 max_carrier_offset_in_mhz; /* Maximum carrier freq offset in MHz. Same as freqSearchRangeKHz, but in unit of MHz. */
+};
+
+struct MXL_HYDRA_DEMOD_SCRAMBLE_CODE_T {
+ u32 demod_index;
+ u8 scramble_sequence[12]; /* scramble sequence */
+ u32 scramble_code; /* scramble gold code */
+};
+
+struct MXL_INTR_CFG_T {
+ u32 intr_type;
+ u32 intr_duration_in_nano_secs;
+ u32 intr_mask;
+};
+
+struct MXL_HYDRA_POWER_MODE_CMD {
+ u8 power_mode; /* enumeration values are defined in MXL_HYDRA_PWR_MODE_E (device API.h) */
+};
+
+struct MXL_HYDRA_RF_WAKEUP_PARAM_T {
+ u32 time_interval_in_seconds; /* in seconds */
+ u32 tuner_index;
+ s32 rssi_threshold;
+};
+
+struct MXL_HYDRA_RF_WAKEUP_CFG_T {
+ u32 tuner_count;
+ struct MXL_HYDRA_RF_WAKEUP_PARAM_T params;
+};
+
+enum MXL_HYDRA_AUX_CTRL_MODE_E {
+ MXL_HYDRA_AUX_CTRL_MODE_FSK = 0, /* Select FSK controller */
+ MXL_HYDRA_AUX_CTRL_MODE_DISEQC, /* Select DiSEqC controller */
+};
+
+enum MXL_HYDRA_DISEQC_OPMODE_E {
+ MXL_HYDRA_DISEQC_ENVELOPE_MODE = 0,
+ MXL_HYDRA_DISEQC_TONE_MODE,
+};
+
+enum MXL_HYDRA_DISEQC_VER_E {
+ MXL_HYDRA_DISEQC_1_X = 0, /* Config DiSEqC 1.x mode */
+ MXL_HYDRA_DISEQC_2_X, /* Config DiSEqC 2.x mode */
+ MXL_HYDRA_DISEQC_DISABLE /* Disable DiSEqC */
+};
+
+enum MXL_HYDRA_DISEQC_CARRIER_FREQ_E {
+ MXL_HYDRA_DISEQC_CARRIER_FREQ_22KHZ = 0, /* DiSEqC signal frequency of 22 KHz */
+ MXL_HYDRA_DISEQC_CARRIER_FREQ_33KHZ, /* DiSEqC signal frequency of 33 KHz */
+ MXL_HYDRA_DISEQC_CARRIER_FREQ_44KHZ /* DiSEqC signal frequency of 44 KHz */
+};
+
+enum MXL_HYDRA_DISEQC_ID_E {
+ MXL_HYDRA_DISEQC_ID_0 = 0,
+ MXL_HYDRA_DISEQC_ID_1,
+ MXL_HYDRA_DISEQC_ID_2,
+ MXL_HYDRA_DISEQC_ID_3
+};
+
+enum MXL_HYDRA_FSK_OP_MODE_E {
+ MXL_HYDRA_FSK_CFG_TYPE_39KPBS = 0, /* 39.0kbps */
+ MXL_HYDRA_FSK_CFG_TYPE_39_017KPBS, /* 39.017kbps */
+ MXL_HYDRA_FSK_CFG_TYPE_115_2KPBS /* 115.2kbps */
+};
+
+struct MXL58X_DSQ_OP_MODE_T {
+ u32 diseqc_id; /* DSQ 0, 1, 2 or 3 */
+ u32 op_mode; /* Envelope mode (0) or internal tone mode (1) */
+ u32 version; /* 0: 1.0, 1: 1.1, 2: Disable */
+ u32 center_freq; /* 0: 22KHz, 1: 33KHz and 2: 44 KHz */
+};
+
+struct MXL_HYDRA_DISEQC_CFG_CONT_TONE_T {
+ u32 diseqc_id;
+ u32 cont_tone_flag; /* 1: Enable , 0: Disable */
+};
diff --git a/drivers/media/dvb-frontends/mxl5xx_regs.h b/drivers/media/dvb-frontends/mxl5xx_regs.h
new file mode 100644
index 000000000000..b38a13847033
--- /dev/null
+++ b/drivers/media/dvb-frontends/mxl5xx_regs.h
@@ -0,0 +1,358 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2011-2013 MaxLinear, Inc. All rights reserved
+ *
+ * This program may alternatively be licensed under a proprietary license from
+ * MaxLinear, Inc.
+ *
+ */
+
+#ifndef __MXL58X_REGISTERS_H__
+#define __MXL58X_REGISTERS_H__
+
+#define HYDRA_INTR_STATUS_REG 0x80030008
+#define HYDRA_INTR_MASK_REG 0x8003000C
+
+#define HYDRA_CRYSTAL_SETTING 0x3FFFC5F0 /* 0 - 24 MHz & 1 - 27 MHz */
+#define HYDRA_CRYSTAL_CAP 0x3FFFEDA4 /* 0 - 24 MHz & 1 - 27 MHz */
+
+#define HYDRA_CPU_RESET_REG 0x8003003C
+#define HYDRA_CPU_RESET_DATA 0x00000400
+
+#define HYDRA_RESET_TRANSPORT_FIFO_REG 0x80030028
+#define HYDRA_RESET_TRANSPORT_FIFO_DATA 0x00000000
+
+#define HYDRA_RESET_BBAND_REG 0x80030024
+#define HYDRA_RESET_BBAND_DATA 0x00000000
+
+#define HYDRA_RESET_XBAR_REG 0x80030020
+#define HYDRA_RESET_XBAR_DATA 0x00000000
+
+#define HYDRA_MODULES_CLK_1_REG 0x80030014
+#define HYDRA_DISABLE_CLK_1 0x00000000
+
+#define HYDRA_MODULES_CLK_2_REG 0x8003001C
+#define HYDRA_DISABLE_CLK_2 0x0000000B
+
+#define HYDRA_PRCM_ROOT_CLK_REG 0x80030018
+#define HYDRA_PRCM_ROOT_CLK_DISABLE 0x00000000
+
+#define HYDRA_CPU_RESET_CHECK_REG 0x80030008
+#define HYDRA_CPU_RESET_CHECK_OFFSET 0x40000000 /* <bit 30> */
+
+#define HYDRA_SKU_ID_REG 0x90000190
+
+#define FW_DL_SIGN_ADDR 0x3FFFEAE0
+
+/* Register to check if FW is running or not */
+#define HYDRA_HEAR_BEAT 0x3FFFEDDC
+
+/* Firmware version */
+#define HYDRA_FIRMWARE_VERSION 0x3FFFEDB8
+#define HYDRA_FW_RC_VERSION 0x3FFFCFAC
+
+/* Firmware patch version */
+#define HYDRA_FIRMWARE_PATCH_VERSION 0x3FFFEDC2
+
+/* SOC operating temperature in C */
+#define HYDRA_TEMPARATURE 0x3FFFEDB4
+
+/* Demod & Tuner status registers */
+/* Demod 0 status base address */
+#define HYDRA_DEMOD_0_BASE_ADDR 0x3FFFC64C
+
+/* Tuner 0 status base address */
+#define HYDRA_TUNER_0_BASE_ADDR 0x3FFFCE4C
+
+#define POWER_FROM_ADCRSSI_READBACK 0x3FFFEB6C
+
+/* Macros to determine base address of respective demod or tuner */
+#define HYDRA_DMD_STATUS_OFFSET(demodID) ((demodID) * 0x100)
+#define HYDRA_TUNER_STATUS_OFFSET(tunerID) ((tunerID) * 0x40)
+
+/* Demod status address offset from respective demod's base address */
+#define HYDRA_DMD_AGC_DIG_LEVEL_ADDR_OFFSET 0x3FFFC64C
+#define HYDRA_DMD_LOCK_STATUS_ADDR_OFFSET 0x3FFFC650
+#define HYDRA_DMD_ACQ_STATUS_ADDR_OFFSET 0x3FFFC654
+
+#define HYDRA_DMD_STANDARD_ADDR_OFFSET 0x3FFFC658
+#define HYDRA_DMD_SPECTRUM_INVERSION_ADDR_OFFSET 0x3FFFC65C
+#define HYDRA_DMD_SPECTRUM_ROLL_OFF_ADDR_OFFSET 0x3FFFC660
+#define HYDRA_DMD_SYMBOL_RATE_ADDR_OFFSET 0x3FFFC664
+#define HYDRA_DMD_MODULATION_SCHEME_ADDR_OFFSET 0x3FFFC668
+#define HYDRA_DMD_FEC_CODE_RATE_ADDR_OFFSET 0x3FFFC66C
+
+#define HYDRA_DMD_SNR_ADDR_OFFSET 0x3FFFC670
+#define HYDRA_DMD_FREQ_OFFSET_ADDR_OFFSET 0x3FFFC674
+#define HYDRA_DMD_CTL_FREQ_OFFSET_ADDR_OFFSET 0x3FFFC678
+#define HYDRA_DMD_STR_FREQ_OFFSET_ADDR_OFFSET 0x3FFFC67C
+#define HYDRA_DMD_FTL_FREQ_OFFSET_ADDR_OFFSET 0x3FFFC680
+#define HYDRA_DMD_STR_NBC_SYNC_LOCK_ADDR_OFFSET 0x3FFFC684
+#define HYDRA_DMD_CYCLE_SLIP_COUNT_ADDR_OFFSET 0x3FFFC688
+
+#define HYDRA_DMD_DISPLAY_I_ADDR_OFFSET 0x3FFFC68C
+#define HYDRA_DMD_DISPLAY_Q_ADDR_OFFSET 0x3FFFC68E
+
+#define HYDRA_DMD_DVBS2_CRC_ERRORS_ADDR_OFFSET 0x3FFFC690
+#define HYDRA_DMD_DVBS2_PER_COUNT_ADDR_OFFSET 0x3FFFC694
+#define HYDRA_DMD_DVBS2_PER_WINDOW_ADDR_OFFSET 0x3FFFC698
+
+#define HYDRA_DMD_DVBS_CORR_RS_ERRORS_ADDR_OFFSET 0x3FFFC69C
+#define HYDRA_DMD_DVBS_UNCORR_RS_ERRORS_ADDR_OFFSET 0x3FFFC6A0
+#define HYDRA_DMD_DVBS_BER_COUNT_ADDR_OFFSET 0x3FFFC6A4
+#define HYDRA_DMD_DVBS_BER_WINDOW_ADDR_OFFSET 0x3FFFC6A8
+
+/* Debug-purpose DVB-S DMD 0 */
+#define HYDRA_DMD_DVBS_1ST_CORR_RS_ERRORS_ADDR_OFFSET 0x3FFFC6C8 /* corrected RS Errors: 1st iteration */
+#define HYDRA_DMD_DVBS_1ST_UNCORR_RS_ERRORS_ADDR_OFFSET 0x3FFFC6CC /* uncorrected RS Errors: 1st iteration */
+#define HYDRA_DMD_DVBS_BER_COUNT_1ST_ADDR_OFFSET 0x3FFFC6D0
+#define HYDRA_DMD_DVBS_BER_WINDOW_1ST_ADDR_OFFSET 0x3FFFC6D4
+
+#define HYDRA_DMD_TUNER_ID_ADDR_OFFSET 0x3FFFC6AC
+#define HYDRA_DMD_DVBS2_PILOT_ON_OFF_ADDR_OFFSET 0x3FFFC6B0
+#define HYDRA_DMD_FREQ_SEARCH_RANGE_KHZ_ADDR_OFFSET 0x3FFFC6B4
+#define HYDRA_DMD_STATUS_LOCK_ADDR_OFFSET 0x3FFFC6B8
+#define HYDRA_DMD_STATUS_CENTER_FREQ_IN_KHZ_ADDR 0x3FFFC704
+#define HYDRA_DMD_STATUS_INPUT_POWER_ADDR 0x3FFFC708
+
+/* DVB-S new scaled_BER_count for a new BER API, see HYDRA-1343 "DVB-S post viterbi information" */
+#define DMD0_STATUS_DVBS_1ST_SCALED_BER_COUNT_ADDR 0x3FFFC710 /* DMD 0: 1st iteration BER count scaled by HYDRA_BER_COUNT_SCALING_FACTOR */
+#define DMD0_STATUS_DVBS_SCALED_BER_COUNT_ADDR 0x3FFFC714 /* DMD 0: 2nd iteration BER count scaled by HYDRA_BER_COUNT_SCALING_FACTOR */
+
+#define DMD0_SPECTRUM_MIN_GAIN_STATUS 0x3FFFC73C
+#define DMD0_SPECTRUM_MIN_GAIN_WB_SAGC_VALUE 0x3FFFC740
+#define DMD0_SPECTRUM_MIN_GAIN_NB_SAGC_VALUE 0x3FFFC744
+
+#define HYDRA_DMD_STATUS_END_ADDR_OFFSET 0x3FFFC748
+
+/* Tuner status address offset from respective tuners's base address */
+#define HYDRA_TUNER_DEMOD_ID_ADDR_OFFSET 0x3FFFCE4C
+#define HYDRA_TUNER_AGC_LOCK_OFFSET 0x3FFFCE50
+#define HYDRA_TUNER_SPECTRUM_STATUS_OFFSET 0x3FFFCE54
+#define HYDRA_TUNER_SPECTRUM_BIN_SIZE_OFFSET 0x3FFFCE58
+#define HYDRA_TUNER_SPECTRUM_ADDRESS_OFFSET 0x3FFFCE5C
+#define HYDRA_TUNER_ENABLE_COMPLETE 0x3FFFEB78
+
+#define HYDRA_DEMOD_STATUS_LOCK(devId, demodId) write_register(devId, (HYDRA_DMD_STATUS_LOCK_ADDR_OFFSET + HYDRA_DMD_STATUS_OFFSET(demodId)), MXL_YES)
+#define HYDRA_DEMOD_STATUS_UNLOCK(devId, demodId) write_register(devId, (HYDRA_DMD_STATUS_LOCK_ADDR_OFFSET + HYDRA_DMD_STATUS_OFFSET(demodId)), MXL_NO)
+
+#define HYDRA_VERSION 0x3FFFEDB8
+#define HYDRA_DEMOD0_VERSION 0x3FFFEDBC
+#define HYDRA_DEMOD1_VERSION 0x3FFFEDC0
+#define HYDRA_DEMOD2_VERSION 0x3FFFEDC4
+#define HYDRA_DEMOD3_VERSION 0x3FFFEDC8
+#define HYDRA_DEMOD4_VERSION 0x3FFFEDCC
+#define HYDRA_DEMOD5_VERSION 0x3FFFEDD0
+#define HYDRA_DEMOD6_VERSION 0x3FFFEDD4
+#define HYDRA_DEMOD7_VERSION 0x3FFFEDD8
+#define HYDRA_HEAR_BEAT 0x3FFFEDDC
+#define HYDRA_SKU_MGMT 0x3FFFEBC0
+
+#define MXL_HYDRA_FPGA_A_ADDRESS 0x91C00000
+#define MXL_HYDRA_FPGA_B_ADDRESS 0x91D00000
+
+/* TS control base address */
+#define HYDRA_TS_CTRL_BASE_ADDR 0x90700000
+
+#define MPEG_MUX_MODE_SLICE0_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x08)
+
+#define MPEG_MUX_MODE_SLICE1_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x08)
+
+#define PID_BANK_SEL_SLICE0_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x190)
+#define PID_BANK_SEL_SLICE1_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x1B0)
+
+#define MPEG_CLK_GATED_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x20)
+
+#define MPEG_CLK_ALWAYS_ON_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x1D4)
+
+#define HYDRA_REGULAR_PID_BANK_A_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x190)
+
+#define HYDRA_FIXED_PID_BANK_A_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x190)
+
+#define HYDRA_REGULAR_PID_BANK_B_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x1B0)
+
+#define HYDRA_FIXED_PID_BANK_B_REG (HYDRA_TS_CTRL_BASE_ADDR + 0x1B0)
+
+#define FIXED_PID_TBL_REG_ADDRESS_0 (HYDRA_TS_CTRL_BASE_ADDR + 0x9000)
+#define FIXED_PID_TBL_REG_ADDRESS_1 (HYDRA_TS_CTRL_BASE_ADDR + 0x9100)
+#define FIXED_PID_TBL_REG_ADDRESS_2 (HYDRA_TS_CTRL_BASE_ADDR + 0x9200)
+#define FIXED_PID_TBL_REG_ADDRESS_3 (HYDRA_TS_CTRL_BASE_ADDR + 0x9300)
+
+#define FIXED_PID_TBL_REG_ADDRESS_4 (HYDRA_TS_CTRL_BASE_ADDR + 0xB000)
+#define FIXED_PID_TBL_REG_ADDRESS_5 (HYDRA_TS_CTRL_BASE_ADDR + 0xB100)
+#define FIXED_PID_TBL_REG_ADDRESS_6 (HYDRA_TS_CTRL_BASE_ADDR + 0xB200)
+#define FIXED_PID_TBL_REG_ADDRESS_7 (HYDRA_TS_CTRL_BASE_ADDR + 0xB300)
+
+#define REGULAR_PID_TBL_REG_ADDRESS_0 (HYDRA_TS_CTRL_BASE_ADDR + 0x8000)
+#define REGULAR_PID_TBL_REG_ADDRESS_1 (HYDRA_TS_CTRL_BASE_ADDR + 0x8200)
+#define REGULAR_PID_TBL_REG_ADDRESS_2 (HYDRA_TS_CTRL_BASE_ADDR + 0x8400)
+#define REGULAR_PID_TBL_REG_ADDRESS_3 (HYDRA_TS_CTRL_BASE_ADDR + 0x8600)
+
+#define REGULAR_PID_TBL_REG_ADDRESS_4 (HYDRA_TS_CTRL_BASE_ADDR + 0xA000)
+#define REGULAR_PID_TBL_REG_ADDRESS_5 (HYDRA_TS_CTRL_BASE_ADDR + 0xA200)
+#define REGULAR_PID_TBL_REG_ADDRESS_6 (HYDRA_TS_CTRL_BASE_ADDR + 0xA400)
+#define REGULAR_PID_TBL_REG_ADDRESS_7 (HYDRA_TS_CTRL_BASE_ADDR + 0xA600)
+
+/***************************************************************************/
+
+#define PAD_MUX_GPIO_00_SYNC_BASEADDR 0x90000188
+
+
+#define PAD_MUX_UART_RX_C_PINMUX_BASEADDR 0x9000001C
+
+#define XPT_PACKET_GAP_MIN_BASEADDR 0x90700044
+#define XPT_NCO_COUNT_BASEADDR 0x90700238
+
+#define XPT_NCO_COUNT_BASEADDR1 0x9070023C
+
+/* V2 DigRF status register */
+
+#define XPT_PID_BASEADDR 0x90708000
+
+#define XPT_PID_REMAP_BASEADDR 0x90708004
+
+#define XPT_KNOWN_PID_BASEADDR 0x90709000
+
+#define XPT_PID_BASEADDR1 0x9070A000
+
+#define XPT_PID_REMAP_BASEADDR1 0x9070A004
+
+#define XPT_KNOWN_PID_BASEADDR1 0x9070B000
+
+#define XPT_BERT_LOCK_BASEADDR 0x907000B8
+
+#define XPT_BERT_BASEADDR 0x907000BC
+
+#define XPT_BERT_INVERT_BASEADDR 0x907000C0
+
+#define XPT_BERT_HEADER_BASEADDR 0x907000C4
+
+#define XPT_BERT_BASEADDR1 0x907000C8
+
+#define XPT_BERT_BIT_COUNT0_BASEADDR 0x907000CC
+
+#define XPT_BERT_BIT_COUNT0_BASEADDR1 0x907000D0
+
+#define XPT_BERT_BIT_COUNT1_BASEADDR 0x907000D4
+
+#define XPT_BERT_BIT_COUNT1_BASEADDR1 0x907000D8
+
+#define XPT_BERT_BIT_COUNT2_BASEADDR 0x907000DC
+
+#define XPT_BERT_BIT_COUNT2_BASEADDR1 0x907000E0
+
+#define XPT_BERT_BIT_COUNT3_BASEADDR 0x907000E4
+
+#define XPT_BERT_BIT_COUNT3_BASEADDR1 0x907000E8
+
+#define XPT_BERT_BIT_COUNT4_BASEADDR 0x907000EC
+
+#define XPT_BERT_BIT_COUNT4_BASEADDR1 0x907000F0
+
+#define XPT_BERT_BIT_COUNT5_BASEADDR 0x907000F4
+
+#define XPT_BERT_BIT_COUNT5_BASEADDR1 0x907000F8
+
+#define XPT_BERT_BIT_COUNT6_BASEADDR 0x907000FC
+
+#define XPT_BERT_BIT_COUNT6_BASEADDR1 0x90700100
+
+#define XPT_BERT_BIT_COUNT7_BASEADDR 0x90700104
+
+#define XPT_BERT_BIT_COUNT7_BASEADDR1 0x90700108
+
+#define XPT_BERT_ERR_COUNT0_BASEADDR 0x9070010C
+
+#define XPT_BERT_ERR_COUNT0_BASEADDR1 0x90700110
+
+#define XPT_BERT_ERR_COUNT1_BASEADDR 0x90700114
+
+#define XPT_BERT_ERR_COUNT1_BASEADDR1 0x90700118
+
+#define XPT_BERT_ERR_COUNT2_BASEADDR 0x9070011C
+
+#define XPT_BERT_ERR_COUNT2_BASEADDR1 0x90700120
+
+#define XPT_BERT_ERR_COUNT3_BASEADDR 0x90700124
+
+#define XPT_BERT_ERR_COUNT3_BASEADDR1 0x90700128
+
+#define XPT_BERT_ERR_COUNT4_BASEADDR 0x9070012C
+
+#define XPT_BERT_ERR_COUNT4_BASEADDR1 0x90700130
+
+#define XPT_BERT_ERR_COUNT5_BASEADDR 0x90700134
+
+#define XPT_BERT_ERR_COUNT5_BASEADDR1 0x90700138
+
+#define XPT_BERT_ERR_COUNT6_BASEADDR 0x9070013C
+
+#define XPT_BERT_ERR_COUNT6_BASEADDR1 0x90700140
+
+#define XPT_BERT_ERR_COUNT7_BASEADDR 0x90700144
+
+#define XPT_BERT_ERR_COUNT7_BASEADDR1 0x90700148
+
+#define XPT_BERT_ERROR_BASEADDR 0x9070014C
+
+#define XPT_BERT_ANALYZER_BASEADDR 0x90700150
+
+#define XPT_BERT_ANALYZER_BASEADDR1 0x90700154
+
+#define XPT_BERT_ANALYZER_BASEADDR2 0x90700158
+
+#define XPT_BERT_ANALYZER_BASEADDR3 0x9070015C
+
+#define XPT_BERT_ANALYZER_BASEADDR4 0x90700160
+
+#define XPT_BERT_ANALYZER_BASEADDR5 0x90700164
+
+#define XPT_BERT_ANALYZER_BASEADDR6 0x90700168
+
+#define XPT_BERT_ANALYZER_BASEADDR7 0x9070016C
+
+#define XPT_BERT_ANALYZER_BASEADDR8 0x90700170
+
+#define XPT_BERT_ANALYZER_BASEADDR9 0x90700174
+
+#define XPT_DMD0_BASEADDR 0x9070024C
+
+/* V2 AGC Gain Freeze & step */
+#define DBG_ENABLE_DISABLE_AGC (0x3FFFCF60) /* 1: DISABLE, 0:ENABLE */
+#define WB_DFE0_DFE_FB_RF1_BASEADDR 0x903004A4
+
+#define WB_DFE1_DFE_FB_RF1_BASEADDR 0x904004A4
+
+#define WB_DFE2_DFE_FB_RF1_BASEADDR 0x905004A4
+
+#define WB_DFE3_DFE_FB_RF1_BASEADDR 0x906004A4
+
+#define AFE_REG_D2A_TA_RFFE_LNA_BO_1P8_BASEADDR 0x90200104
+
+#define AFE_REG_AFE_REG_SPARE_BASEADDR 0x902000A0
+
+#define AFE_REG_AFE_REG_SPARE_BASEADDR1 0x902000B4
+
+#define AFE_REG_AFE_REG_SPARE_BASEADDR2 0x902000C4
+
+#define AFE_REG_AFE_REG_SPARE_BASEADDR3 0x902000D4
+
+#define WB_DFE0_DFE_FB_AGC_BASEADDR 0x90300498
+
+#define WB_DFE1_DFE_FB_AGC_BASEADDR 0x90400498
+
+#define WB_DFE2_DFE_FB_AGC_BASEADDR 0x90500498
+
+#define WB_DFE3_DFE_FB_AGC_BASEADDR 0x90600498
+
+#define WDT_WD_INT_BASEADDR 0x8002000C
+
+#define FSK_TX_FTM_BASEADDR 0x80090000
+
+#define FSK_TX_FTM_TX_CNT_BASEADDR 0x80090018
+
+#define AFE_REG_D2A_FSK_BIAS_BASEADDR 0x90200040
+
+#define DMD_TEI_BASEADDR 0x3FFFEBE0
+
+#endif /* __MXL58X_REGISTERS_H__ */
diff --git a/drivers/media/dvb-frontends/mxl692.c b/drivers/media/dvb-frontends/mxl692.c
new file mode 100644
index 000000000000..bbc2bc778225
--- /dev/null
+++ b/drivers/media/dvb-frontends/mxl692.c
@@ -0,0 +1,1368 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the MaxLinear MxL69x family of combo tuners/demods
+ *
+ * Copyright (C) 2020 Brad Love <brad@nextdimension.cc>
+ *
+ * based on code:
+ * Copyright (c) 2016 MaxLinear, Inc. All rights reserved
+ * which was released under GPL V2
+ */
+
+#include <linux/mutex.h>
+#include <linux/i2c-mux.h>
+#include <linux/string.h>
+#include <linux/firmware.h>
+
+#include "mxl692.h"
+#include "mxl692_defs.h"
+
+static const struct dvb_frontend_ops mxl692_ops;
+
+struct mxl692_dev {
+ struct dvb_frontend fe;
+ struct i2c_client *i2c_client;
+ struct mutex i2c_lock; /* i2c command mutex */
+ enum MXL_EAGLE_DEMOD_TYPE_E demod_type;
+ enum MXL_EAGLE_POWER_MODE_E power_mode;
+ u32 current_frequency;
+ int device_type;
+ int seqnum;
+ int init_done;
+};
+
+static int mxl692_i2c_write(struct mxl692_dev *dev, u8 *buffer, u16 buf_len)
+{
+ int ret = 0;
+ struct i2c_msg msg = {
+ .addr = dev->i2c_client->addr,
+ .flags = 0,
+ .buf = buffer,
+ .len = buf_len
+ };
+
+ ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1);
+ if (ret != 1)
+ dev_dbg(&dev->i2c_client->dev, "i2c write error!\n");
+
+ return ret;
+}
+
+static int mxl692_i2c_read(struct mxl692_dev *dev, u8 *buffer, u16 buf_len)
+{
+ int ret = 0;
+ struct i2c_msg msg = {
+ .addr = dev->i2c_client->addr,
+ .flags = I2C_M_RD,
+ .buf = buffer,
+ .len = buf_len
+ };
+
+ ret = i2c_transfer(dev->i2c_client->adapter, &msg, 1);
+ if (ret != 1)
+ dev_dbg(&dev->i2c_client->dev, "i2c read error!\n");
+
+ return ret;
+}
+
+static int convert_endian(u32 size, u8 *d)
+{
+ u32 i;
+
+ for (i = 0; i < (size & ~3); i += 4) {
+ d[i + 0] ^= d[i + 3];
+ d[i + 3] ^= d[i + 0];
+ d[i + 0] ^= d[i + 3];
+
+ d[i + 1] ^= d[i + 2];
+ d[i + 2] ^= d[i + 1];
+ d[i + 1] ^= d[i + 2];
+ }
+
+ switch (size & 3) {
+ case 0:
+ case 1:
+ /* do nothing */
+ break;
+ case 2:
+ d[i + 0] ^= d[i + 1];
+ d[i + 1] ^= d[i + 0];
+ d[i + 0] ^= d[i + 1];
+ break;
+
+ case 3:
+ d[i + 0] ^= d[i + 2];
+ d[i + 2] ^= d[i + 0];
+ d[i + 0] ^= d[i + 2];
+ break;
+ }
+ return size;
+}
+
+static int convert_endian_n(int n, u32 size, u8 *d)
+{
+ int i, count = 0;
+
+ for (i = 0; i < n; i += size)
+ count += convert_endian(size, d + i);
+ return count;
+}
+
+static void mxl692_tx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer)
+{
+#ifdef __BIG_ENDIAN
+ return;
+#endif
+ buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */
+
+ switch (opcode) {
+ case MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET:
+ case MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET:
+ case MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET:
+ buffer += convert_endian(sizeof(u32), buffer);
+ break;
+ case MXL_EAGLE_OPCODE_QAM_PARAMS_SET:
+ buffer += 5;
+ buffer += convert_endian(2 * sizeof(u32), buffer);
+ break;
+ default:
+ /* no swapping - all get opcodes */
+ /* ATSC/OOB no swapping */
+ break;
+ }
+}
+
+static void mxl692_rx_swap(enum MXL_EAGLE_OPCODE_E opcode, u8 *buffer)
+{
+#ifdef __BIG_ENDIAN
+ return;
+#endif
+ buffer += MXL_EAGLE_HOST_MSG_HEADER_SIZE; /* skip API header */
+
+ switch (opcode) {
+ case MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET:
+ buffer++;
+ buffer += convert_endian(2 * sizeof(u16), buffer);
+ break;
+ case MXL_EAGLE_OPCODE_ATSC_STATUS_GET:
+ buffer += convert_endian_n(2, sizeof(u16), buffer);
+ buffer += convert_endian(sizeof(u32), buffer);
+ break;
+ case MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET:
+ buffer += convert_endian(3 * sizeof(u32), buffer);
+ break;
+ case MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET:
+ buffer += convert_endian_n(24, sizeof(u16), buffer);
+ break;
+ case MXL_EAGLE_OPCODE_QAM_STATUS_GET:
+ buffer += 8;
+ buffer += convert_endian_n(2, sizeof(u16), buffer);
+ buffer += convert_endian(sizeof(u32), buffer);
+ break;
+ case MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET:
+ buffer += convert_endian(7 * sizeof(u32), buffer);
+ break;
+ case MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET:
+ case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET:
+ case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET:
+ case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET:
+ case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET:
+ buffer += convert_endian_n(24, sizeof(u16), buffer);
+ break;
+ case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET:
+ buffer += convert_endian_n(8, sizeof(u16), buffer);
+ break;
+ case MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET:
+ buffer += convert_endian_n(17, sizeof(u16), buffer);
+ break;
+ case MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET:
+ buffer += convert_endian(3 * sizeof(u32), buffer);
+ break;
+ case MXL_EAGLE_OPCODE_OOB_STATUS_GET:
+ buffer += convert_endian_n(2, sizeof(u16), buffer);
+ buffer += convert_endian(sizeof(u32), buffer);
+ break;
+ case MXL_EAGLE_OPCODE_SMA_RECEIVE_GET:
+ buffer += convert_endian(sizeof(u32), buffer);
+ break;
+ default:
+ /* no swapping - all set opcodes */
+ break;
+ }
+}
+
+static u32 mxl692_checksum(u8 *buffer, u32 size)
+{
+ u32 ix, div_size;
+ u32 cur_cksum = 0;
+ __be32 *buf;
+
+ div_size = DIV_ROUND_UP(size, 4);
+
+ buf = (__be32 *)buffer;
+ for (ix = 0; ix < div_size; ix++)
+ cur_cksum += be32_to_cpu(buf[ix]);
+
+ cur_cksum ^= 0xDEADBEEF;
+
+ return cur_cksum;
+}
+
+static int mxl692_validate_fw_header(struct mxl692_dev *dev,
+ const u8 *buffer, u32 buf_len)
+{
+ int status = 0;
+ u32 ix, temp;
+ __be32 *local_buf = NULL;
+ u8 temp_cksum = 0;
+ static const u8 fw_hdr[] = {
+ 0x4D, 0x31, 0x10, 0x02, 0x40, 0x00, 0x00, 0x80
+ };
+
+ if (memcmp(buffer, fw_hdr, 8) != 0) {
+ status = -EINVAL;
+ goto err_finish;
+ }
+
+ local_buf = (__be32 *)(buffer + 8);
+ temp = be32_to_cpu(*local_buf);
+
+ if ((buf_len - 16) != temp >> 8) {
+ status = -EINVAL;
+ goto err_finish;
+ }
+
+ for (ix = 16; ix < buf_len; ix++)
+ temp_cksum += buffer[ix];
+
+ if (temp_cksum != buffer[11])
+ status = -EINVAL;
+
+err_finish:
+ if (status)
+ dev_dbg(&dev->i2c_client->dev, "failed\n");
+ return status;
+}
+
+static int mxl692_write_fw_block(struct mxl692_dev *dev, const u8 *buffer,
+ u32 buf_len, u32 *index)
+{
+ int status = 0;
+ u32 ix = 0, total_len = 0, addr = 0, chunk_len = 0, prevchunk_len = 0;
+ u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL;
+ int payload_max = MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE;
+
+ ix = *index;
+
+ if (buffer[ix] == 0x53) {
+ total_len = buffer[ix + 1] << 16 | buffer[ix + 2] << 8 | buffer[ix + 3];
+ total_len = (total_len + 3) & ~3;
+ addr = buffer[ix + 4] << 24 | buffer[ix + 5] << 16 |
+ buffer[ix + 6] << 8 | buffer[ix + 7];
+ ix += MXL_EAGLE_FW_SEGMENT_HEADER_SIZE;
+
+ while ((total_len > 0) && (status == 0)) {
+ plocal_buf = local_buf;
+ chunk_len = (total_len < payload_max) ? total_len : payload_max;
+
+ *plocal_buf++ = 0xFC;
+ *plocal_buf++ = chunk_len + sizeof(u32);
+
+ *(u32 *)plocal_buf = addr + prevchunk_len;
+#ifdef __BIG_ENDIAN
+ convert_endian(sizeof(u32), plocal_buf);
+#endif
+ plocal_buf += sizeof(u32);
+
+ memcpy(plocal_buf, &buffer[ix], chunk_len);
+ convert_endian(chunk_len, plocal_buf);
+ if (mxl692_i2c_write(dev, local_buf,
+ (chunk_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) {
+ status = -EREMOTEIO;
+ break;
+ }
+
+ prevchunk_len += chunk_len;
+ total_len -= chunk_len;
+ ix += chunk_len;
+ }
+ *index = ix;
+ } else {
+ status = -EINVAL;
+ }
+
+ if (status)
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", status);
+
+ return status;
+}
+
+static int mxl692_memwrite(struct mxl692_dev *dev, u32 addr,
+ u8 *buffer, u32 size)
+{
+ int status = 0, total_len = 0;
+ u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL;
+
+ total_len = size;
+ total_len = (total_len + 3) & ~3; /* 4 byte alignment */
+
+ if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_MHEADER_SIZE))
+ dev_dbg(&dev->i2c_client->dev, "hrmph?\n");
+
+ plocal_buf = local_buf;
+
+ *plocal_buf++ = 0xFC;
+ *plocal_buf++ = total_len + sizeof(u32);
+
+ *(u32 *)plocal_buf = addr;
+ plocal_buf += sizeof(u32);
+
+ memcpy(plocal_buf, buffer, total_len);
+#ifdef __BIG_ENDIAN
+ convert_endian(sizeof(u32) + total_len, local_buf + 2);
+#endif
+ if (mxl692_i2c_write(dev, local_buf,
+ (total_len + MXL_EAGLE_I2C_MHEADER_SIZE)) < 0) {
+ status = -EREMOTEIO;
+ goto err_finish;
+ }
+
+ return status;
+err_finish:
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", status);
+ return status;
+}
+
+static int mxl692_memread(struct mxl692_dev *dev, u32 addr,
+ u8 *buffer, u32 size)
+{
+ int status = 0;
+ u8 local_buf[MXL_EAGLE_I2C_MHEADER_SIZE] = {}, *plocal_buf = NULL;
+
+ plocal_buf = local_buf;
+
+ *plocal_buf++ = 0xFB;
+ *plocal_buf++ = sizeof(u32);
+ *(u32 *)plocal_buf = addr;
+#ifdef __BIG_ENDIAN
+ convert_endian(sizeof(u32), plocal_buf);
+#endif
+ mutex_lock(&dev->i2c_lock);
+
+ if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_MHEADER_SIZE) > 0) {
+ size = (size + 3) & ~3; /* 4 byte alignment */
+ status = mxl692_i2c_read(dev, buffer, (u16)size) < 0 ? -EREMOTEIO : 0;
+#ifdef __BIG_ENDIAN
+ if (status == 0)
+ convert_endian(size, buffer);
+#endif
+ } else {
+ status = -EREMOTEIO;
+ }
+
+ mutex_unlock(&dev->i2c_lock);
+
+ if (status)
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", status);
+
+ return status;
+}
+
+static const char *mxl692_opcode_string(u8 opcode)
+{
+ if (opcode <= MXL_EAGLE_OPCODE_INTERNAL)
+ return MXL_EAGLE_OPCODE_STRING[opcode];
+
+ return "invalid opcode";
+}
+
+static int mxl692_opwrite(struct mxl692_dev *dev, u8 *buffer,
+ u32 size)
+{
+ int status = 0, total_len = 0;
+ u8 local_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {}, *plocal_buf = NULL;
+ struct MXL_EAGLE_HOST_MSG_HEADER_T *tx_hdr = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)buffer;
+
+ total_len = size;
+ total_len = (total_len + 3) & ~3; /* 4 byte alignment */
+
+ if (total_len > (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE))
+ dev_dbg(&dev->i2c_client->dev, "hrmph?\n");
+
+ plocal_buf = local_buf;
+
+ *plocal_buf++ = 0xFE;
+ *plocal_buf++ = (u8)total_len;
+
+ memcpy(plocal_buf, buffer, total_len);
+ convert_endian(total_len, plocal_buf);
+
+ if (mxl692_i2c_write(dev, local_buf,
+ (total_len + MXL_EAGLE_I2C_PHEADER_SIZE)) < 0) {
+ status = -EREMOTEIO;
+ goto err_finish;
+ }
+err_finish:
+ if (status)
+ dev_dbg(&dev->i2c_client->dev, "opcode %s err %d\n",
+ mxl692_opcode_string(tx_hdr->opcode), status);
+ return status;
+}
+
+static int mxl692_opread(struct mxl692_dev *dev, u8 *buffer,
+ u32 size)
+{
+ int status = 0;
+ u32 ix = 0;
+ u8 local_buf[MXL_EAGLE_I2C_PHEADER_SIZE] = {};
+
+ local_buf[0] = 0xFD;
+ local_buf[1] = 0;
+
+ if (mxl692_i2c_write(dev, local_buf, MXL_EAGLE_I2C_PHEADER_SIZE) > 0) {
+ size = (size + 3) & ~3; /* 4 byte alignment */
+
+ /* Read in 4 byte chunks */
+ for (ix = 0; ix < size; ix += 4) {
+ if (mxl692_i2c_read(dev, buffer + ix, 4) < 0) {
+ dev_dbg(&dev->i2c_client->dev, "ix=%d size=%d\n", ix, size);
+ status = -EREMOTEIO;
+ goto err_finish;
+ }
+ }
+ convert_endian(size, buffer);
+ } else {
+ status = -EREMOTEIO;
+ }
+err_finish:
+ if (status)
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", status);
+ return status;
+}
+
+static int mxl692_i2c_writeread(struct mxl692_dev *dev,
+ u8 opcode,
+ u8 *tx_payload,
+ u8 tx_payload_size,
+ u8 *rx_payload,
+ u8 rx_payload_expected)
+{
+ int status = 0, timeout = 40;
+ u8 tx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {};
+ u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {};
+ u32 resp_checksum = 0, resp_checksum_tmp = 0;
+ struct MXL_EAGLE_HOST_MSG_HEADER_T *tx_header;
+ struct MXL_EAGLE_HOST_MSG_HEADER_T *rx_header;
+
+ mutex_lock(&dev->i2c_lock);
+
+ if ((tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE) >
+ (MXL_EAGLE_MAX_I2C_PACKET_SIZE - MXL_EAGLE_I2C_PHEADER_SIZE)) {
+ status = -EINVAL;
+ goto err_finish;
+ }
+
+ tx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)tx_buf;
+ tx_header->opcode = opcode;
+ tx_header->seqnum = dev->seqnum++;
+ tx_header->payload_size = tx_payload_size;
+ tx_header->checksum = 0;
+
+ if (dev->seqnum == 0)
+ dev->seqnum = 1;
+
+ if (tx_payload && tx_payload_size > 0)
+ memcpy(&tx_buf[MXL_EAGLE_HOST_MSG_HEADER_SIZE], tx_payload, tx_payload_size);
+
+ mxl692_tx_swap(opcode, tx_buf);
+
+ tx_header->checksum = 0;
+ tx_header->checksum = mxl692_checksum(tx_buf,
+ MXL_EAGLE_HOST_MSG_HEADER_SIZE + tx_payload_size);
+#ifdef __LITTLE_ENDIAN
+ convert_endian(4, (u8 *)&tx_header->checksum); /* cksum is big endian */
+#endif
+ /* send Tx message */
+ status = mxl692_opwrite(dev, tx_buf,
+ tx_payload_size + MXL_EAGLE_HOST_MSG_HEADER_SIZE);
+ if (status) {
+ status = -EREMOTEIO;
+ goto err_finish;
+ }
+
+ /* receive Rx message (polling) */
+ rx_header = (struct MXL_EAGLE_HOST_MSG_HEADER_T *)rx_buf;
+
+ do {
+ status = mxl692_opread(dev, rx_buf,
+ rx_payload_expected + MXL_EAGLE_HOST_MSG_HEADER_SIZE);
+ usleep_range(1000, 2000);
+ timeout--;
+ } while ((timeout > 0) && (status == 0) &&
+ (rx_header->seqnum == 0) &&
+ (rx_header->checksum == 0));
+
+ if (timeout == 0 || status) {
+ dev_dbg(&dev->i2c_client->dev, "timeout=%d status=%d\n",
+ timeout, status);
+ status = -ETIMEDOUT;
+ goto err_finish;
+ }
+
+ if (rx_header->status) {
+ dev_dbg(&dev->i2c_client->dev, "rx header status code: %d\n", rx_header->status);
+ status = -EREMOTEIO;
+ goto err_finish;
+ }
+
+ if (rx_header->seqnum != tx_header->seqnum ||
+ rx_header->opcode != tx_header->opcode ||
+ rx_header->payload_size != rx_payload_expected) {
+ dev_dbg(&dev->i2c_client->dev, "Something failed seq=%s opcode=%s pSize=%s\n",
+ rx_header->seqnum != tx_header->seqnum ? "X" : "0",
+ rx_header->opcode != tx_header->opcode ? "X" : "0",
+ rx_header->payload_size != rx_payload_expected ? "X" : "0");
+ if (rx_header->payload_size != rx_payload_expected)
+ dev_dbg(&dev->i2c_client->dev,
+ "rx_header->payloadSize=%d rx_payload_expected=%d\n",
+ rx_header->payload_size, rx_payload_expected);
+ status = -EREMOTEIO;
+ goto err_finish;
+ }
+
+ resp_checksum = rx_header->checksum;
+ rx_header->checksum = 0;
+
+ resp_checksum_tmp = mxl692_checksum(rx_buf,
+ MXL_EAGLE_HOST_MSG_HEADER_SIZE + rx_header->payload_size);
+#ifdef __LITTLE_ENDIAN
+ convert_endian(4, (u8 *)&resp_checksum_tmp); /* cksum is big endian */
+#endif
+ if (resp_checksum != resp_checksum_tmp) {
+ dev_dbg(&dev->i2c_client->dev, "rx checksum failure\n");
+ status = -EREMOTEIO;
+ goto err_finish;
+ }
+
+ mxl692_rx_swap(rx_header->opcode, rx_buf);
+
+ if (rx_header->payload_size > 0) {
+ if (!rx_payload) {
+ dev_dbg(&dev->i2c_client->dev, "no rx payload?!?\n");
+ status = -EREMOTEIO;
+ goto err_finish;
+ }
+ memcpy(rx_payload, rx_buf + MXL_EAGLE_HOST_MSG_HEADER_SIZE,
+ rx_header->payload_size);
+ }
+err_finish:
+ if (status)
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", status);
+
+ mutex_unlock(&dev->i2c_lock);
+ return status;
+}
+
+static int mxl692_fwdownload(struct mxl692_dev *dev,
+ const u8 *firmware_buf, u32 buf_len)
+{
+ int status = 0;
+ u32 ix, reg_val = 0x1;
+ u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {};
+ struct MXL_EAGLE_DEV_STATUS_T *dev_status;
+
+ if (buf_len < MXL_EAGLE_FW_HEADER_SIZE ||
+ buf_len > MXL_EAGLE_FW_MAX_SIZE_IN_KB * 1000)
+ return -EINVAL;
+
+ mutex_lock(&dev->i2c_lock);
+
+ dev_dbg(&dev->i2c_client->dev, "\n");
+
+ status = mxl692_validate_fw_header(dev, firmware_buf, buf_len);
+ if (status)
+ goto err_finish;
+
+ ix = 16;
+ status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* DRAM */
+ if (status)
+ goto err_finish;
+
+ status = mxl692_write_fw_block(dev, firmware_buf, buf_len, &ix); /* IRAM */
+ if (status)
+ goto err_finish;
+
+ /* release CPU from reset */
+ status = mxl692_memwrite(dev, 0x70000018, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ mutex_unlock(&dev->i2c_lock);
+
+ if (status == 0) {
+ /* verify FW is alive */
+ usleep_range(MXL_EAGLE_FW_LOAD_TIME * 1000, (MXL_EAGLE_FW_LOAD_TIME + 5) * 1000);
+ dev_status = (struct MXL_EAGLE_DEV_STATUS_T *)&rx_buf;
+ status = mxl692_i2c_writeread(dev,
+ MXL_EAGLE_OPCODE_DEVICE_STATUS_GET,
+ NULL,
+ 0,
+ (u8 *)dev_status,
+ sizeof(struct MXL_EAGLE_DEV_STATUS_T));
+ }
+
+ return status;
+err_finish:
+ mutex_unlock(&dev->i2c_lock);
+ if (status)
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", status);
+ return status;
+}
+
+static int mxl692_get_versions(struct mxl692_dev *dev)
+{
+ int status = 0;
+ struct MXL_EAGLE_DEV_VER_T dev_ver = {};
+ static const char * const chip_id[] = {"N/A", "691", "248", "692"};
+
+ status = mxl692_i2c_writeread(dev, MXL_EAGLE_OPCODE_DEVICE_VERSION_GET,
+ NULL,
+ 0,
+ (u8 *)&dev_ver,
+ sizeof(struct MXL_EAGLE_DEV_VER_T));
+ if (status)
+ return status;
+
+ dev_info(&dev->i2c_client->dev, "MxL692_DEMOD Chip ID: %s\n",
+ chip_id[dev_ver.chip_id]);
+
+ dev_info(&dev->i2c_client->dev,
+ "MxL692_DEMOD FW Version: %d.%d.%d.%d_RC%d\n",
+ dev_ver.firmware_ver[0],
+ dev_ver.firmware_ver[1],
+ dev_ver.firmware_ver[2],
+ dev_ver.firmware_ver[3],
+ dev_ver.firmware_ver[4]);
+
+ return status;
+}
+
+static int mxl692_reset(struct mxl692_dev *dev)
+{
+ int status = 0;
+ u32 dev_type = MXL_EAGLE_DEVICE_MAX, reg_val = 0x2;
+
+ dev_dbg(&dev->i2c_client->dev, "\n");
+
+ /* legacy i2c override */
+ status = mxl692_memwrite(dev, 0x80000100, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ /* verify sku */
+ status = mxl692_memread(dev, 0x70000188, (u8 *)&dev_type, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ if (dev_type != dev->device_type)
+ goto err_finish;
+
+err_finish:
+ if (status)
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", status);
+ return status;
+}
+
+static int mxl692_config_regulators(struct mxl692_dev *dev,
+ enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E power_supply)
+{
+ int status = 0;
+ u32 reg_val;
+
+ dev_dbg(&dev->i2c_client->dev, "\n");
+
+ /* configure main regulator according to the power supply source */
+ status = mxl692_memread(dev, 0x90000000, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ reg_val &= 0x00FFFFFF;
+ reg_val |= (power_supply == MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE) ?
+ 0x14000000 : 0x10000000;
+
+ status = mxl692_memwrite(dev, 0x90000000, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ /* configure digital regulator to high current mode */
+ status = mxl692_memread(dev, 0x90000018, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ reg_val |= 0x800;
+
+ status = mxl692_memwrite(dev, 0x90000018, (u8 *)&reg_val, sizeof(u32));
+
+err_finish:
+ if (status)
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", status);
+ return status;
+}
+
+static int mxl692_config_xtal(struct mxl692_dev *dev,
+ struct MXL_EAGLE_DEV_XTAL_T *dev_xtal)
+{
+ int status = 0;
+ u32 reg_val, reg_val1;
+
+ dev_dbg(&dev->i2c_client->dev, "\n");
+
+ status = mxl692_memread(dev, 0x90000000, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ /* set XTAL capacitance */
+ reg_val &= 0xFFFFFFE0;
+ reg_val |= dev_xtal->xtal_cap;
+
+ /* set CLK OUT */
+ reg_val = dev_xtal->clk_out_enable ? (reg_val | 0x0100) : (reg_val & 0xFFFFFEFF);
+
+ status = mxl692_memwrite(dev, 0x90000000, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ /* set CLK OUT divider */
+ reg_val = dev_xtal->clk_out_div_enable ? (reg_val | 0x0200) : (reg_val & 0xFFFFFDFF);
+
+ status = mxl692_memwrite(dev, 0x90000000, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ /* set XTAL sharing */
+ reg_val = dev_xtal->xtal_sharing_enable ? (reg_val | 0x010400) : (reg_val & 0xFFFEFBFF);
+
+ status = mxl692_memwrite(dev, 0x90000000, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ /* enable/disable XTAL calibration, based on master/slave device */
+ status = mxl692_memread(dev, 0x90000030, (u8 *)&reg_val1, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ if (dev_xtal->xtal_calibration_enable) {
+ /* enable XTAL calibration and set XTAL amplitude to a higher value */
+ reg_val1 &= 0xFFFFFFFD;
+ reg_val1 |= 0x30;
+
+ status = mxl692_memwrite(dev, 0x90000030, (u8 *)&reg_val1, sizeof(u32));
+ if (status)
+ goto err_finish;
+ } else {
+ /* disable XTAL calibration */
+ reg_val1 |= 0x2;
+
+ status = mxl692_memwrite(dev, 0x90000030, (u8 *)&reg_val1, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ /* set XTAL bias value */
+ status = mxl692_memread(dev, 0x9000002c, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ reg_val &= 0xC0FFFFFF;
+ reg_val |= 0xA000000;
+
+ status = mxl692_memwrite(dev, 0x9000002c, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+ }
+
+ /* start XTAL calibration */
+ status = mxl692_memread(dev, 0x70000010, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ reg_val |= 0x8;
+
+ status = mxl692_memwrite(dev, 0x70000010, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ status = mxl692_memread(dev, 0x70000018, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ reg_val |= 0x10;
+
+ status = mxl692_memwrite(dev, 0x70000018, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ status = mxl692_memread(dev, 0x9001014c, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ reg_val &= 0xFFFFEFFF;
+
+ status = mxl692_memwrite(dev, 0x9001014c, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ reg_val |= 0x1000;
+
+ status = mxl692_memwrite(dev, 0x9001014c, (u8 *)&reg_val, sizeof(u32));
+ if (status)
+ goto err_finish;
+
+ usleep_range(45000, 55000);
+
+err_finish:
+ if (status)
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", status);
+ return status;
+}
+
+static int mxl692_powermode(struct mxl692_dev *dev,
+ enum MXL_EAGLE_POWER_MODE_E power_mode)
+{
+ int status = 0;
+ u8 mode = power_mode;
+
+ dev_dbg(&dev->i2c_client->dev, "%s\n",
+ power_mode == MXL_EAGLE_POWER_MODE_SLEEP ? "sleep" : "active");
+
+ status = mxl692_i2c_writeread(dev,
+ MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET,
+ &mode,
+ sizeof(u8),
+ NULL,
+ 0);
+ if (status) {
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", status);
+ return status;
+ }
+
+ dev->power_mode = power_mode;
+
+ return status;
+}
+
+static int mxl692_init(struct dvb_frontend *fe)
+{
+ struct mxl692_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->i2c_client;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int status = 0;
+ const struct firmware *firmware;
+ struct MXL_EAGLE_DEV_XTAL_T xtal_config = {};
+
+ dev_dbg(&dev->i2c_client->dev, "\n");
+
+ if (dev->init_done)
+ goto warm;
+
+ dev->seqnum = 1;
+
+ status = mxl692_reset(dev);
+ if (status)
+ goto err;
+
+ usleep_range(50 * 1000, 60 * 1000); /* was 1000! */
+
+ status = mxl692_config_regulators(dev, MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL);
+ if (status)
+ goto err;
+
+ xtal_config.xtal_cap = 26;
+ xtal_config.clk_out_div_enable = 0;
+ xtal_config.clk_out_enable = 0;
+ xtal_config.xtal_calibration_enable = 0;
+ xtal_config.xtal_sharing_enable = 1;
+ status = mxl692_config_xtal(dev, &xtal_config);
+ if (status)
+ goto err;
+
+ status = request_firmware(&firmware, MXL692_FIRMWARE, &client->dev);
+ if (status) {
+ dev_dbg(&dev->i2c_client->dev, "firmware missing? %s\n",
+ MXL692_FIRMWARE);
+ goto err;
+ }
+
+ status = mxl692_fwdownload(dev, firmware->data, firmware->size);
+ if (status)
+ goto err_release_firmware;
+
+ release_firmware(firmware);
+
+ status = mxl692_get_versions(dev);
+ if (status)
+ goto err;
+
+ dev->power_mode = MXL_EAGLE_POWER_MODE_SLEEP;
+warm:
+ /* Config Device Power Mode */
+ if (dev->power_mode != MXL_EAGLE_POWER_MODE_ACTIVE) {
+ status = mxl692_powermode(dev, MXL_EAGLE_POWER_MODE_ACTIVE);
+ if (status)
+ goto err;
+
+ usleep_range(50 * 1000, 60 * 1000); /* was 500! */
+ }
+
+ /* Init stats here to indicate which stats are supported */
+ c->cnr.len = 1;
+ c->cnr.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;
+
+ dev->init_done = 1;
+ return 0;
+err_release_firmware:
+ release_firmware(firmware);
+err:
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", status);
+ return status;
+}
+
+static int mxl692_sleep(struct dvb_frontend *fe)
+{
+ struct mxl692_dev *dev = fe->demodulator_priv;
+
+ if (dev->power_mode != MXL_EAGLE_POWER_MODE_SLEEP)
+ mxl692_powermode(dev, MXL_EAGLE_POWER_MODE_SLEEP);
+
+ return 0;
+}
+
+static int mxl692_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct mxl692_dev *dev = fe->demodulator_priv;
+
+ int status = 0;
+ enum MXL_EAGLE_DEMOD_TYPE_E demod_type;
+ struct MXL_EAGLE_MPEGOUT_PARAMS_T mpeg_params = {};
+ enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E qam_annex = MXL_EAGLE_QAM_DEMOD_ANNEX_B;
+ struct MXL_EAGLE_QAM_DEMOD_PARAMS_T qam_params = {};
+ struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T tuner_params = {};
+ u8 op_param = 0;
+
+ dev_dbg(&dev->i2c_client->dev, "\n");
+
+ switch (p->modulation) {
+ case VSB_8:
+ demod_type = MXL_EAGLE_DEMOD_TYPE_ATSC;
+ break;
+ case QAM_AUTO:
+ case QAM_64:
+ case QAM_128:
+ case QAM_256:
+ demod_type = MXL_EAGLE_DEMOD_TYPE_QAM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (dev->current_frequency == p->frequency && dev->demod_type == demod_type) {
+ dev_dbg(&dev->i2c_client->dev, "already set up\n");
+ return 0;
+ }
+
+ dev->current_frequency = -1;
+ dev->demod_type = -1;
+
+ op_param = demod_type;
+ status = mxl692_i2c_writeread(dev,
+ MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET,
+ &op_param,
+ sizeof(u8),
+ NULL,
+ 0);
+ if (status) {
+ dev_dbg(&dev->i2c_client->dev,
+ "DEVICE_DEMODULATOR_TYPE_SET...FAIL err 0x%x\n", status);
+ goto err;
+ }
+
+ usleep_range(20 * 1000, 30 * 1000); /* was 500! */
+
+ mpeg_params.mpeg_parallel = 0;
+ mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_MSB_1ST;
+ mpeg_params.mpeg_sync_pulse_width = MXL_EAGLE_DATA_SYNC_WIDTH_BIT;
+ mpeg_params.mpeg_valid_pol = MXL_EAGLE_CLOCK_POSITIVE;
+ mpeg_params.mpeg_sync_pol = MXL_EAGLE_CLOCK_POSITIVE;
+ mpeg_params.mpeg_clk_pol = MXL_EAGLE_CLOCK_NEGATIVE;
+ mpeg_params.mpeg3wire_mode_enable = 0;
+ mpeg_params.mpeg_clk_freq = MXL_EAGLE_MPEG_CLOCK_27MHZ;
+
+ switch (demod_type) {
+ case MXL_EAGLE_DEMOD_TYPE_ATSC:
+ status = mxl692_i2c_writeread(dev,
+ MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET,
+ (u8 *)&mpeg_params,
+ sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T),
+ NULL,
+ 0);
+ if (status)
+ goto err;
+ break;
+ case MXL_EAGLE_DEMOD_TYPE_QAM:
+ if (qam_annex == MXL_EAGLE_QAM_DEMOD_ANNEX_A)
+ mpeg_params.msb_first = MXL_EAGLE_DATA_SERIAL_LSB_1ST;
+ status = mxl692_i2c_writeread(dev,
+ MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET,
+ (u8 *)&mpeg_params,
+ sizeof(struct MXL_EAGLE_MPEGOUT_PARAMS_T),
+ NULL,
+ 0);
+ if (status)
+ goto err;
+
+ qam_params.annex_type = qam_annex;
+ qam_params.qam_type = MXL_EAGLE_QAM_DEMOD_AUTO;
+ qam_params.iq_flip = MXL_EAGLE_DEMOD_IQ_AUTO;
+ if (p->modulation == QAM_64)
+ qam_params.symbol_rate_hz = 5057000;
+ else
+ qam_params.symbol_rate_hz = 5361000;
+
+ qam_params.symbol_rate_256qam_hz = 5361000;
+
+ status = mxl692_i2c_writeread(dev,
+ MXL_EAGLE_OPCODE_QAM_PARAMS_SET,
+ (u8 *)&qam_params,
+ sizeof(struct MXL_EAGLE_QAM_DEMOD_PARAMS_T),
+ NULL, 0);
+ if (status)
+ goto err;
+
+ break;
+ default:
+ break;
+ }
+
+ usleep_range(20 * 1000, 30 * 1000); /* was 500! */
+
+ tuner_params.freq_hz = p->frequency;
+ tuner_params.bandwidth = MXL_EAGLE_TUNER_BW_6MHZ;
+ tuner_params.tune_mode = MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW;
+
+ dev_dbg(&dev->i2c_client->dev, " Tuning Freq: %d %s\n", tuner_params.freq_hz,
+ demod_type == MXL_EAGLE_DEMOD_TYPE_ATSC ? "ATSC" : "QAM");
+
+ status = mxl692_i2c_writeread(dev,
+ MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET,
+ (u8 *)&tuner_params,
+ sizeof(struct MXL_EAGLE_TUNER_CHANNEL_PARAMS_T),
+ NULL,
+ 0);
+ if (status)
+ goto err;
+
+ usleep_range(20 * 1000, 30 * 1000); /* was 500! */
+
+ switch (demod_type) {
+ case MXL_EAGLE_DEMOD_TYPE_ATSC:
+ status = mxl692_i2c_writeread(dev,
+ MXL_EAGLE_OPCODE_ATSC_INIT_SET,
+ NULL, 0, NULL, 0);
+ if (status)
+ goto err;
+ break;
+ case MXL_EAGLE_DEMOD_TYPE_QAM:
+ status = mxl692_i2c_writeread(dev,
+ MXL_EAGLE_OPCODE_QAM_RESTART_SET,
+ NULL, 0, NULL, 0);
+ if (status)
+ goto err;
+ break;
+ default:
+ break;
+ }
+
+ dev->demod_type = demod_type;
+ dev->current_frequency = p->frequency;
+
+ return 0;
+err:
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", status);
+ return status;
+}
+
+static int mxl692_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ p->modulation = c->modulation;
+ p->frequency = c->frequency;
+
+ return 0;
+}
+
+static int mxl692_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct mxl692_dev *dev = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {};
+ struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status;
+ struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status;
+ enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type;
+ int mxl_status = 0;
+
+ *snr = 0;
+
+ dev_dbg(&dev->i2c_client->dev, "\n");
+
+ atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf;
+ qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf;
+
+ switch (demod_type) {
+ case MXL_EAGLE_DEMOD_TYPE_ATSC:
+ mxl_status = mxl692_i2c_writeread(dev,
+ MXL_EAGLE_OPCODE_ATSC_STATUS_GET,
+ NULL,
+ 0,
+ rx_buf,
+ sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T));
+ if (!mxl_status) {
+ *snr = (u16)(atsc_status->snr_db_tenths / 10);
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = *snr;
+ }
+ break;
+ case MXL_EAGLE_DEMOD_TYPE_QAM:
+ mxl_status = mxl692_i2c_writeread(dev,
+ MXL_EAGLE_OPCODE_QAM_STATUS_GET,
+ NULL,
+ 0,
+ rx_buf,
+ sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T));
+ if (!mxl_status)
+ *snr = (u16)(qam_status->snr_db_tenths / 10);
+ break;
+ case MXL_EAGLE_DEMOD_TYPE_OOB:
+ default:
+ break;
+ }
+
+ if (mxl_status)
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", mxl_status);
+ return mxl_status;
+}
+
+static int mxl692_read_ber_ucb(struct dvb_frontend *fe)
+{
+ struct mxl692_dev *dev = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {};
+ struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *atsc_errors;
+ enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type;
+ int mxl_status = 0;
+ u32 utmp;
+
+ dev_dbg(&dev->i2c_client->dev, "\n");
+
+ atsc_errors = (struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T *)&rx_buf;
+
+ switch (demod_type) {
+ case MXL_EAGLE_DEMOD_TYPE_ATSC:
+ mxl_status = mxl692_i2c_writeread(dev,
+ MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET,
+ NULL,
+ 0,
+ rx_buf,
+ sizeof(struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T));
+ if (!mxl_status) {
+ if (atsc_errors->error_packets == 0)
+ utmp = 0;
+ else
+ utmp = ((atsc_errors->error_bytes / atsc_errors->error_packets) *
+ atsc_errors->total_packets);
+ /* ber */
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += atsc_errors->error_bytes;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue += utmp;
+ /* ucb */
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue += atsc_errors->error_packets;
+
+ dev_dbg(&dev->i2c_client->dev, "%llu %llu\n",
+ c->post_bit_count.stat[0].uvalue, c->block_error.stat[0].uvalue);
+ }
+ break;
+ case MXL_EAGLE_DEMOD_TYPE_QAM:
+ case MXL_EAGLE_DEMOD_TYPE_OOB:
+ default:
+ break;
+ }
+
+ if (mxl_status)
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", mxl_status);
+
+ return mxl_status;
+}
+
+static int mxl692_read_status(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ struct mxl692_dev *dev = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 rx_buf[MXL_EAGLE_MAX_I2C_PACKET_SIZE] = {};
+ struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *atsc_status;
+ struct MXL_EAGLE_QAM_DEMOD_STATUS_T *qam_status;
+ enum MXL_EAGLE_DEMOD_TYPE_E demod_type = dev->demod_type;
+ int mxl_status = 0;
+ *status = 0;
+
+ dev_dbg(&dev->i2c_client->dev, "\n");
+
+ atsc_status = (struct MXL_EAGLE_ATSC_DEMOD_STATUS_T *)&rx_buf;
+ qam_status = (struct MXL_EAGLE_QAM_DEMOD_STATUS_T *)&rx_buf;
+
+ switch (demod_type) {
+ case MXL_EAGLE_DEMOD_TYPE_ATSC:
+ mxl_status = mxl692_i2c_writeread(dev,
+ MXL_EAGLE_OPCODE_ATSC_STATUS_GET,
+ NULL,
+ 0,
+ rx_buf,
+ sizeof(struct MXL_EAGLE_ATSC_DEMOD_STATUS_T));
+ if (!mxl_status && atsc_status->atsc_lock) {
+ *status |= FE_HAS_SIGNAL;
+ *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_VITERBI;
+ *status |= FE_HAS_SYNC;
+ *status |= FE_HAS_LOCK;
+
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = atsc_status->snr_db_tenths / 10;
+ }
+ break;
+ case MXL_EAGLE_DEMOD_TYPE_QAM:
+ mxl_status = mxl692_i2c_writeread(dev,
+ MXL_EAGLE_OPCODE_QAM_STATUS_GET,
+ NULL,
+ 0,
+ rx_buf,
+ sizeof(struct MXL_EAGLE_QAM_DEMOD_STATUS_T));
+ if (!mxl_status && qam_status->qam_locked) {
+ *status |= FE_HAS_SIGNAL;
+ *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_VITERBI;
+ *status |= FE_HAS_SYNC;
+ *status |= FE_HAS_LOCK;
+
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = qam_status->snr_db_tenths / 10;
+ }
+ break;
+ case MXL_EAGLE_DEMOD_TYPE_OOB:
+ default:
+ break;
+ }
+
+ if ((*status & FE_HAS_LOCK) == 0) {
+ /* No lock, reset all statistics */
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return 0;
+ }
+
+ if (mxl_status)
+ dev_dbg(&dev->i2c_client->dev, "err %d\n", mxl_status);
+ else
+ mxl_status = mxl692_read_ber_ucb(fe);
+
+ return mxl_status;
+}
+
+static const struct dvb_frontend_ops mxl692_ops = {
+ .delsys = { SYS_ATSC },
+ .info = {
+ .name = "MaxLinear MxL692 VSB tuner-demodulator",
+ .frequency_min_hz = 54000000,
+ .frequency_max_hz = 858000000,
+ .frequency_stepsize_hz = 62500,
+ .caps = FE_CAN_8VSB
+ },
+
+ .init = mxl692_init,
+ .sleep = mxl692_sleep,
+ .set_frontend = mxl692_set_frontend,
+ .get_frontend = mxl692_get_frontend,
+
+ .read_status = mxl692_read_status,
+ .read_snr = mxl692_read_snr,
+};
+
+static int mxl692_probe(struct i2c_client *client)
+{
+ struct mxl692_config *config = client->dev.platform_data;
+ struct mxl692_dev *dev;
+ int ret = 0;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ dev_dbg(&client->dev, "kzalloc() failed\n");
+ goto err;
+ }
+
+ memcpy(&dev->fe.ops, &mxl692_ops, sizeof(struct dvb_frontend_ops));
+ dev->fe.demodulator_priv = dev;
+ dev->i2c_client = client;
+ *config->fe = &dev->fe;
+ mutex_init(&dev->i2c_lock);
+ i2c_set_clientdata(client, dev);
+
+ dev_info(&client->dev, "MaxLinear mxl692 successfully attached\n");
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return -ENODEV;
+}
+
+static void mxl692_remove(struct i2c_client *client)
+{
+ struct mxl692_dev *dev = i2c_get_clientdata(client);
+
+ dev->fe.demodulator_priv = NULL;
+ i2c_set_clientdata(client, NULL);
+ kfree(dev);
+}
+
+static const struct i2c_device_id mxl692_id_table[] = {
+ { "mxl692" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mxl692_id_table);
+
+static struct i2c_driver mxl692_driver = {
+ .driver = {
+ .name = "mxl692",
+ },
+ .probe = mxl692_probe,
+ .remove = mxl692_remove,
+ .id_table = mxl692_id_table,
+};
+
+module_i2c_driver(mxl692_driver);
+
+MODULE_AUTHOR("Brad Love <brad@nextdimension.cc>");
+MODULE_DESCRIPTION("MaxLinear MxL692 demodulator/tuner driver");
+MODULE_FIRMWARE(MXL692_FIRMWARE);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/mxl692.h b/drivers/media/dvb-frontends/mxl692.h
new file mode 100644
index 000000000000..77764a047c07
--- /dev/null
+++ b/drivers/media/dvb-frontends/mxl692.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver for the MaxLinear MxL69x family of tuners/demods
+ *
+ * Copyright (C) 2020 Brad Love <brad@nextdimension.cc>
+ *
+ * based on code:
+ * Copyright (c) 2016 MaxLinear, Inc. All rights reserved
+ * which was released under GPL V2
+ */
+
+#ifndef _MXL692_H_
+#define _MXL692_H_
+
+#include <media/dvb_frontend.h>
+
+#define MXL692_FIRMWARE "dvb-demod-mxl692.fw"
+
+struct mxl692_config {
+ unsigned char id;
+ u8 i2c_addr;
+ /*
+ * frontend
+ * returned by driver
+ */
+ struct dvb_frontend **fe;
+};
+
+#endif /* _MXL692_H_ */
diff --git a/drivers/media/dvb-frontends/mxl692_defs.h b/drivers/media/dvb-frontends/mxl692_defs.h
new file mode 100644
index 000000000000..c603f3d6f27f
--- /dev/null
+++ b/drivers/media/dvb-frontends/mxl692_defs.h
@@ -0,0 +1,539 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver for the MaxLinear MxL69x family of combo tuners/demods
+ *
+ * Copyright (C) 2020 Brad Love <brad@nextdimension.cc>
+ *
+ * based on code:
+ * Copyright (c) 2016 MaxLinear, Inc. All rights reserved
+ * which was released under GPL V2
+ */
+
+/*****************************************************************************
+ * Defines
+ *****************************************************************************
+ */
+#define MXL_EAGLE_HOST_MSG_HEADER_SIZE 8
+#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76
+#define MXL_EAGLE_QAM_FFE_TAPS_LENGTH 16
+#define MXL_EAGLE_QAM_SPUR_TAPS_LENGTH 32
+#define MXL_EAGLE_QAM_DFE_TAPS_LENGTH 72
+#define MXL_EAGLE_ATSC_FFE_TAPS_LENGTH 4096
+#define MXL_EAGLE_ATSC_DFE_TAPS_LENGTH 384
+#define MXL_EAGLE_VERSION_SIZE 5 /* A.B.C.D-RCx */
+#define MXL_EAGLE_FW_LOAD_TIME 50
+
+#define MXL_EAGLE_FW_MAX_SIZE_IN_KB 76
+#define MXL_EAGLE_FW_HEADER_SIZE 16
+#define MXL_EAGLE_FW_SEGMENT_HEADER_SIZE 8
+#define MXL_EAGLE_MAX_I2C_PACKET_SIZE 58
+#define MXL_EAGLE_I2C_MHEADER_SIZE 6
+#define MXL_EAGLE_I2C_PHEADER_SIZE 2
+
+/* Enum of Eagle family devices */
+enum MXL_EAGLE_DEVICE_E {
+ MXL_EAGLE_DEVICE_691 = 1, /* Device Mxl691 */
+ MXL_EAGLE_DEVICE_248 = 2, /* Device Mxl248 */
+ MXL_EAGLE_DEVICE_692 = 3, /* Device Mxl692 */
+ MXL_EAGLE_DEVICE_MAX, /* No such device */
+};
+
+#define VER_A 1
+#define VER_B 1
+#define VER_C 1
+#define VER_D 3
+#define VER_E 6
+
+/* Enum of Host to Eagle I2C protocol opcodes */
+enum MXL_EAGLE_OPCODE_E {
+ /* DEVICE */
+ MXL_EAGLE_OPCODE_DEVICE_DEMODULATOR_TYPE_SET,
+ MXL_EAGLE_OPCODE_DEVICE_MPEG_OUT_PARAMS_SET,
+ MXL_EAGLE_OPCODE_DEVICE_POWERMODE_SET,
+ MXL_EAGLE_OPCODE_DEVICE_GPIO_DIRECTION_SET,
+ MXL_EAGLE_OPCODE_DEVICE_GPO_LEVEL_SET,
+ MXL_EAGLE_OPCODE_DEVICE_INTR_MASK_SET,
+ MXL_EAGLE_OPCODE_DEVICE_IO_MUX_SET,
+ MXL_EAGLE_OPCODE_DEVICE_VERSION_GET,
+ MXL_EAGLE_OPCODE_DEVICE_STATUS_GET,
+ MXL_EAGLE_OPCODE_DEVICE_GPI_LEVEL_GET,
+
+ /* TUNER */
+ MXL_EAGLE_OPCODE_TUNER_CHANNEL_TUNE_SET,
+ MXL_EAGLE_OPCODE_TUNER_LOCK_STATUS_GET,
+ MXL_EAGLE_OPCODE_TUNER_AGC_STATUS_GET,
+
+ /* ATSC */
+ MXL_EAGLE_OPCODE_ATSC_INIT_SET,
+ MXL_EAGLE_OPCODE_ATSC_ACQUIRE_CARRIER_SET,
+ MXL_EAGLE_OPCODE_ATSC_STATUS_GET,
+ MXL_EAGLE_OPCODE_ATSC_ERROR_COUNTERS_GET,
+ MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_DFE_TAPS_GET,
+ MXL_EAGLE_OPCODE_ATSC_EQUALIZER_FILTER_FFE_TAPS_GET,
+
+ /* QAM */
+ MXL_EAGLE_OPCODE_QAM_PARAMS_SET,
+ MXL_EAGLE_OPCODE_QAM_RESTART_SET,
+ MXL_EAGLE_OPCODE_QAM_STATUS_GET,
+ MXL_EAGLE_OPCODE_QAM_ERROR_COUNTERS_GET,
+ MXL_EAGLE_OPCODE_QAM_CONSTELLATION_VALUE_GET,
+ MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_FFE_GET,
+ MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_START_GET,
+ MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_SPUR_END_GET,
+ MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_TAPS_NUMBER_GET,
+ MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_START_GET,
+ MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET,
+ MXL_EAGLE_OPCODE_QAM_EQUALIZER_FILTER_DFE_END_GET,
+
+ /* OOB */
+ MXL_EAGLE_OPCODE_OOB_PARAMS_SET,
+ MXL_EAGLE_OPCODE_OOB_RESTART_SET,
+ MXL_EAGLE_OPCODE_OOB_ERROR_COUNTERS_GET,
+ MXL_EAGLE_OPCODE_OOB_STATUS_GET,
+
+ /* SMA */
+ MXL_EAGLE_OPCODE_SMA_INIT_SET,
+ MXL_EAGLE_OPCODE_SMA_PARAMS_SET,
+ MXL_EAGLE_OPCODE_SMA_TRANSMIT_SET,
+ MXL_EAGLE_OPCODE_SMA_RECEIVE_GET,
+
+ /* DEBUG */
+ MXL_EAGLE_OPCODE_INTERNAL,
+
+ MXL_EAGLE_OPCODE_MAX = 70,
+};
+
+/* Enum of Host to Eagle I2C protocol opcodes */
+static const char * const MXL_EAGLE_OPCODE_STRING[] = {
+ /* DEVICE */
+ "DEVICE_DEMODULATOR_TYPE_SET",
+ "DEVICE_MPEG_OUT_PARAMS_SET",
+ "DEVICE_POWERMODE_SET",
+ "DEVICE_GPIO_DIRECTION_SET",
+ "DEVICE_GPO_LEVEL_SET",
+ "DEVICE_INTR_MASK_SET",
+ "DEVICE_IO_MUX_SET",
+ "DEVICE_VERSION_GET",
+ "DEVICE_STATUS_GET",
+ "DEVICE_GPI_LEVEL_GET",
+
+ /* TUNER */
+ "TUNER_CHANNEL_TUNE_SET",
+ "TUNER_LOCK_STATUS_GET",
+ "TUNER_AGC_STATUS_GET",
+
+ /* ATSC */
+ "ATSC_INIT_SET",
+ "ATSC_ACQUIRE_CARRIER_SET",
+ "ATSC_STATUS_GET",
+ "ATSC_ERROR_COUNTERS_GET",
+ "ATSC_EQUALIZER_FILTER_DFE_TAPS_GET",
+ "ATSC_EQUALIZER_FILTER_FFE_TAPS_GET",
+
+ /* QAM */
+ "QAM_PARAMS_SET",
+ "QAM_RESTART_SET",
+ "QAM_STATUS_GET",
+ "QAM_ERROR_COUNTERS_GET",
+ "QAM_CONSTELLATION_VALUE_GET",
+ "QAM_EQUALIZER_FILTER_FFE_GET",
+ "QAM_EQUALIZER_FILTER_SPUR_START_GET",
+ "QAM_EQUALIZER_FILTER_SPUR_END_GET",
+ "QAM_EQUALIZER_FILTER_DFE_TAPS_NUMBER_GET",
+ "QAM_EQUALIZER_FILTER_DFE_START_GET",
+ "QAM_EQUALIZER_FILTER_DFE_MIDDLE_GET",
+ "QAM_EQUALIZER_FILTER_DFE_END_GET",
+
+ /* OOB */
+ "OOB_PARAMS_SET",
+ "OOB_RESTART_SET",
+ "OOB_ERROR_COUNTERS_GET",
+ "OOB_STATUS_GET",
+
+ /* SMA */
+ "SMA_INIT_SET",
+ "SMA_PARAMS_SET",
+ "SMA_TRANSMIT_SET",
+ "SMA_RECEIVE_GET",
+
+ /* DEBUG */
+ "INTERNAL",
+};
+
+/* Enum of Callabck function types */
+enum MXL_EAGLE_CB_TYPE_E {
+ MXL_EAGLE_CB_FW_DOWNLOAD = 0,
+};
+
+/* Enum of power supply types */
+enum MXL_EAGLE_POWER_SUPPLY_SOURCE_E {
+ MXL_EAGLE_POWER_SUPPLY_SOURCE_SINGLE, /* Single supply of 3.3V */
+ MXL_EAGLE_POWER_SUPPLY_SOURCE_DUAL, /* Dual supply, 1.8V & 3.3V */
+};
+
+/* Enum of I/O pad drive modes */
+enum MXL_EAGLE_IO_MUX_DRIVE_MODE_E {
+ MXL_EAGLE_IO_MUX_DRIVE_MODE_1X,
+ MXL_EAGLE_IO_MUX_DRIVE_MODE_2X,
+ MXL_EAGLE_IO_MUX_DRIVE_MODE_3X,
+ MXL_EAGLE_IO_MUX_DRIVE_MODE_4X,
+ MXL_EAGLE_IO_MUX_DRIVE_MODE_5X,
+ MXL_EAGLE_IO_MUX_DRIVE_MODE_6X,
+ MXL_EAGLE_IO_MUX_DRIVE_MODE_7X,
+ MXL_EAGLE_IO_MUX_DRIVE_MODE_8X,
+};
+
+/* Enum of demodulator types. Used for selection of demodulator
+ * type in relevant devices, e.g. ATSC vs. QAM in Mxl691
+ */
+enum MXL_EAGLE_DEMOD_TYPE_E {
+ MXL_EAGLE_DEMOD_TYPE_QAM, /* Mxl248 or Mxl692 */
+ MXL_EAGLE_DEMOD_TYPE_OOB, /* Mxl248 only */
+ MXL_EAGLE_DEMOD_TYPE_ATSC /* Mxl691 or Mxl692 */
+};
+
+/* Enum of power modes. Used for initial
+ * activation, or for activating sleep mode
+ */
+enum MXL_EAGLE_POWER_MODE_E {
+ MXL_EAGLE_POWER_MODE_SLEEP,
+ MXL_EAGLE_POWER_MODE_ACTIVE
+};
+
+/* Enum of GPIOs, used in device GPIO APIs */
+enum MXL_EAGLE_GPIO_NUMBER_E {
+ MXL_EAGLE_GPIO_NUMBER_0,
+ MXL_EAGLE_GPIO_NUMBER_1,
+ MXL_EAGLE_GPIO_NUMBER_2,
+ MXL_EAGLE_GPIO_NUMBER_3,
+ MXL_EAGLE_GPIO_NUMBER_4,
+ MXL_EAGLE_GPIO_NUMBER_5,
+ MXL_EAGLE_GPIO_NUMBER_6
+};
+
+/* Enum of GPIO directions, used in GPIO direction configuration API */
+enum MXL_EAGLE_GPIO_DIRECTION_E {
+ MXL_EAGLE_GPIO_DIRECTION_INPUT,
+ MXL_EAGLE_GPIO_DIRECTION_OUTPUT
+};
+
+/* Enum of GPIO level, used in device GPIO APIs */
+enum MXL_EAGLE_GPIO_LEVEL_E {
+ MXL_EAGLE_GPIO_LEVEL_LOW,
+ MXL_EAGLE_GPIO_LEVEL_HIGH,
+};
+
+/* Enum of I/O Mux function, used in device I/O mux configuration API */
+enum MXL_EAGLE_IOMUX_FUNCTION_E {
+ MXL_EAGLE_IOMUX_FUNC_FEC_LOCK,
+ MXL_EAGLE_IOMUX_FUNC_MERR,
+};
+
+/* Enum of MPEG Data format, used in MPEG and OOB output configuration */
+enum MXL_EAGLE_MPEG_DATA_FORMAT_E {
+ MXL_EAGLE_DATA_SERIAL_LSB_1ST = 0,
+ MXL_EAGLE_DATA_SERIAL_MSB_1ST,
+
+ MXL_EAGLE_DATA_SYNC_WIDTH_BIT = 0,
+ MXL_EAGLE_DATA_SYNC_WIDTH_BYTE
+};
+
+/* Enum of MPEG Clock format, used in MPEG and OOB output configuration */
+enum MXL_EAGLE_MPEG_CLOCK_FORMAT_E {
+ MXL_EAGLE_CLOCK_ACTIVE_HIGH = 0,
+ MXL_EAGLE_CLOCK_ACTIVE_LOW,
+
+ MXL_EAGLE_CLOCK_POSITIVE = 0,
+ MXL_EAGLE_CLOCK_NEGATIVE,
+
+ MXL_EAGLE_CLOCK_IN_PHASE = 0,
+ MXL_EAGLE_CLOCK_INVERTED,
+};
+
+/* Enum of MPEG Clock speeds, used in MPEG output configuration */
+enum MXL_EAGLE_MPEG_CLOCK_RATE_E {
+ MXL_EAGLE_MPEG_CLOCK_54MHZ,
+ MXL_EAGLE_MPEG_CLOCK_40_5MHZ,
+ MXL_EAGLE_MPEG_CLOCK_27MHZ,
+ MXL_EAGLE_MPEG_CLOCK_13_5MHZ,
+};
+
+/* Enum of Interrupt mask bit, used in host interrupt configuration */
+enum MXL_EAGLE_INTR_MASK_BITS_E {
+ MXL_EAGLE_INTR_MASK_DEMOD = 0,
+ MXL_EAGLE_INTR_MASK_SMA_RX = 1,
+ MXL_EAGLE_INTR_MASK_WDOG = 31
+};
+
+/* Enum of QAM Demodulator type, used in QAM configuration */
+enum MXL_EAGLE_QAM_DEMOD_ANNEX_TYPE_E {
+ MXL_EAGLE_QAM_DEMOD_ANNEX_B, /* J.83B */
+ MXL_EAGLE_QAM_DEMOD_ANNEX_A, /* DVB-C */
+};
+
+/* Enum of QAM Demodulator modulation, used in QAM configuration and status */
+enum MXL_EAGLE_QAM_DEMOD_QAM_TYPE_E {
+ MXL_EAGLE_QAM_DEMOD_QAM16,
+ MXL_EAGLE_QAM_DEMOD_QAM64,
+ MXL_EAGLE_QAM_DEMOD_QAM256,
+ MXL_EAGLE_QAM_DEMOD_QAM1024,
+ MXL_EAGLE_QAM_DEMOD_QAM32,
+ MXL_EAGLE_QAM_DEMOD_QAM128,
+ MXL_EAGLE_QAM_DEMOD_QPSK,
+ MXL_EAGLE_QAM_DEMOD_AUTO,
+};
+
+/* Enum of Demodulator IQ setup, used in QAM, OOB configuration and status */
+enum MXL_EAGLE_IQ_FLIP_E {
+ MXL_EAGLE_DEMOD_IQ_NORMAL,
+ MXL_EAGLE_DEMOD_IQ_FLIPPED,
+ MXL_EAGLE_DEMOD_IQ_AUTO,
+};
+
+/* Enum of OOB Demodulator symbol rates, used in OOB configuration */
+enum MXL_EAGLE_OOB_DEMOD_SYMB_RATE_E {
+ MXL_EAGLE_OOB_DEMOD_SYMB_RATE_0_772MHZ, /* ANSI/SCTE 55-2 0.772 MHz */
+ MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_024MHZ, /* ANSI/SCTE 55-1 1.024 MHz */
+ MXL_EAGLE_OOB_DEMOD_SYMB_RATE_1_544MHZ, /* ANSI/SCTE 55-2 1.544 MHz */
+};
+
+/* Enum of tuner channel tuning mode */
+enum MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_E {
+ MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_VIEW, /* Normal "view" mode */
+ MXL_EAGLE_TUNER_CHANNEL_TUNE_MODE_SCAN, /* Fast "scan" mode */
+};
+
+/* Enum of tuner bandwidth */
+enum MXL_EAGLE_TUNER_BW_E {
+ MXL_EAGLE_TUNER_BW_6MHZ,
+ MXL_EAGLE_TUNER_BW_7MHZ,
+ MXL_EAGLE_TUNER_BW_8MHZ,
+};
+
+/* Enum of tuner bandwidth */
+enum MXL_EAGLE_JUNCTION_TEMPERATURE_E {
+ MXL_EAGLE_JUNCTION_TEMPERATURE_BELOW_0_CELSIUS = 0,
+ MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_0_TO_14_CELSIUS = 1,
+ MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_14_TO_28_CELSIUS = 3,
+ MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_28_TO_42_CELSIUS = 2,
+ MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_42_TO_57_CELSIUS = 6,
+ MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_57_TO_71_CELSIUS = 7,
+ MXL_EAGLE_JUNCTION_TEMPERATURE_BETWEEN_71_TO_85_CELSIUS = 5,
+ MXL_EAGLE_JUNCTION_TEMPERATURE_ABOVE_85_CELSIUS = 4,
+};
+
+/* Struct passed in optional callback used during FW download */
+struct MXL_EAGLE_FW_DOWNLOAD_CB_PAYLOAD_T {
+ u32 total_len;
+ u32 downloaded_len;
+};
+
+/* Struct used of I2C protocol between host and Eagle, internal use only */
+struct __packed MXL_EAGLE_HOST_MSG_HEADER_T {
+ u8 opcode;
+ u8 seqnum;
+ u8 payload_size;
+ u8 status;
+ u32 checksum;
+};
+
+/* Device version information struct */
+struct __packed MXL_EAGLE_DEV_VER_T {
+ u8 chip_id;
+ u8 firmware_ver[MXL_EAGLE_VERSION_SIZE];
+ u8 mxlware_ver[MXL_EAGLE_VERSION_SIZE];
+};
+
+/* Xtal configuration struct */
+struct __packed MXL_EAGLE_DEV_XTAL_T {
+ u8 xtal_cap; /* accepted range is 1..31 pF. Default is 26 */
+ u8 clk_out_enable;
+ u8 clk_out_div_enable; /* clock out freq is xtal freq / 6 */
+ u8 xtal_sharing_enable; /* if enabled set xtal_cap to 25 pF */
+ u8 xtal_calibration_enable; /* enable for master, disable for slave */
+};
+
+/* GPIO direction struct, internally used in GPIO configuration API */
+struct __packed MXL_EAGLE_DEV_GPIO_DIRECTION_T {
+ u8 gpio_number;
+ u8 gpio_direction;
+};
+
+/* GPO level struct, internally used in GPIO configuration API */
+struct __packed MXL_EAGLE_DEV_GPO_LEVEL_T {
+ u8 gpio_number;
+ u8 gpo_level;
+};
+
+/* Device Status struct */
+struct MXL_EAGLE_DEV_STATUS_T {
+ u8 temperature;
+ u8 demod_type;
+ u8 power_mode;
+ u8 cpu_utilization_percent;
+};
+
+/* Device interrupt configuration struct */
+struct __packed MXL_EAGLE_DEV_INTR_CFG_T {
+ u32 intr_mask;
+ u8 edge_trigger;
+ u8 positive_trigger;
+ u8 global_enable_interrupt;
+};
+
+/* MPEG pad drive parameters, used on MPEG output configuration */
+/* See MXL_EAGLE_IO_MUX_DRIVE_MODE_E */
+struct MXL_EAGLE_MPEG_PAD_DRIVE_T {
+ u8 pad_drv_mpeg_syn;
+ u8 pad_drv_mpeg_dat;
+ u8 pad_drv_mpeg_val;
+ u8 pad_drv_mpeg_clk;
+};
+
+/* MPEGOUT parameter struct, used in MPEG output configuration */
+struct MXL_EAGLE_MPEGOUT_PARAMS_T {
+ u8 mpeg_parallel;
+ u8 msb_first;
+ u8 mpeg_sync_pulse_width; /* See MXL_EAGLE_MPEG_DATA_FORMAT_E */
+ u8 mpeg_valid_pol;
+ u8 mpeg_sync_pol;
+ u8 mpeg_clk_pol;
+ u8 mpeg3wire_mode_enable;
+ u8 mpeg_clk_freq;
+ struct MXL_EAGLE_MPEG_PAD_DRIVE_T mpeg_pad_drv;
+};
+
+/* QAM Demodulator parameters struct, used in QAM params configuration */
+struct __packed MXL_EAGLE_QAM_DEMOD_PARAMS_T {
+ u8 annex_type;
+ u8 qam_type;
+ u8 iq_flip;
+ u8 search_range_idx;
+ u8 spur_canceller_enable;
+ u32 symbol_rate_hz;
+ u32 symbol_rate_256qam_hz;
+};
+
+/* QAM Demodulator status */
+struct MXL_EAGLE_QAM_DEMOD_STATUS_T {
+ u8 annex_type;
+ u8 qam_type;
+ u8 iq_flip;
+ u8 interleaver_depth_i;
+ u8 interleaver_depth_j;
+ u8 qam_locked;
+ u8 fec_locked;
+ u8 mpeg_locked;
+ u16 snr_db_tenths;
+ s16 timing_offset;
+ s32 carrier_offset_hz;
+};
+
+/* QAM Demodulator error counters */
+struct MXL_EAGLE_QAM_DEMOD_ERROR_COUNTERS_T {
+ u32 corrected_code_words;
+ u32 uncorrected_code_words;
+ u32 total_code_words_received;
+ u32 corrected_bits;
+ u32 error_mpeg_frames;
+ u32 mpeg_frames_received;
+ u32 erasures;
+};
+
+/* QAM Demodulator constellation point */
+struct MXL_EAGLE_QAM_DEMOD_CONSTELLATION_VAL_T {
+ s16 i_value[12];
+ s16 q_value[12];
+};
+
+/* QAM Demodulator equalizer filter taps */
+struct MXL_EAGLE_QAM_DEMOD_EQU_FILTER_T {
+ s16 ffe_taps[MXL_EAGLE_QAM_FFE_TAPS_LENGTH];
+ s16 spur_taps[MXL_EAGLE_QAM_SPUR_TAPS_LENGTH];
+ s16 dfe_taps[MXL_EAGLE_QAM_DFE_TAPS_LENGTH];
+ u8 ffe_leading_tap_index;
+ u8 dfe_taps_number;
+};
+
+/* OOB Demodulator parameters struct, used in OOB params configuration */
+struct __packed MXL_EAGLE_OOB_DEMOD_PARAMS_T {
+ u8 symbol_rate;
+ u8 iq_flip;
+ u8 clk_pol;
+};
+
+/* OOB Demodulator error counters */
+struct MXL_EAGLE_OOB_DEMOD_ERROR_COUNTERS_T {
+ u32 corrected_packets;
+ u32 uncorrected_packets;
+ u32 total_packets_received;
+};
+
+/* OOB status */
+struct __packed MXL_EAGLE_OOB_DEMOD_STATUS_T {
+ u16 snr_db_tenths;
+ s16 timing_offset;
+ s32 carrier_offsetHz;
+ u8 qam_locked;
+ u8 fec_locked;
+ u8 mpeg_locked;
+ u8 retune_required;
+ u8 iq_flip;
+};
+
+/* ATSC Demodulator status */
+struct __packed MXL_EAGLE_ATSC_DEMOD_STATUS_T {
+ s16 snr_db_tenths;
+ s16 timing_offset;
+ s32 carrier_offset_hz;
+ u8 frame_lock;
+ u8 atsc_lock;
+ u8 fec_lock;
+};
+
+/* ATSC Demodulator error counters */
+struct MXL_EAGLE_ATSC_DEMOD_ERROR_COUNTERS_T {
+ u32 error_packets;
+ u32 total_packets;
+ u32 error_bytes;
+};
+
+/* ATSC Demodulator equalizers filter taps */
+struct __packed MXL_EAGLE_ATSC_DEMOD_EQU_FILTER_T {
+ s16 ffe_taps[MXL_EAGLE_ATSC_FFE_TAPS_LENGTH];
+ s8 dfe_taps[MXL_EAGLE_ATSC_DFE_TAPS_LENGTH];
+};
+
+/* Tuner AGC Status */
+struct __packed MXL_EAGLE_TUNER_AGC_STATUS_T {
+ u8 locked;
+ u16 raw_agc_gain; /* AGC gain [dB] = rawAgcGain / 2^6 */
+ s16 rx_power_db_hundredths;
+};
+
+/* Tuner channel tune parameters */
+struct __packed MXL_EAGLE_TUNER_CHANNEL_PARAMS_T {
+ u32 freq_hz;
+ u8 tune_mode;
+ u8 bandwidth;
+};
+
+/* Tuner channel lock indications */
+struct __packed MXL_EAGLE_TUNER_LOCK_STATUS_T {
+ u8 rf_pll_locked;
+ u8 ref_pll_locked;
+};
+
+/* Smart antenna parameters used in Smart antenna params configuration */
+struct __packed MXL_EAGLE_SMA_PARAMS_T {
+ u8 full_duplex_enable;
+ u8 rx_disable;
+ u8 idle_logic_high;
+};
+
+/* Smart antenna message format */
+struct __packed MXL_EAGLE_SMA_MESSAGE_T {
+ u32 payload_bits;
+ u8 total_num_bits;
+};
+
diff --git a/drivers/media/dvb/frontends/nxt200x.c b/drivers/media/dvb-frontends/nxt200x.c
index 49ca78d883b1..1c549ada6ebf 100644
--- a/drivers/media/dvb/frontends/nxt200x.c
+++ b/drivers/media/dvb-frontends/nxt200x.c
@@ -1,25 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Support for NXT2002 and NXT2004 - VSB/QAM
*
* Copyright (C) 2005 Kirk Lapray <kirk.lapray@gmail.com>
- * Copyright (C) 2006 Michael Krufky <mkrufky@m1k.net>
+ * Copyright (C) 2006-2014 Michael Krufky <mkrufky@linuxtv.org>
* based on nxt2002 by Taylor Jacob <rtjacob@earthlink.net>
* and nxt2004 by Jean-Francois Thibert <jeanfrancois@sagetv.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
/*
@@ -31,12 +17,17 @@
* ATI HDTV Wonder (NXT2004)
*
* This driver needs external firmware. Please use the command
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2002" or
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2004" to
+ * "<kerneldir>/scripts/get_dvb_firmware nxt2002" or
+ * "<kerneldir>/scripts/get_dvb_firmware nxt2004" to
* download/extract the appropriate firmware, and then copy it to
* /usr/lib/hotplug/firmware/ or /lib/firmware/
* (depending on configuration of firmware hotplug).
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE 256
+
#define NXT2002_DEFAULT_FIRMWARE "dvb-fe-nxt2002.fw"
#define NXT2004_DEFAULT_FIRMWARE "dvb-fe-nxt2004.fw"
#define CRC_CCIT_MASK 0x1021
@@ -47,7 +38,7 @@
#include <linux/slab.h>
#include <linux/string.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "nxt200x.h"
struct nxt200x_state {
@@ -62,10 +53,7 @@ struct nxt200x_state {
};
static int debug;
-#define dprintk(args...) \
- do { \
- if (debug) printk(KERN_DEBUG "nxt200x: " args); \
- } while (0)
+#define dprintk(args...) do { if (debug) pr_debug(args); } while (0)
static int i2c_writebytes (struct nxt200x_state* state, u8 addr, u8 *buf, u8 len)
{
@@ -73,7 +61,7 @@ static int i2c_writebytes (struct nxt200x_state* state, u8 addr, u8 *buf, u8 len
struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = len };
if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
- printk (KERN_WARNING "nxt200x: %s: i2c write error (addr 0x%02x, err == %i)\n",
+ pr_warn("%s: i2c write error (addr 0x%02x, err == %i)\n",
__func__, addr, err);
return -EREMOTEIO;
}
@@ -86,7 +74,7 @@ static int i2c_readbytes(struct nxt200x_state *state, u8 addr, u8 *buf, u8 len)
struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = buf, .len = len };
if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
- printk (KERN_WARNING "nxt200x: %s: i2c read error (addr 0x%02x, err == %i)\n",
+ pr_warn("%s: i2c read error (addr 0x%02x, err == %i)\n",
__func__, addr, err);
return -EREMOTEIO;
}
@@ -96,15 +84,21 @@ static int i2c_readbytes(struct nxt200x_state *state, u8 addr, u8 *buf, u8 len)
static int nxt200x_writebytes (struct nxt200x_state* state, u8 reg,
const u8 *buf, u8 len)
{
- u8 buf2 [len+1];
+ u8 buf2[MAX_XFER_SIZE];
int err;
struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf2, .len = len + 1 };
+ if (1 + len > sizeof(buf2)) {
+ pr_warn("%s: i2c wr reg=%04x: len=%d is too big!\n",
+ __func__, reg, len);
+ return -EINVAL;
+ }
+
buf2[0] = reg;
memcpy(&buf2[1], buf, len);
if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
- printk (KERN_WARNING "nxt200x: %s: i2c write error (addr 0x%02x, err == %i)\n",
+ pr_warn("%s: i2c write error (addr 0x%02x, err == %i)\n",
__func__, state->config->demod_address, err);
return -EREMOTEIO;
}
@@ -121,7 +115,7 @@ static int nxt200x_readbytes(struct nxt200x_state *state, u8 reg, u8 *buf, u8 le
int err;
if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) {
- printk (KERN_WARNING "nxt200x: %s: i2c read error (addr 0x%02x, err == %i)\n",
+ pr_warn("%s: i2c read error (addr 0x%02x, err == %i)\n",
__func__, state->config->demod_address, err);
return -EREMOTEIO;
}
@@ -149,7 +143,7 @@ static int nxt200x_writereg_multibyte (struct nxt200x_state* state, u8 reg, u8*
u8 attr, len2, buf;
dprintk("%s\n", __func__);
- /* set mutli register register */
+ /* set multi register register */
nxt200x_writebytes(state, 0x35, &reg, 1);
/* send the actual data */
@@ -174,7 +168,6 @@ static int nxt200x_writereg_multibyte (struct nxt200x_state* state, u8 reg, u8*
break;
default:
return -EINVAL;
- break;
}
/* set multi register length */
@@ -196,10 +189,9 @@ static int nxt200x_writereg_multibyte (struct nxt200x_state* state, u8 reg, u8*
break;
default:
return -EINVAL;
- break;
}
- printk(KERN_WARNING "nxt200x: Error writing multireg register 0x%02X\n",reg);
+ pr_warn("Error writing multireg register 0x%02X\n", reg);
return 0;
}
@@ -210,7 +202,7 @@ static int nxt200x_readreg_multibyte (struct nxt200x_state* state, u8 reg, u8* d
u8 buf, len2, attr;
dprintk("%s\n", __func__);
- /* set mutli register register */
+ /* set multi register register */
nxt200x_writebytes(state, 0x35, &reg, 1);
switch (state->demod_chip) {
@@ -222,7 +214,6 @@ static int nxt200x_readreg_multibyte (struct nxt200x_state* state, u8 reg, u8* d
/* read the actual data */
nxt200x_readbytes(state, reg, data, len);
return 0;
- break;
case NXT2004:
/* probably not right, but gives correct values */
attr = 0x02;
@@ -245,10 +236,8 @@ static int nxt200x_readreg_multibyte (struct nxt200x_state* state, u8 reg, u8* d
nxt200x_readbytes(state, 0x36 + i, &data[i], 1);
}
return 0;
- break;
default:
return -EINVAL;
- break;
}
}
@@ -281,7 +270,7 @@ static void nxt200x_microcontroller_stop (struct nxt200x_state* state)
counter++;
}
- printk(KERN_WARNING "nxt200x: Timeout waiting for nxt200x to stop. This is ok after firmware upload.\n");
+ pr_warn("Timeout waiting for nxt200x to stop. This is ok after firmware upload.\n");
return;
}
@@ -320,7 +309,7 @@ static void nxt2004_microcontroller_init (struct nxt200x_state* state)
counter++;
}
- printk(KERN_WARNING "nxt200x: Timeout waiting for nxt2004 to init.\n");
+ pr_warn("Timeout waiting for nxt2004 to init.\n");
return;
}
@@ -331,14 +320,14 @@ static int nxt200x_writetuner (struct nxt200x_state* state, u8* data)
dprintk("%s\n", __func__);
- dprintk("Tuner Bytes: %02X %02X %02X %02X\n", data[1], data[2], data[3], data[4]);
+ dprintk("Tuner Bytes: %*ph\n", 4, data + 1);
/* if NXT2004, write directly to tuner. if NXT2002, write through NXT chip.
* direct write is required for Philips TUV1236D and ALPS TDHU2 */
switch (state->demod_chip) {
case NXT2004:
if (i2c_writebytes(state, data[0], data+1, 4))
- printk(KERN_WARNING "nxt200x: error writing to tuner\n");
+ pr_warn("error writing to tuner\n");
/* wait until we have a lock */
while (count < 20) {
i2c_readbytes(state, data[0], &buf, 1);
@@ -347,7 +336,7 @@ static int nxt200x_writetuner (struct nxt200x_state* state, u8* data)
msleep(100);
count++;
}
- printk("nxt2004: timeout waiting for tuner lock\n");
+ pr_warn("timeout waiting for tuner lock\n");
break;
case NXT2002:
/* set the i2c transfer speed to the tuner */
@@ -376,11 +365,10 @@ static int nxt200x_writetuner (struct nxt200x_state* state, u8* data)
msleep(100);
count++;
}
- printk("nxt2002: timeout error writing tuner\n");
+ pr_warn("timeout error writing to tuner\n");
break;
default:
return -EINVAL;
- break;
}
return 0;
}
@@ -561,7 +549,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe)
break;
default:
return -EINVAL;
- break;
}
if (fe->ops.tuner_ops.calc_regs) {
@@ -586,7 +573,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe)
break;
default:
return -EINVAL;
- break;
}
nxt200x_writebytes(state, 0x42, buf, 1);
@@ -600,7 +586,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe)
break;
default:
return -EINVAL;
- break;
}
nxt200x_writebytes(state, 0x57, buf, 1);
@@ -616,7 +601,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe)
break;
default:
return -EINVAL;
- break;
}
/* write sdmx input */
@@ -632,7 +616,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe)
break;
default:
return -EINVAL;
- break;
}
buf[1] = 0x00;
switch (state->demod_chip) {
@@ -644,7 +627,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe)
break;
default:
return -EINVAL;
- break;
}
/* write adc power lpf fc */
@@ -670,7 +652,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe)
break;
default:
return -EINVAL;
- break;
}
/* write kg1 */
@@ -726,7 +707,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe)
break;
default:
return -EINVAL;
- break;
}
nxt200x_writebytes(state, 0x30, buf, 1);
@@ -748,7 +728,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe)
break;
default:
return -EINVAL;
- break;
}
/* write agc control reg */
@@ -772,7 +751,7 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe)
return 0;
}
-static int nxt200x_read_status(struct dvb_frontend* fe, fe_status_t* status)
+static int nxt200x_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct nxt200x_state* state = fe->demodulator_priv;
u8 lock;
@@ -878,22 +857,24 @@ static int nxt2002_init(struct dvb_frontend* fe)
u8 buf[2];
/* request the firmware, this will block until someone uploads it */
- printk("nxt2002: Waiting for firmware upload (%s)...\n", NXT2002_DEFAULT_FIRMWARE);
+ pr_debug("%s: Waiting for firmware upload (%s)...\n",
+ __func__, NXT2002_DEFAULT_FIRMWARE);
ret = request_firmware(&fw, NXT2002_DEFAULT_FIRMWARE,
state->i2c->dev.parent);
- printk("nxt2002: Waiting for firmware upload(2)...\n");
+ pr_debug("%s: Waiting for firmware upload(2)...\n", __func__);
if (ret) {
- printk("nxt2002: No firmware uploaded (timeout or file not found?)\n");
+ pr_err("%s: No firmware uploaded (timeout or file not found?)\n",
+ __func__);
return ret;
}
ret = nxt2002_load_firmware(fe, fw);
release_firmware(fw);
if (ret) {
- printk("nxt2002: Writing firmware to device failed\n");
+ pr_err("%s: Writing firmware to device failed\n", __func__);
return ret;
}
- printk("nxt2002: Firmware upload complete\n");
+ pr_info("%s: Firmware upload complete\n", __func__);
/* Put the micro into reset */
nxt200x_microcontroller_stop(state);
@@ -943,22 +924,24 @@ static int nxt2004_init(struct dvb_frontend* fe)
nxt200x_writebytes(state, 0x1E, buf, 1);
/* request the firmware, this will block until someone uploads it */
- printk("nxt2004: Waiting for firmware upload (%s)...\n", NXT2004_DEFAULT_FIRMWARE);
+ pr_debug("%s: Waiting for firmware upload (%s)...\n",
+ __func__, NXT2004_DEFAULT_FIRMWARE);
ret = request_firmware(&fw, NXT2004_DEFAULT_FIRMWARE,
state->i2c->dev.parent);
- printk("nxt2004: Waiting for firmware upload(2)...\n");
+ pr_debug("%s: Waiting for firmware upload(2)...\n", __func__);
if (ret) {
- printk("nxt2004: No firmware uploaded (timeout or file not found?)\n");
+ pr_err("%s: No firmware uploaded (timeout or file not found?)\n",
+ __func__);
return ret;
}
ret = nxt2004_load_firmware(fe, fw);
release_firmware(fw);
if (ret) {
- printk("nxt2004: Writing firmware to device failed\n");
+ pr_err("%s: Writing firmware to device failed\n", __func__);
return ret;
}
- printk("nxt2004: Firmware upload complete\n");
+ pr_info("%s: Firmware upload complete\n", __func__);
/* ensure transfer is complete */
buf[0] = 0x01;
@@ -1116,7 +1099,6 @@ static int nxt200x_init(struct dvb_frontend* fe)
break;
default:
return -EINVAL;
- break;
}
state->initialised = 1;
}
@@ -1137,7 +1119,7 @@ static void nxt200x_release(struct dvb_frontend* fe)
kfree(state);
}
-static struct dvb_frontend_ops nxt200x_ops;
+static const struct dvb_frontend_ops nxt200x_ops;
struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config,
struct i2c_adapter* i2c)
@@ -1157,18 +1139,17 @@ struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config,
/* read card id */
nxt200x_readbytes(state, 0x00, buf, 5);
- dprintk("NXT info: %02X %02X %02X %02X %02X\n",
- buf[0], buf[1], buf[2], buf[3], buf[4]);
+ dprintk("NXT info: %*ph\n", 5, buf);
/* set demod chip */
switch (buf[0]) {
case 0x04:
state->demod_chip = NXT2002;
- printk("nxt200x: NXT2002 Detected\n");
+ pr_info("NXT2002 Detected\n");
break;
case 0x05:
state->demod_chip = NXT2004;
- printk("nxt200x: NXT2004 Detected\n");
+ pr_info("NXT2004 Detected\n");
break;
default:
goto error;
@@ -1197,18 +1178,17 @@ struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config,
error:
kfree(state);
- printk("Unknown/Unsupported NXT chip: %02X %02X %02X %02X %02X\n",
- buf[0], buf[1], buf[2], buf[3], buf[4]);
+ pr_err("Unknown/Unsupported NXT chip: %*ph\n", 5, buf);
return NULL;
}
-static struct dvb_frontend_ops nxt200x_ops = {
+static const struct dvb_frontend_ops nxt200x_ops = {
.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
.info = {
.name = "Nextwave NXT200X VSB/QAM frontend",
- .frequency_min = 54000000,
- .frequency_max = 860000000,
- .frequency_stepsize = 166666, /* stepsize is just a guess */
+ .frequency_min_hz = 54 * MHz,
+ .frequency_max_hz = 860 * MHz,
+ .frequency_stepsize_hz = 166666, /* stepsize is just a guess */
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
FE_CAN_8VSB | FE_CAN_QAM_64 | FE_CAN_QAM_256
@@ -1236,5 +1216,5 @@ MODULE_DESCRIPTION("NXT200X (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulat
MODULE_AUTHOR("Kirk Lapray, Michael Krufky, Jean-Francois Thibert, and Taylor Jacob");
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(nxt200x_attach);
+EXPORT_SYMBOL_GPL(nxt200x_attach);
diff --git a/drivers/media/dvb-frontends/nxt200x.h b/drivers/media/dvb-frontends/nxt200x.h
new file mode 100644
index 000000000000..6b03aeb74606
--- /dev/null
+++ b/drivers/media/dvb-frontends/nxt200x.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for NXT2002 and NXT2004 - VSB/QAM
+ *
+ * Copyright (C) 2005 Kirk Lapray (kirk.lapray@gmail.com)
+ * based on nxt2002 by Taylor Jacob <rtjacob@earthlink.net>
+ * and nxt2004 by Jean-Francois Thibert (jeanfrancois@sagetv.com)
+*/
+
+#ifndef NXT200X_H
+#define NXT200X_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+typedef enum nxt_chip_t {
+ NXTUNDEFINED,
+ NXT2002,
+ NXT2004
+}nxt_chip_type;
+
+struct nxt200x_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* need to set device param for start_dma */
+ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
+};
+
+#if IS_REACHABLE(CONFIG_DVB_NXT200X)
+extern struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config,
+ struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_NXT200X
+
+#endif /* NXT200X_H */
diff --git a/drivers/media/dvb/frontends/nxt6000.c b/drivers/media/dvb-frontends/nxt6000.c
index 90ae6c72c0e3..e8d4940370dd 100644
--- a/drivers/media/dvb/frontends/nxt6000.c
+++ b/drivers/media/dvb-frontends/nxt6000.c
@@ -1,31 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
NxtWave Communications - NXT6000 demodulator driver
Copyright (C) 2002-2003 Florian Schirmer <jolt@tuxbox.org>
Copyright (C) 2003 Paul Andreassen <paul@andreassen.com.au>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/slab.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "nxt6000_priv.h"
#include "nxt6000.h"
@@ -39,7 +29,11 @@ struct nxt6000_state {
};
static int debug;
-#define dprintk if (debug) printk
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
static int nxt6000_writereg(struct nxt6000_state* state, u8 reg, u8 data)
{
@@ -109,7 +103,8 @@ static int nxt6000_set_bandwidth(struct nxt6000_state *state, u32 bandwidth)
return nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, (nominal_rate >> 8) & 0xFF);
}
-static int nxt6000_set_guard_interval(struct nxt6000_state* state, fe_guard_interval_t guard_interval)
+static int nxt6000_set_guard_interval(struct nxt6000_state *state,
+ enum fe_guard_interval guard_interval)
{
switch (guard_interval) {
@@ -131,7 +126,8 @@ static int nxt6000_set_guard_interval(struct nxt6000_state* state, fe_guard_inte
}
}
-static int nxt6000_set_inversion(struct nxt6000_state* state, fe_spectral_inversion_t inversion)
+static int nxt6000_set_inversion(struct nxt6000_state *state,
+ enum fe_spectral_inversion inversion)
{
switch (inversion) {
@@ -147,7 +143,9 @@ static int nxt6000_set_inversion(struct nxt6000_state* state, fe_spectral_invers
}
}
-static int nxt6000_set_transmission_mode(struct nxt6000_state* state, fe_transmit_mode_t transmission_mode)
+static int
+nxt6000_set_transmission_mode(struct nxt6000_state *state,
+ enum fe_transmit_mode transmission_mode)
{
int result;
@@ -211,119 +209,129 @@ static void nxt6000_dump_status(struct nxt6000_state *state)
{
u8 val;
-/*
- printk("RS_COR_STAT: 0x%02X\n", nxt6000_readreg(fe, RS_COR_STAT));
- printk("VIT_SYNC_STATUS: 0x%02X\n", nxt6000_readreg(fe, VIT_SYNC_STATUS));
- printk("OFDM_COR_STAT: 0x%02X\n", nxt6000_readreg(fe, OFDM_COR_STAT));
- printk("OFDM_SYR_STAT: 0x%02X\n", nxt6000_readreg(fe, OFDM_SYR_STAT));
- printk("OFDM_TPS_RCVD_1: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_1));
- printk("OFDM_TPS_RCVD_2: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_2));
- printk("OFDM_TPS_RCVD_3: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_3));
- printk("OFDM_TPS_RCVD_4: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_4));
- printk("OFDM_TPS_RESERVED_1: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RESERVED_1));
- printk("OFDM_TPS_RESERVED_2: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RESERVED_2));
-*/
- printk("NXT6000 status:");
+#if 0
+ pr_info("RS_COR_STAT: 0x%02X\n",
+ nxt6000_readreg(fe, RS_COR_STAT));
+ pr_info("VIT_SYNC_STATUS: 0x%02X\n",
+ nxt6000_readreg(fe, VIT_SYNC_STATUS));
+ pr_info("OFDM_COR_STAT: 0x%02X\n",
+ nxt6000_readreg(fe, OFDM_COR_STAT));
+ pr_info("OFDM_SYR_STAT: 0x%02X\n",
+ nxt6000_readreg(fe, OFDM_SYR_STAT));
+ pr_info("OFDM_TPS_RCVD_1: 0x%02X\n",
+ nxt6000_readreg(fe, OFDM_TPS_RCVD_1));
+ pr_info("OFDM_TPS_RCVD_2: 0x%02X\n",
+ nxt6000_readreg(fe, OFDM_TPS_RCVD_2));
+ pr_info("OFDM_TPS_RCVD_3: 0x%02X\n",
+ nxt6000_readreg(fe, OFDM_TPS_RCVD_3));
+ pr_info("OFDM_TPS_RCVD_4: 0x%02X\n",
+ nxt6000_readreg(fe, OFDM_TPS_RCVD_4));
+ pr_info("OFDM_TPS_RESERVED_1: 0x%02X\n",
+ nxt6000_readreg(fe, OFDM_TPS_RESERVED_1));
+ pr_info("OFDM_TPS_RESERVED_2: 0x%02X\n",
+ nxt6000_readreg(fe, OFDM_TPS_RESERVED_2));
+#endif
+ pr_info("NXT6000 status:");
val = nxt6000_readreg(state, RS_COR_STAT);
- printk(" DATA DESCR LOCK: %d,", val & 0x01);
- printk(" DATA SYNC LOCK: %d,", (val >> 1) & 0x01);
+ pr_cont(" DATA DESCR LOCK: %d,", val & 0x01);
+ pr_cont(" DATA SYNC LOCK: %d,", (val >> 1) & 0x01);
val = nxt6000_readreg(state, VIT_SYNC_STATUS);
- printk(" VITERBI LOCK: %d,", (val >> 7) & 0x01);
+ pr_cont(" VITERBI LOCK: %d,", (val >> 7) & 0x01);
switch ((val >> 4) & 0x07) {
case 0x00:
- printk(" VITERBI CODERATE: 1/2,");
+ pr_cont(" VITERBI CODERATE: 1/2,");
break;
case 0x01:
- printk(" VITERBI CODERATE: 2/3,");
+ pr_cont(" VITERBI CODERATE: 2/3,");
break;
case 0x02:
- printk(" VITERBI CODERATE: 3/4,");
+ pr_cont(" VITERBI CODERATE: 3/4,");
break;
case 0x03:
- printk(" VITERBI CODERATE: 5/6,");
+ pr_cont(" VITERBI CODERATE: 5/6,");
break;
case 0x04:
- printk(" VITERBI CODERATE: 7/8,");
+ pr_cont(" VITERBI CODERATE: 7/8,");
break;
default:
- printk(" VITERBI CODERATE: Reserved,");
+ pr_cont(" VITERBI CODERATE: Reserved,");
}
val = nxt6000_readreg(state, OFDM_COR_STAT);
- printk(" CHCTrack: %d,", (val >> 7) & 0x01);
- printk(" TPSLock: %d,", (val >> 6) & 0x01);
- printk(" SYRLock: %d,", (val >> 5) & 0x01);
- printk(" AGCLock: %d,", (val >> 4) & 0x01);
+ pr_cont(" CHCTrack: %d,", (val >> 7) & 0x01);
+ pr_cont(" TPSLock: %d,", (val >> 6) & 0x01);
+ pr_cont(" SYRLock: %d,", (val >> 5) & 0x01);
+ pr_cont(" AGCLock: %d,", (val >> 4) & 0x01);
switch (val & 0x0F) {
case 0x00:
- printk(" CoreState: IDLE,");
+ pr_cont(" CoreState: IDLE,");
break;
case 0x02:
- printk(" CoreState: WAIT_AGC,");
+ pr_cont(" CoreState: WAIT_AGC,");
break;
case 0x03:
- printk(" CoreState: WAIT_SYR,");
+ pr_cont(" CoreState: WAIT_SYR,");
break;
case 0x04:
- printk(" CoreState: WAIT_PPM,");
+ pr_cont(" CoreState: WAIT_PPM,");
break;
case 0x01:
- printk(" CoreState: WAIT_TRL,");
+ pr_cont(" CoreState: WAIT_TRL,");
break;
case 0x05:
- printk(" CoreState: WAIT_TPS,");
+ pr_cont(" CoreState: WAIT_TPS,");
break;
case 0x06:
- printk(" CoreState: MONITOR_TPS,");
+ pr_cont(" CoreState: MONITOR_TPS,");
break;
default:
- printk(" CoreState: Reserved,");
+ pr_cont(" CoreState: Reserved,");
}
val = nxt6000_readreg(state, OFDM_SYR_STAT);
- printk(" SYRLock: %d,", (val >> 4) & 0x01);
- printk(" SYRMode: %s,", (val >> 2) & 0x01 ? "8K" : "2K");
+ pr_cont(" SYRLock: %d,", (val >> 4) & 0x01);
+ pr_cont(" SYRMode: %s,", (val >> 2) & 0x01 ? "8K" : "2K");
switch ((val >> 4) & 0x03) {
case 0x00:
- printk(" SYRGuard: 1/32,");
+ pr_cont(" SYRGuard: 1/32,");
break;
case 0x01:
- printk(" SYRGuard: 1/16,");
+ pr_cont(" SYRGuard: 1/16,");
break;
case 0x02:
- printk(" SYRGuard: 1/8,");
+ pr_cont(" SYRGuard: 1/8,");
break;
case 0x03:
- printk(" SYRGuard: 1/4,");
+ pr_cont(" SYRGuard: 1/4,");
break;
}
@@ -332,77 +340,77 @@ static void nxt6000_dump_status(struct nxt6000_state *state)
switch ((val >> 4) & 0x07) {
case 0x00:
- printk(" TPSLP: 1/2,");
+ pr_cont(" TPSLP: 1/2,");
break;
case 0x01:
- printk(" TPSLP: 2/3,");
+ pr_cont(" TPSLP: 2/3,");
break;
case 0x02:
- printk(" TPSLP: 3/4,");
+ pr_cont(" TPSLP: 3/4,");
break;
case 0x03:
- printk(" TPSLP: 5/6,");
+ pr_cont(" TPSLP: 5/6,");
break;
case 0x04:
- printk(" TPSLP: 7/8,");
+ pr_cont(" TPSLP: 7/8,");
break;
default:
- printk(" TPSLP: Reserved,");
+ pr_cont(" TPSLP: Reserved,");
}
switch (val & 0x07) {
case 0x00:
- printk(" TPSHP: 1/2,");
+ pr_cont(" TPSHP: 1/2,");
break;
case 0x01:
- printk(" TPSHP: 2/3,");
+ pr_cont(" TPSHP: 2/3,");
break;
case 0x02:
- printk(" TPSHP: 3/4,");
+ pr_cont(" TPSHP: 3/4,");
break;
case 0x03:
- printk(" TPSHP: 5/6,");
+ pr_cont(" TPSHP: 5/6,");
break;
case 0x04:
- printk(" TPSHP: 7/8,");
+ pr_cont(" TPSHP: 7/8,");
break;
default:
- printk(" TPSHP: Reserved,");
+ pr_cont(" TPSHP: Reserved,");
}
val = nxt6000_readreg(state, OFDM_TPS_RCVD_4);
- printk(" TPSMode: %s,", val & 0x01 ? "8K" : "2K");
+ pr_cont(" TPSMode: %s,", val & 0x01 ? "8K" : "2K");
switch ((val >> 4) & 0x03) {
case 0x00:
- printk(" TPSGuard: 1/32,");
+ pr_cont(" TPSGuard: 1/32,");
break;
case 0x01:
- printk(" TPSGuard: 1/16,");
+ pr_cont(" TPSGuard: 1/16,");
break;
case 0x02:
- printk(" TPSGuard: 1/8,");
+ pr_cont(" TPSGuard: 1/8,");
break;
case 0x03:
- printk(" TPSGuard: 1/4,");
+ pr_cont(" TPSGuard: 1/4,");
break;
}
@@ -412,11 +420,11 @@ static void nxt6000_dump_status(struct nxt6000_state *state)
val = nxt6000_readreg(state, RF_AGC_STATUS);
val = nxt6000_readreg(state, RF_AGC_STATUS);
- printk(" RF AGC LOCK: %d,", (val >> 4) & 0x01);
- printk("\n");
+ pr_cont(" RF AGC LOCK: %d,", (val >> 4) & 0x01);
+ pr_cont("\n");
}
-static int nxt6000_read_status(struct dvb_frontend* fe, fe_status_t* status)
+static int nxt6000_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
u8 core_status;
struct nxt6000_state* state = fe->demodulator_priv;
@@ -544,7 +552,7 @@ static int nxt6000_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
}
}
-static struct dvb_frontend_ops nxt6000_ops;
+static const struct dvb_frontend_ops nxt6000_ops;
struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
struct i2c_adapter* i2c)
@@ -572,13 +580,13 @@ error:
return NULL;
}
-static struct dvb_frontend_ops nxt6000_ops = {
+static const struct dvb_frontend_ops nxt6000_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = "NxtWave NXT6000 DVB-T",
- .frequency_min = 0,
- .frequency_max = 863250000,
- .frequency_stepsize = 62500,
+ .frequency_min_hz = 0,
+ .frequency_max_hz = 863250 * kHz,
+ .frequency_stepsize_hz = 62500,
/*.frequency_tolerance = *//* FIXME: 12% of SR */
.symbol_rate_min = 0, /* FIXME */
.symbol_rate_max = 9360000, /* FIXME */
@@ -613,4 +621,4 @@ MODULE_DESCRIPTION("NxtWave NXT6000 DVB-T demodulator driver");
MODULE_AUTHOR("Florian Schirmer");
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(nxt6000_attach);
+EXPORT_SYMBOL_GPL(nxt6000_attach);
diff --git a/drivers/media/dvb-frontends/nxt6000.h b/drivers/media/dvb-frontends/nxt6000.h
new file mode 100644
index 000000000000..28d965e53c57
--- /dev/null
+++ b/drivers/media/dvb-frontends/nxt6000.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ NxtWave Communications - NXT6000 demodulator driver
+
+ Copyright (C) 2002-2003 Florian Schirmer <jolt@tuxbox.org>
+ Copyright (C) 2003 Paul Andreassen <paul@andreassen.com.au>
+
+*/
+
+#ifndef NXT6000_H
+#define NXT6000_H
+
+#include <linux/dvb/frontend.h>
+
+struct nxt6000_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* should clock inversion be used? */
+ u8 clock_inversion:1;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_NXT6000)
+extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
+ struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_NXT6000
+
+#endif // NXT6000_H
diff --git a/drivers/media/dvb/frontends/nxt6000_priv.h b/drivers/media/dvb-frontends/nxt6000_priv.h
index 0422e580038a..d317df02c8d7 100644
--- a/drivers/media/dvb/frontends/nxt6000_priv.h
+++ b/drivers/media/dvb-frontends/nxt6000_priv.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Public Include File for DRV6000 users
* (ie. NxtWave Communications - NXT6000 demodulator driver)
diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb-frontends/or51132.c
index 5ef921823c15..74e04c7cca1e 100644
--- a/drivers/media/dvb/frontends/or51132.c
+++ b/drivers/media/dvb-frontends/or51132.c
@@ -1,28 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Support for OR51132 (pcHDTV HD-3000) - VSB/QAM
*
- *
* Copyright (C) 2007 Trent Piepho <xyzzy@speakeasy.org>
*
* Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
*
* Based on code from Jack Kelliher (kelliher@xmission.com)
* Copyright (C) 2002 & pcHDTV, inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
/*
@@ -42,8 +27,8 @@
#include <linux/slab.h>
#include <asm/byteorder.h>
-#include "dvb_math.h"
-#include "dvb_frontend.h"
+#include <linux/int_log.h>
+#include <media/dvb_frontend.h>
#include "or51132.h"
static int debug;
@@ -63,7 +48,7 @@ struct or51132_state
struct dvb_frontend frontend;
/* Demodulator private data */
- fe_modulation_t current_modulation;
+ enum fe_modulation current_modulation;
u32 snr; /* Result of last SNR calculation */
/* Tuner private data */
@@ -137,7 +122,7 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware
u32 firmwareAsize, firmwareBsize;
int i,ret;
- dprintk("Firmware is %Zd bytes\n",fw->size);
+ dprintk("Firmware is %zd bytes\n",fw->size);
/* Get size of firmware A and B */
firmwareAsize = le32_to_cpu(*((__le32*)fw->data));
@@ -292,7 +277,7 @@ static int or51132_setmode(struct dvb_frontend* fe)
#define MOD_FWCLASS_UNKNOWN 0
#define MOD_FWCLASS_VSB 1
#define MOD_FWCLASS_QAM 2
-static int modulation_fw_class(fe_modulation_t modulation)
+static int modulation_fw_class(enum fe_modulation modulation)
{
switch(modulation) {
case VSB_8:
@@ -342,15 +327,13 @@ static int or51132_set_parameters(struct dvb_frontend *fe)
fwname);
ret = request_firmware(&fw, fwname, state->i2c->dev.parent);
if (ret) {
- printk(KERN_WARNING "or51132: No firmware up"
- "loaded(timeout or file not found?)\n");
+ printk(KERN_WARNING "or51132: No firmware uploaded(timeout or file not found?)\n");
return ret;
}
ret = or51132_load_firmware(fe, fw);
release_firmware(fw);
if (ret) {
- printk(KERN_WARNING "or51132: Writing firmware to "
- "device failed!\n");
+ printk(KERN_WARNING "or51132: Writing firmware to device failed!\n");
return ret;
}
printk("or51132: Firmware upload complete.\n");
@@ -375,9 +358,9 @@ static int or51132_set_parameters(struct dvb_frontend *fe)
return 0;
}
-static int or51132_get_parameters(struct dvb_frontend* fe)
+static int or51132_get_parameters(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct or51132_state* state = fe->demodulator_priv;
int status;
int retry = 1;
@@ -415,7 +398,7 @@ start:
return 0;
}
-static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status)
+static int or51132_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct or51132_state* state = fe->demodulator_priv;
int reg;
@@ -499,8 +482,8 @@ start:
switch (reg&0xff) {
case 0x06:
if (reg & 0x1000) usK = 3 << 24;
- /* Fall through to QAM64 case */
- case 0x43:
+ fallthrough;
+ case 0x43: /* QAM64 */
c = 150204167;
break;
case 0x45:
@@ -561,7 +544,7 @@ static void or51132_release(struct dvb_frontend* fe)
kfree(state);
}
-static struct dvb_frontend_ops or51132_ops;
+static const struct dvb_frontend_ops or51132_ops;
struct dvb_frontend* or51132_attach(const struct or51132_config* config,
struct i2c_adapter* i2c)
@@ -585,13 +568,13 @@ struct dvb_frontend* or51132_attach(const struct or51132_config* config,
return &state->frontend;
}
-static struct dvb_frontend_ops or51132_ops = {
+static const struct dvb_frontend_ops or51132_ops = {
.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
.info = {
.name = "Oren OR51132 VSB/QAM Frontend",
- .frequency_min = 44000000,
- .frequency_max = 958000000,
- .frequency_stepsize = 166666,
+ .frequency_min_hz = 44 * MHz,
+ .frequency_max_hz = 958 * MHz,
+ .frequency_stepsize_hz = 166666,
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO |
@@ -622,10 +605,4 @@ MODULE_AUTHOR("Kirk Lapray");
MODULE_AUTHOR("Trent Piepho");
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(or51132_attach);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+EXPORT_SYMBOL_GPL(or51132_attach);
diff --git a/drivers/media/dvb-frontends/or51132.h b/drivers/media/dvb-frontends/or51132.h
new file mode 100644
index 000000000000..75592cd2c683
--- /dev/null
+++ b/drivers/media/dvb-frontends/or51132.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for OR51132 (pcHDTV HD-3000) - VSB/QAM
+ *
+ * Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
+*/
+
+#ifndef OR51132_H
+#define OR51132_H
+
+#include <linux/firmware.h>
+#include <linux/dvb/frontend.h>
+
+struct or51132_config
+{
+ /* The demodulator's i2c address */
+ u8 demod_address;
+
+ /* Need to set device param for start_dma */
+ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
+};
+
+#if IS_REACHABLE(CONFIG_DVB_OR51132)
+extern struct dvb_frontend* or51132_attach(const struct or51132_config* config,
+ struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* or51132_attach(const struct or51132_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_OR51132
+
+#endif // OR51132_H
diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c
new file mode 100644
index 000000000000..2e8e7071a67a
--- /dev/null
+++ b/drivers/media/dvb-frontends/or51211.c
@@ -0,0 +1,555 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for OR51211 (pcHDTV HD-2000) - VSB
+ *
+ * Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
+ *
+ * Based on code from Jack Kelliher (kelliher@xmission.com)
+ * Copyright (C) 2002 & pcHDTV, inc.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
+
+/*
+ * This driver needs external firmware. Please use the command
+ * "<kerneldir>/scripts/get_dvb_firmware or51211" to
+ * download/extract it, and then copy it to /usr/lib/hotplug/firmware
+ * or /lib/firmware (depending on configuration of firmware hotplug).
+ */
+#define OR51211_DEFAULT_FIRMWARE "dvb-fe-or51211.fw"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+
+#include <linux/int_log.h>
+#include <media/dvb_frontend.h>
+#include "or51211.h"
+
+static int debug;
+#define dprintk(args...) \
+ do { if (debug) pr_debug(args); } while (0)
+
+static u8 run_buf[] = {0x7f,0x01};
+static u8 cmd_buf[] = {0x04,0x01,0x50,0x80,0x06}; // ATSC
+
+struct or51211_state {
+
+ struct i2c_adapter* i2c;
+
+ /* Configuration settings */
+ const struct or51211_config* config;
+
+ struct dvb_frontend frontend;
+ struct bt878* bt;
+
+ /* Demodulator private data */
+ u8 initialized:1;
+ u32 snr; /* Result of last SNR calculation */
+
+ /* Tuner private data */
+ u32 current_frequency;
+};
+
+static int i2c_writebytes (struct or51211_state* state, u8 reg, const u8 *buf,
+ int len)
+{
+ int err;
+ struct i2c_msg msg;
+ msg.addr = reg;
+ msg.flags = 0;
+ msg.len = len;
+ msg.buf = (u8 *)buf;
+
+ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+ pr_warn("error (addr %02x, err == %i)\n", reg, err);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static int i2c_readbytes(struct or51211_state *state, u8 reg, u8 *buf, int len)
+{
+ int err;
+ struct i2c_msg msg;
+ msg.addr = reg;
+ msg.flags = I2C_M_RD;
+ msg.len = len;
+ msg.buf = buf;
+
+ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+ pr_warn("error (addr %02x, err == %i)\n", reg, err);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static int or51211_load_firmware (struct dvb_frontend* fe,
+ const struct firmware *fw)
+{
+ struct or51211_state* state = fe->demodulator_priv;
+ u8 tudata[585];
+ int i;
+
+ dprintk("Firmware is %zu bytes\n", fw->size);
+
+ /* Get eprom data */
+ tudata[0] = 17;
+ if (i2c_writebytes(state,0x50,tudata,1)) {
+ pr_warn("error eprom addr\n");
+ return -1;
+ }
+ if (i2c_readbytes(state,0x50,&tudata[145],192)) {
+ pr_warn("error eprom\n");
+ return -1;
+ }
+
+ /* Create firmware buffer */
+ for (i = 0; i < 145; i++)
+ tudata[i] = fw->data[i];
+
+ for (i = 0; i < 248; i++)
+ tudata[i+337] = fw->data[145+i];
+
+ state->config->reset(fe);
+
+ if (i2c_writebytes(state,state->config->demod_address,tudata,585)) {
+ pr_warn("error 1\n");
+ return -1;
+ }
+ msleep(1);
+
+ if (i2c_writebytes(state,state->config->demod_address,
+ &fw->data[393],8125)) {
+ pr_warn("error 2\n");
+ return -1;
+ }
+ msleep(1);
+
+ if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+ pr_warn("error 3\n");
+ return -1;
+ }
+
+ /* Wait at least 5 msec */
+ msleep(10);
+ if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+ pr_warn("error 4\n");
+ return -1;
+ }
+ msleep(10);
+
+ pr_info("Done.\n");
+ return 0;
+};
+
+static int or51211_setmode(struct dvb_frontend* fe, int mode)
+{
+ struct or51211_state* state = fe->demodulator_priv;
+ u8 rec_buf[14];
+
+ state->config->setmode(fe, mode);
+
+ if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+ pr_warn("error 1\n");
+ return -1;
+ }
+
+ /* Wait at least 5 msec */
+ msleep(10);
+ if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+ pr_warn("error 2\n");
+ return -1;
+ }
+
+ msleep(10);
+
+ /* Set operation mode in Receiver 1 register;
+ * type 1:
+ * data 0x50h Automatic sets receiver channel conditions
+ * Automatic NTSC rejection filter
+ * Enable MPEG serial data output
+ * MPEG2tr
+ * High tuner phase noise
+ * normal +/-150kHz Carrier acquisition range
+ */
+ if (i2c_writebytes(state,state->config->demod_address,cmd_buf,3)) {
+ pr_warn("error 3\n");
+ return -1;
+ }
+
+ rec_buf[0] = 0x04;
+ rec_buf[1] = 0x00;
+ rec_buf[2] = 0x03;
+ rec_buf[3] = 0x00;
+ msleep(20);
+ if (i2c_writebytes(state,state->config->demod_address,rec_buf,3)) {
+ pr_warn("error 5\n");
+ }
+ msleep(3);
+ if (i2c_readbytes(state,state->config->demod_address,&rec_buf[10],2)) {
+ pr_warn("error 6\n");
+ return -1;
+ }
+ dprintk("rec status %02x %02x\n", rec_buf[10], rec_buf[11]);
+
+ return 0;
+}
+
+static int or51211_set_parameters(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct or51211_state* state = fe->demodulator_priv;
+
+ /* Change only if we are actually changing the channel */
+ if (state->current_frequency != p->frequency) {
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ /* Set to ATSC mode */
+ or51211_setmode(fe,0);
+
+ /* Update current frequency */
+ state->current_frequency = p->frequency;
+ }
+ return 0;
+}
+
+static int or51211_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct or51211_state* state = fe->demodulator_priv;
+ unsigned char rec_buf[2];
+ unsigned char snd_buf[] = {0x04,0x00,0x03,0x00};
+ *status = 0;
+
+ /* Receiver Status */
+ if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
+ pr_warn("write error\n");
+ return -1;
+ }
+ msleep(3);
+ if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
+ pr_warn("read error\n");
+ return -1;
+ }
+ dprintk("%x %x\n", rec_buf[0], rec_buf[1]);
+
+ if (rec_buf[0] & 0x01) { /* Receiver Lock */
+ *status |= FE_HAS_SIGNAL;
+ *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_VITERBI;
+ *status |= FE_HAS_SYNC;
+ *status |= FE_HAS_LOCK;
+ }
+ return 0;
+}
+
+/* Calculate SNR estimation (scaled by 2^24)
+
+ 8-VSB SNR equation from Oren datasheets
+
+ For 8-VSB:
+ SNR[dB] = 10 * log10(219037.9454 / MSE^2 )
+
+ We re-write the snr equation as:
+ SNR * 2^24 = 10*(c - 2*intlog10(MSE))
+ Where for 8-VSB, c = log10(219037.9454) * 2^24 */
+
+static u32 calculate_snr(u32 mse, u32 c)
+{
+ if (mse == 0) /* No signal */
+ return 0;
+
+ mse = 2*intlog10(mse);
+ if (mse > c) {
+ /* Negative SNR, which is possible, but realisticly the
+ demod will lose lock before the signal gets this bad. The
+ API only allows for unsigned values, so just return 0 */
+ return 0;
+ }
+ return 10*(c - mse);
+}
+
+static int or51211_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct or51211_state* state = fe->demodulator_priv;
+ u8 rec_buf[2];
+ u8 snd_buf[3];
+
+ /* SNR after Equalizer */
+ snd_buf[0] = 0x04;
+ snd_buf[1] = 0x00;
+ snd_buf[2] = 0x04;
+
+ if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
+ pr_warn("error writing snr reg\n");
+ return -1;
+ }
+ if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
+ pr_warn("read_status read error\n");
+ return -1;
+ }
+
+ state->snr = calculate_snr(rec_buf[0], 89599047);
+ *snr = (state->snr) >> 16;
+
+ dprintk("noise = 0x%02x, snr = %d.%02d dB\n", rec_buf[0],
+ state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
+
+ return 0;
+}
+
+static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ /* Calculate Strength from SNR up to 35dB */
+ /* Even though the SNR can go higher than 35dB, there is some comfort */
+ /* factor in having a range of strong signals that can show at 100% */
+ struct or51211_state* state = (struct or51211_state*)fe->demodulator_priv;
+ u16 snr;
+ int ret;
+
+ ret = fe->ops.read_snr(fe, &snr);
+ if (ret != 0)
+ return ret;
+ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
+ /* scale the range 0 - 35*2^24 into 0 - 65535 */
+ if (state->snr >= 8960 * 0x10000)
+ *strength = 0xffff;
+ else
+ *strength = state->snr / 8960;
+
+ return 0;
+}
+
+static int or51211_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ *ber = -ENOSYS;
+ return 0;
+}
+
+static int or51211_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ *ucblocks = -ENOSYS;
+ return 0;
+}
+
+static int or51211_sleep(struct dvb_frontend* fe)
+{
+ return 0;
+}
+
+static int or51211_init(struct dvb_frontend* fe)
+{
+ struct or51211_state* state = fe->demodulator_priv;
+ const struct or51211_config* config = state->config;
+ const struct firmware* fw;
+ unsigned char get_ver_buf[] = {0x04,0x00,0x30,0x00,0x00};
+ unsigned char rec_buf[14];
+ int ret,i;
+
+ if (!state->initialized) {
+ /* Request the firmware, this will block until it uploads */
+ pr_info("Waiting for firmware upload (%s)...\n",
+ OR51211_DEFAULT_FIRMWARE);
+ ret = config->request_firmware(fe, &fw,
+ OR51211_DEFAULT_FIRMWARE);
+ pr_info("Got Hotplug firmware\n");
+ if (ret) {
+ pr_warn("No firmware uploaded (timeout or file not found?)\n");
+ return ret;
+ }
+
+ ret = or51211_load_firmware(fe, fw);
+ release_firmware(fw);
+ if (ret) {
+ pr_warn("Writing firmware to device failed!\n");
+ return ret;
+ }
+ pr_info("Firmware upload complete.\n");
+
+ /* Set operation mode in Receiver 1 register;
+ * type 1:
+ * data 0x50h Automatic sets receiver channel conditions
+ * Automatic NTSC rejection filter
+ * Enable MPEG serial data output
+ * MPEG2tr
+ * High tuner phase noise
+ * normal +/-150kHz Carrier acquisition range
+ */
+ if (i2c_writebytes(state,state->config->demod_address,
+ cmd_buf,3)) {
+ pr_warn("Load DVR Error 5\n");
+ return -1;
+ }
+
+ /* Read back ucode version to besure we loaded correctly */
+ /* and are really up and running */
+ rec_buf[0] = 0x04;
+ rec_buf[1] = 0x00;
+ rec_buf[2] = 0x03;
+ rec_buf[3] = 0x00;
+ msleep(30);
+ if (i2c_writebytes(state,state->config->demod_address,
+ rec_buf,3)) {
+ pr_warn("Load DVR Error A\n");
+ return -1;
+ }
+ msleep(3);
+ if (i2c_readbytes(state,state->config->demod_address,
+ &rec_buf[10],2)) {
+ pr_warn("Load DVR Error B\n");
+ return -1;
+ }
+
+ rec_buf[0] = 0x04;
+ rec_buf[1] = 0x00;
+ rec_buf[2] = 0x01;
+ rec_buf[3] = 0x00;
+ msleep(20);
+ if (i2c_writebytes(state,state->config->demod_address,
+ rec_buf,3)) {
+ pr_warn("Load DVR Error C\n");
+ return -1;
+ }
+ msleep(3);
+ if (i2c_readbytes(state,state->config->demod_address,
+ &rec_buf[12],2)) {
+ pr_warn("Load DVR Error D\n");
+ return -1;
+ }
+
+ for (i = 0; i < 8; i++)
+ rec_buf[i]=0xed;
+
+ for (i = 0; i < 5; i++) {
+ msleep(30);
+ get_ver_buf[4] = i+1;
+ if (i2c_writebytes(state,state->config->demod_address,
+ get_ver_buf,5)) {
+ pr_warn("Load DVR Error 6 - %d\n", i);
+ return -1;
+ }
+ msleep(3);
+
+ if (i2c_readbytes(state,state->config->demod_address,
+ &rec_buf[i*2],2)) {
+ pr_warn("Load DVR Error 7 - %d\n", i);
+ return -1;
+ }
+ /* If we didn't receive the right index, try again */
+ if ((int)rec_buf[i*2+1]!=i+1){
+ i--;
+ }
+ }
+ dprintk("read_fwbits %10ph\n", rec_buf);
+
+ pr_info("ver TU%02x%02x%02x VSB mode %02x Status %02x\n",
+ rec_buf[2], rec_buf[4], rec_buf[6], rec_buf[12],
+ rec_buf[10]);
+
+ rec_buf[0] = 0x04;
+ rec_buf[1] = 0x00;
+ rec_buf[2] = 0x03;
+ rec_buf[3] = 0x00;
+ msleep(20);
+ if (i2c_writebytes(state,state->config->demod_address,
+ rec_buf,3)) {
+ pr_warn("Load DVR Error 8\n");
+ return -1;
+ }
+ msleep(20);
+ if (i2c_readbytes(state,state->config->demod_address,
+ &rec_buf[8],2)) {
+ pr_warn("Load DVR Error 9\n");
+ return -1;
+ }
+ state->initialized = 1;
+ }
+
+ return 0;
+}
+
+static int or51211_get_tune_settings(struct dvb_frontend* fe,
+ struct dvb_frontend_tune_settings* fesettings)
+{
+ fesettings->min_delay_ms = 500;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+}
+
+static void or51211_release(struct dvb_frontend* fe)
+{
+ struct or51211_state* state = fe->demodulator_priv;
+ state->config->sleep(fe);
+ kfree(state);
+}
+
+static const struct dvb_frontend_ops or51211_ops;
+
+struct dvb_frontend* or51211_attach(const struct or51211_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct or51211_state* state = NULL;
+
+ /* Allocate memory for the internal state */
+ state = kzalloc(sizeof(struct or51211_state), GFP_KERNEL);
+ if (state == NULL)
+ return NULL;
+
+ /* Setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ state->initialized = 0;
+ state->current_frequency = 0;
+
+ /* Create dvb_frontend */
+ memcpy(&state->frontend.ops, &or51211_ops, sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+}
+
+static const struct dvb_frontend_ops or51211_ops = {
+ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
+ .info = {
+ .name = "Oren OR51211 VSB Frontend",
+ .frequency_min_hz = 44 * MHz,
+ .frequency_max_hz = 958 * MHz,
+ .frequency_stepsize_hz = 166666,
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_8VSB
+ },
+
+ .release = or51211_release,
+
+ .init = or51211_init,
+ .sleep = or51211_sleep,
+
+ .set_frontend = or51211_set_parameters,
+ .get_tune_settings = or51211_get_tune_settings,
+
+ .read_status = or51211_read_status,
+ .read_ber = or51211_read_ber,
+ .read_signal_strength = or51211_read_signal_strength,
+ .read_snr = or51211_read_snr,
+ .read_ucblocks = or51211_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Oren OR51211 VSB [pcHDTV HD-2000] Demodulator Driver");
+MODULE_AUTHOR("Kirk Lapray");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL_GPL(or51211_attach);
+
diff --git a/drivers/media/dvb-frontends/or51211.h b/drivers/media/dvb-frontends/or51211.h
new file mode 100644
index 000000000000..1d0763916972
--- /dev/null
+++ b/drivers/media/dvb-frontends/or51211.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for OR51211 (pcHDTV HD-2000) - VSB
+ *
+ * Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
+*/
+
+#ifndef OR51211_H
+#define OR51211_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct or51211_config
+{
+ /* The demodulator's i2c address */
+ u8 demod_address;
+
+ /* Request firmware for device */
+ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+ void (*setmode)(struct dvb_frontend * fe, int mode);
+ void (*reset)(struct dvb_frontend * fe);
+ void (*sleep)(struct dvb_frontend * fe);
+};
+
+#if IS_REACHABLE(CONFIG_DVB_OR51211)
+extern struct dvb_frontend* or51211_attach(const struct or51211_config* config,
+ struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* or51211_attach(const struct or51211_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_OR51211
+
+#endif // OR51211_H
+
diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c
new file mode 100644
index 000000000000..aa4ef9aedf17
--- /dev/null
+++ b/drivers/media/dvb-frontends/rtl2830.c
@@ -0,0 +1,898 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ */
+
+#include "rtl2830_priv.h"
+
+/* Our regmap is bypassing I2C adapter lock, thus we do it! */
+static int rtl2830_bulk_write(struct i2c_client *client, unsigned int reg,
+ const void *val, size_t val_count)
+{
+ struct rtl2830_dev *dev = i2c_get_clientdata(client);
+ int ret;
+
+ i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
+ ret = regmap_bulk_write(dev->regmap, reg, val, val_count);
+ i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
+ return ret;
+}
+
+static int rtl2830_update_bits(struct i2c_client *client, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ struct rtl2830_dev *dev = i2c_get_clientdata(client);
+ int ret;
+
+ i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
+ ret = regmap_update_bits(dev->regmap, reg, mask, val);
+ i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
+ return ret;
+}
+
+static int rtl2830_bulk_read(struct i2c_client *client, unsigned int reg,
+ void *val, size_t val_count)
+{
+ struct rtl2830_dev *dev = i2c_get_clientdata(client);
+ int ret;
+
+ i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
+ ret = regmap_bulk_read(dev->regmap, reg, val, val_count);
+ i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
+ return ret;
+}
+
+static int rtl2830_init(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct rtl2830_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
+ int ret, i;
+ struct rtl2830_reg_val_mask tab[] = {
+ {0x00d, 0x01, 0x03},
+ {0x00d, 0x10, 0x10},
+ {0x104, 0x00, 0x1e},
+ {0x105, 0x80, 0x80},
+ {0x110, 0x02, 0x03},
+ {0x110, 0x08, 0x0c},
+ {0x17b, 0x00, 0x40},
+ {0x17d, 0x05, 0x0f},
+ {0x17d, 0x50, 0xf0},
+ {0x18c, 0x08, 0x0f},
+ {0x18d, 0x00, 0xc0},
+ {0x188, 0x05, 0x0f},
+ {0x189, 0x00, 0xfc},
+ {0x2d5, 0x02, 0x02},
+ {0x2f1, 0x02, 0x06},
+ {0x2f1, 0x20, 0xf8},
+ {0x16d, 0x00, 0x01},
+ {0x1a6, 0x00, 0x80},
+ {0x106, dev->pdata->vtop, 0x3f},
+ {0x107, dev->pdata->krf, 0x3f},
+ {0x112, 0x28, 0xff},
+ {0x103, dev->pdata->agc_targ_val, 0xff},
+ {0x00a, 0x02, 0x07},
+ {0x140, 0x0c, 0x3c},
+ {0x140, 0x40, 0xc0},
+ {0x15b, 0x05, 0x07},
+ {0x15b, 0x28, 0x38},
+ {0x15c, 0x05, 0x07},
+ {0x15c, 0x28, 0x38},
+ {0x115, dev->pdata->spec_inv, 0x01},
+ {0x16f, 0x01, 0x07},
+ {0x170, 0x18, 0x38},
+ {0x172, 0x0f, 0x0f},
+ {0x173, 0x08, 0x38},
+ {0x175, 0x01, 0x07},
+ {0x176, 0x00, 0xc0},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = rtl2830_update_bits(client, tab[i].reg, tab[i].mask,
+ tab[i].val);
+ if (ret)
+ goto err;
+ }
+
+ ret = rtl2830_bulk_write(client, 0x18f, "\x28\x00", 2);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_bulk_write(client, 0x195,
+ "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8);
+ if (ret)
+ goto err;
+
+ /* TODO: spec init */
+
+ /* soft reset */
+ ret = rtl2830_update_bits(client, 0x101, 0x04, 0x04);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_update_bits(client, 0x101, 0x04, 0x00);
+ if (ret)
+ goto err;
+
+ /* init stats here in order signal app which stats are supported */
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->cnr.len = 1;
+ c->cnr.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;
+
+ dev->sleeping = false;
+
+ return ret;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2830_sleep(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct rtl2830_dev *dev = i2c_get_clientdata(client);
+
+ dev->sleeping = true;
+ dev->fe_status = 0;
+
+ return 0;
+}
+
+static int rtl2830_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 500;
+ s->step_size = fe->ops.info.frequency_stepsize_hz * 2;
+ s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1;
+
+ return 0;
+}
+
+static int rtl2830_set_frontend(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct rtl2830_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ u64 num;
+ u8 buf[3], u8tmp;
+ u32 if_ctl, if_frequency;
+ static const u8 bw_params1[3][34] = {
+ {
+ 0x1f, 0xf0, 0x1f, 0xf0, 0x1f, 0xfa, 0x00, 0x17, 0x00, 0x41,
+ 0x00, 0x64, 0x00, 0x67, 0x00, 0x38, 0x1f, 0xde, 0x1f, 0x7a,
+ 0x1f, 0x47, 0x1f, 0x7c, 0x00, 0x30, 0x01, 0x4b, 0x02, 0x82,
+ 0x03, 0x73, 0x03, 0xcf, /* 6 MHz */
+ }, {
+ 0x1f, 0xfa, 0x1f, 0xda, 0x1f, 0xc1, 0x1f, 0xb3, 0x1f, 0xca,
+ 0x00, 0x07, 0x00, 0x4d, 0x00, 0x6d, 0x00, 0x40, 0x1f, 0xca,
+ 0x1f, 0x4d, 0x1f, 0x2a, 0x1f, 0xb2, 0x00, 0xec, 0x02, 0x7e,
+ 0x03, 0xd0, 0x04, 0x53, /* 7 MHz */
+ }, {
+ 0x00, 0x10, 0x00, 0x0e, 0x1f, 0xf7, 0x1f, 0xc9, 0x1f, 0xa0,
+ 0x1f, 0xa6, 0x1f, 0xec, 0x00, 0x4e, 0x00, 0x7d, 0x00, 0x3a,
+ 0x1f, 0x98, 0x1f, 0x10, 0x1f, 0x40, 0x00, 0x75, 0x02, 0x5f,
+ 0x04, 0x24, 0x04, 0xdb, /* 8 MHz */
+ },
+ };
+ static const u8 bw_params2[3][6] = {
+ {0xc3, 0x0c, 0x44, 0x33, 0x33, 0x30}, /* 6 MHz */
+ {0xb8, 0xe3, 0x93, 0x99, 0x99, 0x98}, /* 7 MHz */
+ {0xae, 0xba, 0xf3, 0x26, 0x66, 0x64}, /* 8 MHz */
+ };
+
+ dev_dbg(&client->dev, "frequency=%u bandwidth_hz=%u inversion=%u\n",
+ c->frequency, c->bandwidth_hz, c->inversion);
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ i = 0;
+ break;
+ case 7000000:
+ i = 1;
+ break;
+ case 8000000:
+ i = 2;
+ break;
+ default:
+ dev_err(&client->dev, "invalid bandwidth_hz %u\n",
+ c->bandwidth_hz);
+ return -EINVAL;
+ }
+
+ ret = rtl2830_update_bits(client, 0x008, 0x06, i << 1);
+ if (ret)
+ goto err;
+
+ /* program if frequency */
+ if (fe->ops.tuner_ops.get_if_frequency)
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+ else
+ ret = -EINVAL;
+ if (ret)
+ goto err;
+
+ num = if_frequency % dev->pdata->clk;
+ num *= 0x400000;
+ num = div_u64(num, dev->pdata->clk);
+ num = -num;
+ if_ctl = num & 0x3fffff;
+ dev_dbg(&client->dev, "if_frequency=%d if_ctl=%08x\n",
+ if_frequency, if_ctl);
+
+ buf[0] = (if_ctl >> 16) & 0x3f;
+ buf[1] = (if_ctl >> 8) & 0xff;
+ buf[2] = (if_ctl >> 0) & 0xff;
+
+ ret = rtl2830_bulk_read(client, 0x119, &u8tmp, 1);
+ if (ret)
+ goto err;
+
+ buf[0] |= u8tmp & 0xc0; /* [7:6] */
+
+ ret = rtl2830_bulk_write(client, 0x119, buf, 3);
+ if (ret)
+ goto err;
+
+ /* 1/2 split I2C write */
+ ret = rtl2830_bulk_write(client, 0x11c, &bw_params1[i][0], 17);
+ if (ret)
+ goto err;
+
+ /* 2/2 split I2C write */
+ ret = rtl2830_bulk_write(client, 0x12d, &bw_params1[i][17], 17);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_bulk_write(client, 0x19d, bw_params2[i], 6);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2830_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct rtl2830_dev *dev = i2c_get_clientdata(client);
+ int ret;
+ u8 buf[3];
+
+ if (dev->sleeping)
+ return 0;
+
+ ret = rtl2830_bulk_read(client, 0x33c, buf, 2);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_bulk_read(client, 0x351, &buf[2], 1);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "TPS=%*ph\n", 3, buf);
+
+ switch ((buf[0] >> 2) & 3) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = QAM_16;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ }
+
+ switch ((buf[2] >> 2) & 1) {
+ case 0:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ }
+
+ switch ((buf[2] >> 0) & 3) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[0] >> 4) & 7) {
+ case 0:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[1] >> 3) & 7) {
+ case 0:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[1] >> 0) & 7) {
+ case 0:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2830_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct rtl2830_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
+ int ret, stmp;
+ unsigned int utmp;
+ u8 u8tmp, buf[2];
+
+ *status = 0;
+
+ if (dev->sleeping)
+ return 0;
+
+ ret = rtl2830_bulk_read(client, 0x351, &u8tmp, 1);
+ if (ret)
+ goto err;
+
+ u8tmp = (u8tmp >> 3) & 0x0f; /* [6:3] */
+ if (u8tmp == 11) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ } else if (u8tmp == 10) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+ }
+
+ dev->fe_status = *status;
+
+ /* Signal strength */
+ if (dev->fe_status & FE_HAS_SIGNAL) {
+ /* Read IF AGC */
+ ret = rtl2830_bulk_read(client, 0x359, buf, 2);
+ if (ret)
+ goto err;
+
+ stmp = buf[0] << 8 | buf[1] << 0;
+ stmp = sign_extend32(stmp, 13);
+ utmp = clamp_val(-4 * stmp + 32767, 0x0000, 0xffff);
+
+ dev_dbg(&client->dev, "IF AGC=%d\n", stmp);
+
+ c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ c->strength.stat[0].uvalue = utmp;
+ } else {
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* CNR */
+ if (dev->fe_status & FE_HAS_VITERBI) {
+ unsigned int hierarchy, constellation;
+ #define CONSTELLATION_NUM 3
+ #define HIERARCHY_NUM 4
+ static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
+ {70705899, 70705899, 70705899, 70705899},
+ {82433173, 82433173, 87483115, 94445660},
+ {92888734, 92888734, 95487525, 99770748},
+ };
+
+ ret = rtl2830_bulk_read(client, 0x33c, &u8tmp, 1);
+ if (ret)
+ goto err;
+
+ constellation = (u8tmp >> 2) & 0x03; /* [3:2] */
+ if (constellation > CONSTELLATION_NUM - 1)
+ goto err;
+
+ hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */
+ if (hierarchy > HIERARCHY_NUM - 1)
+ goto err;
+
+ ret = rtl2830_bulk_read(client, 0x40c, buf, 2);
+ if (ret)
+ goto err;
+
+ utmp = buf[0] << 8 | buf[1] << 0;
+ if (utmp)
+ stmp = (constant[constellation][hierarchy] -
+ intlog10(utmp)) / ((1 << 24) / 10000);
+ else
+ stmp = 0;
+
+ dev_dbg(&client->dev, "CNR raw=%u\n", utmp);
+
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = stmp;
+ } else {
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* BER */
+ if (dev->fe_status & FE_HAS_LOCK) {
+ ret = rtl2830_bulk_read(client, 0x34e, buf, 2);
+ if (ret)
+ goto err;
+
+ utmp = buf[0] << 8 | buf[1] << 0;
+ dev->post_bit_error += utmp;
+ dev->post_bit_count += 1000000;
+
+ dev_dbg(&client->dev, "BER errors=%u total=1000000\n", utmp);
+
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
+ } else {
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+
+ return ret;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL)
+ *snr = div_s64(c->cnr.stat[0].svalue, 100);
+ else
+ *snr = 0;
+
+ return 0;
+}
+
+static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct rtl2830_dev *dev = i2c_get_clientdata(client);
+
+ *ber = (dev->post_bit_error - dev->post_bit_error_prev);
+ dev->post_bit_error_prev = dev->post_bit_error;
+
+ return 0;
+}
+
+static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+
+ return 0;
+}
+
+static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (c->strength.stat[0].scale == FE_SCALE_RELATIVE)
+ *strength = c->strength.stat[0].uvalue;
+ else
+ *strength = 0;
+
+ return 0;
+}
+
+static const struct dvb_frontend_ops rtl2830_ops = {
+ .delsys = {SYS_DVBT},
+ .info = {
+ .name = "Realtek RTL2830 (DVB-T)",
+ .caps = FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .init = rtl2830_init,
+ .sleep = rtl2830_sleep,
+
+ .get_tune_settings = rtl2830_get_tune_settings,
+
+ .set_frontend = rtl2830_set_frontend,
+ .get_frontend = rtl2830_get_frontend,
+
+ .read_status = rtl2830_read_status,
+ .read_snr = rtl2830_read_snr,
+ .read_ber = rtl2830_read_ber,
+ .read_ucblocks = rtl2830_read_ucblocks,
+ .read_signal_strength = rtl2830_read_signal_strength,
+};
+
+static int rtl2830_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ int ret;
+ u8 u8tmp;
+
+ dev_dbg(&client->dev, "onoff=%d\n", onoff);
+
+ /* enable / disable PID filter */
+ if (onoff)
+ u8tmp = 0x80;
+ else
+ u8tmp = 0x00;
+
+ ret = rtl2830_update_bits(client, 0x061, 0x80, u8tmp);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2830_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, int onoff)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct rtl2830_dev *dev = i2c_get_clientdata(client);
+ int ret;
+ u8 buf[4];
+
+ dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d\n",
+ index, pid, onoff);
+
+ /* skip invalid PIDs (0x2000) */
+ if (pid > 0x1fff || index >= 32)
+ return 0;
+
+ if (onoff)
+ set_bit(index, &dev->filters);
+ else
+ clear_bit(index, &dev->filters);
+
+ /* enable / disable PIDs */
+ buf[0] = (dev->filters >> 0) & 0xff;
+ buf[1] = (dev->filters >> 8) & 0xff;
+ buf[2] = (dev->filters >> 16) & 0xff;
+ buf[3] = (dev->filters >> 24) & 0xff;
+ ret = rtl2830_bulk_write(client, 0x062, buf, 4);
+ if (ret)
+ goto err;
+
+ /* add PID */
+ buf[0] = (pid >> 8) & 0xff;
+ buf[1] = (pid >> 0) & 0xff;
+ ret = rtl2830_bulk_write(client, 0x066 + 2 * index, buf, 2);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+/*
+ * I2C gate/mux/repeater logic
+ * We must use unlocked __i2c_transfer() here (through regmap) because of I2C
+ * adapter lock is already taken by tuner driver.
+ * Gate is closed automatically after single I2C transfer.
+ */
+static int rtl2830_select(struct i2c_mux_core *muxc, u32 chan_id)
+{
+ struct i2c_client *client = i2c_mux_priv(muxc);
+ struct rtl2830_dev *dev = i2c_get_clientdata(client);
+ int ret;
+
+ dev_dbg(&client->dev, "\n");
+
+ /* open I2C repeater for 1 transfer, closes automatically */
+ /* XXX: regmap_update_bits() does not lock I2C adapter */
+ ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x08);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static struct dvb_frontend *rtl2830_get_dvb_frontend(struct i2c_client *client)
+{
+ struct rtl2830_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ return &dev->fe;
+}
+
+static struct i2c_adapter *rtl2830_get_i2c_adapter(struct i2c_client *client)
+{
+ struct rtl2830_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ return dev->muxc->adapter[0];
+}
+
+/*
+ * We implement own I2C access routines for regmap in order to get manual access
+ * to I2C adapter lock, which is needed for I2C mux adapter.
+ */
+static int rtl2830_regmap_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf, size_t val_size)
+{
+ struct i2c_client *client = context;
+ int ret;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = reg_size,
+ .buf = (u8 *)reg_buf,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = val_size,
+ .buf = val_buf,
+ }
+ };
+
+ ret = __i2c_transfer(client->adapter, msg, 2);
+ if (ret != 2) {
+ dev_warn(&client->dev, "i2c reg read failed %d\n", ret);
+ if (ret >= 0)
+ ret = -EREMOTEIO;
+ return ret;
+ }
+ return 0;
+}
+
+static int rtl2830_regmap_write(void *context, const void *data, size_t count)
+{
+ struct i2c_client *client = context;
+ int ret;
+ struct i2c_msg msg[1] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = count,
+ .buf = (u8 *)data,
+ }
+ };
+
+ ret = __i2c_transfer(client->adapter, msg, 1);
+ if (ret != 1) {
+ dev_warn(&client->dev, "i2c reg write failed %d\n", ret);
+ if (ret >= 0)
+ ret = -EREMOTEIO;
+ return ret;
+ }
+ return 0;
+}
+
+static int rtl2830_regmap_gather_write(void *context, const void *reg,
+ size_t reg_len, const void *val,
+ size_t val_len)
+{
+ struct i2c_client *client = context;
+ int ret;
+ u8 buf[256];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1 + val_len,
+ .buf = buf,
+ }
+ };
+
+ buf[0] = *(u8 const *)reg;
+ memcpy(&buf[1], val, val_len);
+
+ ret = __i2c_transfer(client->adapter, msg, 1);
+ if (ret != 1) {
+ dev_warn(&client->dev, "i2c reg write failed %d\n", ret);
+ if (ret >= 0)
+ ret = -EREMOTEIO;
+ return ret;
+ }
+ return 0;
+}
+
+static int rtl2830_probe(struct i2c_client *client)
+{
+ struct rtl2830_platform_data *pdata = client->dev.platform_data;
+ struct rtl2830_dev *dev;
+ int ret;
+ u8 u8tmp;
+ static const struct regmap_bus regmap_bus = {
+ .read = rtl2830_regmap_read,
+ .write = rtl2830_regmap_write,
+ .gather_write = rtl2830_regmap_gather_write,
+ .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
+ };
+ static const struct regmap_range_cfg regmap_range_cfg[] = {
+ {
+ .selector_reg = 0x00,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 0x100,
+ .range_min = 0 * 0x100,
+ .range_max = 5 * 0x100,
+ },
+ };
+ static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 5 * 0x100,
+ .ranges = regmap_range_cfg,
+ .num_ranges = ARRAY_SIZE(regmap_range_cfg),
+ };
+
+ dev_dbg(&client->dev, "\n");
+
+ if (pdata == NULL) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* allocate memory for the internal state */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* setup the state */
+ i2c_set_clientdata(client, dev);
+ dev->client = client;
+ dev->pdata = client->dev.platform_data;
+ dev->sleeping = true;
+ dev->regmap = regmap_init(&client->dev, &regmap_bus, client,
+ &regmap_config);
+ if (IS_ERR(dev->regmap)) {
+ ret = PTR_ERR(dev->regmap);
+ goto err_kfree;
+ }
+
+ /* check if the demod is there */
+ ret = rtl2830_bulk_read(client, 0x000, &u8tmp, 1);
+ if (ret)
+ goto err_regmap_exit;
+
+ /* create muxed i2c adapter for tuner */
+ dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0,
+ rtl2830_select, NULL);
+ if (!dev->muxc) {
+ ret = -ENOMEM;
+ goto err_regmap_exit;
+ }
+ dev->muxc->priv = client;
+ ret = i2c_mux_add_adapter(dev->muxc, 0, 0);
+ if (ret)
+ goto err_regmap_exit;
+
+ /* create dvb frontend */
+ memcpy(&dev->fe.ops, &rtl2830_ops, sizeof(dev->fe.ops));
+ dev->fe.demodulator_priv = client;
+
+ /* setup callbacks */
+ pdata->get_dvb_frontend = rtl2830_get_dvb_frontend;
+ pdata->get_i2c_adapter = rtl2830_get_i2c_adapter;
+ pdata->pid_filter = rtl2830_pid_filter;
+ pdata->pid_filter_ctrl = rtl2830_pid_filter_ctrl;
+
+ dev_info(&client->dev, "Realtek RTL2830 successfully attached\n");
+
+ return 0;
+err_regmap_exit:
+ regmap_exit(dev->regmap);
+err_kfree:
+ kfree(dev);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static void rtl2830_remove(struct i2c_client *client)
+{
+ struct rtl2830_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ i2c_mux_del_adapters(dev->muxc);
+ regmap_exit(dev->regmap);
+ kfree(dev);
+}
+
+static const struct i2c_device_id rtl2830_id_table[] = {
+ { "rtl2830" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rtl2830_id_table);
+
+static struct i2c_driver rtl2830_driver = {
+ .driver = {
+ .name = "rtl2830",
+ .suppress_bind_attrs = true,
+ },
+ .probe = rtl2830_probe,
+ .remove = rtl2830_remove,
+ .id_table = rtl2830_id_table,
+};
+
+module_i2c_driver(rtl2830_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Realtek RTL2830 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/rtl2830.h b/drivers/media/dvb-frontends/rtl2830.h
new file mode 100644
index 000000000000..fb5522025c2c
--- /dev/null
+++ b/drivers/media/dvb-frontends/rtl2830.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ */
+
+#ifndef RTL2830_H
+#define RTL2830_H
+
+#include <linux/dvb/frontend.h>
+
+/**
+ * struct rtl2830_platform_data - Platform data for the rtl2830 driver
+ * @clk: Clock frequency (4000000, 16000000, 25000000, 28800000).
+ * @spec_inv: Spectrum inversion.
+ * @vtop: AGC take-over point.
+ * @krf: AGC ratio.
+ * @agc_targ_val: AGC.
+ * @get_dvb_frontend: Get DVB frontend.
+ * @get_i2c_adapter: Get I2C adapter.
+ * @pid_filter: Set PID to PID filter.
+ * @pid_filter_ctrl: Control PID filter.
+ */
+struct rtl2830_platform_data {
+ u32 clk;
+ bool spec_inv;
+ u8 vtop;
+ u8 krf;
+ u8 agc_targ_val;
+
+ struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
+ struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *);
+ int (*pid_filter)(struct dvb_frontend *, u8, u16, int);
+ int (*pid_filter_ctrl)(struct dvb_frontend *, int);
+};
+
+#endif /* RTL2830_H */
diff --git a/drivers/media/dvb-frontends/rtl2830_priv.h b/drivers/media/dvb-frontends/rtl2830_priv.h
new file mode 100644
index 000000000000..ae1fc24a4d84
--- /dev/null
+++ b/drivers/media/dvb-frontends/rtl2830_priv.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ */
+
+#ifndef RTL2830_PRIV_H
+#define RTL2830_PRIV_H
+
+#include <media/dvb_frontend.h>
+#include <linux/int_log.h>
+#include "rtl2830.h"
+#include <linux/i2c-mux.h>
+#include <linux/math64.h>
+#include <linux/regmap.h>
+#include <linux/bitops.h>
+
+struct rtl2830_dev {
+ struct rtl2830_platform_data *pdata;
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct i2c_mux_core *muxc;
+ struct dvb_frontend fe;
+ bool sleeping;
+ unsigned long filters;
+ enum fe_status fe_status;
+ u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */
+ u64 post_bit_error;
+ u64 post_bit_count;
+};
+
+struct rtl2830_reg_val_mask {
+ u16 reg;
+ u8 val;
+ u8 mask;
+};
+
+#endif /* RTL2830_PRIV_H */
diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c
new file mode 100644
index 000000000000..3b4e46dac1bf
--- /dev/null
+++ b/drivers/media/dvb-frontends/rtl2832.c
@@ -0,0 +1,1148 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Realtek RTL2832 DVB-T demodulator driver
+ *
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com>
+ * Copyright (C) 2012-2014 Antti Palosaari <crope@iki.fi>
+ */
+
+#include "rtl2832_priv.h"
+
+#define REG_MASK(b) (BIT(b + 1) - 1)
+
+static const struct rtl2832_reg_entry registers[] = {
+ [DVBT_SOFT_RST] = {0x101, 2, 2},
+ [DVBT_IIC_REPEAT] = {0x101, 3, 3},
+ [DVBT_TR_WAIT_MIN_8K] = {0x188, 11, 2},
+ [DVBT_RSD_BER_FAIL_VAL] = {0x18f, 15, 0},
+ [DVBT_EN_BK_TRK] = {0x1a6, 7, 7},
+ [DVBT_AD_EN_REG] = {0x008, 7, 7},
+ [DVBT_AD_EN_REG1] = {0x008, 6, 6},
+ [DVBT_EN_BBIN] = {0x1b1, 0, 0},
+ [DVBT_MGD_THD0] = {0x195, 7, 0},
+ [DVBT_MGD_THD1] = {0x196, 7, 0},
+ [DVBT_MGD_THD2] = {0x197, 7, 0},
+ [DVBT_MGD_THD3] = {0x198, 7, 0},
+ [DVBT_MGD_THD4] = {0x199, 7, 0},
+ [DVBT_MGD_THD5] = {0x19a, 7, 0},
+ [DVBT_MGD_THD6] = {0x19b, 7, 0},
+ [DVBT_MGD_THD7] = {0x19c, 7, 0},
+ [DVBT_EN_CACQ_NOTCH] = {0x161, 4, 4},
+ [DVBT_AD_AV_REF] = {0x009, 6, 0},
+ [DVBT_REG_PI] = {0x00a, 2, 0},
+ [DVBT_PIP_ON] = {0x021, 3, 3},
+ [DVBT_SCALE1_B92] = {0x292, 7, 0},
+ [DVBT_SCALE1_B93] = {0x293, 7, 0},
+ [DVBT_SCALE1_BA7] = {0x2a7, 7, 0},
+ [DVBT_SCALE1_BA9] = {0x2a9, 7, 0},
+ [DVBT_SCALE1_BAA] = {0x2aa, 7, 0},
+ [DVBT_SCALE1_BAB] = {0x2ab, 7, 0},
+ [DVBT_SCALE1_BAC] = {0x2ac, 7, 0},
+ [DVBT_SCALE1_BB0] = {0x2b0, 7, 0},
+ [DVBT_SCALE1_BB1] = {0x2b1, 7, 0},
+ [DVBT_KB_P1] = {0x164, 3, 1},
+ [DVBT_KB_P2] = {0x164, 6, 4},
+ [DVBT_KB_P3] = {0x165, 2, 0},
+ [DVBT_OPT_ADC_IQ] = {0x006, 5, 4},
+ [DVBT_AD_AVI] = {0x009, 1, 0},
+ [DVBT_AD_AVQ] = {0x009, 3, 2},
+ [DVBT_K1_CR_STEP12] = {0x2ad, 9, 4},
+ [DVBT_TRK_KS_P2] = {0x16f, 2, 0},
+ [DVBT_TRK_KS_I2] = {0x170, 5, 3},
+ [DVBT_TR_THD_SET2] = {0x172, 3, 0},
+ [DVBT_TRK_KC_P2] = {0x173, 5, 3},
+ [DVBT_TRK_KC_I2] = {0x175, 2, 0},
+ [DVBT_CR_THD_SET2] = {0x176, 7, 6},
+ [DVBT_PSET_IFFREQ] = {0x119, 21, 0},
+ [DVBT_SPEC_INV] = {0x115, 0, 0},
+ [DVBT_RSAMP_RATIO] = {0x19f, 27, 2},
+ [DVBT_CFREQ_OFF_RATIO] = {0x19d, 23, 4},
+ [DVBT_FSM_STAGE] = {0x351, 6, 3},
+ [DVBT_RX_CONSTEL] = {0x33c, 3, 2},
+ [DVBT_RX_HIER] = {0x33c, 6, 4},
+ [DVBT_RX_C_RATE_LP] = {0x33d, 2, 0},
+ [DVBT_RX_C_RATE_HP] = {0x33d, 5, 3},
+ [DVBT_GI_IDX] = {0x351, 1, 0},
+ [DVBT_FFT_MODE_IDX] = {0x351, 2, 2},
+ [DVBT_RSD_BER_EST] = {0x34e, 15, 0},
+ [DVBT_CE_EST_EVM] = {0x40c, 15, 0},
+ [DVBT_RF_AGC_VAL] = {0x35b, 13, 0},
+ [DVBT_IF_AGC_VAL] = {0x359, 13, 0},
+ [DVBT_DAGC_VAL] = {0x305, 7, 0},
+ [DVBT_SFREQ_OFF] = {0x318, 13, 0},
+ [DVBT_CFREQ_OFF] = {0x35f, 17, 0},
+ [DVBT_POLAR_RF_AGC] = {0x00e, 1, 1},
+ [DVBT_POLAR_IF_AGC] = {0x00e, 0, 0},
+ [DVBT_AAGC_HOLD] = {0x104, 5, 5},
+ [DVBT_EN_RF_AGC] = {0x104, 6, 6},
+ [DVBT_EN_IF_AGC] = {0x104, 7, 7},
+ [DVBT_IF_AGC_MIN] = {0x108, 7, 0},
+ [DVBT_IF_AGC_MAX] = {0x109, 7, 0},
+ [DVBT_RF_AGC_MIN] = {0x10a, 7, 0},
+ [DVBT_RF_AGC_MAX] = {0x10b, 7, 0},
+ [DVBT_IF_AGC_MAN] = {0x10c, 6, 6},
+ [DVBT_IF_AGC_MAN_VAL] = {0x10c, 13, 0},
+ [DVBT_RF_AGC_MAN] = {0x10e, 6, 6},
+ [DVBT_RF_AGC_MAN_VAL] = {0x10e, 13, 0},
+ [DVBT_DAGC_TRG_VAL] = {0x112, 7, 0},
+ [DVBT_AGC_TARG_VAL_0] = {0x102, 0, 0},
+ [DVBT_AGC_TARG_VAL_8_1] = {0x103, 7, 0},
+ [DVBT_AAGC_LOOP_GAIN] = {0x1c7, 5, 1},
+ [DVBT_LOOP_GAIN2_3_0] = {0x104, 4, 1},
+ [DVBT_LOOP_GAIN2_4] = {0x105, 7, 7},
+ [DVBT_LOOP_GAIN3] = {0x1c8, 4, 0},
+ [DVBT_VTOP1] = {0x106, 5, 0},
+ [DVBT_VTOP2] = {0x1c9, 5, 0},
+ [DVBT_VTOP3] = {0x1ca, 5, 0},
+ [DVBT_KRF1] = {0x1cb, 7, 0},
+ [DVBT_KRF2] = {0x107, 7, 0},
+ [DVBT_KRF3] = {0x1cd, 7, 0},
+ [DVBT_KRF4] = {0x1ce, 7, 0},
+ [DVBT_EN_GI_PGA] = {0x1e5, 0, 0},
+ [DVBT_THD_LOCK_UP] = {0x1d9, 8, 0},
+ [DVBT_THD_LOCK_DW] = {0x1db, 8, 0},
+ [DVBT_THD_UP1] = {0x1dd, 7, 0},
+ [DVBT_THD_DW1] = {0x1de, 7, 0},
+ [DVBT_INTER_CNT_LEN] = {0x1d8, 3, 0},
+ [DVBT_GI_PGA_STATE] = {0x1e6, 3, 3},
+ [DVBT_EN_AGC_PGA] = {0x1d7, 0, 0},
+ [DVBT_CKOUTPAR] = {0x17b, 5, 5},
+ [DVBT_CKOUT_PWR] = {0x17b, 6, 6},
+ [DVBT_SYNC_DUR] = {0x17b, 7, 7},
+ [DVBT_ERR_DUR] = {0x17c, 0, 0},
+ [DVBT_SYNC_LVL] = {0x17c, 1, 1},
+ [DVBT_ERR_LVL] = {0x17c, 2, 2},
+ [DVBT_VAL_LVL] = {0x17c, 3, 3},
+ [DVBT_SERIAL] = {0x17c, 4, 4},
+ [DVBT_SER_LSB] = {0x17c, 5, 5},
+ [DVBT_CDIV_PH0] = {0x17d, 3, 0},
+ [DVBT_CDIV_PH1] = {0x17d, 7, 4},
+ [DVBT_MPEG_IO_OPT_2_2] = {0x006, 7, 7},
+ [DVBT_MPEG_IO_OPT_1_0] = {0x007, 7, 6},
+ [DVBT_CKOUTPAR_PIP] = {0x0b7, 4, 4},
+ [DVBT_CKOUT_PWR_PIP] = {0x0b7, 3, 3},
+ [DVBT_SYNC_LVL_PIP] = {0x0b7, 2, 2},
+ [DVBT_ERR_LVL_PIP] = {0x0b7, 1, 1},
+ [DVBT_VAL_LVL_PIP] = {0x0b7, 0, 0},
+ [DVBT_CKOUTPAR_PID] = {0x0b9, 4, 4},
+ [DVBT_CKOUT_PWR_PID] = {0x0b9, 3, 3},
+ [DVBT_SYNC_LVL_PID] = {0x0b9, 2, 2},
+ [DVBT_ERR_LVL_PID] = {0x0b9, 1, 1},
+ [DVBT_VAL_LVL_PID] = {0x0b9, 0, 0},
+ [DVBT_SM_PASS] = {0x193, 11, 0},
+ [DVBT_AD7_SETTING] = {0x011, 15, 0},
+ [DVBT_RSSI_R] = {0x301, 6, 0},
+ [DVBT_ACI_DET_IND] = {0x312, 0, 0},
+ [DVBT_REG_MON] = {0x00d, 1, 0},
+ [DVBT_REG_MONSEL] = {0x00d, 2, 2},
+ [DVBT_REG_GPE] = {0x00d, 7, 7},
+ [DVBT_REG_GPO] = {0x010, 0, 0},
+ [DVBT_REG_4MSEL] = {0x013, 0, 0},
+};
+
+static int rtl2832_rd_demod_reg(struct rtl2832_dev *dev, int reg, u32 *val)
+{
+ struct i2c_client *client = dev->client;
+ int ret, i;
+ u16 reg_start_addr;
+ u8 msb, lsb, reading[4], len;
+ u32 reading_tmp, mask;
+
+ reg_start_addr = registers[reg].start_address;
+ msb = registers[reg].msb;
+ lsb = registers[reg].lsb;
+ len = (msb >> 3) + 1;
+ mask = REG_MASK(msb - lsb);
+
+ ret = regmap_bulk_read(dev->regmap, reg_start_addr, reading, len);
+ if (ret)
+ goto err;
+
+ reading_tmp = 0;
+ for (i = 0; i < len; i++)
+ reading_tmp |= reading[i] << ((len - 1 - i) * 8);
+
+ *val = (reading_tmp >> lsb) & mask;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2832_wr_demod_reg(struct rtl2832_dev *dev, int reg, u32 val)
+{
+ struct i2c_client *client = dev->client;
+ int ret, i;
+ u16 reg_start_addr;
+ u8 msb, lsb, reading[4], writing[4], len;
+ u32 reading_tmp, writing_tmp, mask;
+
+ reg_start_addr = registers[reg].start_address;
+ msb = registers[reg].msb;
+ lsb = registers[reg].lsb;
+ len = (msb >> 3) + 1;
+ mask = REG_MASK(msb - lsb);
+
+ ret = regmap_bulk_read(dev->regmap, reg_start_addr, reading, len);
+ if (ret)
+ goto err;
+
+ reading_tmp = 0;
+ for (i = 0; i < len; i++)
+ reading_tmp |= reading[i] << ((len - 1 - i) * 8);
+
+ writing_tmp = reading_tmp & ~(mask << lsb);
+ writing_tmp |= ((val & mask) << lsb);
+
+ for (i = 0; i < len; i++)
+ writing[i] = (writing_tmp >> ((len - 1 - i) * 8)) & 0xff;
+
+ ret = regmap_bulk_write(dev->regmap, reg_start_addr, writing, len);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2832_set_if(struct dvb_frontend *fe, u32 if_freq)
+{
+ struct rtl2832_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+ u64 pset_iffreq;
+ u8 en_bbin = (if_freq == 0 ? 0x1 : 0x0);
+
+ /*
+ * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22)
+ * / CrystalFreqHz)
+ */
+ pset_iffreq = if_freq % dev->pdata->clk;
+ pset_iffreq *= 0x400000;
+ pset_iffreq = div_u64(pset_iffreq, dev->pdata->clk);
+ pset_iffreq = -pset_iffreq;
+ pset_iffreq = pset_iffreq & 0x3fffff;
+ dev_dbg(&client->dev, "if_frequency=%d pset_iffreq=%08x\n",
+ if_freq, (unsigned)pset_iffreq);
+
+ ret = rtl2832_wr_demod_reg(dev, DVBT_EN_BBIN, en_bbin);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_wr_demod_reg(dev, DVBT_PSET_IFFREQ, pset_iffreq);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2832_init(struct dvb_frontend *fe)
+{
+ struct rtl2832_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
+ const struct rtl2832_reg_value *init;
+ int i, ret, len;
+ /* initialization values for the demodulator registers */
+ struct rtl2832_reg_value rtl2832_initial_regs[] = {
+ {DVBT_AD_EN_REG, 0x1},
+ {DVBT_AD_EN_REG1, 0x1},
+ {DVBT_RSD_BER_FAIL_VAL, 0x2800},
+ {DVBT_MGD_THD0, 0x10},
+ {DVBT_MGD_THD1, 0x20},
+ {DVBT_MGD_THD2, 0x20},
+ {DVBT_MGD_THD3, 0x40},
+ {DVBT_MGD_THD4, 0x22},
+ {DVBT_MGD_THD5, 0x32},
+ {DVBT_MGD_THD6, 0x37},
+ {DVBT_MGD_THD7, 0x39},
+ {DVBT_EN_BK_TRK, 0x0},
+ {DVBT_EN_CACQ_NOTCH, 0x0},
+ {DVBT_AD_AV_REF, 0x2a},
+ {DVBT_REG_PI, 0x6},
+ {DVBT_PIP_ON, 0x0},
+ {DVBT_CDIV_PH0, 0x8},
+ {DVBT_CDIV_PH1, 0x8},
+ {DVBT_SCALE1_B92, 0x4},
+ {DVBT_SCALE1_B93, 0xb0},
+ {DVBT_SCALE1_BA7, 0x78},
+ {DVBT_SCALE1_BA9, 0x28},
+ {DVBT_SCALE1_BAA, 0x59},
+ {DVBT_SCALE1_BAB, 0x83},
+ {DVBT_SCALE1_BAC, 0xd4},
+ {DVBT_SCALE1_BB0, 0x65},
+ {DVBT_SCALE1_BB1, 0x43},
+ {DVBT_KB_P1, 0x1},
+ {DVBT_KB_P2, 0x4},
+ {DVBT_KB_P3, 0x7},
+ {DVBT_K1_CR_STEP12, 0xa},
+ {DVBT_REG_GPE, 0x1},
+ {DVBT_SERIAL, 0x0},
+ {DVBT_CDIV_PH0, 0x9},
+ {DVBT_CDIV_PH1, 0x9},
+ {DVBT_MPEG_IO_OPT_2_2, 0x0},
+ {DVBT_MPEG_IO_OPT_1_0, 0x0},
+ {DVBT_TRK_KS_P2, 0x4},
+ {DVBT_TRK_KS_I2, 0x7},
+ {DVBT_TR_THD_SET2, 0x6},
+ {DVBT_TRK_KC_I2, 0x5},
+ {DVBT_CR_THD_SET2, 0x1},
+ };
+
+ dev_dbg(&client->dev, "\n");
+
+ ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) {
+ ret = rtl2832_wr_demod_reg(dev, rtl2832_initial_regs[i].reg,
+ rtl2832_initial_regs[i].value);
+ if (ret)
+ goto err;
+ }
+
+ /* load tuner specific settings */
+ dev_dbg(&client->dev, "load settings for tuner=%02x\n",
+ dev->pdata->tuner);
+ switch (dev->pdata->tuner) {
+ case RTL2832_TUNER_FC2580:
+ len = ARRAY_SIZE(rtl2832_tuner_init_fc2580);
+ init = rtl2832_tuner_init_fc2580;
+ break;
+ case RTL2832_TUNER_FC0012:
+ case RTL2832_TUNER_FC0013:
+ len = ARRAY_SIZE(rtl2832_tuner_init_fc0012);
+ init = rtl2832_tuner_init_fc0012;
+ break;
+ case RTL2832_TUNER_TUA9001:
+ len = ARRAY_SIZE(rtl2832_tuner_init_tua9001);
+ init = rtl2832_tuner_init_tua9001;
+ break;
+ case RTL2832_TUNER_E4000:
+ len = ARRAY_SIZE(rtl2832_tuner_init_e4000);
+ init = rtl2832_tuner_init_e4000;
+ break;
+ case RTL2832_TUNER_R820T:
+ case RTL2832_TUNER_R828D:
+ len = ARRAY_SIZE(rtl2832_tuner_init_r820t);
+ init = rtl2832_tuner_init_r820t;
+ break;
+ case RTL2832_TUNER_SI2157:
+ len = ARRAY_SIZE(rtl2832_tuner_init_si2157);
+ init = rtl2832_tuner_init_si2157;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = rtl2832_wr_demod_reg(dev, init[i].reg, init[i].value);
+ if (ret)
+ goto err;
+ }
+
+ /* init stats here in order signal app which stats are supported */
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->cnr.len = 1;
+ c->cnr.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;
+ dev->sleeping = false;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2832_sleep(struct dvb_frontend *fe)
+{
+ struct rtl2832_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+
+ dev_dbg(&client->dev, "\n");
+
+ dev->sleeping = true;
+ dev->fe_status = 0;
+
+ ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2832_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ struct rtl2832_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+
+ dev_dbg(&client->dev, "\n");
+ s->min_delay_ms = 1000;
+ s->step_size = fe->ops.info.frequency_stepsize_hz * 2;
+ s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1;
+ return 0;
+}
+
+static int rtl2832_set_frontend(struct dvb_frontend *fe)
+{
+ struct rtl2832_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, j;
+ u64 bw_mode, num, num2;
+ u32 resamp_ratio, cfreq_off_ratio;
+ static u8 bw_params[3][32] = {
+ /* 6 MHz bandwidth */
+ {
+ 0xf5, 0xff, 0x15, 0x38, 0x5d, 0x6d, 0x52, 0x07, 0xfa, 0x2f,
+ 0x53, 0xf5, 0x3f, 0xca, 0x0b, 0x91, 0xea, 0x30, 0x63, 0xb2,
+ 0x13, 0xda, 0x0b, 0xc4, 0x18, 0x7e, 0x16, 0x66, 0x08, 0x67,
+ 0x19, 0xe0,
+ },
+
+ /* 7 MHz bandwidth */
+ {
+ 0xe7, 0xcc, 0xb5, 0xba, 0xe8, 0x2f, 0x67, 0x61, 0x00, 0xaf,
+ 0x86, 0xf2, 0xbf, 0x59, 0x04, 0x11, 0xb6, 0x33, 0xa4, 0x30,
+ 0x15, 0x10, 0x0a, 0x42, 0x18, 0xf8, 0x17, 0xd9, 0x07, 0x22,
+ 0x19, 0x10,
+ },
+
+ /* 8 MHz bandwidth */
+ {
+ 0x09, 0xf6, 0xd2, 0xa7, 0x9a, 0xc9, 0x27, 0x77, 0x06, 0xbf,
+ 0xec, 0xf4, 0x4f, 0x0b, 0xfc, 0x01, 0x63, 0x35, 0x54, 0xa7,
+ 0x16, 0x66, 0x08, 0xb4, 0x19, 0x6e, 0x19, 0x65, 0x05, 0xc8,
+ 0x19, 0xe0,
+ },
+ };
+
+ dev_dbg(&client->dev, "frequency=%u bandwidth_hz=%u inversion=%u\n",
+ c->frequency, c->bandwidth_hz, c->inversion);
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ /* If the frontend has get_if_frequency(), use it */
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ u32 if_freq;
+
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_set_if(fe, if_freq);
+ if (ret)
+ goto err;
+ }
+
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ i = 0;
+ bw_mode = 48000000;
+ break;
+ case 7000000:
+ i = 1;
+ bw_mode = 56000000;
+ break;
+ case 8000000:
+ i = 2;
+ bw_mode = 64000000;
+ break;
+ default:
+ dev_err(&client->dev, "invalid bandwidth_hz %u\n",
+ c->bandwidth_hz);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (j = 0; j < sizeof(bw_params[0]); j++) {
+ ret = regmap_bulk_write(dev->regmap,
+ 0x11c + j, &bw_params[i][j], 1);
+ if (ret)
+ goto err;
+ }
+
+ /* calculate and set resample ratio
+ * RSAMP_RATIO = floor(CrystalFreqHz * 7 * pow(2, 22)
+ * / ConstWithBandwidthMode)
+ */
+ num = dev->pdata->clk * 7ULL;
+ num *= 0x400000;
+ num = div_u64(num, bw_mode);
+ resamp_ratio = num & 0x3ffffff;
+ ret = rtl2832_wr_demod_reg(dev, DVBT_RSAMP_RATIO, resamp_ratio);
+ if (ret)
+ goto err;
+
+ /* calculate and set cfreq off ratio
+ * CFREQ_OFF_RATIO = - floor(ConstWithBandwidthMode * pow(2, 20)
+ * / (CrystalFreqHz * 7))
+ */
+ num = bw_mode << 20;
+ num2 = dev->pdata->clk * 7ULL;
+ num = div_u64(num, num2);
+ num = -num;
+ cfreq_off_ratio = num & 0xfffff;
+ ret = rtl2832_wr_demod_reg(dev, DVBT_CFREQ_OFF_RATIO, cfreq_off_ratio);
+ if (ret)
+ goto err;
+
+ /* soft reset */
+ ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2832_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ struct rtl2832_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+ u8 buf[3];
+
+ if (dev->sleeping)
+ return 0;
+
+ ret = regmap_bulk_read(dev->regmap, 0x33c, buf, 2);
+ if (ret)
+ goto err;
+
+ ret = regmap_bulk_read(dev->regmap, 0x351, &buf[2], 1);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "TPS=%*ph\n", 3, buf);
+
+ switch ((buf[0] >> 2) & 3) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = QAM_16;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ }
+
+ switch ((buf[2] >> 2) & 1) {
+ case 0:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ }
+
+ switch ((buf[2] >> 0) & 3) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[0] >> 4) & 7) {
+ case 0:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[1] >> 3) & 7) {
+ case 0:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[1] >> 0) & 7) {
+ case 0:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2832_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct rtl2832_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ u32 tmp;
+ u8 u8tmp, buf[2];
+ u16 u16tmp;
+
+ dev_dbg(&client->dev, "\n");
+
+ *status = 0;
+ if (dev->sleeping)
+ return 0;
+
+ ret = rtl2832_rd_demod_reg(dev, DVBT_FSM_STAGE, &tmp);
+ if (ret)
+ goto err;
+
+ if (tmp == 11) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ } else if (tmp == 10) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+ }
+
+ dev->fe_status = *status;
+
+ /* signal strength */
+ if (dev->fe_status & FE_HAS_SIGNAL) {
+ /* read digital AGC */
+ ret = regmap_bulk_read(dev->regmap, 0x305, &u8tmp, 1);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "digital agc=%02x", u8tmp);
+
+ u8tmp = ~u8tmp;
+ u16tmp = u8tmp << 8 | u8tmp << 0;
+
+ c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+ c->strength.stat[0].uvalue = u16tmp;
+ } else {
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* CNR */
+ if (dev->fe_status & FE_HAS_VITERBI) {
+ unsigned hierarchy, constellation;
+ #define CONSTELLATION_NUM 3
+ #define HIERARCHY_NUM 4
+ static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
+ {85387325, 85387325, 85387325, 85387325},
+ {86676178, 86676178, 87167949, 87795660},
+ {87659938, 87659938, 87885178, 88241743},
+ };
+
+ ret = regmap_bulk_read(dev->regmap, 0x33c, &u8tmp, 1);
+ if (ret)
+ goto err;
+
+ constellation = (u8tmp >> 2) & 0x03; /* [3:2] */
+ ret = -EINVAL;
+ if (constellation > CONSTELLATION_NUM - 1)
+ goto err;
+
+ hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */
+ if (hierarchy > HIERARCHY_NUM - 1)
+ goto err;
+
+ ret = regmap_bulk_read(dev->regmap, 0x40c, buf, 2);
+ if (ret)
+ goto err;
+
+ u16tmp = buf[0] << 8 | buf[1] << 0;
+ if (u16tmp)
+ tmp = (constant[constellation][hierarchy] -
+ intlog10(u16tmp)) / ((1 << 24) / 10000);
+ else
+ tmp = 0;
+
+ dev_dbg(&client->dev, "cnr raw=%u\n", u16tmp);
+
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = tmp;
+ } else {
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ /* BER */
+ if (dev->fe_status & FE_HAS_LOCK) {
+ ret = regmap_bulk_read(dev->regmap, 0x34e, buf, 2);
+ if (ret)
+ goto err;
+
+ u16tmp = buf[0] << 8 | buf[1] << 0;
+ dev->post_bit_error += u16tmp;
+ dev->post_bit_count += 1000000;
+
+ dev_dbg(&client->dev, "ber errors=%u total=1000000\n", u16tmp);
+
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
+ } else {
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ /* report SNR in resolution of 0.1 dB */
+ if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL)
+ *snr = div_s64(c->cnr.stat[0].svalue, 100);
+ else
+ *snr = 0;
+
+ return 0;
+}
+
+static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct rtl2832_dev *dev = fe->demodulator_priv;
+
+ *ber = (dev->post_bit_error - dev->post_bit_error_prev);
+ dev->post_bit_error_prev = dev->post_bit_error;
+
+ return 0;
+}
+
+/*
+ * I2C gate/mux/repeater logic
+ * There is delay mechanism to avoid unneeded I2C gate open / close. Gate close
+ * is delayed here a little bit in order to see if there is sequence of I2C
+ * messages sent to same I2C bus.
+ */
+static void rtl2832_i2c_gate_work(struct work_struct *work)
+{
+ struct rtl2832_dev *dev = container_of(work, struct rtl2832_dev, i2c_gate_work.work);
+ struct i2c_client *client = dev->client;
+ int ret;
+
+ /* close gate */
+ ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x00);
+ if (ret)
+ goto err;
+
+ return;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+}
+
+static int rtl2832_select(struct i2c_mux_core *muxc, u32 chan_id)
+{
+ struct rtl2832_dev *dev = i2c_mux_priv(muxc);
+ struct i2c_client *client = dev->client;
+ int ret;
+
+ /* terminate possible gate closing */
+ cancel_delayed_work(&dev->i2c_gate_work);
+
+ /* open gate */
+ ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x08);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2832_deselect(struct i2c_mux_core *muxc, u32 chan_id)
+{
+ struct rtl2832_dev *dev = i2c_mux_priv(muxc);
+
+ schedule_delayed_work(&dev->i2c_gate_work, usecs_to_jiffies(100));
+ return 0;
+}
+
+static const struct dvb_frontend_ops rtl2832_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Realtek RTL2832 (DVB-T)",
+ .frequency_min_hz = 174 * MHz,
+ .frequency_max_hz = 862 * MHz,
+ .frequency_stepsize_hz = 166667,
+ .caps = FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .init = rtl2832_init,
+ .sleep = rtl2832_sleep,
+
+ .get_tune_settings = rtl2832_get_tune_settings,
+
+ .set_frontend = rtl2832_set_frontend,
+ .get_frontend = rtl2832_get_frontend,
+
+ .read_status = rtl2832_read_status,
+ .read_snr = rtl2832_read_snr,
+ .read_ber = rtl2832_read_ber,
+};
+
+static bool rtl2832_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x305:
+ case 0x33c:
+ case 0x34e:
+ case 0x351:
+ case 0x40c ... 0x40d:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static struct dvb_frontend *rtl2832_get_dvb_frontend(struct i2c_client *client)
+{
+ struct rtl2832_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+ return &dev->fe;
+}
+
+static struct i2c_adapter *rtl2832_get_i2c_adapter(struct i2c_client *client)
+{
+ struct rtl2832_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+ return dev->muxc->adapter[0];
+}
+
+static int rtl2832_slave_ts_ctrl(struct i2c_client *client, bool enable)
+{
+ struct rtl2832_dev *dev = i2c_get_clientdata(client);
+ int ret;
+
+ dev_dbg(&client->dev, "enable=%d\n", enable);
+
+ if (enable) {
+ ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0);
+ if (ret)
+ goto err;
+ ret = regmap_bulk_write(dev->regmap, 0x10c, "\x5f\xff", 2);
+ if (ret)
+ goto err;
+ ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x1);
+ if (ret)
+ goto err;
+ ret = regmap_bulk_write(dev->regmap, 0x0bc, "\x18", 1);
+ if (ret)
+ goto err;
+ ret = regmap_bulk_write(dev->regmap, 0x192, "\x7f\xf7\xff", 3);
+ if (ret)
+ goto err;
+ } else {
+ ret = regmap_bulk_write(dev->regmap, 0x192, "\x00\x0f\xff", 3);
+ if (ret)
+ goto err;
+ ret = regmap_bulk_write(dev->regmap, 0x0bc, "\x08", 1);
+ if (ret)
+ goto err;
+ ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x0);
+ if (ret)
+ goto err;
+ ret = regmap_bulk_write(dev->regmap, 0x10c, "\x00\x00", 2);
+ if (ret)
+ goto err;
+ ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1);
+ if (ret)
+ goto err;
+ }
+
+ dev->slave_ts = enable;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct rtl2832_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+ u8 u8tmp;
+
+ dev_dbg(&client->dev, "onoff=%d, slave_ts=%d\n", onoff, dev->slave_ts);
+
+ /* enable / disable PID filter */
+ if (onoff)
+ u8tmp = 0x80;
+ else
+ u8tmp = 0x00;
+
+ if (dev->slave_ts)
+ ret = regmap_update_bits(dev->regmap, 0x021, 0xc0, u8tmp);
+ else
+ ret = regmap_update_bits(dev->regmap, 0x061, 0xc0, u8tmp);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid,
+ int onoff)
+{
+ struct rtl2832_dev *dev = fe->demodulator_priv;
+ struct i2c_client *client = dev->client;
+ int ret;
+ u8 buf[4];
+
+ dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d slave_ts=%d\n",
+ index, pid, onoff, dev->slave_ts);
+
+ /* skip invalid PIDs (0x2000) */
+ if (pid > 0x1fff || index >= 32)
+ return 0;
+
+ if (onoff)
+ set_bit(index, &dev->filters);
+ else
+ clear_bit(index, &dev->filters);
+
+ /* enable / disable PIDs */
+ buf[0] = (dev->filters >> 0) & 0xff;
+ buf[1] = (dev->filters >> 8) & 0xff;
+ buf[2] = (dev->filters >> 16) & 0xff;
+ buf[3] = (dev->filters >> 24) & 0xff;
+
+ if (dev->slave_ts)
+ ret = regmap_bulk_write(dev->regmap, 0x022, buf, 4);
+ else
+ ret = regmap_bulk_write(dev->regmap, 0x062, buf, 4);
+ if (ret)
+ goto err;
+
+ /* add PID */
+ buf[0] = (pid >> 8) & 0xff;
+ buf[1] = (pid >> 0) & 0xff;
+
+ if (dev->slave_ts)
+ ret = regmap_bulk_write(dev->regmap, 0x026 + 2 * index, buf, 2);
+ else
+ ret = regmap_bulk_write(dev->regmap, 0x066 + 2 * index, buf, 2);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int rtl2832_probe(struct i2c_client *client)
+{
+ struct rtl2832_platform_data *pdata = client->dev.platform_data;
+ struct i2c_adapter *i2c = client->adapter;
+ struct rtl2832_dev *dev;
+ int ret;
+ u8 tmp;
+ static const struct regmap_range_cfg regmap_range_cfg[] = {
+ {
+ .selector_reg = 0x00,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 0x100,
+ .range_min = 0 * 0x100,
+ .range_max = 5 * 0x100,
+ },
+ };
+
+ dev_dbg(&client->dev, "\n");
+
+ /* allocate memory for the internal state */
+ dev = kzalloc(sizeof(struct rtl2832_dev), GFP_KERNEL);
+ if (dev == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* setup the state */
+ i2c_set_clientdata(client, dev);
+ dev->client = client;
+ dev->pdata = client->dev.platform_data;
+ dev->sleeping = true;
+ INIT_DELAYED_WORK(&dev->i2c_gate_work, rtl2832_i2c_gate_work);
+ /* create regmap */
+ dev->regmap_config.reg_bits = 8;
+ dev->regmap_config.val_bits = 8;
+ dev->regmap_config.volatile_reg = rtl2832_volatile_reg;
+ dev->regmap_config.max_register = 5 * 0x100;
+ dev->regmap_config.ranges = regmap_range_cfg;
+ dev->regmap_config.num_ranges = ARRAY_SIZE(regmap_range_cfg);
+ dev->regmap_config.cache_type = REGCACHE_NONE;
+ dev->regmap = regmap_init_i2c(client, &dev->regmap_config);
+ if (IS_ERR(dev->regmap)) {
+ ret = PTR_ERR(dev->regmap);
+ goto err_kfree;
+ }
+
+ /* check if the demod is there */
+ ret = regmap_bulk_read(dev->regmap, 0x000, &tmp, 1);
+ if (ret)
+ goto err_regmap_exit;
+
+ /* create muxed i2c adapter for demod tuner bus */
+ dev->muxc = i2c_mux_alloc(i2c, &i2c->dev, 1, 0, I2C_MUX_LOCKED,
+ rtl2832_select, rtl2832_deselect);
+ if (!dev->muxc) {
+ ret = -ENOMEM;
+ goto err_regmap_exit;
+ }
+ dev->muxc->priv = dev;
+ ret = i2c_mux_add_adapter(dev->muxc, 0, 0);
+ if (ret)
+ goto err_regmap_exit;
+
+ /* create dvb_frontend */
+ memcpy(&dev->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops));
+ dev->fe.demodulator_priv = dev;
+
+ /* setup callbacks */
+ pdata->get_dvb_frontend = rtl2832_get_dvb_frontend;
+ pdata->get_i2c_adapter = rtl2832_get_i2c_adapter;
+ pdata->slave_ts_ctrl = rtl2832_slave_ts_ctrl;
+ pdata->pid_filter = rtl2832_pid_filter;
+ pdata->pid_filter_ctrl = rtl2832_pid_filter_ctrl;
+ pdata->regmap = dev->regmap;
+
+ dev_info(&client->dev, "Realtek RTL2832 successfully attached\n");
+ return 0;
+err_regmap_exit:
+ regmap_exit(dev->regmap);
+err_kfree:
+ kfree(dev);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static void rtl2832_remove(struct i2c_client *client)
+{
+ struct rtl2832_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ cancel_delayed_work_sync(&dev->i2c_gate_work);
+
+ i2c_mux_del_adapters(dev->muxc);
+
+ regmap_exit(dev->regmap);
+
+ kfree(dev);
+}
+
+static const struct i2c_device_id rtl2832_id_table[] = {
+ { "rtl2832" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rtl2832_id_table);
+
+static struct i2c_driver rtl2832_driver = {
+ .driver = {
+ .name = "rtl2832",
+ .suppress_bind_attrs = true,
+ },
+ .probe = rtl2832_probe,
+ .remove = rtl2832_remove,
+ .id_table = rtl2832_id_table,
+};
+
+module_i2c_driver(rtl2832_driver);
+
+MODULE_AUTHOR("Thomas Mair <mair.thomas86@gmail.com>");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Realtek RTL2832 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h
new file mode 100644
index 000000000000..85a8064bf933
--- /dev/null
+++ b/drivers/media/dvb-frontends/rtl2832.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Realtek RTL2832 DVB-T demodulator driver
+ *
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com>
+ * Copyright (C) 2012-2014 Antti Palosaari <crope@iki.fi>
+ */
+
+#ifndef RTL2832_H
+#define RTL2832_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/i2c-mux.h>
+
+/**
+ * struct rtl2832_platform_data - Platform data for the rtl2832 driver
+ * @clk: Clock frequency (4000000, 16000000, 25000000, 28800000).
+ * @tuner: Used tuner model.
+ * @get_dvb_frontend: Get DVB frontend.
+ * @get_i2c_adapter: Get I2C adapter.
+ * @slave_ts_ctrl: Control slave TS interface.
+ * @pid_filter: Set PID to PID filter.
+ * @pid_filter_ctrl: Control PID filter.
+ */
+struct rtl2832_platform_data {
+ u32 clk;
+ /*
+ * XXX: This list must be kept sync with dvb_usb_rtl28xxu USB IF driver.
+ */
+#define RTL2832_TUNER_FC2580 0x21
+#define RTL2832_TUNER_TUA9001 0x24
+#define RTL2832_TUNER_FC0012 0x26
+#define RTL2832_TUNER_E4000 0x27
+#define RTL2832_TUNER_FC0013 0x29
+#define RTL2832_TUNER_R820T 0x2a
+#define RTL2832_TUNER_R828D 0x2b
+#define RTL2832_TUNER_SI2157 0x2c
+ u8 tuner;
+
+ struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
+ struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *);
+ int (*slave_ts_ctrl)(struct i2c_client *, bool);
+ int (*pid_filter)(struct dvb_frontend *, u8, u16, int);
+ int (*pid_filter_ctrl)(struct dvb_frontend *, int);
+/* private: Register access for SDR module use only */
+ struct regmap *regmap;
+};
+
+#endif /* RTL2832_H */
diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h
new file mode 100644
index 000000000000..f11ba038d5f0
--- /dev/null
+++ b/drivers/media/dvb-frontends/rtl2832_priv.h
@@ -0,0 +1,415 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Realtek RTL2832 DVB-T demodulator driver
+ *
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com>
+ * Copyright (C) 2012-2014 Antti Palosaari <crope@iki.fi>
+ */
+
+#ifndef RTL2832_PRIV_H
+#define RTL2832_PRIV_H
+
+#include <linux/regmap.h>
+#include <linux/math64.h>
+#include <linux/bitops.h>
+
+#include <media/dvb_frontend.h>
+#include <linux/int_log.h>
+#include "rtl2832.h"
+
+struct rtl2832_dev {
+ struct rtl2832_platform_data *pdata;
+ struct i2c_client *client;
+ struct regmap_config regmap_config;
+ struct regmap *regmap;
+ struct i2c_mux_core *muxc;
+ struct dvb_frontend fe;
+ enum fe_status fe_status;
+ u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */
+ u64 post_bit_error;
+ u64 post_bit_count;
+ bool sleeping;
+ struct delayed_work i2c_gate_work;
+ unsigned long filters; /* PID filter */
+ bool slave_ts;
+};
+
+struct rtl2832_reg_entry {
+ u16 start_address;
+ u8 msb;
+ u8 lsb;
+};
+
+struct rtl2832_reg_value {
+ int reg;
+ u32 value;
+};
+
+/* Demod register bit names */
+enum DVBT_REG_BIT_NAME {
+ DVBT_SOFT_RST,
+ DVBT_IIC_REPEAT,
+ DVBT_TR_WAIT_MIN_8K,
+ DVBT_RSD_BER_FAIL_VAL,
+ DVBT_EN_BK_TRK,
+ DVBT_REG_PI,
+ DVBT_REG_PFREQ_1_0,
+ DVBT_PD_DA8,
+ DVBT_LOCK_TH,
+ DVBT_BER_PASS_SCAL,
+ DVBT_CE_FFSM_BYPASS,
+ DVBT_ALPHAIIR_N,
+ DVBT_ALPHAIIR_DIF,
+ DVBT_EN_TRK_SPAN,
+ DVBT_LOCK_TH_LEN,
+ DVBT_CCI_THRE,
+ DVBT_CCI_MON_SCAL,
+ DVBT_CCI_M0,
+ DVBT_CCI_M1,
+ DVBT_CCI_M2,
+ DVBT_CCI_M3,
+ DVBT_SPEC_INIT_0,
+ DVBT_SPEC_INIT_1,
+ DVBT_SPEC_INIT_2,
+ DVBT_AD_EN_REG,
+ DVBT_AD_EN_REG1,
+ DVBT_EN_BBIN,
+ DVBT_MGD_THD0,
+ DVBT_MGD_THD1,
+ DVBT_MGD_THD2,
+ DVBT_MGD_THD3,
+ DVBT_MGD_THD4,
+ DVBT_MGD_THD5,
+ DVBT_MGD_THD6,
+ DVBT_MGD_THD7,
+ DVBT_EN_CACQ_NOTCH,
+ DVBT_AD_AV_REF,
+ DVBT_PIP_ON,
+ DVBT_SCALE1_B92,
+ DVBT_SCALE1_B93,
+ DVBT_SCALE1_BA7,
+ DVBT_SCALE1_BA9,
+ DVBT_SCALE1_BAA,
+ DVBT_SCALE1_BAB,
+ DVBT_SCALE1_BAC,
+ DVBT_SCALE1_BB0,
+ DVBT_SCALE1_BB1,
+ DVBT_KB_P1,
+ DVBT_KB_P2,
+ DVBT_KB_P3,
+ DVBT_OPT_ADC_IQ,
+ DVBT_AD_AVI,
+ DVBT_AD_AVQ,
+ DVBT_K1_CR_STEP12,
+ DVBT_TRK_KS_P2,
+ DVBT_TRK_KS_I2,
+ DVBT_TR_THD_SET2,
+ DVBT_TRK_KC_P2,
+ DVBT_TRK_KC_I2,
+ DVBT_CR_THD_SET2,
+ DVBT_PSET_IFFREQ,
+ DVBT_SPEC_INV,
+ DVBT_BW_INDEX,
+ DVBT_RSAMP_RATIO,
+ DVBT_CFREQ_OFF_RATIO,
+ DVBT_FSM_STAGE,
+ DVBT_RX_CONSTEL,
+ DVBT_RX_HIER,
+ DVBT_RX_C_RATE_LP,
+ DVBT_RX_C_RATE_HP,
+ DVBT_GI_IDX,
+ DVBT_FFT_MODE_IDX,
+ DVBT_RSD_BER_EST,
+ DVBT_CE_EST_EVM,
+ DVBT_RF_AGC_VAL,
+ DVBT_IF_AGC_VAL,
+ DVBT_DAGC_VAL,
+ DVBT_SFREQ_OFF,
+ DVBT_CFREQ_OFF,
+ DVBT_POLAR_RF_AGC,
+ DVBT_POLAR_IF_AGC,
+ DVBT_AAGC_HOLD,
+ DVBT_EN_RF_AGC,
+ DVBT_EN_IF_AGC,
+ DVBT_IF_AGC_MIN,
+ DVBT_IF_AGC_MAX,
+ DVBT_RF_AGC_MIN,
+ DVBT_RF_AGC_MAX,
+ DVBT_IF_AGC_MAN,
+ DVBT_IF_AGC_MAN_VAL,
+ DVBT_RF_AGC_MAN,
+ DVBT_RF_AGC_MAN_VAL,
+ DVBT_DAGC_TRG_VAL,
+ DVBT_AGC_TARG_VAL,
+ DVBT_LOOP_GAIN_3_0,
+ DVBT_LOOP_GAIN_4,
+ DVBT_VTOP,
+ DVBT_KRF,
+ DVBT_AGC_TARG_VAL_0,
+ DVBT_AGC_TARG_VAL_8_1,
+ DVBT_AAGC_LOOP_GAIN,
+ DVBT_LOOP_GAIN2_3_0,
+ DVBT_LOOP_GAIN2_4,
+ DVBT_LOOP_GAIN3,
+ DVBT_VTOP1,
+ DVBT_VTOP2,
+ DVBT_VTOP3,
+ DVBT_KRF1,
+ DVBT_KRF2,
+ DVBT_KRF3,
+ DVBT_KRF4,
+ DVBT_EN_GI_PGA,
+ DVBT_THD_LOCK_UP,
+ DVBT_THD_LOCK_DW,
+ DVBT_THD_UP1,
+ DVBT_THD_DW1,
+ DVBT_INTER_CNT_LEN,
+ DVBT_GI_PGA_STATE,
+ DVBT_EN_AGC_PGA,
+ DVBT_CKOUTPAR,
+ DVBT_CKOUT_PWR,
+ DVBT_SYNC_DUR,
+ DVBT_ERR_DUR,
+ DVBT_SYNC_LVL,
+ DVBT_ERR_LVL,
+ DVBT_VAL_LVL,
+ DVBT_SERIAL,
+ DVBT_SER_LSB,
+ DVBT_CDIV_PH0,
+ DVBT_CDIV_PH1,
+ DVBT_MPEG_IO_OPT_2_2,
+ DVBT_MPEG_IO_OPT_1_0,
+ DVBT_CKOUTPAR_PIP,
+ DVBT_CKOUT_PWR_PIP,
+ DVBT_SYNC_LVL_PIP,
+ DVBT_ERR_LVL_PIP,
+ DVBT_VAL_LVL_PIP,
+ DVBT_CKOUTPAR_PID,
+ DVBT_CKOUT_PWR_PID,
+ DVBT_SYNC_LVL_PID,
+ DVBT_ERR_LVL_PID,
+ DVBT_VAL_LVL_PID,
+ DVBT_SM_PASS,
+ DVBT_UPDATE_REG_2,
+ DVBT_BTHD_P3,
+ DVBT_BTHD_D3,
+ DVBT_FUNC4_REG0,
+ DVBT_FUNC4_REG1,
+ DVBT_FUNC4_REG2,
+ DVBT_FUNC4_REG3,
+ DVBT_FUNC4_REG4,
+ DVBT_FUNC4_REG5,
+ DVBT_FUNC4_REG6,
+ DVBT_FUNC4_REG7,
+ DVBT_FUNC4_REG8,
+ DVBT_FUNC4_REG9,
+ DVBT_FUNC4_REG10,
+ DVBT_FUNC5_REG0,
+ DVBT_FUNC5_REG1,
+ DVBT_FUNC5_REG2,
+ DVBT_FUNC5_REG3,
+ DVBT_FUNC5_REG4,
+ DVBT_FUNC5_REG5,
+ DVBT_FUNC5_REG6,
+ DVBT_FUNC5_REG7,
+ DVBT_FUNC5_REG8,
+ DVBT_FUNC5_REG9,
+ DVBT_FUNC5_REG10,
+ DVBT_FUNC5_REG11,
+ DVBT_FUNC5_REG12,
+ DVBT_FUNC5_REG13,
+ DVBT_FUNC5_REG14,
+ DVBT_FUNC5_REG15,
+ DVBT_FUNC5_REG16,
+ DVBT_FUNC5_REG17,
+ DVBT_FUNC5_REG18,
+ DVBT_AD7_SETTING,
+ DVBT_RSSI_R,
+ DVBT_ACI_DET_IND,
+ DVBT_REG_MON,
+ DVBT_REG_MONSEL,
+ DVBT_REG_GPE,
+ DVBT_REG_GPO,
+ DVBT_REG_4MSEL,
+ DVBT_TEST_REG_1,
+ DVBT_TEST_REG_2,
+ DVBT_TEST_REG_3,
+ DVBT_TEST_REG_4,
+ DVBT_REG_BIT_NAME_ITEM_TERMINATOR,
+};
+
+static const struct rtl2832_reg_value rtl2832_tuner_init_fc2580[] = {
+ {DVBT_DAGC_TRG_VAL, 0x39},
+ {DVBT_AGC_TARG_VAL_0, 0x0},
+ {DVBT_AGC_TARG_VAL_8_1, 0x5a},
+ {DVBT_AAGC_LOOP_GAIN, 0x16},
+ {DVBT_LOOP_GAIN2_3_0, 0x6},
+ {DVBT_LOOP_GAIN2_4, 0x1},
+ {DVBT_LOOP_GAIN3, 0x16},
+ {DVBT_VTOP1, 0x35},
+ {DVBT_VTOP2, 0x21},
+ {DVBT_VTOP3, 0x21},
+ {DVBT_KRF1, 0x0},
+ {DVBT_KRF2, 0x40},
+ {DVBT_KRF3, 0x10},
+ {DVBT_KRF4, 0x10},
+ {DVBT_IF_AGC_MIN, 0x80},
+ {DVBT_IF_AGC_MAX, 0x7f},
+ {DVBT_RF_AGC_MIN, 0x9c},
+ {DVBT_RF_AGC_MAX, 0x7f},
+ {DVBT_POLAR_RF_AGC, 0x0},
+ {DVBT_POLAR_IF_AGC, 0x0},
+ {DVBT_AD7_SETTING, 0xe9f4},
+};
+
+static const struct rtl2832_reg_value rtl2832_tuner_init_tua9001[] = {
+ {DVBT_DAGC_TRG_VAL, 0x39},
+ {DVBT_AGC_TARG_VAL_0, 0x0},
+ {DVBT_AGC_TARG_VAL_8_1, 0x5a},
+ {DVBT_AAGC_LOOP_GAIN, 0x16},
+ {DVBT_LOOP_GAIN2_3_0, 0x6},
+ {DVBT_LOOP_GAIN2_4, 0x1},
+ {DVBT_LOOP_GAIN3, 0x16},
+ {DVBT_VTOP1, 0x35},
+ {DVBT_VTOP2, 0x21},
+ {DVBT_VTOP3, 0x21},
+ {DVBT_KRF1, 0x0},
+ {DVBT_KRF2, 0x40},
+ {DVBT_KRF3, 0x10},
+ {DVBT_KRF4, 0x10},
+ {DVBT_IF_AGC_MIN, 0x80},
+ {DVBT_IF_AGC_MAX, 0x7f},
+ {DVBT_RF_AGC_MIN, 0x9c},
+ {DVBT_RF_AGC_MAX, 0x7f},
+ {DVBT_POLAR_RF_AGC, 0x0},
+ {DVBT_POLAR_IF_AGC, 0x0},
+ {DVBT_AD7_SETTING, 0xe9f4},
+ {DVBT_OPT_ADC_IQ, 0x1},
+ {DVBT_AD_AVI, 0x0},
+ {DVBT_AD_AVQ, 0x0},
+ {DVBT_SPEC_INV, 0x0},
+};
+
+static const struct rtl2832_reg_value rtl2832_tuner_init_fc0012[] = {
+ {DVBT_DAGC_TRG_VAL, 0x5a},
+ {DVBT_AGC_TARG_VAL_0, 0x0},
+ {DVBT_AGC_TARG_VAL_8_1, 0x5a},
+ {DVBT_AAGC_LOOP_GAIN, 0x16},
+ {DVBT_LOOP_GAIN2_3_0, 0x6},
+ {DVBT_LOOP_GAIN2_4, 0x1},
+ {DVBT_LOOP_GAIN3, 0x16},
+ {DVBT_VTOP1, 0x35},
+ {DVBT_VTOP2, 0x21},
+ {DVBT_VTOP3, 0x21},
+ {DVBT_KRF1, 0x0},
+ {DVBT_KRF2, 0x40},
+ {DVBT_KRF3, 0x10},
+ {DVBT_KRF4, 0x10},
+ {DVBT_IF_AGC_MIN, 0x80},
+ {DVBT_IF_AGC_MAX, 0x7f},
+ {DVBT_RF_AGC_MIN, 0x80},
+ {DVBT_RF_AGC_MAX, 0x7f},
+ {DVBT_POLAR_RF_AGC, 0x0},
+ {DVBT_POLAR_IF_AGC, 0x0},
+ {DVBT_AD7_SETTING, 0xe9bf},
+ {DVBT_EN_GI_PGA, 0x0},
+ {DVBT_THD_LOCK_UP, 0x0},
+ {DVBT_THD_LOCK_DW, 0x0},
+ {DVBT_THD_UP1, 0x11},
+ {DVBT_THD_DW1, 0xef},
+ {DVBT_INTER_CNT_LEN, 0xc},
+ {DVBT_GI_PGA_STATE, 0x0},
+ {DVBT_EN_AGC_PGA, 0x1},
+ {DVBT_IF_AGC_MAN, 0x0},
+ {DVBT_SPEC_INV, 0x0},
+};
+
+static const struct rtl2832_reg_value rtl2832_tuner_init_e4000[] = {
+ {DVBT_DAGC_TRG_VAL, 0x5a},
+ {DVBT_AGC_TARG_VAL_0, 0x0},
+ {DVBT_AGC_TARG_VAL_8_1, 0x5a},
+ {DVBT_AAGC_LOOP_GAIN, 0x18},
+ {DVBT_LOOP_GAIN2_3_0, 0x8},
+ {DVBT_LOOP_GAIN2_4, 0x1},
+ {DVBT_LOOP_GAIN3, 0x18},
+ {DVBT_VTOP1, 0x35},
+ {DVBT_VTOP2, 0x21},
+ {DVBT_VTOP3, 0x21},
+ {DVBT_KRF1, 0x0},
+ {DVBT_KRF2, 0x40},
+ {DVBT_KRF3, 0x10},
+ {DVBT_KRF4, 0x10},
+ {DVBT_IF_AGC_MIN, 0x80},
+ {DVBT_IF_AGC_MAX, 0x7f},
+ {DVBT_RF_AGC_MIN, 0x80},
+ {DVBT_RF_AGC_MAX, 0x7f},
+ {DVBT_POLAR_RF_AGC, 0x0},
+ {DVBT_POLAR_IF_AGC, 0x0},
+ {DVBT_AD7_SETTING, 0xe9d4},
+ {DVBT_EN_GI_PGA, 0x0},
+ {DVBT_THD_LOCK_UP, 0x0},
+ {DVBT_THD_LOCK_DW, 0x0},
+ {DVBT_THD_UP1, 0x14},
+ {DVBT_THD_DW1, 0xec},
+ {DVBT_INTER_CNT_LEN, 0xc},
+ {DVBT_GI_PGA_STATE, 0x0},
+ {DVBT_EN_AGC_PGA, 0x1},
+ {DVBT_REG_GPE, 0x1},
+ {DVBT_REG_GPO, 0x1},
+ {DVBT_REG_MONSEL, 0x1},
+ {DVBT_REG_MON, 0x1},
+ {DVBT_REG_4MSEL, 0x0},
+ {DVBT_SPEC_INV, 0x0},
+};
+
+static const struct rtl2832_reg_value rtl2832_tuner_init_r820t[] = {
+ {DVBT_DAGC_TRG_VAL, 0x39},
+ {DVBT_AGC_TARG_VAL_0, 0x0},
+ {DVBT_AGC_TARG_VAL_8_1, 0x40},
+ {DVBT_AAGC_LOOP_GAIN, 0x16},
+ {DVBT_LOOP_GAIN2_3_0, 0x8},
+ {DVBT_LOOP_GAIN2_4, 0x1},
+ {DVBT_LOOP_GAIN3, 0x18},
+ {DVBT_VTOP1, 0x35},
+ {DVBT_VTOP2, 0x21},
+ {DVBT_VTOP3, 0x21},
+ {DVBT_KRF1, 0x0},
+ {DVBT_KRF2, 0x40},
+ {DVBT_KRF3, 0x10},
+ {DVBT_KRF4, 0x10},
+ {DVBT_IF_AGC_MIN, 0x80},
+ {DVBT_IF_AGC_MAX, 0x7f},
+ {DVBT_RF_AGC_MIN, 0x80},
+ {DVBT_RF_AGC_MAX, 0x7f},
+ {DVBT_POLAR_RF_AGC, 0x0},
+ {DVBT_POLAR_IF_AGC, 0x0},
+ {DVBT_AD7_SETTING, 0xe9f4},
+ {DVBT_SPEC_INV, 0x1},
+};
+
+static const struct rtl2832_reg_value rtl2832_tuner_init_si2157[] = {
+ {DVBT_DAGC_TRG_VAL, 0x39},
+ {DVBT_AGC_TARG_VAL_0, 0x0},
+ {DVBT_AGC_TARG_VAL_8_1, 0x40},
+ {DVBT_AAGC_LOOP_GAIN, 0x16},
+ {DVBT_LOOP_GAIN2_3_0, 0x8},
+ {DVBT_LOOP_GAIN2_4, 0x1},
+ {DVBT_LOOP_GAIN3, 0x18},
+ {DVBT_VTOP1, 0x35},
+ {DVBT_VTOP2, 0x21},
+ {DVBT_VTOP3, 0x21},
+ {DVBT_KRF1, 0x0},
+ {DVBT_KRF2, 0x40},
+ {DVBT_KRF3, 0x10},
+ {DVBT_KRF4, 0x10},
+ {DVBT_IF_AGC_MIN, 0x80},
+ {DVBT_IF_AGC_MAX, 0x7f},
+ {DVBT_RF_AGC_MIN, 0x80},
+ {DVBT_RF_AGC_MAX, 0x7f},
+ {DVBT_POLAR_RF_AGC, 0x0},
+ {DVBT_POLAR_IF_AGC, 0x0},
+ {DVBT_AD7_SETTING, 0xe9f4},
+ {DVBT_SPEC_INV, 0x0},
+};
+
+#endif /* RTL2832_PRIV_H */
diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c
new file mode 100644
index 000000000000..0357624968f1
--- /dev/null
+++ b/drivers/media/dvb-frontends/rtl2832_sdr.c
@@ -0,0 +1,1494 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Realtek RTL2832U SDR driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ * GNU Radio plugin "gr-kernel" for device usage will be on:
+ * https://git.linuxtv.org/anttip/gr-kernel.git
+ */
+
+#include "rtl2832_sdr.h"
+#include "dvb_usb.h"
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include <linux/platform_device.h>
+#include <linux/jiffies.h>
+#include <linux/math64.h>
+#include <linux/regmap.h>
+
+static bool rtl2832_sdr_emulated_fmt;
+module_param_named(emulated_formats, rtl2832_sdr_emulated_fmt, bool, 0644);
+MODULE_PARM_DESC(emulated_formats, "enable emulated formats (disappears in future)");
+
+/* Original macro does not contain enough null pointer checks for our need */
+#define V4L2_SUBDEV_HAS_OP(sd, o, f) \
+ ((sd) && (sd)->ops && (sd)->ops->o && (sd)->ops->o->f)
+
+#define MAX_BULK_BUFS (10)
+#define BULK_BUFFER_SIZE (128 * 512)
+
+static const struct v4l2_frequency_band bands_adc[] = {
+ {
+ .tuner = 0,
+ .type = V4L2_TUNER_ADC,
+ .index = 0,
+ .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 300000,
+ .rangehigh = 300000,
+ },
+ {
+ .tuner = 0,
+ .type = V4L2_TUNER_ADC,
+ .index = 1,
+ .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 900001,
+ .rangehigh = 2800000,
+ },
+ {
+ .tuner = 0,
+ .type = V4L2_TUNER_ADC,
+ .index = 2,
+ .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 3200000,
+ .rangehigh = 3200000,
+ },
+};
+
+static const struct v4l2_frequency_band bands_fm[] = {
+ {
+ .tuner = 1,
+ .type = V4L2_TUNER_RF,
+ .index = 0,
+ .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 50000000,
+ .rangehigh = 2000000000,
+ },
+};
+
+/* stream formats */
+struct rtl2832_sdr_format {
+ char *name;
+ u32 pixelformat;
+ u32 buffersize;
+};
+
+static struct rtl2832_sdr_format formats[] = {
+ {
+ .pixelformat = V4L2_SDR_FMT_CU8,
+ .buffersize = BULK_BUFFER_SIZE,
+ }, {
+ .pixelformat = V4L2_SDR_FMT_CU16LE,
+ .buffersize = BULK_BUFFER_SIZE * 2,
+ },
+};
+
+static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats);
+
+/* intermediate buffers with raw data from the USB device */
+struct rtl2832_sdr_frame_buf {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+};
+
+struct rtl2832_sdr_dev {
+#define POWER_ON 0 /* BIT(0) */
+#define URB_BUF 1 /* BIT(1) */
+ unsigned long flags;
+
+ struct platform_device *pdev;
+ struct regmap *regmap;
+
+ struct video_device vdev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_subdev *v4l2_subdev;
+
+ /* videobuf2 queue and queued buffers list */
+ struct vb2_queue vb_queue;
+ struct list_head queued_bufs;
+ spinlock_t queued_bufs_lock; /* Protects queued_bufs */
+ unsigned sequence; /* buffer sequence counter */
+
+ /* Note if taking both locks v4l2_lock must always be locked first! */
+ struct mutex v4l2_lock; /* Protects everything else */
+ struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */
+
+ /* Pointer to our usb_device, will be NULL after unplug */
+ struct usb_device *udev; /* Both mutexes most be hold when setting! */
+
+ unsigned int vb_full; /* vb is full and packets dropped */
+
+ struct urb *urb_list[MAX_BULK_BUFS];
+ int buf_num;
+ unsigned long buf_size;
+ u8 *buf_list[MAX_BULK_BUFS];
+ dma_addr_t dma_addr[MAX_BULK_BUFS];
+ int urbs_initialized;
+ int urbs_submitted;
+
+ unsigned int f_adc, f_tuner;
+ u32 pixelformat;
+ u32 buffersize;
+ unsigned int num_formats;
+
+ /* Controls */
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *bandwidth_auto;
+ struct v4l2_ctrl *bandwidth;
+
+ /* for sample rate calc */
+ unsigned int sample;
+ unsigned int sample_measured;
+ unsigned long jiffies_next;
+};
+
+/* Private functions */
+static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf(
+ struct rtl2832_sdr_dev *dev)
+{
+ unsigned long flags;
+ struct rtl2832_sdr_frame_buf *buf = NULL;
+
+ spin_lock_irqsave(&dev->queued_bufs_lock, flags);
+ if (list_empty(&dev->queued_bufs))
+ goto leave;
+
+ buf = list_entry(dev->queued_bufs.next,
+ struct rtl2832_sdr_frame_buf, list);
+ list_del(&buf->list);
+leave:
+ spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
+ return buf;
+}
+
+static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_dev *dev,
+ void *dst, const u8 *src, unsigned int src_len)
+{
+ struct platform_device *pdev = dev->pdev;
+ unsigned int dst_len;
+
+ if (dev->pixelformat == V4L2_SDR_FMT_CU8) {
+ /* native stream, no need to convert */
+ memcpy(dst, src, src_len);
+ dst_len = src_len;
+ } else if (dev->pixelformat == V4L2_SDR_FMT_CU16LE) {
+ /* convert u8 to u16 */
+ unsigned int i;
+ u16 *u16dst = dst;
+
+ for (i = 0; i < src_len; i++)
+ *u16dst++ = (src[i] << 8) | (src[i] >> 0);
+ dst_len = 2 * src_len;
+ } else {
+ dst_len = 0;
+ }
+
+ /* calculate sample rate and output it in 10 seconds intervals */
+ if (unlikely(time_is_before_jiffies(dev->jiffies_next))) {
+ #define MSECS 10000UL
+ unsigned int msecs = jiffies_to_msecs(jiffies -
+ dev->jiffies_next + msecs_to_jiffies(MSECS));
+ unsigned int samples = dev->sample - dev->sample_measured;
+
+ dev->jiffies_next = jiffies + msecs_to_jiffies(MSECS);
+ dev->sample_measured = dev->sample;
+ dev_dbg(&pdev->dev,
+ "slen=%u samples=%u msecs=%u sample rate=%lu\n",
+ src_len, samples, msecs, samples * 1000UL / msecs);
+ }
+
+ /* total number of I+Q pairs */
+ dev->sample += src_len / 2;
+
+ return dst_len;
+}
+
+/*
+ * This gets called for the bulk stream pipe. This is done in interrupt
+ * time, so it has to be fast, not crash, and not stall. Neat.
+ */
+static void rtl2832_sdr_urb_complete(struct urb *urb)
+{
+ struct rtl2832_sdr_dev *dev = urb->context;
+ struct platform_device *pdev = dev->pdev;
+ struct rtl2832_sdr_frame_buf *fbuf;
+
+ dev_dbg_ratelimited(&pdev->dev, "status=%d length=%d/%d errors=%d\n",
+ urb->status, urb->actual_length,
+ urb->transfer_buffer_length, urb->error_count);
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* error */
+ dev_err_ratelimited(&pdev->dev, "urb failed=%d\n", urb->status);
+ break;
+ }
+
+ if (likely(urb->actual_length > 0)) {
+ void *ptr;
+ unsigned int len;
+ /* get free framebuffer */
+ fbuf = rtl2832_sdr_get_next_fill_buf(dev);
+ if (unlikely(fbuf == NULL)) {
+ dev->vb_full++;
+ dev_notice_ratelimited(&pdev->dev,
+ "video buffer is full, %d packets dropped\n",
+ dev->vb_full);
+ goto skip;
+ }
+
+ /* fill framebuffer */
+ ptr = vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0);
+ len = rtl2832_sdr_convert_stream(dev, ptr, urb->transfer_buffer,
+ urb->actual_length);
+ vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0, len);
+ fbuf->vb.vb2_buf.timestamp = ktime_get_ns();
+ fbuf->vb.sequence = dev->sequence++;
+ vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ }
+skip:
+ usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int rtl2832_sdr_kill_urbs(struct rtl2832_sdr_dev *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+ int i;
+
+ for (i = dev->urbs_submitted - 1; i >= 0; i--) {
+ dev_dbg(&pdev->dev, "kill urb=%d\n", i);
+ /* stop the URB */
+ usb_kill_urb(dev->urb_list[i]);
+ }
+ dev->urbs_submitted = 0;
+
+ return 0;
+}
+
+static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_dev *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+ int i, ret;
+
+ for (i = 0; i < dev->urbs_initialized; i++) {
+ dev_dbg(&pdev->dev, "submit urb=%d\n", i);
+ ret = usb_submit_urb(dev->urb_list[i], GFP_KERNEL);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not submit urb no. %d - get them all back\n",
+ i);
+ rtl2832_sdr_kill_urbs(dev);
+ return ret;
+ }
+ dev->urbs_submitted++;
+ }
+
+ return 0;
+}
+
+static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_dev *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+
+ if (test_bit(URB_BUF, &dev->flags)) {
+ while (dev->buf_num) {
+ dev->buf_num--;
+ dev_dbg(&pdev->dev, "free buf=%d\n", dev->buf_num);
+ usb_free_coherent(dev->udev, dev->buf_size,
+ dev->buf_list[dev->buf_num],
+ dev->dma_addr[dev->buf_num]);
+ }
+ }
+ clear_bit(URB_BUF, &dev->flags);
+
+ return 0;
+}
+
+static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_dev *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+
+ dev->buf_num = 0;
+ dev->buf_size = BULK_BUFFER_SIZE;
+
+ dev_dbg(&pdev->dev, "all in all I will use %u bytes for streaming\n",
+ MAX_BULK_BUFS * BULK_BUFFER_SIZE);
+
+ for (dev->buf_num = 0; dev->buf_num < MAX_BULK_BUFS; dev->buf_num++) {
+ dev->buf_list[dev->buf_num] = usb_alloc_coherent(dev->udev,
+ BULK_BUFFER_SIZE, GFP_KERNEL,
+ &dev->dma_addr[dev->buf_num]);
+ if (!dev->buf_list[dev->buf_num]) {
+ dev_dbg(&pdev->dev, "alloc buf=%d failed\n",
+ dev->buf_num);
+ rtl2832_sdr_free_stream_bufs(dev);
+ return -ENOMEM;
+ }
+
+ dev_dbg(&pdev->dev, "alloc buf=%d %p (dma %llu)\n",
+ dev->buf_num, dev->buf_list[dev->buf_num],
+ (long long)dev->dma_addr[dev->buf_num]);
+ set_bit(URB_BUF, &dev->flags);
+ }
+
+ return 0;
+}
+
+static int rtl2832_sdr_free_urbs(struct rtl2832_sdr_dev *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+ int i;
+
+ rtl2832_sdr_kill_urbs(dev);
+
+ for (i = dev->urbs_initialized - 1; i >= 0; i--) {
+ if (dev->urb_list[i]) {
+ dev_dbg(&pdev->dev, "free urb=%d\n", i);
+ /* free the URBs */
+ usb_free_urb(dev->urb_list[i]);
+ }
+ }
+ dev->urbs_initialized = 0;
+
+ return 0;
+}
+
+static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_dev *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+ int i, j;
+
+ /* allocate the URBs */
+ for (i = 0; i < MAX_BULK_BUFS; i++) {
+ dev_dbg(&pdev->dev, "alloc urb=%d\n", i);
+ dev->urb_list[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->urb_list[i]) {
+ for (j = 0; j < i; j++) {
+ usb_free_urb(dev->urb_list[j]);
+ dev->urb_list[j] = NULL;
+ }
+ dev->urbs_initialized = 0;
+ return -ENOMEM;
+ }
+ usb_fill_bulk_urb(dev->urb_list[i],
+ dev->udev,
+ usb_rcvbulkpipe(dev->udev, 0x81),
+ dev->buf_list[i],
+ BULK_BUFFER_SIZE,
+ rtl2832_sdr_urb_complete, dev);
+
+ dev->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ dev->urb_list[i]->transfer_dma = dev->dma_addr[i];
+ dev->urbs_initialized++;
+ }
+
+ return 0;
+}
+
+/* Must be called with vb_queue_lock hold */
+static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+ unsigned long flags;
+
+ dev_dbg(&pdev->dev, "\n");
+
+ spin_lock_irqsave(&dev->queued_bufs_lock, flags);
+ while (!list_empty(&dev->queued_bufs)) {
+ struct rtl2832_sdr_frame_buf *buf;
+
+ buf = list_entry(dev->queued_bufs.next,
+ struct rtl2832_sdr_frame_buf, list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
+}
+
+static int rtl2832_sdr_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct rtl2832_sdr_dev *dev = video_drvdata(file);
+ struct platform_device *pdev = dev->pdev;
+
+ dev_dbg(&pdev->dev, "\n");
+
+ strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+ strscpy(cap->card, dev->vdev.name, sizeof(cap->card));
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+ return 0;
+}
+
+/* Videobuf2 operations */
+static int rtl2832_sdr_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq);
+ struct platform_device *pdev = dev->pdev;
+ unsigned int q_num_bufs = vb2_get_num_buffers(vq);
+
+ dev_dbg(&pdev->dev, "nbuffers=%d\n", *nbuffers);
+
+ /* Need at least 8 buffers */
+ if (q_num_bufs + *nbuffers < 8)
+ *nbuffers = 8 - q_num_bufs;
+ *nplanes = 1;
+ sizes[0] = PAGE_ALIGN(dev->buffersize);
+ dev_dbg(&pdev->dev, "nbuffers=%d sizes[0]=%d\n", *nbuffers, sizes[0]);
+ return 0;
+}
+
+static int rtl2832_sdr_buf_prepare(struct vb2_buffer *vb)
+{
+ struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+
+ /* Don't allow queueing new buffers after device disconnection */
+ if (!dev->udev)
+ return -ENODEV;
+
+ return 0;
+}
+
+static void rtl2832_sdr_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct rtl2832_sdr_frame_buf *buf =
+ container_of(vbuf, struct rtl2832_sdr_frame_buf, vb);
+ unsigned long flags;
+
+ /* Check the device has not disconnected between prep and queuing */
+ if (!dev->udev) {
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ return;
+ }
+
+ spin_lock_irqsave(&dev->queued_bufs_lock, flags);
+ list_add_tail(&buf->list, &dev->queued_bufs);
+ spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
+}
+
+static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+ struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+ struct dvb_frontend *fe = pdata->dvb_frontend;
+ int ret;
+ unsigned int f_sr, f_if;
+ u8 buf[4], u8tmp1, u8tmp2;
+ u64 u64tmp;
+ u32 u32tmp;
+
+ dev_dbg(&pdev->dev, "f_adc=%u\n", dev->f_adc);
+
+ if (!test_bit(POWER_ON, &dev->flags))
+ return 0;
+
+ if (dev->f_adc == 0)
+ return 0;
+
+ f_sr = dev->f_adc;
+
+ ret = regmap_bulk_write(dev->regmap, 0x13e, "\x00\x00", 2);
+ if (ret)
+ goto err;
+
+ ret = regmap_bulk_write(dev->regmap, 0x115, "\x00\x00\x00\x00", 4);
+ if (ret)
+ goto err;
+
+ /* get IF from tuner */
+ if (fe->ops.tuner_ops.get_if_frequency)
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &f_if);
+ else
+ ret = -EINVAL;
+
+ if (ret)
+ goto err;
+
+ /* program IF */
+ u64tmp = f_if % pdata->clk;
+ u64tmp *= 0x400000;
+ u64tmp = div_u64(u64tmp, pdata->clk);
+ u64tmp = -u64tmp;
+ u32tmp = u64tmp & 0x3fffff;
+
+ dev_dbg(&pdev->dev, "f_if=%u if_ctl=%08x\n", f_if, u32tmp);
+
+ buf[0] = (u32tmp >> 16) & 0xff;
+ buf[1] = (u32tmp >> 8) & 0xff;
+ buf[2] = (u32tmp >> 0) & 0xff;
+
+ ret = regmap_bulk_write(dev->regmap, 0x119, buf, 3);
+ if (ret)
+ goto err;
+
+ /* BB / IF mode */
+ /* POR: 0x1b1=0x1f, 0x008=0x0d, 0x006=0x80 */
+ if (f_if) {
+ u8tmp1 = 0x1a; /* disable Zero-IF */
+ u8tmp2 = 0x8d; /* enable ADC I */
+ } else {
+ u8tmp1 = 0x1b; /* enable Zero-IF, DC, IQ */
+ u8tmp2 = 0xcd; /* enable ADC I, ADC Q */
+ }
+
+ ret = regmap_write(dev->regmap, 0x1b1, u8tmp1);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap, 0x008, u8tmp2);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap, 0x006, 0x80);
+ if (ret)
+ goto err;
+
+ /* program sampling rate (resampling down) */
+ u32tmp = div_u64(pdata->clk * 0x400000ULL, f_sr * 4U);
+ u32tmp <<= 2;
+ buf[0] = (u32tmp >> 24) & 0xff;
+ buf[1] = (u32tmp >> 16) & 0xff;
+ buf[2] = (u32tmp >> 8) & 0xff;
+ buf[3] = (u32tmp >> 0) & 0xff;
+ ret = regmap_bulk_write(dev->regmap, 0x19f, buf, 4);
+ if (ret)
+ goto err;
+
+ /* low-pass filter */
+ ret = regmap_bulk_write(dev->regmap, 0x11c,
+ "\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5",
+ 20);
+ if (ret)
+ goto err;
+
+ ret = regmap_bulk_write(dev->regmap, 0x017, "\x11\x10", 2);
+ if (ret)
+ goto err;
+
+ /* mode */
+ ret = regmap_write(dev->regmap, 0x019, 0x05);
+ if (ret)
+ goto err;
+
+ ret = regmap_bulk_write(dev->regmap, 0x01a,
+ "\x1b\x16\x0d\x06\x01\xff", 6);
+ if (ret)
+ goto err;
+
+ /* FSM */
+ ret = regmap_bulk_write(dev->regmap, 0x192, "\x00\xf0\x0f", 3);
+ if (ret)
+ goto err;
+
+ /* PID filter */
+ ret = regmap_write(dev->regmap, 0x061, 0x60);
+ if (ret)
+ goto err;
+
+ /* used RF tuner based settings */
+ switch (pdata->tuner) {
+ case RTL2832_SDR_TUNER_E4000:
+ ret = regmap_write(dev->regmap, 0x112, 0x5a);
+ ret = regmap_write(dev->regmap, 0x102, 0x40);
+ ret = regmap_write(dev->regmap, 0x103, 0x5a);
+ ret = regmap_write(dev->regmap, 0x1c7, 0x30);
+ ret = regmap_write(dev->regmap, 0x104, 0xd0);
+ ret = regmap_write(dev->regmap, 0x105, 0xbe);
+ ret = regmap_write(dev->regmap, 0x1c8, 0x18);
+ ret = regmap_write(dev->regmap, 0x106, 0x35);
+ ret = regmap_write(dev->regmap, 0x1c9, 0x21);
+ ret = regmap_write(dev->regmap, 0x1ca, 0x21);
+ ret = regmap_write(dev->regmap, 0x1cb, 0x00);
+ ret = regmap_write(dev->regmap, 0x107, 0x40);
+ ret = regmap_write(dev->regmap, 0x1cd, 0x10);
+ ret = regmap_write(dev->regmap, 0x1ce, 0x10);
+ ret = regmap_write(dev->regmap, 0x108, 0x80);
+ ret = regmap_write(dev->regmap, 0x109, 0x7f);
+ ret = regmap_write(dev->regmap, 0x10a, 0x80);
+ ret = regmap_write(dev->regmap, 0x10b, 0x7f);
+ ret = regmap_write(dev->regmap, 0x00e, 0xfc);
+ ret = regmap_write(dev->regmap, 0x00e, 0xfc);
+ ret = regmap_write(dev->regmap, 0x011, 0xd4);
+ ret = regmap_write(dev->regmap, 0x1e5, 0xf0);
+ ret = regmap_write(dev->regmap, 0x1d9, 0x00);
+ ret = regmap_write(dev->regmap, 0x1db, 0x00);
+ ret = regmap_write(dev->regmap, 0x1dd, 0x14);
+ ret = regmap_write(dev->regmap, 0x1de, 0xec);
+ ret = regmap_write(dev->regmap, 0x1d8, 0x0c);
+ ret = regmap_write(dev->regmap, 0x1e6, 0x02);
+ ret = regmap_write(dev->regmap, 0x1d7, 0x09);
+ ret = regmap_write(dev->regmap, 0x00d, 0x83);
+ ret = regmap_write(dev->regmap, 0x010, 0x49);
+ ret = regmap_write(dev->regmap, 0x00d, 0x87);
+ ret = regmap_write(dev->regmap, 0x00d, 0x85);
+ ret = regmap_write(dev->regmap, 0x013, 0x02);
+ break;
+ case RTL2832_SDR_TUNER_FC0012:
+ case RTL2832_SDR_TUNER_FC0013:
+ ret = regmap_write(dev->regmap, 0x112, 0x5a);
+ ret = regmap_write(dev->regmap, 0x102, 0x40);
+ ret = regmap_write(dev->regmap, 0x103, 0x5a);
+ ret = regmap_write(dev->regmap, 0x1c7, 0x2c);
+ ret = regmap_write(dev->regmap, 0x104, 0xcc);
+ ret = regmap_write(dev->regmap, 0x105, 0xbe);
+ ret = regmap_write(dev->regmap, 0x1c8, 0x16);
+ ret = regmap_write(dev->regmap, 0x106, 0x35);
+ ret = regmap_write(dev->regmap, 0x1c9, 0x21);
+ ret = regmap_write(dev->regmap, 0x1ca, 0x21);
+ ret = regmap_write(dev->regmap, 0x1cb, 0x00);
+ ret = regmap_write(dev->regmap, 0x107, 0x40);
+ ret = regmap_write(dev->regmap, 0x1cd, 0x10);
+ ret = regmap_write(dev->regmap, 0x1ce, 0x10);
+ ret = regmap_write(dev->regmap, 0x108, 0x80);
+ ret = regmap_write(dev->regmap, 0x109, 0x7f);
+ ret = regmap_write(dev->regmap, 0x10a, 0x80);
+ ret = regmap_write(dev->regmap, 0x10b, 0x7f);
+ ret = regmap_write(dev->regmap, 0x00e, 0xfc);
+ ret = regmap_write(dev->regmap, 0x00e, 0xfc);
+ ret = regmap_bulk_write(dev->regmap, 0x011, "\xe9\xbf", 2);
+ ret = regmap_write(dev->regmap, 0x1e5, 0xf0);
+ ret = regmap_write(dev->regmap, 0x1d9, 0x00);
+ ret = regmap_write(dev->regmap, 0x1db, 0x00);
+ ret = regmap_write(dev->regmap, 0x1dd, 0x11);
+ ret = regmap_write(dev->regmap, 0x1de, 0xef);
+ ret = regmap_write(dev->regmap, 0x1d8, 0x0c);
+ ret = regmap_write(dev->regmap, 0x1e6, 0x02);
+ ret = regmap_write(dev->regmap, 0x1d7, 0x09);
+ break;
+ case RTL2832_SDR_TUNER_R820T:
+ case RTL2832_SDR_TUNER_R828D:
+ ret = regmap_write(dev->regmap, 0x112, 0x5a);
+ ret = regmap_write(dev->regmap, 0x102, 0x40);
+ ret = regmap_write(dev->regmap, 0x115, 0x01);
+ ret = regmap_write(dev->regmap, 0x103, 0x80);
+ ret = regmap_write(dev->regmap, 0x1c7, 0x24);
+ ret = regmap_write(dev->regmap, 0x104, 0xcc);
+ ret = regmap_write(dev->regmap, 0x105, 0xbe);
+ ret = regmap_write(dev->regmap, 0x1c8, 0x14);
+ ret = regmap_write(dev->regmap, 0x106, 0x35);
+ ret = regmap_write(dev->regmap, 0x1c9, 0x21);
+ ret = regmap_write(dev->regmap, 0x1ca, 0x21);
+ ret = regmap_write(dev->regmap, 0x1cb, 0x00);
+ ret = regmap_write(dev->regmap, 0x107, 0x40);
+ ret = regmap_write(dev->regmap, 0x1cd, 0x10);
+ ret = regmap_write(dev->regmap, 0x1ce, 0x10);
+ ret = regmap_write(dev->regmap, 0x108, 0x80);
+ ret = regmap_write(dev->regmap, 0x109, 0x7f);
+ ret = regmap_write(dev->regmap, 0x10a, 0x80);
+ ret = regmap_write(dev->regmap, 0x10b, 0x7f);
+ ret = regmap_write(dev->regmap, 0x00e, 0xfc);
+ ret = regmap_write(dev->regmap, 0x00e, 0xfc);
+ ret = regmap_write(dev->regmap, 0x011, 0xf4);
+ break;
+ case RTL2832_SDR_TUNER_FC2580:
+ ret = regmap_write(dev->regmap, 0x112, 0x39);
+ ret = regmap_write(dev->regmap, 0x102, 0x40);
+ ret = regmap_write(dev->regmap, 0x103, 0x5a);
+ ret = regmap_write(dev->regmap, 0x1c7, 0x2c);
+ ret = regmap_write(dev->regmap, 0x104, 0xcc);
+ ret = regmap_write(dev->regmap, 0x105, 0xbe);
+ ret = regmap_write(dev->regmap, 0x1c8, 0x16);
+ ret = regmap_write(dev->regmap, 0x106, 0x35);
+ ret = regmap_write(dev->regmap, 0x1c9, 0x21);
+ ret = regmap_write(dev->regmap, 0x1ca, 0x21);
+ ret = regmap_write(dev->regmap, 0x1cb, 0x00);
+ ret = regmap_write(dev->regmap, 0x107, 0x40);
+ ret = regmap_write(dev->regmap, 0x1cd, 0x10);
+ ret = regmap_write(dev->regmap, 0x1ce, 0x10);
+ ret = regmap_write(dev->regmap, 0x108, 0x80);
+ ret = regmap_write(dev->regmap, 0x109, 0x7f);
+ ret = regmap_write(dev->regmap, 0x10a, 0x9c);
+ ret = regmap_write(dev->regmap, 0x10b, 0x7f);
+ ret = regmap_write(dev->regmap, 0x00e, 0xfc);
+ ret = regmap_write(dev->regmap, 0x00e, 0xfc);
+ ret = regmap_bulk_write(dev->regmap, 0x011, "\xe9\xf4", 2);
+ break;
+ default:
+ dev_notice(&pdev->dev, "Unsupported tuner\n");
+ }
+
+ /* software reset */
+ ret = regmap_update_bits(dev->regmap, 0x101, 0x04, 0x04);
+ if (ret)
+ goto err;
+
+ ret = regmap_update_bits(dev->regmap, 0x101, 0x04, 0x00);
+ if (ret)
+ goto err;
+err:
+ return ret;
+};
+
+static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_dev *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+ int ret;
+
+ dev_dbg(&pdev->dev, "\n");
+
+ /* PID filter */
+ ret = regmap_write(dev->regmap, 0x061, 0xe0);
+ if (ret)
+ goto err;
+
+ /* mode */
+ ret = regmap_write(dev->regmap, 0x019, 0x20);
+ if (ret)
+ goto err;
+
+ ret = regmap_bulk_write(dev->regmap, 0x017, "\x11\x10", 2);
+ if (ret)
+ goto err;
+
+ /* FSM */
+ ret = regmap_bulk_write(dev->regmap, 0x192, "\x00\x0f\xff", 3);
+ if (ret)
+ goto err;
+
+ ret = regmap_bulk_write(dev->regmap, 0x13e, "\x40\x00", 2);
+ if (ret)
+ goto err;
+
+ ret = regmap_bulk_write(dev->regmap, 0x115, "\x06\x3f\xce\xcc", 4);
+ if (ret)
+ goto err;
+err:
+ return;
+};
+
+static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_dev *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+ struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+ struct dvb_frontend *fe = pdata->dvb_frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct v4l2_ctrl *bandwidth_auto;
+ struct v4l2_ctrl *bandwidth;
+
+ /*
+ * tuner RF (Hz)
+ */
+ if (dev->f_tuner == 0)
+ return 0;
+
+ /*
+ * bandwidth (Hz)
+ */
+ bandwidth_auto = v4l2_ctrl_find(&dev->hdl,
+ V4L2_CID_RF_TUNER_BANDWIDTH_AUTO);
+ bandwidth = v4l2_ctrl_find(&dev->hdl, V4L2_CID_RF_TUNER_BANDWIDTH);
+ if (v4l2_ctrl_g_ctrl(bandwidth_auto)) {
+ c->bandwidth_hz = dev->f_adc;
+ v4l2_ctrl_s_ctrl(bandwidth, dev->f_adc);
+ } else {
+ c->bandwidth_hz = v4l2_ctrl_g_ctrl(bandwidth);
+ }
+
+ c->frequency = dev->f_tuner;
+ c->delivery_system = SYS_DVBT;
+
+ dev_dbg(&pdev->dev, "frequency=%u bandwidth=%d\n",
+ c->frequency, c->bandwidth_hz);
+
+ if (!test_bit(POWER_ON, &dev->flags))
+ return 0;
+
+ if (!V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, s_frequency)) {
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+ }
+
+ return 0;
+};
+
+static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_dev *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+ struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+ struct dvb_frontend *fe = pdata->dvb_frontend;
+
+ dev_dbg(&pdev->dev, "\n");
+
+ if (fe->ops.tuner_ops.init)
+ fe->ops.tuner_ops.init(fe);
+
+ return 0;
+};
+
+static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_dev *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+ struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+ struct dvb_frontend *fe = pdata->dvb_frontend;
+
+ dev_dbg(&pdev->dev, "\n");
+
+ if (fe->ops.tuner_ops.sleep)
+ fe->ops.tuner_ops.sleep(fe);
+
+ return;
+};
+
+static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq);
+ struct platform_device *pdev = dev->pdev;
+ struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+ struct dvb_usb_device *d = pdata->dvb_usb_device;
+ int ret;
+
+ dev_dbg(&pdev->dev, "\n");
+
+ if (!dev->udev)
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&dev->v4l2_lock))
+ return -ERESTARTSYS;
+
+ if (d->props->power_ctrl)
+ d->props->power_ctrl(d, 1);
+
+ /* enable ADC */
+ if (d->props->frontend_ctrl)
+ d->props->frontend_ctrl(pdata->dvb_frontend, 1);
+
+ set_bit(POWER_ON, &dev->flags);
+
+ /* wake-up tuner */
+ if (V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, core, s_power))
+ ret = v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 1);
+ else
+ ret = rtl2832_sdr_set_tuner(dev);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_set_tuner_freq(dev);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_set_adc(dev);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_alloc_stream_bufs(dev);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_sdr_alloc_urbs(dev);
+ if (ret)
+ goto err;
+
+ dev->sequence = 0;
+
+ ret = rtl2832_sdr_submit_urbs(dev);
+ if (ret)
+ goto err;
+
+err:
+ mutex_unlock(&dev->v4l2_lock);
+
+ return ret;
+}
+
+static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq)
+{
+ struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq);
+ struct platform_device *pdev = dev->pdev;
+ struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+ struct dvb_usb_device *d = pdata->dvb_usb_device;
+
+ dev_dbg(&pdev->dev, "\n");
+
+ mutex_lock(&dev->v4l2_lock);
+
+ rtl2832_sdr_kill_urbs(dev);
+ rtl2832_sdr_free_urbs(dev);
+ rtl2832_sdr_free_stream_bufs(dev);
+ rtl2832_sdr_cleanup_queued_bufs(dev);
+ rtl2832_sdr_unset_adc(dev);
+
+ /* sleep tuner */
+ if (V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, core, s_power))
+ v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 0);
+ else
+ rtl2832_sdr_unset_tuner(dev);
+
+ clear_bit(POWER_ON, &dev->flags);
+
+ /* disable ADC */
+ if (d->props->frontend_ctrl)
+ d->props->frontend_ctrl(pdata->dvb_frontend, 0);
+
+ if (d->props->power_ctrl)
+ d->props->power_ctrl(d, 0);
+
+ mutex_unlock(&dev->v4l2_lock);
+}
+
+static const struct vb2_ops rtl2832_sdr_vb2_ops = {
+ .queue_setup = rtl2832_sdr_queue_setup,
+ .buf_prepare = rtl2832_sdr_buf_prepare,
+ .buf_queue = rtl2832_sdr_buf_queue,
+ .start_streaming = rtl2832_sdr_start_streaming,
+ .stop_streaming = rtl2832_sdr_stop_streaming,
+};
+
+static int rtl2832_sdr_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct rtl2832_sdr_dev *dev = video_drvdata(file);
+ struct platform_device *pdev = dev->pdev;
+ int ret;
+
+ dev_dbg(&pdev->dev, "index=%d type=%d\n", v->index, v->type);
+
+ if (v->index == 0) {
+ strscpy(v->name, "ADC: Realtek RTL2832", sizeof(v->name));
+ v->type = V4L2_TUNER_ADC;
+ v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+ v->rangelow = 300000;
+ v->rangehigh = 3200000;
+ ret = 0;
+ } else if (v->index == 1 &&
+ V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, g_tuner)) {
+ ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, g_tuner, v);
+ } else if (v->index == 1) {
+ strscpy(v->name, "RF: <unknown>", sizeof(v->name));
+ v->type = V4L2_TUNER_RF;
+ v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+ v->rangelow = 50000000;
+ v->rangehigh = 2000000000;
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int rtl2832_sdr_s_tuner(struct file *file, void *priv,
+ const struct v4l2_tuner *v)
+{
+ struct rtl2832_sdr_dev *dev = video_drvdata(file);
+ struct platform_device *pdev = dev->pdev;
+ int ret;
+
+ dev_dbg(&pdev->dev, "\n");
+
+ if (v->index == 0) {
+ ret = 0;
+ } else if (v->index == 1 &&
+ V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, s_tuner)) {
+ ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, s_tuner, v);
+ } else if (v->index == 1) {
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv,
+ struct v4l2_frequency_band *band)
+{
+ struct rtl2832_sdr_dev *dev = video_drvdata(file);
+ struct platform_device *pdev = dev->pdev;
+ int ret;
+
+ dev_dbg(&pdev->dev, "tuner=%d type=%d index=%d\n",
+ band->tuner, band->type, band->index);
+
+ if (band->tuner == 0) {
+ if (band->index >= ARRAY_SIZE(bands_adc))
+ return -EINVAL;
+
+ *band = bands_adc[band->index];
+ ret = 0;
+ } else if (band->tuner == 1 &&
+ V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, enum_freq_bands)) {
+ ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, enum_freq_bands, band);
+ } else if (band->tuner == 1) {
+ if (band->index >= ARRAY_SIZE(bands_fm))
+ return -EINVAL;
+
+ *band = bands_fm[band->index];
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int rtl2832_sdr_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct rtl2832_sdr_dev *dev = video_drvdata(file);
+ struct platform_device *pdev = dev->pdev;
+ int ret;
+
+ dev_dbg(&pdev->dev, "tuner=%d type=%d\n", f->tuner, f->type);
+
+ if (f->tuner == 0) {
+ f->frequency = dev->f_adc;
+ f->type = V4L2_TUNER_ADC;
+ ret = 0;
+ } else if (f->tuner == 1 &&
+ V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, g_frequency)) {
+ f->type = V4L2_TUNER_RF;
+ ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, g_frequency, f);
+ } else if (f->tuner == 1) {
+ f->frequency = dev->f_tuner;
+ f->type = V4L2_TUNER_RF;
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int rtl2832_sdr_s_frequency(struct file *file, void *priv,
+ const struct v4l2_frequency *f)
+{
+ struct rtl2832_sdr_dev *dev = video_drvdata(file);
+ struct platform_device *pdev = dev->pdev;
+ int ret, band;
+
+ dev_dbg(&pdev->dev, "tuner=%d type=%d frequency=%u\n",
+ f->tuner, f->type, f->frequency);
+
+ /* ADC band midpoints */
+ #define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2)
+ #define BAND_ADC_1 ((bands_adc[1].rangehigh + bands_adc[2].rangelow) / 2)
+
+ if (f->tuner == 0 && f->type == V4L2_TUNER_ADC) {
+ if (f->frequency < BAND_ADC_0)
+ band = 0;
+ else if (f->frequency < BAND_ADC_1)
+ band = 1;
+ else
+ band = 2;
+
+ dev->f_adc = clamp_t(unsigned int, f->frequency,
+ bands_adc[band].rangelow,
+ bands_adc[band].rangehigh);
+
+ dev_dbg(&pdev->dev, "ADC frequency=%u Hz\n", dev->f_adc);
+ ret = rtl2832_sdr_set_adc(dev);
+ } else if (f->tuner == 1 &&
+ V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, s_frequency)) {
+ ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, s_frequency, f);
+ } else if (f->tuner == 1) {
+ dev->f_tuner = clamp_t(unsigned int, f->frequency,
+ bands_fm[0].rangelow,
+ bands_fm[0].rangehigh);
+ dev_dbg(&pdev->dev, "RF frequency=%u Hz\n", f->frequency);
+
+ ret = rtl2832_sdr_set_tuner_freq(dev);
+ } else {
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct rtl2832_sdr_dev *dev = video_drvdata(file);
+ struct platform_device *pdev = dev->pdev;
+
+ dev_dbg(&pdev->dev, "\n");
+
+ if (f->index >= dev->num_formats)
+ return -EINVAL;
+
+ f->pixelformat = formats[f->index].pixelformat;
+
+ return 0;
+}
+
+static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rtl2832_sdr_dev *dev = video_drvdata(file);
+ struct platform_device *pdev = dev->pdev;
+
+ dev_dbg(&pdev->dev, "\n");
+
+ f->fmt.sdr.pixelformat = dev->pixelformat;
+ f->fmt.sdr.buffersize = dev->buffersize;
+
+ return 0;
+}
+
+static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rtl2832_sdr_dev *dev = video_drvdata(file);
+ struct platform_device *pdev = dev->pdev;
+ struct vb2_queue *q = &dev->vb_queue;
+ int i;
+
+ dev_dbg(&pdev->dev, "pixelformat fourcc %4.4s\n",
+ (char *)&f->fmt.sdr.pixelformat);
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ for (i = 0; i < dev->num_formats; i++) {
+ if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
+ dev->pixelformat = formats[i].pixelformat;
+ dev->buffersize = formats[i].buffersize;
+ f->fmt.sdr.buffersize = formats[i].buffersize;
+ return 0;
+ }
+ }
+
+ dev->pixelformat = formats[0].pixelformat;
+ dev->buffersize = formats[0].buffersize;
+ f->fmt.sdr.pixelformat = formats[0].pixelformat;
+ f->fmt.sdr.buffersize = formats[0].buffersize;
+
+ return 0;
+}
+
+static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rtl2832_sdr_dev *dev = video_drvdata(file);
+ struct platform_device *pdev = dev->pdev;
+ int i;
+
+ dev_dbg(&pdev->dev, "pixelformat fourcc %4.4s\n",
+ (char *)&f->fmt.sdr.pixelformat);
+
+ for (i = 0; i < dev->num_formats; i++) {
+ if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
+ f->fmt.sdr.buffersize = formats[i].buffersize;
+ return 0;
+ }
+ }
+
+ f->fmt.sdr.pixelformat = formats[0].pixelformat;
+ f->fmt.sdr.buffersize = formats[0].buffersize;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops rtl2832_sdr_ioctl_ops = {
+ .vidioc_querycap = rtl2832_sdr_querycap,
+
+ .vidioc_enum_fmt_sdr_cap = rtl2832_sdr_enum_fmt_sdr_cap,
+ .vidioc_g_fmt_sdr_cap = rtl2832_sdr_g_fmt_sdr_cap,
+ .vidioc_s_fmt_sdr_cap = rtl2832_sdr_s_fmt_sdr_cap,
+ .vidioc_try_fmt_sdr_cap = rtl2832_sdr_try_fmt_sdr_cap,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_g_tuner = rtl2832_sdr_g_tuner,
+ .vidioc_s_tuner = rtl2832_sdr_s_tuner,
+
+ .vidioc_enum_freq_bands = rtl2832_sdr_enum_freq_bands,
+ .vidioc_g_frequency = rtl2832_sdr_g_frequency,
+ .vidioc_s_frequency = rtl2832_sdr_s_frequency,
+
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+};
+
+static const struct v4l2_file_operations rtl2832_sdr_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static struct video_device rtl2832_sdr_template = {
+ .name = "Realtek RTL2832 SDR",
+ .release = video_device_release_empty,
+ .fops = &rtl2832_sdr_fops,
+ .ioctl_ops = &rtl2832_sdr_ioctl_ops,
+ .device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE | V4L2_CAP_TUNER,
+};
+
+static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct rtl2832_sdr_dev *dev =
+ container_of(ctrl->handler, struct rtl2832_sdr_dev,
+ hdl);
+ struct platform_device *pdev = dev->pdev;
+ struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+ struct dvb_frontend *fe = pdata->dvb_frontend;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+
+ dev_dbg(&pdev->dev, "id=%d name=%s val=%d min=%lld max=%lld step=%lld\n",
+ ctrl->id, ctrl->name, ctrl->val, ctrl->minimum, ctrl->maximum,
+ ctrl->step);
+
+ switch (ctrl->id) {
+ case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
+ case V4L2_CID_RF_TUNER_BANDWIDTH:
+ /* TODO: these controls should be moved to tuner drivers */
+ if (dev->bandwidth_auto->val) {
+ /* Round towards the closest legal value */
+ s32 val = dev->f_adc + div_u64(dev->bandwidth->step, 2);
+ u32 offset;
+
+ val = clamp_t(s32, val, dev->bandwidth->minimum,
+ dev->bandwidth->maximum);
+ offset = val - dev->bandwidth->minimum;
+ offset = dev->bandwidth->step *
+ div_u64(offset, dev->bandwidth->step);
+ dev->bandwidth->val = dev->bandwidth->minimum + offset;
+ }
+ c->bandwidth_hz = dev->bandwidth->val;
+
+ if (!test_bit(POWER_ON, &dev->flags))
+ return 0;
+
+ if (fe->ops.tuner_ops.set_params)
+ ret = fe->ops.tuner_ops.set_params(fe);
+ else
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops rtl2832_sdr_ctrl_ops = {
+ .s_ctrl = rtl2832_sdr_s_ctrl,
+};
+
+static void rtl2832_sdr_video_release(struct v4l2_device *v)
+{
+ struct rtl2832_sdr_dev *dev =
+ container_of(v, struct rtl2832_sdr_dev, v4l2_dev);
+ struct platform_device *pdev = dev->pdev;
+
+ dev_dbg(&pdev->dev, "\n");
+
+ v4l2_ctrl_handler_free(&dev->hdl);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ kfree(dev);
+}
+
+/* Platform driver interface */
+static int rtl2832_sdr_probe(struct platform_device *pdev)
+{
+ struct rtl2832_sdr_dev *dev;
+ struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
+ const struct v4l2_ctrl_ops *ops = &rtl2832_sdr_ctrl_ops;
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ dev_dbg(&pdev->dev, "\n");
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Cannot proceed without platform data\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ if (!pdev->dev.parent->driver) {
+ dev_dbg(&pdev->dev, "No parent device\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ /* try to refcount host drv since we are the consumer */
+ if (!try_module_get(pdev->dev.parent->driver->owner)) {
+ dev_err(&pdev->dev, "Refcount fail");
+ ret = -EINVAL;
+ goto err;
+ }
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ ret = -ENOMEM;
+ goto err_module_put;
+ }
+
+ /* setup the state */
+ subdev = pdata->v4l2_subdev;
+ dev->v4l2_subdev = pdata->v4l2_subdev;
+ dev->pdev = pdev;
+ dev->regmap = pdata->regmap;
+ dev->udev = pdata->dvb_usb_device->udev;
+ dev->f_adc = bands_adc[0].rangelow;
+ dev->f_tuner = bands_fm[0].rangelow;
+ dev->pixelformat = formats[0].pixelformat;
+ dev->buffersize = formats[0].buffersize;
+ dev->num_formats = NUM_FORMATS;
+ if (!rtl2832_sdr_emulated_fmt)
+ dev->num_formats -= 1;
+
+ mutex_init(&dev->v4l2_lock);
+ mutex_init(&dev->vb_queue_lock);
+ spin_lock_init(&dev->queued_bufs_lock);
+ INIT_LIST_HEAD(&dev->queued_bufs);
+
+ /* Init videobuf2 queue structure */
+ dev->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
+ dev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+ dev->vb_queue.drv_priv = dev;
+ dev->vb_queue.buf_struct_size = sizeof(struct rtl2832_sdr_frame_buf);
+ dev->vb_queue.ops = &rtl2832_sdr_vb2_ops;
+ dev->vb_queue.mem_ops = &vb2_vmalloc_memops;
+ dev->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ dev->vb_queue.lock = &dev->vb_queue_lock;
+ ret = vb2_queue_init(&dev->vb_queue);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not initialize vb2 queue\n");
+ goto err_kfree;
+ }
+
+ /* Register controls */
+ switch (pdata->tuner) {
+ case RTL2832_SDR_TUNER_E4000:
+ v4l2_ctrl_handler_init(&dev->hdl, 9);
+ if (subdev)
+ v4l2_ctrl_add_handler(&dev->hdl, subdev->ctrl_handler,
+ NULL, true);
+ break;
+ case RTL2832_SDR_TUNER_R820T:
+ case RTL2832_SDR_TUNER_R828D:
+ v4l2_ctrl_handler_init(&dev->hdl, 2);
+ dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, ops,
+ V4L2_CID_RF_TUNER_BANDWIDTH_AUTO,
+ 0, 1, 1, 1);
+ dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, ops,
+ V4L2_CID_RF_TUNER_BANDWIDTH,
+ 0, 8000000, 100000, 0);
+ v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false);
+ break;
+ case RTL2832_SDR_TUNER_FC0012:
+ case RTL2832_SDR_TUNER_FC0013:
+ v4l2_ctrl_handler_init(&dev->hdl, 2);
+ dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, ops,
+ V4L2_CID_RF_TUNER_BANDWIDTH_AUTO,
+ 0, 1, 1, 1);
+ dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, ops,
+ V4L2_CID_RF_TUNER_BANDWIDTH,
+ 6000000, 8000000, 1000000,
+ 6000000);
+ v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false);
+ break;
+ case RTL2832_SDR_TUNER_FC2580:
+ v4l2_ctrl_handler_init(&dev->hdl, 2);
+ if (subdev)
+ v4l2_ctrl_add_handler(&dev->hdl, subdev->ctrl_handler,
+ NULL, true);
+ break;
+ default:
+ v4l2_ctrl_handler_init(&dev->hdl, 0);
+ dev_err(&pdev->dev, "Unsupported tuner\n");
+ ret = -ENODEV;
+ goto err_v4l2_ctrl_handler_free;
+ }
+ if (dev->hdl.error) {
+ ret = dev->hdl.error;
+ dev_err(&pdev->dev, "Could not initialize controls\n");
+ goto err_v4l2_ctrl_handler_free;
+ }
+
+ /* Init video_device structure */
+ dev->vdev = rtl2832_sdr_template;
+ dev->vdev.queue = &dev->vb_queue;
+ video_set_drvdata(&dev->vdev, dev);
+
+ /* Register the v4l2_device structure */
+ dev->v4l2_dev.release = rtl2832_sdr_video_release;
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register v4l2-device %d\n", ret);
+ goto err_v4l2_ctrl_handler_free;
+ }
+
+ dev->v4l2_dev.ctrl_handler = &dev->hdl;
+ dev->vdev.v4l2_dev = &dev->v4l2_dev;
+ dev->vdev.lock = &dev->v4l2_lock;
+ dev->vdev.vfl_dir = VFL_DIR_RX;
+
+ ret = video_register_device(&dev->vdev, VFL_TYPE_SDR, -1);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register as video device %d\n",
+ ret);
+ goto err_v4l2_device_unregister;
+ }
+ dev_info(&pdev->dev, "Registered as %s\n",
+ video_device_node_name(&dev->vdev));
+ dev_info(&pdev->dev, "Realtek RTL2832 SDR attached\n");
+ dev_notice(&pdev->dev,
+ "SDR API is still slightly experimental and functionality changes may follow\n");
+ platform_set_drvdata(pdev, dev);
+ return 0;
+err_v4l2_device_unregister:
+ v4l2_device_unregister(&dev->v4l2_dev);
+err_v4l2_ctrl_handler_free:
+ v4l2_ctrl_handler_free(&dev->hdl);
+err_kfree:
+ kfree(dev);
+err_module_put:
+ module_put(pdev->dev.parent->driver->owner);
+err:
+ return ret;
+}
+
+static void rtl2832_sdr_remove(struct platform_device *pdev)
+{
+ struct rtl2832_sdr_dev *dev = platform_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "\n");
+
+ mutex_lock(&dev->vb_queue_lock);
+ mutex_lock(&dev->v4l2_lock);
+ /* No need to keep the urbs around after disconnection */
+ dev->udev = NULL;
+ v4l2_device_disconnect(&dev->v4l2_dev);
+ video_unregister_device(&dev->vdev);
+ mutex_unlock(&dev->v4l2_lock);
+ mutex_unlock(&dev->vb_queue_lock);
+ v4l2_device_put(&dev->v4l2_dev);
+ module_put(pdev->dev.parent->driver->owner);
+}
+
+static struct platform_driver rtl2832_sdr_driver = {
+ .driver = {
+ .name = "rtl2832_sdr",
+ },
+ .probe = rtl2832_sdr_probe,
+ .remove = rtl2832_sdr_remove,
+};
+module_platform_driver(rtl2832_sdr_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Realtek RTL2832 SDR driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.h b/drivers/media/dvb-frontends/rtl2832_sdr.h
new file mode 100644
index 000000000000..26ad709ccd98
--- /dev/null
+++ b/drivers/media/dvb-frontends/rtl2832_sdr.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Realtek RTL2832U SDR driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ * GNU Radio plugin "gr-kernel" for device usage will be on:
+ * http://git.linuxtv.org/anttip/gr-kernel.git
+ */
+
+#ifndef RTL2832_SDR_H
+#define RTL2832_SDR_H
+
+#include <linux/i2c.h>
+#include <media/v4l2-subdev.h>
+#include <media/dvb_frontend.h>
+
+/**
+ * struct rtl2832_sdr_platform_data - Platform data for the rtl2832_sdr driver
+ * @clk: Clock frequency (4000000, 16000000, 25000000, 28800000).
+ * @tuner: Used tuner model.
+ * @regmap: pointer to &struct regmap.
+ * @dvb_frontend: rtl2832 DVB frontend.
+ * @v4l2_subdev: Tuner v4l2 controls.
+ * @dvb_usb_device: DVB USB interface for USB streaming.
+ */
+struct rtl2832_sdr_platform_data {
+ u32 clk;
+ /*
+ * XXX: This list must be kept sync with dvb_usb_rtl28xxu USB IF driver.
+ */
+#define RTL2832_SDR_TUNER_FC2580 0x21
+#define RTL2832_SDR_TUNER_TUA9001 0x24
+#define RTL2832_SDR_TUNER_FC0012 0x26
+#define RTL2832_SDR_TUNER_E4000 0x27
+#define RTL2832_SDR_TUNER_FC0013 0x29
+#define RTL2832_SDR_TUNER_R820T 0x2a
+#define RTL2832_SDR_TUNER_R828D 0x2b
+ u8 tuner;
+
+ struct regmap *regmap;
+ struct dvb_frontend *dvb_frontend;
+ struct v4l2_subdev *v4l2_subdev;
+ struct dvb_usb_device *dvb_usb_device;
+};
+
+#endif /* RTL2832_SDR_H */
diff --git a/drivers/media/dvb/frontends/s5h1409.c b/drivers/media/dvb-frontends/s5h1409.c
index f71b06221e14..28b1dca077ea 100644
--- a/drivers/media/dvb/frontends/s5h1409.c
+++ b/drivers/media/dvb-frontends/s5h1409.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Samsung S5H1409 VSB/QAM demodulator driver
Copyright (C) 2006 Steven Toth <stoth@linuxtv.org>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
@@ -25,7 +13,7 @@
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "s5h1409.h"
struct s5h1409_state {
@@ -38,7 +26,7 @@ struct s5h1409_state {
struct dvb_frontend frontend;
/* previous uncorrected block counter */
- fe_modulation_t current_modulation;
+ enum fe_modulation current_modulation;
u32 current_frequency;
int if_freq;
@@ -321,8 +309,8 @@ static int s5h1409_writereg(struct s5h1409_state *state, u8 reg, u16 data)
ret = i2c_transfer(state->i2c, &msg, 1);
if (ret != 1)
- printk(KERN_ERR "%s: error (reg == 0x%02x, val == 0x%04x, "
- "ret == %i)\n", __func__, reg, data, ret);
+ printk(KERN_ERR "%s: error (reg == 0x%02x, val == 0x%04x, ret == %i)\n",
+ __func__, reg, data, ret);
return (ret != 1) ? -1 : 0;
}
@@ -400,7 +388,7 @@ static int s5h1409_set_spectralinversion(struct dvb_frontend *fe, int inverted)
}
static int s5h1409_enable_modulation(struct dvb_frontend *fe,
- fe_modulation_t m)
+ enum fe_modulation m)
{
struct s5h1409_state *state = fe->demodulator_priv;
@@ -490,7 +478,7 @@ static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe)
if (state->qam_state == QAM_STATE_QAM_OPTIMIZED_L3) {
/* We've already reached the maximum optimization level, so
- dont bother banging on the status registers */
+ don't bother banging on the status registers */
return;
}
@@ -682,17 +670,17 @@ static int s5h1409_set_mpeg_timing(struct dvb_frontend *fe, int mode)
val = s5h1409_readreg(state, 0xac) & 0xcfff;
switch (mode) {
- case S5H1409_MPEGTIMING_CONTINOUS_INVERTING_CLOCK:
+ case S5H1409_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK:
val |= 0x0000;
break;
- case S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK:
+ case S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK:
dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode);
val |= 0x1000;
break;
- case S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK:
+ case S5H1409_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK:
val |= 0x2000;
break;
- case S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK:
+ case S5H1409_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK:
val |= 0x3000;
break;
default:
@@ -755,7 +743,7 @@ static int s5h1409_init(struct dvb_frontend *fe)
return 0;
}
-static int s5h1409_read_status(struct dvb_frontend *fe, fe_status_t *status)
+static int s5h1409_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct s5h1409_state *state = fe->demodulator_priv;
u16 reg;
@@ -925,9 +913,9 @@ static int s5h1409_read_ber(struct dvb_frontend *fe, u32 *ber)
return s5h1409_read_ucblocks(fe, ber);
}
-static int s5h1409_get_frontend(struct dvb_frontend *fe)
+static int s5h1409_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct s5h1409_state *state = fe->demodulator_priv;
p->frequency = state->current_frequency;
@@ -949,7 +937,7 @@ static void s5h1409_release(struct dvb_frontend *fe)
kfree(state);
}
-static struct dvb_frontend_ops s5h1409_ops;
+static const struct dvb_frontend_ops s5h1409_ops;
struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config,
struct i2c_adapter *i2c)
@@ -993,15 +981,15 @@ error:
kfree(state);
return NULL;
}
-EXPORT_SYMBOL(s5h1409_attach);
+EXPORT_SYMBOL_GPL(s5h1409_attach);
-static struct dvb_frontend_ops s5h1409_ops = {
+static const struct dvb_frontend_ops s5h1409_ops = {
.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
.info = {
.name = "Samsung S5H1409 QAM/8VSB Frontend",
- .frequency_min = 54000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 62500,
+ .frequency_min_hz = 54 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 62500,
.caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
},
@@ -1021,9 +1009,3 @@ static struct dvb_frontend_ops s5h1409_ops = {
MODULE_DESCRIPTION("Samsung S5H1409 QAM-B/ATSC Demodulator driver");
MODULE_AUTHOR("Steven Toth");
MODULE_LICENSE("GPL");
-
-
-/*
- * Local variables:
- * c-basic-offset: 8
- */
diff --git a/drivers/media/dvb-frontends/s5h1409.h b/drivers/media/dvb-frontends/s5h1409.h
new file mode 100644
index 000000000000..f6063e43909f
--- /dev/null
+++ b/drivers/media/dvb-frontends/s5h1409.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Samsung S5H1409 VSB/QAM demodulator driver
+
+ Copyright (C) 2006 Steven Toth <stoth@linuxtv.org>
+
+
+*/
+
+#ifndef __S5H1409_H__
+#define __S5H1409_H__
+
+#include <linux/dvb/frontend.h>
+
+struct s5h1409_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* serial/parallel output */
+#define S5H1409_PARALLEL_OUTPUT 0
+#define S5H1409_SERIAL_OUTPUT 1
+ u8 output_mode;
+
+ /* GPIO Setting */
+#define S5H1409_GPIO_OFF 0
+#define S5H1409_GPIO_ON 1
+ u8 gpio;
+
+ /* IF Freq for QAM in KHz, VSB is hardcoded to 5380 */
+ u16 qam_if;
+
+ /* Spectral Inversion */
+#define S5H1409_INVERSION_OFF 0
+#define S5H1409_INVERSION_ON 1
+ u8 inversion;
+
+ /* Return lock status based on tuner lock, or demod lock */
+#define S5H1409_TUNERLOCKING 0
+#define S5H1409_DEMODLOCKING 1
+ u8 status_mode;
+
+ /* MPEG signal timing */
+#define S5H1409_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK 0
+#define S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK 1
+#define S5H1409_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK 2
+#define S5H1409_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK 3
+ u16 mpeg_timing;
+
+ /* HVR-1600 optimizations (to better work with MXL5005s)
+ Note: some of these are likely to be folded into the generic driver
+ after being regression tested with other boards */
+#define S5H1409_HVR1600_NOOPTIMIZE 0
+#define S5H1409_HVR1600_OPTIMIZE 1
+ u8 hvr1600_opt;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_S5H1409)
+extern struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *s5h1409_attach(
+ const struct s5h1409_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_S5H1409 */
+
+#endif /* __S5H1409_H__ */
diff --git a/drivers/media/dvb/frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c
index 6cc4b7a9dd60..fc48e659c2d8 100644
--- a/drivers/media/dvb/frontends/s5h1411.c
+++ b/drivers/media/dvb-frontends/s5h1411.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Samsung S5H1411 VSB/QAM demodulator driver
Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
@@ -25,7 +13,7 @@
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "s5h1411.h"
struct s5h1411_state {
@@ -37,7 +25,7 @@ struct s5h1411_state {
struct dvb_frontend frontend;
- fe_modulation_t current_modulation;
+ enum fe_modulation current_modulation;
unsigned int first_tune:1;
u32 current_frequency;
@@ -51,7 +39,7 @@ static int debug;
#define dprintk(arg...) do { \
if (debug) \
printk(arg); \
- } while (0)
+} while (0)
/* Register values to initialise the demod, defaults to VSB */
static struct init_tab {
@@ -162,7 +150,7 @@ static struct vsb_snr_tab {
{ 0x35b, 235, },
{ 0x353, 230, },
{ 0x349, 225, },
- { 0x340, 320, },
+ { 0x340, 220, },
{ 0x337, 215, },
{ 0x327, 210, },
{ 0x31b, 205, },
@@ -350,8 +338,8 @@ static int s5h1411_writereg(struct s5h1411_state *state,
ret = i2c_transfer(state->i2c, &msg, 1);
if (ret != 1)
- printk(KERN_ERR "%s: writereg error 0x%02x 0x%02x 0x%04x, "
- "ret == %i)\n", __func__, addr, reg, data, ret);
+ printk(KERN_ERR "%s: writereg error 0x%02x 0x%02x 0x%04x, ret == %i)\n",
+ __func__, addr, reg, data, ret);
return (ret != 1) ? -1 : 0;
}
@@ -410,7 +398,7 @@ static int s5h1411_set_if_freq(struct dvb_frontend *fe, int KHz)
default:
dprintk("%s(%d KHz) Invalid, defaulting to 5380\n",
__func__, KHz);
- /* no break, need to continue */
+ fallthrough;
case 5380:
case 44000:
s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x1be4);
@@ -433,17 +421,17 @@ static int s5h1411_set_mpeg_timing(struct dvb_frontend *fe, int mode)
val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xbe) & 0xcfff;
switch (mode) {
- case S5H1411_MPEGTIMING_CONTINOUS_INVERTING_CLOCK:
+ case S5H1411_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK:
val |= 0x0000;
break;
- case S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK:
+ case S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK:
dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode);
val |= 0x1000;
break;
- case S5H1411_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK:
+ case S5H1411_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK:
val |= 0x2000;
break;
- case S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK:
+ case S5H1411_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK:
val |= 0x3000;
break;
default:
@@ -484,7 +472,7 @@ static int s5h1411_set_serialmode(struct dvb_frontend *fe, int serial)
}
static int s5h1411_enable_modulation(struct dvb_frontend *fe,
- fe_modulation_t m)
+ enum fe_modulation m)
{
struct s5h1411_state *state = fe->demodulator_priv;
@@ -659,7 +647,7 @@ static int s5h1411_init(struct dvb_frontend *fe)
return 0;
}
-static int s5h1411_read_status(struct dvb_frontend *fe, fe_status_t *status)
+static int s5h1411_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct s5h1411_state *state = fe->demodulator_priv;
u16 reg;
@@ -840,9 +828,9 @@ static int s5h1411_read_ber(struct dvb_frontend *fe, u32 *ber)
return s5h1411_read_ucblocks(fe, ber);
}
-static int s5h1411_get_frontend(struct dvb_frontend *fe)
+static int s5h1411_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct s5h1411_state *state = fe->demodulator_priv;
p->frequency = state->current_frequency;
@@ -864,7 +852,7 @@ static void s5h1411_release(struct dvb_frontend *fe)
kfree(state);
}
-static struct dvb_frontend_ops s5h1411_ops;
+static const struct dvb_frontend_ops s5h1411_ops;
struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config,
struct i2c_adapter *i2c)
@@ -912,15 +900,15 @@ error:
kfree(state);
return NULL;
}
-EXPORT_SYMBOL(s5h1411_attach);
+EXPORT_SYMBOL_GPL(s5h1411_attach);
-static struct dvb_frontend_ops s5h1411_ops = {
+static const struct dvb_frontend_ops s5h1411_ops = {
.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
.info = {
.name = "Samsung S5H1411 QAM/8VSB Frontend",
- .frequency_min = 54000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 62500,
+ .frequency_min_hz = 54 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 62500,
.caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
},
@@ -944,8 +932,3 @@ MODULE_PARM_DESC(debug, "Enable verbose debug messages");
MODULE_DESCRIPTION("Samsung S5H1411 QAM-B/ATSC Demodulator driver");
MODULE_AUTHOR("Steven Toth");
MODULE_LICENSE("GPL");
-
-/*
- * Local variables:
- * c-basic-offset: 8
- */
diff --git a/drivers/media/dvb-frontends/s5h1411.h b/drivers/media/dvb-frontends/s5h1411.h
new file mode 100644
index 000000000000..939bac35c86b
--- /dev/null
+++ b/drivers/media/dvb-frontends/s5h1411.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ Samsung S5H1411 VSB/QAM demodulator driver
+
+ Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
+
+
+*/
+
+#ifndef __S5H1411_H__
+#define __S5H1411_H__
+
+#include <linux/dvb/frontend.h>
+
+#define S5H1411_I2C_TOP_ADDR (0x32 >> 1)
+#define S5H1411_I2C_QAM_ADDR (0x34 >> 1)
+
+struct s5h1411_config {
+
+ /* serial/parallel output */
+#define S5H1411_PARALLEL_OUTPUT 0
+#define S5H1411_SERIAL_OUTPUT 1
+ u8 output_mode;
+
+ /* GPIO Setting */
+#define S5H1411_GPIO_OFF 0
+#define S5H1411_GPIO_ON 1
+ u8 gpio;
+
+ /* MPEG signal timing */
+#define S5H1411_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK 0
+#define S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK 1
+#define S5H1411_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK 2
+#define S5H1411_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK 3
+ u16 mpeg_timing;
+
+ /* IF Freq for QAM and VSB in KHz */
+#define S5H1411_IF_3250 3250
+#define S5H1411_IF_3500 3500
+#define S5H1411_IF_4000 4000
+#define S5H1411_IF_5380 5380
+#define S5H1411_IF_44000 44000
+#define S5H1411_VSB_IF_DEFAULT S5H1411_IF_44000
+#define S5H1411_QAM_IF_DEFAULT S5H1411_IF_44000
+ u16 qam_if;
+ u16 vsb_if;
+
+ /* Spectral Inversion */
+#define S5H1411_INVERSION_OFF 0
+#define S5H1411_INVERSION_ON 1
+ u8 inversion;
+
+ /* Return lock status based on tuner lock, or demod lock */
+#define S5H1411_TUNERLOCKING 0
+#define S5H1411_DEMODLOCKING 1
+ u8 status_mode;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_S5H1411)
+extern struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *s5h1411_attach(
+ const struct s5h1411_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_S5H1411 */
+
+#endif /* __S5H1411_H__ */
diff --git a/drivers/media/dvb/frontends/s5h1420.c b/drivers/media/dvb-frontends/s5h1420.c
index 2322257c69ae..d700de1ea6c2 100644
--- a/drivers/media/dvb/frontends/s5h1420.c
+++ b/drivers/media/dvb-frontends/s5h1420.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for
* Samsung S5H1420 and
@@ -5,21 +6,6 @@
*
* Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net>
* Copyright (C) 2005-8 Patrick Boettcher <pb@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
@@ -34,7 +20,7 @@
#include <linux/i2c.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "s5h1420.h"
#include "s5h1420_priv.h"
@@ -52,7 +38,7 @@ struct s5h1420_state {
u8 postlocked:1;
u32 fclk;
u32 tunedfreq;
- fe_code_rate_t fec_inner;
+ enum fe_code_rate fec_inner;
u32 symbol_rate;
/* FIXME: ugly workaround for flexcop's incapable i2c-controller
@@ -124,7 +110,8 @@ static int s5h1420_writereg (struct s5h1420_state* state, u8 reg, u8 data)
return 0;
}
-static int s5h1420_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+static int s5h1420_set_voltage(struct dvb_frontend *fe,
+ enum fe_sec_voltage voltage)
{
struct s5h1420_state* state = fe->demodulator_priv;
@@ -149,7 +136,8 @@ static int s5h1420_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltag
return 0;
}
-static int s5h1420_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+static int s5h1420_set_tone(struct dvb_frontend *fe,
+ enum fe_sec_tone_mode tone)
{
struct s5h1420_state* state = fe->demodulator_priv;
@@ -180,7 +168,7 @@ static int s5h1420_send_master_cmd (struct dvb_frontend* fe,
int result = 0;
dprintk("enter %s\n", __func__);
- if (cmd->msg_len > 8)
+ if (cmd->msg_len > sizeof(cmd->msg))
return -EINVAL;
/* setup for DISEQC */
@@ -270,7 +258,8 @@ exit:
return result;
}
-static int s5h1420_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+static int s5h1420_send_burst(struct dvb_frontend *fe,
+ enum fe_sec_mini_cmd minicmd)
{
struct s5h1420_state* state = fe->demodulator_priv;
u8 val;
@@ -307,10 +296,10 @@ static int s5h1420_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicm
return result;
}
-static fe_status_t s5h1420_get_status_bits(struct s5h1420_state* state)
+static enum fe_status s5h1420_get_status_bits(struct s5h1420_state *state)
{
u8 val;
- fe_status_t status = 0;
+ enum fe_status status = 0;
val = s5h1420_readreg(state, 0x14);
if (val & 0x02)
@@ -328,7 +317,8 @@ static fe_status_t s5h1420_get_status_bits(struct s5h1420_state* state)
return status;
}
-static int s5h1420_read_status(struct dvb_frontend* fe, fe_status_t* status)
+static int s5h1420_read_status(struct dvb_frontend *fe,
+ enum fe_status *status)
{
struct s5h1420_state* state = fe->demodulator_priv;
u8 val;
@@ -561,27 +551,33 @@ static void s5h1420_setfec_inversion(struct s5h1420_state* state,
} else {
switch (p->fec_inner) {
case FEC_1_2:
- vit08 = 0x01; vit09 = 0x10;
+ vit08 = 0x01;
+ vit09 = 0x10;
break;
case FEC_2_3:
- vit08 = 0x02; vit09 = 0x11;
+ vit08 = 0x02;
+ vit09 = 0x11;
break;
case FEC_3_4:
- vit08 = 0x04; vit09 = 0x12;
+ vit08 = 0x04;
+ vit09 = 0x12;
break;
case FEC_5_6:
- vit08 = 0x08; vit09 = 0x13;
+ vit08 = 0x08;
+ vit09 = 0x13;
break;
case FEC_6_7:
- vit08 = 0x10; vit09 = 0x14;
+ vit08 = 0x10;
+ vit09 = 0x14;
break;
case FEC_7_8:
- vit08 = 0x20; vit09 = 0x15;
+ vit08 = 0x20;
+ vit09 = 0x15;
break;
default:
@@ -595,7 +591,7 @@ static void s5h1420_setfec_inversion(struct s5h1420_state* state,
dprintk("leave %s\n", __func__);
}
-static fe_code_rate_t s5h1420_getfec(struct s5h1420_state* state)
+static enum fe_code_rate s5h1420_getfec(struct s5h1420_state *state)
{
switch(s5h1420_readreg(state, 0x32) & 0x07) {
case 0:
@@ -620,7 +616,8 @@ static fe_code_rate_t s5h1420_getfec(struct s5h1420_state* state)
return FEC_NONE;
}
-static fe_spectral_inversion_t s5h1420_getinversion(struct s5h1420_state* state)
+static enum fe_spectral_inversion
+s5h1420_getinversion(struct s5h1420_state *state)
{
if (s5h1420_readreg(state, 0x32) & 0x08)
return INVERSION_ON;
@@ -634,7 +631,6 @@ static int s5h1420_set_frontend(struct dvb_frontend *fe)
struct s5h1420_state* state = fe->demodulator_priv;
int frequency_delta;
struct dvb_frontend_tune_settings fesettings;
- uint8_t clock_setting;
dprintk("enter %s\n", __func__);
@@ -679,25 +675,6 @@ static int s5h1420_set_frontend(struct dvb_frontend *fe)
else
state->fclk = 44000000;
- /* Clock */
- switch (state->fclk) {
- default:
- case 88000000:
- clock_setting = 80;
- break;
- case 86000000:
- clock_setting = 78;
- break;
- case 80000000:
- clock_setting = 72;
- break;
- case 59000000:
- clock_setting = 51;
- break;
- case 44000000:
- clock_setting = 36;
- break;
- }
dprintk("pll01: %d, ToneFreq: %d\n", state->fclk/1000000 - 8, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32));
s5h1420_writereg(state, PLL01, state->fclk/1000000 - 8);
s5h1420_writereg(state, PLL02, 0x40);
@@ -765,9 +742,9 @@ static int s5h1420_set_frontend(struct dvb_frontend *fe)
return 0;
}
-static int s5h1420_get_frontend(struct dvb_frontend* fe)
+static int s5h1420_get_frontend(struct dvb_frontend* fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct s5h1420_state* state = fe->demodulator_priv;
p->frequency = state->tunedfreq + s5h1420_getfreqoffset(state);
@@ -856,9 +833,16 @@ static u32 s5h1420_tuner_i2c_func(struct i2c_adapter *adapter)
static int s5h1420_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
{
struct s5h1420_state *state = i2c_get_adapdata(i2c_adap);
- struct i2c_msg m[1 + num];
+ struct i2c_msg m[3];
u8 tx_open[2] = { CON_1, state->CON_1_val | 1 }; /* repeater stops once there was a stop condition */
+ if (1 + num > ARRAY_SIZE(m)) {
+ printk(KERN_WARNING
+ "%s: i2c xfer: num=%d is too big!\n",
+ KBUILD_MODNAME, num);
+ return -EOPNOTSUPP;
+ }
+
memset(m, 0, sizeof(struct i2c_msg) * (1 + num));
m[0].addr = state->config->demod_address;
@@ -867,10 +851,10 @@ static int s5h1420_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c
memcpy(&m[1], msg, sizeof(struct i2c_msg) * num);
- return i2c_transfer(state->i2c, m, 1+num) == 1 + num ? num : -EIO;
+ return i2c_transfer(state->i2c, m, 1 + num) == 1 + num ? num : -EIO;
}
-static struct i2c_algorithm s5h1420_tuner_i2c_algo = {
+static const struct i2c_algorithm s5h1420_tuner_i2c_algo = {
.master_xfer = s5h1420_tuner_i2c_tuner_xfer,
.functionality = s5h1420_tuner_i2c_func,
};
@@ -882,7 +866,7 @@ struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe)
}
EXPORT_SYMBOL(s5h1420_get_tuner_i2c_adapter);
-static struct dvb_frontend_ops s5h1420_ops;
+static const struct dvb_frontend_ops s5h1420_ops;
struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config,
struct i2c_adapter *i2c)
@@ -918,7 +902,7 @@ struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config,
state->frontend.demodulator_priv = state;
/* create tuner i2c adapter */
- strlcpy(state->tuner_i2c_adapter.name, "S5H1420-PN1010 tuner I2C bus",
+ strscpy(state->tuner_i2c_adapter.name, "S5H1420-PN1010 tuner I2C bus",
sizeof(state->tuner_i2c_adapter.name));
state->tuner_i2c_adapter.algo = &s5h1420_tuner_i2c_algo;
state->tuner_i2c_adapter.algo_data = NULL;
@@ -934,16 +918,16 @@ error:
kfree(state);
return NULL;
}
-EXPORT_SYMBOL(s5h1420_attach);
+EXPORT_SYMBOL_GPL(s5h1420_attach);
-static struct dvb_frontend_ops s5h1420_ops = {
+static const struct dvb_frontend_ops s5h1420_ops = {
.delsys = { SYS_DVBS },
.info = {
.name = "Samsung S5H1420/PnpNetwork PN1010 DVB-S",
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .frequency_stepsize = 125, /* kHz for QPSK frontends */
- .frequency_tolerance = 29500,
+ .frequency_min_hz = 950 * MHz,
+ .frequency_max_hz = 2150 * MHz,
+ .frequency_stepsize_hz = 125 * kHz,
+ .frequency_tolerance_hz = 29500 * kHz,
.symbol_rate_min = 1000000,
.symbol_rate_max = 45000000,
/* .symbol_rate_tolerance = ???,*/
diff --git a/drivers/media/dvb-frontends/s5h1420.h b/drivers/media/dvb-frontends/s5h1420.h
new file mode 100644
index 000000000000..f5da808df0c1
--- /dev/null
+++ b/drivers/media/dvb-frontends/s5h1420.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for
+ * Samsung S5H1420 and
+ * PnpNetwork PN1010 QPSK Demodulator
+ *
+ * Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net>
+ * Copyright (C) 2005-8 Patrick Boettcher <pb@linuxtv.org>
+ */
+#ifndef S5H1420_H
+#define S5H1420_H
+
+#include <linux/dvb/frontend.h>
+
+struct s5h1420_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* does the inversion require inversion? */
+ u8 invert:1;
+
+ u8 repeated_start_workaround:1;
+ u8 cdclk_polarity:1; /* 1 == falling edge, 0 == raising edge */
+
+ u8 serial_mpeg:1;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_S5H1420)
+extern struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config,
+ struct i2c_adapter *i2c);
+extern struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe);
+#else
+static inline struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe)
+{
+ return NULL;
+}
+#endif // CONFIG_DVB_S5H1420
+
+#endif // S5H1420_H
diff --git a/drivers/media/dvb-frontends/s5h1420_priv.h b/drivers/media/dvb-frontends/s5h1420_priv.h
new file mode 100644
index 000000000000..1a699027a00a
--- /dev/null
+++ b/drivers/media/dvb-frontends/s5h1420_priv.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for
+ * Samsung S5H1420 and
+ * PnpNetwork PN1010 QPSK Demodulator
+ *
+ * Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net>
+ * Copyright (C) 2005 Patrick Boettcher <pb@linuxtv.org>
+ */
+#ifndef S5H1420_PRIV
+#define S5H1420_PRIV
+
+#include <asm/types.h>
+
+enum s5h1420_register {
+ ID01 = 0x00,
+ CON_0 = 0x01,
+ CON_1 = 0x02,
+ PLL01 = 0x03,
+ PLL02 = 0x04,
+ QPSK01 = 0x05,
+ QPSK02 = 0x06,
+ Pre01 = 0x07,
+ Post01 = 0x08,
+ Loop01 = 0x09,
+ Loop02 = 0x0a,
+ Loop03 = 0x0b,
+ Loop04 = 0x0c,
+ Loop05 = 0x0d,
+ Pnco01 = 0x0e,
+ Pnco02 = 0x0f,
+ Pnco03 = 0x10,
+ Tnco01 = 0x11,
+ Tnco02 = 0x12,
+ Tnco03 = 0x13,
+ Monitor01 = 0x14,
+ Monitor02 = 0x15,
+ Monitor03 = 0x16,
+ Monitor04 = 0x17,
+ Monitor05 = 0x18,
+ Monitor06 = 0x19,
+ Monitor07 = 0x1a,
+ Monitor12 = 0x1f,
+
+ FEC01 = 0x22,
+ Soft01 = 0x23,
+ Soft02 = 0x24,
+ Soft03 = 0x25,
+ Soft04 = 0x26,
+ Soft05 = 0x27,
+ Soft06 = 0x28,
+ Vit01 = 0x29,
+ Vit02 = 0x2a,
+ Vit03 = 0x2b,
+ Vit04 = 0x2c,
+ Vit05 = 0x2d,
+ Vit06 = 0x2e,
+ Vit07 = 0x2f,
+ Vit08 = 0x30,
+ Vit09 = 0x31,
+ Vit10 = 0x32,
+ Vit11 = 0x33,
+ Vit12 = 0x34,
+ Sync01 = 0x35,
+ Sync02 = 0x36,
+ Rs01 = 0x37,
+ Mpeg01 = 0x38,
+ Mpeg02 = 0x39,
+ DiS01 = 0x3a,
+ DiS02 = 0x3b,
+ DiS03 = 0x3c,
+ DiS04 = 0x3d,
+ DiS05 = 0x3e,
+ DiS06 = 0x3f,
+ DiS07 = 0x40,
+ DiS08 = 0x41,
+ DiS09 = 0x42,
+ DiS10 = 0x43,
+ DiS11 = 0x44,
+ Rf01 = 0x45,
+ Err01 = 0x46,
+ Err02 = 0x47,
+ Err03 = 0x48,
+ Err04 = 0x49,
+};
+
+
+#endif
diff --git a/drivers/media/dvb/frontends/s5h1432.c b/drivers/media/dvb-frontends/s5h1432.c
index 8352ce1c9556..ff5d3bdf3bc6 100644
--- a/drivers/media/dvb/frontends/s5h1432.c
+++ b/drivers/media/dvb-frontends/s5h1432.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Samsung s5h1432 DVB-T demodulator driver
*
* Copyright (C) 2009 Bill Liu <Bill.Liu@Conexant.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
@@ -24,7 +11,7 @@
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "s5h1432.h"
struct s5h1432_state {
@@ -36,7 +23,7 @@ struct s5h1432_state {
struct dvb_frontend frontend;
- fe_modulation_t current_modulation;
+ enum fe_modulation current_modulation;
unsigned int first_tune:1;
u32 current_frequency;
@@ -63,8 +50,8 @@ static int s5h1432_writereg(struct s5h1432_state *state,
ret = i2c_transfer(state->i2c, &msg, 1);
if (ret != 1)
- printk(KERN_ERR "%s: writereg error 0x%02x 0x%02x 0x%04x, "
- "ret == %i)\n", __func__, addr, reg, data, ret);
+ printk(KERN_ERR "%s: writereg error 0x%02x 0x%02x 0x%04x, ret == %i)\n",
+ __func__, addr, reg, data, ret);
return (ret != 1) ? -1 : 0;
}
@@ -302,7 +289,7 @@ static int s5h1432_init(struct dvb_frontend *fe)
return 0;
}
-static int s5h1432_read_status(struct dvb_frontend *fe, fe_status_t *status)
+static int s5h1432_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
return 0;
}
@@ -341,7 +328,7 @@ static void s5h1432_release(struct dvb_frontend *fe)
kfree(state);
}
-static struct dvb_frontend_ops s5h1432_ops;
+static const struct dvb_frontend_ops s5h1432_ops;
struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config,
struct i2c_adapter *i2c)
@@ -351,8 +338,8 @@ struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config,
printk(KERN_INFO " Enter s5h1432_attach(). attach success!\n");
/* allocate memory for the internal state */
state = kmalloc(sizeof(struct s5h1432_state), GFP_KERNEL);
- if (state == NULL)
- goto error;
+ if (!state)
+ return NULL;
/* setup the state */
state->config = config;
@@ -367,20 +354,16 @@ struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config,
state->frontend.demodulator_priv = state;
return &state->frontend;
-
-error:
- kfree(state);
- return NULL;
}
-EXPORT_SYMBOL(s5h1432_attach);
+EXPORT_SYMBOL_GPL(s5h1432_attach);
-static struct dvb_frontend_ops s5h1432_ops = {
+static const struct dvb_frontend_ops s5h1432_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = "Samsung s5h1432 DVB-T Frontend",
- .frequency_min = 177000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 166666,
+ .frequency_min_hz = 177 * MHz,
+ .frequency_max_hz = 858 * MHz,
+ .frequency_stepsize_hz = 166666,
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
diff --git a/drivers/media/dvb-frontends/s5h1432.h b/drivers/media/dvb-frontends/s5h1432.h
new file mode 100644
index 000000000000..f031c0654e47
--- /dev/null
+++ b/drivers/media/dvb-frontends/s5h1432.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Samsung s5h1432 VSB/QAM demodulator driver
+ *
+ * Copyright (C) 2009 Bill Liu <Bill.Liu@Conexant.com>
+ */
+
+#ifndef __S5H1432_H__
+#define __S5H1432_H__
+
+#include <linux/dvb/frontend.h>
+
+#define S5H1432_I2C_TOP_ADDR (0x02 >> 1)
+
+#define TAIWAN_HI_IF_FREQ_44_MHZ 44000000
+#define EUROPE_HI_IF_FREQ_36_MHZ 36000000
+#define IF_FREQ_6_MHZ 6000000
+#define IF_FREQ_3point3_MHZ 3300000
+#define IF_FREQ_3point5_MHZ 3500000
+#define IF_FREQ_4_MHZ 4000000
+
+struct s5h1432_config {
+
+ /* serial/parallel output */
+#define S5H1432_PARALLEL_OUTPUT 0
+#define S5H1432_SERIAL_OUTPUT 1
+ u8 output_mode;
+
+ /* GPIO Setting */
+#define S5H1432_GPIO_OFF 0
+#define S5H1432_GPIO_ON 1
+ u8 gpio;
+
+ /* MPEG signal timing */
+#define S5H1432_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK 0
+#define S5H1432_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK 1
+#define S5H1432_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK 2
+#define S5H1432_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK 3
+ u16 mpeg_timing;
+
+ /* IF Freq for QAM and VSB in KHz */
+#define S5H1432_IF_3250 3250
+#define S5H1432_IF_3500 3500
+#define S5H1432_IF_4000 4000
+#define S5H1432_IF_5380 5380
+#define S5H1432_IF_44000 44000
+#define S5H1432_VSB_IF_DEFAULT s5h1432_IF_44000
+#define S5H1432_QAM_IF_DEFAULT s5h1432_IF_44000
+ u16 qam_if;
+ u16 vsb_if;
+
+ /* Spectral Inversion */
+#define S5H1432_INVERSION_OFF 0
+#define S5H1432_INVERSION_ON 1
+ u8 inversion;
+
+ /* Return lock status based on tuner lock, or demod lock */
+#define S5H1432_TUNERLOCKING 0
+#define S5H1432_DEMODLOCKING 1
+ u8 status_mode;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_S5H1432)
+extern struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *s5h1432_attach(const struct s5h1432_config
+ *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_s5h1432 */
+
+#endif /* __s5h1432_H__ */
diff --git a/drivers/media/dvb/frontends/s921.c b/drivers/media/dvb-frontends/s921.c
index cd2288c07147..7e461ac159fc 100644
--- a/drivers/media/dvb/frontends/s921.c
+++ b/drivers/media/dvb-frontends/s921.c
@@ -1,8 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Sharp VA3A5JZ921 One Seg Broadcast Module driver
* This device is labeled as just S. 921 at the top of the frontend can
*
- * Copyright (C) 2009-2010 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Copyright (C) 2009-2010 Mauro Carvalho Chehab
* Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com>
*
* Developed for Leadership SBTVD 1seg device sold in Brazil
@@ -11,21 +12,12 @@
* the old s921 driver.
*
* FIXME: Need to port to DVB v5.2 API
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/kernel.h>
#include <asm/div64.h>
-#include "dvb_frontend.h"
+#include <media/dvb_frontend.h>
#include "s921.h"
static int debug = 1;
@@ -214,8 +206,8 @@ static int s921_i2c_writereg(struct s921_state *state,
rc = i2c_transfer(state->i2c, &msg, 1);
if (rc != 1) {
- printk("%s: writereg rcor(rc == %i, reg == 0x%02x,"
- " data == 0x%02x)\n", __func__, rc, reg, data);
+ printk("%s: writereg rcor(rc == %i, reg == 0x%02x, data == 0x%02x)\n",
+ __func__, rc, reg, data);
return rc;
}
@@ -348,7 +340,7 @@ static int s921_initfe(struct dvb_frontend *fe)
return 0;
}
-static int s921_read_status(struct dvb_frontend *fe, fe_status_t *status)
+static int s921_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct s921_state *state = fe->demodulator_priv;
int regstatus, rc;
@@ -389,7 +381,7 @@ static int s921_read_status(struct dvb_frontend *fe, fe_status_t *status)
static int s921_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
{
- fe_status_t status;
+ enum fe_status status;
struct s921_state *state = fe->demodulator_priv;
int rc;
@@ -433,9 +425,9 @@ static int s921_set_frontend(struct dvb_frontend *fe)
return 0;
}
-static int s921_get_frontend(struct dvb_frontend *fe)
+static int s921_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct s921_state *state = fe->demodulator_priv;
/* FIXME: Probably it is possible to get it from regs f1 and f2 */
@@ -449,7 +441,7 @@ static int s921_tune(struct dvb_frontend *fe,
bool re_tune,
unsigned int mode_flags,
unsigned int *delay,
- fe_status_t *status)
+ enum fe_status *status)
{
int rc = 0;
@@ -464,9 +456,9 @@ static int s921_tune(struct dvb_frontend *fe,
return rc;
}
-static int s921_get_algo(struct dvb_frontend *fe)
+static enum dvbfe_algo s921_get_algo(struct dvb_frontend *fe)
{
- return 1; /* FE_ALGO_HW */
+ return DVBFE_ALGO_HW;
}
static void s921_release(struct dvb_frontend *fe)
@@ -477,7 +469,7 @@ static void s921_release(struct dvb_frontend *fe)
kfree(state);
}
-static struct dvb_frontend_ops s921_ops;
+static const struct dvb_frontend_ops s921_ops;
struct dvb_frontend *s921_attach(const struct s921_config *config,
struct i2c_adapter *i2c)
@@ -487,9 +479,9 @@ struct dvb_frontend *s921_attach(const struct s921_config *config,
kzalloc(sizeof(struct s921_state), GFP_KERNEL);
dprintk("\n");
- if (state == NULL) {
+ if (!state) {
rc("Unable to kzalloc\n");
- goto rcor;
+ return NULL;
}
/* setup the state */
@@ -502,28 +494,22 @@ struct dvb_frontend *s921_attach(const struct s921_config *config,
state->frontend.demodulator_priv = state;
return &state->frontend;
-
-rcor:
- kfree(state);
-
- return NULL;
}
-EXPORT_SYMBOL(s921_attach);
+EXPORT_SYMBOL_GPL(s921_attach);
-static struct dvb_frontend_ops s921_ops = {
+static const struct dvb_frontend_ops s921_ops = {
.delsys = { SYS_ISDBT },
/* Use dib8000 values per default */
.info = {
.name = "Sharp S921",
- .frequency_min = 470000000,
+ .frequency_min_hz = 470 * MHz,
/*
* Max should be 770MHz instead, according with Sharp docs,
* but Leadership doc says it works up to 806 MHz. This is
* required to get channel 69, used in Brazil
*/
- .frequency_max = 806000000,
- .frequency_tolerance = 0,
- .caps = FE_CAN_INVERSION_AUTO |
+ .frequency_max_hz = 806 * MHz,
+ .caps = FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
@@ -544,6 +530,6 @@ static struct dvb_frontend_ops s921_ops = {
};
MODULE_DESCRIPTION("DVB Frontend module for Sharp S921 hardware");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_AUTHOR("Douglas Landgraf <dougsland@redhat.com>");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/s921.h b/drivers/media/dvb-frontends/s921.h
new file mode 100644
index 000000000000..436fc7921486
--- /dev/null
+++ b/drivers/media/dvb-frontends/s921.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Sharp s921 driver
+ *
+ * Copyright (C) 2009 Mauro Carvalho Chehab
+ * Copyright (C) 2009 Douglas Landgraf <dougsland@redhat.com>
+ */
+
+#ifndef S921_H
+#define S921_H
+
+#include <linux/dvb/frontend.h>
+
+struct s921_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_S921)
+extern struct dvb_frontend *s921_attach(const struct s921_config *config,
+ struct i2c_adapter *i2c);
+extern struct i2c_adapter *s921_get_tuner_i2c_adapter(struct dvb_frontend *);
+#else
+static inline struct dvb_frontend *s921_attach(
+ const struct s921_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+static inline struct i2c_adapter *
+ s921_get_tuner_i2c_adapter(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* S921_H */
diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c
new file mode 100644
index 000000000000..f87c9357cee3
--- /dev/null
+++ b/drivers/media/dvb-frontends/si2165.c
@@ -0,0 +1,1303 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Silicon Labs Si2161 DVB-T and Si2165 DVB-C/-T Demodulator
+ *
+ * Copyright (C) 2013-2017 Matthias Schwarzott <zzam@gentoo.org>
+ *
+ * References:
+ * https://www.silabs.com/Support%20Documents/TechnicalDocs/Si2165-short.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+
+#include <media/dvb_frontend.h>
+#include <linux/int_log.h>
+#include "si2165_priv.h"
+#include "si2165.h"
+
+/*
+ * Hauppauge WinTV-HVR-930C-HD B130 / PCTV QuatroStick 521e 1113xx
+ * uses 16 MHz xtal
+ *
+ * Hauppauge WinTV-HVR-930C-HD B131 / PCTV QuatroStick 522e 1114xx
+ * uses 24 MHz clock provided by tuner
+ */
+
+struct si2165_state {
+ struct i2c_client *client;
+
+ struct regmap *regmap;
+
+ struct dvb_frontend fe;
+
+ struct si2165_config config;
+
+ u8 chip_revcode;
+ u8 chip_type;
+
+ /* calculated by xtal and div settings */
+ u32 fvco_hz;
+ u32 sys_clk;
+ u32 adc_clk;
+
+ /* DVBv3 stats */
+ u64 ber_prev;
+
+ bool has_dvbc;
+ bool has_dvbt;
+ bool firmware_loaded;
+};
+
+static int si2165_write(struct si2165_state *state, const u16 reg,
+ const u8 *src, const int count)
+{
+ int ret;
+
+ dev_dbg(&state->client->dev, "i2c write: reg: 0x%04x, data: %*ph\n",
+ reg, count, src);
+
+ ret = regmap_bulk_write(state->regmap, reg, src, count);
+
+ if (ret)
+ dev_err(&state->client->dev, "%s: ret == %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int si2165_read(struct si2165_state *state,
+ const u16 reg, u8 *val, const int count)
+{
+ int ret = regmap_bulk_read(state->regmap, reg, val, count);
+
+ if (ret) {
+ dev_err(&state->client->dev, "%s: error (addr %02x reg %04x error (ret == %i)\n",
+ __func__, state->config.i2c_addr, reg, ret);
+ return ret;
+ }
+
+ dev_dbg(&state->client->dev, "i2c read: reg: 0x%04x, data: %*ph\n",
+ reg, count, val);
+
+ return 0;
+}
+
+static int si2165_readreg8(struct si2165_state *state,
+ const u16 reg, u8 *val)
+{
+ unsigned int val_tmp;
+ int ret = regmap_read(state->regmap, reg, &val_tmp);
+ *val = (u8)val_tmp;
+ dev_dbg(&state->client->dev, "reg read: R(0x%04x)=0x%02x\n", reg, *val);
+ return ret;
+}
+
+static int si2165_readreg16(struct si2165_state *state,
+ const u16 reg, u16 *val)
+{
+ u8 buf[2];
+
+ int ret = si2165_read(state, reg, buf, 2);
+ *val = buf[0] | buf[1] << 8;
+ dev_dbg(&state->client->dev, "reg read: R(0x%04x)=0x%04x\n", reg, *val);
+ return ret;
+}
+
+static int si2165_readreg24(struct si2165_state *state,
+ const u16 reg, u32 *val)
+{
+ u8 buf[3];
+
+ int ret = si2165_read(state, reg, buf, 3);
+ *val = buf[0] | buf[1] << 8 | buf[2] << 16;
+ dev_dbg(&state->client->dev, "reg read: R(0x%04x)=0x%06x\n", reg, *val);
+ return ret;
+}
+
+static int si2165_writereg8(struct si2165_state *state, const u16 reg, u8 val)
+{
+ return regmap_write(state->regmap, reg, val);
+}
+
+static int si2165_writereg16(struct si2165_state *state, const u16 reg, u16 val)
+{
+ u8 buf[2] = { val & 0xff, (val >> 8) & 0xff };
+
+ return si2165_write(state, reg, buf, 2);
+}
+
+static int si2165_writereg24(struct si2165_state *state, const u16 reg, u32 val)
+{
+ u8 buf[3] = { val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff };
+
+ return si2165_write(state, reg, buf, 3);
+}
+
+static int si2165_writereg32(struct si2165_state *state, const u16 reg, u32 val)
+{
+ u8 buf[4] = {
+ val & 0xff,
+ (val >> 8) & 0xff,
+ (val >> 16) & 0xff,
+ (val >> 24) & 0xff
+ };
+ return si2165_write(state, reg, buf, 4);
+}
+
+static int si2165_writereg_mask8(struct si2165_state *state, const u16 reg,
+ u8 val, u8 mask)
+{
+ if (mask != 0xff) {
+ u8 tmp;
+ int ret = si2165_readreg8(state, reg, &tmp);
+
+ if (ret < 0)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+ return si2165_writereg8(state, reg, val);
+}
+
+#define REG16(reg, val) \
+ { (reg), (val) & 0xff }, \
+ { (reg) + 1, (val) >> 8 & 0xff }
+struct si2165_reg_value_pair {
+ u16 reg;
+ u8 val;
+};
+
+static int si2165_write_reg_list(struct si2165_state *state,
+ const struct si2165_reg_value_pair *regs,
+ int count)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < count; i++) {
+ ret = si2165_writereg8(state, regs[i].reg, regs[i].val);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int si2165_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 1000;
+ return 0;
+}
+
+static int si2165_init_pll(struct si2165_state *state)
+{
+ u32 ref_freq_hz = state->config.ref_freq_hz;
+ u8 divr = 1; /* 1..7 */
+ u8 divp = 1; /* only 1 or 4 */
+ u8 divn = 56; /* 1..63 */
+ u8 divm = 8;
+ u8 divl = 12;
+ u8 buf[4];
+
+ /*
+ * hardcoded values can be deleted if calculation is verified
+ * or it yields the same values as the windows driver
+ */
+ switch (ref_freq_hz) {
+ case 16000000u:
+ divn = 56;
+ break;
+ case 24000000u:
+ divr = 2;
+ divp = 4;
+ divn = 19;
+ break;
+ default:
+ /* ref_freq / divr must be between 4 and 16 MHz */
+ if (ref_freq_hz > 16000000u)
+ divr = 2;
+
+ /*
+ * now select divn and divp such that
+ * fvco is in 1624..1824 MHz
+ */
+ if (1624000000u * divr > ref_freq_hz * 2u * 63u)
+ divp = 4;
+
+ /* is this already correct regarding rounding? */
+ divn = 1624000000u * divr / (ref_freq_hz * 2u * divp);
+ break;
+ }
+
+ /* adc_clk and sys_clk depend on xtal and pll settings */
+ state->fvco_hz = ref_freq_hz / divr
+ * 2u * divn * divp;
+ state->adc_clk = state->fvco_hz / (divm * 4u);
+ state->sys_clk = state->fvco_hz / (divl * 2u);
+
+ /* write all 4 pll registers 0x00a0..0x00a3 at once */
+ buf[0] = divl;
+ buf[1] = divm;
+ buf[2] = (divn & 0x3f) | ((divp == 1) ? 0x40 : 0x00) | 0x80;
+ buf[3] = divr;
+ return si2165_write(state, REG_PLL_DIVL, buf, 4);
+}
+
+static int si2165_adjust_pll_divl(struct si2165_state *state, u8 divl)
+{
+ state->sys_clk = state->fvco_hz / (divl * 2u);
+ return si2165_writereg8(state, REG_PLL_DIVL, divl);
+}
+
+static u32 si2165_get_fe_clk(struct si2165_state *state)
+{
+ /* assume Oversampling mode Ovr4 is used */
+ return state->adc_clk;
+}
+
+static int si2165_wait_init_done(struct si2165_state *state)
+{
+ int ret;
+ u8 val = 0;
+ int i;
+
+ for (i = 0; i < 3; ++i) {
+ ret = si2165_readreg8(state, REG_INIT_DONE, &val);
+ if (ret < 0)
+ return ret;
+ if (val == 0x01)
+ return 0;
+ usleep_range(1000, 50000);
+ }
+ dev_err(&state->client->dev, "init_done was not set\n");
+ return -EINVAL;
+}
+
+static int si2165_upload_firmware_block(struct si2165_state *state,
+ const u8 *data, u32 len, u32 *poffset,
+ u32 block_count)
+{
+ int ret;
+ u8 buf_ctrl[4] = { 0x00, 0x00, 0x00, 0xc0 };
+ u8 wordcount;
+ u32 cur_block = 0;
+ u32 offset = poffset ? *poffset : 0;
+
+ if (len < 4)
+ return -EINVAL;
+ if (len % 4 != 0)
+ return -EINVAL;
+
+ dev_dbg(&state->client->dev,
+ "fw load: %s: called with len=0x%x offset=0x%x blockcount=0x%x\n",
+ __func__, len, offset, block_count);
+ while (offset + 12 <= len && cur_block < block_count) {
+ dev_dbg(&state->client->dev,
+ "fw load: %s: in while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n",
+ __func__, len, offset, cur_block, block_count);
+ wordcount = data[offset];
+ if (wordcount < 1 || data[offset + 1] ||
+ data[offset + 2] || data[offset + 3]) {
+ dev_warn(&state->client->dev,
+ "bad fw data[0..3] = %*ph\n",
+ 4, data);
+ return -EINVAL;
+ }
+
+ if (offset + 8 + wordcount * 4 > len) {
+ dev_warn(&state->client->dev,
+ "len is too small for block len=%d, wordcount=%d\n",
+ len, wordcount);
+ return -EINVAL;
+ }
+
+ buf_ctrl[0] = wordcount - 1;
+
+ ret = si2165_write(state, REG_DCOM_CONTROL_BYTE, buf_ctrl, 4);
+ if (ret < 0)
+ goto error;
+ ret = si2165_write(state, REG_DCOM_ADDR, data + offset + 4, 4);
+ if (ret < 0)
+ goto error;
+
+ offset += 8;
+
+ while (wordcount > 0) {
+ ret = si2165_write(state, REG_DCOM_DATA,
+ data + offset, 4);
+ if (ret < 0)
+ goto error;
+ wordcount--;
+ offset += 4;
+ }
+ cur_block++;
+ }
+
+ dev_dbg(&state->client->dev,
+ "fw load: %s: after while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n",
+ __func__, len, offset, cur_block, block_count);
+
+ if (poffset)
+ *poffset = offset;
+
+ dev_dbg(&state->client->dev,
+ "fw load: %s: returned offset=0x%x\n",
+ __func__, offset);
+
+ return 0;
+error:
+ return ret;
+}
+
+static int si2165_upload_firmware(struct si2165_state *state)
+{
+ /* int ret; */
+ u8 val[3];
+ u16 val16;
+ int ret;
+
+ const struct firmware *fw = NULL;
+ u8 *fw_file;
+ const u8 *data;
+ u32 len;
+ u32 offset;
+ u8 patch_version;
+ u8 block_count;
+ u16 crc_expected;
+
+ switch (state->chip_revcode) {
+ case 0x03: /* revision D */
+ fw_file = SI2165_FIRMWARE_REV_D;
+ break;
+ default:
+ dev_info(&state->client->dev, "no firmware file for revision=%d\n",
+ state->chip_revcode);
+ return 0;
+ }
+
+ /* request the firmware, this will block and timeout */
+ ret = request_firmware(&fw, fw_file, &state->client->dev);
+ if (ret) {
+ dev_warn(&state->client->dev, "firmware file '%s' not found\n",
+ fw_file);
+ goto error;
+ }
+
+ data = fw->data;
+ len = fw->size;
+
+ dev_info(&state->client->dev, "downloading firmware from file '%s' size=%d\n",
+ fw_file, len);
+
+ if (len % 4 != 0) {
+ dev_warn(&state->client->dev, "firmware size is not multiple of 4\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* check header (8 bytes) */
+ if (len < 8) {
+ dev_warn(&state->client->dev, "firmware header is missing\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (data[0] != 1 || data[1] != 0) {
+ dev_warn(&state->client->dev, "firmware file version is wrong\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ patch_version = data[2];
+ block_count = data[4];
+ crc_expected = data[7] << 8 | data[6];
+
+ /* start uploading fw */
+ /* boot/wdog status */
+ ret = si2165_writereg8(state, REG_WDOG_AND_BOOT, 0x00);
+ if (ret < 0)
+ goto error;
+ /* reset */
+ ret = si2165_writereg8(state, REG_RST_ALL, 0x00);
+ if (ret < 0)
+ goto error;
+ /* boot/wdog status */
+ ret = si2165_readreg8(state, REG_WDOG_AND_BOOT, val);
+ if (ret < 0)
+ goto error;
+
+ /* enable reset on error */
+ ret = si2165_readreg8(state, REG_EN_RST_ERROR, val);
+ if (ret < 0)
+ goto error;
+ ret = si2165_readreg8(state, REG_EN_RST_ERROR, val);
+ if (ret < 0)
+ goto error;
+ ret = si2165_writereg8(state, REG_EN_RST_ERROR, 0x02);
+ if (ret < 0)
+ goto error;
+
+ /* start right after the header */
+ offset = 8;
+
+ dev_info(&state->client->dev, "%s: extracted patch_version=0x%02x, block_count=0x%02x, crc_expected=0x%04x\n",
+ __func__, patch_version, block_count, crc_expected);
+
+ ret = si2165_upload_firmware_block(state, data, len, &offset, 1);
+ if (ret < 0)
+ goto error;
+
+ ret = si2165_writereg8(state, REG_PATCH_VERSION, patch_version);
+ if (ret < 0)
+ goto error;
+
+ /* reset crc */
+ ret = si2165_writereg8(state, REG_RST_CRC, 0x01);
+ if (ret)
+ goto error;
+
+ ret = si2165_upload_firmware_block(state, data, len,
+ &offset, block_count);
+ if (ret < 0) {
+ dev_err(&state->client->dev,
+ "firmware could not be uploaded\n");
+ goto error;
+ }
+
+ /* read crc */
+ ret = si2165_readreg16(state, REG_CRC, &val16);
+ if (ret)
+ goto error;
+
+ if (val16 != crc_expected) {
+ dev_err(&state->client->dev,
+ "firmware crc mismatch %04x != %04x\n",
+ val16, crc_expected);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = si2165_upload_firmware_block(state, data, len, &offset, 5);
+ if (ret)
+ goto error;
+
+ if (len != offset) {
+ dev_err(&state->client->dev,
+ "firmware len mismatch %04x != %04x\n",
+ len, offset);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* reset watchdog error register */
+ ret = si2165_writereg_mask8(state, REG_WDOG_AND_BOOT, 0x02, 0x02);
+ if (ret < 0)
+ goto error;
+
+ /* enable reset on error */
+ ret = si2165_writereg_mask8(state, REG_EN_RST_ERROR, 0x01, 0x01);
+ if (ret < 0)
+ goto error;
+
+ dev_info(&state->client->dev, "fw load finished\n");
+