From 3785bc170f79ef04129731582b468c28e1326d6d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 14 Jun 2012 16:35:58 -0300 Subject: [media] b2c2: break it into common/pci/usb directories b2c2 is, in fact, 2 drivers: one for PCI and one for USB, plus a common bus-independent code. Break it accordingly. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/common/Makefile') diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index e3ec9639321b..d0512d7e5555 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,6 +1,6 @@ saa7146-objs := saa7146_i2c.o saa7146_core.o saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o -obj-y += tuners/ +obj-y += tuners/ b2c2/ obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o -- cgit v1.2.3 From ccae7af2bf07dfef69cc2eb6ebc9e1ff15addfbd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 14 Jun 2012 16:35:59 -0300 Subject: [media] common: move media/common/tuners to media/tuners Move the tuners one level up, as the "common" directory will be used by drivers that are shared between more than one driver. Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 4 +- drivers/media/Kconfig | 2 +- drivers/media/Makefile | 2 +- drivers/media/common/Makefile | 2 +- drivers/media/common/b2c2/Makefile | 2 +- drivers/media/common/tuners/Kconfig | 243 -- drivers/media/common/tuners/Makefile | 37 - drivers/media/common/tuners/fc0011.c | 524 --- drivers/media/common/tuners/fc0011.h | 41 - drivers/media/common/tuners/fc0012-priv.h | 43 - drivers/media/common/tuners/fc0012.c | 467 --- drivers/media/common/tuners/fc0012.h | 44 - drivers/media/common/tuners/fc0013-priv.h | 44 - drivers/media/common/tuners/fc0013.c | 634 ---- drivers/media/common/tuners/fc0013.h | 57 - drivers/media/common/tuners/fc001x-common.h | 39 - drivers/media/common/tuners/max2165.c | 433 --- drivers/media/common/tuners/max2165.h | 48 - drivers/media/common/tuners/max2165_priv.h | 60 - drivers/media/common/tuners/mc44s803.c | 372 -- drivers/media/common/tuners/mc44s803.h | 46 - drivers/media/common/tuners/mc44s803_priv.h | 208 -- drivers/media/common/tuners/mt2060.c | 403 --- drivers/media/common/tuners/mt2060.h | 43 - drivers/media/common/tuners/mt2060_priv.h | 104 - drivers/media/common/tuners/mt2063.c | 2307 ------------ drivers/media/common/tuners/mt2063.h | 32 - drivers/media/common/tuners/mt20xx.c | 670 ---- drivers/media/common/tuners/mt20xx.h | 37 - drivers/media/common/tuners/mt2131.c | 301 -- drivers/media/common/tuners/mt2131.h | 54 - drivers/media/common/tuners/mt2131_priv.h | 48 - drivers/media/common/tuners/mt2266.c | 353 -- drivers/media/common/tuners/mt2266.h | 37 - drivers/media/common/tuners/mxl5005s.c | 4109 ---------------------- drivers/media/common/tuners/mxl5005s.h | 135 - drivers/media/common/tuners/mxl5007t.c | 928 ----- drivers/media/common/tuners/mxl5007t.h | 104 - drivers/media/common/tuners/qt1010.c | 480 --- drivers/media/common/tuners/qt1010.h | 53 - drivers/media/common/tuners/qt1010_priv.h | 104 - drivers/media/common/tuners/tda18212.c | 319 -- drivers/media/common/tuners/tda18212.h | 52 - drivers/media/common/tuners/tda18218.c | 342 -- drivers/media/common/tuners/tda18218.h | 45 - drivers/media/common/tuners/tda18218_priv.h | 108 - drivers/media/common/tuners/tda18271-common.c | 703 ---- drivers/media/common/tuners/tda18271-fe.c | 1345 ------- drivers/media/common/tuners/tda18271-maps.c | 1317 ------- drivers/media/common/tuners/tda18271-priv.h | 236 -- drivers/media/common/tuners/tda18271.h | 134 - drivers/media/common/tuners/tda827x.c | 917 ----- drivers/media/common/tuners/tda827x.h | 68 - drivers/media/common/tuners/tda8290.c | 874 ----- drivers/media/common/tuners/tda8290.h | 56 - drivers/media/common/tuners/tda9887.c | 717 ---- drivers/media/common/tuners/tda9887.h | 38 - drivers/media/common/tuners/tea5761.c | 348 -- drivers/media/common/tuners/tea5761.h | 47 - drivers/media/common/tuners/tea5767.c | 475 --- drivers/media/common/tuners/tea5767.h | 66 - drivers/media/common/tuners/tua9001.c | 215 -- drivers/media/common/tuners/tua9001.h | 46 - drivers/media/common/tuners/tua9001_priv.h | 34 - drivers/media/common/tuners/tuner-i2c.h | 182 - drivers/media/common/tuners/tuner-simple.c | 1155 ------ drivers/media/common/tuners/tuner-simple.h | 39 - drivers/media/common/tuners/tuner-types.c | 1883 ---------- drivers/media/common/tuners/tuner-xc2028-types.h | 141 - drivers/media/common/tuners/tuner-xc2028.c | 1509 -------- drivers/media/common/tuners/tuner-xc2028.h | 72 - drivers/media/common/tuners/xc4000.c | 1757 --------- drivers/media/common/tuners/xc4000.h | 67 - drivers/media/common/tuners/xc5000.c | 1366 ------- drivers/media/common/tuners/xc5000.h | 74 - drivers/media/dvb-frontends/Makefile | 2 +- drivers/media/pci/bt8xx/Makefile | 2 +- drivers/media/pci/ddbridge/Makefile | 2 +- drivers/media/pci/ngene/Makefile | 2 +- drivers/media/pci/ttpci/Makefile | 2 +- drivers/media/tuners/Kconfig | 243 ++ drivers/media/tuners/Makefile | 37 + drivers/media/tuners/fc0011.c | 524 +++ drivers/media/tuners/fc0011.h | 41 + drivers/media/tuners/fc0012-priv.h | 43 + drivers/media/tuners/fc0012.c | 467 +++ drivers/media/tuners/fc0012.h | 44 + drivers/media/tuners/fc0013-priv.h | 44 + drivers/media/tuners/fc0013.c | 634 ++++ drivers/media/tuners/fc0013.h | 57 + drivers/media/tuners/fc001x-common.h | 39 + drivers/media/tuners/max2165.c | 433 +++ drivers/media/tuners/max2165.h | 48 + drivers/media/tuners/max2165_priv.h | 60 + drivers/media/tuners/mc44s803.c | 372 ++ drivers/media/tuners/mc44s803.h | 46 + drivers/media/tuners/mc44s803_priv.h | 208 ++ drivers/media/tuners/mt2060.c | 403 +++ drivers/media/tuners/mt2060.h | 43 + drivers/media/tuners/mt2060_priv.h | 104 + drivers/media/tuners/mt2063.c | 2307 ++++++++++++ drivers/media/tuners/mt2063.h | 32 + drivers/media/tuners/mt20xx.c | 670 ++++ drivers/media/tuners/mt20xx.h | 37 + drivers/media/tuners/mt2131.c | 301 ++ drivers/media/tuners/mt2131.h | 54 + drivers/media/tuners/mt2131_priv.h | 48 + drivers/media/tuners/mt2266.c | 353 ++ drivers/media/tuners/mt2266.h | 37 + drivers/media/tuners/mxl5005s.c | 4109 ++++++++++++++++++++++ drivers/media/tuners/mxl5005s.h | 135 + drivers/media/tuners/mxl5007t.c | 928 +++++ drivers/media/tuners/mxl5007t.h | 104 + drivers/media/tuners/qt1010.c | 480 +++ drivers/media/tuners/qt1010.h | 53 + drivers/media/tuners/qt1010_priv.h | 104 + drivers/media/tuners/tda18212.c | 319 ++ drivers/media/tuners/tda18212.h | 52 + drivers/media/tuners/tda18218.c | 342 ++ drivers/media/tuners/tda18218.h | 45 + drivers/media/tuners/tda18218_priv.h | 108 + drivers/media/tuners/tda18271-common.c | 703 ++++ drivers/media/tuners/tda18271-fe.c | 1345 +++++++ drivers/media/tuners/tda18271-maps.c | 1317 +++++++ drivers/media/tuners/tda18271-priv.h | 236 ++ drivers/media/tuners/tda18271.h | 134 + drivers/media/tuners/tda827x.c | 917 +++++ drivers/media/tuners/tda827x.h | 68 + drivers/media/tuners/tda8290.c | 874 +++++ drivers/media/tuners/tda8290.h | 56 + drivers/media/tuners/tda9887.c | 717 ++++ drivers/media/tuners/tda9887.h | 38 + drivers/media/tuners/tea5761.c | 348 ++ drivers/media/tuners/tea5761.h | 47 + drivers/media/tuners/tea5767.c | 475 +++ drivers/media/tuners/tea5767.h | 66 + drivers/media/tuners/tua9001.c | 215 ++ drivers/media/tuners/tua9001.h | 46 + drivers/media/tuners/tua9001_priv.h | 34 + drivers/media/tuners/tuner-i2c.h | 182 + drivers/media/tuners/tuner-simple.c | 1155 ++++++ drivers/media/tuners/tuner-simple.h | 39 + drivers/media/tuners/tuner-types.c | 1883 ++++++++++ drivers/media/tuners/tuner-xc2028-types.h | 141 + drivers/media/tuners/tuner-xc2028.c | 1509 ++++++++ drivers/media/tuners/tuner-xc2028.h | 72 + drivers/media/tuners/xc4000.c | 1757 +++++++++ drivers/media/tuners/xc4000.h | 67 + drivers/media/tuners/xc5000.c | 1366 +++++++ drivers/media/tuners/xc5000.h | 74 + drivers/media/usb/b2c2/Makefile | 2 +- drivers/media/usb/dvb-usb-v2/Makefile | 2 +- drivers/media/usb/dvb-usb/Makefile | 2 +- drivers/media/v4l2-core/Makefile | 2 +- drivers/media/video/Makefile | 2 +- drivers/media/video/au0828/Makefile | 2 +- drivers/media/video/bt8xx/Makefile | 2 +- drivers/media/video/cx18/Makefile | 2 +- drivers/media/video/cx231xx/Makefile | 2 +- drivers/media/video/cx23885/Makefile | 2 +- drivers/media/video/cx25821/Makefile | 2 +- drivers/media/video/cx88/Makefile | 2 +- drivers/media/video/em28xx/Makefile | 2 +- drivers/media/video/ivtv/Makefile | 2 +- drivers/media/video/pvrusb2/Makefile | 2 +- drivers/media/video/saa7134/Makefile | 2 +- drivers/media/video/saa7164/Makefile | 2 +- drivers/media/video/tlg2300/Makefile | 2 +- drivers/media/video/tm6000/Makefile | 2 +- drivers/media/video/usbvision/Makefile | 2 +- drivers/staging/media/cxd2099/Makefile | 2 +- 171 files changed, 30421 insertions(+), 30421 deletions(-) delete mode 100644 drivers/media/common/tuners/Kconfig delete mode 100644 drivers/media/common/tuners/Makefile delete mode 100644 drivers/media/common/tuners/fc0011.c delete mode 100644 drivers/media/common/tuners/fc0011.h delete mode 100644 drivers/media/common/tuners/fc0012-priv.h delete mode 100644 drivers/media/common/tuners/fc0012.c delete mode 100644 drivers/media/common/tuners/fc0012.h delete mode 100644 drivers/media/common/tuners/fc0013-priv.h delete mode 100644 drivers/media/common/tuners/fc0013.c delete mode 100644 drivers/media/common/tuners/fc0013.h delete mode 100644 drivers/media/common/tuners/fc001x-common.h delete mode 100644 drivers/media/common/tuners/max2165.c delete mode 100644 drivers/media/common/tuners/max2165.h delete mode 100644 drivers/media/common/tuners/max2165_priv.h delete mode 100644 drivers/media/common/tuners/mc44s803.c delete mode 100644 drivers/media/common/tuners/mc44s803.h delete mode 100644 drivers/media/common/tuners/mc44s803_priv.h delete mode 100644 drivers/media/common/tuners/mt2060.c delete mode 100644 drivers/media/common/tuners/mt2060.h delete mode 100644 drivers/media/common/tuners/mt2060_priv.h delete mode 100644 drivers/media/common/tuners/mt2063.c delete mode 100644 drivers/media/common/tuners/mt2063.h delete mode 100644 drivers/media/common/tuners/mt20xx.c delete mode 100644 drivers/media/common/tuners/mt20xx.h delete mode 100644 drivers/media/common/tuners/mt2131.c delete mode 100644 drivers/media/common/tuners/mt2131.h delete mode 100644 drivers/media/common/tuners/mt2131_priv.h delete mode 100644 drivers/media/common/tuners/mt2266.c delete mode 100644 drivers/media/common/tuners/mt2266.h delete mode 100644 drivers/media/common/tuners/mxl5005s.c delete mode 100644 drivers/media/common/tuners/mxl5005s.h delete mode 100644 drivers/media/common/tuners/mxl5007t.c delete mode 100644 drivers/media/common/tuners/mxl5007t.h delete mode 100644 drivers/media/common/tuners/qt1010.c delete mode 100644 drivers/media/common/tuners/qt1010.h delete mode 100644 drivers/media/common/tuners/qt1010_priv.h delete mode 100644 drivers/media/common/tuners/tda18212.c delete mode 100644 drivers/media/common/tuners/tda18212.h delete mode 100644 drivers/media/common/tuners/tda18218.c delete mode 100644 drivers/media/common/tuners/tda18218.h delete mode 100644 drivers/media/common/tuners/tda18218_priv.h delete mode 100644 drivers/media/common/tuners/tda18271-common.c delete mode 100644 drivers/media/common/tuners/tda18271-fe.c delete mode 100644 drivers/media/common/tuners/tda18271-maps.c delete mode 100644 drivers/media/common/tuners/tda18271-priv.h delete mode 100644 drivers/media/common/tuners/tda18271.h delete mode 100644 drivers/media/common/tuners/tda827x.c delete mode 100644 drivers/media/common/tuners/tda827x.h delete mode 100644 drivers/media/common/tuners/tda8290.c delete mode 100644 drivers/media/common/tuners/tda8290.h delete mode 100644 drivers/media/common/tuners/tda9887.c delete mode 100644 drivers/media/common/tuners/tda9887.h delete mode 100644 drivers/media/common/tuners/tea5761.c delete mode 100644 drivers/media/common/tuners/tea5761.h delete mode 100644 drivers/media/common/tuners/tea5767.c delete mode 100644 drivers/media/common/tuners/tea5767.h delete mode 100644 drivers/media/common/tuners/tua9001.c delete mode 100644 drivers/media/common/tuners/tua9001.h delete mode 100644 drivers/media/common/tuners/tua9001_priv.h delete mode 100644 drivers/media/common/tuners/tuner-i2c.h delete mode 100644 drivers/media/common/tuners/tuner-simple.c delete mode 100644 drivers/media/common/tuners/tuner-simple.h delete mode 100644 drivers/media/common/tuners/tuner-types.c delete mode 100644 drivers/media/common/tuners/tuner-xc2028-types.h delete mode 100644 drivers/media/common/tuners/tuner-xc2028.c delete mode 100644 drivers/media/common/tuners/tuner-xc2028.h delete mode 100644 drivers/media/common/tuners/xc4000.c delete mode 100644 drivers/media/common/tuners/xc4000.h delete mode 100644 drivers/media/common/tuners/xc5000.c delete mode 100644 drivers/media/common/tuners/xc5000.h create mode 100644 drivers/media/tuners/Kconfig create mode 100644 drivers/media/tuners/Makefile create mode 100644 drivers/media/tuners/fc0011.c create mode 100644 drivers/media/tuners/fc0011.h create mode 100644 drivers/media/tuners/fc0012-priv.h create mode 100644 drivers/media/tuners/fc0012.c create mode 100644 drivers/media/tuners/fc0012.h create mode 100644 drivers/media/tuners/fc0013-priv.h create mode 100644 drivers/media/tuners/fc0013.c create mode 100644 drivers/media/tuners/fc0013.h create mode 100644 drivers/media/tuners/fc001x-common.h create mode 100644 drivers/media/tuners/max2165.c create mode 100644 drivers/media/tuners/max2165.h create mode 100644 drivers/media/tuners/max2165_priv.h create mode 100644 drivers/media/tuners/mc44s803.c create mode 100644 drivers/media/tuners/mc44s803.h create mode 100644 drivers/media/tuners/mc44s803_priv.h create mode 100644 drivers/media/tuners/mt2060.c create mode 100644 drivers/media/tuners/mt2060.h create mode 100644 drivers/media/tuners/mt2060_priv.h create mode 100644 drivers/media/tuners/mt2063.c create mode 100644 drivers/media/tuners/mt2063.h create mode 100644 drivers/media/tuners/mt20xx.c create mode 100644 drivers/media/tuners/mt20xx.h create mode 100644 drivers/media/tuners/mt2131.c create mode 100644 drivers/media/tuners/mt2131.h create mode 100644 drivers/media/tuners/mt2131_priv.h create mode 100644 drivers/media/tuners/mt2266.c create mode 100644 drivers/media/tuners/mt2266.h create mode 100644 drivers/media/tuners/mxl5005s.c create mode 100644 drivers/media/tuners/mxl5005s.h create mode 100644 drivers/media/tuners/mxl5007t.c create mode 100644 drivers/media/tuners/mxl5007t.h create mode 100644 drivers/media/tuners/qt1010.c create mode 100644 drivers/media/tuners/qt1010.h create mode 100644 drivers/media/tuners/qt1010_priv.h create mode 100644 drivers/media/tuners/tda18212.c create mode 100644 drivers/media/tuners/tda18212.h create mode 100644 drivers/media/tuners/tda18218.c create mode 100644 drivers/media/tuners/tda18218.h create mode 100644 drivers/media/tuners/tda18218_priv.h create mode 100644 drivers/media/tuners/tda18271-common.c create mode 100644 drivers/media/tuners/tda18271-fe.c create mode 100644 drivers/media/tuners/tda18271-maps.c create mode 100644 drivers/media/tuners/tda18271-priv.h create mode 100644 drivers/media/tuners/tda18271.h create mode 100644 drivers/media/tuners/tda827x.c create mode 100644 drivers/media/tuners/tda827x.h create mode 100644 drivers/media/tuners/tda8290.c create mode 100644 drivers/media/tuners/tda8290.h create mode 100644 drivers/media/tuners/tda9887.c create mode 100644 drivers/media/tuners/tda9887.h create mode 100644 drivers/media/tuners/tea5761.c create mode 100644 drivers/media/tuners/tea5761.h create mode 100644 drivers/media/tuners/tea5767.c create mode 100644 drivers/media/tuners/tea5767.h create mode 100644 drivers/media/tuners/tua9001.c create mode 100644 drivers/media/tuners/tua9001.h create mode 100644 drivers/media/tuners/tua9001_priv.h create mode 100644 drivers/media/tuners/tuner-i2c.h create mode 100644 drivers/media/tuners/tuner-simple.c create mode 100644 drivers/media/tuners/tuner-simple.h create mode 100644 drivers/media/tuners/tuner-types.c create mode 100644 drivers/media/tuners/tuner-xc2028-types.h create mode 100644 drivers/media/tuners/tuner-xc2028.c create mode 100644 drivers/media/tuners/tuner-xc2028.h create mode 100644 drivers/media/tuners/xc4000.c create mode 100644 drivers/media/tuners/xc4000.h create mode 100644 drivers/media/tuners/xc5000.c create mode 100644 drivers/media/tuners/xc5000.h (limited to 'drivers/media/common/Makefile') diff --git a/MAINTAINERS b/MAINTAINERS index 94b823f71e94..ceb5b55b2af8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2773,8 +2773,8 @@ FC0011 TUNER DRIVER M: Michael Buesch L: linux-media@vger.kernel.org S: Maintained -F: drivers/media/common/tuners/fc0011.h -F: drivers/media/common/tuners/fc0011.c +F: drivers/media/tuners/fc0011.h +F: drivers/media/tuners/fc0011.c FANOTIFY M: Eric Paris diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 6343e84b361a..aae6fec3b9e1 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -145,7 +145,7 @@ source "drivers/media/rc/Kconfig" # Tuner drivers for DVB and V4L # -source "drivers/media/common/tuners/Kconfig" +source "drivers/media/tuners/Kconfig" # # Video/Radio/Hybrid adapters diff --git a/drivers/media/Makefile b/drivers/media/Makefile index 90ec998a8e6a..f89ccace746f 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -8,7 +8,7 @@ ifeq ($(CONFIG_MEDIA_CONTROLLER),y) obj-$(CONFIG_MEDIA_SUPPORT) += media.o endif -obj-y += v4l2-core/ common/ rc/ video/ +obj-y += v4l2-core/ tuners/ common/ rc/ video/ obj-$(CONFIG_VIDEO_DEV) += radio/ obj-$(CONFIG_DVB_CORE) += dvb-core/ pci/ dvb-frontends/ usb/ diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index d0512d7e5555..a471242c46a7 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,6 +1,6 @@ saa7146-objs := saa7146_i2c.o saa7146_core.o saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o -obj-y += tuners/ b2c2/ +obj-y += b2c2/ obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o diff --git a/drivers/media/common/b2c2/Makefile b/drivers/media/common/b2c2/Makefile index 377d051548a9..48a4c906a173 100644 --- a/drivers/media/common/b2c2/Makefile +++ b/drivers/media/common/b2c2/Makefile @@ -4,4 +4,4 @@ obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o ccflags-y += -Idrivers/media/dvb-core/ ccflags-y += -Idrivers/media/dvb-frontends/ -ccflags-y += -Idrivers/media/common/tuners/ +ccflags-y += -Idrivers/media/tuners/ diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig deleted file mode 100644 index 94c6ff7a5da3..000000000000 --- a/drivers/media/common/tuners/Kconfig +++ /dev/null @@ -1,243 +0,0 @@ -config MEDIA_ATTACH - bool "Load and attach frontend and tuner driver modules as needed" - depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT - depends on MODULES - default y if !EXPERT - 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 - depends on (MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT) && I2C - default y - 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 && MEDIA_RADIO_SUPPORT && EXPERIMENTAL - select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMISE && MEDIA_RADIO_SUPPORT - 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 - depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT - -config MEDIA_TUNER_SIMPLE - tristate "Simple tuner support" - depends on MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE - help - NXP TDA18218 silicon tuner driver. - -config MEDIA_TUNER_FC0011 - tristate "Fitipower FC0011 silicon tuner" - depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE - help - Fitipower FC0011 silicon tuner driver. - -config MEDIA_TUNER_FC0012 - tristate "Fitipower FC0012 silicon tuner" - depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE - help - Fitipower FC0012 silicon tuner driver. - -config MEDIA_TUNER_FC0013 - tristate "Fitipower FC0013 silicon tuner" - depends on MEDIA_SUPPORT && I2C - default m if MEDIA_TUNER_CUSTOMISE - help - Fitipower FC0013 silicon tuner driver. - -config MEDIA_TUNER_TDA18212 - tristate "NXP TDA18212 silicon tuner" - depends on MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 112aeee90202..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-core -ccflags-y += -I$(srctree)/drivers/media/dvb-frontends diff --git a/drivers/media/common/tuners/fc0011.c b/drivers/media/common/tuners/fc0011.c deleted file mode 100644 index e4882546c283..000000000000 --- a/drivers/media/common/tuners/fc0011.c +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Fitipower FC0011 tuner driver - * - * Copyright (C) 2012 Michael Buesch - * - * Derived from FC0012 tuner driver: - * Copyright (C) 2012 Hans-Frieder Vogt - * - * 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 "fc0011.h" - - -/* Tuner registers */ -enum { - FC11_REG_0, - FC11_REG_FA, /* FA */ - FC11_REG_FP, /* FP */ - FC11_REG_XINHI, /* XIN high 8 bit */ - FC11_REG_XINLO, /* XIN low 8 bit */ - FC11_REG_VCO, /* VCO */ - FC11_REG_VCOSEL, /* VCO select */ - FC11_REG_7, /* Unknown tuner reg 7 */ - FC11_REG_8, /* Unknown tuner reg 8 */ - FC11_REG_9, - FC11_REG_10, /* Unknown tuner reg 10 */ - FC11_REG_11, /* Unknown tuner reg 11 */ - FC11_REG_12, - FC11_REG_RCCAL, /* RC calibrate */ - FC11_REG_VCOCAL, /* VCO calibrate */ - FC11_REG_15, - FC11_REG_16, /* Unknown tuner reg 16 */ - FC11_REG_17, - - FC11_NR_REGS, /* Number of registers */ -}; - -enum FC11_REG_VCOSEL_bits { - FC11_VCOSEL_2 = 0x08, /* VCO select 2 */ - FC11_VCOSEL_1 = 0x10, /* VCO select 1 */ - FC11_VCOSEL_CLKOUT = 0x20, /* Fix clock out */ - FC11_VCOSEL_BW7M = 0x40, /* 7MHz bw */ - FC11_VCOSEL_BW6M = 0x80, /* 6MHz bw */ -}; - -enum FC11_REG_RCCAL_bits { - FC11_RCCAL_FORCE = 0x10, /* force */ -}; - -enum FC11_REG_VCOCAL_bits { - FC11_VCOCAL_RUN = 0, /* VCO calibration run */ - FC11_VCOCAL_VALUEMASK = 0x3F, /* VCO calibration value mask */ - FC11_VCOCAL_OK = 0x40, /* VCO calibration Ok */ - FC11_VCOCAL_RESET = 0x80, /* VCO calibration reset */ -}; - - -struct fc0011_priv { - struct i2c_adapter *i2c; - u8 addr; - - u32 frequency; - u32 bandwidth; -}; - - -static int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val) -{ - u8 buf[2] = { reg, val }; - struct i2c_msg msg = { .addr = priv->addr, - .flags = 0, .buf = buf, .len = 2 }; - - if (i2c_transfer(priv->i2c, &msg, 1) != 1) { - dev_err(&priv->i2c->dev, - "I2C write reg failed, reg: %02x, val: %02x\n", - reg, val); - return -EIO; - } - - return 0; -} - -static int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val) -{ - u8 dummy; - struct i2c_msg msg[2] = { - { .addr = priv->addr, - .flags = 0, .buf = ®, .len = 1 }, - { .addr = priv->addr, - .flags = I2C_M_RD, .buf = val ? : &dummy, .len = 1 }, - }; - - if (i2c_transfer(priv->i2c, msg, 2) != 2) { - dev_err(&priv->i2c->dev, - "I2C read failed, reg: %02x\n", reg); - return -EIO; - } - - return 0; -} - -static int fc0011_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - - return 0; -} - -static int fc0011_init(struct dvb_frontend *fe) -{ - struct fc0011_priv *priv = fe->tuner_priv; - int err; - - if (WARN_ON(!fe->callback)) - return -EINVAL; - - err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, - FC0011_FE_CALLBACK_POWER, priv->addr); - if (err) { - dev_err(&priv->i2c->dev, "Power-on callback failed\n"); - return err; - } - err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, - FC0011_FE_CALLBACK_RESET, priv->addr); - if (err) { - dev_err(&priv->i2c->dev, "Reset callback failed\n"); - return err; - } - - return 0; -} - -/* Initiate VCO calibration */ -static int fc0011_vcocal_trigger(struct fc0011_priv *priv) -{ - int err; - - err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET); - if (err) - return err; - err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); - if (err) - return err; - - return 0; -} - -/* Read VCO calibration value */ -static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value) -{ - int err; - - err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); - if (err) - return err; - usleep_range(10000, 20000); - err = fc0011_readreg(priv, FC11_REG_VCOCAL, value); - if (err) - return err; - - return 0; -} - -static int fc0011_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct fc0011_priv *priv = fe->tuner_priv; - int err; - unsigned int i, vco_retries; - u32 freq = p->frequency / 1000; - u32 bandwidth = p->bandwidth_hz / 1000; - u32 fvco, xin, xdiv, xdivr; - u16 frac; - u8 fa, fp, vco_sel, vco_cal; - u8 regs[FC11_NR_REGS] = { }; - - regs[FC11_REG_7] = 0x0F; - regs[FC11_REG_8] = 0x3E; - regs[FC11_REG_10] = 0xB8; - regs[FC11_REG_11] = 0x80; - regs[FC11_REG_RCCAL] = 0x04; - err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); - err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); - err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); - err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); - err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); - if (err) - return -EIO; - - /* Set VCO freq and VCO div */ - if (freq < 54000) { - fvco = freq * 64; - regs[FC11_REG_VCO] = 0x82; - } else if (freq < 108000) { - fvco = freq * 32; - regs[FC11_REG_VCO] = 0x42; - } else if (freq < 216000) { - fvco = freq * 16; - regs[FC11_REG_VCO] = 0x22; - } else if (freq < 432000) { - fvco = freq * 8; - regs[FC11_REG_VCO] = 0x12; - } else { - fvco = freq * 4; - regs[FC11_REG_VCO] = 0x0A; - } - - /* Calc XIN. The PLL reference frequency is 18 MHz. */ - xdiv = fvco / 18000; - frac = fvco - xdiv * 18000; - frac = (frac << 15) / 18000; - if (frac >= 16384) - frac += 32786; - if (!frac) - xin = 0; - else if (frac < 511) - xin = 512; - else if (frac < 65026) - xin = frac; - else - xin = 65024; - regs[FC11_REG_XINHI] = xin >> 8; - regs[FC11_REG_XINLO] = xin; - - /* Calc FP and FA */ - xdivr = xdiv; - if (fvco - xdiv * 18000 >= 9000) - xdivr += 1; /* round */ - fp = xdivr / 8; - fa = xdivr - fp * 8; - if (fa < 2) { - fp -= 1; - fa += 8; - } - if (fp > 0x1F) { - fp &= 0x1F; - fa &= 0xF; - } - if (fa >= fp) { - dev_warn(&priv->i2c->dev, - "fa %02X >= fp %02X, but trying to continue\n", - (unsigned int)(u8)fa, (unsigned int)(u8)fp); - } - regs[FC11_REG_FA] = fa; - regs[FC11_REG_FP] = fp; - - /* Select bandwidth */ - switch (bandwidth) { - case 8000: - break; - case 7000: - regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M; - break; - default: - dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. " - "Using 6000 kHz.\n", - bandwidth); - bandwidth = 6000; - /* fallthrough */ - case 6000: - regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M; - break; - } - - /* Pre VCO select */ - if (fvco < 2320000) { - vco_sel = 0; - regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); - } else if (fvco < 3080000) { - vco_sel = 1; - regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); - regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; - } else { - vco_sel = 2; - regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); - regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; - } - - /* Fix for low freqs */ - if (freq < 45000) { - regs[FC11_REG_FA] = 0x6; - regs[FC11_REG_FP] = 0x11; - } - - /* Clock out fix */ - regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT; - - /* Write the cached registers */ - for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) { - err = fc0011_writereg(priv, i, regs[i]); - if (err) - return err; - } - - /* VCO calibration */ - err = fc0011_vcocal_trigger(priv); - if (err) - return err; - err = fc0011_vcocal_read(priv, &vco_cal); - if (err) - return err; - vco_retries = 0; - while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 3) { - /* Reset the tuner and try again */ - err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, - FC0011_FE_CALLBACK_RESET, priv->addr); - if (err) { - dev_err(&priv->i2c->dev, "Failed to reset tuner\n"); - return err; - } - /* Reinit tuner config */ - err = 0; - for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) - err |= fc0011_writereg(priv, i, regs[i]); - err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); - err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); - err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); - err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); - err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); - if (err) - return -EIO; - /* VCO calibration */ - err = fc0011_vcocal_trigger(priv); - if (err) - return err; - err = fc0011_vcocal_read(priv, &vco_cal); - if (err) - return err; - vco_retries++; - } - if (!(vco_cal & FC11_VCOCAL_OK)) { - dev_err(&priv->i2c->dev, - "Failed to read VCO calibration value (got %02X)\n", - (unsigned int)vco_cal); - return -EIO; - } - vco_cal &= FC11_VCOCAL_VALUEMASK; - - switch (vco_sel) { - case 0: - if (vco_cal < 8) { - regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); - regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; - err = fc0011_writereg(priv, FC11_REG_VCOSEL, - regs[FC11_REG_VCOSEL]); - if (err) - return err; - err = fc0011_vcocal_trigger(priv); - if (err) - return err; - } else { - regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); - err = fc0011_writereg(priv, FC11_REG_VCOSEL, - regs[FC11_REG_VCOSEL]); - if (err) - return err; - } - break; - case 1: - if (vco_cal < 5) { - regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); - regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; - err = fc0011_writereg(priv, FC11_REG_VCOSEL, - regs[FC11_REG_VCOSEL]); - if (err) - return err; - err = fc0011_vcocal_trigger(priv); - if (err) - return err; - } else if (vco_cal <= 48) { - regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); - regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; - err = fc0011_writereg(priv, FC11_REG_VCOSEL, - regs[FC11_REG_VCOSEL]); - if (err) - return err; - } else { - regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); - err = fc0011_writereg(priv, FC11_REG_VCOSEL, - regs[FC11_REG_VCOSEL]); - if (err) - return err; - err = fc0011_vcocal_trigger(priv); - if (err) - return err; - } - break; - case 2: - if (vco_cal > 53) { - regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); - regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; - err = fc0011_writereg(priv, FC11_REG_VCOSEL, - regs[FC11_REG_VCOSEL]); - if (err) - return err; - err = fc0011_vcocal_trigger(priv); - if (err) - return err; - } else { - regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); - regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; - err = fc0011_writereg(priv, FC11_REG_VCOSEL, - regs[FC11_REG_VCOSEL]); - if (err) - return err; - } - break; - } - err = fc0011_vcocal_read(priv, NULL); - if (err) - return err; - usleep_range(10000, 50000); - - err = fc0011_readreg(priv, FC11_REG_RCCAL, ®s[FC11_REG_RCCAL]); - if (err) - return err; - regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE; - err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); - if (err) - return err; - err = fc0011_writereg(priv, FC11_REG_16, 0xB); - if (err) - return err; - - dev_dbg(&priv->i2c->dev, "Tuned to " - "fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X " - "vcocal=%02X(%u) bw=%u\n", - (unsigned int)regs[FC11_REG_FA], - (unsigned int)regs[FC11_REG_FP], - (unsigned int)regs[FC11_REG_XINHI], - (unsigned int)regs[FC11_REG_XINLO], - (unsigned int)regs[FC11_REG_VCO], - (unsigned int)regs[FC11_REG_VCOSEL], - (unsigned int)vco_cal, vco_retries, - (unsigned int)bandwidth); - - priv->frequency = p->frequency; - priv->bandwidth = p->bandwidth_hz; - - return 0; -} - -static int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct fc0011_priv *priv = fe->tuner_priv; - - *frequency = priv->frequency; - - return 0; -} - -static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - *frequency = 0; - - return 0; -} - -static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) -{ - struct fc0011_priv *priv = fe->tuner_priv; - - *bandwidth = priv->bandwidth; - - return 0; -} - -static const struct dvb_tuner_ops fc0011_tuner_ops = { - .info = { - .name = "Fitipower FC0011", - - .frequency_min = 45000000, - .frequency_max = 1000000000, - }, - - .release = fc0011_release, - .init = fc0011_init, - - .set_params = fc0011_set_params, - - .get_frequency = fc0011_get_frequency, - .get_if_frequency = fc0011_get_if_frequency, - .get_bandwidth = fc0011_get_bandwidth, -}; - -struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - const struct fc0011_config *config) -{ - struct fc0011_priv *priv; - - priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL); - if (!priv) - return NULL; - - priv->i2c = i2c; - priv->addr = config->i2c_address; - - fe->tuner_priv = priv; - fe->ops.tuner_ops = fc0011_tuner_ops; - - dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n"); - - return fe; -} -EXPORT_SYMBOL(fc0011_attach); - -MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver"); -MODULE_AUTHOR("Michael Buesch "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/tuners/fc0011.h b/drivers/media/common/tuners/fc0011.h deleted file mode 100644 index 0ee581f122d2..000000000000 --- a/drivers/media/common/tuners/fc0011.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef LINUX_FC0011_H_ -#define LINUX_FC0011_H_ - -#include "dvb_frontend.h" - - -/** struct fc0011_config - fc0011 hardware config - * - * @i2c_address: I2C bus address. - */ -struct fc0011_config { - u8 i2c_address; -}; - -/** enum fc0011_fe_callback_commands - Frontend callbacks - * - * @FC0011_FE_CALLBACK_POWER: Power on tuner hardware. - * @FC0011_FE_CALLBACK_RESET: Request a tuner reset. - */ -enum fc0011_fe_callback_commands { - FC0011_FE_CALLBACK_POWER, - FC0011_FE_CALLBACK_RESET, -}; - -#if defined(CONFIG_MEDIA_TUNER_FC0011) ||\ - defined(CONFIG_MEDIA_TUNER_FC0011_MODULE) -struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - const struct fc0011_config *config); -#else -static inline -struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - const struct fc0011_config *config) -{ - dev_err(&i2c->dev, "fc0011 driver disabled in Kconfig\n"); - return NULL; -} -#endif - -#endif /* LINUX_FC0011_H_ */ 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 - * - * 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.c b/drivers/media/common/tuners/fc0012.c deleted file mode 100644 index 308135abd54c..000000000000 --- a/drivers/media/common/tuners/fc0012.c +++ /dev/null @@ -1,467 +0,0 @@ -/* - * Fitipower FC0012 tuner driver - * - * Copyright (C) 2012 Hans-Frieder Vogt - * - * 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 "fc0012.h" -#include "fc0012-priv.h" - -static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val) -{ - u8 buf[2] = {reg, val}; - struct i2c_msg msg = { - .addr = priv->addr, .flags = 0, .buf = buf, .len = 2 - }; - - if (i2c_transfer(priv->i2c, &msg, 1) != 1) { - err("I2C write reg failed, reg: %02x, val: %02x", reg, val); - return -EREMOTEIO; - } - return 0; -} - -static int fc0012_readreg(struct fc0012_priv *priv, u8 reg, u8 *val) -{ - struct i2c_msg msg[2] = { - { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 }, - { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 }, - }; - - if (i2c_transfer(priv->i2c, msg, 2) != 2) { - err("I2C read reg failed, reg: %02x", reg); - return -EREMOTEIO; - } - return 0; -} - -static int fc0012_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - return 0; -} - -static int fc0012_init(struct dvb_frontend *fe) -{ - struct fc0012_priv *priv = fe->tuner_priv; - int i, ret = 0; - unsigned char reg[] = { - 0x00, /* dummy reg. 0 */ - 0x05, /* reg. 0x01 */ - 0x10, /* reg. 0x02 */ - 0x00, /* reg. 0x03 */ - 0x00, /* reg. 0x04 */ - 0x0f, /* reg. 0x05: may also be 0x0a */ - 0x00, /* reg. 0x06: divider 2, VCO slow */ - 0x00, /* reg. 0x07: may also be 0x0f */ - 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, - Loop Bw 1/8 */ - 0x6e, /* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */ - 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ - 0x82, /* reg. 0x0b: Output Clock is same as clock frequency, - may also be 0x83 */ - 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ - 0x02, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, 0x02 for DVB-T */ - 0x00, /* reg. 0x0e */ - 0x00, /* reg. 0x0f */ - 0x00, /* reg. 0x10: may also be 0x0d */ - 0x00, /* reg. 0x11 */ - 0x1f, /* reg. 0x12: Set to maximum gain */ - 0x08, /* reg. 0x13: Set to Middle Gain: 0x08, - Low Gain: 0x00, High Gain: 0x10, enable IX2: 0x80 */ - 0x00, /* reg. 0x14 */ - 0x04, /* reg. 0x15: Enable LNA COMPS */ - }; - - switch (priv->xtal_freq) { - case FC_XTAL_27_MHZ: - case FC_XTAL_28_8_MHZ: - reg[0x07] |= 0x20; - break; - case FC_XTAL_36_MHZ: - default: - break; - } - - if (priv->dual_master) - reg[0x0c] |= 0x02; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ - - for (i = 1; i < sizeof(reg); i++) { - ret = fc0012_writereg(priv, i, reg[i]); - if (ret) - break; - } - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - - if (ret) - err("fc0012_writereg failed: %d", ret); - - return ret; -} - -static int fc0012_sleep(struct dvb_frontend *fe) -{ - /* nothing to do here */ - return 0; -} - -static int fc0012_set_params(struct dvb_frontend *fe) -{ - struct fc0012_priv *priv = fe->tuner_priv; - int i, ret = 0; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - u32 freq = p->frequency / 1000; - u32 delsys = p->delivery_system; - unsigned char reg[7], am, pm, multi, tmp; - unsigned long f_vco; - unsigned short xtal_freq_khz_2, xin, xdiv; - int vco_select = false; - - if (fe->callback) { - ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, - FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1)); - if (ret) - goto exit; - } - - switch (priv->xtal_freq) { - case FC_XTAL_27_MHZ: - xtal_freq_khz_2 = 27000 / 2; - break; - case FC_XTAL_36_MHZ: - xtal_freq_khz_2 = 36000 / 2; - break; - case FC_XTAL_28_8_MHZ: - default: - xtal_freq_khz_2 = 28800 / 2; - break; - } - - /* select frequency divider and the frequency of VCO */ - if (freq < 37084) { /* freq * 96 < 3560000 */ - multi = 96; - reg[5] = 0x82; - reg[6] = 0x00; - } else if (freq < 55625) { /* freq * 64 < 3560000 */ - multi = 64; - reg[5] = 0x82; - reg[6] = 0x02; - } else if (freq < 74167) { /* freq * 48 < 3560000 */ - multi = 48; - reg[5] = 0x42; - reg[6] = 0x00; - } else if (freq < 111250) { /* freq * 32 < 3560000 */ - multi = 32; - reg[5] = 0x42; - reg[6] = 0x02; - } else if (freq < 148334) { /* freq * 24 < 3560000 */ - multi = 24; - reg[5] = 0x22; - reg[6] = 0x00; - } else if (freq < 222500) { /* freq * 16 < 3560000 */ - multi = 16; - reg[5] = 0x22; - reg[6] = 0x02; - } else if (freq < 296667) { /* freq * 12 < 3560000 */ - multi = 12; - reg[5] = 0x12; - reg[6] = 0x00; - } else if (freq < 445000) { /* freq * 8 < 3560000 */ - multi = 8; - reg[5] = 0x12; - reg[6] = 0x02; - } else if (freq < 593334) { /* freq * 6 < 3560000 */ - multi = 6; - reg[5] = 0x0a; - reg[6] = 0x00; - } else { - multi = 4; - reg[5] = 0x0a; - reg[6] = 0x02; - } - - f_vco = freq * multi; - - if (f_vco >= 3060000) { - reg[6] |= 0x08; - vco_select = true; - } - - if (freq >= 45000) { - /* From divided value (XDIV) determined the FA and FP value */ - xdiv = (unsigned short)(f_vco / xtal_freq_khz_2); - if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2)) - xdiv++; - - pm = (unsigned char)(xdiv / 8); - am = (unsigned char)(xdiv - (8 * pm)); - - if (am < 2) { - reg[1] = am + 8; - reg[2] = pm - 1; - } else { - reg[1] = am; - reg[2] = pm; - } - } else { - /* fix for frequency less than 45 MHz */ - reg[1] = 0x06; - reg[2] = 0x11; - } - - /* fix clock out */ - reg[6] |= 0x20; - - /* From VCO frequency determines the XIN ( fractional part of Delta - Sigma PLL) and divided value (XDIV) */ - xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2); - xin = (xin << 15) / xtal_freq_khz_2; - if (xin >= 16384) - xin += 32768; - - reg[3] = xin >> 8; /* xin with 9 bit resolution */ - reg[4] = xin & 0xff; - - if (delsys == SYS_DVBT) { - reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ - switch (p->bandwidth_hz) { - case 6000000: - reg[6] |= 0x80; - break; - case 7000000: - reg[6] |= 0x40; - break; - case 8000000: - default: - break; - } - } else { - err("%s: modulation type not supported!", __func__); - return -EINVAL; - } - - /* modified for Realtek demod */ - reg[5] |= 0x07; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ - - for (i = 1; i <= 6; i++) { - ret = fc0012_writereg(priv, i, reg[i]); - if (ret) - goto exit; - } - - /* VCO Calibration */ - ret = fc0012_writereg(priv, 0x0e, 0x80); - if (!ret) - ret = fc0012_writereg(priv, 0x0e, 0x00); - - /* VCO Re-Calibration if needed */ - if (!ret) - ret = fc0012_writereg(priv, 0x0e, 0x00); - - if (!ret) { - msleep(10); - ret = fc0012_readreg(priv, 0x0e, &tmp); - } - if (ret) - goto exit; - - /* vco selection */ - tmp &= 0x3f; - - if (vco_select) { - if (tmp > 0x3c) { - reg[6] &= ~0x08; - ret = fc0012_writereg(priv, 0x06, reg[6]); - if (!ret) - ret = fc0012_writereg(priv, 0x0e, 0x80); - if (!ret) - ret = fc0012_writereg(priv, 0x0e, 0x00); - } - } else { - if (tmp < 0x02) { - reg[6] |= 0x08; - ret = fc0012_writereg(priv, 0x06, reg[6]); - if (!ret) - ret = fc0012_writereg(priv, 0x0e, 0x80); - if (!ret) - ret = fc0012_writereg(priv, 0x0e, 0x00); - } - } - - priv->frequency = p->frequency; - priv->bandwidth = p->bandwidth_hz; - -exit: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - if (ret) - warn("%s: failed: %d", __func__, ret); - return ret; -} - -static int fc0012_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct fc0012_priv *priv = fe->tuner_priv; - *frequency = priv->frequency; - return 0; -} - -static int fc0012_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - /* CHECK: always ? */ - *frequency = 0; - return 0; -} - -static int fc0012_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) -{ - struct fc0012_priv *priv = fe->tuner_priv; - *bandwidth = priv->bandwidth; - return 0; -} - -#define INPUT_ADC_LEVEL -8 - -static int fc0012_get_rf_strength(struct dvb_frontend *fe, u16 *strength) -{ - struct fc0012_priv *priv = fe->tuner_priv; - int ret; - unsigned char tmp; - int int_temp, lna_gain, int_lna, tot_agc_gain, power; - const int fc0012_lna_gain_table[] = { - /* low gain */ - -63, -58, -99, -73, - -63, -65, -54, -60, - /* middle gain */ - 71, 70, 68, 67, - 65, 63, 61, 58, - /* high gain */ - 197, 191, 188, 186, - 184, 182, 181, 179, - }; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ - - ret = fc0012_writereg(priv, 0x12, 0x00); - if (ret) - goto err; - - ret = fc0012_readreg(priv, 0x12, &tmp); - if (ret) - goto err; - int_temp = tmp; - - ret = fc0012_readreg(priv, 0x13, &tmp); - if (ret) - goto err; - lna_gain = tmp & 0x1f; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - - if (lna_gain < ARRAY_SIZE(fc0012_lna_gain_table)) { - int_lna = fc0012_lna_gain_table[lna_gain]; - tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 + - (int_temp & 0x1f)) * 2; - power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10; - - if (power >= 45) - *strength = 255; /* 100% */ - else if (power < -95) - *strength = 0; - else - *strength = (power + 95) * 255 / 140; - - *strength |= *strength << 8; - } else { - ret = -1; - } - - goto exit; - -err: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ -exit: - if (ret) - warn("%s: failed: %d", __func__, ret); - return ret; -} - -static const struct dvb_tuner_ops fc0012_tuner_ops = { - .info = { - .name = "Fitipower FC0012", - - .frequency_min = 37000000, /* estimate */ - .frequency_max = 862000000, /* estimate */ - .frequency_step = 0, - }, - - .release = fc0012_release, - - .init = fc0012_init, - .sleep = fc0012_sleep, - - .set_params = fc0012_set_params, - - .get_frequency = fc0012_get_frequency, - .get_if_frequency = fc0012_get_if_frequency, - .get_bandwidth = fc0012_get_bandwidth, - - .get_rf_strength = fc0012_get_rf_strength, -}; - -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) -{ - struct fc0012_priv *priv = NULL; - - priv = kzalloc(sizeof(struct fc0012_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - - priv->i2c = i2c; - priv->dual_master = dual_master; - priv->addr = i2c_address; - priv->xtal_freq = xtal_freq; - - info("Fitipower FC0012 successfully attached."); - - fe->tuner_priv = priv; - - memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - return fe; -} -EXPORT_SYMBOL(fc0012_attach); - -MODULE_DESCRIPTION("Fitipower FC0012 silicon tuner driver"); -MODULE_AUTHOR("Hans-Frieder Vogt "); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.6"); 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 - * - * 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 - * - * 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.c b/drivers/media/common/tuners/fc0013.c deleted file mode 100644 index bd8f0f1e8f3b..000000000000 --- a/drivers/media/common/tuners/fc0013.c +++ /dev/null @@ -1,634 +0,0 @@ -/* - * Fitipower FC0013 tuner driver - * - * Copyright (C) 2012 Hans-Frieder Vogt - * partially based on driver code from Fitipower - * Copyright (C) 2010 Fitipower Integrated Technology 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. - * - */ - -#include "fc0013.h" -#include "fc0013-priv.h" - -static int fc0013_writereg(struct fc0013_priv *priv, u8 reg, u8 val) -{ - u8 buf[2] = {reg, val}; - struct i2c_msg msg = { - .addr = priv->addr, .flags = 0, .buf = buf, .len = 2 - }; - - if (i2c_transfer(priv->i2c, &msg, 1) != 1) { - err("I2C write reg failed, reg: %02x, val: %02x", reg, val); - return -EREMOTEIO; - } - return 0; -} - -static int fc0013_readreg(struct fc0013_priv *priv, u8 reg, u8 *val) -{ - struct i2c_msg msg[2] = { - { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 }, - { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 }, - }; - - if (i2c_transfer(priv->i2c, msg, 2) != 2) { - err("I2C read reg failed, reg: %02x", reg); - return -EREMOTEIO; - } - return 0; -} - -static int fc0013_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - return 0; -} - -static int fc0013_init(struct dvb_frontend *fe) -{ - struct fc0013_priv *priv = fe->tuner_priv; - int i, ret = 0; - unsigned char reg[] = { - 0x00, /* reg. 0x00: dummy */ - 0x09, /* reg. 0x01 */ - 0x16, /* reg. 0x02 */ - 0x00, /* reg. 0x03 */ - 0x00, /* reg. 0x04 */ - 0x17, /* reg. 0x05 */ - 0x02, /* reg. 0x06 */ - 0x0a, /* reg. 0x07: CHECK */ - 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, - Loop Bw 1/8 */ - 0x6f, /* reg. 0x09: enable LoopThrough */ - 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ - 0x82, /* reg. 0x0b: CHECK */ - 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ - 0x01, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */ - 0x00, /* reg. 0x0e */ - 0x00, /* reg. 0x0f */ - 0x00, /* reg. 0x10 */ - 0x00, /* reg. 0x11 */ - 0x00, /* reg. 0x12 */ - 0x00, /* reg. 0x13 */ - 0x50, /* reg. 0x14: DVB-t High Gain, UHF. - Middle Gain: 0x48, Low Gain: 0x40 */ - 0x01, /* reg. 0x15 */ - }; - - switch (priv->xtal_freq) { - case FC_XTAL_27_MHZ: - case FC_XTAL_28_8_MHZ: - reg[0x07] |= 0x20; - break; - case FC_XTAL_36_MHZ: - default: - break; - } - - if (priv->dual_master) - reg[0x0c] |= 0x02; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ - - for (i = 1; i < sizeof(reg); i++) { - ret = fc0013_writereg(priv, i, reg[i]); - if (ret) - break; - } - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - - if (ret) - err("fc0013_writereg failed: %d", ret); - - return ret; -} - -static int fc0013_sleep(struct dvb_frontend *fe) -{ - /* nothing to do here */ - return 0; -} - -int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val) -{ - struct fc0013_priv *priv = fe->tuner_priv; - int ret; - u8 rc_cal; - int val; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ - - /* push rc_cal value, get rc_cal value */ - ret = fc0013_writereg(priv, 0x10, 0x00); - if (ret) - goto error_out; - - /* get rc_cal value */ - ret = fc0013_readreg(priv, 0x10, &rc_cal); - if (ret) - goto error_out; - - rc_cal &= 0x0f; - - val = (int)rc_cal + rc_val; - - /* forcing rc_cal */ - ret = fc0013_writereg(priv, 0x0d, 0x11); - if (ret) - goto error_out; - - /* modify rc_cal value */ - if (val > 15) - ret = fc0013_writereg(priv, 0x10, 0x0f); - else if (val < 0) - ret = fc0013_writereg(priv, 0x10, 0x00); - else - ret = fc0013_writereg(priv, 0x10, (u8)val); - -error_out: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - - return ret; -} -EXPORT_SYMBOL(fc0013_rc_cal_add); - -int fc0013_rc_cal_reset(struct dvb_frontend *fe) -{ - struct fc0013_priv *priv = fe->tuner_priv; - int ret; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ - - ret = fc0013_writereg(priv, 0x0d, 0x01); - if (!ret) - ret = fc0013_writereg(priv, 0x10, 0x00); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - - return ret; -} -EXPORT_SYMBOL(fc0013_rc_cal_reset); - -static int fc0013_set_vhf_track(struct fc0013_priv *priv, u32 freq) -{ - int ret; - u8 tmp; - - ret = fc0013_readreg(priv, 0x1d, &tmp); - if (ret) - goto error_out; - tmp &= 0xe3; - if (freq <= 177500) { /* VHF Track: 7 */ - ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c); - } else if (freq <= 184500) { /* VHF Track: 6 */ - ret = fc0013_writereg(priv, 0x1d, tmp | 0x18); - } else if (freq <= 191500) { /* VHF Track: 5 */ - ret = fc0013_writereg(priv, 0x1d, tmp | 0x14); - } else if (freq <= 198500) { /* VHF Track: 4 */ - ret = fc0013_writereg(priv, 0x1d, tmp | 0x10); - } else if (freq <= 205500) { /* VHF Track: 3 */ - ret = fc0013_writereg(priv, 0x1d, tmp | 0x0c); - } else if (freq <= 219500) { /* VHF Track: 2 */ - ret = fc0013_writereg(priv, 0x1d, tmp | 0x08); - } else if (freq < 300000) { /* VHF Track: 1 */ - ret = fc0013_writereg(priv, 0x1d, tmp | 0x04); - } else { /* UHF and GPS */ - ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c); - } - if (ret) - goto error_out; -error_out: - return ret; -} - -static int fc0013_set_params(struct dvb_frontend *fe) -{ - struct fc0013_priv *priv = fe->tuner_priv; - int i, ret = 0; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - u32 freq = p->frequency / 1000; - u32 delsys = p->delivery_system; - unsigned char reg[7], am, pm, multi, tmp; - unsigned long f_vco; - unsigned short xtal_freq_khz_2, xin, xdiv; - int vco_select = false; - - if (fe->callback) { - ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, - FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1)); - if (ret) - goto exit; - } - - switch (priv->xtal_freq) { - case FC_XTAL_27_MHZ: - xtal_freq_khz_2 = 27000 / 2; - break; - case FC_XTAL_36_MHZ: - xtal_freq_khz_2 = 36000 / 2; - break; - case FC_XTAL_28_8_MHZ: - default: - xtal_freq_khz_2 = 28800 / 2; - break; - } - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ - - /* set VHF track */ - ret = fc0013_set_vhf_track(priv, freq); - if (ret) - goto exit; - - if (freq < 300000) { - /* enable VHF filter */ - ret = fc0013_readreg(priv, 0x07, &tmp); - if (ret) - goto exit; - ret = fc0013_writereg(priv, 0x07, tmp | 0x10); - if (ret) - goto exit; - - /* disable UHF & disable GPS */ - ret = fc0013_readreg(priv, 0x14, &tmp); - if (ret) - goto exit; - ret = fc0013_writereg(priv, 0x14, tmp & 0x1f); - if (ret) - goto exit; - } else if (freq <= 862000) { - /* disable VHF filter */ - ret = fc0013_readreg(priv, 0x07, &tmp); - if (ret) - goto exit; - ret = fc0013_writereg(priv, 0x07, tmp & 0xef); - if (ret) - goto exit; - - /* enable UHF & disable GPS */ - ret = fc0013_readreg(priv, 0x14, &tmp); - if (ret) - goto exit; - ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x40); - if (ret) - goto exit; - } else { - /* disable VHF filter */ - ret = fc0013_readreg(priv, 0x07, &tmp); - if (ret) - goto exit; - ret = fc0013_writereg(priv, 0x07, tmp & 0xef); - if (ret) - goto exit; - - /* disable UHF & enable GPS */ - ret = fc0013_readreg(priv, 0x14, &tmp); - if (ret) - goto exit; - ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x20); - if (ret) - goto exit; - } - - /* select frequency divider and the frequency of VCO */ - if (freq < 37084) { /* freq * 96 < 3560000 */ - multi = 96; - reg[5] = 0x82; - reg[6] = 0x00; - } else if (freq < 55625) { /* freq * 64 < 3560000 */ - multi = 64; - reg[5] = 0x02; - reg[6] = 0x02; - } else if (freq < 74167) { /* freq * 48 < 3560000 */ - multi = 48; - reg[5] = 0x42; - reg[6] = 0x00; - } else if (freq < 111250) { /* freq * 32 < 3560000 */ - multi = 32; - reg[5] = 0x82; - reg[6] = 0x02; - } else if (freq < 148334) { /* freq * 24 < 3560000 */ - multi = 24; - reg[5] = 0x22; - reg[6] = 0x00; - } else if (freq < 222500) { /* freq * 16 < 3560000 */ - multi = 16; - reg[5] = 0x42; - reg[6] = 0x02; - } else if (freq < 296667) { /* freq * 12 < 3560000 */ - multi = 12; - reg[5] = 0x12; - reg[6] = 0x00; - } else if (freq < 445000) { /* freq * 8 < 3560000 */ - multi = 8; - reg[5] = 0x22; - reg[6] = 0x02; - } else if (freq < 593334) { /* freq * 6 < 3560000 */ - multi = 6; - reg[5] = 0x0a; - reg[6] = 0x00; - } else if (freq < 950000) { /* freq * 4 < 3800000 */ - multi = 4; - reg[5] = 0x12; - reg[6] = 0x02; - } else { - multi = 2; - reg[5] = 0x0a; - reg[6] = 0x02; - } - - f_vco = freq * multi; - - if (f_vco >= 3060000) { - reg[6] |= 0x08; - vco_select = true; - } - - if (freq >= 45000) { - /* From divided value (XDIV) determined the FA and FP value */ - xdiv = (unsigned short)(f_vco / xtal_freq_khz_2); - if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2)) - xdiv++; - - pm = (unsigned char)(xdiv / 8); - am = (unsigned char)(xdiv - (8 * pm)); - - if (am < 2) { - reg[1] = am + 8; - reg[2] = pm - 1; - } else { - reg[1] = am; - reg[2] = pm; - } - } else { - /* fix for frequency less than 45 MHz */ - reg[1] = 0x06; - reg[2] = 0x11; - } - - /* fix clock out */ - reg[6] |= 0x20; - - /* From VCO frequency determines the XIN ( fractional part of Delta - Sigma PLL) and divided value (XDIV) */ - xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2); - xin = (xin << 15) / xtal_freq_khz_2; - if (xin >= 16384) - xin += 32768; - - reg[3] = xin >> 8; - reg[4] = xin & 0xff; - - if (delsys == SYS_DVBT) { - reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ - switch (p->bandwidth_hz) { - case 6000000: - reg[6] |= 0x80; - break; - case 7000000: - reg[6] |= 0x40; - break; - case 8000000: - default: - break; - } - } else { - err("%s: modulation type not supported!", __func__); - return -EINVAL; - } - - /* modified for Realtek demod */ - reg[5] |= 0x07; - - for (i = 1; i <= 6; i++) { - ret = fc0013_writereg(priv, i, reg[i]); - if (ret) - goto exit; - } - - ret = fc0013_readreg(priv, 0x11, &tmp); - if (ret) - goto exit; - if (multi == 64) - ret = fc0013_writereg(priv, 0x11, tmp | 0x04); - else - ret = fc0013_writereg(priv, 0x11, tmp & 0xfb); - if (ret) - goto exit; - - /* VCO Calibration */ - ret = fc0013_writereg(priv, 0x0e, 0x80); - if (!ret) - ret = fc0013_writereg(priv, 0x0e, 0x00); - - /* VCO Re-Calibration if needed */ - if (!ret) - ret = fc0013_writereg(priv, 0x0e, 0x00); - - if (!ret) { - msleep(10); - ret = fc0013_readreg(priv, 0x0e, &tmp); - } - if (ret) - goto exit; - - /* vco selection */ - tmp &= 0x3f; - - if (vco_select) { - if (tmp > 0x3c) { - reg[6] &= ~0x08; - ret = fc0013_writereg(priv, 0x06, reg[6]); - if (!ret) - ret = fc0013_writereg(priv, 0x0e, 0x80); - if (!ret) - ret = fc0013_writereg(priv, 0x0e, 0x00); - } - } else { - if (tmp < 0x02) { - reg[6] |= 0x08; - ret = fc0013_writereg(priv, 0x06, reg[6]); - if (!ret) - ret = fc0013_writereg(priv, 0x0e, 0x80); - if (!ret) - ret = fc0013_writereg(priv, 0x0e, 0x00); - } - } - - priv->frequency = p->frequency; - priv->bandwidth = p->bandwidth_hz; - -exit: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - if (ret) - warn("%s: failed: %d", __func__, ret); - return ret; -} - -static int fc0013_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct fc0013_priv *priv = fe->tuner_priv; - *frequency = priv->frequency; - return 0; -} - -static int fc0013_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - /* always ? */ - *frequency = 0; - return 0; -} - -static int fc0013_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) -{ - struct fc0013_priv *priv = fe->tuner_priv; - *bandwidth = priv->bandwidth; - return 0; -} - -#define INPUT_ADC_LEVEL -8 - -static int fc0013_get_rf_strength(struct dvb_frontend *fe, u16 *strength) -{ - struct fc0013_priv *priv = fe->tuner_priv; - int ret; - unsigned char tmp; - int int_temp, lna_gain, int_lna, tot_agc_gain, power; - const int fc0013_lna_gain_table[] = { - /* low gain */ - -63, -58, -99, -73, - -63, -65, -54, -60, - /* middle gain */ - 71, 70, 68, 67, - 65, 63, 61, 58, - /* high gain */ - 197, 191, 188, 186, - 184, 182, 181, 179, - }; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ - - ret = fc0013_writereg(priv, 0x13, 0x00); - if (ret) - goto err; - - ret = fc0013_readreg(priv, 0x13, &tmp); - if (ret) - goto err; - int_temp = tmp; - - ret = fc0013_readreg(priv, 0x14, &tmp); - if (ret) - goto err; - lna_gain = tmp & 0x1f; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - - if (lna_gain < ARRAY_SIZE(fc0013_lna_gain_table)) { - int_lna = fc0013_lna_gain_table[lna_gain]; - tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 + - (int_temp & 0x1f)) * 2; - power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10; - - if (power >= 45) - *strength = 255; /* 100% */ - else if (power < -95) - *strength = 0; - else - *strength = (power + 95) * 255 / 140; - - *strength |= *strength << 8; - } else { - ret = -1; - } - - goto exit; - -err: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ -exit: - if (ret) - warn("%s: failed: %d", __func__, ret); - return ret; -} - -static const struct dvb_tuner_ops fc0013_tuner_ops = { - .info = { - .name = "Fitipower FC0013", - - .frequency_min = 37000000, /* estimate */ - .frequency_max = 1680000000, /* CHECK */ - .frequency_step = 0, - }, - - .release = fc0013_release, - - .init = fc0013_init, - .sleep = fc0013_sleep, - - .set_params = fc0013_set_params, - - .get_frequency = fc0013_get_frequency, - .get_if_frequency = fc0013_get_if_frequency, - .get_bandwidth = fc0013_get_bandwidth, - - .get_rf_strength = fc0013_get_rf_strength, -}; - -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) -{ - struct fc0013_priv *priv = NULL; - - priv = kzalloc(sizeof(struct fc0013_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - - priv->i2c = i2c; - priv->dual_master = dual_master; - priv->addr = i2c_address; - priv->xtal_freq = xtal_freq; - - info("Fitipower FC0013 successfully attached."); - - fe->tuner_priv = priv; - - memcpy(&fe->ops.tuner_ops, &fc0013_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - return fe; -} -EXPORT_SYMBOL(fc0013_attach); - -MODULE_DESCRIPTION("Fitipower FC0013 silicon tuner driver"); -MODULE_AUTHOR("Hans-Frieder Vogt "); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.2"); 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 - * - * 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 - * - * 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.c b/drivers/media/common/tuners/max2165.c deleted file mode 100644 index ba84936aafd6..000000000000 --- a/drivers/media/common/tuners/max2165.c +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Driver for Maxim MAX2165 silicon tuner - * - * Copyright (c) 2009 David T. L. Wong - * - * 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 -#include -#include -#include -#include -#include -#include - -#include "dvb_frontend.h" - -#include "max2165.h" -#include "max2165_priv.h" -#include "tuner-i2c.h" - -#define dprintk(args...) \ - do { \ - if (debug) \ - printk(KERN_DEBUG "max2165: " args); \ - } while (0) - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); - -static int max2165_write_reg(struct max2165_priv *priv, u8 reg, u8 data) -{ - int ret; - u8 buf[] = { reg, data }; - struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; - - msg.addr = priv->config->i2c_address; - - if (debug >= 2) - dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, data); - - ret = i2c_transfer(priv->i2c, &msg, 1); - - if (ret != 1) - dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", - __func__, reg, data, ret); - - return (ret != 1) ? -EIO : 0; -} - -static int max2165_read_reg(struct max2165_priv *priv, u8 reg, u8 *p_data) -{ - int ret; - u8 dev_addr = priv->config->i2c_address; - - u8 b0[] = { reg }; - u8 b1[] = { 0 }; - struct i2c_msg msg[] = { - { .addr = dev_addr, .flags = 0, .buf = b0, .len = 1 }, - { .addr = dev_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 }, - }; - - ret = i2c_transfer(priv->i2c, msg, 2); - if (ret != 2) { - dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ret); - return -EIO; - } - - *p_data = b1[0]; - if (debug >= 2) - dprintk("%s: reg=0x%02X, data=0x%02X\n", - __func__, reg, b1[0]); - return 0; -} - -static int max2165_mask_write_reg(struct max2165_priv *priv, u8 reg, - u8 mask, u8 data) -{ - int ret; - u8 v; - - data &= mask; - ret = max2165_read_reg(priv, reg, &v); - if (ret != 0) - return ret; - v &= ~mask; - v |= data; - ret = max2165_write_reg(priv, reg, v); - - return ret; -} - -static int max2165_read_rom_table(struct max2165_priv *priv) -{ - u8 dat[3]; - int i; - - for (i = 0; i < 3; i++) { - max2165_write_reg(priv, REG_ROM_TABLE_ADDR, i + 1); - max2165_read_reg(priv, REG_ROM_TABLE_DATA, &dat[i]); - } - - priv->tf_ntch_low_cfg = dat[0] >> 4; - priv->tf_ntch_hi_cfg = dat[0] & 0x0F; - priv->tf_balun_low_ref = dat[1] & 0x0F; - priv->tf_balun_hi_ref = dat[1] >> 4; - priv->bb_filter_7mhz_cfg = dat[2] & 0x0F; - priv->bb_filter_8mhz_cfg = dat[2] >> 4; - - dprintk("tf_ntch_low_cfg = 0x%X\n", priv->tf_ntch_low_cfg); - dprintk("tf_ntch_hi_cfg = 0x%X\n", priv->tf_ntch_hi_cfg); - dprintk("tf_balun_low_ref = 0x%X\n", priv->tf_balun_low_ref); - dprintk("tf_balun_hi_ref = 0x%X\n", priv->tf_balun_hi_ref); - dprintk("bb_filter_7mhz_cfg = 0x%X\n", priv->bb_filter_7mhz_cfg); - dprintk("bb_filter_8mhz_cfg = 0x%X\n", priv->bb_filter_8mhz_cfg); - - return 0; -} - -static int max2165_set_osc(struct max2165_priv *priv, u8 osc /*MHz*/) -{ - u8 v; - - v = (osc / 2); - if (v == 2) - v = 0x7; - else - v -= 8; - - max2165_mask_write_reg(priv, REG_PLL_CFG, 0x07, v); - - return 0; -} - -static int max2165_set_bandwidth(struct max2165_priv *priv, u32 bw) -{ - u8 val; - - if (bw == 8000000) - val = priv->bb_filter_8mhz_cfg; - else - val = priv->bb_filter_7mhz_cfg; - - max2165_mask_write_reg(priv, REG_BASEBAND_CTRL, 0xF0, val << 4); - - return 0; -} - -int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction) -{ - u32 remainder; - u32 q, f = 0; - int i; - - if (0 == divisor) - return -EINVAL; - - q = dividend / divisor; - remainder = dividend - q * divisor; - - for (i = 0; i < 31; i++) { - remainder <<= 1; - if (remainder >= divisor) { - f += 1; - remainder -= divisor; - } - f <<= 1; - } - - *quotient = q; - *fraction = f; - - return 0; -} - -static int max2165_set_rf(struct max2165_priv *priv, u32 freq) -{ - u8 tf; - u8 tf_ntch; - u32 t; - u32 quotient, fraction; - int ret; - - /* Set PLL divider according to RF frequency */ - ret = fixpt_div32(freq / 1000, priv->config->osc_clk * 1000, - "ient, &fraction); - if (ret != 0) - return ret; - - /* 20-bit fraction */ - fraction >>= 12; - - max2165_write_reg(priv, REG_NDIV_INT, quotient); - max2165_mask_write_reg(priv, REG_NDIV_FRAC2, 0x0F, fraction >> 16); - max2165_write_reg(priv, REG_NDIV_FRAC1, fraction >> 8); - max2165_write_reg(priv, REG_NDIV_FRAC0, fraction); - - /* Norch Filter */ - tf_ntch = (freq < 725000000) ? - priv->tf_ntch_low_cfg : priv->tf_ntch_hi_cfg; - - /* Tracking filter balun */ - t = priv->tf_balun_low_ref; - t += (priv->tf_balun_hi_ref - priv->tf_balun_low_ref) - * (freq / 1000 - 470000) / (780000 - 470000); - - tf = t; - dprintk("tf = %X\n", tf); - tf |= tf_ntch << 4; - - max2165_write_reg(priv, REG_TRACK_FILTER, tf); - - return 0; -} - -static void max2165_debug_status(struct max2165_priv *priv) -{ - u8 status, autotune; - u8 auto_vco_success, auto_vco_active; - u8 pll_locked; - u8 dc_offset_low, dc_offset_hi; - u8 signal_lv_over_threshold; - u8 vco, vco_sub_band, adc; - - max2165_read_reg(priv, REG_STATUS, &status); - max2165_read_reg(priv, REG_AUTOTUNE, &autotune); - - auto_vco_success = (status >> 6) & 0x01; - auto_vco_active = (status >> 5) & 0x01; - pll_locked = (status >> 4) & 0x01; - dc_offset_low = (status >> 3) & 0x01; - dc_offset_hi = (status >> 2) & 0x01; - signal_lv_over_threshold = status & 0x01; - - vco = autotune >> 6; - vco_sub_band = (autotune >> 3) & 0x7; - adc = autotune & 0x7; - - dprintk("auto VCO active: %d, auto VCO success: %d\n", - auto_vco_active, auto_vco_success); - dprintk("PLL locked: %d\n", pll_locked); - dprintk("DC offset low: %d, DC offset high: %d\n", - dc_offset_low, dc_offset_hi); - dprintk("Signal lvl over threshold: %d\n", signal_lv_over_threshold); - dprintk("VCO: %d, VCO Sub-band: %d, ADC: %d\n", vco, vco_sub_band, adc); -} - -static int max2165_set_params(struct dvb_frontend *fe) -{ - struct max2165_priv *priv = fe->tuner_priv; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret; - - switch (c->bandwidth_hz) { - case 7000000: - case 8000000: - priv->frequency = c->frequency; - break; - default: - printk(KERN_INFO "MAX2165: bandwidth %d Hz not supported.\n", - c->bandwidth_hz); - return -EINVAL; - } - - dprintk("%s() frequency=%d\n", __func__, c->frequency); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - max2165_set_bandwidth(priv, c->bandwidth_hz); - ret = max2165_set_rf(priv, priv->frequency); - mdelay(50); - max2165_debug_status(priv); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - if (ret != 0) - return -EREMOTEIO; - - return 0; -} - -static int max2165_get_frequency(struct dvb_frontend *fe, u32 *freq) -{ - struct max2165_priv *priv = fe->tuner_priv; - dprintk("%s()\n", __func__); - *freq = priv->frequency; - return 0; -} - -static int max2165_get_bandwidth(struct dvb_frontend *fe, u32 *bw) -{ - struct max2165_priv *priv = fe->tuner_priv; - dprintk("%s()\n", __func__); - - *bw = priv->bandwidth; - return 0; -} - -static int max2165_get_status(struct dvb_frontend *fe, u32 *status) -{ - struct max2165_priv *priv = fe->tuner_priv; - u16 lock_status = 0; - - dprintk("%s()\n", __func__); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - max2165_debug_status(priv); - *status = lock_status; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return 0; -} - -static int max2165_sleep(struct dvb_frontend *fe) -{ - dprintk("%s()\n", __func__); - return 0; -} - -static int max2165_init(struct dvb_frontend *fe) -{ - struct max2165_priv *priv = fe->tuner_priv; - dprintk("%s()\n", __func__); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - /* Setup initial values */ - /* Fractional Mode on */ - max2165_write_reg(priv, REG_NDIV_FRAC2, 0x18); - /* LNA on */ - max2165_write_reg(priv, REG_LNA, 0x01); - max2165_write_reg(priv, REG_PLL_CFG, 0x7A); - max2165_write_reg(priv, REG_TEST, 0x08); - max2165_write_reg(priv, REG_SHUTDOWN, 0x40); - max2165_write_reg(priv, REG_VCO_CTRL, 0x84); - max2165_write_reg(priv, REG_BASEBAND_CTRL, 0xC3); - max2165_write_reg(priv, REG_DC_OFFSET_CTRL, 0x75); - max2165_write_reg(priv, REG_DC_OFFSET_DAC, 0x00); - max2165_write_reg(priv, REG_ROM_TABLE_ADDR, 0x00); - - max2165_set_osc(priv, priv->config->osc_clk); - - max2165_read_rom_table(priv); - - max2165_set_bandwidth(priv, 8000000); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return 0; -} - -static int max2165_release(struct dvb_frontend *fe) -{ - struct max2165_priv *priv = fe->tuner_priv; - dprintk("%s()\n", __func__); - - kfree(priv); - fe->tuner_priv = NULL; - - return 0; -} - -static const struct dvb_tuner_ops max2165_tuner_ops = { - .info = { - .name = "Maxim MAX2165", - .frequency_min = 470000000, - .frequency_max = 780000000, - .frequency_step = 50000, - }, - - .release = max2165_release, - .init = max2165_init, - .sleep = max2165_sleep, - - .set_params = max2165_set_params, - .set_analog_params = NULL, - .get_frequency = max2165_get_frequency, - .get_bandwidth = max2165_get_bandwidth, - .get_status = max2165_get_status -}; - -struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct max2165_config *cfg) -{ - struct max2165_priv *priv = NULL; - - dprintk("%s(%d-%04x)\n", __func__, - i2c ? i2c_adapter_id(i2c) : -1, - cfg ? cfg->i2c_address : -1); - - priv = kzalloc(sizeof(struct max2165_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - - memcpy(&fe->ops.tuner_ops, &max2165_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - priv->config = cfg; - priv->i2c = i2c; - fe->tuner_priv = priv; - - max2165_init(fe); - max2165_debug_status(priv); - - return fe; -} -EXPORT_SYMBOL(max2165_attach); - -MODULE_AUTHOR("David T. L. Wong "); -MODULE_DESCRIPTION("Maxim MAX2165 silicon tuner driver"); -MODULE_LICENSE("GPL"); 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 - * - * 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 - * - * 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.c b/drivers/media/common/tuners/mc44s803.c deleted file mode 100644 index 5ddce7e326f7..000000000000 --- a/drivers/media/common/tuners/mc44s803.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner - * - * Copyright (c) 2009 Jochen Friedrich - * - * 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 -#include -#include -#include -#include - -#include "dvb_frontend.h" - -#include "mc44s803.h" -#include "mc44s803_priv.h" - -#define mc_printk(level, format, arg...) \ - printk(level "mc44s803: " format , ## arg) - -/* Writes a single register */ -static int mc44s803_writereg(struct mc44s803_priv *priv, u32 val) -{ - u8 buf[3]; - struct i2c_msg msg = { - .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 3 - }; - - buf[0] = (val & 0xff0000) >> 16; - buf[1] = (val & 0xff00) >> 8; - buf[2] = (val & 0xff); - - if (i2c_transfer(priv->i2c, &msg, 1) != 1) { - mc_printk(KERN_WARNING, "I2C write failed\n"); - return -EREMOTEIO; - } - return 0; -} - -/* Reads a single register */ -static int mc44s803_readreg(struct mc44s803_priv *priv, u8 reg, u32 *val) -{ - u32 wval; - u8 buf[3]; - int ret; - struct i2c_msg msg[] = { - { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, - .buf = buf, .len = 3 }, - }; - - wval = MC44S803_REG_SM(MC44S803_REG_DATAREG, MC44S803_ADDR) | - MC44S803_REG_SM(reg, MC44S803_D); - - ret = mc44s803_writereg(priv, wval); - if (ret) - return ret; - - if (i2c_transfer(priv->i2c, msg, 1) != 1) { - mc_printk(KERN_WARNING, "I2C read failed\n"); - return -EREMOTEIO; - } - - *val = (buf[0] << 16) | (buf[1] << 8) | buf[2]; - - return 0; -} - -static int mc44s803_release(struct dvb_frontend *fe) -{ - struct mc44s803_priv *priv = fe->tuner_priv; - - fe->tuner_priv = NULL; - kfree(priv); - - return 0; -} - -static int mc44s803_init(struct dvb_frontend *fe) -{ - struct mc44s803_priv *priv = fe->tuner_priv; - u32 val; - int err; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - -/* Reset chip */ - val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR) | - MC44S803_REG_SM(1, MC44S803_RS); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - - val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - -/* Power Up and Start Osc */ - - val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) | - MC44S803_REG_SM(0xC0, MC44S803_REFOSC) | - MC44S803_REG_SM(1, MC44S803_OSCSEL); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - - val = MC44S803_REG_SM(MC44S803_REG_POWER, MC44S803_ADDR) | - MC44S803_REG_SM(0x200, MC44S803_POWER); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - - msleep(10); - - val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) | - MC44S803_REG_SM(0x40, MC44S803_REFOSC) | - MC44S803_REG_SM(1, MC44S803_OSCSEL); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - - msleep(20); - -/* Setup Mixer */ - - val = MC44S803_REG_SM(MC44S803_REG_MIXER, MC44S803_ADDR) | - MC44S803_REG_SM(1, MC44S803_TRI_STATE) | - MC44S803_REG_SM(0x7F, MC44S803_MIXER_RES); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - -/* Setup Cirquit Adjust */ - - val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) | - MC44S803_REG_SM(1, MC44S803_G1) | - MC44S803_REG_SM(1, MC44S803_G3) | - MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) | - MC44S803_REG_SM(1, MC44S803_G6) | - MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) | - MC44S803_REG_SM(0x3, MC44S803_LP) | - MC44S803_REG_SM(1, MC44S803_CLRF) | - MC44S803_REG_SM(1, MC44S803_CLIF); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - - val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) | - MC44S803_REG_SM(1, MC44S803_G1) | - MC44S803_REG_SM(1, MC44S803_G3) | - MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) | - MC44S803_REG_SM(1, MC44S803_G6) | - MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) | - MC44S803_REG_SM(0x3, MC44S803_LP); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - -/* Setup Digtune */ - - val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | - MC44S803_REG_SM(3, MC44S803_XOD); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - -/* Setup AGC */ - - val = MC44S803_REG_SM(MC44S803_REG_LNAAGC, MC44S803_ADDR) | - MC44S803_REG_SM(1, MC44S803_AT1) | - MC44S803_REG_SM(1, MC44S803_AT2) | - MC44S803_REG_SM(1, MC44S803_AGC_AN_DIG) | - MC44S803_REG_SM(1, MC44S803_AGC_READ_EN) | - MC44S803_REG_SM(1, MC44S803_LNA0); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - return 0; - -exit: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - mc_printk(KERN_WARNING, "I/O Error\n"); - return err; -} - -static int mc44s803_set_params(struct dvb_frontend *fe) -{ - struct mc44s803_priv *priv = fe->tuner_priv; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - u32 r1, r2, n1, n2, lo1, lo2, freq, val; - int err; - - priv->frequency = c->frequency; - - r1 = MC44S803_OSC / 1000000; - r2 = MC44S803_OSC / 100000; - - n1 = (c->frequency + MC44S803_IF1 + 500000) / 1000000; - freq = MC44S803_OSC / r1 * n1; - lo1 = ((60 * n1) + (r1 / 2)) / r1; - freq = freq - c->frequency; - - n2 = (freq - MC44S803_IF2 + 50000) / 100000; - lo2 = ((60 * n2) + (r2 / 2)) / r2; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - val = MC44S803_REG_SM(MC44S803_REG_REFDIV, MC44S803_ADDR) | - MC44S803_REG_SM(r1-1, MC44S803_R1) | - MC44S803_REG_SM(r2-1, MC44S803_R2) | - MC44S803_REG_SM(1, MC44S803_REFBUF_EN); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - - val = MC44S803_REG_SM(MC44S803_REG_LO1, MC44S803_ADDR) | - MC44S803_REG_SM(n1-2, MC44S803_LO1); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - - val = MC44S803_REG_SM(MC44S803_REG_LO2, MC44S803_ADDR) | - MC44S803_REG_SM(n2-2, MC44S803_LO2); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - - val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | - MC44S803_REG_SM(1, MC44S803_DA) | - MC44S803_REG_SM(lo1, MC44S803_LO_REF) | - MC44S803_REG_SM(1, MC44S803_AT); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - - val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | - MC44S803_REG_SM(2, MC44S803_DA) | - MC44S803_REG_SM(lo2, MC44S803_LO_REF) | - MC44S803_REG_SM(1, MC44S803_AT); - - err = mc44s803_writereg(priv, val); - if (err) - goto exit; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return 0; - -exit: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - mc_printk(KERN_WARNING, "I/O Error\n"); - return err; -} - -static int mc44s803_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct mc44s803_priv *priv = fe->tuner_priv; - *frequency = priv->frequency; - return 0; -} - -static const struct dvb_tuner_ops mc44s803_tuner_ops = { - .info = { - .name = "Freescale MC44S803", - .frequency_min = 48000000, - .frequency_max = 1000000000, - .frequency_step = 100000, - }, - - .release = mc44s803_release, - .init = mc44s803_init, - .set_params = mc44s803_set_params, - .get_frequency = mc44s803_get_frequency -}; - -/* This functions tries to identify a MC44S803 tuner by reading the ID - register. This is hasty. */ -struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, struct mc44s803_config *cfg) -{ - struct mc44s803_priv *priv; - u32 reg; - u8 id; - int ret; - - reg = 0; - - priv = kzalloc(sizeof(struct mc44s803_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - - priv->cfg = cfg; - priv->i2c = i2c; - priv->fe = fe; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ - - ret = mc44s803_readreg(priv, MC44S803_REG_ID, ®); - if (ret) - goto error; - - id = MC44S803_REG_MS(reg, MC44S803_ID); - - if (id != 0x14) { - mc_printk(KERN_ERR, "unsupported ID " - "(%x should be 0x14)\n", id); - goto error; - } - - mc_printk(KERN_INFO, "successfully identified (ID = %x)\n", id); - memcpy(&fe->ops.tuner_ops, &mc44s803_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - fe->tuner_priv = priv; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ - - return fe; - -error: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ - - kfree(priv); - return NULL; -} -EXPORT_SYMBOL(mc44s803_attach); - -MODULE_AUTHOR("Jochen Friedrich"); -MODULE_DESCRIPTION("Freescale MC44S803 silicon tuner driver"); -MODULE_LICENSE("GPL"); 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 - * - * 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/mc44s803_priv.h b/drivers/media/common/tuners/mc44s803_priv.h deleted file mode 100644 index 14a92780906d..000000000000 --- a/drivers/media/common/tuners/mc44s803_priv.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner - * - * Copyright (c) 2009 Jochen Friedrich - * - * 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_PRIV_H -#define MC44S803_PRIV_H - -/* This driver is based on the information available in the datasheet - http://www.freescale.com/files/rf_if/doc/data_sheet/MC44S803.pdf - - SPI or I2C Address : 0xc0-0xc6 - - Reg.No | Function - ------------------------------------------- - 00 | Power Down - 01 | Reference Oszillator - 02 | Reference Dividers - 03 | Mixer and Reference Buffer - 04 | Reset/Serial Out - 05 | LO 1 - 06 | LO 2 - 07 | Circuit Adjust - 08 | Test - 09 | Digital Tune - 0A | LNA AGC - 0B | Data Register Address - 0C | Regulator Test - 0D | VCO Test - 0E | LNA Gain/Input Power - 0F | ID Bits - -*/ - -#define MC44S803_OSC 26000000 /* 26 MHz */ -#define MC44S803_IF1 1086000000 /* 1086 MHz */ -#define MC44S803_IF2 36125000 /* 36.125 MHz */ - -#define MC44S803_REG_POWER 0 -#define MC44S803_REG_REFOSC 1 -#define MC44S803_REG_REFDIV 2 -#define MC44S803_REG_MIXER 3 -#define MC44S803_REG_RESET 4 -#define MC44S803_REG_LO1 5 -#define MC44S803_REG_LO2 6 -#define MC44S803_REG_CIRCADJ 7 -#define MC44S803_REG_TEST 8 -#define MC44S803_REG_DIGTUNE 9 -#define MC44S803_REG_LNAAGC 0x0A -#define MC44S803_REG_DATAREG 0x0B -#define MC44S803_REG_REGTEST 0x0C -#define MC44S803_REG_VCOTEST 0x0D -#define MC44S803_REG_LNAGAIN 0x0E -#define MC44S803_REG_ID 0x0F - -/* Register definitions */ -#define MC44S803_ADDR 0x0F -#define MC44S803_ADDR_S 0 -/* REG_POWER */ -#define MC44S803_POWER 0xFFFFF0 -#define MC44S803_POWER_S 4 -/* REG_REFOSC */ -#define MC44S803_REFOSC 0x1FF0 -#define MC44S803_REFOSC_S 4 -#define MC44S803_OSCSEL 0x2000 -#define MC44S803_OSCSEL_S 13 -/* REG_REFDIV */ -#define MC44S803_R2 0x1FF0 -#define MC44S803_R2_S 4 -#define MC44S803_REFBUF_EN 0x2000 -#define MC44S803_REFBUF_EN_S 13 -#define MC44S803_R1 0x7C000 -#define MC44S803_R1_S 14 -/* REG_MIXER */ -#define MC44S803_R3 0x70 -#define MC44S803_R3_S 4 -#define MC44S803_MUX3 0x80 -#define MC44S803_MUX3_S 7 -#define MC44S803_MUX4 0x100 -#define MC44S803_MUX4_S 8 -#define MC44S803_OSC_SCR 0x200 -#define MC44S803_OSC_SCR_S 9 -#define MC44S803_TRI_STATE 0x400 -#define MC44S803_TRI_STATE_S 10 -#define MC44S803_BUF_GAIN 0x800 -#define MC44S803_BUF_GAIN_S 11 -#define MC44S803_BUF_IO 0x1000 -#define MC44S803_BUF_IO_S 12 -#define MC44S803_MIXER_RES 0xFE000 -#define MC44S803_MIXER_RES_S 13 -/* REG_RESET */ -#define MC44S803_RS 0x10 -#define MC44S803_RS_S 4 -#define MC44S803_SO 0x20 -#define MC44S803_SO_S 5 -/* REG_LO1 */ -#define MC44S803_LO1 0xFFF0 -#define MC44S803_LO1_S 4 -/* REG_LO2 */ -#define MC44S803_LO2 0x7FFF0 -#define MC44S803_LO2_S 4 -/* REG_CIRCADJ */ -#define MC44S803_G1 0x20 -#define MC44S803_G1_S 5 -#define MC44S803_G3 0x80 -#define MC44S803_G3_S 7 -#define MC44S803_CIRCADJ_RES 0x300 -#define MC44S803_CIRCADJ_RES_S 8 -#define MC44S803_G6 0x400 -#define MC44S803_G6_S 10 -#define MC44S803_G7 0x800 -#define MC44S803_G7_S 11 -#define MC44S803_S1 0x1000 -#define MC44S803_S1_S 12 -#define MC44S803_LP 0x7E000 -#define MC44S803_LP_S 13 -#define MC44S803_CLRF 0x80000 -#define MC44S803_CLRF_S 19 -#define MC44S803_CLIF 0x100000 -#define MC44S803_CLIF_S 20 -/* REG_TEST */ -/* REG_DIGTUNE */ -#define MC44S803_DA 0xF0 -#define MC44S803_DA_S 4 -#define MC44S803_XOD 0x300 -#define MC44S803_XOD_S 8 -#define MC44S803_RST 0x10000 -#define MC44S803_RST_S 16 -#define MC44S803_LO_REF 0x1FFF00 -#define MC44S803_LO_REF_S 8 -#define MC44S803_AT 0x200000 -#define MC44S803_AT_S 21 -#define MC44S803_MT 0x400000 -#define MC44S803_MT_S 22 -/* REG_LNAAGC */ -#define MC44S803_G 0x3F0 -#define MC44S803_G_S 4 -#define MC44S803_AT1 0x400 -#define MC44S803_AT1_S 10 -#define MC44S803_AT2 0x800 -#define MC44S803_AT2_S 11 -#define MC44S803_HL_GR_EN 0x8000 -#define MC44S803_HL_GR_EN_S 15 -#define MC44S803_AGC_AN_DIG 0x10000 -#define MC44S803_AGC_AN_DIG_S 16 -#define MC44S803_ATTEN_EN 0x20000 -#define MC44S803_ATTEN_EN_S 17 -#define MC44S803_AGC_READ_EN 0x40000 -#define MC44S803_AGC_READ_EN_S 18 -#define MC44S803_LNA0 0x80000 -#define MC44S803_LNA0_S 19 -#define MC44S803_AGC_SEL 0x100000 -#define MC44S803_AGC_SEL_S 20 -#define MC44S803_AT0 0x200000 -#define MC44S803_AT0_S 21 -#define MC44S803_B 0xC00000 -#define MC44S803_B_S 22 -/* REG_DATAREG */ -#define MC44S803_D 0xF0 -#define MC44S803_D_S 4 -/* REG_REGTEST */ -/* REG_VCOTEST */ -/* REG_LNAGAIN */ -#define MC44S803_IF_PWR 0x700 -#define MC44S803_IF_PWR_S 8 -#define MC44S803_RF_PWR 0x3800 -#define MC44S803_RF_PWR_S 11 -#define MC44S803_LNA_GAIN 0xFC000 -#define MC44S803_LNA_GAIN_S 14 -/* REG_ID */ -#define MC44S803_ID 0x3E00 -#define MC44S803_ID_S 9 - -/* Some macros to read/write fields */ - -/* First shift, then mask */ -#define MC44S803_REG_SM(_val, _reg) \ - (((_val) << _reg##_S) & (_reg)) - -/* First mask, then shift */ -#define MC44S803_REG_MS(_val, _reg) \ - (((_val) & (_reg)) >> _reg##_S) - -struct mc44s803_priv { - struct mc44s803_config *cfg; - struct i2c_adapter *i2c; - struct dvb_frontend *fe; - - u32 frequency; -}; - -#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 - * - * 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 -#include -#include -#include -#include - -#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 = ®, .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 - * - * 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/mt2060_priv.h b/drivers/media/common/tuners/mt2060_priv.h deleted file mode 100644 index 2b60de6c707d..000000000000 --- a/drivers/media/common/tuners/mt2060_priv.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" - * - * Copyright (c) 2006 Olivier DANET - * - * 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_PRIV_H -#define MT2060_PRIV_H - -// Uncomment the #define below to enable spurs checking. The results where quite unconvincing. -// #define MT2060_SPURCHECK - -/* This driver is based on the information available in the datasheet of the - "Comtech SDVBT-3K6M" tuner ( K1000737843.pdf ) which features the MT2060 register map : - - I2C Address : 0x60 - - Reg.No | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 | ( defaults ) - -------------------------------------------------------------------------------- - 00 | [ PART ] | [ REV ] | R = 0x63 - 01 | [ LNABAND ] | [ NUM1(5:2) ] | RW = 0x3F - 02 | [ DIV1 ] | RW = 0x74 - 03 | FM1CA | FM1SS | [ NUM1(1:0) ] | [ NUM2(3:0) ] | RW = 0x00 - 04 | NUM2(11:4) ] | RW = 0x08 - 05 | [ DIV2 ] |NUM2(12)| RW = 0x93 - 06 | L1LK | [ TAD1 ] | L2LK | [ TAD2 ] | R - 07 | [ FMF ] | R - 08 | ? | FMCAL | ? | ? | ? | ? | ? | TEMP | R - 09 | 0 | 0 | [ FMGC ] | 0 | GP02 | GP01 | 0 | RW = 0x20 - 0A | ?? - 0B | 0 | 0 | 1 | 1 | 0 | 0 | [ VGAG ] | RW = 0x30 - 0C | V1CSE | 1 | 1 | 1 | 1 | 1 | 1 | 1 | RW = 0xFF - 0D | 1 | 0 | [ V1CS ] | RW = 0xB0 - 0E | ?? - 0F | ?? - 10 | ?? - 11 | [ LOTO ] | 0 | 0 | 1 | 0 | RW = 0x42 - - PART : Part code : 6 for MT2060 - REV : Revision code : 3 for current revision - LNABAND : Input frequency range : ( See code for details ) - NUM1 / DIV1 / NUM2 / DIV2 : Frequencies programming ( See code for details ) - FM1CA : Calibration Start Bit - FM1SS : Calibration Single Step bit - L1LK : LO1 Lock Detect - TAD1 : Tune Line ADC ( ? ) - L2LK : LO2 Lock Detect - TAD2 : Tune Line ADC ( ? ) - FMF : Estimated first IF Center frequency Offset ( ? ) - FM1CAL : Calibration done bit - TEMP : On chip temperature sensor - FMCG : Mixer 1 Cap Gain ( ? ) - GP01 / GP02 : Programmable digital outputs. Unconnected pins ? - V1CSE : LO1 VCO Automatic Capacitor Select Enable ( ? ) - V1CS : LO1 Capacitor Selection Value ( ? ) - LOTO : LO Timeout ( ? ) - VGAG : Tuner Output gain -*/ - -#define I2C_ADDRESS 0x60 - -#define REG_PART_REV 0 -#define REG_LO1C1 1 -#define REG_LO1C2 2 -#define REG_LO2C1 3 -#define REG_LO2C2 4 -#define REG_LO2C3 5 -#define REG_LO_STATUS 6 -#define REG_FM_FREQ 7 -#define REG_MISC_STAT 8 -#define REG_MISC_CTRL 9 -#define REG_RESERVED_A 0x0A -#define REG_VGAG 0x0B -#define REG_LO1B1 0x0C -#define REG_LO1B2 0x0D -#define REG_LOTO 0x11 - -#define PART_REV 0x63 // The current driver works only with PART=6 and REV=3 chips - -struct mt2060_priv { - struct mt2060_config *cfg; - struct i2c_adapter *i2c; - - u32 frequency; - u16 if1_freq; - u8 fmfreq; -}; - -#endif diff --git a/drivers/media/common/tuners/mt2063.c b/drivers/media/common/tuners/mt2063.c deleted file mode 100644 index 0ed9091ff48e..000000000000 --- a/drivers/media/common/tuners/mt2063.c +++ /dev/null @@ -1,2307 +0,0 @@ -/* - * Driver for mt2063 Micronas tuner - * - * Copyright (c) 2011 Mauro Carvalho Chehab - * - * This driver came from a driver originally written by: - * Henry Wang - * Made publicly available by Terratec, at: - * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz - * The original driver's license is GPL, as declared with MODULE_LICENSE() - * - * 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 under version 2 of the License. - * - * 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 -#include -#include -#include -#include - -#include "mt2063.h" - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Set Verbosity level"); - -#define dprintk(level, fmt, arg...) do { \ -if (debug >= level) \ - printk(KERN_DEBUG "mt2063 %s: " fmt, __func__, ## arg); \ -} while (0) - - -/* positive error codes used internally */ - -/* Info: Unavoidable LO-related spur may be present in the output */ -#define MT2063_SPUR_PRESENT_ERR (0x00800000) - -/* Info: Mask of bits used for # of LO-related spurs that were avoided during tuning */ -#define MT2063_SPUR_CNT_MASK (0x001f0000) -#define MT2063_SPUR_SHIFT (16) - -/* Info: Upconverter frequency is out of range (may be reason for MT_UPC_UNLOCK) */ -#define MT2063_UPC_RANGE (0x04000000) - -/* Info: Downconverter frequency is out of range (may be reason for MT_DPC_UNLOCK) */ -#define MT2063_DNC_RANGE (0x08000000) - -/* - * Constant defining the version of the following structure - * and therefore the API for this code. - * - * When compiling the tuner driver, the preprocessor will - * check against this version number to make sure that - * it matches the version that the tuner driver knows about. - */ - -/* DECT Frequency Avoidance */ -#define MT2063_DECT_AVOID_US_FREQS 0x00000001 - -#define MT2063_DECT_AVOID_EURO_FREQS 0x00000002 - -#define MT2063_EXCLUDE_US_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_US_FREQS) != 0) - -#define MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_EURO_FREQS) != 0) - -enum MT2063_DECT_Avoid_Type { - MT2063_NO_DECT_AVOIDANCE = 0, /* Do not create DECT exclusion zones. */ - MT2063_AVOID_US_DECT = MT2063_DECT_AVOID_US_FREQS, /* Avoid US DECT frequencies. */ - MT2063_AVOID_EURO_DECT = MT2063_DECT_AVOID_EURO_FREQS, /* Avoid European DECT frequencies. */ - MT2063_AVOID_BOTH /* Avoid both regions. Not typically used. */ -}; - -#define MT2063_MAX_ZONES 48 - -struct MT2063_ExclZone_t { - u32 min_; - u32 max_; - struct MT2063_ExclZone_t *next_; -}; - -/* - * Structure of data needed for Spur Avoidance - */ -struct MT2063_AvoidSpursData_t { - u32 f_ref; - u32 f_in; - u32 f_LO1; - u32 f_if1_Center; - u32 f_if1_Request; - u32 f_if1_bw; - u32 f_LO2; - u32 f_out; - u32 f_out_bw; - u32 f_LO1_Step; - u32 f_LO2_Step; - u32 f_LO1_FracN_Avoid; - u32 f_LO2_FracN_Avoid; - u32 f_zif_bw; - u32 f_min_LO_Separation; - u32 maxH1; - u32 maxH2; - enum MT2063_DECT_Avoid_Type avoidDECT; - u32 bSpurPresent; - u32 bSpurAvoided; - u32 nSpursFound; - u32 nZones; - struct MT2063_ExclZone_t *freeZones; - struct MT2063_ExclZone_t *usedZones; - struct MT2063_ExclZone_t MT2063_ExclZones[MT2063_MAX_ZONES]; -}; - -/* - * Parameter for function MT2063_SetPowerMask that specifies the power down - * of various sections of the MT2063. - */ -enum MT2063_Mask_Bits { - MT2063_REG_SD = 0x0040, /* Shutdown regulator */ - MT2063_SRO_SD = 0x0020, /* Shutdown SRO */ - MT2063_AFC_SD = 0x0010, /* Shutdown AFC A/D */ - MT2063_PD_SD = 0x0002, /* Enable power detector shutdown */ - MT2063_PDADC_SD = 0x0001, /* Enable power detector A/D shutdown */ - MT2063_VCO_SD = 0x8000, /* Enable VCO shutdown */ - MT2063_LTX_SD = 0x4000, /* Enable LTX shutdown */ - MT2063_LT1_SD = 0x2000, /* Enable LT1 shutdown */ - MT2063_LNA_SD = 0x1000, /* Enable LNA shutdown */ - MT2063_UPC_SD = 0x0800, /* Enable upconverter shutdown */ - MT2063_DNC_SD = 0x0400, /* Enable downconverter shutdown */ - MT2063_VGA_SD = 0x0200, /* Enable VGA shutdown */ - MT2063_AMP_SD = 0x0100, /* Enable AMP shutdown */ - MT2063_ALL_SD = 0xFF73, /* All shutdown bits for this tuner */ - MT2063_NONE_SD = 0x0000 /* No shutdown bits */ -}; - -/* - * Possible values for MT2063_DNC_OUTPUT - */ -enum MT2063_DNC_Output_Enable { - MT2063_DNC_NONE = 0, - MT2063_DNC_1, - MT2063_DNC_2, - MT2063_DNC_BOTH -}; - -/* - * Two-wire serial bus subaddresses of the tuner registers. - * Also known as the tuner's register addresses. - */ -enum MT2063_Register_Offsets { - MT2063_REG_PART_REV = 0, /* 0x00: Part/Rev Code */ - MT2063_REG_LO1CQ_1, /* 0x01: LO1C Queued Byte 1 */ - MT2063_REG_LO1CQ_2, /* 0x02: LO1C Queued Byte 2 */ - MT2063_REG_LO2CQ_1, /* 0x03: LO2C Queued Byte 1 */ - MT2063_REG_LO2CQ_2, /* 0x04: LO2C Queued Byte 2 */ - MT2063_REG_LO2CQ_3, /* 0x05: LO2C Queued Byte 3 */ - MT2063_REG_RSVD_06, /* 0x06: Reserved */ - MT2063_REG_LO_STATUS, /* 0x07: LO Status */ - MT2063_REG_FIFFC, /* 0x08: FIFF Center */ - MT2063_REG_CLEARTUNE, /* 0x09: ClearTune Filter */ - MT2063_REG_ADC_OUT, /* 0x0A: ADC_OUT */ - MT2063_REG_LO1C_1, /* 0x0B: LO1C Byte 1 */ - MT2063_REG_LO1C_2, /* 0x0C: LO1C Byte 2 */ - MT2063_REG_LO2C_1, /* 0x0D: LO2C Byte 1 */ - MT2063_REG_LO2C_2, /* 0x0E: LO2C Byte 2 */ - MT2063_REG_LO2C_3, /* 0x0F: LO2C Byte 3 */ - MT2063_REG_RSVD_10, /* 0x10: Reserved */ - MT2063_REG_PWR_1, /* 0x11: PWR Byte 1 */ - MT2063_REG_PWR_2, /* 0x12: PWR Byte 2 */ - MT2063_REG_TEMP_STATUS, /* 0x13: Temp Status */ - MT2063_REG_XO_STATUS, /* 0x14: Crystal Status */ - MT2063_REG_RF_STATUS, /* 0x15: RF Attn Status */ - MT2063_REG_FIF_STATUS, /* 0x16: FIF Attn Status */ - MT2063_REG_LNA_OV, /* 0x17: LNA Attn Override */ - MT2063_REG_RF_OV, /* 0x18: RF Attn Override */ - MT2063_REG_FIF_OV, /* 0x19: FIF Attn Override */ - MT2063_REG_LNA_TGT, /* 0x1A: Reserved */ - MT2063_REG_PD1_TGT, /* 0x1B: Pwr Det 1 Target */ - MT2063_REG_PD2_TGT, /* 0x1C: Pwr Det 2 Target */ - MT2063_REG_RSVD_1D, /* 0x1D: Reserved */ - MT2063_REG_RSVD_1E, /* 0x1E: Reserved */ - MT2063_REG_RSVD_1F, /* 0x1F: Reserved */ - MT2063_REG_RSVD_20, /* 0x20: Reserved */ - MT2063_REG_BYP_CTRL, /* 0x21: Bypass Control */ - MT2063_REG_RSVD_22, /* 0x22: Reserved */ - MT2063_REG_RSVD_23, /* 0x23: Reserved */ - MT2063_REG_RSVD_24, /* 0x24: Reserved */ - MT2063_REG_RSVD_25, /* 0x25: Reserved */ - MT2063_REG_RSVD_26, /* 0x26: Reserved */ - MT2063_REG_RSVD_27, /* 0x27: Reserved */ - MT2063_REG_FIFF_CTRL, /* 0x28: FIFF Control */ - MT2063_REG_FIFF_OFFSET, /* 0x29: FIFF Offset */ - MT2063_REG_CTUNE_CTRL, /* 0x2A: Reserved */ - MT2063_REG_CTUNE_OV, /* 0x2B: Reserved */ - MT2063_REG_CTRL_2C, /* 0x2C: Reserved */ - MT2063_REG_FIFF_CTRL2, /* 0x2D: Fiff Control */ - MT2063_REG_RSVD_2E, /* 0x2E: Reserved */ - MT2063_REG_DNC_GAIN, /* 0x2F: DNC Control */ - MT2063_REG_VGA_GAIN, /* 0x30: VGA Gain Ctrl */ - MT2063_REG_RSVD_31, /* 0x31: Reserved */ - MT2063_REG_TEMP_SEL, /* 0x32: Temperature Selection */ - MT2063_REG_RSVD_33, /* 0x33: Reserved */ - MT2063_REG_RSVD_34, /* 0x34: Reserved */ - MT2063_REG_RSVD_35, /* 0x35: Reserved */ - MT2063_REG_RSVD_36, /* 0x36: Reserved */ - MT2063_REG_RSVD_37, /* 0x37: Reserved */ - MT2063_REG_RSVD_38, /* 0x38: Reserved */ - MT2063_REG_RSVD_39, /* 0x39: Reserved */ - MT2063_REG_RSVD_3A, /* 0x3A: Reserved */ - MT2063_REG_RSVD_3B, /* 0x3B: Reserved */ - MT2063_REG_RSVD_3C, /* 0x3C: Reserved */ - MT2063_REG_END_REGS -}; - -struct mt2063_state { - struct i2c_adapter *i2c; - - bool init; - - const struct mt2063_config *config; - struct dvb_tuner_ops ops; - struct dvb_frontend *frontend; - struct tuner_state status; - - u32 frequency; - u32 srate; - u32 bandwidth; - u32 reference; - - u32 tuner_id; - struct MT2063_AvoidSpursData_t AS_Data; - u32 f_IF1_actual; - u32 rcvr_mode; - u32 ctfilt_sw; - u32 CTFiltMax[31]; - u32 num_regs; - u8 reg[MT2063_REG_END_REGS]; -}; - -/* - * mt2063_write - Write data into the I2C bus - */ -static u32 mt2063_write(struct mt2063_state *state, u8 reg, u8 *data, u32 len) -{ - struct dvb_frontend *fe = state->frontend; - int ret; - u8 buf[60]; - struct i2c_msg msg = { - .addr = state->config->tuner_address, - .flags = 0, - .buf = buf, - .len = len + 1 - }; - - dprintk(2, "\n"); - - msg.buf[0] = reg; - memcpy(msg.buf + 1, data, len); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - ret = i2c_transfer(state->i2c, &msg, 1); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - if (ret < 0) - printk(KERN_ERR "%s error ret=%d\n", __func__, ret); - - return ret; -} - -/* - * mt2063_write - Write register data into the I2C bus, caching the value - */ -static u32 mt2063_setreg(struct mt2063_state *state, u8 reg, u8 val) -{ - u32 status; - - dprintk(2, "\n"); - - if (reg >= MT2063_REG_END_REGS) - return -ERANGE; - - status = mt2063_write(state, reg, &val, 1); - if (status < 0) - return status; - - state->reg[reg] = val; - - return 0; -} - -/* - * mt2063_read - Read data from the I2C bus - */ -static u32 mt2063_read(struct mt2063_state *state, - u8 subAddress, u8 *pData, u32 cnt) -{ - u32 status = 0; /* Status to be returned */ - struct dvb_frontend *fe = state->frontend; - u32 i = 0; - - dprintk(2, "addr 0x%02x, cnt %d\n", subAddress, cnt); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - for (i = 0; i < cnt; i++) { - u8 b0[] = { subAddress + i }; - struct i2c_msg msg[] = { - { - .addr = state->config->tuner_address, - .flags = 0, - .buf = b0, - .len = 1 - }, { - .addr = state->config->tuner_address, - .flags = I2C_M_RD, - .buf = pData + i, - .len = 1 - } - }; - - status = i2c_transfer(state->i2c, msg, 2); - dprintk(2, "addr 0x%02x, ret = %d, val = 0x%02x\n", - subAddress + i, status, *(pData + i)); - if (status < 0) - break; - } - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - if (status < 0) - printk(KERN_ERR "Can't read from address 0x%02x,\n", - subAddress + i); - - return status; -} - -/* - * FIXME: Is this really needed? - */ -static int MT2063_Sleep(struct dvb_frontend *fe) -{ - /* - * ToDo: Add code here to implement a OS blocking - */ - msleep(100); - - return 0; -} - -/* - * Microtune spur avoidance - */ - -/* Implement ceiling, floor functions. */ -#define ceil(n, d) (((n) < 0) ? (-((-(n))/(d))) : (n)/(d) + ((n)%(d) != 0)) -#define floor(n, d) (((n) < 0) ? (-((-(n))/(d))) - ((n)%(d) != 0) : (n)/(d)) - -struct MT2063_FIFZone_t { - s32 min_; - s32 max_; -}; - -static struct MT2063_ExclZone_t *InsertNode(struct MT2063_AvoidSpursData_t - *pAS_Info, - struct MT2063_ExclZone_t *pPrevNode) -{ - struct MT2063_ExclZone_t *pNode; - - dprintk(2, "\n"); - - /* Check for a node in the free list */ - if (pAS_Info->freeZones != NULL) { - /* Use one from the free list */ - pNode = pAS_Info->freeZones; - pAS_Info->freeZones = pNode->next_; - } else { - /* Grab a node from the array */ - pNode = &pAS_Info->MT2063_ExclZones[pAS_Info->nZones]; - } - - if (pPrevNode != NULL) { - pNode->next_ = pPrevNode->next_; - pPrevNode->next_ = pNode; - } else { /* insert at the beginning of the list */ - - pNode->next_ = pAS_Info->usedZones; - pAS_Info->usedZones = pNode; - } - - pAS_Info->nZones++; - return pNode; -} - -static struct MT2063_ExclZone_t *RemoveNode(struct MT2063_AvoidSpursData_t - *pAS_Info, - struct MT2063_ExclZone_t *pPrevNode, - struct MT2063_ExclZone_t - *pNodeToRemove) -{ - struct MT2063_ExclZone_t *pNext = pNodeToRemove->next_; - - dprintk(2, "\n"); - - /* Make previous node point to the subsequent node */ - if (pPrevNode != NULL) - pPrevNode->next_ = pNext; - - /* Add pNodeToRemove to the beginning of the freeZones */ - pNodeToRemove->next_ = pAS_Info->freeZones; - pAS_Info->freeZones = pNodeToRemove; - - /* Decrement node count */ - pAS_Info->nZones--; - - return pNext; -} - -/* - * MT_AddExclZone() - * - * Add (and merge) an exclusion zone into the list. - * If the range (f_min, f_max) is totally outside the - * 1st IF BW, ignore the entry. - * If the range (f_min, f_max) is negative, ignore the entry. - */ -static void MT2063_AddExclZone(struct MT2063_AvoidSpursData_t *pAS_Info, - u32 f_min, u32 f_max) -{ - struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones; - struct MT2063_ExclZone_t *pPrev = NULL; - struct MT2063_ExclZone_t *pNext = NULL; - - dprintk(2, "\n"); - - /* Check to see if this overlaps the 1st IF filter */ - if ((f_max > (pAS_Info->f_if1_Center - (pAS_Info->f_if1_bw / 2))) - && (f_min < (pAS_Info->f_if1_Center + (pAS_Info->f_if1_bw / 2))) - && (f_min < f_max)) { - /* - * 1 2 3 4 5 6 - * - * New entry: |---| |--| |--| |-| |---| |--| - * or or or or or - * Existing: |--| |--| |--| |---| |-| |--| - */ - - /* Check for our place in the list */ - while ((pNode != NULL) && (pNode->max_ < f_min)) { - pPrev = pNode; - pNode = pNode->next_; - } - - if ((pNode != NULL) && (pNode->min_ < f_max)) { - /* Combine me with pNode */ - if (f_min < pNode->min_) - pNode->min_ = f_min; - if (f_max > pNode->max_) - pNode->max_ = f_max; - } else { - pNode = InsertNode(pAS_Info, pPrev); - pNode->min_ = f_min; - pNode->max_ = f_max; - } - - /* Look for merging possibilities */ - pNext = pNode->next_; - while ((pNext != NULL) && (pNext->min_ < pNode->max_)) { - if (pNext->max_ > pNode->max_) - pNode->max_ = pNext->max_; - /* Remove pNext, return ptr to pNext->next */ - pNext = RemoveNode(pAS_Info, pNode, pNext); - } - } -} - -/* - * Reset all exclusion zones. - * Add zones to protect the PLL FracN regions near zero - */ -static void MT2063_ResetExclZones(struct MT2063_AvoidSpursData_t *pAS_Info) -{ - u32 center; - - dprintk(2, "\n"); - - pAS_Info->nZones = 0; /* this clears the used list */ - pAS_Info->usedZones = NULL; /* reset ptr */ - pAS_Info->freeZones = NULL; /* reset ptr */ - - center = - pAS_Info->f_ref * - ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 + - pAS_Info->f_in) / pAS_Info->f_ref) - pAS_Info->f_in; - while (center < - pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 + - pAS_Info->f_LO1_FracN_Avoid) { - /* Exclude LO1 FracN */ - MT2063_AddExclZone(pAS_Info, - center - pAS_Info->f_LO1_FracN_Avoid, - center - 1); - MT2063_AddExclZone(pAS_Info, center + 1, - center + pAS_Info->f_LO1_FracN_Avoid); - center += pAS_Info->f_ref; - } - - center = - pAS_Info->f_ref * - ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 - - pAS_Info->f_out) / pAS_Info->f_ref) + pAS_Info->f_out; - while (center < - pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 + - pAS_Info->f_LO2_FracN_Avoid) { - /* Exclude LO2 FracN */ - MT2063_AddExclZone(pAS_Info, - center - pAS_Info->f_LO2_FracN_Avoid, - center - 1); - MT2063_AddExclZone(pAS_Info, center + 1, - center + pAS_Info->f_LO2_FracN_Avoid); - center += pAS_Info->f_ref; - } - - if (MT2063_EXCLUDE_US_DECT_FREQUENCIES(pAS_Info->avoidDECT)) { - /* Exclude LO1 values that conflict with DECT channels */ - MT2063_AddExclZone(pAS_Info, 1920836000 - pAS_Info->f_in, 1922236000 - pAS_Info->f_in); /* Ctr = 1921.536 */ - MT2063_AddExclZone(pAS_Info, 1922564000 - pAS_Info->f_in, 1923964000 - pAS_Info->f_in); /* Ctr = 1923.264 */ - MT2063_AddExclZone(pAS_Info, 1924292000 - pAS_Info->f_in, 1925692000 - pAS_Info->f_in); /* Ctr = 1924.992 */ - MT2063_AddExclZone(pAS_Info, 1926020000 - pAS_Info->f_in, 1927420000 - pAS_Info->f_in); /* Ctr = 1926.720 */ - MT2063_AddExclZone(pAS_Info, 1927748000 - pAS_Info->f_in, 1929148000 - pAS_Info->f_in); /* Ctr = 1928.448 */ - } - - if (MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(pAS_Info->avoidDECT)) { - MT2063_AddExclZone(pAS_Info, 1896644000 - pAS_Info->f_in, 1898044000 - pAS_Info->f_in); /* Ctr = 1897.344 */ - MT2063_AddExclZone(pAS_Info, 1894916000 - pAS_Info->f_in, 1896316000 - pAS_Info->f_in); /* Ctr = 1895.616 */ - MT2063_AddExclZone(pAS_Info, 1893188000 - pAS_Info->f_in, 1894588000 - pAS_Info->f_in); /* Ctr = 1893.888 */ - MT2063_AddExclZone(pAS_Info, 1891460000 - pAS_Info->f_in, 1892860000 - pAS_Info->f_in); /* Ctr = 1892.16 */ - MT2063_AddExclZone(pAS_Info, 1889732000 - pAS_Info->f_in, 1891132000 - pAS_Info->f_in); /* Ctr = 1890.432 */ - MT2063_AddExclZone(pAS_Info, 1888004000 - pAS_Info->f_in, 1889404000 - pAS_Info->f_in); /* Ctr = 1888.704 */ - MT2063_AddExclZone(pAS_Info, 1886276000 - pAS_Info->f_in, 1887676000 - pAS_Info->f_in); /* Ctr = 1886.976 */ - MT2063_AddExclZone(pAS_Info, 1884548000 - pAS_Info->f_in, 1885948000 - pAS_Info->f_in); /* Ctr = 1885.248 */ - MT2063_AddExclZone(pAS_Info, 1882820000 - pAS_Info->f_in, 1884220000 - pAS_Info->f_in); /* Ctr = 1883.52 */ - MT2063_AddExclZone(pAS_Info, 1881092000 - pAS_Info->f_in, 1882492000 - pAS_Info->f_in); /* Ctr = 1881.792 */ - } -} - -/* - * MT_ChooseFirstIF - Choose the best available 1st IF - * If f_Desired is not excluded, choose that first. - * Otherwise, return the value closest to f_Center that is - * not excluded - */ -static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info) -{ - /* - * Update "f_Desired" to be the nearest "combinational-multiple" of - * "f_LO1_Step". - * The resulting number, F_LO1 must be a multiple of f_LO1_Step. - * And F_LO1 is the arithmetic sum of f_in + f_Center. - * Neither f_in, nor f_Center must be a multiple of f_LO1_Step. - * However, the sum must be. - */ - const u32 f_Desired = - pAS_Info->f_LO1_Step * - ((pAS_Info->f_if1_Request + pAS_Info->f_in + - pAS_Info->f_LO1_Step / 2) / pAS_Info->f_LO1_Step) - - pAS_Info->f_in; - const u32 f_Step = - (pAS_Info->f_LO1_Step > - pAS_Info->f_LO2_Step) ? pAS_Info->f_LO1_Step : pAS_Info-> - f_LO2_Step; - u32 f_Center; - s32 i; - s32 j = 0; - u32 bDesiredExcluded = 0; - u32 bZeroExcluded = 0; - s32 tmpMin, tmpMax; - s32 bestDiff; - struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones; - struct MT2063_FIFZone_t zones[MT2063_MAX_ZONES]; - - dprintk(2, "\n"); - - if (pAS_Info->nZones == 0) - return f_Desired; - - /* - * f_Center needs to be an integer multiple of f_Step away - * from f_Desired - */ - if (pAS_Info->f_if1_Center > f_Desired) - f_Center = - f_Desired + - f_Step * - ((pAS_Info->f_if1_Center - f_Desired + - f_Step / 2) / f_Step); - else - f_Center = - f_Desired - - f_Step * - ((f_Desired - pAS_Info->f_if1_Center + - f_Step / 2) / f_Step); - - /* - * Take MT_ExclZones, center around f_Center and change the - * resolution to f_Step - */ - while (pNode != NULL) { - /* floor function */ - tmpMin = - floor((s32) (pNode->min_ - f_Center), (s32) f_Step); - - /* ceil function */ - tmpMax = - ceil((s32) (pNode->max_ - f_Center), (s32) f_Step); - - if ((pNode->min_ < f_Desired) && (pNode->max_ > f_Desired)) - bDesiredExcluded = 1; - - if ((tmpMin < 0) && (tmpMax > 0)) - bZeroExcluded = 1; - - /* See if this zone overlaps the previous */ - if ((j > 0) && (tmpMin < zones[j - 1].max_)) - zones[j - 1].max_ = tmpMax; - else { - /* Add new zone */ - zones[j].min_ = tmpMin; - zones[j].max_ = tmpMax; - j++; - } - pNode = pNode->next_; - } - - /* - * If the desired is okay, return with it - */ - if (bDesiredExcluded == 0) - return f_Desired; - - /* - * If the desired is excluded and the center is okay, return with it - */ - if (bZeroExcluded == 0) - return f_Center; - - /* Find the value closest to 0 (f_Center) */ - bestDiff = zones[0].min_; - for (i = 0; i < j; i++) { - if (abs(zones[i].min_) < abs(bestDiff)) - bestDiff = zones[i].min_; - if (abs(zones[i].max_) < abs(bestDiff)) - bestDiff = zones[i].max_; - } - - if (bestDiff < 0) - return f_Center - ((u32) (-bestDiff) * f_Step); - - return f_Center + (bestDiff * f_Step); -} - -/** - * gcd() - Uses Euclid's algorithm - * - * @u, @v: Unsigned values whose GCD is desired. - * - * Returns THE greatest common divisor of u and v, if either value is 0, - * the other value is returned as the result. - */ -static u32 MT2063_gcd(u32 u, u32 v) -{ - u32 r; - - while (v != 0) { - r = u % v; - u = v; - v = r; - } - - return u; -} - -/** - * IsSpurInBand() - Checks to see if a spur will be present within the IF's - * bandwidth. (fIFOut +/- fIFBW, -fIFOut +/- fIFBW) - * - * ma mb mc md - * <--+-+-+-------------------+-------------------+-+-+--> - * | ^ 0 ^ | - * ^ b=-fIFOut+fIFBW/2 -b=+fIFOut-fIFBW/2 ^ - * a=-fIFOut-fIFBW/2 -a=+fIFOut+fIFBW/2 - * - * Note that some equations are doubled to prevent round-off - * problems when calculating fIFBW/2 - * - * @pAS_Info: Avoid Spurs information block - * @fm: If spur, amount f_IF1 has to move negative - * @fp: If spur, amount f_IF1 has to move positive - * - * Returns 1 if an LO spur would be present, otherwise 0. - */ -static u32 IsSpurInBand(struct MT2063_AvoidSpursData_t *pAS_Info, - u32 *fm, u32 * fp) -{ - /* - ** Calculate LO frequency settings. - */ - u32 n, n0; - const u32 f_LO1 = pAS_Info->f_LO1; - const u32 f_LO2 = pAS_Info->f_LO2; - const u32 d = pAS_Info->f_out + pAS_Info->f_out_bw / 2; - const u32 c = d - pAS_Info->f_out_bw; - const u32 f = pAS_Info->f_zif_bw / 2; - const u32 f_Scale = (f_LO1 / (UINT_MAX / 2 / pAS_Info->maxH1)) + 1; - s32 f_nsLO1, f_nsLO2; - s32 f_Spur; - u32 ma, mb, mc, md, me, mf; - u32 lo_gcd, gd_Scale, gc_Scale, gf_Scale, hgds, hgfs, hgcs; - - dprintk(2, "\n"); - - *fm = 0; - - /* - ** For each edge (d, c & f), calculate a scale, based on the gcd - ** of f_LO1, f_LO2 and the edge value. Use the larger of this - ** gcd-based scale factor or f_Scale. - */ - lo_gcd = MT2063_gcd(f_LO1, f_LO2); - gd_Scale = max((u32) MT2063_gcd(lo_gcd, d), f_Scale); - hgds = gd_Scale / 2; - gc_Scale = max((u32) MT2063_gcd(lo_gcd, c), f_Scale); - hgcs = gc_Scale / 2; - gf_Scale = max((u32) MT2063_gcd(lo_gcd, f), f_Scale); - hgfs = gf_Scale / 2; - - n0 = DIV_ROUND_UP(f_LO2 - d, f_LO1 - f_LO2); - - /* Check out all multiples of LO1 from n0 to m_maxLOSpurHarmonic */ - for (n = n0; n <= pAS_Info->maxH1; ++n) { - md = (n * ((f_LO1 + hgds) / gd_Scale) - - ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale); - - /* If # fLO2 harmonics > m_maxLOSpurHarmonic, then no spurs present */ - if (md >= pAS_Info->maxH1) - break; - - ma = (n * ((f_LO1 + hgds) / gd_Scale) + - ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale); - - /* If no spurs between +/- (f_out + f_IFBW/2), then try next harmonic */ - if (md == ma) - continue; - - mc = (n * ((f_LO1 + hgcs) / gc_Scale) - - ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale); - if (mc != md) { - f_nsLO1 = (s32) (n * (f_LO1 / gc_Scale)); - f_nsLO2 = (s32) (mc * (f_LO2 / gc_Scale)); - f_Spur = - (gc_Scale * (f_nsLO1 - f_nsLO2)) + - n * (f_LO1 % gc_Scale) - mc * (f_LO2 % gc_Scale); - - *fp = ((f_Spur - (s32) c) / (mc - n)) + 1; - *fm = (((s32) d - f_Spur) / (mc - n)) + 1; - return 1; - } - - /* Location of Zero-IF-spur to be checked */ - me = (n * ((f_LO1 + hgfs) / gf_Scale) + - ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale); - mf = (n * ((f_LO1 + hgfs) / gf_Scale) - - ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale); - if (me != mf) { - f_nsLO1 = n * (f_LO1 / gf_Scale); - f_nsLO2 = me * (f_LO2 / gf_Scale); - f_Spur = - (gf_Scale * (f_nsLO1 - f_nsLO2)) + - n * (f_LO1 % gf_Scale) - me * (f_LO2 % gf_Scale); - - *fp = ((f_Spur + (s32) f) / (me - n)) + 1; - *fm = (((s32) f - f_Spur) / (me - n)) + 1; - return 1; - } - - mb = (n * ((f_LO1 + hgcs) / gc_Scale) + - ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale); - if (ma != mb) { - f_nsLO1 = n * (f_LO1 / gc_Scale); - f_nsLO2 = ma * (f_LO2 / gc_Scale); - f_Spur = - (gc_Scale * (f_nsLO1 - f_nsLO2)) + - n * (f_LO1 % gc_Scale) - ma * (f_LO2 % gc_Scale); - - *fp = (((s32) d + f_Spur) / (ma - n)) + 1; - *fm = (-(f_Spur + (s32) c) / (ma - n)) + 1; - return 1; - } - } - - /* No spurs found */ - return 0; -} - -/* - * MT_AvoidSpurs() - Main entry point to avoid spurs. - * Checks for existing spurs in present LO1, LO2 freqs - * and if present, chooses spur-free LO1, LO2 combination - * that tunes the same input/output frequencies. - */ -static u32 MT2063_AvoidSpurs(struct MT2063_AvoidSpursData_t *pAS_Info) -{ - u32 status = 0; - u32 fm, fp; /* restricted range on LO's */ - pAS_Info->bSpurAvoided = 0; - pAS_Info->nSpursFound = 0; - - dprintk(2, "\n"); - - if (pAS_Info->maxH1 == 0) - return 0; - - /* - * Avoid LO Generated Spurs - * - * Make sure that have no LO-related spurs within the IF output - * bandwidth. - * - * If there is an LO spur in this band, start at the current IF1 frequency - * and work out until we find a spur-free frequency or run up against the - * 1st IF SAW band edge. Use temporary copies of fLO1 and fLO2 so that they - * will be unchanged if a spur-free setting is not found. - */ - pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp); - if (pAS_Info->bSpurPresent) { - u32 zfIF1 = pAS_Info->f_LO1 - pAS_Info->f_in; /* current attempt at a 1st IF */ - u32 zfLO1 = pAS_Info->f_LO1; /* current attempt at an LO1 freq */ - u32 zfLO2 = pAS_Info->f_LO2; /* current attempt at an LO2 freq */ - u32 delta_IF1; - u32 new_IF1; - - /* - ** Spur was found, attempt to find a spur-free 1st IF - */ - do { - pAS_Info->nSpursFound++; - - /* Raise f_IF1_upper, if needed */ - MT2063_AddExclZone(pAS_Info, zfIF1 - fm, zfIF1 + fp); - - /* Choose next IF1 that is closest to f_IF1_CENTER */ - new_IF1 = MT2063_ChooseFirstIF(pAS_Info); - - if (new_IF1 > zfIF1) { - pAS_Info->f_LO1 += (new_IF1 - zfIF1); - pAS_Info->f_LO2 += (new_IF1 - zfIF1); - } else { - pAS_Info->f_LO1 -= (zfIF1 - new_IF1); - pAS_Info->f_LO2 -= (zfIF1 - new_IF1); - } - zfIF1 = new_IF1; - - if (zfIF1 > pAS_Info->f_if1_Center) - delta_IF1 = zfIF1 - pAS_Info->f_if1_Center; - else - delta_IF1 = pAS_Info->f_if1_Center - zfIF1; - - pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp); - /* - * Continue while the new 1st IF is still within the 1st IF bandwidth - * and there is a spur in the band (again) - */ - } while ((2 * delta_IF1 + pAS_Info->f_out_bw <= pAS_Info->f_if1_bw) && pAS_Info->bSpurPresent); - - /* - * Use the LO-spur free values found. If the search went all - * the way to the 1st IF band edge and always found spurs, just - * leave the original choice. It's as "good" as any other. - */ - if (pAS_Info->bSpurPresent == 1) { - status |= MT2063_SPUR_PRESENT_ERR; - pAS_Info->f_LO1 = zfLO1; - pAS_Info->f_LO2 = zfLO2; - } else - pAS_Info->bSpurAvoided = 1; - } - - status |= - ((pAS_Info-> - nSpursFound << MT2063_SPUR_SHIFT) & MT2063_SPUR_CNT_MASK); - - return status; -} - -/* - * Constants used by the tuning algorithm - */ -#define MT2063_REF_FREQ (16000000UL) /* Reference oscillator Frequency (in Hz) */ -#define MT2063_IF1_BW (22000000UL) /* The IF1 filter bandwidth (in Hz) */ -#define MT2063_TUNE_STEP_SIZE (50000UL) /* Tune in steps of 50 kHz */ -#define MT2063_SPUR_STEP_HZ (250000UL) /* Step size (in Hz) to move IF1 when avoiding spurs */ -#define MT2063_ZIF_BW (2000000UL) /* Zero-IF spur-free bandwidth (in Hz) */ -#define MT2063_MAX_HARMONICS_1 (15UL) /* Highest intra-tuner LO Spur Harmonic to be avoided */ -#define MT2063_MAX_HARMONICS_2 (5UL) /* Highest inter-tuner LO Spur Harmonic to be avoided */ -#define MT2063_MIN_LO_SEP (1000000UL) /* Minimum inter-tuner LO frequency separation */ -#define MT2063_LO1_FRACN_AVOID (0UL) /* LO1 FracN numerator avoid region (in Hz) */ -#define MT2063_LO2_FRACN_AVOID (199999UL) /* LO2 FracN numerator avoid region (in Hz) */ -#define MT2063_MIN_FIN_FREQ (44000000UL) /* Minimum input frequency (in Hz) */ -#define MT2063_MAX_FIN_FREQ (1100000000UL) /* Maximum input frequency (in Hz) */ -#define MT2063_MIN_FOUT_FREQ (36000000UL) /* Minimum output frequency (in Hz) */ -#define MT2063_MAX_FOUT_FREQ (57000000UL) /* Maximum output frequency (in Hz) */ -#define MT2063_MIN_DNC_FREQ (1293000000UL) /* Minimum LO2 frequency (in Hz) */ -#define MT2063_MAX_DNC_FREQ (1614000000UL) /* Maximum LO2 frequency (in Hz) */ -#define MT2063_MIN_UPC_FREQ (1396000000UL) /* Minimum LO1 frequency (in Hz) */ -#define MT2063_MAX_UPC_FREQ (2750000000UL) /* Maximum LO1 frequency (in Hz) */ - -/* - * Define the supported Part/Rev codes for the MT2063 - */ -#define MT2063_B0 (0x9B) -#define MT2063_B1 (0x9C) -#define MT2063_B2 (0x9D) -#define MT2063_B3 (0x9E) - -/** - * mt2063_lockStatus - Checks to see if LO1 and LO2 are locked - * - * @state: struct mt2063_state pointer - * - * This function returns 0, if no lock, 1 if locked and a value < 1 if error - */ -static unsigned int mt2063_lockStatus(struct mt2063_state *state) -{ - const u32 nMaxWait = 100; /* wait a maximum of 100 msec */ - const u32 nPollRate = 2; /* poll status bits every 2 ms */ - const u32 nMaxLoops = nMaxWait / nPollRate; - const u8 LO1LK = 0x80; - u8 LO2LK = 0x08; - u32 status; - u32 nDelays = 0; - - dprintk(2, "\n"); - - /* LO2 Lock bit was in a different place for B0 version */ - if (state->tuner_id == MT2063_B0) - LO2LK = 0x40; - - do { - status = mt2063_read(state, MT2063_REG_LO_STATUS, - &state->reg[MT2063_REG_LO_STATUS], 1); - - if (status < 0) - return status; - - if ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) == - (LO1LK | LO2LK)) { - return TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO; - } - msleep(nPollRate); /* Wait between retries */ - } while (++nDelays < nMaxLoops); - - /* - * Got no lock or partial lock - */ - return 0; -} - -/* - * Constants for setting receiver modes. - * (6 modes defined at this time, enumerated by mt2063_delivery_sys) - * (DNC1GC & DNC2GC are the values, which are used, when the specific - * DNC Output is selected, the other is always off) - * - * enum mt2063_delivery_sys - * -------------+---------------------------------------------- - * Mode 0 : | MT2063_CABLE_QAM - * Mode 1 : | MT2063_CABLE_ANALOG - * Mode 2 : | MT2063_OFFAIR_COFDM - * Mode 3 : | MT2063_OFFAIR_COFDM_SAWLESS - * Mode 4 : | MT2063_OFFAIR_ANALOG - * Mode 5 : | MT2063_OFFAIR_8VSB - * --------------+---------------------------------------------- - * - * |<---------- Mode -------------->| - * Reg Field | 0 | 1 | 2 | 3 | 4 | 5 | - * ------------+-----+-----+-----+-----+-----+-----+ - * RFAGCen | OFF | OFF | OFF | OFF | OFF | OFF - * LNARin | 0 | 0 | 3 | 3 | 3 | 3 - * FIFFQen | 1 | 1 | 1 | 1 | 1 | 1 - * FIFFq | 0 | 0 | 0 | 0 | 0 | 0 - * DNC1gc | 0 | 0 | 0 | 0 | 0 | 0 - * DNC2gc | 0 | 0 | 0 | 0 | 0 | 0 - * GCU Auto | 1 | 1 | 1 | 1 | 1 | 1 - * LNA max Atn | 31 | 31 | 31 | 31 | 31 | 31 - * LNA Target | 44 | 43 | 43 | 43 | 43 | 43 - * ign RF Ovl | 0 | 0 | 0 | 0 | 0 | 0 - * RF max Atn | 31 | 31 | 31 | 31 | 31 | 31 - * PD1 Target | 36 | 36 | 38 | 38 | 36 | 38 - * ign FIF Ovl | 0 | 0 | 0 | 0 | 0 | 0 - * FIF max Atn | 5 | 5 | 5 | 5 | 5 | 5 - * PD2 Target | 40 | 33 | 42 | 42 | 33 | 42 - */ - -enum mt2063_delivery_sys { - MT2063_CABLE_QAM = 0, - MT2063_CABLE_ANALOG, - MT2063_OFFAIR_COFDM, - MT2063_OFFAIR_COFDM_SAWLESS, - MT2063_OFFAIR_ANALOG, - MT2063_OFFAIR_8VSB, - MT2063_NUM_RCVR_MODES -}; - -static const char *mt2063_mode_name[] = { - [MT2063_CABLE_QAM] = "digital cable", - [MT2063_CABLE_ANALOG] = "analog cable", - [MT2063_OFFAIR_COFDM] = "digital offair", - [MT2063_OFFAIR_COFDM_SAWLESS] = "digital offair without SAW", - [MT2063_OFFAIR_ANALOG] = "analog offair", - [MT2063_OFFAIR_8VSB] = "analog offair 8vsb", -}; - -static const u8 RFAGCEN[] = { 0, 0, 0, 0, 0, 0 }; -static const u8 LNARIN[] = { 0, 0, 3, 3, 3, 3 }; -static const u8 FIFFQEN[] = { 1, 1, 1, 1, 1, 1 }; -static const u8 FIFFQ[] = { 0, 0, 0, 0, 0, 0 }; -static const u8 DNC1GC[] = { 0, 0, 0, 0, 0, 0 }; -static const u8 DNC2GC[] = { 0, 0, 0, 0, 0, 0 }; -static const u8 ACLNAMAX[] = { 31, 31, 31, 31, 31, 31 }; -static const u8 LNATGT[] = { 44, 43, 43, 43, 43, 43 }; -static const u8 RFOVDIS[] = { 0, 0, 0, 0, 0, 0 }; -static const u8 ACRFMAX[] = { 31, 31, 31, 31, 31, 31 }; -static const u8 PD1TGT[] = { 36, 36, 38, 38, 36, 38 }; -static const u8 FIFOVDIS[] = { 0, 0, 0, 0, 0, 0 }; -static const u8 ACFIFMAX[] = { 29, 29, 29, 29, 29, 29 }; -static const u8 PD2TGT[] = { 40, 33, 38, 42, 30, 38 }; - -/* - * mt2063_set_dnc_output_enable() - */ -static u32 mt2063_get_dnc_output_enable(struct mt2063_state *state, - enum MT2063_DNC_Output_Enable *pValue) -{ - dprintk(2, "\n"); - - if ((state->reg[MT2063_REG_DNC_GAIN] & 0x03) == 0x03) { /* if DNC1 is off */ - if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */ - *pValue = MT2063_DNC_NONE; - else - *pValue = MT2063_DNC_2; - } else { /* DNC1 is on */ - if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */ - *pValue = MT2063_DNC_1; - else - *pValue = MT2063_DNC_BOTH; - } - return 0; -} - -/* - * mt2063_set_dnc_output_enable() - */ -static u32 mt2063_set_dnc_output_enable(struct mt2063_state *state, - enum MT2063_DNC_Output_Enable nValue) -{ - u32 status = 0; /* Status to be returned */ - u8 val = 0; - - dprintk(2, "\n"); - - /* selects, which DNC output is used */ - switch (nValue) { - case MT2063_DNC_NONE: - val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */ - if (state->reg[MT2063_REG_DNC_GAIN] != - val) - status |= - mt2063_setreg(state, - MT2063_REG_DNC_GAIN, - val); - - val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */ - if (state->reg[MT2063_REG_VGA_GAIN] != - val) - status |= - mt2063_setreg(state, - MT2063_REG_VGA_GAIN, - val); - - val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */ - if (state->reg[MT2063_REG_RSVD_20] != - val) - status |= - mt2063_setreg(state, - MT2063_REG_RSVD_20, - val); - - break; - case MT2063_DNC_1: - val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */ - if (state->reg[MT2063_REG_DNC_GAIN] != - val) - status |= - mt2063_setreg(state, - MT2063_REG_DNC_GAIN, - val); - - val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */ - if (state->reg[MT2063_REG_VGA_GAIN] != - val) - status |= - mt2063_setreg(state, - MT2063_REG_VGA_GAIN, - val); - - val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */ - if (state->reg[MT2063_REG_RSVD_20] != - val) - status |= - mt2063_setreg(state, - MT2063_REG_RSVD_20, - val); - - break; - case MT2063_DNC_2: - val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */ - if (state->reg[MT2063_REG_DNC_GAIN] != - val) - status |= - mt2063_setreg(state, - MT2063_REG_DNC_GAIN, - val); - - val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */ - if (state->reg[MT2063_REG_VGA_GAIN] != - val) - status |= - mt2063_setreg(state, - MT2063_REG_VGA_GAIN, - val); - - val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */ - if (state->reg[MT2063_REG_RSVD_20] != - val) - status |= - mt2063_setreg(state, - MT2063_REG_RSVD_20, - val); - - break; - case MT2063_DNC_BOTH: - val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */ - if (state->reg[MT2063_REG_DNC_GAIN] != - val) - status |= - mt2063_setreg(state, - MT2063_REG_DNC_GAIN, - val); - - val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */ - if (state->reg[MT2063_REG_VGA_GAIN] != - val) - status |= - mt2063_setreg(state, - MT2063_REG_VGA_GAIN, - val); - - val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */ - if (state->reg[MT2063_REG_RSVD_20] != - val) - status |= - mt2063_setreg(state, - MT2063_REG_RSVD_20, - val); - - break; - default: - break; - } - - return status; -} - -/* - * MT2063_SetReceiverMode() - Set the MT2063 receiver mode, according with - * the selected enum mt2063_delivery_sys type. - * - * (DNC1GC & DNC2GC are the values, which are used, when the specific - * DNC Output is selected, the other is always off) - * - * @state: ptr to mt2063_state structure - * @Mode: desired reciever delivery system - * - * Note: Register cache must be valid for it to work - */ - -static u32 MT2063_SetReceiverMode(struct mt2063_state *state, - enum mt2063_delivery_sys Mode) -{ - u32 status = 0; /* Status to be returned */ - u8 val; - u32 longval; - - dprintk(2, "\n"); - - if (Mode >= MT2063_NUM_RCVR_MODES) - status = -ERANGE; - - /* RFAGCen */ - if (status >= 0) { - val = - (state-> - reg[MT2063_REG_PD1_TGT] & (u8) ~0x40) | (RFAGCEN[Mode] - ? 0x40 : - 0x00); - if (state->reg[MT2063_REG_PD1_TGT] != val) - status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val); - } - - /* LNARin */ - if (status >= 0) { - u8 val = (state->reg[MT2063_REG_CTRL_2C] & (u8) ~0x03) | - (LNARIN[Mode] & 0x03); - if (state->reg[MT2063_REG_CTRL_2C] != val) - status |= mt2063_setreg(state, MT2063_REG_CTRL_2C, val); - } - - /* FIFFQEN and FIFFQ */ - if (status >= 0) { - val = - (state-> - reg[MT2063_REG_FIFF_CTRL2] & (u8) ~0xF0) | - (FIFFQEN[Mode] << 7) | (FIFFQ[Mode] << 4); - if (state->reg[MT2063_REG_FIFF_CTRL2] != val) { - status |= - mt2063_setreg(state, MT2063_REG_FIFF_CTRL2, val); - /* trigger FIFF calibration, needed after changing FIFFQ */ - val = - (state->reg[MT2063_REG_FIFF_CTRL] | (u8) 0x01); - status |= - mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val); - val = - (state-> - reg[MT2063_REG_FIFF_CTRL] & (u8) ~0x01); - status |= - mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val); - } - } - - /* DNC1GC & DNC2GC */ - status |= mt2063_get_dnc_output_enable(state, &longval); - status |= mt2063_set_dnc_output_enable(state, longval); - - /* acLNAmax */ - if (status >= 0) { - u8 val = (state->reg[MT2063_REG_LNA_OV] & (u8) ~0x1F) | - (ACLNAMAX[Mode] & 0x1F); - if (state->reg[MT2063_REG_LNA_OV] != val) - status |= mt2063_setreg(state, MT2063_REG_LNA_OV, val); - } - - /* LNATGT */ - if (status >= 0) { - u8 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x3F) | - (LNATGT[Mode] & 0x3F); - if (state->reg[MT2063_REG_LNA_TGT] != val) - status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val); - } - - /* ACRF */ - if (status >= 0) { - u8 val = (state->reg[MT2063_REG_RF_OV] & (u8) ~0x1F) | - (ACRFMAX[Mode] & 0x1F); - if (state->reg[MT2063_REG_RF_OV] != val) - status |= mt2063_setreg(state, MT2063_REG_RF_OV, val); - } - - /* PD1TGT */ - if (status >= 0) { - u8 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x3F) | - (PD1TGT[Mode] & 0x3F); - if (state->reg[MT2063_REG_PD1_TGT] != val) - status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val); - } - - /* FIFATN */ - if (status >= 0) { - u8 val = ACFIFMAX[Mode]; - if (state->reg[MT2063_REG_PART_REV] != MT2063_B3 && val > 5) - val = 5; - val = (state->reg[MT2063_REG_FIF_OV] & (u8) ~0x1F) | - (val & 0x1F); - if (state->reg[MT2063_REG_FIF_OV] != val) - status |= mt2063_setreg(state, MT2063_REG_FIF_OV, val); - } - - /* PD2TGT */ - if (status >= 0) { - u8 val = (state->reg[MT2063_REG_PD2_TGT] & (u8) ~0x3F) | - (PD2TGT[Mode] & 0x3F); - if (state->reg[MT2063_REG_PD2_TGT] != val) - status |= mt2063_setreg(state, MT2063_REG_PD2_TGT, val); - } - - /* Ignore ATN Overload */ - if (status >= 0) { - val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x80) | - (RFOVDIS[Mode] ? 0x80 : 0x00); - if (state->reg[MT2063_REG_LNA_TGT] != val) - status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val); - } - - /* Ignore FIF Overload */ - if (status >= 0) { - val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x80) | - (FIFOVDIS[Mode] ? 0x80 : 0x00); - if (state->reg[MT2063_REG_PD1_TGT] != val) - status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val); - } - - if (status >= 0) { - state->rcvr_mode = Mode; - dprintk(1, "mt2063 mode changed to %s\n", - mt2063_mode_name[state->rcvr_mode]); - } - - return status; -} - -/* - * MT2063_ClearPowerMaskBits () - Clears the power-down mask bits for various - * sections of the MT2063 - * - * @Bits: Mask bits to be cleared. - * - * See definition of MT2063_Mask_Bits type for description - * of each of the power bits. - */ -static u32 MT2063_ClearPowerMaskBits(struct mt2063_state *state, - enum MT2063_Mask_Bits Bits) -{ - u32 status = 0; - - dprintk(2, "\n"); - Bits = (enum MT2063_Mask_Bits)(Bits & MT2063_ALL_SD); /* Only valid bits for this tuner */ - if ((Bits & 0xFF00) != 0) { - state->reg[MT2063_REG_PWR_2] &= ~(u8) (Bits >> 8); - status |= - mt2063_write(state, - MT2063_REG_PWR_2, - &state->reg[MT2063_REG_PWR_2], 1); - } - if ((Bits & 0xFF) != 0) { - state->reg[MT2063_REG_PWR_1] &= ~(u8) (Bits & 0xFF); - status |= - mt2063_write(state, - MT2063_REG_PWR_1, - &state->reg[MT2063_REG_PWR_1], 1); - } - - return status; -} - -/* - * MT2063_SoftwareShutdown() - Enables or disables software shutdown function. - * When Shutdown is 1, any section whose power - * mask is set will be shutdown. - */ -static u32 MT2063_SoftwareShutdown(struct mt2063_state *state, u8 Shutdown) -{ - u32 status; - - dprintk(2, "\n"); - if (Shutdown == 1) - state->reg[MT2063_REG_PWR_1] |= 0x04; - else - state->reg[MT2063_REG_PWR_1] &= ~0x04; - - status = mt2063_write(state, - MT2063_REG_PWR_1, - &state->reg[MT2063_REG_PWR_1], 1); - - if (Shutdown != 1) { - state->reg[MT2063_REG_BYP_CTRL] = - (state->reg[MT2063_REG_BYP_CTRL] & 0x9F) | 0x40; - status |= - mt2063_write(state, - MT2063_REG_BYP_CTRL, - &state->reg[MT2063_REG_BYP_CTRL], - 1); - state->reg[MT2063_REG_BYP_CTRL] = - (state->reg[MT2063_REG_BYP_CTRL] & 0x9F); - status |= - mt2063_write(state, - MT2063_REG_BYP_CTRL, - &state->reg[MT2063_REG_BYP_CTRL], - 1); - } - - return status; -} - -static u32 MT2063_Round_fLO(u32 f_LO, u32 f_LO_Step, u32 f_ref) -{ - return f_ref * (f_LO / f_ref) - + f_LO_Step * (((f_LO % f_ref) + (f_LO_Step / 2)) / f_LO_Step); -} - -/** - * fLO_FractionalTerm() - Calculates the portion contributed by FracN / denom. - * This function preserves maximum precision without - * risk of overflow. It accurately calculates - * f_ref * num / denom to within 1 HZ with fixed math. - * - * @num : Fractional portion of the multiplier - * @denom: denominator portion of the ratio - * @f_Ref: SRO frequency. - * - * This calculation handles f_ref as two separate 14-bit fields. - * Therefore, a maximum value of 2^28-1 may safely be used for f_ref. - * This is the genesis of the magic number "14" and the magic mask value of - * 0x03FFF. - * - * This routine successfully handles denom values up to and including 2^18. - * Returns: f_ref * num / denom - */ -static u32 MT2063_fLO_FractionalTerm(u32 f_ref, u32 num, u32 denom) -{ - u32 t1 = (f_ref >> 14) * num; - u32 term1 = t1 / denom; - u32 loss = t1 % denom; - u32 term2 = - (((f_ref & 0x00003FFF) * num + (loss << 14)) + (denom / 2)) / denom; - return (term1 << 14) + term2; -} - -/* - * CalcLO1Mult()- Calculates Integer divider value and the numerator - * value for a FracN PLL. - * - * This function assumes that the f_LO and f_Ref are - * evenly divisible by f_LO_Step. - * - * @Div: OUTPUT: Whole number portion of the multiplier - * @FracN: OUTPUT: Fractional portion of the multiplier - * @f_LO: desired LO frequency. - * @f_LO_Step: Minimum step size for the LO (in Hz). - * @f_Ref: SRO frequency. - * @f_Avoid: Range of PLL frequencies to avoid near integer multiples - * of f_Ref (in Hz). - * - * Returns: Recalculated LO frequency. - */ -static u32 MT2063_CalcLO1Mult(u32 *Div, - u32 *FracN, - u32 f_LO, - u32 f_LO_Step, u32 f_Ref) -{ - /* Calculate the whole number portion of the divider */ - *Div = f_LO / f_Ref; - - /* Calculate the numerator value (round to nearest f_LO_Step) */ - *FracN = - (64 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) + - (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step); - - return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN, 64); -} - -/** - * CalcLO2Mult() - Calculates Integer divider value and the numerator - * value for a FracN PLL. - * - * This function assumes that the f_LO and f_Ref are - * evenly divisible by f_LO_Step. - * - * @Div: OUTPUT: Whole number portion of the multiplier - * @FracN: OUTPUT: Fractional portion of the multiplier - * @f_LO: desired LO frequency. - * @f_LO_Step: Minimum step size for the LO (in Hz). - * @f_Ref: SRO frequency. - * @f_Avoid: Range of PLL frequencies to avoid near - * integer multiples of f_Ref (in Hz). - * - * Returns: Recalculated LO frequency. - */ -static u32 MT2063_CalcLO2Mult(u32 *Div, - u32 *FracN, - u32 f_LO, - u32 f_LO_Step, u32 f_Ref) -{ - /* Calculate the whole number portion of the divider */ - *Div = f_LO / f_Ref; - - /* Calculate the numerator value (round to nearest f_LO_Step) */ - *FracN = - (8191 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) + - (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step); - - return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN, - 8191); -} - -/* - * FindClearTuneFilter() - Calculate the corrrect ClearTune filter to be - * used for a given input frequency. - * - * @state: ptr to tuner data structure - * @f_in: RF input center frequency (in Hz). - * - * Returns: ClearTune filter number (0-31) - */ -static u32 FindClearTuneFilter(struct mt2063_state *state, u32 f_in) -{ - u32 RFBand; - u32 idx; /* index loop */ - - /* - ** Find RF Band setting - */ - RFBand = 31; /* def when f_in > all */ - for (idx = 0; idx < 31; ++idx) { - if (state->CTFiltMax[idx] >= f_in) { - RFBand = idx; - break; - } - } - return RFBand; -} - -/* - * MT2063_Tune() - Change the tuner's tuned frequency to RFin. - */ -static u32 MT2063_Tune(struct mt2063_state *state, u32 f_in) -{ /* RF input center frequency */ - - u32 status = 0; - u32 LO1; /* 1st LO register value */ - u32 Num1; /* Numerator for LO1 reg. value */ - u32 f_IF1; /* 1st IF requested */ - u32 LO2; /* 2nd LO register value */ - u32 Num2; /* Numerator for LO2 reg. value */ - u32 ofLO1, ofLO2; /* last time's LO frequencies */ - u8 fiffc = 0x80; /* FIFF center freq from tuner */ - u32 fiffof; /* Offset from FIFF center freq */ - const u8 LO1LK = 0x80; /* Mask for LO1 Lock bit */ - u8 LO2LK = 0x08; /* Mask for LO2 Lock bit */ - u8 val; - u32 RFBand; - - dprintk(2, "\n"); - /* Check the input and output frequency ranges */ - if ((f_in < MT2063_MIN_FIN_FREQ) || (f_in > MT2063_MAX_FIN_FREQ)) - return -EINVAL; - - if ((state->AS_Data.f_out < MT2063_MIN_FOUT_FREQ) - || (state->AS_Data.f_out > MT2063_MAX_FOUT_FREQ)) - return -EINVAL; - - /* - * Save original LO1 and LO2 register values - */ - ofLO1 = state->AS_Data.f_LO1; - ofLO2 = state->AS_Data.f_LO2; - - /* - * Find and set RF Band setting - */ - if (state->ctfilt_sw == 1) { - val = (state->reg[MT2063_REG_CTUNE_CTRL] | 0x08); - if (state->reg[MT2063_REG_CTUNE_CTRL] != val) { - status |= - mt2063_setreg(state, MT2063_REG_CTUNE_CTRL, val); - } - val = state->reg[MT2063_REG_CTUNE_OV]; - RFBand = FindClearTuneFilter(state, f_in); - state->reg[MT2063_REG_CTUNE_OV] = - (u8) ((state->reg[MT2063_REG_CTUNE_OV] & ~0x1F) - | RFBand); - if (state->reg[MT2063_REG_CTUNE_OV] != val) { - status |= - mt2063_setreg(state, MT2063_REG_CTUNE_OV, val); - } - } - - /* - * Read the FIFF Center Frequency from the tuner - */ - if (status >= 0) { - status |= - mt2063_read(state, - MT2063_REG_FIFFC, - &state->reg[MT2063_REG_FIFFC], 1); - fiffc = state->reg[MT2063_REG_FIFFC]; - } - /* - * Assign in the requested values - */ - state->AS_Data.f_in = f_in; - /* Request a 1st IF such that LO1 is on a step size */ - state->AS_Data.f_if1_Request = - MT2063_Round_fLO(state->AS_Data.f_if1_Request + f_in, - state->AS_Data.f_LO1_Step, - state->AS_Data.f_ref) - f_in; - - /* - * Calculate frequency settings. f_IF1_FREQ + f_in is the - * desired LO1 frequency - */ - MT2063_ResetExclZones(&state->AS_Data); - - f_IF1 = MT2063_ChooseFirstIF(&state->AS_Data); - - state->AS_Data.f_LO1 = - MT2063_Round_fLO(f_IF1 + f_in, state->AS_Data.f_LO1_Step, - state->AS_Data.f_ref); - - state->AS_Data.f_LO2 = - MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in, - state->AS_Data.f_LO2_Step, state->AS_Data.f_ref); - - /* - * Check for any LO spurs in the output bandwidth and adjust - * the LO settings to avoid them if needed - */ - status |= MT2063_AvoidSpurs(&state->AS_Data); - /* - * MT_AvoidSpurs spurs may have changed the LO1 & LO2 values. - * Recalculate the LO frequencies and the values to be placed - * in the tuning registers. - */ - state->AS_Data.f_LO1 = - MT2063_CalcLO1Mult(&LO1, &Num1, state->AS_Data.f_LO1, - state->AS_Data.f_LO1_Step, state->AS_Data.f_ref); - state->AS_Data.f_LO2 = - MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in, - state->AS_Data.f_LO2_Step, state->AS_Data.f_ref); - state->AS_Data.f_LO2 = - MT2063_CalcLO2Mult(&LO2, &Num2, state->AS_Data.f_LO2, - state->AS_Data.f_LO2_Step, state->AS_Data.f_ref); - - /* - * Check the upconverter and downconverter frequency ranges - */ - if ((state->AS_Data.f_LO1 < MT2063_MIN_UPC_FREQ) - || (state->AS_Data.f_LO1 > MT2063_MAX_UPC_FREQ)) - status |= MT2063_UPC_RANGE; - if ((state->AS_Data.f_LO2 < MT2063_MIN_DNC_FREQ) - || (state->AS_Data.f_LO2 > MT2063_MAX_DNC_FREQ)) - status |= MT2063_DNC_RANGE; - /* LO2 Lock bit was in a different place for B0 version */ - if (state->tuner_id == MT2063_B0) - LO2LK = 0x40; - - /* - * If we have the same LO frequencies and we're already locked, - * then skip re-programming the LO registers. - */ - if ((ofLO1 != state->AS_Data.f_LO1) - || (ofLO2 != state->AS_Data.f_LO2) - || ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) != - (LO1LK | LO2LK))) { - /* - * Calculate the FIFFOF register value - * - * IF1_Actual - * FIFFOF = ------------ - 8 * FIFFC - 4992 - * f_ref/64 - */ - fiffof = - (state->AS_Data.f_LO1 - - f_in) / (state->AS_Data.f_ref / 64) - 8 * (u32) fiffc - - 4992; - if (fiffof > 0xFF) - fiffof = 0xFF; - - /* - * Place all of the calculated values into the local tuner - * register fields. - */ - if (status >= 0) { - state->reg[MT2063_REG_LO1CQ_1] = (u8) (LO1 & 0xFF); /* DIV1q */ - state->reg[MT2063_REG_LO1CQ_2] = (u8) (Num1 & 0x3F); /* NUM1q */ - state->reg[MT2063_REG_LO2CQ_1] = (u8) (((LO2 & 0x7F) << 1) /* DIV2q */ - |(Num2 >> 12)); /* NUM2q (hi) */ - state->reg[MT2063_REG_LO2CQ_2] = (u8) ((Num2 & 0x0FF0) >> 4); /* NUM2q (mid) */ - state->reg[MT2063_REG_LO2CQ_3] = (u8) (0xE0 | (Num2 & 0x000F)); /* NUM2q (lo) */ - - /* - * Now write out the computed register values - * IMPORTANT: There is a required order for writing - * (0x05 must follow all the others). - */ - status |= mt2063_write(state, MT2063_REG_LO1CQ_1, &state->reg[MT2063_REG_LO1CQ_1], 5); /* 0x01 - 0x05 */ - if (state->tuner_id == MT2063_B0) { - /* Re-write the one-shot bits to trigger the tune operation */ - status |= mt2063_write(state, MT2063_REG_LO2CQ_3, &state->reg[MT2063_REG_LO2CQ_3], 1); /* 0x05 */ - } - /* Write out the FIFF offset only if it's changing */ - if (state->reg[MT2063_REG_FIFF_OFFSET] != - (u8) fiffof) { - state->reg[MT2063_REG_FIFF_OFFSET] = - (u8) fiffof; - status |= - mt2063_write(state, - MT2063_REG_FIFF_OFFSET, - &state-> - reg[MT2063_REG_FIFF_OFFSET], - 1); - } - } - - /* - * Check for LO's locking - */ - - if (status < 0) - return status; - - status = mt2063_lockStatus(state); - if (status < 0) - return status; - if (!status) - return -EINVAL; /* Couldn't lock */ - - /* - * If we locked OK, assign calculated data to mt2063_state structure - */ - state->f_IF1_actual = state->AS_Data.f_LO1 - f_in; - } - - return status; -} - -static const u8 MT2063B0_defaults[] = { - /* Reg, Value */ - 0x19, 0x05, - 0x1B, 0x1D, - 0x1C, 0x1F, - 0x1D, 0x0F, - 0x1E, 0x3F, - 0x1F, 0x0F, - 0x20, 0x3F, - 0x22, 0x21, - 0x23, 0x3F, - 0x24, 0x20, - 0x25, 0x3F, - 0x27, 0xEE, - 0x2C, 0x27, /* bit at 0x20 is cleared below */ - 0x30, 0x03, - 0x2C, 0x07, /* bit at 0x20 is cleared here */ - 0x2D, 0x87, - 0x2E, 0xAA, - 0x28, 0xE1, /* Set the FIFCrst bit here */ - 0x28, 0xE0, /* Clear the FIFCrst bit here */ - 0x00 -}; - -/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */ -static const u8 MT2063B1_defaults[] = { - /* Reg, Value */ - 0x05, 0xF0, - 0x11, 0x10, /* New Enable AFCsd */ - 0x19, 0x05, - 0x1A, 0x6C, - 0x1B, 0x24, - 0x1C, 0x28, - 0x1D, 0x8F, - 0x1E, 0x14, - 0x1F, 0x8F, - 0x20, 0x57, - 0x22, 0x21, /* New - ver 1.03 */ - 0x23, 0x3C, /* New - ver 1.10 */ - 0x24, 0x20, /* New - ver 1.03 */ - 0x2C, 0x24, /* bit at 0x20 is cleared below */ - 0x2D, 0x87, /* FIFFQ=0 */ - 0x2F, 0xF3, - 0x30, 0x0C, /* New - ver 1.11 */ - 0x31, 0x1B, /* New - ver 1.11 */ - 0x2C, 0x04, /* bit at 0x20 is cleared here */ - 0x28, 0xE1, /* Set the FIFCrst bit here */ - 0x28, 0xE0, /* Clear the FIFCrst bit here */ - 0x00 -}; - -/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */ -static const u8 MT2063B3_defaults[] = { - /* Reg, Value */ - 0x05, 0xF0, - 0x19, 0x3D, - 0x2C, 0x24, /* bit at 0x20 is cleared below */ - 0x2C, 0x04, /* bit at 0x20 is cleared here */ - 0x28, 0xE1, /* Set the FIFCrst bit here */ - 0x28, 0xE0, /* Clear the FIFCrst bit here */ - 0x00 -}; - -static int mt2063_init(struct dvb_frontend *fe) -{ - u32 status; - struct mt2063_state *state = fe->tuner_priv; - u8 all_resets = 0xF0; /* reset/load bits */ - const u8 *def = NULL; - char *step; - u32 FCRUN; - s32 maxReads; - u32 fcu_osc; - u32 i; - - dprintk(2, "\n"); - - state->rcvr_mode = MT2063_CABLE_QAM; - - /* Read the Part/Rev code from the tuner */ - status = mt2063_read(state, MT2063_REG_PART_REV, - &state->reg[MT2063_REG_PART_REV], 1); - if (status < 0) { - printk(KERN_ERR "Can't read mt2063 part ID\n"); - return status; - } - - /* Check the part/rev code */ - switch (state->reg[MT2063_REG_PART_REV]) { - case MT2063_B0: - step = "B0"; - break; - case MT2063_B1: - step = "B1"; - break; - case MT2063_B2: - step = "B2"; - break; - case MT2063_B3: - step = "B3"; - break; - default: - printk(KERN_ERR "mt2063: Unknown mt2063 device ID (0x%02x)\n", - state->reg[MT2063_REG_PART_REV]); - return -ENODEV; /* Wrong tuner Part/Rev code */ - } - - /* Check the 2nd byte of the Part/Rev code from the tuner */ - status = mt2063_read(state, MT2063_REG_RSVD_3B, - &state->reg[MT2063_REG_RSVD_3B], 1); - - /* b7 != 0 ==> NOT MT2063 */ - if (status < 0 || ((state->reg[MT2063_REG_RSVD_3B] & 0x80) != 0x00)) { - printk(KERN_ERR "mt2063: Unknown part ID (0x%02x%02x)\n", - state->reg[MT2063_REG_PART_REV], - state->reg[MT2063_REG_RSVD_3B]); - return -ENODEV; /* Wrong tuner Part/Rev code */ - } - - printk(KERN_INFO "mt2063: detected a mt2063 %s\n", step); - - /* Reset the tuner */ - status = mt2063_write(state, MT2063_REG_LO2CQ_3, &all_resets, 1); - if (status < 0) - return status; - - /* change all of the default values that vary from the HW reset values */ - /* def = (state->reg[PART_REV] == MT2063_B0) ? MT2063B0_defaults : MT2063B1_defaults; */ - switch (state->reg[MT2063_REG_PART_REV]) { - case MT2063_B3: - def = MT2063B3_defaults; - break; - - case MT2063_B1: - def = MT2063B1_defaults; - break; - - case MT2063_B0: - def = MT2063B0_defaults; - break; - - default: - return -ENODEV; - break; - } - - while (status >= 0 && *def) { - u8 reg = *def++; - u8 val = *def++; - status = mt2063_write(state, reg, &val, 1); - } - if (status < 0) - return status; - - /* Wait for FIFF location to complete. */ - FCRUN = 1; - maxReads = 10; - while (status >= 0 && (FCRUN != 0) && (maxReads-- > 0)) { - msleep(2); - status = mt2063_read(state, - MT2063_REG_XO_STATUS, - &state-> - reg[MT2063_REG_XO_STATUS], 1); - FCRUN = (state->reg[MT2063_REG_XO_STATUS] & 0x40) >> 6; - } - - if (FCRUN != 0 || status < 0) - return -ENODEV; - - status = mt2063_read(state, - MT2063_REG_FIFFC, - &state->reg[MT2063_REG_FIFFC], 1); - if (status < 0) - return status; - - /* Read back all the registers from the tuner */ - status = mt2063_read(state, - MT2063_REG_PART_REV, - state->reg, MT2063_REG_END_REGS); - if (status < 0) - return status; - - /* Initialize the tuner state. */ - state->tuner_id = state->reg[MT2063_REG_PART_REV]; - state->AS_Data.f_ref = MT2063_REF_FREQ; - state->AS_Data.f_if1_Center = (state->AS_Data.f_ref / 8) * - ((u32) state->reg[MT2063_REG_FIFFC] + 640); - state->AS_Data.f_if1_bw = MT2063_IF1_BW; - state->AS_Data.f_out = 43750000UL; - state->AS_Data.f_out_bw = 6750000UL; - state->AS_Data.f_zif_bw = MT2063_ZIF_BW; - state->AS_Data.f_LO1_Step = state->AS_Data.f_ref / 64; - state->AS_Data.f_LO2_Step = MT2063_TUNE_STEP_SIZE; - state->AS_Data.maxH1 = MT2063_MAX_HARMONICS_1; - state->AS_Data.maxH2 = MT2063_MAX_HARMONICS_2; - state->AS_Data.f_min_LO_Separation = MT2063_MIN_LO_SEP; - state->AS_Data.f_if1_Request = state->AS_Data.f_if1_Center; - state->AS_Data.f_LO1 = 2181000000UL; - state->AS_Data.f_LO2 = 1486249786UL; - state->f_IF1_actual = state->AS_Data.f_if1_Center; - state->AS_Data.f_in = state->AS_Data.f_LO1 - state->f_IF1_actual; - state->AS_Data.f_LO1_FracN_Avoid = MT2063_LO1_FRACN_AVOID; - state->AS_Data.f_LO2_FracN_Avoid = MT2063_LO2_FRACN_AVOID; - state->num_regs = MT2063_REG_END_REGS; - state->AS_Data.avoidDECT = MT2063_AVOID_BOTH; - state->ctfilt_sw = 0; - - state->CTFiltMax[0] = 69230000; - state->CTFiltMax[1] = 105770000; - state->CTFiltMax[2] = 140350000; - state->CTFiltMax[3] = 177110000; - state->CTFiltMax[4] = 212860000; - state->CTFiltMax[5] = 241130000; - state->CTFiltMax[6] = 274370000; - state->CTFiltMax[7] = 309820000; - state->CTFiltMax[8] = 342450000; - state->CTFiltMax[9] = 378870000; - state->CTFiltMax[10] = 416210000; - state->CTFiltMax[11] = 456500000; - state->CTFiltMax[12] = 495790000; - state->CTFiltMax[13] = 534530000; - state->CTFiltMax[14] = 572610000; - state->CTFiltMax[15] = 598970000; - state->CTFiltMax[16] = 635910000; - state->CTFiltMax[17] = 672130000; - state->CTFiltMax[18] = 714840000; - state->CTFiltMax[19] = 739660000; - state->CTFiltMax[20] = 770410000; - state->CTFiltMax[21] = 814660000; - state->CTFiltMax[22] = 846950000; - state->CTFiltMax[23] = 867820000; - state->CTFiltMax[24] = 915980000; - state->CTFiltMax[25] = 947450000; - state->CTFiltMax[26] = 983110000; - state->CTFiltMax[27] = 1021630000; - state->CTFiltMax[28] = 1061870000; - state->CTFiltMax[29] = 1098330000; - state->CTFiltMax[30] = 1138990000; - - /* - ** Fetch the FCU osc value and use it and the fRef value to - ** scale all of the Band Max values - */ - - state->reg[MT2063_REG_CTUNE_CTRL] = 0x0A; - status = mt2063_write(state, MT2063_REG_CTUNE_CTRL, - &state->reg[MT2063_REG_CTUNE_CTRL], 1); - if (status < 0) - return status; - - /* Read the ClearTune filter calibration value */ - status = mt2063_read(state, MT2063_REG_FIFFC, - &state->reg[MT2063_REG_FIFFC], 1); - if (status < 0) - return status; - - fcu_osc = state->reg[MT2063_REG_FIFFC]; - - state->reg[MT2063_REG_CTUNE_CTRL] = 0x00; - status = mt2063_write(state, MT2063_REG_CTUNE_CTRL, - &state->reg[MT2063_REG_CTUNE_CTRL], 1); - if (status < 0) - return status; - - /* Adjust each of the values in the ClearTune filter cross-over table */ - for (i = 0; i < 31; i++) - state->CTFiltMax[i] = (state->CTFiltMax[i] / 768) * (fcu_osc + 640); - - status = MT2063_SoftwareShutdown(state, 1); - if (status < 0) - return status; - status = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD); - if (status < 0) - return status; - - state->init = true; - - return 0; -} - -static int mt2063_get_status(struct dvb_frontend *fe, u32 *tuner_status) -{ - struct mt2063_state *state = fe->tuner_priv; - int status; - - dprintk(2, "\n"); - - if (!state->init) - return -ENODEV; - - *tuner_status = 0; - status = mt2063_lockStatus(state); - if (status < 0) - return status; - if (status) - *tuner_status = TUNER_STATUS_LOCKED; - - dprintk(1, "Tuner status: %d", *tuner_status); - - return 0; -} - -static int mt2063_release(struct dvb_frontend *fe) -{ - struct mt2063_state *state = fe->tuner_priv; - - dprintk(2, "\n"); - - fe->tuner_priv = NULL; - kfree(state); - - return 0; -} - -static int mt2063_set_analog_params(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct mt2063_state *state = fe->tuner_priv; - s32 pict_car; - s32 pict2chanb_vsb; - s32 ch_bw; - s32 if_mid; - s32 rcvr_mode; - int status; - - dprintk(2, "\n"); - - if (!state->init) { - status = mt2063_init(fe); - if (status < 0) - return status; - } - - switch (params->mode) { - case V4L2_TUNER_RADIO: - pict_car = 38900000; - ch_bw = 8000000; - pict2chanb_vsb = -(ch_bw / 2); - rcvr_mode = MT2063_OFFAIR_ANALOG; - break; - case V4L2_TUNER_ANALOG_TV: - rcvr_mode = MT2063_CABLE_ANALOG; - if (params->std & ~V4L2_STD_MN) { - pict_car = 38900000; - ch_bw = 6000000; - pict2chanb_vsb = -1250000; - } else if (params->std & V4L2_STD_PAL_G) { - pict_car = 38900000; - ch_bw = 7000000; - pict2chanb_vsb = -1250000; - } else { /* PAL/SECAM standards */ - pict_car = 38900000; - ch_bw = 8000000; - pict2chanb_vsb = -1250000; - } - break; - default: - return -EINVAL; - } - if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2)); - - state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */ - state->AS_Data.f_out = if_mid; - state->AS_Data.f_out_bw = ch_bw + 750000; - status = MT2063_SetReceiverMode(state, rcvr_mode); - if (status < 0) - return status; - - dprintk(1, "Tuning to frequency: %d, bandwidth %d, foffset %d\n", - params->frequency, ch_bw, pict2chanb_vsb); - - status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2)))); - if (status < 0) - return status; - - state->frequency = params->frequency; - return 0; -} - -/* - * As defined on EN 300 429, the DVB-C roll-off factor is 0.15. - * So, the amount of the needed bandwith is given by: - * Bw = Symbol_rate * (1 + 0.15) - * As such, the maximum symbol rate supported by 6 MHz is given by: - * max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds - */ -#define MAX_SYMBOL_RATE_6MHz 5217391 - -static int mt2063_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct mt2063_state *state = fe->tuner_priv; - int status; - s32 pict_car; - s32 pict2chanb_vsb; - s32 ch_bw; - s32 if_mid; - s32 rcvr_mode; - - if (!state->init) { - status = mt2063_init(fe); - if (status < 0) - return status; - } - - dprintk(2, "\n"); - - if (c->bandwidth_hz == 0) - return -EINVAL; - if (c->bandwidth_hz <= 6000000) - ch_bw = 6000000; - else if (c->bandwidth_hz <= 7000000) - ch_bw = 7000000; - else - ch_bw = 8000000; - - switch (c->delivery_system) { - case SYS_DVBT: - rcvr_mode = MT2063_OFFAIR_COFDM; - pict_car = 36125000; - pict2chanb_vsb = -(ch_bw / 2); - break; - case SYS_DVBC_ANNEX_A: - case SYS_DVBC_ANNEX_C: - rcvr_mode = MT2063_CABLE_QAM; - pict_car = 36125000; - pict2chanb_vsb = -(ch_bw / 2); - break; - default: - return -EINVAL; - } - if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2)); - - state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */ - state->AS_Data.f_out = if_mid; - state->AS_Data.f_out_bw = ch_bw + 750000; - status = MT2063_SetReceiverMode(state, rcvr_mode); - if (status < 0) - return status; - - dprintk(1, "Tuning to frequency: %d, bandwidth %d, foffset %d\n", - c->frequency, ch_bw, pict2chanb_vsb); - - status = MT2063_Tune(state, (c->frequency + (pict2chanb_vsb + (ch_bw / 2)))); - - if (status < 0) - return status; - - state->frequency = c->frequency; - return 0; -} - -static int mt2063_get_if_frequency(struct dvb_frontend *fe, u32 *freq) -{ - struct mt2063_state *state = fe->tuner_priv; - - dprintk(2, "\n"); - - if (!state->init) - return -ENODEV; - - *freq = state->AS_Data.f_out; - - dprintk(1, "IF frequency: %d\n", *freq); - - return 0; -} - -static int mt2063_get_bandwidth(struct dvb_frontend *fe, u32 *bw) -{ - struct mt2063_state *state = fe->tuner_priv; - - dprintk(2, "\n"); - - if (!state->init) - return -ENODEV; - - *bw = state->AS_Data.f_out_bw - 750000; - - dprintk(1, "bandwidth: %d\n", *bw); - - return 0; -} - -static struct dvb_tuner_ops mt2063_ops = { - .info = { - .name = "MT2063 Silicon Tuner", - .frequency_min = 45000000, - .frequency_max = 865000000, - .frequency_step = 0, - }, - - .init = mt2063_init, - .sleep = MT2063_Sleep, - .get_status = mt2063_get_status, - .set_analog_params = mt2063_set_analog_params, - .set_params = mt2063_set_params, - .get_if_frequency = mt2063_get_if_frequency, - .get_bandwidth = mt2063_get_bandwidth, - .release = mt2063_release, -}; - -struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe, - struct mt2063_config *config, - struct i2c_adapter *i2c) -{ - struct mt2063_state *state = NULL; - - dprintk(2, "\n"); - - state = kzalloc(sizeof(struct mt2063_state), GFP_KERNEL); - if (state == NULL) - goto error; - - state->config = config; - state->i2c = i2c; - state->frontend = fe; - state->reference = config->refclock / 1000; /* kHz */ - fe->tuner_priv = state; - fe->ops.tuner_ops = mt2063_ops; - - printk(KERN_INFO "%s: Attaching MT2063\n", __func__); - return fe; - -error: - kfree(state); - return NULL; -} -EXPORT_SYMBOL_GPL(mt2063_attach); - -/* - * Ancillary routines visible outside mt2063 - * FIXME: Remove them in favor of using standard tuner callbacks - */ -unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe) -{ - struct mt2063_state *state = fe->tuner_priv; - int err = 0; - - dprintk(2, "\n"); - - err = MT2063_SoftwareShutdown(state, 1); - if (err < 0) - printk(KERN_ERR "%s: Couldn't shutdown\n", __func__); - - return err; -} -EXPORT_SYMBOL_GPL(tuner_MT2063_SoftwareShutdown); - -unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe) -{ - struct mt2063_state *state = fe->tuner_priv; - int err = 0; - - dprintk(2, "\n"); - - err = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD); - if (err < 0) - printk(KERN_ERR "%s: Invalid parameter\n", __func__); - - return err; -} -EXPORT_SYMBOL_GPL(tuner_MT2063_ClearPowerMaskBits); - -MODULE_AUTHOR("Mauro Carvalho Chehab "); -MODULE_DESCRIPTION("MT2063 Silicon tuner"); -MODULE_LICENSE("GPL"); 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.c b/drivers/media/common/tuners/mt20xx.c deleted file mode 100644 index 0e74e97e0d1a..000000000000 --- a/drivers/media/common/tuners/mt20xx.c +++ /dev/null @@ -1,670 +0,0 @@ -/* - * i2c tv tuner chip device driver - * controls microtune tuners, mt2032 + mt2050 at the moment. - * - * This "mt20xx" module was split apart from the original "tuner" module. - */ -#include -#include -#include -#include -#include "tuner-i2c.h" -#include "mt20xx.h" - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable verbose debug messages"); - -/* ---------------------------------------------------------------------- */ - -static unsigned int optimize_vco = 1; -module_param(optimize_vco, int, 0644); - -static unsigned int tv_antenna = 1; -module_param(tv_antenna, int, 0644); - -static unsigned int radio_antenna; -module_param(radio_antenna, int, 0644); - -/* ---------------------------------------------------------------------- */ - -#define MT2032 0x04 -#define MT2030 0x06 -#define MT2040 0x07 -#define MT2050 0x42 - -static char *microtune_part[] = { - [ MT2030 ] = "MT2030", - [ MT2032 ] = "MT2032", - [ MT2040 ] = "MT2040", - [ MT2050 ] = "MT2050", -}; - -struct microtune_priv { - struct tuner_i2c_props i2c_props; - - unsigned int xogc; - //unsigned int radio_if2; - - u32 frequency; -}; - -static int microtune_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - - return 0; -} - -static int microtune_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct microtune_priv *priv = fe->tuner_priv; - *frequency = priv->frequency; - return 0; -} - -// IsSpurInBand()? -static int mt2032_spurcheck(struct dvb_frontend *fe, - int f1, int f2, int spectrum_from,int spectrum_to) -{ - struct microtune_priv *priv = fe->tuner_priv; - int n1=1,n2,f; - - f1=f1/1000; //scale to kHz to avoid 32bit overflows - f2=f2/1000; - spectrum_from/=1000; - spectrum_to/=1000; - - tuner_dbg("spurcheck f1=%d f2=%d from=%d to=%d\n", - f1,f2,spectrum_from,spectrum_to); - - do { - n2=-n1; - f=n1*(f1-f2); - do { - n2--; - f=f-f2; - tuner_dbg("spurtest n1=%d n2=%d ftest=%d\n",n1,n2,f); - - if( (f>spectrum_from) && (f(f2-spectrum_to)) || (n2>-5)); - n1++; - } while (n1<5); - - return 1; -} - -static int mt2032_compute_freq(struct dvb_frontend *fe, - unsigned int rfin, - unsigned int if1, unsigned int if2, - unsigned int spectrum_from, - unsigned int spectrum_to, - unsigned char *buf, - int *ret_sel, - unsigned int xogc) //all in Hz -{ - struct microtune_priv *priv = fe->tuner_priv; - unsigned int fref,lo1,lo1n,lo1a,s,sel,lo1freq, desired_lo1, - desired_lo2,lo2,lo2n,lo2a,lo2num,lo2freq; - - fref= 5250 *1000; //5.25MHz - desired_lo1=rfin+if1; - - lo1=(2*(desired_lo1/1000)+(fref/1000)) / (2*fref/1000); - lo1n=lo1/8; - lo1a=lo1-(lo1n*8); - - s=rfin/1000/1000+1090; - - if(optimize_vco) { - if(s>1890) sel=0; - else if(s>1720) sel=1; - else if(s>1530) sel=2; - else if(s>1370) sel=3; - else sel=4; // >1090 - } - else { - if(s>1790) sel=0; // <1958 - else if(s>1617) sel=1; - else if(s>1449) sel=2; - else if(s>1291) sel=3; - else sel=4; // >1090 - } - *ret_sel=sel; - - lo1freq=(lo1a+8*lo1n)*fref; - - tuner_dbg("mt2032: rfin=%d lo1=%d lo1n=%d lo1a=%d sel=%d, lo1freq=%d\n", - rfin,lo1,lo1n,lo1a,sel,lo1freq); - - desired_lo2=lo1freq-rfin-if2; - lo2=(desired_lo2)/fref; - lo2n=lo2/8; - lo2a=lo2-(lo2n*8); - lo2num=((desired_lo2/1000)%(fref/1000))* 3780/(fref/1000); //scale to fit in 32bit arith - lo2freq=(lo2a+8*lo2n)*fref + lo2num*(fref/1000)/3780*1000; - - tuner_dbg("mt2032: rfin=%d lo2=%d lo2n=%d lo2a=%d num=%d lo2freq=%d\n", - rfin,lo2,lo2n,lo2a,lo2num,lo2freq); - - if (lo1a > 7 || lo1n < 17 || lo1n > 48 || lo2a > 7 || lo2n < 17 || - lo2n > 30) { - tuner_info("mt2032: frequency parameters out of range: %d %d %d %d\n", - lo1a, lo1n, lo2a,lo2n); - return(-1); - } - - mt2032_spurcheck(fe, lo1freq, desired_lo2, spectrum_from, spectrum_to); - // should recalculate lo1 (one step up/down) - - // set up MT2032 register map for transfer over i2c - buf[0]=lo1n-1; - buf[1]=lo1a | (sel<<4); - buf[2]=0x86; // LOGC - buf[3]=0x0f; //reserved - buf[4]=0x1f; - buf[5]=(lo2n-1) | (lo2a<<5); - if(rfin >400*1000*1000) - buf[6]=0xe4; - else - buf[6]=0xf4; // set PKEN per rev 1.2 - buf[7]=8+xogc; - buf[8]=0xc3; //reserved - buf[9]=0x4e; //reserved - buf[10]=0xec; //reserved - buf[11]=(lo2num&0xff); - buf[12]=(lo2num>>8) |0x80; // Lo2RST - - return 0; -} - -static int mt2032_check_lo_lock(struct dvb_frontend *fe) -{ - struct microtune_priv *priv = fe->tuner_priv; - int try,lock=0; - unsigned char buf[2]; - - for(try=0;try<10;try++) { - buf[0]=0x0e; - tuner_i2c_xfer_send(&priv->i2c_props,buf,1); - tuner_i2c_xfer_recv(&priv->i2c_props,buf,1); - tuner_dbg("mt2032 Reg.E=0x%02x\n",buf[0]); - lock=buf[0] &0x06; - - if (lock==6) - break; - - tuner_dbg("mt2032: pll wait 1ms for lock (0x%2x)\n",buf[0]); - udelay(1000); - } - return lock; -} - -static int mt2032_optimize_vco(struct dvb_frontend *fe,int sel,int lock) -{ - struct microtune_priv *priv = fe->tuner_priv; - unsigned char buf[2]; - int tad1; - - buf[0]=0x0f; - tuner_i2c_xfer_send(&priv->i2c_props,buf,1); - tuner_i2c_xfer_recv(&priv->i2c_props,buf,1); - tuner_dbg("mt2032 Reg.F=0x%02x\n",buf[0]); - tad1=buf[0]&0x07; - - if(tad1 ==0) return lock; - if(tad1 ==1) return lock; - - if(tad1==2) { - if(sel==0) - return lock; - else sel--; - } - else { - if(sel<4) - sel++; - else - return lock; - } - - tuner_dbg("mt2032 optimize_vco: sel=%d\n",sel); - - buf[0]=0x0f; - buf[1]=sel; - tuner_i2c_xfer_send(&priv->i2c_props,buf,2); - lock=mt2032_check_lo_lock(fe); - return lock; -} - - -static void mt2032_set_if_freq(struct dvb_frontend *fe, unsigned int rfin, - unsigned int if1, unsigned int if2, - unsigned int from, unsigned int to) -{ - unsigned char buf[21]; - int lint_try,ret,sel,lock=0; - struct microtune_priv *priv = fe->tuner_priv; - - tuner_dbg("mt2032_set_if_freq rfin=%d if1=%d if2=%d from=%d to=%d\n", - rfin,if1,if2,from,to); - - buf[0]=0; - ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,1); - tuner_i2c_xfer_recv(&priv->i2c_props,buf,21); - - buf[0]=0; - ret=mt2032_compute_freq(fe,rfin,if1,if2,from,to,&buf[1],&sel,priv->xogc); - if (ret<0) - return; - - // send only the relevant registers per Rev. 1.2 - buf[0]=0; - ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,4); - buf[5]=5; - ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+5,4); - buf[11]=11; - ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+11,3); - if(ret!=3) - tuner_warn("i2c i/o error: rc == %d (should be 3)\n",ret); - - // wait for PLLs to lock (per manual), retry LINT if not. - for(lint_try=0; lint_try<2; lint_try++) { - lock=mt2032_check_lo_lock(fe); - - if(optimize_vco) - lock=mt2032_optimize_vco(fe,sel,lock); - if(lock==6) break; - - tuner_dbg("mt2032: re-init PLLs by LINT\n"); - buf[0]=7; - buf[1]=0x80 +8+priv->xogc; // set LINT to re-init PLLs - tuner_i2c_xfer_send(&priv->i2c_props,buf,2); - mdelay(10); - buf[1]=8+priv->xogc; - tuner_i2c_xfer_send(&priv->i2c_props,buf,2); - } - - if (lock!=6) - tuner_warn("MT2032 Fatal Error: PLLs didn't lock.\n"); - - buf[0]=2; - buf[1]=0x20; // LOGC for optimal phase noise - ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); - if (ret!=2) - tuner_warn("i2c i/o error: rc == %d (should be 2)\n",ret); -} - - -static int mt2032_set_tv_freq(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - int if2,from,to; - - // signal bandwidth and picture carrier - if (params->std & V4L2_STD_525_60) { - // NTSC - from = 40750*1000; - to = 46750*1000; - if2 = 45750*1000; - } else { - // PAL - from = 32900*1000; - to = 39900*1000; - if2 = 38900*1000; - } - - mt2032_set_if_freq(fe, params->frequency*62500, - 1090*1000*1000, if2, from, to); - - return 0; -} - -static int mt2032_set_radio_freq(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct microtune_priv *priv = fe->tuner_priv; - int if2; - - if (params->std & V4L2_STD_525_60) { - tuner_dbg("pinnacle ntsc\n"); - if2 = 41300 * 1000; - } else { - tuner_dbg("pinnacle pal\n"); - if2 = 33300 * 1000; - } - - // per Manual for FM tuning: first if center freq. 1085 MHz - mt2032_set_if_freq(fe, params->frequency * 125 / 2, - 1085*1000*1000,if2,if2,if2); - - return 0; -} - -static int mt2032_set_params(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct microtune_priv *priv = fe->tuner_priv; - int ret = -EINVAL; - - switch (params->mode) { - case V4L2_TUNER_RADIO: - ret = mt2032_set_radio_freq(fe, params); - priv->frequency = params->frequency * 125 / 2; - break; - case V4L2_TUNER_ANALOG_TV: - case V4L2_TUNER_DIGITAL_TV: - ret = mt2032_set_tv_freq(fe, params); - priv->frequency = params->frequency * 62500; - break; - } - - return ret; -} - -static struct dvb_tuner_ops mt2032_tuner_ops = { - .set_analog_params = mt2032_set_params, - .release = microtune_release, - .get_frequency = microtune_get_frequency, -}; - -// Initialization as described in "MT203x Programming Procedures", Rev 1.2, Feb.2001 -static int mt2032_init(struct dvb_frontend *fe) -{ - struct microtune_priv *priv = fe->tuner_priv; - unsigned char buf[21]; - int ret,xogc,xok=0; - - // Initialize Registers per spec. - buf[1]=2; // Index to register 2 - buf[2]=0xff; - buf[3]=0x0f; - buf[4]=0x1f; - ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+1,4); - - buf[5]=6; // Index register 6 - buf[6]=0xe4; - buf[7]=0x8f; - buf[8]=0xc3; - buf[9]=0x4e; - buf[10]=0xec; - ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+5,6); - - buf[12]=13; // Index register 13 - buf[13]=0x32; - ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+12,2); - - // Adjust XOGC (register 7), wait for XOK - xogc=7; - do { - tuner_dbg("mt2032: xogc = 0x%02x\n",xogc&0x07); - mdelay(10); - buf[0]=0x0e; - tuner_i2c_xfer_send(&priv->i2c_props,buf,1); - tuner_i2c_xfer_recv(&priv->i2c_props,buf,1); - xok=buf[0]&0x01; - tuner_dbg("mt2032: xok = 0x%02x\n",xok); - if (xok == 1) break; - - xogc--; - tuner_dbg("mt2032: xogc = 0x%02x\n",xogc&0x07); - if (xogc == 3) { - xogc=4; // min. 4 per spec - break; - } - buf[0]=0x07; - buf[1]=0x88 + xogc; - ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); - if (ret!=2) - tuner_warn("i2c i/o error: rc == %d (should be 2)\n",ret); - } while (xok != 1 ); - priv->xogc=xogc; - - memcpy(&fe->ops.tuner_ops, &mt2032_tuner_ops, sizeof(struct dvb_tuner_ops)); - - return(1); -} - -static void mt2050_set_antenna(struct dvb_frontend *fe, unsigned char antenna) -{ - struct microtune_priv *priv = fe->tuner_priv; - unsigned char buf[2]; - - buf[0] = 6; - buf[1] = antenna ? 0x11 : 0x10; - tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); - tuner_dbg("mt2050: enabled antenna connector %d\n", antenna); -} - -static void mt2050_set_if_freq(struct dvb_frontend *fe,unsigned int freq, unsigned int if2) -{ - struct microtune_priv *priv = fe->tuner_priv; - unsigned int if1=1218*1000*1000; - unsigned int f_lo1,f_lo2,lo1,lo2,f_lo1_modulo,f_lo2_modulo,num1,num2,div1a,div1b,div2a,div2b; - int ret; - unsigned char buf[6]; - - tuner_dbg("mt2050_set_if_freq freq=%d if1=%d if2=%d\n", - freq,if1,if2); - - f_lo1=freq+if1; - f_lo1=(f_lo1/1000000)*1000000; - - f_lo2=f_lo1-freq-if2; - f_lo2=(f_lo2/50000)*50000; - - lo1=f_lo1/4000000; - lo2=f_lo2/4000000; - - f_lo1_modulo= f_lo1-(lo1*4000000); - f_lo2_modulo= f_lo2-(lo2*4000000); - - num1=4*f_lo1_modulo/4000000; - num2=4096*(f_lo2_modulo/1000)/4000; - - // todo spurchecks - - div1a=(lo1/12)-1; - div1b=lo1-(div1a+1)*12; - - div2a=(lo2/8)-1; - div2b=lo2-(div2a+1)*8; - - if (debug > 1) { - tuner_dbg("lo1 lo2 = %d %d\n", lo1, lo2); - tuner_dbg("num1 num2 div1a div1b div2a div2b= %x %x %x %x %x %x\n", - num1,num2,div1a,div1b,div2a,div2b); - } - - buf[0]=1; - buf[1]= 4*div1b + num1; - if(freq<275*1000*1000) buf[1] = buf[1]|0x80; - - buf[2]=div1a; - buf[3]=32*div2b + num2/256; - buf[4]=num2-(num2/256)*256; - buf[5]=div2a; - if(num2!=0) buf[5]=buf[5]|0x40; - - if (debug > 1) { - int i; - tuner_dbg("bufs is: "); - for(i=0;i<6;i++) - printk("%x ",buf[i]); - printk("\n"); - } - - ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,6); - if (ret!=6) - tuner_warn("i2c i/o error: rc == %d (should be 6)\n",ret); -} - -static int mt2050_set_tv_freq(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - unsigned int if2; - - if (params->std & V4L2_STD_525_60) { - // NTSC - if2 = 45750*1000; - } else { - // PAL - if2 = 38900*1000; - } - if (V4L2_TUNER_DIGITAL_TV == params->mode) { - // DVB (pinnacle 300i) - if2 = 36150*1000; - } - mt2050_set_if_freq(fe, params->frequency*62500, if2); - mt2050_set_antenna(fe, tv_antenna); - - return 0; -} - -static int mt2050_set_radio_freq(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct microtune_priv *priv = fe->tuner_priv; - int if2; - - if (params->std & V4L2_STD_525_60) { - tuner_dbg("pinnacle ntsc\n"); - if2 = 41300 * 1000; - } else { - tuner_dbg("pinnacle pal\n"); - if2 = 33300 * 1000; - } - - mt2050_set_if_freq(fe, params->frequency * 125 / 2, if2); - mt2050_set_antenna(fe, radio_antenna); - - return 0; -} - -static int mt2050_set_params(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct microtune_priv *priv = fe->tuner_priv; - int ret = -EINVAL; - - switch (params->mode) { - case V4L2_TUNER_RADIO: - ret = mt2050_set_radio_freq(fe, params); - priv->frequency = params->frequency * 125 / 2; - break; - case V4L2_TUNER_ANALOG_TV: - case V4L2_TUNER_DIGITAL_TV: - ret = mt2050_set_tv_freq(fe, params); - priv->frequency = params->frequency * 62500; - break; - } - - return ret; -} - -static struct dvb_tuner_ops mt2050_tuner_ops = { - .set_analog_params = mt2050_set_params, - .release = microtune_release, - .get_frequency = microtune_get_frequency, -}; - -static int mt2050_init(struct dvb_frontend *fe) -{ - struct microtune_priv *priv = fe->tuner_priv; - unsigned char buf[2]; - - buf[0] = 6; - buf[1] = 0x10; - tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); /* power */ - - buf[0] = 0x0f; - buf[1] = 0x0f; - tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); /* m1lo */ - - buf[0] = 0x0d; - tuner_i2c_xfer_send(&priv->i2c_props, buf, 1); - tuner_i2c_xfer_recv(&priv->i2c_props, buf, 1); - - tuner_dbg("mt2050: sro is %x\n", buf[0]); - - memcpy(&fe->ops.tuner_ops, &mt2050_tuner_ops, sizeof(struct dvb_tuner_ops)); - - return 0; -} - -struct dvb_frontend *microtune_attach(struct dvb_frontend *fe, - struct i2c_adapter* i2c_adap, - u8 i2c_addr) -{ - struct microtune_priv *priv = NULL; - char *name; - unsigned char buf[21]; - int company_code; - - priv = kzalloc(sizeof(struct microtune_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - fe->tuner_priv = priv; - - priv->i2c_props.addr = i2c_addr; - priv->i2c_props.adap = i2c_adap; - priv->i2c_props.name = "mt20xx"; - - //priv->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */ - - memset(buf,0,sizeof(buf)); - - name = "unknown"; - - tuner_i2c_xfer_send(&priv->i2c_props,buf,1); - tuner_i2c_xfer_recv(&priv->i2c_props,buf,21); - if (debug) { - int i; - tuner_dbg("MT20xx hexdump:"); - for(i=0;i<21;i++) { - printk(" %02x",buf[i]); - if(((i+1)%8)==0) printk(" "); - } - printk("\n"); - } - company_code = buf[0x11] << 8 | buf[0x12]; - tuner_info("microtune: companycode=%04x part=%02x rev=%02x\n", - company_code,buf[0x13],buf[0x14]); - - - if (buf[0x13] < ARRAY_SIZE(microtune_part) && - NULL != microtune_part[buf[0x13]]) - name = microtune_part[buf[0x13]]; - switch (buf[0x13]) { - case MT2032: - mt2032_init(fe); - break; - case MT2050: - mt2050_init(fe); - break; - default: - tuner_info("microtune %s found, not (yet?) supported, sorry :-/\n", - name); - return NULL; - } - - strlcpy(fe->ops.tuner_ops.info.name, name, - sizeof(fe->ops.tuner_ops.info.name)); - tuner_info("microtune %s found, OK\n",name); - return fe; -} - -EXPORT_SYMBOL_GPL(microtune_attach); - -MODULE_DESCRIPTION("Microtune tuner driver"); -MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); -MODULE_LICENSE("GPL"); - -/* - * 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/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 -#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.c b/drivers/media/common/tuners/mt2131.c deleted file mode 100644 index f83b0c1ea6c8..000000000000 --- a/drivers/media/common/tuners/mt2131.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Driver for Microtune MT2131 "QAM/8VSB single chip tuner" - * - * Copyright (c) 2006 Steven Toth - * - * 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 -#include -#include -#include -#include - -#include "dvb_frontend.h" - -#include "mt2131.h" -#include "mt2131_priv.h" - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); - -#define dprintk(level,fmt, arg...) if (debug >= level) \ - printk(KERN_INFO "%s: " fmt, "mt2131", ## arg) - -static u8 mt2131_config1[] = { - 0x01, - 0x50, 0x00, 0x50, 0x80, 0x00, 0x49, 0xfa, 0x88, - 0x08, 0x77, 0x41, 0x04, 0x00, 0x00, 0x00, 0x32, - 0x7f, 0xda, 0x4c, 0x00, 0x10, 0xaa, 0x78, 0x80, - 0xff, 0x68, 0xa0, 0xff, 0xdd, 0x00, 0x00 -}; - -static u8 mt2131_config2[] = { - 0x10, - 0x7f, 0xc8, 0x0a, 0x5f, 0x00, 0x04 -}; - -static int mt2131_readreg(struct mt2131_priv *priv, u8 reg, u8 *val) -{ - struct i2c_msg msg[2] = { - { .addr = priv->cfg->i2c_address, .flags = 0, - .buf = ®, .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 "mt2131 I2C read failed\n"); - return -EREMOTEIO; - } - return 0; -} - -static int mt2131_writereg(struct mt2131_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 "mt2131 I2C write failed\n"); - return -EREMOTEIO; - } - return 0; -} - -static int mt2131_writeregs(struct mt2131_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 "mt2131 I2C write failed (len=%i)\n", - (int)len); - return -EREMOTEIO; - } - return 0; -} - -static int mt2131_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct mt2131_priv *priv; - int ret=0, i; - u32 freq; - u8 if_band_center; - u32 f_lo1, f_lo2; - u32 div1, num1, div2, num2; - u8 b[8]; - u8 lockval = 0; - - priv = fe->tuner_priv; - - freq = c->frequency / 1000; /* Hz -> kHz */ - dprintk(1, "%s() freq=%d\n", __func__, freq); - - f_lo1 = freq + MT2131_IF1 * 1000; - f_lo1 = (f_lo1 / 250) * 250; - f_lo2 = f_lo1 - freq - MT2131_IF2; - - priv->frequency = (f_lo1 - f_lo2 - MT2131_IF2) * 1000; - - /* Frequency LO1 = 16MHz * (DIV1 + NUM1/8192 ) */ - num1 = f_lo1 * 64 / (MT2131_FREF / 128); - div1 = num1 / 8192; - num1 &= 0x1fff; - - /* Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) */ - num2 = f_lo2 * 64 / (MT2131_FREF / 128); - div2 = num2 / 8192; - num2 &= 0x1fff; - - if (freq <= 82500) if_band_center = 0x00; else - if (freq <= 137500) if_band_center = 0x01; else - if (freq <= 192500) if_band_center = 0x02; else - if (freq <= 247500) if_band_center = 0x03; else - if (freq <= 302500) if_band_center = 0x04; else - if (freq <= 357500) if_band_center = 0x05; else - if (freq <= 412500) if_band_center = 0x06; else - if (freq <= 467500) if_band_center = 0x07; else - if (freq <= 522500) if_band_center = 0x08; else - if (freq <= 577500) if_band_center = 0x09; else - if (freq <= 632500) if_band_center = 0x0A; else - if (freq <= 687500) if_band_center = 0x0B; else - if (freq <= 742500) if_band_center = 0x0C; else - if (freq <= 797500) if_band_center = 0x0D; else - if (freq <= 852500) if_band_center = 0x0E; else - if (freq <= 907500) if_band_center = 0x0F; else - if (freq <= 962500) if_band_center = 0x10; else - if (freq <= 1017500) if_band_center = 0x11; else - if (freq <= 1072500) if_band_center = 0x12; else if_band_center = 0x13; - - b[0] = 1; - b[1] = (num1 >> 5) & 0xFF; - b[2] = (num1 & 0x1F); - b[3] = div1; - b[4] = (num2 >> 5) & 0xFF; - b[5] = num2 & 0x1F; - b[6] = div2; - - dprintk(1, "IF1: %dMHz IF2: %dMHz\n", MT2131_IF1, MT2131_IF2); - dprintk(1, "PLL freq=%dkHz band=%d\n", (int)freq, (int)if_band_center); - dprintk(1, "PLL f_lo1=%dkHz f_lo2=%dkHz\n", (int)f_lo1, (int)f_lo2); - dprintk(1, "PLL div1=%d num1=%d div2=%d num2=%d\n", - (int)div1, (int)num1, (int)div2, (int)num2); - dprintk(1, "PLL [1..6]: %2x %2x %2x %2x %2x %2x\n", - (int)b[1], (int)b[2], (int)b[3], (int)b[4], (int)b[5], - (int)b[6]); - - ret = mt2131_writeregs(priv,b,7); - if (ret < 0) - return ret; - - mt2131_writereg(priv, 0x0b, if_band_center); - - /* Wait for lock */ - i = 0; - do { - mt2131_readreg(priv, 0x08, &lockval); - if ((lockval & 0x88) == 0x88) - break; - msleep(4); - i++; - } while (i < 10); - - return ret; -} - -static int mt2131_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct mt2131_priv *priv = fe->tuner_priv; - dprintk(1, "%s()\n", __func__); - *frequency = priv->frequency; - return 0; -} - -static int mt2131_get_status(struct dvb_frontend *fe, u32 *status) -{ - struct mt2131_priv *priv = fe->tuner_priv; - u8 lock_status = 0; - u8 afc_status = 0; - - *status = 0; - - mt2131_readreg(priv, 0x08, &lock_status); - if ((lock_status & 0x88) == 0x88) - *status = TUNER_STATUS_LOCKED; - - mt2131_readreg(priv, 0x09, &afc_status); - dprintk(1, "%s() - LO Status = 0x%x, AFC Status = 0x%x\n", - __func__, lock_status, afc_status); - - return 0; -} - -static int mt2131_init(struct dvb_frontend *fe) -{ - struct mt2131_priv *priv = fe->tuner_priv; - int ret; - dprintk(1, "%s()\n", __func__); - - if ((ret = mt2131_writeregs(priv, mt2131_config1, - sizeof(mt2131_config1))) < 0) - return ret; - - mt2131_writereg(priv, 0x0b, 0x09); - mt2131_writereg(priv, 0x15, 0x47); - mt2131_writereg(priv, 0x07, 0xf2); - mt2131_writereg(priv, 0x0b, 0x01); - - if ((ret = mt2131_writeregs(priv, mt2131_config2, - sizeof(mt2131_config2))) < 0) - return ret; - - return ret; -} - -static int mt2131_release(struct dvb_frontend *fe) -{ - dprintk(1, "%s()\n", __func__); - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - return 0; -} - -static const struct dvb_tuner_ops mt2131_tuner_ops = { - .info = { - .name = "Microtune MT2131", - .frequency_min = 48000000, - .frequency_max = 860000000, - .frequency_step = 50000, - }, - - .release = mt2131_release, - .init = mt2131_init, - - .set_params = mt2131_set_params, - .get_frequency = mt2131_get_frequency, - .get_status = mt2131_get_status -}; - -struct dvb_frontend * mt2131_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct mt2131_config *cfg, u16 if1) -{ - struct mt2131_priv *priv = NULL; - u8 id = 0; - - dprintk(1, "%s()\n", __func__); - - priv = kzalloc(sizeof(struct mt2131_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - - priv->cfg = cfg; - priv->i2c = i2c; - - if (mt2131_readreg(priv, 0, &id) != 0) { - kfree(priv); - return NULL; - } - if ( (id != 0x3E) && (id != 0x3F) ) { - printk(KERN_ERR "MT2131: Device not found at addr 0x%02x\n", - cfg->i2c_address); - kfree(priv); - return NULL; - } - - printk(KERN_INFO "MT2131: successfully identified at address 0x%02x\n", - cfg->i2c_address); - memcpy(&fe->ops.tuner_ops, &mt2131_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - fe->tuner_priv = priv; - return fe; -} -EXPORT_SYMBOL(mt2131_attach); - -MODULE_AUTHOR("Steven Toth"); -MODULE_DESCRIPTION("Microtune MT2131 silicon tuner driver"); -MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - */ 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 - * - * 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 - * - * 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.c b/drivers/media/common/tuners/mt2266.c deleted file mode 100644 index bca4d75e42d4..000000000000 --- a/drivers/media/common/tuners/mt2266.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Driver for Microtune MT2266 "Direct conversion low power broadband tuner" - * - * Copyright (c) 2007 Olivier DANET - * - * 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. - */ - -#include -#include -#include -#include -#include - -#include "dvb_frontend.h" -#include "mt2266.h" - -#define I2C_ADDRESS 0x60 - -#define REG_PART_REV 0 -#define REG_TUNE 1 -#define REG_BAND 6 -#define REG_BANDWIDTH 8 -#define REG_LOCK 0x12 - -#define PART_REV 0x85 - -struct mt2266_priv { - struct mt2266_config *cfg; - struct i2c_adapter *i2c; - - u32 frequency; - u32 bandwidth; - u8 band; -}; - -#define MT2266_VHF 1 -#define MT2266_UHF 0 - -/* Here, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ - -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 "MT2266: " args); printk("\n"); }} while (0) - -// Reads a single register -static int mt2266_readreg(struct mt2266_priv *priv, u8 reg, u8 *val) -{ - struct i2c_msg msg[2] = { - { .addr = priv->cfg->i2c_address, .flags = 0, .buf = ®, .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 "MT2266 I2C read failed\n"); - return -EREMOTEIO; - } - return 0; -} - -// Writes a single register -static int mt2266_writereg(struct mt2266_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 "MT2266 I2C write failed\n"); - return -EREMOTEIO; - } - return 0; -} - -// Writes a set of consecutive registers -static int mt2266_writeregs(struct mt2266_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 "MT2266 I2C write failed (len=%i)\n",(int)len); - return -EREMOTEIO; - } - return 0; -} - -// Initialisation sequences -static u8 mt2266_init1[] = { REG_TUNE, 0x00, 0x00, 0x28, - 0x00, 0x52, 0x99, 0x3f }; - -static u8 mt2266_init2[] = { - 0x17, 0x6d, 0x71, 0x61, 0xc0, 0xbf, 0xff, 0xdc, 0x00, 0x0a, 0xd4, - 0x03, 0x64, 0x64, 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7f, 0x5e, 0x3f, 0xff, 0xff, - 0xff, 0x00, 0x77, 0x0f, 0x2d -}; - -static u8 mt2266_init_8mhz[] = { REG_BANDWIDTH, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22 }; - -static u8 mt2266_init_7mhz[] = { REG_BANDWIDTH, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32 }; - -static u8 mt2266_init_6mhz[] = { REG_BANDWIDTH, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7 }; - -static u8 mt2266_uhf[] = { 0x1d, 0xdc, 0x00, 0x0a, 0xd4, 0x03, 0x64, 0x64, - 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14 }; - -static u8 mt2266_vhf[] = { 0x1d, 0xfe, 0x00, 0x00, 0xb4, 0x03, 0xa5, 0xa5, - 0xa5, 0xa5, 0x82, 0xaa, 0xf1, 0x17, 0x80, 0x1f }; - -#define FREF 30000 // Quartz oscillator 30 MHz - -static int mt2266_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct mt2266_priv *priv; - int ret=0; - u32 freq; - u32 tune; - u8 lnaband; - u8 b[10]; - int i; - u8 band; - - priv = fe->tuner_priv; - - freq = priv->frequency / 1000; /* Hz -> kHz */ - if (freq < 470000 && freq > 230000) - return -EINVAL; /* Gap between VHF and UHF bands */ - - priv->frequency = c->frequency; - tune = 2 * freq * (8192/16) / (FREF/16); - band = (freq < 300000) ? MT2266_VHF : MT2266_UHF; - if (band == MT2266_VHF) - tune *= 2; - - switch (c->bandwidth_hz) { - case 6000000: - mt2266_writeregs(priv, mt2266_init_6mhz, - sizeof(mt2266_init_6mhz)); - break; - case 8000000: - mt2266_writeregs(priv, mt2266_init_8mhz, - sizeof(mt2266_init_8mhz)); - break; - case 7000000: - default: - mt2266_writeregs(priv, mt2266_init_7mhz, - sizeof(mt2266_init_7mhz)); - break; - } - priv->bandwidth = c->bandwidth_hz; - - if (band == MT2266_VHF && priv->band == MT2266_UHF) { - dprintk("Switch from UHF to VHF"); - mt2266_writereg(priv, 0x05, 0x04); - mt2266_writereg(priv, 0x19, 0x61); - mt2266_writeregs(priv, mt2266_vhf, sizeof(mt2266_vhf)); - } else if (band == MT2266_UHF && priv->band == MT2266_VHF) { - dprintk("Switch from VHF to UHF"); - mt2266_writereg(priv, 0x05, 0x52); - mt2266_writereg(priv, 0x19, 0x61); - mt2266_writeregs(priv, mt2266_uhf, sizeof(mt2266_uhf)); - } - msleep(10); - - if (freq <= 495000) - lnaband = 0xEE; - else if (freq <= 525000) - lnaband = 0xDD; - else if (freq <= 550000) - lnaband = 0xCC; - else if (freq <= 580000) - lnaband = 0xBB; - else if (freq <= 605000) - lnaband = 0xAA; - else if (freq <= 630000) - lnaband = 0x99; - else if (freq <= 655000) - lnaband = 0x88; - else if (freq <= 685000) - lnaband = 0x77; - else if (freq <= 710000) - lnaband = 0x66; - else if (freq <= 735000) - lnaband = 0x55; - else if (freq <= 765000) - lnaband = 0x44; - else if (freq <= 802000) - lnaband = 0x33; - else if (freq <= 840000) - lnaband = 0x22; - else - lnaband = 0x11; - - b[0] = REG_TUNE; - b[1] = (tune >> 8) & 0x1F; - b[2] = tune & 0xFF; - b[3] = tune >> 13; - mt2266_writeregs(priv,b,4); - - dprintk("set_parms: tune=%d band=%d %s", - (int) tune, (int) lnaband, - (band == MT2266_UHF) ? "UHF" : "VHF"); - dprintk("set_parms: [1..3]: %2x %2x %2x", - (int) b[1], (int) b[2], (int)b[3]); - - if (band == MT2266_UHF) { - b[0] = 0x05; - b[1] = (priv->band == MT2266_VHF) ? 0x52 : 0x62; - b[2] = lnaband; - mt2266_writeregs(priv, b, 3); - } - - /* Wait for pll lock or timeout */ - i = 0; - do { - mt2266_readreg(priv,REG_LOCK,b); - if (b[0] & 0x40) - break; - msleep(10); - i++; - } while (i<10); - dprintk("Lock when i=%i",(int)i); - - if (band == MT2266_UHF && priv->band == MT2266_VHF) - mt2266_writereg(priv, 0x05, 0x62); - - priv->band = band; - - return ret; -} - -static void mt2266_calibrate(struct mt2266_priv *priv) -{ - mt2266_writereg(priv, 0x11, 0x03); - mt2266_writereg(priv, 0x11, 0x01); - mt2266_writeregs(priv, mt2266_init1, sizeof(mt2266_init1)); - mt2266_writeregs(priv, mt2266_init2, sizeof(mt2266_init2)); - mt2266_writereg(priv, 0x33, 0x5e); - mt2266_writereg(priv, 0x10, 0x10); - mt2266_writereg(priv, 0x10, 0x00); - mt2266_writeregs(priv, mt2266_init_8mhz, sizeof(mt2266_init_8mhz)); - msleep(25); - mt2266_writereg(priv, 0x17, 0x6d); - mt2266_writereg(priv, 0x1c, 0x00); - msleep(75); - mt2266_writereg(priv, 0x17, 0x6d); - mt2266_writereg(priv, 0x1c, 0xff); -} - -static int mt2266_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct mt2266_priv *priv = fe->tuner_priv; - *frequency = priv->frequency; - return 0; -} - -static int mt2266_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) -{ - struct mt2266_priv *priv = fe->tuner_priv; - *bandwidth = priv->bandwidth; - return 0; -} - -static int mt2266_init(struct dvb_frontend *fe) -{ - int ret; - struct mt2266_priv *priv = fe->tuner_priv; - ret = mt2266_writereg(priv, 0x17, 0x6d); - if (ret < 0) - return ret; - ret = mt2266_writereg(priv, 0x1c, 0xff); - if (ret < 0) - return ret; - return 0; -} - -static int mt2266_sleep(struct dvb_frontend *fe) -{ - struct mt2266_priv *priv = fe->tuner_priv; - mt2266_writereg(priv, 0x17, 0x6d); - mt2266_writereg(priv, 0x1c, 0x00); - return 0; -} - -static int mt2266_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - return 0; -} - -static const struct dvb_tuner_ops mt2266_tuner_ops = { - .info = { - .name = "Microtune MT2266", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_step = 50000, - }, - .release = mt2266_release, - .init = mt2266_init, - .sleep = mt2266_sleep, - .set_params = mt2266_set_params, - .get_frequency = mt2266_get_frequency, - .get_bandwidth = mt2266_get_bandwidth -}; - -struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg) -{ - struct mt2266_priv *priv = NULL; - u8 id = 0; - - priv = kzalloc(sizeof(struct mt2266_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - - priv->cfg = cfg; - priv->i2c = i2c; - priv->band = MT2266_UHF; - - if (mt2266_readreg(priv, 0, &id)) { - kfree(priv); - return NULL; - } - if (id != PART_REV) { - kfree(priv); - return NULL; - } - printk(KERN_INFO "MT2266: successfully identified\n"); - memcpy(&fe->ops.tuner_ops, &mt2266_tuner_ops, sizeof(struct dvb_tuner_ops)); - - fe->tuner_priv = priv; - mt2266_calibrate(priv); - return fe; -} -EXPORT_SYMBOL(mt2266_attach); - -MODULE_AUTHOR("Olivier DANET"); -MODULE_DESCRIPTION("Microtune MT2266 silicon tuner driver"); -MODULE_LICENSE("GPL"); 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 - * - * 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/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c deleted file mode 100644 index 6133315fb0e3..000000000000 --- a/drivers/media/common/tuners/mxl5005s.c +++ /dev/null @@ -1,4109 +0,0 @@ -/* - MaxLinear MXL5005S VSB/QAM/DVBT tuner driver - - Copyright (C) 2008 MaxLinear - Copyright (C) 2006 Steven Toth - Functions: - mxl5005s_reset() - mxl5005s_writereg() - mxl5005s_writeregs() - mxl5005s_init() - mxl5005s_reconfigure() - mxl5005s_AssignTunerMode() - mxl5005s_set_params() - mxl5005s_get_frequency() - mxl5005s_get_bandwidth() - mxl5005s_release() - mxl5005s_attach() - - Copyright (C) 2008 Realtek - Copyright (C) 2008 Jan Hoogenraad - Functions: - mxl5005s_SetRfFreqHz() - - 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. - -*/ - -/* - History of this driver (Steven Toth): - I was given a public release of a linux driver that included - support for the MaxLinear MXL5005S silicon tuner. Analysis of - the tuner driver showed clearly three things. - - 1. The tuner driver didn't support the LinuxTV tuner API - so the code Realtek added had to be removed. - - 2. A significant amount of the driver is reference driver code - from MaxLinear, I felt it was important to identify and - preserve this. - - 3. New code has to be added to interface correctly with the - LinuxTV API, as a regular kernel module. - - Other than the reference driver enum's, I've clearly marked - sections of the code and retained the copyright of the - respective owners. -*/ -#include -#include -#include -#include -#include -#include -#include "dvb_frontend.h" -#include "mxl5005s.h" - -static int debug; - -#define dprintk(level, arg...) do { \ - if (level <= debug) \ - printk(arg); \ - } while (0) - -#define TUNER_REGS_NUM 104 -#define INITCTRL_NUM 40 - -#ifdef _MXL_PRODUCTION -#define CHCTRL_NUM 39 -#else -#define CHCTRL_NUM 36 -#endif - -#define MXLCTRL_NUM 189 -#define MASTER_CONTROL_ADDR 9 - -/* Enumeration of Master Control Register State */ -enum master_control_state { - MC_LOAD_START = 1, - MC_POWER_DOWN, - MC_SYNTH_RESET, - MC_SEQ_OFF -}; - -/* Enumeration of MXL5005 Tuner Modulation Type */ -enum { - MXL_DEFAULT_MODULATION = 0, - MXL_DVBT, - MXL_ATSC, - MXL_QAM, - MXL_ANALOG_CABLE, - MXL_ANALOG_OTA -}; - -/* MXL5005 Tuner Register Struct */ -struct TunerReg { - u16 Reg_Num; /* Tuner Register Address */ - u16 Reg_Val; /* Current sw programmed value waiting to be written */ -}; - -enum { - /* Initialization Control Names */ - DN_IQTN_AMP_CUT = 1, /* 1 */ - BB_MODE, /* 2 */ - BB_BUF, /* 3 */ - BB_BUF_OA, /* 4 */ - BB_ALPF_BANDSELECT, /* 5 */ - BB_IQSWAP, /* 6 */ - BB_DLPF_BANDSEL, /* 7 */ - RFSYN_CHP_GAIN, /* 8 */ - RFSYN_EN_CHP_HIGAIN, /* 9 */ - AGC_IF, /* 10 */ - AGC_RF, /* 11 */ - IF_DIVVAL, /* 12 */ - IF_VCO_BIAS, /* 13 */ - CHCAL_INT_MOD_IF, /* 14 */ - CHCAL_FRAC_MOD_IF, /* 15 */ - DRV_RES_SEL, /* 16 */ - I_DRIVER, /* 17 */ - EN_AAF, /* 18 */ - EN_3P, /* 19 */ - EN_AUX_3P, /* 20 */ - SEL_AAF_BAND, /* 21 */ - SEQ_ENCLK16_CLK_OUT, /* 22 */ - SEQ_SEL4_16B, /* 23 */ - XTAL_CAPSELECT, /* 24 */ - IF_SEL_DBL, /* 25 */ - RFSYN_R_DIV, /* 26 */ - SEQ_EXTSYNTHCALIF, /* 27 */ - SEQ_EXTDCCAL, /* 28 */ - AGC_EN_RSSI, /* 29 */ - RFA_ENCLKRFAGC, /* 30 */ - RFA_RSSI_REFH, /* 31 */ - RFA_RSSI_REF, /* 32 */ - RFA_RSSI_REFL, /* 33 */ - RFA_FLR, /* 34 */ - RFA_CEIL, /* 35 */ - SEQ_EXTIQFSMPULSE, /* 36 */ - OVERRIDE_1, /* 37 */ - BB_INITSTATE_DLPF_TUNE, /* 38 */ - TG_R_DIV, /* 39 */ - EN_CHP_LIN_B, /* 40 */ - - /* Channel Change Control Names */ - DN_POLY = 51, /* 51 */ - DN_RFGAIN, /* 52 */ - DN_CAP_RFLPF, /* 53 */ - DN_EN_VHFUHFBAR, /* 54 */ - DN_GAIN_ADJUST, /* 55 */ - DN_IQTNBUF_AMP, /* 56 */ - DN_IQTNGNBFBIAS_BST, /* 57 */ - RFSYN_EN_OUTMUX, /* 58 */ - RFSYN_SEL_VCO_OUT, /* 59 */ - RFSYN_SEL_VCO_HI, /* 60 */ - RFSYN_SEL_DIVM, /* 61 */ - RFSYN_RF_DIV_BIAS, /* 62 */ - DN_SEL_FREQ, /* 63 */ - RFSYN_VCO_BIAS, /* 64 */ - CHCAL_INT_MOD_RF, /* 65 */ - CHCAL_FRAC_MOD_RF, /* 66 */ - RFSYN_LPF_R, /* 67 */ - CHCAL_EN_INT_RF, /* 68 */ - TG_LO_DIVVAL, /* 69 */ - TG_LO_SELVAL, /* 70 */ - TG_DIV_VAL, /* 71 */ - TG_VCO_BIAS, /* 72 */ - SEQ_EXTPOWERUP, /* 73 */ - OVERRIDE_2, /* 74 */ - OVERRIDE_3, /* 75 */ - OVERRIDE_4, /* 76 */ - SEQ_FSM_PULSE, /* 77 */ - GPIO_4B, /* 78 */ - GPIO_3B, /* 79 */ - GPIO_4, /* 80 */ - GPIO_3, /* 81 */ - GPIO_1B, /* 82 */ - DAC_A_ENABLE, /* 83 */ - DAC_B_ENABLE, /* 84 */ - DAC_DIN_A, /* 85 */ - DAC_DIN_B, /* 86 */ -#ifdef _MXL_PRODUCTION - RFSYN_EN_DIV, /* 87 */ - RFSYN_DIVM, /* 88 */ - DN_BYPASS_AGC_I2C /* 89 */ -#endif -}; - -/* - * The following context is source code provided by MaxLinear. - * MaxLinear source code - Common_MXL.h (?) - */ - -/* Constants */ -#define MXL5005S_REG_WRITING_TABLE_LEN_MAX 104 -#define MXL5005S_LATCH_BYTE 0xfe - -/* Register address, MSB, and LSB */ -#define MXL5005S_BB_IQSWAP_ADDR 59 -#define MXL5005S_BB_IQSWAP_MSB 0 -#define MXL5005S_BB_IQSWAP_LSB 0 - -#define MXL5005S_BB_DLPF_BANDSEL_ADDR 53 -#define MXL5005S_BB_DLPF_BANDSEL_MSB 4 -#define MXL5005S_BB_DLPF_BANDSEL_LSB 3 - -/* Standard modes */ -enum { - MXL5005S_STANDARD_DVBT, - MXL5005S_STANDARD_ATSC, -}; -#define MXL5005S_STANDARD_MODE_NUM 2 - -/* Bandwidth modes */ -enum { - MXL5005S_BANDWIDTH_6MHZ = 6000000, - MXL5005S_BANDWIDTH_7MHZ = 7000000, - MXL5005S_BANDWIDTH_8MHZ = 8000000, -}; -#define MXL5005S_BANDWIDTH_MODE_NUM 3 - -/* MXL5005 Tuner Control Struct */ -struct TunerControl { - u16 Ctrl_Num; /* Control Number */ - u16 size; /* Number of bits to represent Value */ - u16 addr[25]; /* Array of Tuner Register Address for each bit pos */ - u16 bit[25]; /* Array of bit pos in Reg Addr for each bit pos */ - u16 val[25]; /* Binary representation of Value */ -}; - -/* MXL5005 Tuner Struct */ -struct mxl5005s_state { - u8 Mode; /* 0: Analog Mode ; 1: Digital Mode */ - u8 IF_Mode; /* for Analog Mode, 0: zero IF; 1: low IF */ - u32 Chan_Bandwidth; /* filter channel bandwidth (6, 7, 8) */ - u32 IF_OUT; /* Desired IF Out Frequency */ - u16 IF_OUT_LOAD; /* IF Out Load Resistor (200/300 Ohms) */ - u32 RF_IN; /* RF Input Frequency */ - u32 Fxtal; /* XTAL Frequency */ - u8 AGC_Mode; /* AGC Mode 0: Dual AGC; 1: Single AGC */ - u16 TOP; /* Value: take over point */ - u8 CLOCK_OUT; /* 0: turn off clk out; 1: turn on clock out */ - u8 DIV_OUT; /* 4MHz or 16MHz */ - u8 CAPSELECT; /* 0: disable On-Chip pulling cap; 1: enable */ - u8 EN_RSSI; /* 0: disable RSSI; 1: enable RSSI */ - - /* Modulation Type; */ - /* 0 - Default; 1 - DVB-T; 2 - ATSC; 3 - QAM; 4 - Analog Cable */ - u8 Mod_Type; - - /* Tracking Filter Type */ - /* 0 - Default; 1 - Off; 2 - Type C; 3 - Type C-H */ - u8 TF_Type; - - /* Calculated Settings */ - u32 RF_LO; /* Synth RF LO Frequency */ - u32 IF_LO; /* Synth IF LO Frequency */ - u32 TG_LO; /* Synth TG_LO Frequency */ - - /* Pointers to ControlName Arrays */ - u16 Init_Ctrl_Num; /* Number of INIT Control Names */ - struct TunerControl - Init_Ctrl[INITCTRL_NUM]; /* INIT Control Names Array Pointer */ - - u16 CH_Ctrl_Num; /* Number of CH Control Names */ - struct TunerControl - CH_Ctrl[CHCTRL_NUM]; /* CH Control Name Array Pointer */ - - u16 MXL_Ctrl_Num; /* Number of MXL Control Names */ - struct TunerControl - MXL_Ctrl[MXLCTRL_NUM]; /* MXL Control Name Array Pointer */ - - /* Pointer to Tuner Register Array */ - u16 TunerRegs_Num; /* Number of Tuner Registers */ - struct TunerReg - TunerRegs[TUNER_REGS_NUM]; /* Tuner Register Array Pointer */ - - /* Linux driver framework specific */ - struct mxl5005s_config *config; - struct dvb_frontend *frontend; - struct i2c_adapter *i2c; - - /* Cache values */ - u32 current_mode; - -}; - -static u16 MXL_GetMasterControl(u8 *MasterReg, int state); -static u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value); -static u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value); -static void MXL_RegWriteBit(struct dvb_frontend *fe, u8 address, u8 bit, - u8 bitVal); -static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum, - u8 *RegVal, int *count); -static u32 MXL_Ceiling(u32 value, u32 resolution); -static u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal); -static u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum, - u32 value, u16 controlGroup); -static u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val); -static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum, - u8 *RegVal, int *count); -static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq); -static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe); -static void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe); -static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum, - u8 *RegVal, int *count); -static int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable, - u8 *datatable, u8 len); -static u16 MXL_IFSynthInit(struct dvb_frontend *fe); -static int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type, - u32 bandwidth); -static int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type, - u32 bandwidth); - -/* ---------------------------------------------------------------- - * Begin: Custom code salvaged from the Realtek driver. - * Copyright (C) 2008 Realtek - * Copyright (C) 2008 Jan Hoogenraad - * This code is placed under the terms of the GNU General Public License - * - * Released by Realtek under GPLv2. - * Thanks to Realtek for a lot of support we received ! - * - * Revision: 080314 - original version - */ - -static int mxl5005s_SetRfFreqHz(struct dvb_frontend *fe, unsigned long RfFreqHz) -{ - struct mxl5005s_state *state = fe->tuner_priv; - unsigned char AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; - unsigned char ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; - int TableLen; - - u32 IfDivval = 0; - unsigned char MasterControlByte; - - dprintk(1, "%s() freq=%ld\n", __func__, RfFreqHz); - - /* Set MxL5005S tuner RF frequency according to example code. */ - - /* Tuner RF frequency setting stage 0 */ - MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET); - AddrTable[0] = MASTER_CONTROL_ADDR; - ByteTable[0] |= state->config->AgcMasterByte; - - mxl5005s_writeregs(fe, AddrTable, ByteTable, 1); - - /* Tuner RF frequency setting stage 1 */ - MXL_TuneRF(fe, RfFreqHz); - - MXL_ControlRead(fe, IF_DIVVAL, &IfDivval); - - MXL_ControlWrite(fe, SEQ_FSM_PULSE, 0); - MXL_ControlWrite(fe, SEQ_EXTPOWERUP, 1); - MXL_ControlWrite(fe, IF_DIVVAL, 8); - MXL_GetCHRegister(fe, AddrTable, ByteTable, &TableLen); - - MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START); - AddrTable[TableLen] = MASTER_CONTROL_ADDR ; - ByteTable[TableLen] = MasterControlByte | - state->config->AgcMasterByte; - TableLen += 1; - - mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen); - - /* Wait 30 ms. */ - msleep(150); - - /* Tuner RF frequency setting stage 2 */ - MXL_ControlWrite(fe, SEQ_FSM_PULSE, 1); - MXL_ControlWrite(fe, IF_DIVVAL, IfDivval); - MXL_GetCHRegister_ZeroIF(fe, AddrTable, ByteTable, &TableLen); - - MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START); - AddrTable[TableLen] = MASTER_CONTROL_ADDR ; - ByteTable[TableLen] = MasterControlByte | - state->config->AgcMasterByte ; - TableLen += 1; - - mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen); - - msleep(100); - - return 0; -} -/* End: Custom code taken from the Realtek driver */ - -/* ---------------------------------------------------------------- - * Begin: Reference driver code found in the Realtek driver. - * Copyright (C) 2008 MaxLinear - */ -static u16 MXL5005_RegisterInit(struct dvb_frontend *fe) -{ - struct mxl5005s_state *state = fe->tuner_priv; - state->TunerRegs_Num = TUNER_REGS_NUM ; - - state->TunerRegs[0].Reg_Num = 9 ; - state->TunerRegs[0].Reg_Val = 0x40 ; - - state->TunerRegs[1].Reg_Num = 11 ; - state->TunerRegs[1].Reg_Val = 0x19 ; - - state->TunerRegs[2].Reg_Num = 12 ; - state->TunerRegs[2].Reg_Val = 0x60 ; - - state->TunerRegs[3].Reg_Num = 13 ; - state->TunerRegs[3].Reg_Val = 0x00 ; - - state->TunerRegs[4].Reg_Num = 14 ; - state->TunerRegs[4].Reg_Val = 0x00 ; - - state->TunerRegs[5].Reg_Num = 15 ; - state->TunerRegs[5].Reg_Val = 0xC0 ; - - state->TunerRegs[6].Reg_Num = 16 ; - state->TunerRegs[6].Reg_Val = 0x00 ; - - state->TunerRegs[7].Reg_Num = 17 ; - state->TunerRegs[7].Reg_Val = 0x00 ; - - state->TunerRegs[8].Reg_Num = 18 ; - state->TunerRegs[8].Reg_Val = 0x00 ; - - state->TunerRegs[9].Reg_Num = 19 ; - state->TunerRegs[9].Reg_Val = 0x34 ; - - state->TunerRegs[10].Reg_Num = 21 ; - state->TunerRegs[10].Reg_Val = 0x00 ; - - state->TunerRegs[11].Reg_Num = 22 ; - state->TunerRegs[11].Reg_Val = 0x6B ; - - state->TunerRegs[12].Reg_Num = 23 ; - state->TunerRegs[12].Reg_Val = 0x35 ; - - state->TunerRegs[13].Reg_Num = 24 ; - state->TunerRegs[13].Reg_Val = 0x70 ; - - state->TunerRegs[14].Reg_Num = 25 ; - state->TunerRegs[14].Reg_Val = 0x3E ; - - state->TunerRegs[15].Reg_Num = 26 ; - state->TunerRegs[15].Reg_Val = 0x82 ; - - state->TunerRegs[16].Reg_Num = 31 ; - state->TunerRegs[16].Reg_Val = 0x00 ; - - state->TunerRegs[17].Reg_Num = 32 ; - state->TunerRegs[17].Reg_Val = 0x40 ; - - state->TunerRegs[18].Reg_Num = 33 ; - state->TunerRegs[18].Reg_Val = 0x53 ; - - state->TunerRegs[19].Reg_Num = 34 ; - state->TunerRegs[19].Reg_Val = 0x81 ; - - state->TunerRegs[20].Reg_Num = 35 ; - state->TunerRegs[20].Reg_Val = 0xC9 ; - - state->TunerRegs[21].Reg_Num = 36 ; - state->TunerRegs[21].Reg_Val = 0x01 ; - - state->TunerRegs[22].Reg_Num = 37 ; - state->TunerRegs[22].Reg_Val = 0x00 ; - - state->TunerRegs[23].Reg_Num = 41 ; - state->TunerRegs[23].Reg_Val = 0x00 ; - - state->TunerRegs[24].Reg_Num = 42 ; - state->TunerRegs[24].Reg_Val = 0xF8 ; - - state->TunerRegs[25].Reg_Num = 43 ; - state->TunerRegs[25].Reg_Val = 0x43 ; - - state->TunerRegs[26].Reg_Num = 44 ; - state->TunerRegs[26].Reg_Val = 0x20 ; - - state->TunerRegs[27].Reg_Num = 45 ; - state->TunerRegs[27].Reg_Val = 0x80 ; - - state->TunerRegs[28].Reg_Num = 46 ; - state->TunerRegs[28].Reg_Val = 0x88 ; - - state->TunerRegs[29].Reg_Num = 47 ; - state->TunerRegs[29].Reg_Val = 0x86 ; - - state->TunerRegs[30].Reg_Num = 48 ; - state->TunerRegs[30].Reg_Val = 0x00 ; - - state->TunerRegs[31].Reg_Num = 49 ; - state->TunerRegs[31].Reg_Val = 0x00 ; - - state->TunerRegs[32].Reg_Num = 53 ; - state->TunerRegs[32].Reg_Val = 0x94 ; - - state->TunerRegs[33].Reg_Num = 54 ; - state->TunerRegs[33].Reg_Val = 0xFA ; - - state->TunerRegs[34].Reg_Num = 55 ; - state->TunerRegs[34].Reg_Val = 0x92 ; - - state->TunerRegs[35].Reg_Num = 56 ; - state->TunerRegs[35].Reg_Val = 0x80 ; - - state->TunerRegs[36].Reg_Num = 57 ; - state->TunerRegs[36].Reg_Val = 0x41 ; - - state->TunerRegs[37].Reg_Num = 58 ; - state->TunerRegs[37].Reg_Val = 0xDB ; - - state->TunerRegs[38].Reg_Num = 59 ; - state->TunerRegs[38].Reg_Val = 0x00 ; - - state->TunerRegs[39].Reg_Num = 60 ; - state->TunerRegs[39].Reg_Val = 0x00 ; - - state->TunerRegs[40].Reg_Num = 61 ; - state->TunerRegs[40].Reg_Val = 0x00 ; - - state->TunerRegs[41].Reg_Num = 62 ; - state->TunerRegs[41].Reg_Val = 0x00 ; - - state->TunerRegs[42].Reg_Num = 65 ; - state->TunerRegs[42].Reg_Val = 0xF8 ; - - state->TunerRegs[43].Reg_Num = 66 ; - state->TunerRegs[43].Reg_Val = 0xE4 ; - - state->TunerRegs[44].Reg_Num = 67 ; - state->TunerRegs[44].Reg_Val = 0x90 ; - - state->TunerRegs[45].Reg_Num = 68 ; - state->TunerRegs[45].Reg_Val = 0xC0 ; - - state->TunerRegs[46].Reg_Num = 69 ; - state->TunerRegs[46].Reg_Val = 0x01 ; - - state->TunerRegs[47].Reg_Num = 70 ; - state->TunerRegs[47].Reg_Val = 0x50 ; - - state->TunerRegs[48].Reg_Num = 71 ; - state->TunerRegs[48].Reg_Val = 0x06 ; - - state->TunerRegs[49].Reg_Num = 72 ; - state->TunerRegs[49].Reg_Val = 0x00 ; - - state->TunerRegs[50].Reg_Num = 73 ; - state->TunerRegs[50].Reg_Val = 0x20 ; - - state->TunerRegs[51].Reg_Num = 76 ; - state->TunerRegs[51].Reg_Val = 0xBB ; - - state->TunerRegs[52].Reg_Num = 77 ; - state->TunerRegs[52].Reg_Val = 0x13 ; - - state->TunerRegs[53].Reg_Num = 81 ; - state->TunerRegs[53].Reg_Val = 0x04 ; - - state->TunerRegs[54].Reg_Num = 82 ; - state->TunerRegs[54].Reg_Val = 0x75 ; - - state->TunerRegs[55].Reg_Num = 83 ; - state->TunerRegs[55].Reg_Val = 0x00 ; - - state->TunerRegs[56].Reg_Num = 84 ; - state->TunerRegs[56].Reg_Val = 0x00 ; - - state->TunerRegs[57].Reg_Num = 85 ; - state->TunerRegs[57].Reg_Val = 0x00 ; - - state->TunerRegs[58].Reg_Num = 91 ; - state->TunerRegs[58].Reg_Val = 0x70 ; - - state->TunerRegs[59].Reg_Num = 92 ; - state->TunerRegs[59].Reg_Val = 0x00 ; - - state->TunerRegs[60].Reg_Num = 93 ; - state->TunerRegs[60].Reg_Val = 0x00 ; - - state->TunerRegs[61].Reg_Num = 94 ; - state->TunerRegs[61].Reg_Val = 0x00 ; - - state->TunerRegs[62].Reg_Num = 95 ; - state->TunerRegs[62].Reg_Val = 0x0C ; - - state->TunerRegs[63].Reg_Num = 96 ; - state->TunerRegs[63].Reg_Val = 0x00 ; - - state->TunerRegs[64].Reg_Num = 97 ; - state->TunerRegs[64].Reg_Val = 0x00 ; - - state->TunerRegs[65].Reg_Num = 98 ; - state->TunerRegs[65].Reg_Val = 0xE2 ; - - state->TunerRegs[66].Reg_Num = 99 ; - state->TunerRegs[66].Reg_Val = 0x00 ; - - state->TunerRegs[67].Reg_Num = 100 ; - state->TunerRegs[67].Reg_Val = 0x00 ; - - state->TunerRegs[68].Reg_Num = 101 ; - state->TunerRegs[68].Reg_Val = 0x12 ; - - state->TunerRegs[69].Reg_Num = 102 ; - state->TunerRegs[69].Reg_Val = 0x80 ; - - state->TunerRegs[70].Reg_Num = 103 ; - state->TunerRegs[70].Reg_Val = 0x32 ; - - state->TunerRegs[71].Reg_Num = 104 ; - state->TunerRegs[71].Reg_Val = 0xB4 ; - - state->TunerRegs[72].Reg_Num = 105 ; - state->TunerRegs[72].Reg_Val = 0x60 ; - - state->TunerRegs[73].Reg_Num = 106 ; - state->TunerRegs[73].Reg_Val = 0x83 ; - - state->TunerRegs[74].Reg_Num = 107 ; - state->TunerRegs[74].Reg_Val = 0x84 ; - - state->TunerRegs[75].Reg_Num = 108 ; - state->TunerRegs[75].Reg_Val = 0x9C ; - - state->TunerRegs[76].Reg_Num = 109 ; - state->TunerRegs[76].Reg_Val = 0x02 ; - - state->TunerRegs[77].Reg_Num = 110 ; - state->TunerRegs[77].Reg_Val = 0x81 ; - - state->TunerRegs[78].Reg_Num = 111 ; - state->TunerRegs[78].Reg_Val = 0xC0 ; - - state->TunerRegs[79].Reg_Num = 112 ; - state->TunerRegs[79].Reg_Val = 0x10 ; - - state->TunerRegs[80].Reg_Num = 131 ; - state->TunerRegs[80].Reg_Val = 0x8A ; - - state->TunerRegs[81].Reg_Num = 132 ; - state->TunerRegs[81].Reg_Val = 0x10 ; - - state->TunerRegs[82].Reg_Num = 133 ; - state->TunerRegs[82].Reg_Val = 0x24 ; - - state->TunerRegs[83].Reg_Num = 134 ; - state->TunerRegs[83].Reg_Val = 0x00 ; - - state->TunerRegs[84].Reg_Num = 135 ; - state->TunerRegs[84].Reg_Val = 0x00 ; - - state->TunerRegs[85].Reg_Num = 136 ; - state->TunerRegs[85].Reg_Val = 0x7E ; - - state->TunerRegs[86].Reg_Num = 137 ; - state->TunerRegs[86].Reg_Val = 0x40 ; - - state->TunerRegs[87].Reg_Num = 138 ; - state->TunerRegs[87].Reg_Val = 0x38 ; - - state->TunerRegs[88].Reg_Num = 146 ; - state->TunerRegs[88].Reg_Val = 0xF6 ; - - state->TunerRegs[89].Reg_Num = 147 ; - state->TunerRegs[89].Reg_Val = 0x1A ; - - state->TunerRegs[90].Reg_Num = 148 ; - state->TunerRegs[90].Reg_Val = 0x62 ; - - state->TunerRegs[91].Reg_Num = 149 ; - state->TunerRegs[91].Reg_Val = 0x33 ; - - state->TunerRegs[92].Reg_Num = 150 ; - state->TunerRegs[92].Reg_Val = 0x80 ; - - state->TunerRegs[93].Reg_Num = 156 ; - state->TunerRegs[93].Reg_Val = 0x56 ; - - state->TunerRegs[94].Reg_Num = 157 ; - state->TunerRegs[94].Reg_Val = 0x17 ; - - state->TunerRegs[95].Reg_Num = 158 ; - state->TunerRegs[95].Reg_Val = 0xA9 ; - - state->TunerRegs[96].Reg_Num = 159 ; - state->TunerRegs[96].Reg_Val = 0x00 ; - - state->TunerRegs[97].Reg_Num = 160 ; - state->TunerRegs[97].Reg_Val = 0x00 ; - - state->TunerRegs[98].Reg_Num = 161 ; - state->TunerRegs[98].Reg_Val = 0x00 ; - - state->TunerRegs[99].Reg_Num = 162 ; - state->TunerRegs[99].Reg_Val = 0x40 ; - - state->TunerRegs[100].Reg_Num = 166 ; - state->TunerRegs[100].Reg_Val = 0xAE ; - - state->TunerRegs[101].Reg_Num = 167 ; - state->TunerRegs[101].Reg_Val = 0x1B ; - - state->TunerRegs[102].Reg_Num = 168 ; - state->TunerRegs[102].Reg_Val = 0xF2 ; - - state->TunerRegs[103].Reg_Num = 195 ; - state->TunerRegs[103].Reg_Val = 0x00 ; - - return 0 ; -} - -static u16 MXL5005_ControlInit(struct dvb_frontend *fe) -{ - struct mxl5005s_state *state = fe->tuner_priv; - state->Init_Ctrl_Num = INITCTRL_NUM; - - state->Init_Ctrl[0].Ctrl_Num = DN_IQTN_AMP_CUT ; - state->Init_Ctrl[0].size = 1 ; - state->Init_Ctrl[0].addr[0] = 73; - state->Init_Ctrl[0].bit[0] = 7; - state->Init_Ctrl[0].val[0] = 0; - - state->Init_Ctrl[1].Ctrl_Num = BB_MODE ; - state->Init_Ctrl[1].size = 1 ; - state->Init_Ctrl[1].addr[0] = 53; - state->Init_Ctrl[1].bit[0] = 2; - state->Init_Ctrl[1].val[0] = 1; - - state->Init_Ctrl[2].Ctrl_Num = BB_BUF ; - state->Init_Ctrl[2].size = 2 ; - state->Init_Ctrl[2].addr[0] = 53; - state->Init_Ctrl[2].bit[0] = 1; - state->Init_Ctrl[2].val[0] = 0; - state->Init_Ctrl[2].addr[1] = 57; - state->Init_Ctrl[2].bit[1] = 0; - state->Init_Ctrl[2].val[1] = 1; - - state->Init_Ctrl[3].Ctrl_Num = BB_BUF_OA ; - state->Init_Ctrl[3].size = 1 ; - state->Init_Ctrl[3].addr[0] = 53; - state->Init_Ctrl[3].bit[0] = 0; - state->Init_Ctrl[3].val[0] = 0; - - state->Init_Ctrl[4].Ctrl_Num = BB_ALPF_BANDSELECT ; - state->Init_Ctrl[4].size = 3 ; - state->Init_Ctrl[4].addr[0] = 53; - state->Init_Ctrl[4].bit[0] = 5; - state->Init_Ctrl[4].val[0] = 0; - state->Init_Ctrl[4].addr[1] = 53; - state->Init_Ctrl[4].bit[1] = 6; - state->Init_Ctrl[4].val[1] = 0; - state->Init_Ctrl[4].addr[2] = 53; - state->Init_Ctrl[4].bit[2] = 7; - state->Init_Ctrl[4].val[2] = 1; - - state->Init_Ctrl[5].Ctrl_Num = BB_IQSWAP ; - state->Init_Ctrl[5].size = 1 ; - state->Init_Ctrl[5].addr[0] = 59; - state->Init_Ctrl[5].bit[0] = 0; - state->Init_Ctrl[5].val[0] = 0; - - state->Init_Ctrl[6].Ctrl_Num = BB_DLPF_BANDSEL ; - state->Init_Ctrl[6].size = 2 ; - state->Init_Ctrl[6].addr[0] = 53; - state->Init_Ctrl[6].bit[0] = 3; - state->Init_Ctrl[6].val[0] = 0; - state->Init_Ctrl[6].addr[1] = 53; - state->Init_Ctrl[6].bit[1] = 4; - state->Init_Ctrl[6].val[1] = 1; - - state->Init_Ctrl[7].Ctrl_Num = RFSYN_CHP_GAIN ; - state->Init_Ctrl[7].size = 4 ; - state->Init_Ctrl[7].addr[0] = 22; - state->Init_Ctrl[7].bit[0] = 4; - state->Init_Ctrl[7].val[0] = 0; - state->Init_Ctrl[7].addr[1] = 22; - state->Init_Ctrl[7].bit[1] = 5; - state->Init_Ctrl[7].val[1] = 1; - state->Init_Ctrl[7].addr[2] = 22; - state->Init_Ctrl[7].bit[2] = 6; - state->Init_Ctrl[7].val[2] = 1; - state->Init_Ctrl[7].addr[3] = 22; - state->Init_Ctrl[7].bit[3] = 7; - state->Init_Ctrl[7].val[3] = 0; - - state->Init_Ctrl[8].Ctrl_Num = RFSYN_EN_CHP_HIGAIN ; - state->Init_Ctrl[8].size = 1 ; - state->Init_Ctrl[8].addr[0] = 22; - state->Init_Ctrl[8].bit[0] = 2; - state->Init_Ctrl[8].val[0] = 0; - - state->Init_Ctrl[9].Ctrl_Num = AGC_IF ; - state->Init_Ctrl[9].size = 4 ; - state->Init_Ctrl[9].addr[0] = 76; - state->Init_Ctrl[9].bit[0] = 0; - state->Init_Ctrl[9].val[0] = 1; - state->Init_Ctrl[9].addr[1] = 76; - state->Init_Ctrl[9].bit[1] = 1; - state->Init_Ctrl[9].val[1] = 1; - state->Init_Ctrl[9].addr[2] = 76; - state->Init_Ctrl[9].bit[2] = 2; - state->Init_Ctrl[9].val[2] = 0; - state->Init_Ctrl[9].addr[3] = 76; - state->Init_Ctrl[9].bit[3] = 3; - state->Init_Ctrl[9].val[3] = 1; - - state->Init_Ctrl[10].Ctrl_Num = AGC_RF ; - state->Init_Ctrl[10].size = 4 ; - state->Init_Ctrl[10].addr[0] = 76; - state->Init_Ctrl[10].bit[0] = 4; - state->Init_Ctrl[10].val[0] = 1; - state->Init_Ctrl[10].addr[1] = 76; - state->Init_Ctrl[10].bit[1] = 5; - state->Init_Ctrl[10].val[1] = 1; - state->Init_Ctrl[10].addr[2] = 76; - state->Init_Ctrl[10].bit[2] = 6; - state->Init_Ctrl[10].val[2] = 0; - state->Init_Ctrl[10].addr[3] = 76; - state->Init_Ctrl[10].bit[3] = 7; - state->Init_Ctrl[10].val[3] = 1; - - state->Init_Ctrl[11].Ctrl_Num = IF_DIVVAL ; - state->Init_Ctrl[11].size = 5 ; - state->Init_Ctrl[11].addr[0] = 43; - state->Init_Ctrl[11].bit[0] = 3; - state->Init_Ctrl[11].val[0] = 0; - state->Init_Ctrl[11].addr[1] = 43; - state->Init_Ctrl[11].bit[1] = 4; - state->Init_Ctrl[11].val[1] = 0; - state->Init_Ctrl[11].addr[2] = 43; - state->Init_Ctrl[11].bit[2] = 5; - state->Init_Ctrl[11].val[2] = 0; - state->Init_Ctrl[11].addr[3] = 43; - state->Init_Ctrl[11].bit[3] = 6; - state->Init_Ctrl[11].val[3] = 1; - state->Init_Ctrl[11].addr[4] = 43; - state->Init_Ctrl[11].bit[4] = 7; - state->Init_Ctrl[11].val[4] = 0; - - state->Init_Ctrl[12].Ctrl_Num = IF_VCO_BIAS ; - state->Init_Ctrl[12].size = 6 ; - state->Init_Ctrl[12].addr[0] = 44; - state->Init_Ctrl[12].bit[0] = 2; - state->Init_Ctrl[12].val[0] = 0; - state->Init_Ctrl[12].addr[1] = 44; - state->Init_Ctrl[12].bit[1] = 3; - state->Init_Ctrl[12].val[1] = 0; - state->Init_Ctrl[12].addr[2] = 44; - state->Init_Ctrl[12].bit[2] = 4; - state->Init_Ctrl[12].val[2] = 0; - state->Init_Ctrl[12].addr[3] = 44; - state->Init_Ctrl[12].bit[3] = 5; - state->Init_Ctrl[12].val[3] = 1; - state->Init_Ctrl[12].addr[4] = 44; - state->Init_Ctrl[12].bit[4] = 6; - state->Init_Ctrl[12].val[4] = 0; - state->Init_Ctrl[12].addr[5] = 44; - state->Init_Ctrl[12].bit[5] = 7; - state->Init_Ctrl[12].val[5] = 0; - - state->Init_Ctrl[13].Ctrl_Num = CHCAL_INT_MOD_IF ; - state->Init_Ctrl[13].size = 7 ; - state->Init_Ctrl[13].addr[0] = 11; - state->Init_Ctrl[13].bit[0] = 0; - state->Init_Ctrl[13].val[0] = 1; - state->Init_Ctrl[13].addr[1] = 11; - state->Init_Ctrl[13].bit[1] = 1; - state->Init_Ctrl[13].val[1] = 0; - state->Init_Ctrl[13].addr[2] = 11; - state->Init_Ctrl[13].bit[2] = 2; - state->Init_Ctrl[13].val[2] = 0; - state->Init_Ctrl[13].addr[3] = 11; - state->Init_Ctrl[13].bit[3] = 3; - state->Init_Ctrl[13].val[3] = 1; - state->Init_Ctrl[13].addr[4] = 11; - state->Init_Ctrl[13].bit[4] = 4; - state->Init_Ctrl[13].val[4] = 1; - state->Init_Ctrl[13].addr[5] = 11; - state->Init_Ctrl[13].bit[5] = 5; - state->Init_Ctrl[13].val[5] = 0; - state->Init_Ctrl[13].addr[6] = 11; - state->Init_Ctrl[13].bit[6] = 6; - state->Init_Ctrl[13].val[6] = 0; - - state->Init_Ctrl[14].Ctrl_Num = CHCAL_FRAC_MOD_IF ; - state->Init_Ctrl[14].size = 16 ; - state->Init_Ctrl[14].addr[0] = 13; - state->Init_Ctrl[14].bit[0] = 0; - state->Init_Ctrl[14].val[0] = 0; - state->Init_Ctrl[14].addr[1] = 13; - state->Init_Ctrl[14].bit[1] = 1; - state->Init_Ctrl[14].val[1] = 0; - state->Init_Ctrl[14].addr[2] = 13; - state->Init_Ctrl[14].bit[2] = 2; - state->Init_Ctrl[14].val[2] = 0; - state->Init_Ctrl[14].addr[3] = 13; - state->Init_Ctrl[14].bit[3] = 3; - state->Init_Ctrl[14].val[3] = 0; - state->Init_Ctrl[14].addr[4] = 13; - state->Init_Ctrl[14].bit[4] = 4; - state->Init_Ctrl[14].val[4] = 0; - state->Init_Ctrl[14].addr[5] = 13; - state->Init_Ctrl[14].bit[5] = 5; - state->Init_Ctrl[14].val[5] = 0; - state->Init_Ctrl[14].addr[6] = 13; - state->Init_Ctrl[14].bit[6] = 6; - state->Init_Ctrl[14].val[6] = 0; - state->Init_Ctrl[14].addr[7] = 13; - state->Init_Ctrl[14].bit[7] = 7; - state->Init_Ctrl[14].val[7] = 0; - state->Init_Ctrl[14].addr[8] = 12; - state->Init_Ctrl[14].bit[8] = 0; - state->Init_Ctrl[14].val[8] = 0; - state->Init_Ctrl[14].addr[9] = 12; - state->Init_Ctrl[14].bit[9] = 1; - state->Init_Ctrl[14].val[9] = 0; - state->Init_Ctrl[14].addr[10] = 12; - state->Init_Ctrl[14].bit[10] = 2; - state->Init_Ctrl[14].val[10] = 0; - state->Init_Ctrl[14].addr[11] = 12; - state->Init_Ctrl[14].bit[11] = 3; - state->Init_Ctrl[14].val[11] = 0; - state->Init_Ctrl[14].addr[12] = 12; - state->Init_Ctrl[14].bit[12] = 4; - state->Init_Ctrl[14].val[12] = 0; - state->Init_Ctrl[14].addr[13] = 12; - state->Init_Ctrl[14].bit[13] = 5; - state->Init_Ctrl[14].val[13] = 1; - state->Init_Ctrl[14].addr[14] = 12; - state->Init_Ctrl[14].bit[14] = 6; - state->Init_Ctrl[14].val[14] = 1; - state->Init_Ctrl[14].addr[15] = 12; - state->Init_Ctrl[14].bit[15] = 7; - state->Init_Ctrl[14].val[15] = 0; - - state->Init_Ctrl[15].Ctrl_Num = DRV_RES_SEL ; - state->Init_Ctrl[15].size = 3 ; - state->Init_Ctrl[15].addr[0] = 147; - state->Init_Ctrl[15].bit[0] = 2; - state->Init_Ctrl[15].val[0] = 0; - state->Init_Ctrl[15].addr[1] = 147; - state->Init_Ctrl[15].bit[1] = 3; - state->Init_Ctrl[15].val[1] = 1; - state->Init_Ctrl[15].addr[2] = 147; - state->Init_Ctrl[15].bit[2] = 4; - state->Init_Ctrl[15].val[2] = 1; - - state->Init_Ctrl[16].Ctrl_Num = I_DRIVER ; - state->Init_Ctrl[16].size = 2 ; - state->Init_Ctrl[16].addr[0] = 147; - state->Init_Ctrl[16].bit[0] = 0; - state->Init_Ctrl[16].val[0] = 0; - state->Init_Ctrl[16].addr[1] = 147; - state->Init_Ctrl[16].bit[1] = 1; - state->Init_Ctrl[16].val[1] = 1; - - state->Init_Ctrl[17].Ctrl_Num = EN_AAF ; - state->Init_Ctrl[17].size = 1 ; - state->Init_Ctrl[17].addr[0] = 147; - state->Init_Ctrl[17].bit[0] = 7; - state->Init_Ctrl[17].val[0] = 0; - - state->Init_Ctrl[18].Ctrl_Num = EN_3P ; - state->Init_Ctrl[18].size = 1 ; - state->Init_Ctrl[18].addr[0] = 147; - state->Init_Ctrl[18].bit[0] = 6; - state->Init_Ctrl[18].val[0] = 0; - - state->Init_Ctrl[19].Ctrl_Num = EN_AUX_3P ; - state->Init_Ctrl[19].size = 1 ; - state->Init_Ctrl[19].addr[0] = 156; - state->Init_Ctrl[19].bit[0] = 0; - state->Init_Ctrl[19].val[0] = 0; - - state->Init_Ctrl[20].Ctrl_Num = SEL_AAF_BAND ; - state->Init_Ctrl[20].size = 1 ; - state->Init_Ctrl[20].addr[0] = 147; - state->Init_Ctrl[20].bit[0] = 5; - state->Init_Ctrl[20].val[0] = 0; - - state->Init_Ctrl[21].Ctrl_Num = SEQ_ENCLK16_CLK_OUT ; - state->Init_Ctrl[21].size = 1 ; - state->Init_Ctrl[21].addr[0] = 137; - state->Init_Ctrl[21].bit[0] = 4; - state->Init_Ctrl[21].val[0] = 0; - - state->Init_Ctrl[22].Ctrl_Num = SEQ_SEL4_16B ; - state->Init_Ctrl[22].size = 1 ; - state->Init_Ctrl[22].addr[0] = 137; - state->Init_Ctrl[22].bit[0] = 7; - state->Init_Ctrl[22].val[0] = 0; - - state->Init_Ctrl[23].Ctrl_Num = XTAL_CAPSELECT ; - state->Init_Ctrl[23].size = 1 ; - state->Init_Ctrl[23].addr[0] = 91; - state->Init_Ctrl[23].bit[0] = 5; - state->Init_Ctrl[23].val[0] = 1; - - state->Init_Ctrl[24].Ctrl_Num = IF_SEL_DBL ; - state->Init_Ctrl[24].size = 1 ; - state->Init_Ctrl[24].addr[0] = 43; - state->Init_Ctrl[24].bit[0] = 0; - state->Init_Ctrl[24].val[0] = 1; - - state->Init_Ctrl[25].Ctrl_Num = RFSYN_R_DIV ; - state->Init_Ctrl[25].size = 2 ; - state->Init_Ctrl[25].addr[0] = 22; - state->Init_Ctrl[25].bit[0] = 0; - state->Init_Ctrl[25].val[0] = 1; - state->Init_Ctrl[25].addr[1] = 22; - state->Init_Ctrl[25].bit[1] = 1; - state->Init_Ctrl[25].val[1] = 1; - - state->Init_Ctrl[26].Ctrl_Num = SEQ_EXTSYNTHCALIF ; - state->Init_Ctrl[26].size = 1 ; - state->Init_Ctrl[26].addr[0] = 134; - state->Init_Ctrl[26].bit[0] = 2; - state->Init_Ctrl[26].val[0] = 0; - - state->Init_Ctrl[27].Ctrl_Num = SEQ_EXTDCCAL ; - state->Init_Ctrl[27].size = 1 ; - state->Init_Ctrl[27].addr[0] = 137; - state->Init_Ctrl[27].bit[0] = 3; - state->Init_Ctrl[27].val[0] = 0; - - state->Init_Ctrl[28].Ctrl_Num = AGC_EN_RSSI ; - state->Init_Ctrl[28].size = 1 ; - state->Init_Ctrl[28].addr[0] = 77; - state->Init_Ctrl[28].bit[0] = 7; - state->Init_Ctrl[28].val[0] = 0; - - state->Init_Ctrl[29].Ctrl_Num = RFA_ENCLKRFAGC ; - state->Init_Ctrl[29].size = 1 ; - state->Init_Ctrl[29].addr[0] = 166; - state->Init_Ctrl[29].bit[0] = 7; - state->Init_Ctrl[29].val[0] = 1; - - state->Init_Ctrl[30].Ctrl_Num = RFA_RSSI_REFH ; - state->Init_Ctrl[30].size = 3 ; - state->Init_Ctrl[30].addr[0] = 166; - state->Init_Ctrl[30].bit[0] = 0; - state->Init_Ctrl[30].val[0] = 0; - state->Init_Ctrl[30].addr[1] = 166; - state->Init_Ctrl[30].bit[1] = 1; - state->Init_Ctrl[30].val[1] = 1; - state->Init_Ctrl[30].addr[2] = 166; - state->Init_Ctrl[30].bit[2] = 2; - state->Init_Ctrl[30].val[2] = 1; - - state->Init_Ctrl[31].Ctrl_Num = RFA_RSSI_REF ; - state->Init_Ctrl[31].size = 3 ; - state->Init_Ctrl[31].addr[0] = 166; - state->Init_Ctrl[31].bit[0] = 3; - state->Init_Ctrl[31].val[0] = 1; - state->Init_Ctrl[31].addr[1] = 166; - state->Init_Ctrl[31].bit[1] = 4; - state->Init_Ctrl[31].val[1] = 0; - state->Init_Ctrl[31].addr[2] = 166; - state->Init_Ctrl[31].bit[2] = 5; - state->Init_Ctrl[31].val[2] = 1; - - state->Init_Ctrl[32].Ctrl_Num = RFA_RSSI_REFL ; - state->Init_Ctrl[32].size = 3 ; - state->Init_Ctrl[32].addr[0] = 167; - state->Init_Ctrl[32].bit[0] = 0; - state->Init_Ctrl[32].val[0] = 1; - state->Init_Ctrl[32].addr[1] = 167; - state->Init_Ctrl[32].bit[1] = 1; - state->Init_Ctrl[32].val[1] = 1; - state->Init_Ctrl[32].addr[2] = 167; - state->Init_Ctrl[32].bit[2] = 2; - state->Init_Ctrl[32].val[2] = 0; - - state->Init_Ctrl[33].Ctrl_Num = RFA_FLR ; - state->Init_Ctrl[33].size = 4 ; - state->Init_Ctrl[33].addr[0] = 168; - state->Init_Ctrl[33].bit[0] = 0; - state->Init_Ctrl[33].val[0] = 0; - state->Init_Ctrl[33].addr[1] = 168; - state->Init_Ctrl[33].bit[1] = 1; - state->Init_Ctrl[33].val[1] = 1; - state->Init_Ctrl[33].addr[2] = 168; - state->Init_Ctrl[33].bit[2] = 2; - state->Init_Ctrl[33].val[2] = 0; - state->Init_Ctrl[33].addr[3] = 168; - state->Init_Ctrl[33].bit[3] = 3; - state->Init_Ctrl[33].val[3] = 0; - - state->Init_Ctrl[34].Ctrl_Num = RFA_CEIL ; - state->Init_Ctrl[34].size = 4 ; - state->Init_Ctrl[34].addr[0] = 168; - state->Init_Ctrl[34].bit[0] = 4; - state->Init_Ctrl[34].val[0] = 1; - state->Init_Ctrl[34].addr[1] = 168; - state->Init_Ctrl[34].bit[1] = 5; - state->Init_Ctrl[34].val[1] = 1; - state->Init_Ctrl[34].addr[2] = 168; - state->Init_Ctrl[34].bit[2] = 6; - state->Init_Ctrl[34].val[2] = 1; - state->Init_Ctrl[34].addr[3] = 168; - state->Init_Ctrl[34].bit[3] = 7; - state->Init_Ctrl[34].val[3] = 1; - - state->Init_Ctrl[35].Ctrl_Num = SEQ_EXTIQFSMPULSE ; - state->Init_Ctrl[35].size = 1 ; - state->Init_Ctrl[35].addr[0] = 135; - state->Init_Ctrl[35].bit[0] = 0; - state->Init_Ctrl[35].val[0] = 0; - - state->Init_Ctrl[36].Ctrl_Num = OVERRIDE_1 ; - state->Init_Ctrl[36].size = 1 ; - state->Init_Ctrl[36].addr[0] = 56; - state->Init_Ctrl[36].bit[0] = 3; - state->Init_Ctrl[36].val[0] = 0; - - state->Init_Ctrl[37].Ctrl_Num = BB_INITSTATE_DLPF_TUNE ; - state->Init_Ctrl[37].size = 7 ; - state->Init_Ctrl[37].addr[0] = 59; - state->Init_Ctrl[37].bit[0] = 1; - state->Init_Ctrl[37].val[0] = 0; - state->Init_Ctrl[37].addr[1] = 59; - state->Init_Ctrl[37].bit[1] = 2; - state->Init_Ctrl[37].val[1] = 0; - state->Init_Ctrl[37].addr[2] = 59; - state->Init_Ctrl[37].bit[2] = 3; - state->Init_Ctrl[37].val[2] = 0; - state->Init_Ctrl[37].addr[3] = 59; - state->Init_Ctrl[37].bit[3] = 4; - state->Init_Ctrl[37].val[3] = 0; - state->Init_Ctrl[37].addr[4] = 59; - state->Init_Ctrl[37].bit[4] = 5; - state->Init_Ctrl[37].val[4] = 0; - state->Init_Ctrl[37].addr[5] = 59; - state->Init_Ctrl[37].bit[5] = 6; - state->Init_Ctrl[37].val[5] = 0; - state->Init_Ctrl[37].addr[6] = 59; - state->Init_Ctrl[37].bit[6] = 7; - state->Init_Ctrl[37].val[6] = 0; - - state->Init_Ctrl[38].Ctrl_Num = TG_R_DIV ; - state->Init_Ctrl[38].size = 6 ; - state->Init_Ctrl[38].addr[0] = 32; - state->Init_Ctrl[38].bit[0] = 2; - state->Init_Ctrl[38].val[0] = 0; - state->Init_Ctrl[38].addr[1] = 32; - state->Init_Ctrl[38].bit[1] = 3; - state->Init_Ctrl[38].val[1] = 0; - state->Init_Ctrl[38].addr[2] = 32; - state->Init_Ctrl[38].bit[2] = 4; - state->Init_Ctrl[38].val[2] = 0; - state->Init_Ctrl[38].addr[3] = 32; - state->Init_Ctrl[38].bit[3] = 5; - state->Init_Ctrl[38].val[3] = 0; - state->Init_Ctrl[38].addr[4] = 32; - state->Init_Ctrl[38].bit[4] = 6; - state->Init_Ctrl[38].val[4] = 1; - state->Init_Ctrl[38].addr[5] = 32; - state->Init_Ctrl[38].bit[5] = 7; - state->Init_Ctrl[38].val[5] = 0; - - state->Init_Ctrl[39].Ctrl_Num = EN_CHP_LIN_B ; - state->Init_Ctrl[39].size = 1 ; - state->Init_Ctrl[39].addr[0] = 25; - state->Init_Ctrl[39].bit[0] = 3; - state->Init_Ctrl[39].val[0] = 1; - - - state->CH_Ctrl_Num = CHCTRL_NUM ; - - state->CH_Ctrl[0].Ctrl_Num = DN_POLY ; - state->CH_Ctrl[0].size = 2 ; - state->CH_Ctrl[0].addr[0] = 68; - state->CH_Ctrl[0].bit[0] = 6; - state->CH_Ctrl[0].val[0] = 1; - state->CH_Ctrl[0].addr[1] = 68; - state->CH_Ctrl[0].bit[1] = 7; - state->CH_Ctrl[0].val[1] = 1; - - state->CH_Ctrl[1].Ctrl_Num = DN_RFGAIN ; - state->CH_Ctrl[1].size = 2 ; - state->CH_Ctrl[1].addr[0] = 70; - state->CH_Ctrl[1].bit[0] = 6; - state->CH_Ctrl[1].val[0] = 1; - state->CH_Ctrl[1].addr[1] = 70; - state->CH_Ctrl[1].bit[1] = 7; - state->CH_Ctrl[1].val[1] = 0; - - state->CH_Ctrl[2].Ctrl_Num = DN_CAP_RFLPF ; - state->CH_Ctrl[2].size = 9 ; - state->CH_Ctrl[2].addr[0] = 69; - state->CH_Ctrl[2].bit[0] = 5; - state->CH_Ctrl[2].val[0] = 0; - state->CH_Ctrl[2].addr[1] = 69; - state->CH_Ctrl[2].bit[1] = 6; - state->CH_Ctrl[2].val[1] = 0; - state->CH_Ctrl[2].addr[2] = 69; - state->CH_Ctrl[2].bit[2] = 7; - state->CH_Ctrl[2].val[2] = 0; - state->CH_Ctrl[2].addr[3] = 68; - state->CH_Ctrl[2].bit[3] = 0; - state->CH_Ctrl[2].val[3] = 0; - state->CH_Ctrl[2].addr[4] = 68; - state->CH_Ctrl[2].bit[4] = 1; - state->CH_Ctrl[2].val[4] = 0; - state->CH_Ctrl[2].addr[5] = 68; - state->CH_Ctrl[2].bit[5] = 2; - state->CH_Ctrl[2].val[5] = 0; - state->CH_Ctrl[2].addr[6] = 68; - state->CH_Ctrl[2].bit[6] = 3; - state->CH_Ctrl[2].val[6] = 0; - state->CH_Ctrl[2].addr[7] = 68; - state->CH_Ctrl[2].bit[7] = 4; - state->CH_Ctrl[2].val[7] = 0; - state->CH_Ctrl[2].addr[8] = 68; - state->CH_Ctrl[2].bit[8] = 5; - state->CH_Ctrl[2].val[8] = 0; - - state->CH_Ctrl[3].Ctrl_Num = DN_EN_VHFUHFBAR ; - state->CH_Ctrl[3].size = 1 ; - state->CH_Ctrl[3].addr[0] = 70; - state->CH_Ctrl[3].bit[0] = 5; - state->CH_Ctrl[3].val[0] = 0; - - state->CH_Ctrl[4].Ctrl_Num = DN_GAIN_ADJUST ; - state->CH_Ctrl[4].size = 3 ; - state->CH_Ctrl[4].addr[0] = 73; - state->CH_Ctrl[4].bit[0] = 4; - state->CH_Ctrl[4].val[0] = 0; - state->CH_Ctrl[4].addr[1] = 73; - state->CH_Ctrl[4].bit[1] = 5; - state->CH_Ctrl[4].val[1] = 1; - state->CH_Ctrl[4].addr[2] = 73; - state->CH_Ctrl[4].bit[2] = 6; - state->CH_Ctrl[4].val[2] = 0; - - state->CH_Ctrl[5].Ctrl_Num = DN_IQTNBUF_AMP ; - state->CH_Ctrl[5].size = 4 ; - state->CH_Ctrl[5].addr[0] = 70; - state->CH_Ctrl[5].bit[0] = 0; - state->CH_Ctrl[5].val[0] = 0; - state->CH_Ctrl[5].addr[1] = 70; - state->CH_Ctrl[5].bit[1] = 1; - state->CH_Ctrl[5].val[1] = 0; - state->CH_Ctrl[5].addr[2] = 70; - state->CH_Ctrl[5].bit[2] = 2; - state->CH_Ctrl[5].val[2] = 0; - state->CH_Ctrl[5].addr[3] = 70; - state->CH_Ctrl[5].bit[3] = 3; - state->CH_Ctrl[5].val[3] = 0; - - state->CH_Ctrl[6].Ctrl_Num = DN_IQTNGNBFBIAS_BST ; - state->CH_Ctrl[6].size = 1 ; - state->CH_Ctrl[6].addr[0] = 70; - state->CH_Ctrl[6].bit[0] = 4; - state->CH_Ctrl[6].val[0] = 1; - - state->CH_Ctrl[7].Ctrl_Num = RFSYN_EN_OUTMUX ; - state->CH_Ctrl[7].size = 1 ; - state->CH_Ctrl[7].addr[0] = 111; - state->CH_Ctrl[7].bit[0] = 4; - state->CH_Ctrl[7].val[0] = 0; - - state->CH_Ctrl[8].Ctrl_Num = RFSYN_SEL_VCO_OUT ; - state->CH_Ctrl[8].size = 1 ; - state->CH_Ctrl[8].addr[0] = 111; - state->CH_Ctrl[8].bit[0] = 7; - state->CH_Ctrl[8].val[0] = 1; - - state->CH_Ctrl[9].Ctrl_Num = RFSYN_SEL_VCO_HI ; - state->CH_Ctrl[9].size = 1 ; - state->CH_Ctrl[9].addr[0] = 111; - state->CH_Ctrl[9].bit[0] = 6; - state->CH_Ctrl[9].val[0] = 1; - - state->CH_Ctrl[10].Ctrl_Num = RFSYN_SEL_DIVM ; - state->CH_Ctrl[10].size = 1 ; - state->CH_Ctrl[10].addr[0] = 111; - state->CH_Ctrl[10].bit[0] = 5; - state->CH_Ctrl[10].val[0] = 0; - - state->CH_Ctrl[11].Ctrl_Num = RFSYN_RF_DIV_BIAS ; - state->CH_Ctrl[11].size = 2 ; - state->CH_Ctrl[11].addr[0] = 110; - state->CH_Ctrl[11].bit[0] = 0; - state->CH_Ctrl[11].val[0] = 1; - state->CH_Ctrl[11].addr[1] = 110; - state->CH_Ctrl[11].bit[1] = 1; - state->CH_Ctrl[11].val[1] = 0; - - state->CH_Ctrl[12].Ctrl_Num = DN_SEL_FREQ ; - state->CH_Ctrl[12].size = 3 ; - state->CH_Ctrl[12].addr[0] = 69; - state->CH_Ctrl[12].bit[0] = 2; - state->CH_Ctrl[12].val[0] = 0; - state->CH_Ctrl[12].addr[1] = 69; - state->CH_Ctrl[12].bit[1] = 3; - state->CH_Ctrl[12].val[1] = 0; - state->CH_Ctrl[12].addr[2] = 69; - state->CH_Ctrl[12].bit[2] = 4; - state->CH_Ctrl[12].val[2] = 0; - - state->CH_Ctrl[13].Ctrl_Num = RFSYN_VCO_BIAS ; - state->CH_Ctrl[13].size = 6 ; - state->CH_Ctrl[13].addr[0] = 110; - state->CH_Ctrl[13].bit[0] = 2; - state->CH_Ctrl[13].val[0] = 0; - state->CH_Ctrl[13].addr[1] = 110; - state->CH_Ctrl[13].bit[1] = 3; - state->CH_Ctrl[13].val[1] = 0; - state->CH_Ctrl[13].addr[2] = 110; - state->CH_Ctrl[13].bit[2] = 4; - state->CH_Ctrl[13].val[2] = 0; - state->CH_Ctrl[13].addr[3] = 110; - state->CH_Ctrl[13].bit[3] = 5; - state->CH_Ctrl[13].val[3] = 0; - state->CH_Ctrl[13].addr[4] = 110; - state->CH_Ctrl[13].bit[4] = 6; - state->CH_Ctrl[13].val[4] = 0; - state->CH_Ctrl[13].addr[5] = 110; - state->CH_Ctrl[13].bit[5] = 7; - state->CH_Ctrl[13].val[5] = 1; - - state->CH_Ctrl[14].Ctrl_Num = CHCAL_INT_MOD_RF ; - state->CH_Ctrl[14].size = 7 ; - state->CH_Ctrl[14].addr[0] = 14; - state->CH_Ctrl[14].bit[0] = 0; - state->CH_Ctrl[14].val[0] = 0; - state->CH_Ctrl[14].addr[1] = 14; - state->CH_Ctrl[14].bit[1] = 1; - state->CH_Ctrl[14].val[1] = 0; - state->CH_Ctrl[14].addr[2] = 14; - state->CH_Ctrl[14].bit[2] = 2; - state->CH_Ctrl[14].val[2] = 0; - state->CH_Ctrl[14].addr[3] = 14; - state->CH_Ctrl[14].bit[3] = 3; - state->CH_Ctrl[14].val[3] = 0; - state->CH_Ctrl[14].addr[4] = 14; - state->CH_Ctrl[14].bit[4] = 4; - state->CH_Ctrl[14].val[4] = 0; - state->CH_Ctrl[14].addr[5] = 14; - state->CH_Ctrl[14].bit[5] = 5; - state->CH_Ctrl[14].val[5] = 0; - state->CH_Ctrl[14].addr[6] = 14; - state->CH_Ctrl[14].bit[6] = 6; - state->CH_Ctrl[14].val[6] = 0; - - state->CH_Ctrl[15].Ctrl_Num = CHCAL_FRAC_MOD_RF ; - state->CH_Ctrl[15].size = 18 ; - state->CH_Ctrl[15].addr[0] = 17; - state->CH_Ctrl[15].bit[0] = 6; - state->CH_Ctrl[15].val[0] = 0; - state->CH_Ctrl[15].addr[1] = 17; - state->CH_Ctrl[15].bit[1] = 7; - state->CH_Ctrl[15].val[1] = 0; - state->CH_Ctrl[15].addr[2] = 16; - state->CH_Ctrl[15].bit[2] = 0; - state->CH_Ctrl[15].val[2] = 0; - state->CH_Ctrl[15].addr[3] = 16; - state->CH_Ctrl[15].bit[3] = 1; - state->CH_Ctrl[15].val[3] = 0; - state->CH_Ctrl[15].addr[4] = 16; - state->CH_Ctrl[15].bit[4] = 2; - state->CH_Ctrl[15].val[4] = 0; - state->CH_Ctrl[15].addr[5] = 16; - state->CH_Ctrl[15].bit[5] = 3; - state->CH_Ctrl[15].val[5] = 0; - state->CH_Ctrl[15].addr[6] = 16; - state->CH_Ctrl[15].bit[6] = 4; - state->CH_Ctrl[15].val[6] = 0; - state->CH_Ctrl[15].addr[7] = 16; - state->CH_Ctrl[15].bit[7] = 5; - state->CH_Ctrl[15].val[7] = 0; - state->CH_Ctrl[15].addr[8] = 16; - state->CH_Ctrl[15].bit[8] = 6; - state->CH_Ctrl[15].val[8] = 0; - state->CH_Ctrl[15].addr[9] = 16; - state->CH_Ctrl[15].bit[9] = 7; - state->CH_Ctrl[15].val[9] = 0; - state->CH_Ctrl[15].addr[10] = 15; - state->CH_Ctrl[15].bit[10] = 0; - state->CH_Ctrl[15].val[10] = 0; - state->CH_Ctrl[15].addr[11] = 15; - state->CH_Ctrl[15].bit[11] = 1; - state->CH_Ctrl[15].val[11] = 0; - state->CH_Ctrl[15].addr[12] = 15; - state->CH_Ctrl[15].bit[12] = 2; - state->CH_Ctrl[15].val[12] = 0; - state->CH_Ctrl[15].addr[13] = 15; - state->CH_Ctrl[15].bit[13] = 3; - state->CH_Ctrl[15].val[13] = 0; - state->CH_Ctrl[15].addr[14] = 15; - state->CH_Ctrl[15].bit[14] = 4; - state->CH_Ctrl[15].val[14] = 0; - state->CH_Ctrl[15].addr[15] = 15; - state->CH_Ctrl[15].bit[15] = 5; - state->CH_Ctrl[15].val[15] = 0; - state->CH_Ctrl[15].addr[16] = 15; - state->CH_Ctrl[15].bit[16] = 6; - state->CH_Ctrl[15].val[16] = 1; - state->CH_Ctrl[15].addr[17] = 15; - state->CH_Ctrl[15].bit[17] = 7; - state->CH_Ctrl[15].val[17] = 1; - - state->CH_Ctrl[16].Ctrl_Num = RFSYN_LPF_R ; - state->CH_Ctrl[16].size = 5 ; - state->CH_Ctrl[16].addr[0] = 112; - state->CH_Ctrl[16].bit[0] = 0; - state->CH_Ctrl[16].val[0] = 0; - state->CH_Ctrl[16].addr[1] = 112; - state->CH_Ctrl[16].bit[1] = 1; - state->CH_Ctrl[16].val[1] = 0; - state->CH_Ctrl[16].addr[2] = 112; - state->CH_Ctrl[16].bit[2] = 2; - state->CH_Ctrl[16].val[2] = 0; - state->CH_Ctrl[16].addr[3] = 112; - state->CH_Ctrl[16].bit[3] = 3; - state->CH_Ctrl[16].val[3] = 0; - state->CH_Ctrl[16].addr[4] = 112; - state->CH_Ctrl[16].bit[4] = 4; - state->CH_Ctrl[16].val[4] = 1; - - state->CH_Ctrl[17].Ctrl_Num = CHCAL_EN_INT_RF ; - state->CH_Ctrl[17].size = 1 ; - state->CH_Ctrl[17].addr[0] = 14; - state->CH_Ctrl[17].bit[0] = 7; - state->CH_Ctrl[17].val[0] = 0; - - state->CH_Ctrl[18].Ctrl_Num = TG_LO_DIVVAL ; - state->CH_Ctrl[18].size = 4 ; - state->CH_Ctrl[18].addr[0] = 107; - state->CH_Ctrl[18].bit[0] = 3; - state->CH_Ctrl[18].val[0] = 0; - state->CH_Ctrl[18].addr[1] = 107; - state->CH_Ctrl[18].bit[1] = 4; - state->CH_Ctrl[18].val[1] = 0; - state->CH_Ctrl[18].addr[2] = 107; - state->CH_Ctrl[18].bit[2] = 5; - state->CH_Ctrl[18].val[2] = 0; - state->CH_Ctrl[18].addr[3] = 107; - state->CH_Ctrl[18].bit[3] = 6; - state->CH_Ctrl[18].val[3] = 0; - - state->CH_Ctrl[19].Ctrl_Num = TG_LO_SELVAL ; - state->CH_Ctrl[19].size = 3 ; - state->CH_Ctrl[19].addr[0] = 107; - state->CH_Ctrl[19].bit[0] = 7; - state->CH_Ctrl[19].val[0] = 1; - state->CH_Ctrl[19].addr[1] = 106; - state->CH_Ctrl[19].bit[1] = 0; - state->CH_Ctrl[19].val[1] = 1; - state->CH_Ctrl[19].addr[2] = 106; - state->CH_Ctrl[19].bit[2] = 1; - state->CH_Ctrl[19].val[2] = 1; - - state->CH_Ctrl[20].Ctrl_Num = TG_DIV_VAL ; - state->CH_Ctrl[20].size = 11 ; - state->CH_Ctrl[20].addr[0] = 109; - state->CH_Ctrl[20].bit[0] = 2; - state->CH_Ctrl[20].val[0] = 0; - state->CH_Ctrl[20].addr[1] = 109; - state->CH_Ctrl[20].bit[1] = 3; - state->CH_Ctrl[20].val[1] = 0; - state->CH_Ctrl[20].addr[2] = 109; - state->CH_Ctrl[20].bit[2] = 4; - state->CH_Ctrl[20].val[2] = 0; - state->CH_Ctrl[20].addr[3] = 109; - state->CH_Ctrl[20].bit[3] = 5; - state->CH_Ctrl[20].val[3] = 0; - state->CH_Ctrl[20].addr[4] = 109; - state->CH_Ctrl[20].bit[4] = 6; - state->CH_Ctrl[20].val[4] = 0; - state->CH_Ctrl[20].addr[5] = 109; - state->CH_Ctrl[20].bit[5] = 7; - state->CH_Ctrl[20].val[5] = 0; - state->CH_Ctrl[20].addr[6] = 108; - state->CH_Ctrl[20].bit[6] = 0; - state->CH_Ctrl[20].val[6] = 0; - state->CH_Ctrl[20].addr[7] = 108; - state->CH_Ctrl[20].bit[7] = 1; - state->CH_Ctrl[20].val[7] = 0; - state->CH_Ctrl[20].addr[8] = 108; - state->CH_Ctrl[20].bit[8] = 2; - state->CH_Ctrl[20].val[8] = 1; - state->CH_Ctrl[20].addr[9] = 108; - state->CH_Ctrl[20].bit[9] = 3; - state->CH_Ctrl[20].val[9] = 1; - state->CH_Ctrl[20].addr[10] = 108; - state->CH_Ctrl[20].bit[10] = 4; - state->CH_Ctrl[20].val[10] = 1; - - state->CH_Ctrl[21].Ctrl_Num = TG_VCO_BIAS ; - state->CH_Ctrl[21].size = 6 ; - state->CH_Ctrl[21].addr[0] = 106; - state->CH_Ctrl[21].bit[0] = 2; - state->CH_Ctrl[21].val[0] = 0; - state->CH_Ctrl[21].addr[1] = 106; - state->CH_Ctrl[21].bit[1] = 3; - state->CH_Ctrl[21].val[1] = 0; - state->CH_Ctrl[21].addr[2] = 106; - state->CH_Ctrl[21].bit[2] = 4; - state->CH_Ctrl[21].val[2] = 0; - state->CH_Ctrl[21].addr[3] = 106; - state->CH_Ctrl[21].bit[3] = 5; - state->CH_Ctrl[21].val[3] = 0; - state->CH_Ctrl[21].addr[4] = 106; - state->CH_Ctrl[21].bit[4] = 6; - state->CH_Ctrl[21].val[4] = 0; - state->CH_Ctrl[21].addr[5] = 106; - state->CH_Ctrl[21].bit[5] = 7; - state->CH_Ctrl[21].val[5] = 1; - - state->CH_Ctrl[22].Ctrl_Num = SEQ_EXTPOWERUP ; - state->CH_Ctrl[22].size = 1 ; - state->CH_Ctrl[22].addr[0] = 138; - state->CH_Ctrl[22].bit[0] = 4; - state->CH_Ctrl[22].val[0] = 1; - - state->CH_Ctrl[23].Ctrl_Num = OVERRIDE_2 ; - state->CH_Ctrl[23].size = 1 ; - state->CH_Ctrl[23].addr[0] = 17; - state->CH_Ctrl[23].bit[0] = 5; - state->CH_Ctrl[23].val[0] = 0; - - state->CH_Ctrl[24].Ctrl_Num = OVERRIDE_3 ; - state->CH_Ctrl[24].size = 1 ; - state->CH_Ctrl[24].addr[0] = 111; - state->CH_Ctrl[24].bit[0] = 3; - state->CH_Ctrl[24].val[0] = 0; - - state->CH_Ctrl[25].Ctrl_Num = OVERRIDE_4 ; - state->CH_Ctrl[25].size = 1 ; - state->CH_Ctrl[25].addr[0] = 112; - state->CH_Ctrl[25].bit[0] = 7; - state->CH_Ctrl[25].val[0] = 0; - - state->CH_Ctrl[26].Ctrl_Num = SEQ_FSM_PULSE ; - state->CH_Ctrl[26].size = 1 ; - state->CH_Ctrl[26].addr[0] = 136; - state->CH_Ctrl[26].bit[0] = 7; - state->CH_Ctrl[26].val[0] = 0; - - state->CH_Ctrl[27].Ctrl_Num = GPIO_4B ; - state->CH_Ctrl[27].size = 1 ; - state->CH_Ctrl[27].addr[0] = 149; - state->CH_Ctrl[27].bit[0] = 7; - state->CH_Ctrl[27].val[0] = 0; - - state->CH_Ctrl[28].Ctrl_Num = GPIO_3B ; - state->CH_Ctrl[28].size = 1 ; - state->CH_Ctrl[28].addr[0] = 149; - state->CH_Ctrl[28].bit[0] = 6; - state->CH_Ctrl[28].val[0] = 0; - - state->CH_Ctrl[29].Ctrl_Num = GPIO_4 ; - state->CH_Ctrl[29].size = 1 ; - state->CH_Ctrl[29].addr[0] = 149; - state->CH_Ctrl[29].bit[0] = 5; - state->CH_Ctrl[29].val[0] = 1; - - state->CH_Ctrl[30].Ctrl_Num = GPIO_3 ; - state->CH_Ctrl[30].size = 1 ; - state->CH_Ctrl[30].addr[0] = 149; - state->CH_Ctrl[30].bit[0] = 4; - state->CH_Ctrl[30].val[0] = 1; - - state->CH_Ctrl[31].Ctrl_Num = GPIO_1B ; - state->CH_Ctrl[31].size = 1 ; - state->CH_Ctrl[31].addr[0] = 149; - state->CH_Ctrl[31].bit[0] = 3; - state->CH_Ctrl[31].val[0] = 0; - - state->CH_Ctrl[32].Ctrl_Num = DAC_A_ENABLE ; - state->CH_Ctrl[32].size = 1 ; - state->CH_Ctrl[32].addr[0] = 93; - state->CH_Ctrl[32].bit[0] = 1; - state->CH_Ctrl[32].val[0] = 0; - - state->CH_Ctrl[33].Ctrl_Num = DAC_B_ENABLE ; - state->CH_Ctrl[33].size = 1 ; - state->CH_Ctrl[33].addr[0] = 93; - state->CH_Ctrl[33].bit[0] = 0; - state->CH_Ctrl[33].val[0] = 0; - - state->CH_Ctrl[34].Ctrl_Num = DAC_DIN_A ; - state->CH_Ctrl[34].size = 6 ; - state->CH_Ctrl[34].addr[0] = 92; - state->CH_Ctrl[34].bit[0] = 2; - state->CH_Ctrl[34].val[0] = 0; - state->CH_Ctrl[34].addr[1] = 92; - state->CH_Ctrl[34].bit[1] = 3; - state->CH_Ctrl[34].val[1] = 0; - state->CH_Ctrl[34].addr[2] = 92; - state->CH_Ctrl[34].bit[2] = 4; - state->CH_Ctrl[34].val[2] = 0; - state->CH_Ctrl[34].addr[3] = 92; - state->CH_Ctrl[34].bit[3] = 5; - state->CH_Ctrl[34].val[3] = 0; - state->CH_Ctrl[34].addr[4] = 92; - state->CH_Ctrl[34].bit[4] = 6; - state->CH_Ctrl[34].val[4] = 0; - state->CH_Ctrl[34].addr[5] = 92; - state->CH_Ctrl[34].bit[5] = 7; - state->CH_Ctrl[34].val[5] = 0; - - state->CH_Ctrl[35].Ctrl_Num = DAC_DIN_B ; - state->CH_Ctrl[35].size = 6 ; - state->CH_Ctrl[35].addr[0] = 93; - state->CH_Ctrl[35].bit[0] = 2; - state->CH_Ctrl[35].val[0] = 0; - state->CH_Ctrl[35].addr[1] = 93; - state->CH_Ctrl[35].bit[1] = 3; - state->CH_Ctrl[35].val[1] = 0; - state->CH_Ctrl[35].addr[2] = 93; - state->CH_Ctrl[35].bit[2] = 4; - state->CH_Ctrl[35].val[2] = 0; - state->CH_Ctrl[35].addr[3] = 93; - state->CH_Ctrl[35].bit[3] = 5; - state->CH_Ctrl[35].val[3] = 0; - state->CH_Ctrl[35].addr[4] = 93; - state->CH_Ctrl[35].bit[4] = 6; - state->CH_Ctrl[35].val[4] = 0; - state->CH_Ctrl[35].addr[5] = 93; - state->CH_Ctrl[35].bit[5] = 7; - state->CH_Ctrl[35].val[5] = 0; - -#ifdef _MXL_PRODUCTION - state->CH_Ctrl[36].Ctrl_Num = RFSYN_EN_DIV ; - state->CH_Ctrl[36].size = 1 ; - state->CH_Ctrl[36].addr[0] = 109; - state->CH_Ctrl[36].bit[0] = 1; - state->CH_Ctrl[36].val[0] = 1; - - state->CH_Ctrl[37].Ctrl_Num = RFSYN_DIVM ; - state->CH_Ctrl[37].size = 2 ; - state->CH_Ctrl[37].addr[0] = 112; - state->CH_Ctrl[37].bit[0] = 5; - state->CH_Ctrl[37].val[0] = 0; - state->CH_Ctrl[37].addr[1] = 112; - state->CH_Ctrl[37].bit[1] = 6; - state->CH_Ctrl[37].val[1] = 0; - - state->CH_Ctrl[38].Ctrl_Num = DN_BYPASS_AGC_I2C ; - state->CH_Ctrl[38].size = 1 ; - state->CH_Ctrl[38].addr[0] = 65; - state->CH_Ctrl[38].bit[0] = 1; - state->CH_Ctrl[38].val[0] = 0; -#endif - - return 0 ; -} - -static void InitTunerControls(struct dvb_frontend *fe) -{ - MXL5005_RegisterInit(fe); - MXL5005_ControlInit(fe); -#ifdef _MXL_INTERNAL - MXL5005_MXLControlInit(fe); -#endif -} - -static u16 MXL5005_TunerConfig(struct dvb_frontend *fe, - u8 Mode, /* 0: Analog Mode ; 1: Digital Mode */ - u8 IF_mode, /* for Analog Mode, 0: zero IF; 1: low IF */ - u32 Bandwidth, /* filter channel bandwidth (6, 7, 8) */ - u32 IF_out, /* Desired IF Out Frequency */ - u32 Fxtal, /* XTAL Frequency */ - u8 AGC_Mode, /* AGC Mode - Dual AGC: 0, Single AGC: 1 */ - u16 TOP, /* 0: Dual AGC; Value: take over point */ - u16 IF_OUT_LOAD, /* IF Out Load Resistor (200 / 300 Ohms) */ - u8 CLOCK_OUT, /* 0: turn off clk out; 1: turn on clock out */ - u8 DIV_OUT, /* 0: Div-1; 1: Div-4 */ - u8 CAPSELECT, /* 0: disable On-Chip pulling cap; 1: enable */ - u8 EN_RSSI, /* 0: disable RSSI; 1: enable RSSI */ - - /* Modulation Type; */ - /* 0 - Default; 1 - DVB-T; 2 - ATSC; 3 - QAM; 4 - Analog Cable */ - u8 Mod_Type, - - /* Tracking Filter */ - /* 0 - Default; 1 - Off; 2 - Type C; 3 - Type C-H */ - u8 TF_Type - ) -{ - struct mxl5005s_state *state = fe->tuner_priv; - u16 status = 0; - - state->Mode = Mode; - state->IF_Mode = IF_mode; - state->Chan_Bandwidth = Bandwidth; - state->IF_OUT = IF_out; - state->Fxtal = Fxtal; - state->AGC_Mode = AGC_Mode; - state->TOP = TOP; - state->IF_OUT_LOAD = IF_OUT_LOAD; - state->CLOCK_OUT = CLOCK_OUT; - state->DIV_OUT = DIV_OUT; - state->CAPSELECT = CAPSELECT; - state->EN_RSSI = EN_RSSI; - state->Mod_Type = Mod_Type; - state->TF_Type = TF_Type; - - /* Initialize all the controls and registers */ - InitTunerControls(fe); - - /* Synthesizer LO frequency calculation */ - MXL_SynthIFLO_Calc(fe); - - return status; -} - -static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe) -{ - struct mxl5005s_state *state = fe->tuner_priv; - if (state->Mode == 1) /* Digital Mode */ - state->IF_LO = state->IF_OUT; - else /* Analog Mode */ { - if (state->IF_Mode == 0) /* Analog Zero IF mode */ - state->IF_LO = state->IF_OUT + 400000; - else /* Analog Low IF mode */ - state->IF_LO = state->IF_OUT + state->Chan_Bandwidth/2; - } -} - -static void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe) -{ - struct mxl5005s_state *state = fe->tuner_priv; - - if (state->Mode == 1) /* Digital Mode */ { - /* remove 20.48MHz setting for 2.6.10 */ - state->RF_LO = state->RF_IN; - /* change for 2.6.6 */ - state->TG_LO = state->RF_IN - 750000; - } else /* Analog Mode */ { - if (state->IF_Mode == 0) /* Analog Zero IF mode */ { - state->RF_LO = state->RF_IN - 400000; - state->TG_LO = state->RF_IN - 1750000; - } else /* Analog Low IF mode */ { - state->RF_LO = state->RF_IN - state->Chan_Bandwidth/2; - state->TG_LO = state->RF_IN - - state->Chan_Bandwidth + 500000; - } - } -} - -static u16 MXL_OverwriteICDefault(struct dvb_frontend *fe) -{ - u16 status = 0; - - status += MXL_ControlWrite(fe, OVERRIDE_1, 1); - status += MXL_ControlWrite(fe, OVERRIDE_2, 1); - status += MXL_ControlWrite(fe, OVERRIDE_3, 1); - status += MXL_ControlWrite(fe, OVERRIDE_4, 1); - - return status; -} - -static u16 MXL_BlockInit(struct dvb_frontend *fe) -{ - struct mxl5005s_state *state = fe->tuner_priv; - u16 status = 0; - - status += MXL_OverwriteICDefault(fe); - - /* Downconverter Control Dig Ana */ - status += MXL_ControlWrite(fe, DN_IQTN_AMP_CUT, state->Mode ? 1 : 0); - - /* Filter Control Dig Ana */ - status += MXL_ControlWrite(fe, BB_MODE, state->Mode ? 0 : 1); - status += MXL_ControlWrite(fe, BB_BUF, state->Mode ? 3 : 2); - status += MXL_ControlWrite(fe, BB_BUF_OA, state->Mode ? 1 : 0); - status += MXL_ControlWrite(fe, BB_IQSWAP, state->Mode ? 0 : 1); - status += MXL_ControlWrite(fe, BB_INITSTATE_DLPF_TUNE, 0); - - /* Initialize Low-Pass Filter */ - if (state->Mode) { /* Digital Mode */ - switch (state->Chan_Bandwidth) { - case 8000000: - status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 0); - break; - case 7000000: - status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 2); - break; - case 6000000: - status += MXL_ControlWrite(fe, - BB_DLPF_BANDSEL, 3); - break; - } - } else { /* Analog Mode */ - switch (state->Chan_Bandwidth) { - case 8000000: /* Low Zero */ - status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT, - (state->IF_Mode ? 0 : 3)); - break; - case 7000000: - status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT, - (state->IF_Mode ? 1 : 4)); - break; - case 6000000: - status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT, - (state->IF_Mode ? 2 : 5)); - break; - } - } - - /* Charge Pump Control Dig Ana */ - status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, state->Mode ? 5 : 8); - status += MXL_ControlWrite(fe, - RFSYN_EN_CHP_HIGAIN, state->Mode ? 1 : 1); - status += MXL_ControlWrite(fe, EN_CHP_LIN_B, state->Mode ? 0 : 0); - - /* AGC TOP Control */ - if (state->AGC_Mode == 0) /* Dual AGC */ { - status += MXL_ControlWrite(fe, AGC_IF, 15); - status += MXL_ControlWrite(fe, AGC_RF, 15); - } else /* Single AGC Mode Dig Ana */ - status += MXL_ControlWrite(fe, AGC_RF, state->Mode ? 15 : 12); - - if (state->TOP == 55) /* TOP == 5.5 */ - status += MXL_ControlWrite(fe, AGC_IF, 0x0); - - if (state->TOP == 72) /* TOP == 7.2 */ - status += MXL_ControlWrite(fe, AGC_IF, 0x1); - - if (state->TOP == 92) /* TOP == 9.2 */ - status += MXL_ControlWrite(fe, AGC_IF, 0x2); - - if (state->TOP == 110) /* TOP == 11.0 */ - status += MXL_ControlWrite(fe, AGC_IF, 0x3); - - if (state->TOP == 129) /* TOP == 12.9 */ - status += MXL_ControlWrite(fe, AGC_IF, 0x4); - - if (state->TOP == 147) /* TOP == 14.7 */ - status += MXL_ControlWrite(fe, AGC_IF, 0x5); - - if (state->TOP == 168) /* TOP == 16.8 */ - status += MXL_ControlWrite(fe, AGC_IF, 0x6); - - if (state->TOP == 194) /* TOP == 19.4 */ - status += MXL_ControlWrite(fe, AGC_IF, 0x7); - - if (state->TOP == 212) /* TOP == 21.2 */ - status += MXL_ControlWrite(fe, AGC_IF, 0x9); - - if (state->TOP == 232) /* TOP == 23.2 */ - status += MXL_ControlWrite(fe, AGC_IF, 0xA); - - if (state->TOP == 252) /* TOP == 25.2 */ - status += MXL_ControlWrite(fe, AGC_IF, 0xB); - - if (state->TOP == 271) /* TOP == 27.1 */ - status += MXL_ControlWrite(fe, AGC_IF, 0xC); - - if (state->TOP == 292) /* TOP == 29.2 */ - status += MXL_ControlWrite(fe, AGC_IF, 0xD); - - if (state->TOP == 317) /* TOP == 31.7 */ - status += MXL_ControlWrite(fe, AGC_IF, 0xE); - - if (state->TOP == 349) /* TOP == 34.9 */ - status += MXL_ControlWrite(fe, AGC_IF, 0xF); - - /* IF Synthesizer Control */ - status += MXL_IFSynthInit(fe); - - /* IF UpConverter Control */ - if (state->IF_OUT_LOAD == 200) { - status += MXL_ControlWrite(fe, DRV_RES_SEL, 6); - status += MXL_ControlWrite(fe, I_DRIVER, 2); - } - if (state->IF_OUT_LOAD == 300) { - status += MXL_ControlWrite(fe, DRV_RES_SEL, 4); - status += MXL_ControlWrite(fe, I_DRIVER, 1); - } - - /* Anti-Alias Filtering Control - * initialise Anti-Aliasing Filter - */ - if (state->Mode) { /* Digital Mode */ - if (state->IF_OUT >= 4000000UL && state->IF_OUT <= 6280000UL) { - status += MXL_ControlWrite(fe, EN_AAF, 1); - status += MXL_ControlWrite(fe, EN_3P, 1); - status += MXL_ControlWrite(fe, EN_AUX_3P, 1); - status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0); - } - if ((state->IF_OUT == 36125000UL) || - (state->IF_OUT == 36150000UL)) { - status += MXL_ControlWrite(fe, EN_AAF, 1); - status += MXL_ControlWrite(fe, EN_3P, 1); - status += MXL_ControlWrite(fe, EN_AUX_3P, 1); - status += MXL_ControlWrite(fe, SEL_AAF_BAND, 1); - } - if (state->IF_OUT > 36150000UL) { - status += MXL_ControlWrite(fe, EN_AAF, 0); - status += MXL_ControlWrite(fe, EN_3P, 1); - status += MXL_ControlWrite(fe, EN_AUX_3P, 1); - status += MXL_ControlWrite(fe, SEL_AAF_BAND, 1); - } - } else { /* Analog Mode */ - if (state->IF_OUT >= 4000000UL && state->IF_OUT <= 5000000UL) { - status += MXL_ControlWrite(fe, EN_AAF, 1); - status += MXL_ControlWrite(fe, EN_3P, 1); - status += MXL_ControlWrite(fe, EN_AUX_3P, 1); - status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0); - } - if (state->IF_OUT > 5000000UL) { - status += MXL_ControlWrite(fe, EN_AAF, 0); - status += MXL_ControlWrite(fe, EN_3P, 0); - status += MXL_ControlWrite(fe, EN_AUX_3P, 0); - status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0); - } - } - - /* Demod Clock Out */ - if (state->CLOCK_OUT) - status += MXL_ControlWrite(fe, SEQ_ENCLK16_CLK_OUT, 1); - else - status += MXL_ControlWrite(fe, SEQ_ENCLK16_CLK_OUT, 0); - - if (state->DIV_OUT == 1) - status += MXL_ControlWrite(fe, SEQ_SEL4_16B, 1); - if (state->DIV_OUT == 0) - status += MXL_ControlWrite(fe, SEQ_SEL4_16B, 0); - - /* Crystal Control */ - if (state->CAPSELECT) - status += MXL_ControlWrite(fe, XTAL_CAPSELECT, 1); - else - status += MXL_ControlWrite(fe, XTAL_CAPSELECT, 0); - - if (state->Fxtal >= 12000000UL && state->Fxtal <= 16000000UL) - status += MXL_ControlWrite(fe, IF_SEL_DBL, 1); - if (state->Fxtal > 16000000UL && state->Fxtal <= 32000000UL) - status += MXL_ControlWrite(fe, IF_SEL_DBL, 0); - - if (state->Fxtal >= 12000000UL && state->Fxtal <= 22000000UL) - status += MXL_ControlWrite(fe, RFSYN_R_DIV, 3); - if (state->Fxtal > 22000000UL && state->Fxtal <= 32000000UL) - status += MXL_ControlWrite(fe, RFSYN_R_DIV, 0); - - /* Misc Controls */ - if (state->Mode == 0 && state->IF_Mode == 1) /* Analog LowIF mode */ - status += MXL_ControlWrite(fe, SEQ_EXTIQFSMPULSE, 0); - else - status += MXL_ControlWrite(fe, SEQ_EXTIQFSMPULSE, 1); - - /* status += MXL_ControlRead(fe, IF_DIVVAL, &IF_DIVVAL_Val); */ - - /* Set TG_R_DIV */ - status += MXL_ControlWrite(fe, TG_R_DIV, - MXL_Ceiling(state->Fxtal, 1000000)); - - /* Apply Default value to BB_INITSTATE_DLPF_TUNE */ - - /* RSSI Control */ - if (state->EN_RSSI) { - status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); - status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); - status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); - status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); - - /* RSSI reference point */ - status += MXL_ControlWrite(fe, RFA_RSSI_REF, 2); - status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 3); - status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1); - - /* TOP point */ - status += MXL_ControlWrite(fe, RFA_FLR, 0); - status += MXL_ControlWrite(fe, RFA_CEIL, 12); - } - - /* Modulation type bit settings - * Override the control values preset - */ - if (state->Mod_Type == MXL_DVBT) /* DVB-T Mode */ { - state->AGC_Mode = 1; /* Single AGC Mode */ - - /* Enable RSSI */ - status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); - status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); - status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); - status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); - - /* RSSI reference point */ - status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); - status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); - status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1); - - /* TOP point */ - status += MXL_ControlWrite(fe, RFA_FLR, 2); - status += MXL_ControlWrite(fe, RFA_CEIL, 13); - if (state->IF_OUT <= 6280000UL) /* Low IF */ - status += MXL_ControlWrite(fe, BB_IQSWAP, 0); - else /* High IF */ - status += MXL_ControlWrite(fe, BB_IQSWAP, 1); - - } - if (state->Mod_Type == MXL_ATSC) /* ATSC Mode */ { - state->AGC_Mode = 1; /* Single AGC Mode */ - - /* Enable RSSI */ - status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); - status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); - status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); - status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); - - /* RSSI reference point */ - status += MXL_ControlWrite(fe, RFA_RSSI_REF, 2); - status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 4); - status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1); - - /* TOP point */ - status += MXL_ControlWrite(fe, RFA_FLR, 2); - status += MXL_ControlWrite(fe, RFA_CEIL, 13); - status += MXL_ControlWrite(fe, BB_INITSTATE_DLPF_TUNE, 1); - /* Low Zero */ - status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 5); - - if (state->IF_OUT <= 6280000UL) /* Low IF */ - status += MXL_ControlWrite(fe, BB_IQSWAP, 0); - else /* High IF */ - status += MXL_ControlWrite(fe, BB_IQSWAP, 1); - } - if (state->Mod_Type == MXL_QAM) /* QAM Mode */ { - state->Mode = MXL_DIGITAL_MODE; - - /* state->AGC_Mode = 1; */ /* Single AGC Mode */ - - /* Disable RSSI */ /* change here for v2.6.5 */ - status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); - status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); - status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); - status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); - - /* RSSI reference point */ - status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); - status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); - status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2); - /* change here for v2.6.5 */ - status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); - - if (state->IF_OUT <= 6280000UL) /* Low IF */ - status += MXL_ControlWrite(fe, BB_IQSWAP, 0); - else /* High IF */ - status += MXL_ControlWrite(fe, BB_IQSWAP, 1); - status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2); - - } - if (state->Mod_Type == MXL_ANALOG_CABLE) { - /* Analog Cable Mode */ - /* state->Mode = MXL_DIGITAL_MODE; */ - - state->AGC_Mode = 1; /* Single AGC Mode */ - - /* Disable RSSI */ - status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); - status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); - status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); - status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); - /* change for 2.6.3 */ - status += MXL_ControlWrite(fe, AGC_IF, 1); - status += MXL_ControlWrite(fe, AGC_RF, 15); - status += MXL_ControlWrite(fe, BB_IQSWAP, 1); - } - - if (state->Mod_Type == MXL_ANALOG_OTA) { - /* Analog OTA Terrestrial mode add for 2.6.7 */ - /* state->Mode = MXL_ANALOG_MODE; */ - - /* Enable RSSI */ - status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); - status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); - status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); - status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); - - /* RSSI reference point */ - status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); - status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); - status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2); - status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); - status += MXL_ControlWrite(fe, BB_IQSWAP, 1); - } - - /* RSSI disable */ - if (state->EN_RSSI == 0) { - status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); - status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); - status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); - status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); - } - - return status; -} - -static u16 MXL_IFSynthInit(struct dvb_frontend *fe) -{ - struct mxl5005s_state *state = fe->tuner_priv; - u16 status = 0 ; - u32 Fref = 0 ; - u32 Kdbl, intModVal ; - u32 fracModVal ; - Kdbl = 2 ; - - if (state->Fxtal >= 12000000UL && state->Fxtal <= 16000000UL) - Kdbl = 2 ; - if (state->Fxtal > 16000000UL && state->Fxtal <= 32000000UL) - Kdbl = 1 ; - - /* IF Synthesizer Control */ - if (state->Mode == 0 && state->IF_Mode == 1) /* Analog Low IF mode */ { - if (state->IF_LO == 41000000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); - Fref = 328000000UL ; - } - if (state->IF_LO == 47000000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 376000000UL ; - } - if (state->IF_LO == 54000000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); - Fref = 324000000UL ; - } - if (state->IF_LO == 60000000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 360000000UL ; - } - if (state->IF_LO == 39250000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); - Fref = 314000000UL ; - } - if (state->IF_LO == 39650000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); - Fref = 317200000UL ; - } - if (state->IF_LO == 40150000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); - Fref = 321200000UL ; - } - if (state->IF_LO == 40650000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); - Fref = 325200000UL ; - } - } - - if (state->Mode || (state->Mode == 0 && state->IF_Mode == 0)) { - if (state->IF_LO == 57000000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 342000000UL ; - } - if (state->IF_LO == 44000000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 352000000UL ; - } - if (state->IF_LO == 43750000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 350000000UL ; - } - if (state->IF_LO == 36650000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 366500000UL ; - } - if (state->IF_LO == 36150000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 361500000UL ; - } - if (state->IF_LO == 36000000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 360000000UL ; - } - if (state->IF_LO == 35250000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 352500000UL ; - } - if (state->IF_LO == 34750000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 347500000UL ; - } - if (state->IF_LO == 6280000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 376800000UL ; - } - if (state->IF_LO == 5000000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 360000000UL ; - } - if (state->IF_LO == 4500000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 360000000UL ; - } - if (state->IF_LO == 4570000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 365600000UL ; - } - if (state->IF_LO == 4000000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x05); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 360000000UL ; - } - if (state->IF_LO == 57400000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 344400000UL ; - } - if (state->IF_LO == 44400000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 355200000UL ; - } - if (state->IF_LO == 44150000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 353200000UL ; - } - if (state->IF_LO == 37050000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 370500000UL ; - } - if (state->IF_LO == 36550000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 365500000UL ; - } - if (state->IF_LO == 36125000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 361250000UL ; - } - if (state->IF_LO == 6000000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 360000000UL ; - } - if (state->IF_LO == 5400000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); - Fref = 324000000UL ; - } - if (state->IF_LO == 5380000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); - Fref = 322800000UL ; - } - if (state->IF_LO == 5200000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 374400000UL ; - } - if (state->IF_LO == 4900000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 352800000UL ; - } - if (state->IF_LO == 4400000UL) { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 352000000UL ; - } - if (state->IF_LO == 4063000UL) /* add for 2.6.8 */ { - status += MXL_ControlWrite(fe, IF_DIVVAL, 0x05); - status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); - Fref = 365670000UL ; - } - } - /* CHCAL_INT_MOD_IF */ - /* CHCAL_FRAC_MOD_IF */ - intModVal = Fref / (state->Fxtal * Kdbl/2); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_IF, intModVal); - - fracModVal = (2<<15)*(Fref/1000 - (state->Fxtal/1000 * Kdbl/2) * - intModVal); - - fracModVal = fracModVal / ((state->Fxtal * Kdbl/2)/1000); - status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_IF, fracModVal); - - return status ; -} - -static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq) -{ - struct mxl5005s_state *state = fe->tuner_priv; - u16 status = 0; - u32 divider_val, E3, E4, E5, E5A; - u32 Fmax, Fmin, FmaxBin, FminBin; - u32 Kdbl_RF = 2; - u32 tg_divval; - u32 tg_lo; - - u32 Fref_TG; - u32 Fvco; - - state->RF_IN = RF_Freq; - - MXL_SynthRFTGLO_Calc(fe); - - if (state->Fxtal >= 12000000UL && state->Fxtal <= 22000000UL) - Kdbl_RF = 2; - if (state->Fxtal > 22000000 && state->Fxtal <= 32000000) - Kdbl_RF = 1; - - /* Downconverter Controls - * Look-Up Table Implementation for: - * DN_POLY - * DN_RFGAIN - * DN_CAP_RFLPF - * DN_EN_VHFUHFBAR - * DN_GAIN_ADJUST - * Change the boundary reference from RF_IN to RF_LO - */ - if (state->RF_LO < 40000000UL) - return -1; - - if (state->RF_LO >= 40000000UL && state->RF_LO <= 75000000UL) { - status += MXL_ControlWrite(fe, DN_POLY, 2); - status += MXL_ControlWrite(fe, DN_RFGAIN, 3); - status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 423); - status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); - status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 1); - } - if (state->RF_LO > 75000000UL && state->RF_LO <= 100000000UL) { - status += MXL_ControlWrite(fe, DN_POLY, 3); - status += MXL_ControlWrite(fe, DN_RFGAIN, 3); - status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 222); - status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); - status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 1); - } - if (state->RF_LO > 100000000UL && state->RF_LO <= 150000000UL) { - status += MXL_ControlWrite(fe, DN_POLY, 3); - status += MXL_ControlWrite(fe, DN_RFGAIN, 3); - status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 147); - status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); - status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 2); - } - if (state->RF_LO > 150000000UL && state->RF_LO <= 200000000UL) { - status += MXL_ControlWrite(fe, DN_POLY, 3); - status += MXL_ControlWrite(fe, DN_RFGAIN, 3); - status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 9); - status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); - status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 2); - } - if (state->RF_LO > 200000000UL && state->RF_LO <= 300000000UL) { - status += MXL_ControlWrite(fe, DN_POLY, 3); - status += MXL_ControlWrite(fe, DN_RFGAIN, 3); - status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0); - status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); - status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3); - } - if (state->RF_LO > 300000000UL && state->RF_LO <= 650000000UL) { - status += MXL_ControlWrite(fe, DN_POLY, 3); - status += MXL_ControlWrite(fe, DN_RFGAIN, 1); - status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0); - status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 0); - status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3); - } - if (state->RF_LO > 650000000UL && state->RF_LO <= 900000000UL) { - status += MXL_ControlWrite(fe, DN_POLY, 3); - status += MXL_ControlWrite(fe, DN_RFGAIN, 2); - status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0); - status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 0); - status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3); - } - if (state->RF_LO > 900000000UL) - return -1; - - /* DN_IQTNBUF_AMP */ - /* DN_IQTNGNBFBIAS_BST */ - if (state->RF_LO >= 40000000UL && state->RF_LO <= 75000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); - } - if (state->RF_LO > 75000000UL && state->RF_LO <= 100000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); - } - if (state->RF_LO > 100000000UL && state->RF_LO <= 150000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); - } - if (state->RF_LO > 150000000UL && state->RF_LO <= 200000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); - } - if (state->RF_LO > 200000000UL && state->RF_LO <= 300000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); - } - if (state->RF_LO > 300000000UL && state->RF_LO <= 400000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); - } - if (state->RF_LO > 400000000UL && state->RF_LO <= 450000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); - } - if (state->RF_LO > 450000000UL && state->RF_LO <= 500000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); - } - if (state->RF_LO > 500000000UL && state->RF_LO <= 550000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); - } - if (state->RF_LO > 550000000UL && state->RF_LO <= 600000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); - } - if (state->RF_LO > 600000000UL && state->RF_LO <= 650000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); - } - if (state->RF_LO > 650000000UL && state->RF_LO <= 700000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); - } - if (state->RF_LO > 700000000UL && state->RF_LO <= 750000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); - } - if (state->RF_LO > 750000000UL && state->RF_LO <= 800000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); - } - if (state->RF_LO > 800000000UL && state->RF_LO <= 850000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 10); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 1); - } - if (state->RF_LO > 850000000UL && state->RF_LO <= 900000000UL) { - status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 10); - status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 1); - } - - /* - * Set RF Synth and LO Path Control - * - * Look-Up table implementation for: - * RFSYN_EN_OUTMUX - * RFSYN_SEL_VCO_OUT - * RFSYN_SEL_VCO_HI - * RFSYN_SEL_DIVM - * RFSYN_RF_DIV_BIAS - * DN_SEL_FREQ - * - * Set divider_val, Fmax, Fmix to use in Equations - */ - FminBin = 28000000UL ; - FmaxBin = 42500000UL ; - if (state->RF_LO >= 40000000UL && state->RF_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); - divider_val = 64 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 42500000UL ; - FmaxBin = 56000000UL ; - if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); - divider_val = 64 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 56000000UL ; - FmaxBin = 85000000UL ; - if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); - divider_val = 32 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 85000000UL ; - FmaxBin = 112000000UL ; - if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); - divider_val = 32 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 112000000UL ; - FmaxBin = 170000000UL ; - if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 2); - divider_val = 16 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 170000000UL ; - FmaxBin = 225000000UL ; - if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 2); - divider_val = 16 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 225000000UL ; - FmaxBin = 300000000UL ; - if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 4); - divider_val = 8 ; - Fmax = 340000000UL ; - Fmin = FminBin ; - } - FminBin = 300000000UL ; - FmaxBin = 340000000UL ; - if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); - divider_val = 8 ; - Fmax = FmaxBin ; - Fmin = 225000000UL ; - } - FminBin = 340000000UL ; - FmaxBin = 450000000UL ; - if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 2); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); - divider_val = 8 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 450000000UL ; - FmaxBin = 680000000UL ; - if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 1); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); - divider_val = 4 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 680000000UL ; - FmaxBin = 900000000UL ; - if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 1); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); - divider_val = 4 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - - /* CHCAL_INT_MOD_RF - * CHCAL_FRAC_MOD_RF - * RFSYN_LPF_R - * CHCAL_EN_INT_RF - */ - /* Equation E3 RFSYN_VCO_BIAS */ - E3 = (((Fmax-state->RF_LO)/1000)*32)/((Fmax-Fmin)/1000) + 8 ; - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, E3); - - /* Equation E4 CHCAL_INT_MOD_RF */ - E4 = (state->RF_LO*divider_val/1000)/(2*state->Fxtal*Kdbl_RF/1000); - MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, E4); - - /* Equation E5 CHCAL_FRAC_MOD_RF CHCAL_EN_INT_RF */ - E5 = ((2<<17)*(state->RF_LO/10000*divider_val - - (E4*(2*state->Fxtal*Kdbl_RF)/10000))) / - (2*state->Fxtal*Kdbl_RF/10000); - - status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_RF, E5); - - /* Equation E5A RFSYN_LPF_R */ - E5A = (((Fmax - state->RF_LO)/1000)*4/((Fmax-Fmin)/1000)) + 1 ; - status += MXL_ControlWrite(fe, RFSYN_LPF_R, E5A); - - /* Euqation E5B CHCAL_EN_INIT_RF */ - status += MXL_ControlWrite(fe, CHCAL_EN_INT_RF, ((E5 == 0) ? 1 : 0)); - /*if (E5 == 0) - * status += MXL_ControlWrite(fe, CHCAL_EN_INT_RF, 1); - *else - * status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_RF, E5); - */ - - /* - * Set TG Synth - * - * Look-Up table implementation for: - * TG_LO_DIVVAL - * TG_LO_SELVAL - * - * Set divider_val, Fmax, Fmix to use in Equations - */ - if (state->TG_LO < 33000000UL) - return -1; - - FminBin = 33000000UL ; - FmaxBin = 50000000UL ; - if (state->TG_LO >= FminBin && state->TG_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x6); - status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x0); - divider_val = 36 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 50000000UL ; - FmaxBin = 67000000UL ; - if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x1); - status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x0); - divider_val = 24 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 67000000UL ; - FmaxBin = 100000000UL ; - if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0xC); - status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2); - divider_val = 18 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 100000000UL ; - FmaxBin = 150000000UL ; - if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8); - status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2); - divider_val = 12 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 150000000UL ; - FmaxBin = 200000000UL ; - if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0); - status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2); - divider_val = 8 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 200000000UL ; - FmaxBin = 300000000UL ; - if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8); - status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x3); - divider_val = 6 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 300000000UL ; - FmaxBin = 400000000UL ; - if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0); - status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x3); - divider_val = 4 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 400000000UL ; - FmaxBin = 600000000UL ; - if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8); - status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x7); - divider_val = 3 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - FminBin = 600000000UL ; - FmaxBin = 900000000UL ; - if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { - status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0); - status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x7); - divider_val = 2 ; - Fmax = FmaxBin ; - Fmin = FminBin ; - } - - /* TG_DIV_VAL */ - tg_divval = (state->TG_LO*divider_val/100000) * - (MXL_Ceiling(state->Fxtal, 1000000) * 100) / - (state->Fxtal/1000); - - status += MXL_ControlWrite(fe, TG_DIV_VAL, tg_divval); - - if (state->TG_LO > 600000000UL) - status += MXL_ControlWrite(fe, TG_DIV_VAL, tg_divval + 1); - - Fmax = 1800000000UL ; - Fmin = 1200000000UL ; - - /* prevent overflow of 32 bit unsigned integer, use - * following equation. Edit for v2.6.4 - */ - /* Fref_TF = Fref_TG * 1000 */ - Fref_TG = (state->Fxtal/1000) / MXL_Ceiling(state->Fxtal, 1000000); - - /* Fvco = Fvco/10 */ - Fvco = (state->TG_LO/10000) * divider_val * Fref_TG; - - tg_lo = (((Fmax/10 - Fvco)/100)*32) / ((Fmax-Fmin)/1000)+8; - - /* below equation is same as above but much harder to debug. - * - * static u32 MXL_GetXtalInt(u32 Xtal_Freq) - * { - * if ((Xtal_Freq % 1000000) == 0) - * return (Xtal_Freq / 10000); - * else - * return (((Xtal_Freq / 1000000) + 1)*100); - * } - * - * u32 Xtal_Int = MXL_GetXtalInt(state->Fxtal); - * tg_lo = ( ((Fmax/10000 * Xtal_Int)/100) - - * ((state->TG_LO/10000)*divider_val * - * (state->Fxtal/10000)/100) )*32/((Fmax-Fmin)/10000 * - * Xtal_Int/100) + 8; - */ - - status += MXL_ControlWrite(fe, TG_VCO_BIAS , tg_lo); - - /* add for 2.6.5 Special setting for QAM */ - if (state->Mod_Type == MXL_QAM) { - if (state->config->qam_gain != 0) - status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, - state->config->qam_gain); - else if (state->RF_IN < 680000000) - status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); - else - status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2); - } - - /* Off Chip Tracking Filter Control */ - if (state->TF_Type == MXL_TF_OFF) { - /* Tracking Filter Off State; turn off all the banks */ - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 3, 1); /* Bank1 Off */ - status += MXL_SetGPIO(fe, 1, 1); /* Bank2 Off */ - status += MXL_SetGPIO(fe, 4, 1); /* Bank3 Off */ - } - - if (state->TF_Type == MXL_TF_C) /* Tracking Filter type C */ { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); - status += MXL_ControlWrite(fe, DAC_DIN_A, 0); - - if (state->RF_IN >= 43000000 && state->RF_IN < 150000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_ControlWrite(fe, DAC_DIN_B, 0); - status += MXL_SetGPIO(fe, 3, 0); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 4, 1); - } - if (state->RF_IN >= 150000000 && state->RF_IN < 280000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_ControlWrite(fe, DAC_DIN_B, 0); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 4, 1); - } - if (state->RF_IN >= 280000000 && state->RF_IN < 360000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_ControlWrite(fe, DAC_DIN_B, 0); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 4, 0); - } - if (state->RF_IN >= 360000000 && state->RF_IN < 560000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_ControlWrite(fe, DAC_DIN_B, 0); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 4, 0); - } - if (state->RF_IN >= 560000000 && state->RF_IN < 580000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); - status += MXL_ControlWrite(fe, DAC_DIN_B, 29); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 4, 0); - } - if (state->RF_IN >= 580000000 && state->RF_IN < 630000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); - status += MXL_ControlWrite(fe, DAC_DIN_B, 0); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 4, 0); - } - if (state->RF_IN >= 630000000 && state->RF_IN < 700000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); - status += MXL_ControlWrite(fe, DAC_DIN_B, 16); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 4, 1); - } - if (state->RF_IN >= 700000000 && state->RF_IN < 760000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); - status += MXL_ControlWrite(fe, DAC_DIN_B, 7); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 4, 1); - } - if (state->RF_IN >= 760000000 && state->RF_IN <= 900000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); - status += MXL_ControlWrite(fe, DAC_DIN_B, 0); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 4, 1); - } - } - - if (state->TF_Type == MXL_TF_C_H) { - - /* Tracking Filter type C-H for Hauppauge only */ - status += MXL_ControlWrite(fe, DAC_DIN_A, 0); - - if (state->RF_IN >= 43000000 && state->RF_IN < 150000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 1); - } - if (state->RF_IN >= 150000000 && state->RF_IN < 280000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 3, 0); - status += MXL_SetGPIO(fe, 1, 1); - } - if (state->RF_IN >= 280000000 && state->RF_IN < 360000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 3, 0); - status += MXL_SetGPIO(fe, 1, 0); - } - if (state->RF_IN >= 360000000 && state->RF_IN < 560000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 0); - } - if (state->RF_IN >= 560000000 && state->RF_IN < 580000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 0); - } - if (state->RF_IN >= 580000000 && state->RF_IN < 630000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 0); - } - if (state->RF_IN >= 630000000 && state->RF_IN < 700000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 1); - } - if (state->RF_IN >= 700000000 && state->RF_IN < 760000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 1); - } - if (state->RF_IN >= 760000000 && state->RF_IN <= 900000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 1); - } - } - - if (state->TF_Type == MXL_TF_D) { /* Tracking Filter type D */ - - status += MXL_ControlWrite(fe, DAC_DIN_B, 0); - - if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 250000000 && state->RF_IN < 310000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 310000000 && state->RF_IN < 360000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 360000000 && state->RF_IN < 470000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 640000000 && state->RF_IN <= 900000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - } - - if (state->TF_Type == MXL_TF_D_L) { - - /* Tracking Filter type D-L for Lumanate ONLY change 2.6.3 */ - status += MXL_ControlWrite(fe, DAC_DIN_A, 0); - - /* if UHF and terrestrial => Turn off Tracking Filter */ - if (state->RF_IN >= 471000000 && - (state->RF_IN - 471000000)%6000000 != 0) { - /* Turn off all the banks */ - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_ControlWrite(fe, AGC_IF, 10); - } else { - /* if VHF or cable => Turn on Tracking Filter */ - if (state->RF_IN >= 43000000 && - state->RF_IN < 140000000) { - - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 140000000 && - state->RF_IN < 240000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 240000000 && - state->RF_IN < 340000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 340000000 && - state->RF_IN < 430000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 430000000 && - state->RF_IN < 470000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 470000000 && - state->RF_IN < 570000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 570000000 && - state->RF_IN < 620000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 620000000 && - state->RF_IN < 760000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 760000000 && - state->RF_IN <= 900000000) { - status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - } - } - - if (state->TF_Type == MXL_TF_E) /* Tracking Filter type E */ { - - status += MXL_ControlWrite(fe, DAC_DIN_B, 0); - - if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 250000000 && state->RF_IN < 310000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 310000000 && state->RF_IN < 360000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 360000000 && state->RF_IN < 470000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 640000000 && state->RF_IN <= 900000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - } - - if (state->TF_Type == MXL_TF_F) { - - /* Tracking Filter type F */ - status += MXL_ControlWrite(fe, DAC_DIN_B, 0); - - if (state->RF_IN >= 43000000 && state->RF_IN < 160000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 160000000 && state->RF_IN < 210000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 210000000 && state->RF_IN < 300000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 300000000 && state->RF_IN < 390000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 390000000 && state->RF_IN < 515000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 515000000 && state->RF_IN < 650000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 650000000 && state->RF_IN <= 900000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - } - - if (state->TF_Type == MXL_TF_E_2) { - - /* Tracking Filter type E_2 */ - status += MXL_ControlWrite(fe, DAC_DIN_B, 0); - - if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 250000000 && state->RF_IN < 350000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 400000000 && state->RF_IN < 570000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 570000000 && state->RF_IN < 770000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 770000000 && state->RF_IN <= 900000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - } - - if (state->TF_Type == MXL_TF_G) { - - /* Tracking Filter type G add for v2.6.8 */ - status += MXL_ControlWrite(fe, DAC_DIN_B, 0); - - if (state->RF_IN >= 50000000 && state->RF_IN < 190000000) { - - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 190000000 && state->RF_IN < 280000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 280000000 && state->RF_IN < 350000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 400000000 && state->RF_IN < 470000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 640000000 && state->RF_IN < 820000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 820000000 && state->RF_IN <= 900000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - } - - if (state->TF_Type == MXL_TF_E_NA) { - - /* Tracking Filter type E-NA for Empia ONLY change for 2.6.8 */ - status += MXL_ControlWrite(fe, DAC_DIN_B, 0); - - /* if UHF and terrestrial=> Turn off Tracking Filter */ - if (state->RF_IN >= 471000000 && - (state->RF_IN - 471000000)%6000000 != 0) { - - /* Turn off all the banks */ - status += MXL_SetGPIO(fe, 3, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - - /* 2.6.12 Turn on RSSI */ - status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); - status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); - status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); - status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); - - /* RSSI reference point */ - status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); - status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); - status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2); - - /* following parameter is from analog OTA mode, - * can be change to seek better performance */ - status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); - } else { - /* if VHF or Cable => Turn on Tracking Filter */ - - /* 2.6.12 Turn off RSSI */ - status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); - - /* change back from above condition */ - status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 5); - - - if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { - - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 0); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 250000000 && state->RF_IN < 350000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 1); - } - if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 0); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 400000000 && state->RF_IN < 570000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 570000000 && state->RF_IN < 770000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 0); - } - if (state->RF_IN >= 770000000 && state->RF_IN <= 900000000) { - status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); - status += MXL_SetGPIO(fe, 4, 1); - status += MXL_SetGPIO(fe, 1, 1); - status += MXL_SetGPIO(fe, 3, 1); - } - } - } - return status ; -} - -static u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val) -{ - u16 status = 0; - - if (GPIO_Num == 1) - status += MXL_ControlWrite(fe, GPIO_1B, GPIO_Val ? 0 : 1); - - /* GPIO2 is not available */ - - if (GPIO_Num == 3) { - if (GPIO_Val == 1) { - status += MXL_ControlWrite(fe, GPIO_3, 0); - status += MXL_ControlWrite(fe, GPIO_3B, 0); - } - if (GPIO_Val == 0) { - status += MXL_ControlWrite(fe, GPIO_3, 1); - status += MXL_ControlWrite(fe, GPIO_3B, 1); - } - if (GPIO_Val == 3) { /* tri-state */ - status += MXL_ControlWrite(fe, GPIO_3, 0); - status += MXL_ControlWrite(fe, GPIO_3B, 1); - } - } - if (GPIO_Num == 4) { - if (GPIO_Val == 1) { - status += MXL_ControlWrite(fe, GPIO_4, 0); - status += MXL_ControlWrite(fe, GPIO_4B, 0); - } - if (GPIO_Val == 0) { - status += MXL_ControlWrite(fe, GPIO_4, 1); - status += MXL_ControlWrite(fe, GPIO_4B, 1); - } - if (GPIO_Val == 3) { /* tri-state */ - status += MXL_ControlWrite(fe, GPIO_4, 0); - status += MXL_ControlWrite(fe, GPIO_4B, 1); - } - } - - return status; -} - -static u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value) -{ - u16 status = 0; - - /* Will write ALL Matching Control Name */ - /* Write Matching INIT Control */ - status += MXL_ControlWrite_Group(fe, ControlNum, value, 1); - /* Write Matching CH Control */ - status += MXL_ControlWrite_Group(fe, ControlNum, value, 2); -#ifdef _MXL_INTERNAL - /* Write Matching MXL Control */ - status += MXL_ControlWrite_Group(fe, ControlNum, value, 3); -#endif - return status; -} - -static u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum, - u32 value, u16 controlGroup) -{ - struct mxl5005s_state *state = fe->tuner_priv; - u16 i, j, k; - u32 highLimit; - u32 ctrlVal; - - if (controlGroup == 1) /* Initial Control */ { - - for (i = 0; i < state->Init_Ctrl_Num; i++) { - - if (controlNum == state->Init_Ctrl[i].Ctrl_Num) { - - highLimit = 1 << state->Init_Ctrl[i].size; - if (value < highLimit) { - for (j = 0; j < state->Init_Ctrl[i].size; j++) { - state->Init_Ctrl[i].val[j] = (u8)((value >> j) & 0x01); - MXL_RegWriteBit(fe, (u8)(state->Init_Ctrl[i].addr[j]), - (u8)(state->Init_Ctrl[i].bit[j]), - (u8)((value>>j) & 0x01)); - } - ctrlVal = 0; - for (k = 0; k < state->Init_Ctrl[i].size; k++) - ctrlVal += state->Init_Ctrl[i].val[k] * (1 << k); - } else - return -1; - } - } - } - if (controlGroup == 2) /* Chan change Control */ { - - for (i = 0; i < state->CH_Ctrl_Num; i++) { - - if (controlNum == state->CH_Ctrl[i].Ctrl_Num) { - - highLimit = 1 << state->CH_Ctrl[i].size; - if (value < highLimit) { - for (j = 0; j < state->CH_Ctrl[i].size; j++) { - state->CH_Ctrl[i].val[j] = (u8)((value >> j) & 0x01); - MXL_RegWriteBit(fe, (u8)(state->CH_Ctrl[i].addr[j]), - (u8)(state->CH_Ctrl[i].bit[j]), - (u8)((value>>j) & 0x01)); - } - ctrlVal = 0; - for (k = 0; k < state->CH_Ctrl[i].size; k++) - ctrlVal += state->CH_Ctrl[i].val[k] * (1 << k); - } else - return -1; - } - } - } -#ifdef _MXL_INTERNAL - if (controlGroup == 3) /* Maxlinear Control */ { - - for (i = 0; i < state->MXL_Ctrl_Num; i++) { - - if (controlNum == state->MXL_Ctrl[i].Ctrl_Num) { - - highLimit = (1 << state->MXL_Ctrl[i].size); - if (value < highLimit) { - for (j = 0; j < state->MXL_Ctrl[i].size; j++) { - state->MXL_Ctrl[i].val[j] = (u8)((value >> j) & 0x01); - MXL_RegWriteBit(fe, (u8)(state->MXL_Ctrl[i].addr[j]), - (u8)(state->MXL_Ctrl[i].bit[j]), - (u8)((value>>j) & 0x01)); - } - ctrlVal = 0; - for (k = 0; k < state->MXL_Ctrl[i].size; k++) - ctrlVal += state-> - MXL_Ctrl[i].val[k] * - (1 << k); - } else - return -1; - } - } - } -#endif - return 0 ; /* successful return */ -} - -static u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal) -{ - struct mxl5005s_state *state = fe->tuner_priv; - int i ; - - for (i = 0; i < 104; i++) { - if (RegNum == state->TunerRegs[i].Reg_Num) { - *RegVal = (u8)(state->TunerRegs[i].Reg_Val); - return 0; - } - } - - return 1; -} - -static u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value) -{ - struct mxl5005s_state *state = fe->tuner_priv; - u32 ctrlVal ; - u16 i, k ; - - for (i = 0; i < state->Init_Ctrl_Num ; i++) { - - if (controlNum == state->Init_Ctrl[i].Ctrl_Num) { - - ctrlVal = 0; - for (k = 0; k < state->Init_Ctrl[i].size; k++) - ctrlVal += state->Init_Ctrl[i].val[k] * (1<CH_Ctrl_Num ; i++) { - - if (controlNum == state->CH_Ctrl[i].Ctrl_Num) { - - ctrlVal = 0; - for (k = 0; k < state->CH_Ctrl[i].size; k++) - ctrlVal += state->CH_Ctrl[i].val[k] * (1 << k); - *value = ctrlVal; - return 0; - - } - } - -#ifdef _MXL_INTERNAL - for (i = 0; i < state->MXL_Ctrl_Num ; i++) { - - if (controlNum == state->MXL_Ctrl[i].Ctrl_Num) { - - ctrlVal = 0; - for (k = 0; k < state->MXL_Ctrl[i].size; k++) - ctrlVal += state->MXL_Ctrl[i].val[k] * (1<tuner_priv; - int i ; - - const u8 AND_MAP[8] = { - 0xFE, 0xFD, 0xFB, 0xF7, - 0xEF, 0xDF, 0xBF, 0x7F } ; - - const u8 OR_MAP[8] = { - 0x01, 0x02, 0x04, 0x08, - 0x10, 0x20, 0x40, 0x80 } ; - - for (i = 0; i < state->TunerRegs_Num; i++) { - if (state->TunerRegs[i].Reg_Num == address) { - if (bitVal) - state->TunerRegs[i].Reg_Val |= OR_MAP[bit]; - else - state->TunerRegs[i].Reg_Val &= AND_MAP[bit]; - break ; - } - } -} - -static u32 MXL_Ceiling(u32 value, u32 resolution) -{ - return value / resolution + (value % resolution > 0 ? 1 : 0); -} - -/* Retrieve the Initialzation Registers */ -static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum, - u8 *RegVal, int *count) -{ - u16 status = 0; - int i ; - - u8 RegAddr[] = { - 11, 12, 13, 22, 32, 43, 44, 53, 56, 59, 73, - 76, 77, 91, 134, 135, 137, 147, - 156, 166, 167, 168, 25 }; - - *count = ARRAY_SIZE(RegAddr); - - status += MXL_BlockInit(fe); - - for (i = 0 ; i < *count; i++) { - RegNum[i] = RegAddr[i]; - status += MXL_RegRead(fe, RegNum[i], &RegVal[i]); - } - - return status; -} - -static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum, u8 *RegVal, - int *count) -{ - u16 status = 0; - int i ; - -/* add 77, 166, 167, 168 register for 2.6.12 */ -#ifdef _MXL_PRODUCTION - u8 RegAddr[] = {14, 15, 16, 17, 22, 43, 65, 68, 69, 70, 73, 92, 93, 106, - 107, 108, 109, 110, 111, 112, 136, 138, 149, 77, 166, 167, 168 } ; -#else - u8 RegAddr[] = {14, 15, 16, 17, 22, 43, 68, 69, 70, 73, 92, 93, 106, - 107, 108, 109, 110, 111, 112, 136, 138, 149, 77, 166, 167, 168 } ; - /* - u8 RegAddr[171]; - for (i = 0; i <= 170; i++) - RegAddr[i] = i; - */ -#endif - - *count = ARRAY_SIZE(RegAddr); - - for (i = 0 ; i < *count; i++) { - RegNum[i] = RegAddr[i]; - status += MXL_RegRead(fe, RegNum[i], &RegVal[i]); - } - - return status; -} - -static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum, - u8 *RegVal, int *count) -{ - u16 status = 0; - int i; - - u8 RegAddr[] = {43, 136}; - - *count = ARRAY_SIZE(RegAddr); - - for (i = 0; i < *count; i++) { - RegNum[i] = RegAddr[i]; - status += MXL_RegRead(fe, RegNum[i], &RegVal[i]); - } - - return status; -} - -static u16 MXL_GetMasterControl(u8 *MasterReg, int state) -{ - if (state == 1) /* Load_Start */ - *MasterReg = 0xF3; - if (state == 2) /* Power_Down */ - *MasterReg = 0x41; - if (state == 3) /* Synth_Reset */ - *MasterReg = 0xB1; - if (state == 4) /* Seq_Off */ - *MasterReg = 0xF1; - - return 0; -} - -#ifdef _MXL_PRODUCTION -static u16 MXL_VCORange_Test(struct dvb_frontend *fe, int VCO_Range) -{ - struct mxl5005s_state *state = fe->tuner_priv; - u16 status = 0 ; - - if (VCO_Range == 1) { - status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); - status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); - if (state->Mode == 0 && state->IF_Mode == 1) { - /* Analog Low IF Mode */ - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56); - status += MXL_ControlWrite(fe, - CHCAL_FRAC_MOD_RF, 180224); - } - if (state->Mode == 0 && state->IF_Mode == 0) { - /* Analog Zero IF Mode */ - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56); - status += MXL_ControlWrite(fe, - CHCAL_FRAC_MOD_RF, 222822); - } - if (state->Mode == 1) /* Digital Mode */ { - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56); - status += MXL_ControlWrite(fe, - CHCAL_FRAC_MOD_RF, 229376); - } - } - - if (VCO_Range == 2) { - status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); - status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 41); - if (state->Mode == 0 && state->IF_Mode == 1) { - /* Analog Low IF Mode */ - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); - status += MXL_ControlWrite(fe, - CHCAL_FRAC_MOD_RF, 206438); - } - if (state->Mode == 0 && state->IF_Mode == 0) { - /* Analog Zero IF Mode */ - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); - status += MXL_ControlWrite(fe, - CHCAL_FRAC_MOD_RF, 206438); - } - if (state->Mode == 1) /* Digital Mode */ { - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 41); - status += MXL_ControlWrite(fe, - CHCAL_FRAC_MOD_RF, 16384); - } - } - - if (VCO_Range == 3) { - status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); - status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); - if (state->Mode == 0 && state->IF_Mode == 1) { - /* Analog Low IF Mode */ - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 44); - status += MXL_ControlWrite(fe, - CHCAL_FRAC_MOD_RF, 173670); - } - if (state->Mode == 0 && state->IF_Mode == 0) { - /* Analog Zero IF Mode */ - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 44); - status += MXL_ControlWrite(fe, - CHCAL_FRAC_MOD_RF, 173670); - } - if (state->Mode == 1) /* Digital Mode */ { - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); - status += MXL_ControlWrite(fe, - CHCAL_FRAC_MOD_RF, 245760); - } - } - - if (VCO_Range == 4) { - status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); - status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); - status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); - status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); - status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); - if (state->Mode == 0 && state->IF_Mode == 1) { - /* Analog Low IF Mode */ - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); - status += MXL_ControlWrite(fe, - CHCAL_FRAC_MOD_RF, 206438); - } - if (state->Mode == 0 && state->IF_Mode == 0) { - /* Analog Zero IF Mode */ - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); - status += MXL_ControlWrite(fe, - CHCAL_FRAC_MOD_RF, 206438); - } - if (state->Mode == 1) /* Digital Mode */ { - status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); - status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); - status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); - status += MXL_ControlWrite(fe, - CHCAL_FRAC_MOD_RF, 212992); - } - } - - return status; -} - -static u16 MXL_Hystersis_Test(struct dvb_frontend *fe, int Hystersis) -{ - struct mxl5005s_state *state = fe->tuner_priv; - u16 status = 0; - - if (Hystersis == 1) - status += MXL_ControlWrite(fe, DN_BYPASS_AGC_I2C, 1); - - return status; -} -#endif -/* End: Reference driver code found in the Realtek driver that - * is copyright MaxLinear */ - -/* ---------------------------------------------------------------- - * Begin: Everything after here is new code to adapt the - * proprietary Realtek driver into a Linux API tuner. - * Copyright (C) 2008 Steven Toth - */ -static int mxl5005s_reset(struct dvb_frontend *fe) -{ - struct mxl5005s_state *state = fe->tuner_priv; - int ret = 0; - - u8 buf[2] = { 0xff, 0x00 }; - struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0, - .buf = buf, .len = 2 }; - - dprintk(2, "%s()\n", __func__); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - if (i2c_transfer(state->i2c, &msg, 1) != 1) { - printk(KERN_WARNING "mxl5005s I2C reset failed\n"); - ret = -EREMOTEIO; - } - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return ret; -} - -/* Write a single byte to a single reg, latch the value if required by - * following the transaction with the latch byte. - */ -static int mxl5005s_writereg(struct dvb_frontend *fe, u8 reg, u8 val, int latch) -{ - struct mxl5005s_state *state = fe->tuner_priv; - u8 buf[3] = { reg, val, MXL5005S_LATCH_BYTE }; - struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0, - .buf = buf, .len = 3 }; - - if (latch == 0) - msg.len = 2; - - dprintk(2, "%s(0x%x, 0x%x, 0x%x)\n", __func__, reg, val, msg.addr); - - if (i2c_transfer(state->i2c, &msg, 1) != 1) { - printk(KERN_WARNING "mxl5005s I2C write failed\n"); - return -EREMOTEIO; - } - return 0; -} - -static int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable, - u8 *datatable, u8 len) -{ - int ret = 0, i; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - for (i = 0 ; i < len-1; i++) { - ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 0); - if (ret < 0) - break; - } - - ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 1); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return ret; -} - -static int mxl5005s_init(struct dvb_frontend *fe) -{ - struct mxl5005s_state *state = fe->tuner_priv; - - dprintk(1, "%s()\n", __func__); - state->current_mode = MXL_QAM; - return mxl5005s_reconfigure(fe, MXL_QAM, MXL5005S_BANDWIDTH_6MHZ); -} - -static int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type, - u32 bandwidth) -{ - struct mxl5005s_state *state = fe->tuner_priv; - - u8 AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; - u8 ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; - int TableLen; - - dprintk(1, "%s(type=%d, bw=%d)\n", __func__, mod_type, bandwidth); - - mxl5005s_reset(fe); - - /* Tuner initialization stage 0 */ - MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET); - AddrTable[0] = MASTER_CONTROL_ADDR; - ByteTable[0] |= state->config->AgcMasterByte; - - mxl5005s_writeregs(fe, AddrTable, ByteTable, 1); - - mxl5005s_AssignTunerMode(fe, mod_type, bandwidth); - - /* Tuner initialization stage 1 */ - MXL_GetInitRegister(fe, AddrTable, ByteTable, &TableLen); - - mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen); - - return 0; -} - -static int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type, - u32 bandwidth) -{ - struct mxl5005s_state *state = fe->tuner_priv; - struct mxl5005s_config *c = state->config; - - InitTunerControls(fe); - - /* Set MxL5005S parameters. */ - MXL5005_TunerConfig( - fe, - c->mod_mode, - c->if_mode, - bandwidth, - c->if_freq, - c->xtal_freq, - c->agc_mode, - c->top, - c->output_load, - c->clock_out, - c->div_out, - c->cap_select, - c->rssi_enable, - mod_type, - c->tracking_filter); - - return 0; -} - -static int mxl5005s_set_params(struct dvb_frontend *fe) -{ - struct mxl5005s_state *state = fe->tuner_priv; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - u32 delsys = c->delivery_system; - u32 bw = c->bandwidth_hz; - u32 req_mode, req_bw = 0; - int ret; - - dprintk(1, "%s()\n", __func__); - - switch (delsys) { - case SYS_ATSC: - req_mode = MXL_ATSC; - req_bw = MXL5005S_BANDWIDTH_6MHZ; - break; - case SYS_DVBC_ANNEX_B: - req_mode = MXL_QAM; - req_bw = MXL5005S_BANDWIDTH_6MHZ; - break; - default: /* Assume DVB-T */ - req_mode = MXL_DVBT; - switch (bw) { - case 6000000: - req_bw = MXL5005S_BANDWIDTH_6MHZ; - break; - case 7000000: - req_bw = MXL5005S_BANDWIDTH_7MHZ; - break; - case 8000000: - case 0: - req_bw = MXL5005S_BANDWIDTH_8MHZ; - break; - default: - return -EINVAL; - } - } - - /* Change tuner for new modulation type if reqd */ - if (req_mode != state->current_mode || - req_bw != state->Chan_Bandwidth) { - state->current_mode = req_mode; - ret = mxl5005s_reconfigure(fe, req_mode, req_bw); - - } else - ret = 0; - - if (ret == 0) { - dprintk(1, "%s() freq=%d\n", __func__, c->frequency); - ret = mxl5005s_SetRfFreqHz(fe, c->frequency); - } - - return ret; -} - -static int mxl5005s_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct mxl5005s_state *state = fe->tuner_priv; - dprintk(1, "%s()\n", __func__); - - *frequency = state->RF_IN; - - return 0; -} - -static int mxl5005s_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) -{ - struct mxl5005s_state *state = fe->tuner_priv; - dprintk(1, "%s()\n", __func__); - - *bandwidth = state->Chan_Bandwidth; - - return 0; -} - -static int mxl5005s_release(struct dvb_frontend *fe) -{ - dprintk(1, "%s()\n", __func__); - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - return 0; -} - -static const struct dvb_tuner_ops mxl5005s_tuner_ops = { - .info = { - .name = "MaxLinear MXL5005S", - .frequency_min = 48000000, - .frequency_max = 860000000, - .frequency_step = 50000, - }, - - .release = mxl5005s_release, - .init = mxl5005s_init, - - .set_params = mxl5005s_set_params, - .get_frequency = mxl5005s_get_frequency, - .get_bandwidth = mxl5005s_get_bandwidth, -}; - -struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct mxl5005s_config *config) -{ - struct mxl5005s_state *state = NULL; - dprintk(1, "%s()\n", __func__); - - state = kzalloc(sizeof(struct mxl5005s_state), GFP_KERNEL); - if (state == NULL) - return NULL; - - state->frontend = fe; - state->config = config; - state->i2c = i2c; - - printk(KERN_INFO "MXL5005S: Attached at address 0x%02x\n", - config->i2c_address); - - memcpy(&fe->ops.tuner_ops, &mxl5005s_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - fe->tuner_priv = state; - return fe; -} -EXPORT_SYMBOL(mxl5005s_attach); - -MODULE_DESCRIPTION("MaxLinear MXL5005S silicon tuner driver"); -MODULE_AUTHOR("Steven Toth"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/tuners/mxl5005s.h b/drivers/media/common/tuners/mxl5005s.h deleted file mode 100644 index fc8a1ffc53b4..000000000000 --- a/drivers/media/common/tuners/mxl5005s.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - MaxLinear MXL5005S VSB/QAM/DVBT tuner driver - - Copyright (C) 2008 MaxLinear - Copyright (C) 2008 Steven Toth - - 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 __MXL5005S_H -#define __MXL5005S_H - -#include -#include "dvb_frontend.h" - -struct mxl5005s_config { - - /* 7 bit i2c address */ - u8 i2c_address; - -#define IF_FREQ_4570000HZ 4570000 -#define IF_FREQ_4571429HZ 4571429 -#define IF_FREQ_5380000HZ 5380000 -#define IF_FREQ_36000000HZ 36000000 -#define IF_FREQ_36125000HZ 36125000 -#define IF_FREQ_36166667HZ 36166667 -#define IF_FREQ_44000000HZ 44000000 - u32 if_freq; - -#define CRYSTAL_FREQ_4000000HZ 4000000 -#define CRYSTAL_FREQ_16000000HZ 16000000 -#define CRYSTAL_FREQ_25000000HZ 25000000 -#define CRYSTAL_FREQ_28800000HZ 28800000 - u32 xtal_freq; - -#define MXL_DUAL_AGC 0 -#define MXL_SINGLE_AGC 1 - u8 agc_mode; - -#define MXL_TF_DEFAULT 0 -#define MXL_TF_OFF 1 -#define MXL_TF_C 2 -#define MXL_TF_C_H 3 -#define MXL_TF_D 4 -#define MXL_TF_D_L 5 -#define MXL_TF_E 6 -#define MXL_TF_F 7 -#define MXL_TF_E_2 8 -#define MXL_TF_E_NA 9 -#define MXL_TF_G 10 - u8 tracking_filter; - -#define MXL_RSSI_DISABLE 0 -#define MXL_RSSI_ENABLE 1 - u8 rssi_enable; - -#define MXL_CAP_SEL_DISABLE 0 -#define MXL_CAP_SEL_ENABLE 1 - u8 cap_select; - -#define MXL_DIV_OUT_1 0 -#define MXL_DIV_OUT_4 1 - u8 div_out; - -#define MXL_CLOCK_OUT_DISABLE 0 -#define MXL_CLOCK_OUT_ENABLE 1 - u8 clock_out; - -#define MXL5005S_IF_OUTPUT_LOAD_200_OHM 200 -#define MXL5005S_IF_OUTPUT_LOAD_300_OHM 300 - u32 output_load; - -#define MXL5005S_TOP_5P5 55 -#define MXL5005S_TOP_7P2 72 -#define MXL5005S_TOP_9P2 92 -#define MXL5005S_TOP_11P0 110 -#define MXL5005S_TOP_12P9 129 -#define MXL5005S_TOP_14P7 147 -#define MXL5005S_TOP_16P8 168 -#define MXL5005S_TOP_19P4 194 -#define MXL5005S_TOP_21P2 212 -#define MXL5005S_TOP_23P2 232 -#define MXL5005S_TOP_25P2 252 -#define MXL5005S_TOP_27P1 271 -#define MXL5005S_TOP_29P2 292 -#define MXL5005S_TOP_31P7 317 -#define MXL5005S_TOP_34P9 349 - u32 top; - -#define MXL_ANALOG_MODE 0 -#define MXL_DIGITAL_MODE 1 - u8 mod_mode; - -#define MXL_ZERO_IF 0 -#define MXL_LOW_IF 1 - u8 if_mode; - - /* Some boards need to override the built-in logic for determining - the gain when in QAM mode (the HVR-1600 is one such case) */ - u8 qam_gain; - - /* Stuff I don't know what to do with */ - u8 AgcMasterByte; -}; - -#if defined(CONFIG_MEDIA_TUNER_MXL5005S) || \ - (defined(CONFIG_MEDIA_TUNER_MXL5005S_MODULE) && defined(MODULE)) -extern struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct mxl5005s_config *config); -#else -static inline struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct mxl5005s_config *config) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif /* CONFIG_DVB_TUNER_MXL5005S */ - -#endif /* __MXL5005S_H */ - diff --git a/drivers/media/common/tuners/mxl5007t.c b/drivers/media/common/tuners/mxl5007t.c deleted file mode 100644 index 69e453ef0a1a..000000000000 --- a/drivers/media/common/tuners/mxl5007t.c +++ /dev/null @@ -1,928 +0,0 @@ -/* - * mxl5007t.c - driver for the MaxLinear MxL5007T silicon tuner - * - * Copyright (C) 2008, 2009 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. - */ - -#include -#include -#include -#include "tuner-i2c.h" -#include "mxl5007t.h" - -static DEFINE_MUTEX(mxl5007t_list_mutex); -static LIST_HEAD(hybrid_tuner_instance_list); - -static int mxl5007t_debug; -module_param_named(debug, mxl5007t_debug, int, 0644); -MODULE_PARM_DESC(debug, "set debug level"); - -/* ------------------------------------------------------------------------- */ - -#define mxl_printk(kern, fmt, arg...) \ - printk(kern "%s: " fmt "\n", __func__, ##arg) - -#define mxl_err(fmt, arg...) \ - mxl_printk(KERN_ERR, "%d: " fmt, __LINE__, ##arg) - -#define mxl_warn(fmt, arg...) \ - mxl_printk(KERN_WARNING, fmt, ##arg) - -#define mxl_info(fmt, arg...) \ - mxl_printk(KERN_INFO, fmt, ##arg) - -#define mxl_debug(fmt, arg...) \ -({ \ - if (mxl5007t_debug) \ - mxl_printk(KERN_DEBUG, fmt, ##arg); \ -}) - -#define mxl_fail(ret) \ -({ \ - int __ret; \ - __ret = (ret < 0); \ - if (__ret) \ - mxl_printk(KERN_ERR, "error %d on line %d", \ - ret, __LINE__); \ - __ret; \ -}) - -/* ------------------------------------------------------------------------- */ - -#define MHz 1000000 - -enum mxl5007t_mode { - MxL_MODE_ISDBT = 0, - MxL_MODE_DVBT = 1, - MxL_MODE_ATSC = 2, - MxL_MODE_CABLE = 0x10, -}; - -enum mxl5007t_chip_version { - MxL_UNKNOWN_ID = 0x00, - MxL_5007_V1_F1 = 0x11, - MxL_5007_V1_F2 = 0x12, - MxL_5007_V4 = 0x14, - MxL_5007_V2_100_F1 = 0x21, - MxL_5007_V2_100_F2 = 0x22, - MxL_5007_V2_200_F1 = 0x23, - MxL_5007_V2_200_F2 = 0x24, -}; - -struct reg_pair_t { - u8 reg; - u8 val; -}; - -/* ------------------------------------------------------------------------- */ - -static struct reg_pair_t init_tab[] = { - { 0x02, 0x06 }, - { 0x03, 0x48 }, - { 0x05, 0x04 }, - { 0x06, 0x10 }, - { 0x2e, 0x15 }, /* OVERRIDE */ - { 0x30, 0x10 }, /* OVERRIDE */ - { 0x45, 0x58 }, /* OVERRIDE */ - { 0x48, 0x19 }, /* OVERRIDE */ - { 0x52, 0x03 }, /* OVERRIDE */ - { 0x53, 0x44 }, /* OVERRIDE */ - { 0x6a, 0x4b }, /* OVERRIDE */ - { 0x76, 0x00 }, /* OVERRIDE */ - { 0x78, 0x18 }, /* OVERRIDE */ - { 0x7a, 0x17 }, /* OVERRIDE */ - { 0x85, 0x06 }, /* OVERRIDE */ - { 0x01, 0x01 }, /* TOP_MASTER_ENABLE */ - { 0, 0 } -}; - -static struct reg_pair_t init_tab_cable[] = { - { 0x02, 0x06 }, - { 0x03, 0x48 }, - { 0x05, 0x04 }, - { 0x06, 0x10 }, - { 0x09, 0x3f }, - { 0x0a, 0x3f }, - { 0x0b, 0x3f }, - { 0x2e, 0x15 }, /* OVERRIDE */ - { 0x30, 0x10 }, /* OVERRIDE */ - { 0x45, 0x58 }, /* OVERRIDE */ - { 0x48, 0x19 }, /* OVERRIDE */ - { 0x52, 0x03 }, /* OVERRIDE */ - { 0x53, 0x44 }, /* OVERRIDE */ - { 0x6a, 0x4b }, /* OVERRIDE */ - { 0x76, 0x00 }, /* OVERRIDE */ - { 0x78, 0x18 }, /* OVERRIDE */ - { 0x7a, 0x17 }, /* OVERRIDE */ - { 0x85, 0x06 }, /* OVERRIDE */ - { 0x01, 0x01 }, /* TOP_MASTER_ENABLE */ - { 0, 0 } -}; - -/* ------------------------------------------------------------------------- */ - -static struct reg_pair_t reg_pair_rftune[] = { - { 0x0f, 0x00 }, /* abort tune */ - { 0x0c, 0x15 }, - { 0x0d, 0x40 }, - { 0x0e, 0x0e }, - { 0x1f, 0x87 }, /* OVERRIDE */ - { 0x20, 0x1f }, /* OVERRIDE */ - { 0x21, 0x87 }, /* OVERRIDE */ - { 0x22, 0x1f }, /* OVERRIDE */ - { 0x80, 0x01 }, /* freq dependent */ - { 0x0f, 0x01 }, /* start tune */ - { 0, 0 } -}; - -/* ------------------------------------------------------------------------- */ - -struct mxl5007t_state { - struct list_head hybrid_tuner_instance_list; - struct tuner_i2c_props i2c_props; - - struct mutex lock; - - struct mxl5007t_config *config; - - enum mxl5007t_chip_version chip_id; - - struct reg_pair_t tab_init[ARRAY_SIZE(init_tab)]; - struct reg_pair_t tab_init_cable[ARRAY_SIZE(init_tab_cable)]; - struct reg_pair_t tab_rftune[ARRAY_SIZE(reg_pair_rftune)]; - - enum mxl5007t_if_freq if_freq; - - u32 frequency; - u32 bandwidth; -}; - -/* ------------------------------------------------------------------------- */ - -/* called by _init and _rftun to manipulate the register arrays */ - -static void set_reg_bits(struct reg_pair_t *reg_pair, u8 reg, u8 mask, u8 val) -{ - unsigned int i = 0; - - while (reg_pair[i].reg || reg_pair[i].val) { - if (reg_pair[i].reg == reg) { - reg_pair[i].val &= ~mask; - reg_pair[i].val |= val; - } - i++; - - } - return; -} - -static void copy_reg_bits(struct reg_pair_t *reg_pair1, - struct reg_pair_t *reg_pair2) -{ - unsigned int i, j; - - i = j = 0; - - while (reg_pair1[i].reg || reg_pair1[i].val) { - while (reg_pair2[j].reg || reg_pair2[j].val) { - if (reg_pair1[i].reg != reg_pair2[j].reg) { - j++; - continue; - } - reg_pair2[j].val = reg_pair1[i].val; - break; - } - i++; - } - return; -} - -/* ------------------------------------------------------------------------- */ - -static void mxl5007t_set_mode_bits(struct mxl5007t_state *state, - enum mxl5007t_mode mode, - s32 if_diff_out_level) -{ - switch (mode) { - case MxL_MODE_ATSC: - set_reg_bits(state->tab_init, 0x06, 0x1f, 0x12); - break; - case MxL_MODE_DVBT: - set_reg_bits(state->tab_init, 0x06, 0x1f, 0x11); - break; - case MxL_MODE_ISDBT: - set_reg_bits(state->tab_init, 0x06, 0x1f, 0x10); - break; - case MxL_MODE_CABLE: - set_reg_bits(state->tab_init_cable, 0x09, 0xff, 0xc1); - set_reg_bits(state->tab_init_cable, 0x0a, 0xff, - 8 - if_diff_out_level); - set_reg_bits(state->tab_init_cable, 0x0b, 0xff, 0x17); - break; - default: - mxl_fail(-EINVAL); - } - return; -} - -static void mxl5007t_set_if_freq_bits(struct mxl5007t_state *state, - enum mxl5007t_if_freq if_freq, - int invert_if) -{ - u8 val; - - switch (if_freq) { - case MxL_IF_4_MHZ: - val = 0x00; - break; - case MxL_IF_4_5_MHZ: - val = 0x02; - break; - case MxL_IF_4_57_MHZ: - val = 0x03; - break; - case MxL_IF_5_MHZ: - val = 0x04; - break; - case MxL_IF_5_38_MHZ: - val = 0x05; - break; - case MxL_IF_6_MHZ: - val = 0x06; - break; - case MxL_IF_6_28_MHZ: - val = 0x07; - break; - case MxL_IF_9_1915_MHZ: - val = 0x08; - break; - case MxL_IF_35_25_MHZ: - val = 0x09; - break; - case MxL_IF_36_15_MHZ: - val = 0x0a; - break; - case MxL_IF_44_MHZ: - val = 0x0b; - break; - default: - mxl_fail(-EINVAL); - return; - } - set_reg_bits(state->tab_init, 0x02, 0x0f, val); - - /* set inverted IF or normal IF */ - set_reg_bits(state->tab_init, 0x02, 0x10, invert_if ? 0x10 : 0x00); - - state->if_freq = if_freq; - - return; -} - -static void mxl5007t_set_xtal_freq_bits(struct mxl5007t_state *state, - enum mxl5007t_xtal_freq xtal_freq) -{ - switch (xtal_freq) { - case MxL_XTAL_16_MHZ: - /* select xtal freq & ref freq */ - set_reg_bits(state->tab_init, 0x03, 0xf0, 0x00); - set_reg_bits(state->tab_init, 0x05, 0x0f, 0x00); - break; - case MxL_XTAL_20_MHZ: - set_reg_bits(state->tab_init, 0x03, 0xf0, 0x10); - set_reg_bits(state->tab_init, 0x05, 0x0f, 0x01); - break; - case MxL_XTAL_20_25_MHZ: - set_reg_bits(state->tab_init, 0x03, 0xf0, 0x20); - set_reg_bits(state->tab_init, 0x05, 0x0f, 0x02); - break; - case MxL_XTAL_20_48_MHZ: - set_reg_bits(state->tab_init, 0x03, 0xf0, 0x30); - set_reg_bits(state->tab_init, 0x05, 0x0f, 0x03); - break; - case MxL_XTAL_24_MHZ: - set_reg_bits(state->tab_init, 0x03, 0xf0, 0x40); - set_reg_bits(state->tab_init, 0x05, 0x0f, 0x04); - break; - case MxL_XTAL_25_MHZ: - set_reg_bits(state->tab_init, 0x03, 0xf0, 0x50); - set_reg_bits(state->tab_init, 0x05, 0x0f, 0x05); - break; - case MxL_XTAL_25_14_MHZ: - set_reg_bits(state->tab_init, 0x03, 0xf0, 0x60); - set_reg_bits(state->tab_init, 0x05, 0x0f, 0x06); - break; - case MxL_XTAL_27_MHZ: - set_reg_bits(state->tab_init, 0x03, 0xf0, 0x70); - set_reg_bits(state->tab_init, 0x05, 0x0f, 0x07); - break; - case MxL_XTAL_28_8_MHZ: - set_reg_bits(state->tab_init, 0x03, 0xf0, 0x80); - set_reg_bits(state->tab_init, 0x05, 0x0f, 0x08); - break; - case MxL_XTAL_32_MHZ: - set_reg_bits(state->tab_init, 0x03, 0xf0, 0x90); - set_reg_bits(state->tab_init, 0x05, 0x0f, 0x09); - break; - case MxL_XTAL_40_MHZ: - set_reg_bits(state->tab_init, 0x03, 0xf0, 0xa0); - set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0a); - break; - case MxL_XTAL_44_MHZ: - set_reg_bits(state->tab_init, 0x03, 0xf0, 0xb0); - set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0b); - break; - case MxL_XTAL_48_MHZ: - set_reg_bits(state->tab_init, 0x03, 0xf0, 0xc0); - set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0c); - break; - case MxL_XTAL_49_3811_MHZ: - set_reg_bits(state->tab_init, 0x03, 0xf0, 0xd0); - set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0d); - break; - default: - mxl_fail(-EINVAL); - return; - } - - return; -} - -static struct reg_pair_t *mxl5007t_calc_init_regs(struct mxl5007t_state *state, - enum mxl5007t_mode mode) -{ - struct mxl5007t_config *cfg = state->config; - - memcpy(&state->tab_init, &init_tab, sizeof(init_tab)); - memcpy(&state->tab_init_cable, &init_tab_cable, sizeof(init_tab_cable)); - - mxl5007t_set_mode_bits(state, mode, cfg->if_diff_out_level); - mxl5007t_set_if_freq_bits(state, cfg->if_freq_hz, cfg->invert_if); - mxl5007t_set_xtal_freq_bits(state, cfg->xtal_freq_hz); - - set_reg_bits(state->tab_init, 0x04, 0x01, cfg->loop_thru_enable); - set_reg_bits(state->tab_init, 0x03, 0x08, cfg->clk_out_enable << 3); - set_reg_bits(state->tab_init, 0x03, 0x07, cfg->clk_out_amp); - - if (mode >= MxL_MODE_CABLE) { - copy_reg_bits(state->tab_init, state->tab_init_cable); - return state->tab_init_cable; - } else - return state->tab_init; -} - -/* ------------------------------------------------------------------------- */ - -enum mxl5007t_bw_mhz { - MxL_BW_6MHz = 6, - MxL_BW_7MHz = 7, - MxL_BW_8MHz = 8, -}; - -static void mxl5007t_set_bw_bits(struct mxl5007t_state *state, - enum mxl5007t_bw_mhz bw) -{ - u8 val; - - switch (bw) { - case MxL_BW_6MHz: - val = 0x15; /* set DIG_MODEINDEX, DIG_MODEINDEX_A, - * and DIG_MODEINDEX_CSF */ - break; - case MxL_BW_7MHz: - val = 0x2a; - break; - case MxL_BW_8MHz: - val = 0x3f; - break; - default: - mxl_fail(-EINVAL); - return; - } - set_reg_bits(state->tab_rftune, 0x0c, 0x3f, val); - - return; -} - -static struct -reg_pair_t *mxl5007t_calc_rf_tune_regs(struct mxl5007t_state *state, - u32 rf_freq, enum mxl5007t_bw_mhz bw) -{ - u32 dig_rf_freq = 0; - u32 temp; - u32 frac_divider = 1000000; - unsigned int i; - - memcpy(&state->tab_rftune, ®_pair_rftune, sizeof(reg_pair_rftune)); - - mxl5007t_set_bw_bits(state, bw); - - /* Convert RF frequency into 16 bits => - * 10 bit integer (MHz) + 6 bit fraction */ - dig_rf_freq = rf_freq / MHz; - - temp = rf_freq % MHz; - - for (i = 0; i < 6; i++) { - dig_rf_freq <<= 1; - frac_divider /= 2; - if (temp > frac_divider) { - temp -= frac_divider; - dig_rf_freq++; - } - } - - /* add to have shift center point by 7.8124 kHz */ - if (temp > 7812) - dig_rf_freq++; - - set_reg_bits(state->tab_rftune, 0x0d, 0xff, (u8) dig_rf_freq); - set_reg_bits(state->tab_rftune, 0x0e, 0xff, (u8) (dig_rf_freq >> 8)); - - if (rf_freq >= 333000000) - set_reg_bits(state->tab_rftune, 0x80, 0x40, 0x40); - - return state->tab_rftune; -} - -/* ------------------------------------------------------------------------- */ - -static int mxl5007t_write_reg(struct mxl5007t_state *state, u8 reg, u8 val) -{ - u8 buf[] = { reg, val }; - struct i2c_msg msg = { .addr = state->i2c_props.addr, .flags = 0, - .buf = buf, .len = 2 }; - int ret; - - ret = i2c_transfer(state->i2c_props.adap, &msg, 1); - if (ret != 1) { - mxl_err("failed!"); - return -EREMOTEIO; - } - return 0; -} - -static int mxl5007t_write_regs(struct mxl5007t_state *state, - struct reg_pair_t *reg_pair) -{ - unsigned int i = 0; - int ret = 0; - - while ((ret == 0) && (reg_pair[i].reg || reg_pair[i].val)) { - ret = mxl5007t_write_reg(state, - reg_pair[i].reg, reg_pair[i].val); - i++; - } - return ret; -} - -static int mxl5007t_read_reg(struct mxl5007t_state *state, u8 reg, u8 *val) -{ - u8 buf[2] = { 0xfb, reg }; - struct i2c_msg msg[] = { - { .addr = state->i2c_props.addr, .flags = 0, - .buf = buf, .len = 2 }, - { .addr = state->i2c_props.addr, .flags = I2C_M_RD, - .buf = val, .len = 1 }, - }; - int ret; - - ret = i2c_transfer(state->i2c_props.adap, msg, 2); - if (ret != 2) { - mxl_err("failed!"); - return -EREMOTEIO; - } - return 0; -} - -static int mxl5007t_soft_reset(struct mxl5007t_state *state) -{ - u8 d = 0xff; - struct i2c_msg msg = { - .addr = state->i2c_props.addr, .flags = 0, - .buf = &d, .len = 1 - }; - int ret = i2c_transfer(state->i2c_props.adap, &msg, 1); - - if (ret != 1) { - mxl_err("failed!"); - return -EREMOTEIO; - } - return 0; -} - -static int mxl5007t_tuner_init(struct mxl5007t_state *state, - enum mxl5007t_mode mode) -{ - struct reg_pair_t *init_regs; - int ret; - - ret = mxl5007t_soft_reset(state); - if (mxl_fail(ret)) - goto fail; - - /* calculate initialization reg array */ - init_regs = mxl5007t_calc_init_regs(state, mode); - - ret = mxl5007t_write_regs(state, init_regs); - if (mxl_fail(ret)) - goto fail; - mdelay(1); -fail: - return ret; -} - -static int mxl5007t_tuner_rf_tune(struct mxl5007t_state *state, u32 rf_freq_hz, - enum mxl5007t_bw_mhz bw) -{ - struct reg_pair_t *rf_tune_regs; - int ret; - - /* calculate channel change reg array */ - rf_tune_regs = mxl5007t_calc_rf_tune_regs(state, rf_freq_hz, bw); - - ret = mxl5007t_write_regs(state, rf_tune_regs); - if (mxl_fail(ret)) - goto fail; - msleep(3); -fail: - return ret; -} - -/* ------------------------------------------------------------------------- */ - -static int mxl5007t_synth_lock_status(struct mxl5007t_state *state, - int *rf_locked, int *ref_locked) -{ - u8 d; - int ret; - - *rf_locked = 0; - *ref_locked = 0; - - ret = mxl5007t_read_reg(state, 0xd8, &d); - if (mxl_fail(ret)) - goto fail; - - if ((d & 0x0c) == 0x0c) - *rf_locked = 1; - - if ((d & 0x03) == 0x03) - *ref_locked = 1; -fail: - return ret; -} - -/* ------------------------------------------------------------------------- */ - -static int mxl5007t_get_status(struct dvb_frontend *fe, u32 *status) -{ - struct mxl5007t_state *state = fe->tuner_priv; - int rf_locked, ref_locked, ret; - - *status = 0; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - ret = mxl5007t_synth_lock_status(state, &rf_locked, &ref_locked); - if (mxl_fail(ret)) - goto fail; - mxl_debug("%s%s", rf_locked ? "rf locked " : "", - ref_locked ? "ref locked" : ""); - - if ((rf_locked) || (ref_locked)) - *status |= TUNER_STATUS_LOCKED; -fail: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return ret; -} - -/* ------------------------------------------------------------------------- */ - -static int mxl5007t_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - u32 delsys = c->delivery_system; - struct mxl5007t_state *state = fe->tuner_priv; - enum mxl5007t_bw_mhz bw; - enum mxl5007t_mode mode; - int ret; - u32 freq = c->frequency; - - switch (delsys) { - case SYS_ATSC: - mode = MxL_MODE_ATSC; - bw = MxL_BW_6MHz; - break; - case SYS_DVBC_ANNEX_B: - mode = MxL_MODE_CABLE; - bw = MxL_BW_6MHz; - break; - case SYS_DVBT: - case SYS_DVBT2: - mode = MxL_MODE_DVBT; - switch (c->bandwidth_hz) { - case 6000000: - bw = MxL_BW_6MHz; - break; - case 7000000: - bw = MxL_BW_7MHz; - break; - case 8000000: - bw = MxL_BW_8MHz; - break; - default: - return -EINVAL; - } - break; - default: - mxl_err("modulation type not supported!"); - return -EINVAL; - } - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - mutex_lock(&state->lock); - - ret = mxl5007t_tuner_init(state, mode); - if (mxl_fail(ret)) - goto fail; - - ret = mxl5007t_tuner_rf_tune(state, freq, bw); - if (mxl_fail(ret)) - goto fail; - - state->frequency = freq; - state->bandwidth = c->bandwidth_hz; -fail: - mutex_unlock(&state->lock); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return ret; -} - -/* ------------------------------------------------------------------------- */ - -static int mxl5007t_init(struct dvb_frontend *fe) -{ - struct mxl5007t_state *state = fe->tuner_priv; - int ret; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - /* wake from standby */ - ret = mxl5007t_write_reg(state, 0x01, 0x01); - mxl_fail(ret); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return ret; -} - -static int mxl5007t_sleep(struct dvb_frontend *fe) -{ - struct mxl5007t_state *state = fe->tuner_priv; - int ret; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - /* enter standby mode */ - ret = mxl5007t_write_reg(state, 0x01, 0x00); - mxl_fail(ret); - ret = mxl5007t_write_reg(state, 0x0f, 0x00); - mxl_fail(ret); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return ret; -} - -/* ------------------------------------------------------------------------- */ - -static int mxl5007t_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct mxl5007t_state *state = fe->tuner_priv; - *frequency = state->frequency; - return 0; -} - -static int mxl5007t_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) -{ - struct mxl5007t_state *state = fe->tuner_priv; - *bandwidth = state->bandwidth; - return 0; -} - -static int mxl5007t_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct mxl5007t_state *state = fe->tuner_priv; - - *frequency = 0; - - switch (state->if_freq) { - case MxL_IF_4_MHZ: - *frequency = 4000000; - break; - case MxL_IF_4_5_MHZ: - *frequency = 4500000; - break; - case MxL_IF_4_57_MHZ: - *frequency = 4570000; - break; - case MxL_IF_5_MHZ: - *frequency = 5000000; - break; - case MxL_IF_5_38_MHZ: - *frequency = 5380000; - break; - case MxL_IF_6_MHZ: - *frequency = 6000000; - break; - case MxL_IF_6_28_MHZ: - *frequency = 6280000; - break; - case MxL_IF_9_1915_MHZ: - *frequency = 9191500; - break; - case MxL_IF_35_25_MHZ: - *frequency = 35250000; - break; - case MxL_IF_36_15_MHZ: - *frequency = 36150000; - break; - case MxL_IF_44_MHZ: - *frequency = 44000000; - break; - } - return 0; -} - -static int mxl5007t_release(struct dvb_frontend *fe) -{ - struct mxl5007t_state *state = fe->tuner_priv; - - mutex_lock(&mxl5007t_list_mutex); - - if (state) - hybrid_tuner_release_state(state); - - mutex_unlock(&mxl5007t_list_mutex); - - fe->tuner_priv = NULL; - - return 0; -} - -/* ------------------------------------------------------------------------- */ - -static struct dvb_tuner_ops mxl5007t_tuner_ops = { - .info = { - .name = "MaxLinear MxL5007T", - }, - .init = mxl5007t_init, - .sleep = mxl5007t_sleep, - .set_params = mxl5007t_set_params, - .get_status = mxl5007t_get_status, - .get_frequency = mxl5007t_get_frequency, - .get_bandwidth = mxl5007t_get_bandwidth, - .release = mxl5007t_release, - .get_if_frequency = mxl5007t_get_if_frequency, -}; - -static int mxl5007t_get_chip_id(struct mxl5007t_state *state) -{ - char *name; - int ret; - u8 id; - - ret = mxl5007t_read_reg(state, 0xd9, &id); - if (mxl_fail(ret)) - goto fail; - - switch (id) { - case MxL_5007_V1_F1: - name = "MxL5007.v1.f1"; - break; - case MxL_5007_V1_F2: - name = "MxL5007.v1.f2"; - break; - case MxL_5007_V2_100_F1: - name = "MxL5007.v2.100.f1"; - break; - case MxL_5007_V2_100_F2: - name = "MxL5007.v2.100.f2"; - break; - case MxL_5007_V2_200_F1: - name = "MxL5007.v2.200.f1"; - break; - case MxL_5007_V2_200_F2: - name = "MxL5007.v2.200.f2"; - break; - case MxL_5007_V4: - name = "MxL5007T.v4"; - break; - default: - name = "MxL5007T"; - printk(KERN_WARNING "%s: unknown rev (%02x)\n", __func__, id); - id = MxL_UNKNOWN_ID; - } - state->chip_id = id; - mxl_info("%s detected @ %d-%04x", name, - i2c_adapter_id(state->i2c_props.adap), - state->i2c_props.addr); - return 0; -fail: - mxl_warn("unable to identify device @ %d-%04x", - i2c_adapter_id(state->i2c_props.adap), - state->i2c_props.addr); - - state->chip_id = MxL_UNKNOWN_ID; - return ret; -} - -struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, u8 addr, - struct mxl5007t_config *cfg) -{ - struct mxl5007t_state *state = NULL; - int instance, ret; - - mutex_lock(&mxl5007t_list_mutex); - instance = hybrid_tuner_request_state(struct mxl5007t_state, state, - hybrid_tuner_instance_list, - i2c, addr, "mxl5007t"); - switch (instance) { - case 0: - goto fail; - case 1: - /* new tuner instance */ - state->config = cfg; - - mutex_init(&state->lock); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - ret = mxl5007t_get_chip_id(state); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - /* check return value of mxl5007t_get_chip_id */ - if (mxl_fail(ret)) - goto fail; - break; - default: - /* existing tuner instance */ - break; - } - fe->tuner_priv = state; - mutex_unlock(&mxl5007t_list_mutex); - - memcpy(&fe->ops.tuner_ops, &mxl5007t_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - return fe; -fail: - mutex_unlock(&mxl5007t_list_mutex); - - mxl5007t_release(fe); - return NULL; -} -EXPORT_SYMBOL_GPL(mxl5007t_attach); -MODULE_DESCRIPTION("MaxLinear MxL5007T Silicon IC tuner driver"); -MODULE_AUTHOR("Michael Krufky "); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.2"); - -/* - * 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/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 - * - * 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.c b/drivers/media/common/tuners/qt1010.c deleted file mode 100644 index bdc39e11030e..000000000000 --- a/drivers/media/common/tuners/qt1010.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - * Driver for Quantek QT1010 silicon tuner - * - * Copyright (C) 2006 Antti Palosaari - * Aapo Tahkola - * - * 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 "qt1010.h" -#include "qt1010_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 "QT1010: " args); \ - } while (0) - -/* read single register */ -static int qt1010_readreg(struct qt1010_priv *priv, u8 reg, u8 *val) -{ - struct i2c_msg msg[2] = { - { .addr = priv->cfg->i2c_address, - .flags = 0, .buf = ®, .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 "qt1010 I2C read failed\n"); - return -EREMOTEIO; - } - return 0; -} - -/* write single register */ -static int qt1010_writereg(struct qt1010_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 "qt1010 I2C write failed\n"); - return -EREMOTEIO; - } - return 0; -} - -/* dump all registers */ -static void qt1010_dump_regs(struct qt1010_priv *priv) -{ - u8 reg, val; - - for (reg = 0; ; reg++) { - if (reg % 16 == 0) { - if (reg) - printk(KERN_CONT "\n"); - printk(KERN_DEBUG "%02x:", reg); - } - if (qt1010_readreg(priv, reg, &val) == 0) - printk(KERN_CONT " %02x", val); - else - printk(KERN_CONT " --"); - if (reg == 0x2f) - break; - } - printk(KERN_CONT "\n"); -} - -static int qt1010_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct qt1010_priv *priv; - int err; - u32 freq, div, mod1, mod2; - u8 i, tmpval, reg05; - qt1010_i2c_oper_t rd[48] = { - { QT1010_WR, 0x01, 0x80 }, - { QT1010_WR, 0x02, 0x3f }, - { QT1010_WR, 0x05, 0xff }, /* 02 c write */ - { QT1010_WR, 0x06, 0x44 }, - { QT1010_WR, 0x07, 0xff }, /* 04 c write */ - { QT1010_WR, 0x08, 0x08 }, - { QT1010_WR, 0x09, 0xff }, /* 06 c write */ - { QT1010_WR, 0x0a, 0xff }, /* 07 c write */ - { QT1010_WR, 0x0b, 0xff }, /* 08 c write */ - { QT1010_WR, 0x0c, 0xe1 }, - { QT1010_WR, 0x1a, 0xff }, /* 10 c write */ - { QT1010_WR, 0x1b, 0x00 }, - { QT1010_WR, 0x1c, 0x89 }, - { QT1010_WR, 0x11, 0xff }, /* 13 c write */ - { QT1010_WR, 0x12, 0xff }, /* 14 c write */ - { QT1010_WR, 0x22, 0xff }, /* 15 c write */ - { QT1010_WR, 0x1e, 0x00 }, - { QT1010_WR, 0x1e, 0xd0 }, - { QT1010_RD, 0x22, 0xff }, /* 16 c read */ - { QT1010_WR, 0x1e, 0x00 }, - { QT1010_RD, 0x05, 0xff }, /* 20 c read */ - { QT1010_RD, 0x22, 0xff }, /* 21 c read */ - { QT1010_WR, 0x23, 0xd0 }, - { QT1010_WR, 0x1e, 0x00 }, - { QT1010_WR, 0x1e, 0xe0 }, - { QT1010_RD, 0x23, 0xff }, /* 25 c read */ - { QT1010_RD, 0x23, 0xff }, /* 26 c read */ - { QT1010_WR, 0x1e, 0x00 }, - { QT1010_WR, 0x24, 0xd0 }, - { QT1010_WR, 0x1e, 0x00 }, - { QT1010_WR, 0x1e, 0xf0 }, - { QT1010_RD, 0x24, 0xff }, /* 31 c read */ - { QT1010_WR, 0x1e, 0x00 }, - { QT1010_WR, 0x14, 0x7f }, - { QT1010_WR, 0x15, 0x7f }, - { QT1010_WR, 0x05, 0xff }, /* 35 c write */ - { QT1010_WR, 0x06, 0x00 }, - { QT1010_WR, 0x15, 0x1f }, - { QT1010_WR, 0x16, 0xff }, - { QT1010_WR, 0x18, 0xff }, - { QT1010_WR, 0x1f, 0xff }, /* 40 c write */ - { QT1010_WR, 0x20, 0xff }, /* 41 c write */ - { QT1010_WR, 0x21, 0x53 }, - { QT1010_WR, 0x25, 0xff }, /* 43 c write */ - { QT1010_WR, 0x26, 0x15 }, - { QT1010_WR, 0x00, 0xff }, /* 45 c write */ - { QT1010_WR, 0x02, 0x00 }, - { QT1010_WR, 0x01, 0x00 } - }; - -#define FREQ1 32000000 /* 32 MHz */ -#define FREQ2 4000000 /* 4 MHz Quartz oscillator in the stick? */ - - priv = fe->tuner_priv; - freq = c->frequency; - div = (freq + QT1010_OFFSET) / QT1010_STEP; - freq = (div * QT1010_STEP) - QT1010_OFFSET; - mod1 = (freq + QT1010_OFFSET) % FREQ1; - mod2 = (freq + QT1010_OFFSET) % FREQ2; - priv->frequency = freq; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ - - /* reg 05 base value */ - if (freq < 290000000) reg05 = 0x14; /* 290 MHz */ - else if (freq < 610000000) reg05 = 0x34; /* 610 MHz */ - else if (freq < 802000000) reg05 = 0x54; /* 802 MHz */ - else reg05 = 0x74; - - /* 0x5 */ - rd[2].val = reg05; - - /* 07 - set frequency: 32 MHz scale */ - rd[4].val = (freq + QT1010_OFFSET) / FREQ1; - - /* 09 - changes every 8/24 MHz */ - if (mod1 < 8000000) rd[6].val = 0x1d; - else rd[6].val = 0x1c; - - /* 0a - set frequency: 4 MHz scale (max 28 MHz) */ - if (mod1 < 1*FREQ2) rd[7].val = 0x09; /* +0 MHz */ - else if (mod1 < 2*FREQ2) rd[7].val = 0x08; /* +4 MHz */ - else if (mod1 < 3*FREQ2) rd[7].val = 0x0f; /* +8 MHz */ - else if (mod1 < 4*FREQ2) rd[7].val = 0x0e; /* +12 MHz */ - else if (mod1 < 5*FREQ2) rd[7].val = 0x0d; /* +16 MHz */ - else if (mod1 < 6*FREQ2) rd[7].val = 0x0c; /* +20 MHz */ - else if (mod1 < 7*FREQ2) rd[7].val = 0x0b; /* +24 MHz */ - else rd[7].val = 0x0a; /* +28 MHz */ - - /* 0b - changes every 2/2 MHz */ - if (mod2 < 2000000) rd[8].val = 0x45; - else rd[8].val = 0x44; - - /* 1a - set frequency: 125 kHz scale (max 3875 kHz)*/ - tmpval = 0x78; /* byte, overflows intentionally */ - rd[10].val = tmpval-((mod2/QT1010_STEP)*0x08); - - /* 11 */ - rd[13].val = 0xfd; /* TODO: correct value calculation */ - - /* 12 */ - rd[14].val = 0x91; /* TODO: correct value calculation */ - - /* 22 */ - if (freq < 450000000) rd[15].val = 0xd0; /* 450 MHz */ - else if (freq < 482000000) rd[15].val = 0xd1; /* 482 MHz */ - else if (freq < 514000000) rd[15].val = 0xd4; /* 514 MHz */ - else if (freq < 546000000) rd[15].val = 0xd7; /* 546 MHz */ - else if (freq < 610000000) rd[15].val = 0xda; /* 610 MHz */ - else rd[15].val = 0xd0; - - /* 05 */ - rd[35].val = (reg05 & 0xf0); - - /* 1f */ - if (mod1 < 8000000) tmpval = 0x00; - else if (mod1 < 12000000) tmpval = 0x01; - else if (mod1 < 16000000) tmpval = 0x02; - else if (mod1 < 24000000) tmpval = 0x03; - else if (mod1 < 28000000) tmpval = 0x04; - else tmpval = 0x05; - rd[40].val = (priv->reg1f_init_val + 0x0e + tmpval); - - /* 20 */ - if (mod1 < 8000000) tmpval = 0x00; - else if (mod1 < 12000000) tmpval = 0x01; - else if (mod1 < 20000000) tmpval = 0x02; - else if (mod1 < 24000000) tmpval = 0x03; - else if (mod1 < 28000000) tmpval = 0x04; - else tmpval = 0x05; - rd[41].val = (priv->reg20_init_val + 0x0d + tmpval); - - /* 25 */ - rd[43].val = priv->reg25_init_val; - - /* 00 */ - rd[45].val = 0x92; /* TODO: correct value calculation */ - - dprintk("freq:%u 05:%02x 07:%02x 09:%02x 0a:%02x 0b:%02x " \ - "1a:%02x 11:%02x 12:%02x 22:%02x 05:%02x 1f:%02x " \ - "20:%02x 25:%02x 00:%02x", \ - freq, rd[2].val, rd[4].val, rd[6].val, rd[7].val, rd[8].val, \ - rd[10].val, rd[13].val, rd[14].val, rd[15].val, rd[35].val, \ - rd[40].val, rd[41].val, rd[43].val, rd[45].val); - - for (i = 0; i < ARRAY_SIZE(rd); i++) { - if (rd[i].oper == QT1010_WR) { - err = qt1010_writereg(priv, rd[i].reg, rd[i].val); - } else { /* read is required to proper locking */ - err = qt1010_readreg(priv, rd[i].reg, &tmpval); - } - if (err) return err; - } - - if (debug) - qt1010_dump_regs(priv); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ - - return 0; -} - -static int qt1010_init_meas1(struct qt1010_priv *priv, - u8 oper, u8 reg, u8 reg_init_val, u8 *retval) -{ - u8 i, val1, val2; - int err; - - qt1010_i2c_oper_t i2c_data[] = { - { QT1010_WR, reg, reg_init_val }, - { QT1010_WR, 0x1e, 0x00 }, - { QT1010_WR, 0x1e, oper }, - { QT1010_RD, reg, 0xff } - }; - - for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { - if (i2c_data[i].oper == QT1010_WR) { - err = qt1010_writereg(priv, i2c_data[i].reg, - i2c_data[i].val); - } else { - err = qt1010_readreg(priv, i2c_data[i].reg, &val2); - } - if (err) return err; - } - - do { - val1 = val2; - err = qt1010_readreg(priv, reg, &val2); - if (err) return err; - dprintk("compare reg:%02x %02x %02x", reg, val1, val2); - } while (val1 != val2); - *retval = val1; - - return qt1010_writereg(priv, 0x1e, 0x00); -} - -static int qt1010_init_meas2(struct qt1010_priv *priv, - u8 reg_init_val, u8 *retval) -{ - u8 i, val; - int err; - qt1010_i2c_oper_t i2c_data[] = { - { QT1010_WR, 0x07, reg_init_val }, - { QT1010_WR, 0x22, 0xd0 }, - { QT1010_WR, 0x1e, 0x00 }, - { QT1010_WR, 0x1e, 0xd0 }, - { QT1010_RD, 0x22, 0xff }, - { QT1010_WR, 0x1e, 0x00 }, - { QT1010_WR, 0x22, 0xff } - }; - for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { - if (i2c_data[i].oper == QT1010_WR) { - err = qt1010_writereg(priv, i2c_data[i].reg, - i2c_data[i].val); - } else { - err = qt1010_readreg(priv, i2c_data[i].reg, &val); - } - if (err) return err; - } - *retval = val; - return 0; -} - -static int qt1010_init(struct dvb_frontend *fe) -{ - struct qt1010_priv *priv = fe->tuner_priv; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int err = 0; - u8 i, tmpval, *valptr = NULL; - - qt1010_i2c_oper_t i2c_data[] = { - { QT1010_WR, 0x01, 0x80 }, - { QT1010_WR, 0x0d, 0x84 }, - { QT1010_WR, 0x0e, 0xb7 }, - { QT1010_WR, 0x2a, 0x23 }, - { QT1010_WR, 0x2c, 0xdc }, - { QT1010_M1, 0x25, 0x40 }, /* get reg 25 init value */ - { QT1010_M1, 0x81, 0xff }, /* get reg 25 init value */ - { QT1010_WR, 0x2b, 0x70 }, - { QT1010_WR, 0x2a, 0x23 }, - { QT1010_M1, 0x26, 0x08 }, - { QT1010_M1, 0x82, 0xff }, - { QT1010_WR, 0x05, 0x14 }, - { QT1010_WR, 0x06, 0x44 }, - { QT1010_WR, 0x07, 0x28 }, - { QT1010_WR, 0x08, 0x0b }, - { QT1010_WR, 0x11, 0xfd }, - { QT1010_M1, 0x22, 0x0d }, - { QT1010_M1, 0xd0, 0xff }, - { QT1010_WR, 0x06, 0x40 }, - { QT1010_WR, 0x16, 0xf0 }, - { QT1010_WR, 0x02, 0x38 }, - { QT1010_WR, 0x03, 0x18 }, - { QT1010_WR, 0x20, 0xe0 }, - { QT1010_M1, 0x1f, 0x20 }, /* get reg 1f init value */ - { QT1010_M1, 0x84, 0xff }, /* get reg 1f init value */ - { QT1010_RD, 0x20, 0x20 }, /* get reg 20 init value */ - { QT1010_WR, 0x03, 0x19 }, - { QT1010_WR, 0x02, 0x3f }, - { QT1010_WR, 0x21, 0x53 }, - { QT1010_RD, 0x21, 0xff }, - { QT1010_WR, 0x11, 0xfd }, - { QT1010_WR, 0x05, 0x34 }, - { QT1010_WR, 0x06, 0x44 }, - { QT1010_WR, 0x08, 0x08 } - }; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ - - for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { - switch (i2c_data[i].oper) { - case QT1010_WR: - err = qt1010_writereg(priv, i2c_data[i].reg, - i2c_data[i].val); - break; - case QT1010_RD: - if (i2c_data[i].val == 0x20) - valptr = &priv->reg20_init_val; - else - valptr = &tmpval; - err = qt1010_readreg(priv, i2c_data[i].reg, valptr); - break; - case QT1010_M1: - if (i2c_data[i].val == 0x25) - valptr = &priv->reg25_init_val; - else if (i2c_data[i].val == 0x1f) - valptr = &priv->reg1f_init_val; - else - valptr = &tmpval; - err = qt1010_init_meas1(priv, i2c_data[i+1].reg, - i2c_data[i].reg, - i2c_data[i].val, valptr); - i++; - break; - } - if (err) return err; - } - - for (i = 0x31; i < 0x3a; i++) /* 0x31 - 0x39 */ - if ((err = qt1010_init_meas2(priv, i, &tmpval))) - return err; - - c->frequency = 545000000; /* Sigmatek DVB-110 545000000 */ - /* MSI Megasky 580 GL861 533000000 */ - return qt1010_set_params(fe); -} - -static int qt1010_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - return 0; -} - -static int qt1010_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct qt1010_priv *priv = fe->tuner_priv; - *frequency = priv->frequency; - return 0; -} - -static int qt1010_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - *frequency = 36125000; - return 0; -} - -static const struct dvb_tuner_ops qt1010_tuner_ops = { - .info = { - .name = "Quantek QT1010", - .frequency_min = QT1010_MIN_FREQ, - .frequency_max = QT1010_MAX_FREQ, - .frequency_step = QT1010_STEP, - }, - - .release = qt1010_release, - .init = qt1010_init, - /* TODO: implement sleep */ - - .set_params = qt1010_set_params, - .get_frequency = qt1010_get_frequency, - .get_if_frequency = qt1010_get_if_frequency, -}; - -struct dvb_frontend * qt1010_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct qt1010_config *cfg) -{ - struct qt1010_priv *priv = NULL; - u8 id; - - priv = kzalloc(sizeof(struct qt1010_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - - priv->cfg = cfg; - priv->i2c = i2c; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ - - - /* Try to detect tuner chip. Probably this is not correct register. */ - if (qt1010_readreg(priv, 0x29, &id) != 0 || (id != 0x39)) { - kfree(priv); - return NULL; - } - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ - - printk(KERN_INFO "Quantek QT1010 successfully identified.\n"); - memcpy(&fe->ops.tuner_ops, &qt1010_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - fe->tuner_priv = priv; - return fe; -} -EXPORT_SYMBOL(qt1010_attach); - -MODULE_DESCRIPTION("Quantek QT1010 silicon tuner driver"); -MODULE_AUTHOR("Antti Palosaari "); -MODULE_AUTHOR("Aapo Tahkola "); -MODULE_VERSION("0.1"); -MODULE_LICENSE("GPL"); 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 - * Aapo Tahkola - * - * 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 - * Aapo Tahkola - * - * 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 5d9f02842501..000000000000 --- a/drivers/media/common/tuners/tda18212.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * NXP TDA18212HN silicon tuner driver - * - * Copyright (C) 2011 Antti Palosaari - * - * 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 "tda18212.h" - -struct tda18212_priv { - struct tda18212_config *cfg; - struct i2c_adapter *i2c; - - u32 if_frequency; -}; - -/* 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 { - dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \ - "len=%d\n", KBUILD_MODNAME, 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 = ®, - }, { - .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 { - dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \ - "len=%d\n", KBUILD_MODNAME, 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 }, - }; - - dev_dbg(&priv->i2c->dev, - "%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", - __func__, 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: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, 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 uninitialized_var(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 */ - - dev_dbg(&priv->i2c->dev, "%s: ret=%d chip id=%02x\n", __func__, ret, - val); - if (ret || val != 0xc7) { - kfree(priv); - return NULL; - } - - dev_info(&priv->i2c->dev, - "%s: NXP TDA18212HN successfully identified\n", - KBUILD_MODNAME); - - 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 "); -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 - * - * 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.c b/drivers/media/common/tuners/tda18218.c deleted file mode 100644 index 8a6f9ca788f0..000000000000 --- a/drivers/media/common/tuners/tda18218.c +++ /dev/null @@ -1,342 +0,0 @@ -/* - * NXP TDA18218HN silicon tuner driver - * - * Copyright (C) 2010 Antti Palosaari - * - * 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 "tda18218.h" -#include "tda18218_priv.h" - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); - -/* write multiple registers */ -static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len) -{ - int ret = 0; - u8 buf[1+len], quotient, remainder, i, msg_len, msg_len_max; - struct i2c_msg msg[1] = { - { - .addr = priv->cfg->i2c_address, - .flags = 0, - .buf = buf, - } - }; - - msg_len_max = priv->cfg->i2c_wr_max - 1; - quotient = len / msg_len_max; - remainder = len % msg_len_max; - msg_len = msg_len_max; - for (i = 0; (i <= quotient && remainder); i++) { - if (i == quotient) /* set len of the last msg */ - msg_len = remainder; - - msg[0].len = msg_len + 1; - buf[0] = reg + i * msg_len_max; - memcpy(&buf[1], &val[i * msg_len_max], msg_len); - - ret = i2c_transfer(priv->i2c, msg, 1); - if (ret != 1) - break; - } - - if (ret == 1) { - ret = 0; - } else { - warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len); - ret = -EREMOTEIO; - } - - return ret; -} - -/* read multiple registers */ -static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len) -{ - int ret; - u8 buf[reg+len]; /* we must start read always from reg 0x00 */ - struct i2c_msg msg[2] = { - { - .addr = priv->cfg->i2c_address, - .flags = 0, - .len = 1, - .buf = "\x00", - }, { - .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[reg], len); - ret = 0; - } else { - warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len); - ret = -EREMOTEIO; - } - - return ret; -} - -/* write single register */ -static int tda18218_wr_reg(struct tda18218_priv *priv, u8 reg, u8 val) -{ - return tda18218_wr_regs(priv, reg, &val, 1); -} - -/* read single register */ - -static int tda18218_rd_reg(struct tda18218_priv *priv, u8 reg, u8 *val) -{ - return tda18218_rd_regs(priv, reg, val, 1); -} - -static int tda18218_set_params(struct dvb_frontend *fe) -{ - struct tda18218_priv *priv = fe->tuner_priv; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - u32 bw = c->bandwidth_hz; - int ret; - u8 buf[3], i, BP_Filter, LP_Fc; - u32 LO_Frac; - /* TODO: find out correct AGC algorithm */ - u8 agc[][2] = { - { R20_AGC11, 0x60 }, - { R23_AGC21, 0x02 }, - { R20_AGC11, 0xa0 }, - { R23_AGC21, 0x09 }, - { R20_AGC11, 0xe0 }, - { R23_AGC21, 0x0c }, - { R20_AGC11, 0x40 }, - { R23_AGC21, 0x01 }, - { R20_AGC11, 0x80 }, - { R23_AGC21, 0x08 }, - { R20_AGC11, 0xc0 }, - { R23_AGC21, 0x0b }, - { R24_AGC22, 0x1c }, - { R24_AGC22, 0x0c }, - }; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ - - /* low-pass filter cut-off frequency */ - if (bw <= 6000000) { - LP_Fc = 0; - priv->if_frequency = 3000000; - } else if (bw <= 7000000) { - LP_Fc = 1; - priv->if_frequency = 3500000; - } else { - LP_Fc = 2; - priv->if_frequency = 4000000; - } - - LO_Frac = c->frequency + priv->if_frequency; - - /* band-pass filter */ - if (LO_Frac < 188000000) - BP_Filter = 3; - else if (LO_Frac < 253000000) - BP_Filter = 4; - else if (LO_Frac < 343000000) - BP_Filter = 5; - else - BP_Filter = 6; - - buf[0] = (priv->regs[R1A_IF1] & ~7) | BP_Filter; /* BP_Filter */ - buf[1] = (priv->regs[R1B_IF2] & ~3) | LP_Fc; /* LP_Fc */ - buf[2] = priv->regs[R1C_AGC2B]; - ret = tda18218_wr_regs(priv, R1A_IF1, buf, 3); - if (ret) - goto error; - - buf[0] = (LO_Frac / 1000) >> 12; /* LO_Frac_0 */ - buf[1] = (LO_Frac / 1000) >> 4; /* LO_Frac_1 */ - buf[2] = (LO_Frac / 1000) << 4 | - (priv->regs[R0C_MD5] & 0x0f); /* LO_Frac_2 */ - ret = tda18218_wr_regs(priv, R0A_MD3, buf, 3); - if (ret) - goto error; - - buf[0] = priv->regs[R0F_MD8] | (1 << 6); /* Freq_prog_Start */ - ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1); - if (ret) - goto error; - - buf[0] = priv->regs[R0F_MD8] & ~(1 << 6); /* Freq_prog_Start */ - ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1); - if (ret) - goto error; - - /* trigger AGC */ - for (i = 0; i < ARRAY_SIZE(agc); i++) { - ret = tda18218_wr_reg(priv, agc[i][0], agc[i][1]); - if (ret) - goto error; - } - -error: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - - if (ret) - dbg("%s: failed ret:%d", __func__, ret); - - return ret; -} - -static int tda18218_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct tda18218_priv *priv = fe->tuner_priv; - *frequency = priv->if_frequency; - dbg("%s: if=%d", __func__, *frequency); - return 0; -} - -static int tda18218_sleep(struct dvb_frontend *fe) -{ - struct tda18218_priv *priv = fe->tuner_priv; - int ret; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ - - /* standby */ - ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0)); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - - if (ret) - dbg("%s: failed ret:%d", __func__, ret); - - return ret; -} - -static int tda18218_init(struct dvb_frontend *fe) -{ - struct tda18218_priv *priv = fe->tuner_priv; - int ret; - - /* TODO: calibrations */ - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ - - ret = tda18218_wr_regs(priv, R00_ID, priv->regs, TDA18218_NUM_REGS); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - - if (ret) - dbg("%s: failed ret:%d", __func__, ret); - - return ret; -} - -static int tda18218_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - return 0; -} - -static const struct dvb_tuner_ops tda18218_tuner_ops = { - .info = { - .name = "NXP TDA18218", - - .frequency_min = 174000000, - .frequency_max = 864000000, - .frequency_step = 1000, - }, - - .release = tda18218_release, - .init = tda18218_init, - .sleep = tda18218_sleep, - - .set_params = tda18218_set_params, - - .get_if_frequency = tda18218_get_if_frequency, -}; - -struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, struct tda18218_config *cfg) -{ - struct tda18218_priv *priv = NULL; - u8 uninitialized_var(val); - int ret; - /* chip default registers values */ - static u8 def_regs[] = { - 0xc0, 0x88, 0x00, 0x8e, 0x03, 0x00, 0x00, 0xd0, 0x00, 0x40, - 0x00, 0x00, 0x07, 0xff, 0x84, 0x09, 0x00, 0x13, 0x00, 0x00, - 0x01, 0x84, 0x09, 0xf0, 0x19, 0x0a, 0x8e, 0x69, 0x98, 0x01, - 0x00, 0x58, 0x10, 0x40, 0x8c, 0x00, 0x0c, 0x48, 0x85, 0xc9, - 0xa7, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x00, 0x39, 0x00, - 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf6 - }; - - priv = kzalloc(sizeof(struct tda18218_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 = tda18218_rd_reg(priv, R00_ID, &val); - dbg("%s: ret:%d chip ID:%02x", __func__, ret, val); - if (ret || val != def_regs[R00_ID]) { - kfree(priv); - return NULL; - } - - info("NXP TDA18218HN successfully identified."); - - memcpy(&fe->ops.tuner_ops, &tda18218_tuner_ops, - sizeof(struct dvb_tuner_ops)); - memcpy(priv->regs, def_regs, sizeof(def_regs)); - - /* loop-through enabled chip default register values */ - if (priv->cfg->loop_through) { - priv->regs[R17_PD1] = 0xb0; - priv->regs[R18_PD2] = 0x59; - } - - /* standby */ - ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0)); - if (ret) - dbg("%s: failed ret:%d", __func__, ret); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - - return fe; -} -EXPORT_SYMBOL(tda18218_attach); - -MODULE_DESCRIPTION("NXP TDA18218HN silicon tuner driver"); -MODULE_AUTHOR("Antti Palosaari "); -MODULE_LICENSE("GPL"); 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 - * - * 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 - * - * 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-common.c b/drivers/media/common/tuners/tda18271-common.c deleted file mode 100644 index 39c645787b62..000000000000 --- a/drivers/media/common/tuners/tda18271-common.c +++ /dev/null @@ -1,703 +0,0 @@ -/* - tda18271-common.c - driver for the Philips / NXP TDA18271 silicon tuner - - Copyright (C) 2007, 2008 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. -*/ - -#include "tda18271-priv.h" - -static int tda18271_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) -{ - struct tda18271_priv *priv = fe->tuner_priv; - enum tda18271_i2c_gate gate; - int ret = 0; - - switch (priv->gate) { - case TDA18271_GATE_DIGITAL: - case TDA18271_GATE_ANALOG: - gate = priv->gate; - break; - case TDA18271_GATE_AUTO: - default: - switch (priv->mode) { - case TDA18271_DIGITAL: - gate = TDA18271_GATE_DIGITAL; - break; - case TDA18271_ANALOG: - default: - gate = TDA18271_GATE_ANALOG; - break; - } - } - - switch (gate) { - case TDA18271_GATE_ANALOG: - if (fe->ops.analog_ops.i2c_gate_ctrl) - ret = fe->ops.analog_ops.i2c_gate_ctrl(fe, enable); - break; - case TDA18271_GATE_DIGITAL: - if (fe->ops.i2c_gate_ctrl) - ret = fe->ops.i2c_gate_ctrl(fe, enable); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -}; - -/*---------------------------------------------------------------------*/ - -static void tda18271_dump_regs(struct dvb_frontend *fe, int extended) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - - tda_reg("=== TDA18271 REG DUMP ===\n"); - tda_reg("ID_BYTE = 0x%02x\n", 0xff & regs[R_ID]); - tda_reg("THERMO_BYTE = 0x%02x\n", 0xff & regs[R_TM]); - tda_reg("POWER_LEVEL_BYTE = 0x%02x\n", 0xff & regs[R_PL]); - tda_reg("EASY_PROG_BYTE_1 = 0x%02x\n", 0xff & regs[R_EP1]); - tda_reg("EASY_PROG_BYTE_2 = 0x%02x\n", 0xff & regs[R_EP2]); - tda_reg("EASY_PROG_BYTE_3 = 0x%02x\n", 0xff & regs[R_EP3]); - tda_reg("EASY_PROG_BYTE_4 = 0x%02x\n", 0xff & regs[R_EP4]); - tda_reg("EASY_PROG_BYTE_5 = 0x%02x\n", 0xff & regs[R_EP5]); - tda_reg("CAL_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_CPD]); - tda_reg("CAL_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_CD1]); - tda_reg("CAL_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_CD2]); - tda_reg("CAL_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_CD3]); - tda_reg("MAIN_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_MPD]); - tda_reg("MAIN_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_MD1]); - tda_reg("MAIN_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_MD2]); - tda_reg("MAIN_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_MD3]); - - /* only dump extended regs if DBG_ADV is set */ - if (!(tda18271_debug & DBG_ADV)) - return; - - /* W indicates write-only registers. - * Register dump for write-only registers shows last value written. */ - - tda_reg("EXTENDED_BYTE_1 = 0x%02x\n", 0xff & regs[R_EB1]); - tda_reg("EXTENDED_BYTE_2 = 0x%02x\n", 0xff & regs[R_EB2]); - tda_reg("EXTENDED_BYTE_3 = 0x%02x\n", 0xff & regs[R_EB3]); - tda_reg("EXTENDED_BYTE_4 = 0x%02x\n", 0xff & regs[R_EB4]); - tda_reg("EXTENDED_BYTE_5 = 0x%02x\n", 0xff & regs[R_EB5]); - tda_reg("EXTENDED_BYTE_6 = 0x%02x\n", 0xff & regs[R_EB6]); - tda_reg("EXTENDED_BYTE_7 = 0x%02x\n", 0xff & regs[R_EB7]); - tda_reg("EXTENDED_BYTE_8 = 0x%02x\n", 0xff & regs[R_EB8]); - tda_reg("EXTENDED_BYTE_9 W = 0x%02x\n", 0xff & regs[R_EB9]); - tda_reg("EXTENDED_BYTE_10 = 0x%02x\n", 0xff & regs[R_EB10]); - tda_reg("EXTENDED_BYTE_11 = 0x%02x\n", 0xff & regs[R_EB11]); - tda_reg("EXTENDED_BYTE_12 = 0x%02x\n", 0xff & regs[R_EB12]); - tda_reg("EXTENDED_BYTE_13 = 0x%02x\n", 0xff & regs[R_EB13]); - tda_reg("EXTENDED_BYTE_14 = 0x%02x\n", 0xff & regs[R_EB14]); - tda_reg("EXTENDED_BYTE_15 = 0x%02x\n", 0xff & regs[R_EB15]); - tda_reg("EXTENDED_BYTE_16 W = 0x%02x\n", 0xff & regs[R_EB16]); - tda_reg("EXTENDED_BYTE_17 W = 0x%02x\n", 0xff & regs[R_EB17]); - tda_reg("EXTENDED_BYTE_18 = 0x%02x\n", 0xff & regs[R_EB18]); - tda_reg("EXTENDED_BYTE_19 W = 0x%02x\n", 0xff & regs[R_EB19]); - tda_reg("EXTENDED_BYTE_20 W = 0x%02x\n", 0xff & regs[R_EB20]); - tda_reg("EXTENDED_BYTE_21 = 0x%02x\n", 0xff & regs[R_EB21]); - tda_reg("EXTENDED_BYTE_22 = 0x%02x\n", 0xff & regs[R_EB22]); - tda_reg("EXTENDED_BYTE_23 = 0x%02x\n", 0xff & regs[R_EB23]); -} - -int tda18271_read_regs(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - unsigned char buf = 0x00; - int ret; - struct i2c_msg msg[] = { - { .addr = priv->i2c_props.addr, .flags = 0, - .buf = &buf, .len = 1 }, - { .addr = priv->i2c_props.addr, .flags = I2C_M_RD, - .buf = regs, .len = 16 } - }; - - tda18271_i2c_gate_ctrl(fe, 1); - - /* read all registers */ - ret = i2c_transfer(priv->i2c_props.adap, msg, 2); - - tda18271_i2c_gate_ctrl(fe, 0); - - if (ret != 2) - tda_err("ERROR: i2c_transfer returned: %d\n", ret); - - if (tda18271_debug & DBG_REG) - tda18271_dump_regs(fe, 0); - - return (ret == 2 ? 0 : ret); -} - -int tda18271_read_extended(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - unsigned char regdump[TDA18271_NUM_REGS]; - unsigned char buf = 0x00; - int ret, i; - struct i2c_msg msg[] = { - { .addr = priv->i2c_props.addr, .flags = 0, - .buf = &buf, .len = 1 }, - { .addr = priv->i2c_props.addr, .flags = I2C_M_RD, - .buf = regdump, .len = TDA18271_NUM_REGS } - }; - - tda18271_i2c_gate_ctrl(fe, 1); - - /* read all registers */ - ret = i2c_transfer(priv->i2c_props.adap, msg, 2); - - tda18271_i2c_gate_ctrl(fe, 0); - - if (ret != 2) - tda_err("ERROR: i2c_transfer returned: %d\n", ret); - - for (i = 0; i < TDA18271_NUM_REGS; i++) { - /* don't update write-only registers */ - if ((i != R_EB9) && - (i != R_EB16) && - (i != R_EB17) && - (i != R_EB19) && - (i != R_EB20)) - regs[i] = regdump[i]; - } - - if (tda18271_debug & DBG_REG) - tda18271_dump_regs(fe, 1); - - return (ret == 2 ? 0 : ret); -} - -int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - unsigned char buf[TDA18271_NUM_REGS + 1]; - struct i2c_msg msg = { .addr = priv->i2c_props.addr, .flags = 0, - .buf = buf }; - int i, ret = 1, max; - - BUG_ON((len == 0) || (idx + len > sizeof(buf))); - - - switch (priv->small_i2c) { - case TDA18271_03_BYTE_CHUNK_INIT: - max = 3; - break; - case TDA18271_08_BYTE_CHUNK_INIT: - max = 8; - break; - case TDA18271_16_BYTE_CHUNK_INIT: - max = 16; - break; - case TDA18271_39_BYTE_CHUNK_INIT: - default: - max = 39; - } - - tda18271_i2c_gate_ctrl(fe, 1); - while (len) { - if (max > len) - max = len; - - buf[0] = idx; - for (i = 1; i <= max; i++) - buf[i] = regs[idx - 1 + i]; - - msg.len = max + 1; - - /* write registers */ - ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); - if (ret != 1) - break; - - idx += max; - len -= max; - } - tda18271_i2c_gate_ctrl(fe, 0); - - if (ret != 1) - tda_err("ERROR: idx = 0x%x, len = %d, " - "i2c_transfer returned: %d\n", idx, max, ret); - - return (ret == 1 ? 0 : ret); -} - -/*---------------------------------------------------------------------*/ - -int tda18271_charge_pump_source(struct dvb_frontend *fe, - enum tda18271_pll pll, int force) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - - int r_cp = (pll == TDA18271_CAL_PLL) ? R_EB7 : R_EB4; - - regs[r_cp] &= ~0x20; - regs[r_cp] |= ((force & 1) << 5); - - return tda18271_write_regs(fe, r_cp, 1); -} - -int tda18271_init_regs(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - - tda_dbg("initializing registers for device @ %d-%04x\n", - i2c_adapter_id(priv->i2c_props.adap), - priv->i2c_props.addr); - - /* initialize registers */ - switch (priv->id) { - case TDA18271HDC1: - regs[R_ID] = 0x83; - break; - case TDA18271HDC2: - regs[R_ID] = 0x84; - break; - }; - - regs[R_TM] = 0x08; - regs[R_PL] = 0x80; - regs[R_EP1] = 0xc6; - regs[R_EP2] = 0xdf; - regs[R_EP3] = 0x16; - regs[R_EP4] = 0x60; - regs[R_EP5] = 0x80; - regs[R_CPD] = 0x80; - regs[R_CD1] = 0x00; - regs[R_CD2] = 0x00; - regs[R_CD3] = 0x00; - regs[R_MPD] = 0x00; - regs[R_MD1] = 0x00; - regs[R_MD2] = 0x00; - regs[R_MD3] = 0x00; - - switch (priv->id) { - case TDA18271HDC1: - regs[R_EB1] = 0xff; - break; - case TDA18271HDC2: - regs[R_EB1] = 0xfc; - break; - }; - - regs[R_EB2] = 0x01; - regs[R_EB3] = 0x84; - regs[R_EB4] = 0x41; - regs[R_EB5] = 0x01; - regs[R_EB6] = 0x84; - regs[R_EB7] = 0x40; - regs[R_EB8] = 0x07; - regs[R_EB9] = 0x00; - regs[R_EB10] = 0x00; - regs[R_EB11] = 0x96; - - switch (priv->id) { - case TDA18271HDC1: - regs[R_EB12] = 0x0f; - break; - case TDA18271HDC2: - regs[R_EB12] = 0x33; - break; - }; - - regs[R_EB13] = 0xc1; - regs[R_EB14] = 0x00; - regs[R_EB15] = 0x8f; - regs[R_EB16] = 0x00; - regs[R_EB17] = 0x00; - - switch (priv->id) { - case TDA18271HDC1: - regs[R_EB18] = 0x00; - break; - case TDA18271HDC2: - regs[R_EB18] = 0x8c; - break; - }; - - regs[R_EB19] = 0x00; - regs[R_EB20] = 0x20; - - switch (priv->id) { - case TDA18271HDC1: - regs[R_EB21] = 0x33; - break; - case TDA18271HDC2: - regs[R_EB21] = 0xb3; - break; - }; - - regs[R_EB22] = 0x48; - regs[R_EB23] = 0xb0; - - tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS); - - /* setup agc1 gain */ - regs[R_EB17] = 0x00; - tda18271_write_regs(fe, R_EB17, 1); - regs[R_EB17] = 0x03; - tda18271_write_regs(fe, R_EB17, 1); - regs[R_EB17] = 0x43; - tda18271_write_regs(fe, R_EB17, 1); - regs[R_EB17] = 0x4c; - tda18271_write_regs(fe, R_EB17, 1); - - /* setup agc2 gain */ - if ((priv->id) == TDA18271HDC1) { - regs[R_EB20] = 0xa0; - tda18271_write_regs(fe, R_EB20, 1); - regs[R_EB20] = 0xa7; - tda18271_write_regs(fe, R_EB20, 1); - regs[R_EB20] = 0xe7; - tda18271_write_regs(fe, R_EB20, 1); - regs[R_EB20] = 0xec; - tda18271_write_regs(fe, R_EB20, 1); - } - - /* image rejection calibration */ - - /* low-band */ - regs[R_EP3] = 0x1f; - regs[R_EP4] = 0x66; - regs[R_EP5] = 0x81; - regs[R_CPD] = 0xcc; - regs[R_CD1] = 0x6c; - regs[R_CD2] = 0x00; - regs[R_CD3] = 0x00; - regs[R_MPD] = 0xcd; - regs[R_MD1] = 0x77; - regs[R_MD2] = 0x08; - regs[R_MD3] = 0x00; - - tda18271_write_regs(fe, R_EP3, 11); - - if ((priv->id) == TDA18271HDC2) { - /* main pll cp source on */ - tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 1); - msleep(1); - - /* main pll cp source off */ - tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 0); - } - - msleep(5); /* pll locking */ - - /* launch detector */ - tda18271_write_regs(fe, R_EP1, 1); - msleep(5); /* wanted low measurement */ - - regs[R_EP5] = 0x85; - regs[R_CPD] = 0xcb; - regs[R_CD1] = 0x66; - regs[R_CD2] = 0x70; - - tda18271_write_regs(fe, R_EP3, 7); - msleep(5); /* pll locking */ - - /* launch optimization algorithm */ - tda18271_write_regs(fe, R_EP2, 1); - msleep(30); /* image low optimization completion */ - - /* mid-band */ - regs[R_EP5] = 0x82; - regs[R_CPD] = 0xa8; - regs[R_CD2] = 0x00; - regs[R_MPD] = 0xa9; - regs[R_MD1] = 0x73; - regs[R_MD2] = 0x1a; - - tda18271_write_regs(fe, R_EP3, 11); - msleep(5); /* pll locking */ - - /* launch detector */ - tda18271_write_regs(fe, R_EP1, 1); - msleep(5); /* wanted mid measurement */ - - regs[R_EP5] = 0x86; - regs[R_CPD] = 0xa8; - regs[R_CD1] = 0x66; - regs[R_CD2] = 0xa0; - - tda18271_write_regs(fe, R_EP3, 7); - msleep(5); /* pll locking */ - - /* launch optimization algorithm */ - tda18271_write_regs(fe, R_EP2, 1); - msleep(30); /* image mid optimization completion */ - - /* high-band */ - regs[R_EP5] = 0x83; - regs[R_CPD] = 0x98; - regs[R_CD1] = 0x65; - regs[R_CD2] = 0x00; - regs[R_MPD] = 0x99; - regs[R_MD1] = 0x71; - regs[R_MD2] = 0xcd; - - tda18271_write_regs(fe, R_EP3, 11); - msleep(5); /* pll locking */ - - /* launch detector */ - tda18271_write_regs(fe, R_EP1, 1); - msleep(5); /* wanted high measurement */ - - regs[R_EP5] = 0x87; - regs[R_CD1] = 0x65; - regs[R_CD2] = 0x50; - - tda18271_write_regs(fe, R_EP3, 7); - msleep(5); /* pll locking */ - - /* launch optimization algorithm */ - tda18271_write_regs(fe, R_EP2, 1); - msleep(30); /* image high optimization completion */ - - /* return to normal mode */ - regs[R_EP4] = 0x64; - tda18271_write_regs(fe, R_EP4, 1); - - /* synchronize */ - tda18271_write_regs(fe, R_EP1, 1); - - return 0; -} - -/*---------------------------------------------------------------------*/ - -/* - * Standby modes, EP3 [7:5] - * - * | SM || SM_LT || SM_XT || mode description - * |=====\\=======\\=======\\=================================== - * | 0 || 0 || 0 || normal mode - * |-----||-------||-------||----------------------------------- - * | || || || standby mode w/ slave tuner output - * | 1 || 0 || 0 || & loop thru & xtal oscillator on - * |-----||-------||-------||----------------------------------- - * | 1 || 1 || 0 || standby mode w/ xtal oscillator on - * |-----||-------||-------||----------------------------------- - * | 1 || 1 || 1 || power off - * - */ - -int tda18271_set_standby_mode(struct dvb_frontend *fe, - int sm, int sm_lt, int sm_xt) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - - if (tda18271_debug & DBG_ADV) - tda_dbg("sm = %d, sm_lt = %d, sm_xt = %d\n", sm, sm_lt, sm_xt); - - regs[R_EP3] &= ~0xe0; /* clear sm, sm_lt, sm_xt */ - regs[R_EP3] |= (sm ? (1 << 7) : 0) | - (sm_lt ? (1 << 6) : 0) | - (sm_xt ? (1 << 5) : 0); - - return tda18271_write_regs(fe, R_EP3, 1); -} - -/*---------------------------------------------------------------------*/ - -int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq) -{ - /* sets main post divider & divider bytes, but does not write them */ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u8 d, pd; - u32 div; - - int ret = tda18271_lookup_pll_map(fe, MAIN_PLL, &freq, &pd, &d); - if (tda_fail(ret)) - goto fail; - - regs[R_MPD] = (0x7f & pd); - - div = ((d * (freq / 1000)) << 7) / 125; - - regs[R_MD1] = 0x7f & (div >> 16); - regs[R_MD2] = 0xff & (div >> 8); - regs[R_MD3] = 0xff & div; -fail: - return ret; -} - -int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq) -{ - /* sets cal post divider & divider bytes, but does not write them */ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u8 d, pd; - u32 div; - - int ret = tda18271_lookup_pll_map(fe, CAL_PLL, &freq, &pd, &d); - if (tda_fail(ret)) - goto fail; - - regs[R_CPD] = pd; - - div = ((d * (freq / 1000)) << 7) / 125; - - regs[R_CD1] = 0x7f & (div >> 16); - regs[R_CD2] = 0xff & (div >> 8); - regs[R_CD3] = 0xff & div; -fail: - return ret; -} - -/*---------------------------------------------------------------------*/ - -int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq) -{ - /* sets bp filter bits, but does not write them */ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u8 val; - - int ret = tda18271_lookup_map(fe, BP_FILTER, freq, &val); - if (tda_fail(ret)) - goto fail; - - regs[R_EP1] &= ~0x07; /* clear bp filter bits */ - regs[R_EP1] |= (0x07 & val); -fail: - return ret; -} - -int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq) -{ - /* sets K & M bits, but does not write them */ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u8 val; - - int ret = tda18271_lookup_map(fe, RF_CAL_KMCO, freq, &val); - if (tda_fail(ret)) - goto fail; - - regs[R_EB13] &= ~0x7c; /* clear k & m bits */ - regs[R_EB13] |= (0x7c & val); -fail: - return ret; -} - -int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq) -{ - /* sets rf band bits, but does not write them */ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u8 val; - - int ret = tda18271_lookup_map(fe, RF_BAND, freq, &val); - if (tda_fail(ret)) - goto fail; - - regs[R_EP2] &= ~0xe0; /* clear rf band bits */ - regs[R_EP2] |= (0xe0 & (val << 5)); -fail: - return ret; -} - -int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq) -{ - /* sets gain taper bits, but does not write them */ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u8 val; - - int ret = tda18271_lookup_map(fe, GAIN_TAPER, freq, &val); - if (tda_fail(ret)) - goto fail; - - regs[R_EP2] &= ~0x1f; /* clear gain taper bits */ - regs[R_EP2] |= (0x1f & val); -fail: - return ret; -} - -int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq) -{ - /* sets IR Meas bits, but does not write them */ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u8 val; - - int ret = tda18271_lookup_map(fe, IR_MEASURE, freq, &val); - if (tda_fail(ret)) - goto fail; - - regs[R_EP5] &= ~0x07; - regs[R_EP5] |= (0x07 & val); -fail: - return ret; -} - -int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq) -{ - /* sets rf cal byte (RFC_Cprog), but does not write it */ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u8 val; - - int ret = tda18271_lookup_map(fe, RF_CAL, freq, &val); - /* The TDA18271HD/C1 rf_cal map lookup is expected to go out of range - * for frequencies above 61.1 MHz. In these cases, the internal RF - * tracking filters calibration mechanism is used. - * - * There is no need to warn the user about this. - */ - if (ret < 0) - goto fail; - - regs[R_EB14] = val; -fail: - return ret; -} - -int _tda_printk(struct tda18271_priv *state, const char *level, - const char *func, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - int rtn; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - if (state) - rtn = printk("%s%s: [%d-%04x|%c] %pV", - level, func, i2c_adapter_id(state->i2c_props.adap), - state->i2c_props.addr, - (state->role == TDA18271_MASTER) ? 'M' : 'S', - &vaf); - else - rtn = printk("%s%s: %pV", level, func, &vaf); - - va_end(args); - - return rtn; -} diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c deleted file mode 100644 index 2e67f4459904..000000000000 --- a/drivers/media/common/tuners/tda18271-fe.c +++ /dev/null @@ -1,1345 +0,0 @@ -/* - tda18271-fe.c - driver for the Philips / NXP TDA18271 silicon tuner - - Copyright (C) 2007, 2008 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. -*/ - -#include -#include -#include "tda18271-priv.h" - -int tda18271_debug; -module_param_named(debug, tda18271_debug, int, 0644); -MODULE_PARM_DESC(debug, "set debug level " - "(info=1, map=2, reg=4, adv=8, cal=16 (or-able))"); - -static int tda18271_cal_on_startup = -1; -module_param_named(cal, tda18271_cal_on_startup, int, 0644); -MODULE_PARM_DESC(cal, "perform RF tracking filter calibration on startup"); - -static DEFINE_MUTEX(tda18271_list_mutex); -static LIST_HEAD(hybrid_tuner_instance_list); - -/*---------------------------------------------------------------------*/ - -static int tda18271_toggle_output(struct dvb_frontend *fe, int standby) -{ - struct tda18271_priv *priv = fe->tuner_priv; - - int ret = tda18271_set_standby_mode(fe, standby ? 1 : 0, - priv->output_opt & TDA18271_OUTPUT_LT_OFF ? 1 : 0, - priv->output_opt & TDA18271_OUTPUT_XT_OFF ? 1 : 0); - - if (tda_fail(ret)) - goto fail; - - tda_dbg("%s mode: xtal oscillator %s, slave tuner loop thru %s\n", - standby ? "standby" : "active", - priv->output_opt & TDA18271_OUTPUT_XT_OFF ? "off" : "on", - priv->output_opt & TDA18271_OUTPUT_LT_OFF ? "off" : "on"); -fail: - return ret; -} - -/*---------------------------------------------------------------------*/ - -static inline int charge_pump_source(struct dvb_frontend *fe, int force) -{ - struct tda18271_priv *priv = fe->tuner_priv; - return tda18271_charge_pump_source(fe, - (priv->role == TDA18271_SLAVE) ? - TDA18271_CAL_PLL : - TDA18271_MAIN_PLL, force); -} - -static inline void tda18271_set_if_notch(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - - switch (priv->mode) { - case TDA18271_ANALOG: - regs[R_MPD] &= ~0x80; /* IF notch = 0 */ - break; - case TDA18271_DIGITAL: - regs[R_MPD] |= 0x80; /* IF notch = 1 */ - break; - } -} - -static int tda18271_channel_configuration(struct dvb_frontend *fe, - struct tda18271_std_map_item *map, - u32 freq, u32 bw) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - int ret; - u32 N; - - /* update TV broadcast parameters */ - - /* set standard */ - regs[R_EP3] &= ~0x1f; /* clear std bits */ - regs[R_EP3] |= (map->agc_mode << 3) | map->std; - - if (priv->id == TDA18271HDC2) { - /* set rfagc to high speed mode */ - regs[R_EP3] &= ~0x04; - } - - /* set cal mode to normal */ - regs[R_EP4] &= ~0x03; - - /* update IF output level */ - regs[R_EP4] &= ~0x1c; /* clear if level bits */ - regs[R_EP4] |= (map->if_lvl << 2); - - /* update FM_RFn */ - regs[R_EP4] &= ~0x80; - regs[R_EP4] |= map->fm_rfn << 7; - - /* update rf top / if top */ - regs[R_EB22] = 0x00; - regs[R_EB22] |= map->rfagc_top; - ret = tda18271_write_regs(fe, R_EB22, 1); - if (tda_fail(ret)) - goto fail; - - /* --------------------------------------------------------------- */ - - /* disable Power Level Indicator */ - regs[R_EP1] |= 0x40; - - /* make sure thermometer is off */ - regs[R_TM] &= ~0x10; - - /* frequency dependent parameters */ - - tda18271_calc_ir_measure(fe, &freq); - - tda18271_calc_bp_filter(fe, &freq); - - tda18271_calc_rf_band(fe, &freq); - - tda18271_calc_gain_taper(fe, &freq); - - /* --------------------------------------------------------------- */ - - /* dual tuner and agc1 extra configuration */ - - switch (priv->role) { - case TDA18271_MASTER: - regs[R_EB1] |= 0x04; /* main vco */ - break; - case TDA18271_SLAVE: - regs[R_EB1] &= ~0x04; /* cal vco */ - break; - } - - /* agc1 always active */ - regs[R_EB1] &= ~0x02; - - /* agc1 has priority on agc2 */ - regs[R_EB1] &= ~0x01; - - ret = tda18271_write_regs(fe, R_EB1, 1); - if (tda_fail(ret)) - goto fail; - - /* --------------------------------------------------------------- */ - - N = map->if_freq * 1000 + freq; - - switch (priv->role) { - case TDA18271_MASTER: - tda18271_calc_main_pll(fe, N); - tda18271_set_if_notch(fe); - tda18271_write_regs(fe, R_MPD, 4); - break; - case TDA18271_SLAVE: - tda18271_calc_cal_pll(fe, N); - tda18271_write_regs(fe, R_CPD, 4); - - regs[R_MPD] = regs[R_CPD] & 0x7f; - tda18271_set_if_notch(fe); - tda18271_write_regs(fe, R_MPD, 1); - break; - } - - ret = tda18271_write_regs(fe, R_TM, 7); - if (tda_fail(ret)) - goto fail; - - /* force charge pump source */ - charge_pump_source(fe, 1); - - msleep(1); - - /* return pll to normal operation */ - charge_pump_source(fe, 0); - - msleep(20); - - if (priv->id == TDA18271HDC2) { - /* set rfagc to normal speed mode */ - if (map->fm_rfn) - regs[R_EP3] &= ~0x04; - else - regs[R_EP3] |= 0x04; - ret = tda18271_write_regs(fe, R_EP3, 1); - } -fail: - return ret; -} - -static int tda18271_read_thermometer(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - int tm; - - /* switch thermometer on */ - regs[R_TM] |= 0x10; - tda18271_write_regs(fe, R_TM, 1); - - /* read thermometer info */ - tda18271_read_regs(fe); - - if ((((regs[R_TM] & 0x0f) == 0x00) && ((regs[R_TM] & 0x20) == 0x20)) || - (((regs[R_TM] & 0x0f) == 0x08) && ((regs[R_TM] & 0x20) == 0x00))) { - - if ((regs[R_TM] & 0x20) == 0x20) - regs[R_TM] &= ~0x20; - else - regs[R_TM] |= 0x20; - - tda18271_write_regs(fe, R_TM, 1); - - msleep(10); /* temperature sensing */ - - /* read thermometer info */ - tda18271_read_regs(fe); - } - - tm = tda18271_lookup_thermometer(fe); - - /* switch thermometer off */ - regs[R_TM] &= ~0x10; - tda18271_write_regs(fe, R_TM, 1); - - /* set CAL mode to normal */ - regs[R_EP4] &= ~0x03; - tda18271_write_regs(fe, R_EP4, 1); - - return tm; -} - -/* ------------------------------------------------------------------ */ - -static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, - u32 freq) -{ - struct tda18271_priv *priv = fe->tuner_priv; - struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; - unsigned char *regs = priv->tda18271_regs; - int i, ret; - u8 tm_current, dc_over_dt, rf_tab; - s32 rfcal_comp, approx; - - /* power up */ - ret = tda18271_set_standby_mode(fe, 0, 0, 0); - if (tda_fail(ret)) - goto fail; - - /* read die current temperature */ - tm_current = tda18271_read_thermometer(fe); - - /* frequency dependent parameters */ - - tda18271_calc_rf_cal(fe, &freq); - rf_tab = regs[R_EB14]; - - i = tda18271_lookup_rf_band(fe, &freq, NULL); - if (tda_fail(i)) - return i; - - if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) { - approx = map[i].rf_a1 * (s32)(freq / 1000 - map[i].rf1) + - map[i].rf_b1 + rf_tab; - } else { - approx = map[i].rf_a2 * (s32)(freq / 1000 - map[i].rf2) + - map[i].rf_b2 + rf_tab; - } - - if (approx < 0) - approx = 0; - if (approx > 255) - approx = 255; - - tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt); - - /* calculate temperature compensation */ - rfcal_comp = dc_over_dt * (s32)(tm_current - priv->tm_rfcal) / 1000; - - regs[R_EB14] = (unsigned char)(approx + rfcal_comp); - ret = tda18271_write_regs(fe, R_EB14, 1); -fail: - return ret; -} - -static int tda18271_por(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - int ret; - - /* power up detector 1 */ - regs[R_EB12] &= ~0x20; - ret = tda18271_write_regs(fe, R_EB12, 1); - if (tda_fail(ret)) - goto fail; - - regs[R_EB18] &= ~0x80; /* turn agc1 loop on */ - regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ - ret = tda18271_write_regs(fe, R_EB18, 1); - if (tda_fail(ret)) - goto fail; - - regs[R_EB21] |= 0x03; /* set agc2_gain to -6 dB */ - - /* POR mode */ - ret = tda18271_set_standby_mode(fe, 1, 0, 0); - if (tda_fail(ret)) - goto fail; - - /* disable 1.5 MHz low pass filter */ - regs[R_EB23] &= ~0x04; /* forcelp_fc2_en = 0 */ - regs[R_EB23] &= ~0x02; /* XXX: lp_fc[2] = 0 */ - ret = tda18271_write_regs(fe, R_EB21, 3); -fail: - return ret; -} - -static int tda18271_calibrate_rf(struct dvb_frontend *fe, u32 freq) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u32 N; - - /* set CAL mode to normal */ - regs[R_EP4] &= ~0x03; - tda18271_write_regs(fe, R_EP4, 1); - - /* switch off agc1 */ - regs[R_EP3] |= 0x40; /* sm_lt = 1 */ - - regs[R_EB18] |= 0x03; /* set agc1_gain to 15 dB */ - tda18271_write_regs(fe, R_EB18, 1); - - /* frequency dependent parameters */ - - tda18271_calc_bp_filter(fe, &freq); - tda18271_calc_gain_taper(fe, &freq); - tda18271_calc_rf_band(fe, &freq); - tda18271_calc_km(fe, &freq); - - tda18271_write_regs(fe, R_EP1, 3); - tda18271_write_regs(fe, R_EB13, 1); - - /* main pll charge pump source */ - tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 1); - - /* cal pll charge pump source */ - tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 1); - - /* force dcdc converter to 0 V */ - regs[R_EB14] = 0x00; - tda18271_write_regs(fe, R_EB14, 1); - - /* disable plls lock */ - regs[R_EB20] &= ~0x20; - tda18271_write_regs(fe, R_EB20, 1); - - /* set CAL mode to RF tracking filter calibration */ - regs[R_EP4] |= 0x03; - tda18271_write_regs(fe, R_EP4, 2); - - /* --------------------------------------------------------------- */ - - /* set the internal calibration signal */ - N = freq; - - tda18271_calc_cal_pll(fe, N); - tda18271_write_regs(fe, R_CPD, 4); - - /* downconvert internal calibration */ - N += 1000000; - - tda18271_calc_main_pll(fe, N); - tda18271_write_regs(fe, R_MPD, 4); - - msleep(5); - - tda18271_write_regs(fe, R_EP2, 1); - tda18271_write_regs(fe, R_EP1, 1); - tda18271_write_regs(fe, R_EP2, 1); - tda18271_write_regs(fe, R_EP1, 1); - - /* --------------------------------------------------------------- */ - - /* normal operation for the main pll */ - tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 0); - - /* normal operation for the cal pll */ - tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 0); - - msleep(10); /* plls locking */ - - /* launch the rf tracking filters calibration */ - regs[R_EB20] |= 0x20; - tda18271_write_regs(fe, R_EB20, 1); - - msleep(60); /* calibration */ - - /* --------------------------------------------------------------- */ - - /* set CAL mode to normal */ - regs[R_EP4] &= ~0x03; - - /* switch on agc1 */ - regs[R_EP3] &= ~0x40; /* sm_lt = 0 */ - - regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ - tda18271_write_regs(fe, R_EB18, 1); - - tda18271_write_regs(fe, R_EP3, 2); - - /* synchronization */ - tda18271_write_regs(fe, R_EP1, 1); - - /* get calibration result */ - tda18271_read_extended(fe); - - return regs[R_EB14]; -} - -static int tda18271_powerscan(struct dvb_frontend *fe, - u32 *freq_in, u32 *freq_out) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - int sgn, bcal, count, wait, ret; - u8 cid_target; - u16 count_limit; - u32 freq; - - freq = *freq_in; - - tda18271_calc_rf_band(fe, &freq); - tda18271_calc_rf_cal(fe, &freq); - tda18271_calc_gain_taper(fe, &freq); - tda18271_lookup_cid_target(fe, &freq, &cid_target, &count_limit); - - tda18271_write_regs(fe, R_EP2, 1); - tda18271_write_regs(fe, R_EB14, 1); - - /* downconvert frequency */ - freq += 1000000; - - tda18271_calc_main_pll(fe, freq); - tda18271_write_regs(fe, R_MPD, 4); - - msleep(5); /* pll locking */ - - /* detection mode */ - regs[R_EP4] &= ~0x03; - regs[R_EP4] |= 0x01; - tda18271_write_regs(fe, R_EP4, 1); - - /* launch power detection measurement */ - tda18271_write_regs(fe, R_EP2, 1); - - /* read power detection info, stored in EB10 */ - ret = tda18271_read_extended(fe); - if (tda_fail(ret)) - return ret; - - /* algorithm initialization */ - sgn = 1; - *freq_out = *freq_in; - bcal = 0; - count = 0; - wait = false; - - while ((regs[R_EB10] & 0x3f) < cid_target) { - /* downconvert updated freq to 1 MHz */ - freq = *freq_in + (sgn * count) + 1000000; - - tda18271_calc_main_pll(fe, freq); - tda18271_write_regs(fe, R_MPD, 4); - - if (wait) { - msleep(5); /* pll locking */ - wait = false; - } else - udelay(100); /* pll locking */ - - /* launch power detection measurement */ - tda18271_write_regs(fe, R_EP2, 1); - - /* read power detection info, stored in EB10 */ - ret = tda18271_read_extended(fe); - if (tda_fail(ret)) - return ret; - - count += 200; - - if (count <= count_limit) - continue; - - if (sgn <= 0) - break; - - sgn = -1 * sgn; - count = 200; - wait = true; - } - - if ((regs[R_EB10] & 0x3f) >= cid_target) { - bcal = 1; - *freq_out = freq - 1000000; - } else - bcal = 0; - - tda_cal("bcal = %d, freq_in = %d, freq_out = %d (freq = %d)\n", - bcal, *freq_in, *freq_out, freq); - - return bcal; -} - -static int tda18271_powerscan_init(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - int ret; - - /* set standard to digital */ - regs[R_EP3] &= ~0x1f; /* clear std bits */ - regs[R_EP3] |= 0x12; - - /* set cal mode to normal */ - regs[R_EP4] &= ~0x03; - - /* update IF output level */ - regs[R_EP4] &= ~0x1c; /* clear if level bits */ - - ret = tda18271_write_regs(fe, R_EP3, 2); - if (tda_fail(ret)) - goto fail; - - regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ - ret = tda18271_write_regs(fe, R_EB18, 1); - if (tda_fail(ret)) - goto fail; - - regs[R_EB21] &= ~0x03; /* set agc2_gain to -15 dB */ - - /* 1.5 MHz low pass filter */ - regs[R_EB23] |= 0x04; /* forcelp_fc2_en = 1 */ - regs[R_EB23] |= 0x02; /* lp_fc[2] = 1 */ - - ret = tda18271_write_regs(fe, R_EB21, 3); -fail: - return ret; -} - -static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) -{ - struct tda18271_priv *priv = fe->tuner_priv; - struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; - unsigned char *regs = priv->tda18271_regs; - int bcal, rf, i; - s32 divisor, dividend; -#define RF1 0 -#define RF2 1 -#define RF3 2 - u32 rf_default[3]; - u32 rf_freq[3]; - s32 prog_cal[3]; - s32 prog_tab[3]; - - i = tda18271_lookup_rf_band(fe, &freq, NULL); - - if (tda_fail(i)) - return i; - - rf_default[RF1] = 1000 * map[i].rf1_def; - rf_default[RF2] = 1000 * map[i].rf2_def; - rf_default[RF3] = 1000 * map[i].rf3_def; - - for (rf = RF1; rf <= RF3; rf++) { - if (0 == rf_default[rf]) - return 0; - tda_cal("freq = %d, rf = %d\n", freq, rf); - - /* look for optimized calibration frequency */ - bcal = tda18271_powerscan(fe, &rf_default[rf], &rf_freq[rf]); - if (tda_fail(bcal)) - return bcal; - - tda18271_calc_rf_cal(fe, &rf_freq[rf]); - prog_tab[rf] = (s32)regs[R_EB14]; - - if (1 == bcal) - prog_cal[rf] = - (s32)tda18271_calibrate_rf(fe, rf_freq[rf]); - else - prog_cal[rf] = prog_tab[rf]; - - switch (rf) { - case RF1: - map[i].rf_a1 = 0; - map[i].rf_b1 = (prog_cal[RF1] - prog_tab[RF1]); - map[i].rf1 = rf_freq[RF1] / 1000; - break; - case RF2: - dividend = (prog_cal[RF2] - prog_tab[RF2] - - prog_cal[RF1] + prog_tab[RF1]); - divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000; - map[i].rf_a1 = (dividend / divisor); - map[i].rf2 = rf_freq[RF2] / 1000; - break; - case RF3: - dividend = (prog_cal[RF3] - prog_tab[RF3] - - prog_cal[RF2] + prog_tab[RF2]); - divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000; - map[i].rf_a2 = (dividend / divisor); - map[i].rf_b2 = (prog_cal[RF2] - prog_tab[RF2]); - map[i].rf3 = rf_freq[RF3] / 1000; - break; - default: - BUG(); - } - } - - return 0; -} - -static int tda18271_calc_rf_filter_curve(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned int i; - int ret; - - tda_info("tda18271: performing RF tracking filter calibration\n"); - - /* wait for die temperature stabilization */ - msleep(200); - - ret = tda18271_powerscan_init(fe); - if (tda_fail(ret)) - goto fail; - - /* rf band calibration */ - for (i = 0; priv->rf_cal_state[i].rfmax != 0; i++) { - ret = - tda18271_rf_tracking_filters_init(fe, 1000 * - priv->rf_cal_state[i].rfmax); - if (tda_fail(ret)) - goto fail; - } - - priv->tm_rfcal = tda18271_read_thermometer(fe); -fail: - return ret; -} - -/* ------------------------------------------------------------------ */ - -static int tda18271c2_rf_cal_init(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - int ret; - - /* test RF_CAL_OK to see if we need init */ - if ((regs[R_EP1] & 0x10) == 0) - priv->cal_initialized = false; - - if (priv->cal_initialized) - return 0; - - ret = tda18271_calc_rf_filter_curve(fe); - if (tda_fail(ret)) - goto fail; - - ret = tda18271_por(fe); - if (tda_fail(ret)) - goto fail; - - tda_info("tda18271: RF tracking filter calibration complete\n"); - - priv->cal_initialized = true; - goto end; -fail: - tda_info("tda18271: RF tracking filter calibration failed!\n"); -end: - return ret; -} - -static int tda18271c1_rf_tracking_filter_calibration(struct dvb_frontend *fe, - u32 freq, u32 bw) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - int ret; - u32 N = 0; - - /* calculate bp filter */ - tda18271_calc_bp_filter(fe, &freq); - tda18271_write_regs(fe, R_EP1, 1); - - regs[R_EB4] &= 0x07; - regs[R_EB4] |= 0x60; - tda18271_write_regs(fe, R_EB4, 1); - - regs[R_EB7] = 0x60; - tda18271_write_regs(fe, R_EB7, 1); - - regs[R_EB14] = 0x00; - tda18271_write_regs(fe, R_EB14, 1); - - regs[R_EB20] = 0xcc; - tda18271_write_regs(fe, R_EB20, 1); - - /* set cal mode to RF tracking filter calibration */ - regs[R_EP4] |= 0x03; - - /* calculate cal pll */ - - switch (priv->mode) { - case TDA18271_ANALOG: - N = freq - 1250000; - break; - case TDA18271_DIGITAL: - N = freq + bw / 2; - break; - } - - tda18271_calc_cal_pll(fe, N); - - /* calculate main pll */ - - switch (priv->mode) { - case TDA18271_ANALOG: - N = freq - 250000; - break; - case TDA18271_DIGITAL: - N = freq + bw / 2 + 1000000; - break; - } - - tda18271_calc_main_pll(fe, N); - - ret = tda18271_write_regs(fe, R_EP3, 11); - if (tda_fail(ret)) - return ret; - - msleep(5); /* RF tracking filter calibration initialization */ - - /* search for K,M,CO for RF calibration */ - tda18271_calc_km(fe, &freq); - tda18271_write_regs(fe, R_EB13, 1); - - /* search for rf band */ - tda18271_calc_rf_band(fe, &freq); - - /* search for gain taper */ - tda18271_calc_gain_taper(fe, &freq); - - tda18271_write_regs(fe, R_EP2, 1); - tda18271_write_regs(fe, R_EP1, 1); - tda18271_write_regs(fe, R_EP2, 1); - tda18271_write_regs(fe, R_EP1, 1); - - regs[R_EB4] &= 0x07; - regs[R_EB4] |= 0x40; - tda18271_write_regs(fe, R_EB4, 1); - - regs[R_EB7] = 0x40; - tda18271_write_regs(fe, R_EB7, 1); - msleep(10); /* pll locking */ - - regs[R_EB20] = 0xec; - tda18271_write_regs(fe, R_EB20, 1); - msleep(60); /* RF tracking filter calibration completion */ - - regs[R_EP4] &= ~0x03; /* set cal mode to normal */ - tda18271_write_regs(fe, R_EP4, 1); - - tda18271_write_regs(fe, R_EP1, 1); - - /* RF tracking filter correction for VHF_Low band */ - if (0 == tda18271_calc_rf_cal(fe, &freq)) - tda18271_write_regs(fe, R_EB14, 1); - - return 0; -} - -/* ------------------------------------------------------------------ */ - -static int tda18271_ir_cal_init(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - int ret; - - ret = tda18271_read_regs(fe); - if (tda_fail(ret)) - goto fail; - - /* test IR_CAL_OK to see if we need init */ - if ((regs[R_EP1] & 0x08) == 0) - ret = tda18271_init_regs(fe); -fail: - return ret; -} - -static int tda18271_init(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - int ret; - - mutex_lock(&priv->lock); - - /* full power up */ - ret = tda18271_set_standby_mode(fe, 0, 0, 0); - if (tda_fail(ret)) - goto fail; - - /* initialization */ - ret = tda18271_ir_cal_init(fe); - if (tda_fail(ret)) - goto fail; - - if (priv->id == TDA18271HDC2) - tda18271c2_rf_cal_init(fe); -fail: - mutex_unlock(&priv->lock); - - return ret; -} - -static int tda18271_sleep(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - int ret; - - mutex_lock(&priv->lock); - - /* enter standby mode, with required output features enabled */ - ret = tda18271_toggle_output(fe, 1); - - mutex_unlock(&priv->lock); - - return ret; -} - -/* ------------------------------------------------------------------ */ - -static int tda18271_agc(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - int ret = 0; - - switch (priv->config) { - case 0: - /* no external agc configuration required */ - if (tda18271_debug & DBG_ADV) - tda_dbg("no agc configuration provided\n"); - break; - case 3: - /* switch with GPIO of saa713x */ - tda_dbg("invoking callback\n"); - if (fe->callback) - ret = fe->callback(priv->i2c_props.adap->algo_data, - DVB_FRONTEND_COMPONENT_TUNER, - TDA18271_CALLBACK_CMD_AGC_ENABLE, - priv->mode); - break; - case 1: - case 2: - default: - /* n/a - currently not supported */ - tda_err("unsupported configuration: %d\n", priv->config); - ret = -EINVAL; - break; - } - return ret; -} - -static int tda18271_tune(struct dvb_frontend *fe, - struct tda18271_std_map_item *map, u32 freq, u32 bw) -{ - struct tda18271_priv *priv = fe->tuner_priv; - int ret; - - tda_dbg("freq = %d, ifc = %d, bw = %d, agc_mode = %d, std = %d\n", - freq, map->if_freq, bw, map->agc_mode, map->std); - - ret = tda18271_agc(fe); - if (tda_fail(ret)) - tda_warn("failed to configure agc\n"); - - ret = tda18271_init(fe); - if (tda_fail(ret)) - goto fail; - - mutex_lock(&priv->lock); - - switch (priv->id) { - case TDA18271HDC1: - tda18271c1_rf_tracking_filter_calibration(fe, freq, bw); - break; - case TDA18271HDC2: - tda18271c2_rf_tracking_filters_correction(fe, freq); - break; - } - ret = tda18271_channel_configuration(fe, map, freq, bw); - - mutex_unlock(&priv->lock); -fail: - return ret; -} - -/* ------------------------------------------------------------------ */ - -static int tda18271_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; - u32 freq = c->frequency; - struct tda18271_priv *priv = fe->tuner_priv; - struct tda18271_std_map *std_map = &priv->std; - struct tda18271_std_map_item *map; - int ret; - - priv->mode = TDA18271_DIGITAL; - - switch (delsys) { - case SYS_ATSC: - map = &std_map->atsc_6; - bw = 6000000; - break; - case SYS_ISDBT: - case SYS_DVBT: - case SYS_DVBT2: - if (bw <= 6000000) { - map = &std_map->dvbt_6; - } else if (bw <= 7000000) { - map = &std_map->dvbt_7; - } else { - map = &std_map->dvbt_8; - } - break; - case SYS_DVBC_ANNEX_B: - bw = 6000000; - /* falltrough */ - case SYS_DVBC_ANNEX_A: - case SYS_DVBC_ANNEX_C: - if (bw <= 6000000) { - map = &std_map->qam_6; - } else if (bw <= 7000000) { - map = &std_map->qam_7; - } else { - map = &std_map->qam_8; - } - break; - default: - tda_warn("modulation type not supported!\n"); - return -EINVAL; - } - - /* When tuning digital, the analog demod must be tri-stated */ - if (fe->ops.analog_ops.standby) - fe->ops.analog_ops.standby(fe); - - ret = tda18271_tune(fe, map, freq, bw); - - if (tda_fail(ret)) - goto fail; - - priv->if_freq = map->if_freq; - priv->frequency = freq; - priv->bandwidth = bw; -fail: - return ret; -} - -static int tda18271_set_analog_params(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct tda18271_priv *priv = fe->tuner_priv; - struct tda18271_std_map *std_map = &priv->std; - struct tda18271_std_map_item *map; - char *mode; - int ret; - u32 freq = params->frequency * 125 * - ((params->mode == V4L2_TUNER_RADIO) ? 1 : 1000) / 2; - - priv->mode = TDA18271_ANALOG; - - if (params->mode == V4L2_TUNER_RADIO) { - map = &std_map->fm_radio; - mode = "fm"; - } else if (params->std & V4L2_STD_MN) { - map = &std_map->atv_mn; - mode = "MN"; - } else if (params->std & V4L2_STD_B) { - map = &std_map->atv_b; - mode = "B"; - } else if (params->std & V4L2_STD_GH) { - map = &std_map->atv_gh; - mode = "GH"; - } else if (params->std & V4L2_STD_PAL_I) { - map = &std_map->atv_i; - mode = "I"; - } else if (params->std & V4L2_STD_DK) { - map = &std_map->atv_dk; - mode = "DK"; - } else if (params->std & V4L2_STD_SECAM_L) { - map = &std_map->atv_l; - mode = "L"; - } else if (params->std & V4L2_STD_SECAM_LC) { - map = &std_map->atv_lc; - mode = "L'"; - } else { - map = &std_map->atv_i; - mode = "xx"; - } - - tda_dbg("setting tda18271 to system %s\n", mode); - - ret = tda18271_tune(fe, map, freq, 0); - - if (tda_fail(ret)) - goto fail; - - priv->if_freq = map->if_freq; - priv->frequency = freq; - priv->bandwidth = 0; -fail: - return ret; -} - -static int tda18271_release(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - - mutex_lock(&tda18271_list_mutex); - - if (priv) - hybrid_tuner_release_state(priv); - - mutex_unlock(&tda18271_list_mutex); - - fe->tuner_priv = NULL; - - return 0; -} - -static int tda18271_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct tda18271_priv *priv = fe->tuner_priv; - *frequency = priv->frequency; - return 0; -} - -static int tda18271_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) -{ - struct tda18271_priv *priv = fe->tuner_priv; - *bandwidth = priv->bandwidth; - return 0; -} - -static int tda18271_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct tda18271_priv *priv = fe->tuner_priv; - *frequency = (u32)priv->if_freq * 1000; - return 0; -} - -/* ------------------------------------------------------------------ */ - -#define tda18271_update_std(std_cfg, name) do { \ - if (map->std_cfg.if_freq + \ - map->std_cfg.agc_mode + map->std_cfg.std + \ - map->std_cfg.if_lvl + map->std_cfg.rfagc_top > 0) { \ - tda_dbg("Using custom std config for %s\n", name); \ - memcpy(&std->std_cfg, &map->std_cfg, \ - sizeof(struct tda18271_std_map_item)); \ - } } while (0) - -#define tda18271_dump_std_item(std_cfg, name) do { \ - tda_dbg("(%s) if_freq = %d, agc_mode = %d, std = %d, " \ - "if_lvl = %d, rfagc_top = 0x%02x\n", \ - name, std->std_cfg.if_freq, \ - std->std_cfg.agc_mode, std->std_cfg.std, \ - std->std_cfg.if_lvl, std->std_cfg.rfagc_top); \ - } while (0) - -static int tda18271_dump_std_map(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - struct tda18271_std_map *std = &priv->std; - - tda_dbg("========== STANDARD MAP SETTINGS ==========\n"); - tda18271_dump_std_item(fm_radio, " fm "); - tda18271_dump_std_item(atv_b, "atv b "); - tda18271_dump_std_item(atv_dk, "atv dk"); - tda18271_dump_std_item(atv_gh, "atv gh"); - tda18271_dump_std_item(atv_i, "atv i "); - tda18271_dump_std_item(atv_l, "atv l "); - tda18271_dump_std_item(atv_lc, "atv l'"); - tda18271_dump_std_item(atv_mn, "atv mn"); - tda18271_dump_std_item(atsc_6, "atsc 6"); - tda18271_dump_std_item(dvbt_6, "dvbt 6"); - tda18271_dump_std_item(dvbt_7, "dvbt 7"); - tda18271_dump_std_item(dvbt_8, "dvbt 8"); - tda18271_dump_std_item(qam_6, "qam 6 "); - tda18271_dump_std_item(qam_8, "qam 8 "); - - return 0; -} - -static int tda18271_update_std_map(struct dvb_frontend *fe, - struct tda18271_std_map *map) -{ - struct tda18271_priv *priv = fe->tuner_priv; - struct tda18271_std_map *std = &priv->std; - - if (!map) - return -EINVAL; - - tda18271_update_std(fm_radio, "fm"); - tda18271_update_std(atv_b, "atv b"); - tda18271_update_std(atv_dk, "atv dk"); - tda18271_update_std(atv_gh, "atv gh"); - tda18271_update_std(atv_i, "atv i"); - tda18271_update_std(atv_l, "atv l"); - tda18271_update_std(atv_lc, "atv l'"); - tda18271_update_std(atv_mn, "atv mn"); - tda18271_update_std(atsc_6, "atsc 6"); - tda18271_update_std(dvbt_6, "dvbt 6"); - tda18271_update_std(dvbt_7, "dvbt 7"); - tda18271_update_std(dvbt_8, "dvbt 8"); - tda18271_update_std(qam_6, "qam 6"); - tda18271_update_std(qam_8, "qam 8"); - - return 0; -} - -static int tda18271_get_id(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - char *name; - - mutex_lock(&priv->lock); - tda18271_read_regs(fe); - mutex_unlock(&priv->lock); - - switch (regs[R_ID] & 0x7f) { - case 3: - name = "TDA18271HD/C1"; - priv->id = TDA18271HDC1; - break; - case 4: - name = "TDA18271HD/C2"; - priv->id = TDA18271HDC2; - break; - default: - tda_info("Unknown device (%i) detected @ %d-%04x, device not supported.\n", - regs[R_ID], i2c_adapter_id(priv->i2c_props.adap), - priv->i2c_props.addr); - return -EINVAL; - } - - tda_info("%s detected @ %d-%04x\n", name, - i2c_adapter_id(priv->i2c_props.adap), priv->i2c_props.addr); - - return 0; -} - -static int tda18271_setup_configuration(struct dvb_frontend *fe, - struct tda18271_config *cfg) -{ - struct tda18271_priv *priv = fe->tuner_priv; - - priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; - priv->role = (cfg) ? cfg->role : TDA18271_MASTER; - priv->config = (cfg) ? cfg->config : 0; - priv->small_i2c = (cfg) ? - cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT; - priv->output_opt = (cfg) ? - cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON; - - return 0; -} - -static inline int tda18271_need_cal_on_startup(struct tda18271_config *cfg) -{ - /* tda18271_cal_on_startup == -1 when cal module option is unset */ - return ((tda18271_cal_on_startup == -1) ? - /* honor configuration setting */ - ((cfg) && (cfg->rf_cal_on_startup)) : - /* module option overrides configuration setting */ - (tda18271_cal_on_startup)) ? 1 : 0; -} - -static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg) -{ - struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg; - - tda18271_setup_configuration(fe, cfg); - - if (tda18271_need_cal_on_startup(cfg)) - tda18271_init(fe); - - /* override default std map with values in config struct */ - if ((cfg) && (cfg->std_map)) - tda18271_update_std_map(fe, cfg->std_map); - - return 0; -} - -static const struct dvb_tuner_ops tda18271_tuner_ops = { - .info = { - .name = "NXP TDA18271HD", - .frequency_min = 45000000, - .frequency_max = 864000000, - .frequency_step = 62500 - }, - .init = tda18271_init, - .sleep = tda18271_sleep, - .set_params = tda18271_set_params, - .set_analog_params = tda18271_set_analog_params, - .release = tda18271_release, - .set_config = tda18271_set_config, - .get_frequency = tda18271_get_frequency, - .get_bandwidth = tda18271_get_bandwidth, - .get_if_frequency = tda18271_get_if_frequency, -}; - -struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, - struct i2c_adapter *i2c, - struct tda18271_config *cfg) -{ - struct tda18271_priv *priv = NULL; - int instance, ret; - - mutex_lock(&tda18271_list_mutex); - - instance = hybrid_tuner_request_state(struct tda18271_priv, priv, - hybrid_tuner_instance_list, - i2c, addr, "tda18271"); - switch (instance) { - case 0: - goto fail; - case 1: - /* new tuner instance */ - fe->tuner_priv = priv; - - tda18271_setup_configuration(fe, cfg); - - priv->cal_initialized = false; - mutex_init(&priv->lock); - - ret = tda18271_get_id(fe); - if (tda_fail(ret)) - goto fail; - - ret = tda18271_assign_map_layout(fe); - if (tda_fail(ret)) - goto fail; - - mutex_lock(&priv->lock); - tda18271_init_regs(fe); - - if ((tda18271_need_cal_on_startup(cfg)) && - (priv->id == TDA18271HDC2)) - tda18271c2_rf_cal_init(fe); - - mutex_unlock(&priv->lock); - break; - default: - /* existing tuner instance */ - fe->tuner_priv = priv; - - /* allow dvb driver to override configuration settings */ - if (cfg) { - if (cfg->gate != TDA18271_GATE_ANALOG) - priv->gate = cfg->gate; - if (cfg->role) - priv->role = cfg->role; - if (cfg->config) - priv->config = cfg->config; - if (cfg->small_i2c) - priv->small_i2c = cfg->small_i2c; - if (cfg->output_opt) - priv->output_opt = cfg->output_opt; - if (cfg->std_map) - tda18271_update_std_map(fe, cfg->std_map); - } - if (tda18271_need_cal_on_startup(cfg)) - tda18271_init(fe); - break; - } - - /* override default std map with values in config struct */ - if ((cfg) && (cfg->std_map)) - tda18271_update_std_map(fe, cfg->std_map); - - mutex_unlock(&tda18271_list_mutex); - - memcpy(&fe->ops.tuner_ops, &tda18271_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - if (tda18271_debug & (DBG_MAP | DBG_ADV)) - tda18271_dump_std_map(fe); - - return fe; -fail: - mutex_unlock(&tda18271_list_mutex); - - tda18271_release(fe); - return NULL; -} -EXPORT_SYMBOL_GPL(tda18271_attach); -MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver"); -MODULE_AUTHOR("Michael Krufky "); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.4"); - -/* - * 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/tda18271-maps.c b/drivers/media/common/tuners/tda18271-maps.c deleted file mode 100644 index fb881c667c94..000000000000 --- a/drivers/media/common/tuners/tda18271-maps.c +++ /dev/null @@ -1,1317 +0,0 @@ -/* - tda18271-maps.c - driver for the Philips / NXP TDA18271 silicon tuner - - Copyright (C) 2007, 2008 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. -*/ - -#include "tda18271-priv.h" - -struct tda18271_pll_map { - u32 lomax; - u8 pd; /* post div */ - u8 d; /* div */ -}; - -struct tda18271_map { - u32 rfmax; - u8 val; -}; - -/*---------------------------------------------------------------------*/ - -static struct tda18271_pll_map tda18271c1_main_pll[] = { - { .lomax = 32000, .pd = 0x5f, .d = 0xf0 }, - { .lomax = 35000, .pd = 0x5e, .d = 0xe0 }, - { .lomax = 37000, .pd = 0x5d, .d = 0xd0 }, - { .lomax = 41000, .pd = 0x5c, .d = 0xc0 }, - { .lomax = 44000, .pd = 0x5b, .d = 0xb0 }, - { .lomax = 49000, .pd = 0x5a, .d = 0xa0 }, - { .lomax = 54000, .pd = 0x59, .d = 0x90 }, - { .lomax = 61000, .pd = 0x58, .d = 0x80 }, - { .lomax = 65000, .pd = 0x4f, .d = 0x78 }, - { .lomax = 70000, .pd = 0x4e, .d = 0x70 }, - { .lomax = 75000, .pd = 0x4d, .d = 0x68 }, - { .lomax = 82000, .pd = 0x4c, .d = 0x60 }, - { .lomax = 89000, .pd = 0x4b, .d = 0x58 }, - { .lomax = 98000, .pd = 0x4a, .d = 0x50 }, - { .lomax = 109000, .pd = 0x49, .d = 0x48 }, - { .lomax = 123000, .pd = 0x48, .d = 0x40 }, - { .lomax = 131000, .pd = 0x3f, .d = 0x3c }, - { .lomax = 141000, .pd = 0x3e, .d = 0x38 }, - { .lomax = 151000, .pd = 0x3d, .d = 0x34 }, - { .lomax = 164000, .pd = 0x3c, .d = 0x30 }, - { .lomax = 179000, .pd = 0x3b, .d = 0x2c }, - { .lomax = 197000, .pd = 0x3a, .d = 0x28 }, - { .lomax = 219000, .pd = 0x39, .d = 0x24 }, - { .lomax = 246000, .pd = 0x38, .d = 0x20 }, - { .lomax = 263000, .pd = 0x2f, .d = 0x1e }, - { .lomax = 282000, .pd = 0x2e, .d = 0x1c }, - { .lomax = 303000, .pd = 0x2d, .d = 0x1a }, - { .lomax = 329000, .pd = 0x2c, .d = 0x18 }, - { .lomax = 359000, .pd = 0x2b, .d = 0x16 }, - { .lomax = 395000, .pd = 0x2a, .d = 0x14 }, - { .lomax = 438000, .pd = 0x29, .d = 0x12 }, - { .lomax = 493000, .pd = 0x28, .d = 0x10 }, - { .lomax = 526000, .pd = 0x1f, .d = 0x0f }, - { .lomax = 564000, .pd = 0x1e, .d = 0x0e }, - { .lomax = 607000, .pd = 0x1d, .d = 0x0d }, - { .lomax = 658000, .pd = 0x1c, .d = 0x0c }, - { .lomax = 718000, .pd = 0x1b, .d = 0x0b }, - { .lomax = 790000, .pd = 0x1a, .d = 0x0a }, - { .lomax = 877000, .pd = 0x19, .d = 0x09 }, - { .lomax = 987000, .pd = 0x18, .d = 0x08 }, - { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ -}; - -static struct tda18271_pll_map tda18271c2_main_pll[] = { - { .lomax = 33125, .pd = 0x57, .d = 0xf0 }, - { .lomax = 35500, .pd = 0x56, .d = 0xe0 }, - { .lomax = 38188, .pd = 0x55, .d = 0xd0 }, - { .lomax = 41375, .pd = 0x54, .d = 0xc0 }, - { .lomax = 45125, .pd = 0x53, .d = 0xb0 }, - { .lomax = 49688, .pd = 0x52, .d = 0xa0 }, - { .lomax = 55188, .pd = 0x51, .d = 0x90 }, - { .lomax = 62125, .pd = 0x50, .d = 0x80 }, - { .lomax = 66250, .pd = 0x47, .d = 0x78 }, - { .lomax = 71000, .pd = 0x46, .d = 0x70 }, - { .lomax = 76375, .pd = 0x45, .d = 0x68 }, - { .lomax = 82750, .pd = 0x44, .d = 0x60 }, - { .lomax = 90250, .pd = 0x43, .d = 0x58 }, - { .lomax = 99375, .pd = 0x42, .d = 0x50 }, - { .lomax = 110375, .pd = 0x41, .d = 0x48 }, - { .lomax = 124250, .pd = 0x40, .d = 0x40 }, - { .lomax = 132500, .pd = 0x37, .d = 0x3c }, - { .lomax = 142000, .pd = 0x36, .d = 0x38 }, - { .lomax = 152750, .pd = 0x35, .d = 0x34 }, - { .lomax = 165500, .pd = 0x34, .d = 0x30 }, - { .lomax = 180500, .pd = 0x33, .d = 0x2c }, - { .lomax = 198750, .pd = 0x32, .d = 0x28 }, - { .lomax = 220750, .pd = 0x31, .d = 0x24 }, - { .lomax = 248500, .pd = 0x30, .d = 0x20 }, - { .lomax = 265000, .pd = 0x27, .d = 0x1e }, - { .lomax = 284000, .pd = 0x26, .d = 0x1c }, - { .lomax = 305500, .pd = 0x25, .d = 0x1a }, - { .lomax = 331000, .pd = 0x24, .d = 0x18 }, - { .lomax = 361000, .pd = 0x23, .d = 0x16 }, - { .lomax = 397500, .pd = 0x22, .d = 0x14 }, - { .lomax = 441500, .pd = 0x21, .d = 0x12 }, - { .lomax = 497000, .pd = 0x20, .d = 0x10 }, - { .lomax = 530000, .pd = 0x17, .d = 0x0f }, - { .lomax = 568000, .pd = 0x16, .d = 0x0e }, - { .lomax = 611000, .pd = 0x15, .d = 0x0d }, - { .lomax = 662000, .pd = 0x14, .d = 0x0c }, - { .lomax = 722000, .pd = 0x13, .d = 0x0b }, - { .lomax = 795000, .pd = 0x12, .d = 0x0a }, - { .lomax = 883000, .pd = 0x11, .d = 0x09 }, - { .lomax = 994000, .pd = 0x10, .d = 0x08 }, - { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ -}; - -static struct tda18271_pll_map tda18271c1_cal_pll[] = { - { .lomax = 33000, .pd = 0xdd, .d = 0xd0 }, - { .lomax = 36000, .pd = 0xdc, .d = 0xc0 }, - { .lomax = 40000, .pd = 0xdb, .d = 0xb0 }, - { .lomax = 44000, .pd = 0xda, .d = 0xa0 }, - { .lomax = 49000, .pd = 0xd9, .d = 0x90 }, - { .lomax = 55000, .pd = 0xd8, .d = 0x80 }, - { .lomax = 63000, .pd = 0xd3, .d = 0x70 }, - { .lomax = 67000, .pd = 0xcd, .d = 0x68 }, - { .lomax = 73000, .pd = 0xcc, .d = 0x60 }, - { .lomax = 80000, .pd = 0xcb, .d = 0x58 }, - { .lomax = 88000, .pd = 0xca, .d = 0x50 }, - { .lomax = 98000, .pd = 0xc9, .d = 0x48 }, - { .lomax = 110000, .pd = 0xc8, .d = 0x40 }, - { .lomax = 126000, .pd = 0xc3, .d = 0x38 }, - { .lomax = 135000, .pd = 0xbd, .d = 0x34 }, - { .lomax = 147000, .pd = 0xbc, .d = 0x30 }, - { .lomax = 160000, .pd = 0xbb, .d = 0x2c }, - { .lomax = 176000, .pd = 0xba, .d = 0x28 }, - { .lomax = 196000, .pd = 0xb9, .d = 0x24 }, - { .lomax = 220000, .pd = 0xb8, .d = 0x20 }, - { .lomax = 252000, .pd = 0xb3, .d = 0x1c }, - { .lomax = 271000, .pd = 0xad, .d = 0x1a }, - { .lomax = 294000, .pd = 0xac, .d = 0x18 }, - { .lomax = 321000, .pd = 0xab, .d = 0x16 }, - { .lomax = 353000, .pd = 0xaa, .d = 0x14 }, - { .lomax = 392000, .pd = 0xa9, .d = 0x12 }, - { .lomax = 441000, .pd = 0xa8, .d = 0x10 }, - { .lomax = 505000, .pd = 0xa3, .d = 0x0e }, - { .lomax = 543000, .pd = 0x9d, .d = 0x0d }, - { .lomax = 589000, .pd = 0x9c, .d = 0x0c }, - { .lomax = 642000, .pd = 0x9b, .d = 0x0b }, - { .lomax = 707000, .pd = 0x9a, .d = 0x0a }, - { .lomax = 785000, .pd = 0x99, .d = 0x09 }, - { .lomax = 883000, .pd = 0x98, .d = 0x08 }, - { .lomax = 1010000, .pd = 0x93, .d = 0x07 }, - { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ -}; - -static struct tda18271_pll_map tda18271c2_cal_pll[] = { - { .lomax = 33813, .pd = 0xdd, .d = 0xd0 }, - { .lomax = 36625, .pd = 0xdc, .d = 0xc0 }, - { .lomax = 39938, .pd = 0xdb, .d = 0xb0 }, - { .lomax = 43938, .pd = 0xda, .d = 0xa0 }, - { .lomax = 48813, .pd = 0xd9, .d = 0x90 }, - { .lomax = 54938, .pd = 0xd8, .d = 0x80 }, - { .lomax = 62813, .pd = 0xd3, .d = 0x70 }, - { .lomax = 67625, .pd = 0xcd, .d = 0x68 }, - { .lomax = 73250, .pd = 0xcc, .d = 0x60 }, - { .lomax = 79875, .pd = 0xcb, .d = 0x58 }, - { .lomax = 87875, .pd = 0xca, .d = 0x50 }, - { .lomax = 97625, .pd = 0xc9, .d = 0x48 }, - { .lomax = 109875, .pd = 0xc8, .d = 0x40 }, - { .lomax = 125625, .pd = 0xc3, .d = 0x38 }, - { .lomax = 135250, .pd = 0xbd, .d = 0x34 }, - { .lomax = 146500, .pd = 0xbc, .d = 0x30 }, - { .lomax = 159750, .pd = 0xbb, .d = 0x2c }, - { .lomax = 175750, .pd = 0xba, .d = 0x28 }, - { .lomax = 195250, .pd = 0xb9, .d = 0x24 }, - { .lomax = 219750, .pd = 0xb8, .d = 0x20 }, - { .lomax = 251250, .pd = 0xb3, .d = 0x1c }, - { .lomax = 270500, .pd = 0xad, .d = 0x1a }, - { .lomax = 293000, .pd = 0xac, .d = 0x18 }, - { .lomax = 319500, .pd = 0xab, .d = 0x16 }, - { .lomax = 351500, .pd = 0xaa, .d = 0x14 }, - { .lomax = 390500, .pd = 0xa9, .d = 0x12 }, - { .lomax = 439500, .pd = 0xa8, .d = 0x10 }, - { .lomax = 502500, .pd = 0xa3, .d = 0x0e }, - { .lomax = 541000, .pd = 0x9d, .d = 0x0d }, - { .lomax = 586000, .pd = 0x9c, .d = 0x0c }, - { .lomax = 639000, .pd = 0x9b, .d = 0x0b }, - { .lomax = 703000, .pd = 0x9a, .d = 0x0a }, - { .lomax = 781000, .pd = 0x99, .d = 0x09 }, - { .lomax = 879000, .pd = 0x98, .d = 0x08 }, - { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ -}; - -static struct tda18271_map tda18271_bp_filter[] = { - { .rfmax = 62000, .val = 0x00 }, - { .rfmax = 84000, .val = 0x01 }, - { .rfmax = 100000, .val = 0x02 }, - { .rfmax = 140000, .val = 0x03 }, - { .rfmax = 170000, .val = 0x04 }, - { .rfmax = 180000, .val = 0x05 }, - { .rfmax = 865000, .val = 0x06 }, - { .rfmax = 0, .val = 0x00 }, /* end */ -}; - -static struct tda18271_map tda18271c1_km[] = { - { .rfmax = 61100, .val = 0x74 }, - { .rfmax = 350000, .val = 0x40 }, - { .rfmax = 720000, .val = 0x30 }, - { .rfmax = 865000, .val = 0x40 }, - { .rfmax = 0, .val = 0x00 }, /* end */ -}; - -static struct tda18271_map tda18271c2_km[] = { - { .rfmax = 47900, .val = 0x38 }, - { .rfmax = 61100, .val = 0x44 }, - { .rfmax = 350000, .val = 0x30 }, - { .rfmax = 720000, .val = 0x24 }, - { .rfmax = 865000, .val = 0x3c }, - { .rfmax = 0, .val = 0x00 }, /* end */ -}; - -static struct tda18271_map tda18271_rf_band[] = { - { .rfmax = 47900, .val = 0x00 }, - { .rfmax = 61100, .val = 0x01 }, - { .rfmax = 152600, .val = 0x02 }, - { .rfmax = 164700, .val = 0x03 }, - { .rfmax = 203500, .val = 0x04 }, - { .rfmax = 457800, .val = 0x05 }, - { .rfmax = 865000, .val = 0x06 }, - { .rfmax = 0, .val = 0x00 }, /* end */ -}; - -static struct tda18271_map tda18271_gain_taper[] = { - { .rfmax = 45400, .val = 0x1f }, - { .rfmax = 45800, .val = 0x1e }, - { .rfmax = 46200, .val = 0x1d }, - { .rfmax = 46700, .val = 0x1c }, - { .rfmax = 47100, .val = 0x1b }, - { .rfmax = 47500, .val = 0x1a }, - { .rfmax = 47900, .val = 0x19 }, - { .rfmax = 49600, .val = 0x17 }, - { .rfmax = 51200, .val = 0x16 }, - { .rfmax = 52900, .val = 0x15 }, - { .rfmax = 54500, .val = 0x14 }, - { .rfmax = 56200, .val = 0x13 }, - { .rfmax = 57800, .val = 0x12 }, - { .rfmax = 59500, .val = 0x11 }, - { .rfmax = 61100, .val = 0x10 }, - { .rfmax = 67600, .val = 0x0d }, - { .rfmax = 74200, .val = 0x0c }, - { .rfmax = 80700, .val = 0x0b }, - { .rfmax = 87200, .val = 0x0a }, - { .rfmax = 93800, .val = 0x09 }, - { .rfmax = 100300, .val = 0x08 }, - { .rfmax = 106900, .val = 0x07 }, - { .rfmax = 113400, .val = 0x06 }, - { .rfmax = 119900, .val = 0x05 }, - { .rfmax = 126500, .val = 0x04 }, - { .rfmax = 133000, .val = 0x03 }, - { .rfmax = 139500, .val = 0x02 }, - { .rfmax = 146100, .val = 0x01 }, - { .rfmax = 152600, .val = 0x00 }, - { .rfmax = 154300, .val = 0x1f }, - { .rfmax = 156100, .val = 0x1e }, - { .rfmax = 157800, .val = 0x1d }, - { .rfmax = 159500, .val = 0x1c }, - { .rfmax = 161200, .val = 0x1b }, - { .rfmax = 163000, .val = 0x1a }, - { .rfmax = 164700, .val = 0x19 }, - { .rfmax = 170200, .val = 0x17 }, - { .rfmax = 175800, .val = 0x16 }, - { .rfmax = 181300, .val = 0x15 }, - { .rfmax = 186900, .val = 0x14 }, - { .rfmax = 192400, .val = 0x13 }, - { .rfmax = 198000, .val = 0x12 }, - { .rfmax = 203500, .val = 0x11 }, - { .rfmax = 216200, .val = 0x14 }, - { .rfmax = 228900, .val = 0x13 }, - { .rfmax = 241600, .val = 0x12 }, - { .rfmax = 254400, .val = 0x11 }, - { .rfmax = 267100, .val = 0x10 }, - { .rfmax = 279800, .val = 0x0f }, - { .rfmax = 292500, .val = 0x0e }, - { .rfmax = 305200, .val = 0x0d }, - { .rfmax = 317900, .val = 0x0c }, - { .rfmax = 330700, .val = 0x0b }, - { .rfmax = 343400, .val = 0x0a }, - { .rfmax = 356100, .val = 0x09 }, - { .rfmax = 368800, .val = 0x08 }, - { .rfmax = 381500, .val = 0x07 }, - { .rfmax = 394200, .val = 0x06 }, - { .rfmax = 406900, .val = 0x05 }, - { .rfmax = 419700, .val = 0x04 }, - { .rfmax = 432400, .val = 0x03 }, - { .rfmax = 445100, .val = 0x02 }, - { .rfmax = 457800, .val = 0x01 }, - { .rfmax = 476300, .val = 0x19 }, - { .rfmax = 494800, .val = 0x18 }, - { .rfmax = 513300, .val = 0x17 }, - { .rfmax = 531800, .val = 0x16 }, - { .rfmax = 550300, .val = 0x15 }, - { .rfmax = 568900, .val = 0x14 }, - { .rfmax = 587400, .val = 0x13 }, - { .rfmax = 605900, .val = 0x12 }, - { .rfmax = 624400, .val = 0x11 }, - { .rfmax = 642900, .val = 0x10 }, - { .rfmax = 661400, .val = 0x0f }, - { .rfmax = 679900, .val = 0x0e }, - { .rfmax = 698400, .val = 0x0d }, - { .rfmax = 716900, .val = 0x0c }, - { .rfmax = 735400, .val = 0x0b }, - { .rfmax = 753900, .val = 0x0a }, - { .rfmax = 772500, .val = 0x09 }, - { .rfmax = 791000, .val = 0x08 }, - { .rfmax = 809500, .val = 0x07 }, - { .rfmax = 828000, .val = 0x06 }, - { .rfmax = 846500, .val = 0x05 }, - { .rfmax = 865000, .val = 0x04 }, - { .rfmax = 0, .val = 0x00 }, /* end */ -}; - -static struct tda18271_map tda18271c1_rf_cal[] = { - { .rfmax = 41000, .val = 0x1e }, - { .rfmax = 43000, .val = 0x30 }, - { .rfmax = 45000, .val = 0x43 }, - { .rfmax = 46000, .val = 0x4d }, - { .rfmax = 47000, .val = 0x54 }, - { .rfmax = 47900, .val = 0x64 }, - { .rfmax = 49100, .val = 0x20 }, - { .rfmax = 50000, .val = 0x22 }, - { .rfmax = 51000, .val = 0x2a }, - { .rfmax = 53000, .val = 0x32 }, - { .rfmax = 55000, .val = 0x35 }, - { .rfmax = 56000, .val = 0x3c }, - { .rfmax = 57000, .val = 0x3f }, - { .rfmax = 58000, .val = 0x48 }, - { .rfmax = 59000, .val = 0x4d }, - { .rfmax = 60000, .val = 0x58 }, - { .rfmax = 61100, .val = 0x5f }, - { .rfmax = 0, .val = 0x00 }, /* end */ -}; - -static struct tda18271_map tda18271c2_rf_cal[] = { - { .rfmax = 41000, .val = 0x0f }, - { .rfmax = 43000, .val = 0x1c }, - { .rfmax = 45000, .val = 0x2f }, - { .rfmax = 46000, .val = 0x39 }, - { .rfmax = 47000, .val = 0x40 }, - { .rfmax = 47900, .val = 0x50 }, - { .rfmax = 49100, .val = 0x16 }, - { .rfmax = 50000, .val = 0x18 }, - { .rfmax = 51000, .val = 0x20 }, - { .rfmax = 53000, .val = 0x28 }, - { .rfmax = 55000, .val = 0x2b }, - { .rfmax = 56000, .val = 0x32 }, - { .rfmax = 57000, .val = 0x35 }, - { .rfmax = 58000, .val = 0x3e }, - { .rfmax = 59000, .val = 0x43 }, - { .rfmax = 60000, .val = 0x4e }, - { .rfmax = 61100, .val = 0x55 }, - { .rfmax = 63000, .val = 0x0f }, - { .rfmax = 64000, .val = 0x11 }, - { .rfmax = 65000, .val = 0x12 }, - { .rfmax = 66000, .val = 0x15 }, - { .rfmax = 67000, .val = 0x16 }, - { .rfmax = 68000, .val = 0x17 }, - { .rfmax = 70000, .val = 0x19 }, - { .rfmax = 71000, .val = 0x1c }, - { .rfmax = 72000, .val = 0x1d }, - { .rfmax = 73000, .val = 0x1f }, - { .rfmax = 74000, .val = 0x20 }, - { .rfmax = 75000, .val = 0x21 }, - { .rfmax = 76000, .val = 0x24 }, - { .rfmax = 77000, .val = 0x25 }, - { .rfmax = 78000, .val = 0x27 }, - { .rfmax = 80000, .val = 0x28 }, - { .rfmax = 81000, .val = 0x29 }, - { .rfmax = 82000, .val = 0x2d }, - { .rfmax = 83000, .val = 0x2e }, - { .rfmax = 84000, .val = 0x2f }, - { .rfmax = 85000, .val = 0x31 }, - { .rfmax = 86000, .val = 0x33 }, - { .rfmax = 87000, .val = 0x34 }, - { .rfmax = 88000, .val = 0x35 }, - { .rfmax = 89000, .val = 0x37 }, - { .rfmax = 90000, .val = 0x38 }, - { .rfmax = 91000, .val = 0x39 }, - { .rfmax = 93000, .val = 0x3c }, - { .rfmax = 94000, .val = 0x3e }, - { .rfmax = 95000, .val = 0x3f }, - { .rfmax = 96000, .val = 0x40 }, - { .rfmax = 97000, .val = 0x42 }, - { .rfmax = 99000, .val = 0x45 }, - { .rfmax = 100000, .val = 0x46 }, - { .rfmax = 102000, .val = 0x48 }, - { .rfmax = 103000, .val = 0x4a }, - { .rfmax = 105000, .val = 0x4d }, - { .rfmax = 106000, .val = 0x4e }, - { .rfmax = 107000, .val = 0x50 }, - { .rfmax = 108000, .val = 0x51 }, - { .rfmax = 110000, .val = 0x54 }, - { .rfmax = 111000, .val = 0x56 }, - { .rfmax = 112000, .val = 0x57 }, - { .rfmax = 113000, .val = 0x58 }, - { .rfmax = 114000, .val = 0x59 }, - { .rfmax = 115000, .val = 0x5c }, - { .rfmax = 116000, .val = 0x5d }, - { .rfmax = 117000, .val = 0x5f }, - { .rfmax = 119000, .val = 0x60 }, - { .rfmax = 120000, .val = 0x64 }, - { .rfmax = 121000, .val = 0x65 }, - { .rfmax = 122000, .val = 0x66 }, - { .rfmax = 123000, .val = 0x68 }, - { .rfmax = 124000, .val = 0x69 }, - { .rfmax = 125000, .val = 0x6c }, - { .rfmax = 126000, .val = 0x6d }, - { .rfmax = 127000, .val = 0x6e }, - { .rfmax = 128000, .val = 0x70 }, - { .rfmax = 129000, .val = 0x71 }, - { .rfmax = 130000, .val = 0x75 }, - { .rfmax = 131000, .val = 0x77 }, - { .rfmax = 132000, .val = 0x78 }, - { .rfmax = 133000, .val = 0x7b }, - { .rfmax = 134000, .val = 0x7e }, - { .rfmax = 135000, .val = 0x81 }, - { .rfmax = 136000, .val = 0x82 }, - { .rfmax = 137000, .val = 0x87 }, - { .rfmax = 138000, .val = 0x88 }, - { .rfmax = 139000, .val = 0x8d }, - { .rfmax = 140000, .val = 0x8e }, - { .rfmax = 141000, .val = 0x91 }, - { .rfmax = 142000, .val = 0x95 }, - { .rfmax = 143000, .val = 0x9a }, - { .rfmax = 144000, .val = 0x9d }, - { .rfmax = 145000, .val = 0xa1 }, - { .rfmax = 146000, .val = 0xa2 }, - { .rfmax = 147000, .val = 0xa4 }, - { .rfmax = 148000, .val = 0xa9 }, - { .rfmax = 149000, .val = 0xae }, - { .rfmax = 150000, .val = 0xb0 }, - { .rfmax = 151000, .val = 0xb1 }, - { .rfmax = 152000, .val = 0xb7 }, - { .rfmax = 152600, .val = 0xbd }, - { .rfmax = 154000, .val = 0x20 }, - { .rfmax = 155000, .val = 0x22 }, - { .rfmax = 156000, .val = 0x24 }, - { .rfmax = 157000, .val = 0x25 }, - { .rfmax = 158000, .val = 0x27 }, - { .rfmax = 159000, .val = 0x29 }, - { .rfmax = 160000, .val = 0x2c }, - { .rfmax = 161000, .val = 0x2d }, - { .rfmax = 163000, .val = 0x2e }, - { .rfmax = 164000, .val = 0x2f }, - { .rfmax = 164700, .val = 0x30 }, - { .rfmax = 166000, .val = 0x11 }, - { .rfmax = 167000, .val = 0x12 }, - { .rfmax = 168000, .val = 0x13 }, - { .rfmax = 169000, .val = 0x14 }, - { .rfmax = 170000, .val = 0x15 }, - { .rfmax = 172000, .val = 0x16 }, - { .rfmax = 173000, .val = 0x17 }, - { .rfmax = 174000, .val = 0x18 }, - { .rfmax = 175000, .val = 0x1a }, - { .rfmax = 176000, .val = 0x1b }, - { .rfmax = 178000, .val = 0x1d }, - { .rfmax = 179000, .val = 0x1e }, - { .rfmax = 180000, .val = 0x1f }, - { .rfmax = 181000, .val = 0x20 }, - { .rfmax = 182000, .val = 0x21 }, - { .rfmax = 183000, .val = 0x22 }, - { .rfmax = 184000, .val = 0x24 }, - { .rfmax = 185000, .val = 0x25 }, - { .rfmax = 186000, .val = 0x26 }, - { .rfmax = 187000, .val = 0x27 }, - { .rfmax = 188000, .val = 0x29 }, - { .rfmax = 189000, .val = 0x2a }, - { .rfmax = 190000, .val = 0x2c }, - { .rfmax = 191000, .val = 0x2d }, - { .rfmax = 192000, .val = 0x2e }, - { .rfmax = 193000, .val = 0x2f }, - { .rfmax = 194000, .val = 0x30 }, - { .rfmax = 195000, .val = 0x33 }, - { .rfmax = 196000, .val = 0x35 }, - { .rfmax = 198000, .val = 0x36 }, - { .rfmax = 200000, .val = 0x38 }, - { .rfmax = 201000, .val = 0x3c }, - { .rfmax = 202000, .val = 0x3d }, - { .rfmax = 203500, .val = 0x3e }, - { .rfmax = 206000, .val = 0x0e }, - { .rfmax = 208000, .val = 0x0f }, - { .rfmax = 212000, .val = 0x10 }, - { .rfmax = 216000, .val = 0x11 }, - { .rfmax = 217000, .val = 0x12 }, - { .rfmax = 218000, .val = 0x13 }, - { .rfmax = 220000, .val = 0x14 }, - { .rfmax = 222000, .val = 0x15 }, - { .rfmax = 225000, .val = 0x16 }, - { .rfmax = 228000, .val = 0x17 }, - { .rfmax = 231000, .val = 0x18 }, - { .rfmax = 234000, .val = 0x19 }, - { .rfmax = 235000, .val = 0x1a }, - { .rfmax = 236000, .val = 0x1b }, - { .rfmax = 237000, .val = 0x1c }, - { .rfmax = 240000, .val = 0x1d }, - { .rfmax = 242000, .val = 0x1e }, - { .rfmax = 244000, .val = 0x1f }, - { .rfmax = 247000, .val = 0x20 }, - { .rfmax = 249000, .val = 0x21 }, - { .rfmax = 252000, .val = 0x22 }, - { .rfmax = 253000, .val = 0x23 }, - { .rfmax = 254000, .val = 0x24 }, - { .rfmax = 256000, .val = 0x25 }, - { .rfmax = 259000, .val = 0x26 }, - { .rfmax = 262000, .val = 0x27 }, - { .rfmax = 264000, .val = 0x28 }, - { .rfmax = 267000, .val = 0x29 }, - { .rfmax = 269000, .val = 0x2a }, - { .rfmax = 271000, .val = 0x2b }, - { .rfmax = 273000, .val = 0x2c }, - { .rfmax = 275000, .val = 0x2d }, - { .rfmax = 277000, .val = 0x2e }, - { .rfmax = 279000, .val = 0x2f }, - { .rfmax = 282000, .val = 0x30 }, - { .rfmax = 284000, .val = 0x31 }, - { .rfmax = 286000, .val = 0x32 }, - { .rfmax = 287000, .val = 0x33 }, - { .rfmax = 290000, .val = 0x34 }, - { .rfmax = 293000, .val = 0x35 }, - { .rfmax = 295000, .val = 0x36 }, - { .rfmax = 297000, .val = 0x37 }, - { .rfmax = 300000, .val = 0x38 }, - { .rfmax = 303000, .val = 0x39 }, - { .rfmax = 305000, .val = 0x3a }, - { .rfmax = 306000, .val = 0x3b }, - { .rfmax = 307000, .val = 0x3c }, - { .rfmax = 310000, .val = 0x3d }, - { .rfmax = 312000, .val = 0x3e }, - { .rfmax = 315000, .val = 0x3f }, - { .rfmax = 318000, .val = 0x40 }, - { .rfmax = 320000, .val = 0x41 }, - { .rfmax = 323000, .val = 0x42 }, - { .rfmax = 324000, .val = 0x43 }, - { .rfmax = 325000, .val = 0x44 }, - { .rfmax = 327000, .val = 0x45 }, - { .rfmax = 331000, .val = 0x46 }, - { .rfmax = 334000, .val = 0x47 }, - { .rfmax = 337000, .val = 0x48 }, - { .rfmax = 339000, .val = 0x49 }, - { .rfmax = 340000, .val = 0x4a }, - { .rfmax = 341000, .val = 0x4b }, - { .rfmax = 343000, .val = 0x4c }, - { .rfmax = 345000, .val = 0x4d }, - { .rfmax = 349000, .val = 0x4e }, - { .rfmax = 352000, .val = 0x4f }, - { .rfmax = 353000, .val = 0x50 }, - { .rfmax = 355000, .val = 0x51 }, - { .rfmax = 357000, .val = 0x52 }, - { .rfmax = 359000, .val = 0x53 }, - { .rfmax = 361000, .val = 0x54 }, - { .rfmax = 362000, .val = 0x55 }, - { .rfmax = 364000, .val = 0x56 }, - { .rfmax = 368000, .val = 0x57 }, - { .rfmax = 370000, .val = 0x58 }, - { .rfmax = 372000, .val = 0x59 }, - { .rfmax = 375000, .val = 0x5a }, - { .rfmax = 376000, .val = 0x5b }, - { .rfmax = 377000, .val = 0x5c }, - { .rfmax = 379000, .val = 0x5d }, - { .rfmax = 382000, .val = 0x5e }, - { .rfmax = 384000, .val = 0x5f }, - { .rfmax = 385000, .val = 0x60 }, - { .rfmax = 386000, .val = 0x61 }, - { .rfmax = 388000, .val = 0x62 }, - { .rfmax = 390000, .val = 0x63 }, - { .rfmax = 393000, .val = 0x64 }, - { .rfmax = 394000, .val = 0x65 }, - { .rfmax = 396000, .val = 0x66 }, - { .rfmax = 397000, .val = 0x67 }, - { .rfmax = 398000, .val = 0x68 }, - { .rfmax = 400000, .val = 0x69 }, - { .rfmax = 402000, .val = 0x6a }, - { .rfmax = 403000, .val = 0x6b }, - { .rfmax = 407000, .val = 0x6c }, - { .rfmax = 408000, .val = 0x6d }, - { .rfmax = 409000, .val = 0x6e }, - { .rfmax = 410000, .val = 0x6f }, - { .rfmax = 411000, .val = 0x70 }, - { .rfmax = 412000, .val = 0x71 }, - { .rfmax = 413000, .val = 0x72 }, - { .rfmax = 414000, .val = 0x73 }, - { .rfmax = 417000, .val = 0x74 }, - { .rfmax = 418000, .val = 0x75 }, - { .rfmax = 420000, .val = 0x76 }, - { .rfmax = 422000, .val = 0x77 }, - { .rfmax = 423000, .val = 0x78 }, - { .rfmax = 424000, .val = 0x79 }, - { .rfmax = 427000, .val = 0x7a }, - { .rfmax = 428000, .val = 0x7b }, - { .rfmax = 429000, .val = 0x7d }, - { .rfmax = 432000, .val = 0x7f }, - { .rfmax = 434000, .val = 0x80 }, - { .rfmax = 435000, .val = 0x81 }, - { .rfmax = 436000, .val = 0x83 }, - { .rfmax = 437000, .val = 0x84 }, - { .rfmax = 438000, .val = 0x85 }, - { .rfmax = 439000, .val = 0x86 }, - { .rfmax = 440000, .val = 0x87 }, - { .rfmax = 441000, .val = 0x88 }, - { .rfmax = 442000, .val = 0x89 }, - { .rfmax = 445000, .val = 0x8a }, - { .rfmax = 446000, .val = 0x8b }, - { .rfmax = 447000, .val = 0x8c }, - { .rfmax = 448000, .val = 0x8e }, - { .rfmax = 449000, .val = 0x8f }, - { .rfmax = 450000, .val = 0x90 }, - { .rfmax = 452000, .val = 0x91 }, - { .rfmax = 453000, .val = 0x93 }, - { .rfmax = 454000, .val = 0x94 }, - { .rfmax = 456000, .val = 0x96 }, - { .rfmax = 457800, .val = 0x98 }, - { .rfmax = 461000, .val = 0x11 }, - { .rfmax = 468000, .val = 0x12 }, - { .rfmax = 472000, .val = 0x13 }, - { .rfmax = 473000, .val = 0x14 }, - { .rfmax = 474000, .val = 0x15 }, - { .rfmax = 481000, .val = 0x16 }, - { .rfmax = 486000, .val = 0x17 }, - { .rfmax = 491000, .val = 0x18 }, - { .rfmax = 498000, .val = 0x19 }, - { .rfmax = 499000, .val = 0x1a }, - { .rfmax = 501000, .val = 0x1b }, - { .rfmax = 506000, .val = 0x1c }, - { .rfmax = 511000, .val = 0x1d }, - { .rfmax = 516000, .val = 0x1e }, - { .rfmax = 520000, .val = 0x1f }, - { .rfmax = 521000, .val = 0x20 }, - { .rfmax = 525000, .val = 0x21 }, - { .rfmax = 529000, .val = 0x22 }, - { .rfmax = 533000, .val = 0x23 }, - { .rfmax = 539000, .val = 0x24 }, - { .rfmax = 541000, .val = 0x25 }, - { .rfmax = 547000, .val = 0x26 }, - { .rfmax = 549000, .val = 0x27 }, - { .rfmax = 551000, .val = 0x28 }, - { .rfmax = 556000, .val = 0x29 }, - { .rfmax = 561000, .val = 0x2a }, - { .rfmax = 563000, .val = 0x2b }, - { .rfmax = 565000, .val = 0x2c }, - { .rfmax = 569000, .val = 0x2d }, - { .rfmax = 571000, .val = 0x2e }, - { .rfmax = 577000, .val = 0x2f }, - { .rfmax = 580000, .val = 0x30 }, - { .rfmax = 582000, .val = 0x31 }, - { .rfmax = 584000, .val = 0x32 }, - { .rfmax = 588000, .val = 0x33 }, - { .rfmax = 591000, .val = 0x34 }, - { .rfmax = 596000, .val = 0x35 }, - { .rfmax = 598000, .val = 0x36 }, - { .rfmax = 603000, .val = 0x37 }, - { .rfmax = 604000, .val = 0x38 }, - { .rfmax = 606000, .val = 0x39 }, - { .rfmax = 612000, .val = 0x3a }, - { .rfmax = 615000, .val = 0x3b }, - { .rfmax = 617000, .val = 0x3c }, - { .rfmax = 621000, .val = 0x3d }, - { .rfmax = 622000, .val = 0x3e }, - { .rfmax = 625000, .val = 0x3f }, - { .rfmax = 632000, .val = 0x40 }, - { .rfmax = 633000, .val = 0x41 }, - { .rfmax = 634000, .val = 0x42 }, - { .rfmax = 642000, .val = 0x43 }, - { .rfmax = 643000, .val = 0x44 }, - { .rfmax = 647000, .val = 0x45 }, - { .rfmax = 650000, .val = 0x46 }, - { .rfmax = 652000, .val = 0x47 }, - { .rfmax = 657000, .val = 0x48 }, - { .rfmax = 661000, .val = 0x49 }, - { .rfmax = 662000, .val = 0x4a }, - { .rfmax = 665000, .val = 0x4b }, - { .rfmax = 667000, .val = 0x4c }, - { .rfmax = 670000, .val = 0x4d }, - { .rfmax = 673000, .val = 0x4e }, - { .rfmax = 676000, .val = 0x4f }, - { .rfmax = 677000, .val = 0x50 }, - { .rfmax = 681000, .val = 0x51 }, - { .rfmax = 683000, .val = 0x52 }, - { .rfmax = 686000, .val = 0x53 }, - { .rfmax = 688000, .val = 0x54 }, - { .rfmax = 689000, .val = 0x55 }, - { .rfmax = 691000, .val = 0x56 }, - { .rfmax = 695000, .val = 0x57 }, - { .rfmax = 698000, .val = 0x58 }, - { .rfmax = 703000, .val = 0x59 }, - { .rfmax = 704000, .val = 0x5a }, - { .rfmax = 705000, .val = 0x5b }, - { .rfmax = 707000, .val = 0x5c }, - { .rfmax = 710000, .val = 0x5d }, - { .rfmax = 712000, .val = 0x5e }, - { .rfmax = 717000, .val = 0x5f }, - { .rfmax = 718000, .val = 0x60 }, - { .rfmax = 721000, .val = 0x61 }, - { .rfmax = 722000, .val = 0x62 }, - { .rfmax = 723000, .val = 0x63 }, - { .rfmax = 725000, .val = 0x64 }, - { .rfmax = 727000, .val = 0x65 }, - { .rfmax = 730000, .val = 0x66 }, - { .rfmax = 732000, .val = 0x67 }, - { .rfmax = 735000, .val = 0x68 }, - { .rfmax = 740000, .val = 0x69 }, - { .rfmax = 741000, .val = 0x6a }, - { .rfmax = 742000, .val = 0x6b }, - { .rfmax = 743000, .val = 0x6c }, - { .rfmax = 745000, .val = 0x6d }, - { .rfmax = 747000, .val = 0x6e }, - { .rfmax = 748000, .val = 0x6f }, - { .rfmax = 750000, .val = 0x70 }, - { .rfmax = 752000, .val = 0x71 }, - { .rfmax = 754000, .val = 0x72 }, - { .rfmax = 757000, .val = 0x73 }, - { .rfmax = 758000, .val = 0x74 }, - { .rfmax = 760000, .val = 0x75 }, - { .rfmax = 763000, .val = 0x76 }, - { .rfmax = 764000, .val = 0x77 }, - { .rfmax = 766000, .val = 0x78 }, - { .rfmax = 767000, .val = 0x79 }, - { .rfmax = 768000, .val = 0x7a }, - { .rfmax = 773000, .val = 0x7b }, - { .rfmax = 774000, .val = 0x7c }, - { .rfmax = 776000, .val = 0x7d }, - { .rfmax = 777000, .val = 0x7e }, - { .rfmax = 778000, .val = 0x7f }, - { .rfmax = 779000, .val = 0x80 }, - { .rfmax = 781000, .val = 0x81 }, - { .rfmax = 783000, .val = 0x82 }, - { .rfmax = 784000, .val = 0x83 }, - { .rfmax = 785000, .val = 0x84 }, - { .rfmax = 786000, .val = 0x85 }, - { .rfmax = 793000, .val = 0x86 }, - { .rfmax = 794000, .val = 0x87 }, - { .rfmax = 795000, .val = 0x88 }, - { .rfmax = 797000, .val = 0x89 }, - { .rfmax = 799000, .val = 0x8a }, - { .rfmax = 801000, .val = 0x8b }, - { .rfmax = 802000, .val = 0x8c }, - { .rfmax = 803000, .val = 0x8d }, - { .rfmax = 804000, .val = 0x8e }, - { .rfmax = 810000, .val = 0x90 }, - { .rfmax = 811000, .val = 0x91 }, - { .rfmax = 812000, .val = 0x92 }, - { .rfmax = 814000, .val = 0x93 }, - { .rfmax = 816000, .val = 0x94 }, - { .rfmax = 817000, .val = 0x96 }, - { .rfmax = 818000, .val = 0x97 }, - { .rfmax = 820000, .val = 0x98 }, - { .rfmax = 821000, .val = 0x99 }, - { .rfmax = 822000, .val = 0x9a }, - { .rfmax = 828000, .val = 0x9b }, - { .rfmax = 829000, .val = 0x9d }, - { .rfmax = 830000, .val = 0x9f }, - { .rfmax = 831000, .val = 0xa0 }, - { .rfmax = 833000, .val = 0xa1 }, - { .rfmax = 835000, .val = 0xa2 }, - { .rfmax = 836000, .val = 0xa3 }, - { .rfmax = 837000, .val = 0xa4 }, - { .rfmax = 838000, .val = 0xa6 }, - { .rfmax = 840000, .val = 0xa8 }, - { .rfmax = 842000, .val = 0xa9 }, - { .rfmax = 845000, .val = 0xaa }, - { .rfmax = 846000, .val = 0xab }, - { .rfmax = 847000, .val = 0xad }, - { .rfmax = 848000, .val = 0xae }, - { .rfmax = 852000, .val = 0xaf }, - { .rfmax = 853000, .val = 0xb0 }, - { .rfmax = 858000, .val = 0xb1 }, - { .rfmax = 860000, .val = 0xb2 }, - { .rfmax = 861000, .val = 0xb3 }, - { .rfmax = 862000, .val = 0xb4 }, - { .rfmax = 863000, .val = 0xb6 }, - { .rfmax = 864000, .val = 0xb8 }, - { .rfmax = 865000, .val = 0xb9 }, - { .rfmax = 0, .val = 0x00 }, /* end */ -}; - -static struct tda18271_map tda18271_ir_measure[] = { - { .rfmax = 30000, .val = 4 }, - { .rfmax = 200000, .val = 5 }, - { .rfmax = 600000, .val = 6 }, - { .rfmax = 865000, .val = 7 }, - { .rfmax = 0, .val = 0 }, /* end */ -}; - -static struct tda18271_map tda18271_rf_cal_dc_over_dt[] = { - { .rfmax = 47900, .val = 0x00 }, - { .rfmax = 55000, .val = 0x00 }, - { .rfmax = 61100, .val = 0x0a }, - { .rfmax = 64000, .val = 0x0a }, - { .rfmax = 82000, .val = 0x14 }, - { .rfmax = 84000, .val = 0x19 }, - { .rfmax = 119000, .val = 0x1c }, - { .rfmax = 124000, .val = 0x20 }, - { .rfmax = 129000, .val = 0x2a }, - { .rfmax = 134000, .val = 0x32 }, - { .rfmax = 139000, .val = 0x39 }, - { .rfmax = 144000, .val = 0x3e }, - { .rfmax = 149000, .val = 0x3f }, - { .rfmax = 152600, .val = 0x40 }, - { .rfmax = 154000, .val = 0x40 }, - { .rfmax = 164700, .val = 0x41 }, - { .rfmax = 203500, .val = 0x32 }, - { .rfmax = 353000, .val = 0x19 }, - { .rfmax = 356000, .val = 0x1a }, - { .rfmax = 359000, .val = 0x1b }, - { .rfmax = 363000, .val = 0x1c }, - { .rfmax = 366000, .val = 0x1d }, - { .rfmax = 369000, .val = 0x1e }, - { .rfmax = 373000, .val = 0x1f }, - { .rfmax = 376000, .val = 0x20 }, - { .rfmax = 379000, .val = 0x21 }, - { .rfmax = 383000, .val = 0x22 }, - { .rfmax = 386000, .val = 0x23 }, - { .rfmax = 389000, .val = 0x24 }, - { .rfmax = 393000, .val = 0x25 }, - { .rfmax = 396000, .val = 0x26 }, - { .rfmax = 399000, .val = 0x27 }, - { .rfmax = 402000, .val = 0x28 }, - { .rfmax = 404000, .val = 0x29 }, - { .rfmax = 407000, .val = 0x2a }, - { .rfmax = 409000, .val = 0x2b }, - { .rfmax = 412000, .val = 0x2c }, - { .rfmax = 414000, .val = 0x2d }, - { .rfmax = 417000, .val = 0x2e }, - { .rfmax = 419000, .val = 0x2f }, - { .rfmax = 422000, .val = 0x30 }, - { .rfmax = 424000, .val = 0x31 }, - { .rfmax = 427000, .val = 0x32 }, - { .rfmax = 429000, .val = 0x33 }, - { .rfmax = 432000, .val = 0x34 }, - { .rfmax = 434000, .val = 0x35 }, - { .rfmax = 437000, .val = 0x36 }, - { .rfmax = 439000, .val = 0x37 }, - { .rfmax = 442000, .val = 0x38 }, - { .rfmax = 444000, .val = 0x39 }, - { .rfmax = 447000, .val = 0x3a }, - { .rfmax = 449000, .val = 0x3b }, - { .rfmax = 457800, .val = 0x3c }, - { .rfmax = 465000, .val = 0x0f }, - { .rfmax = 477000, .val = 0x12 }, - { .rfmax = 483000, .val = 0x14 }, - { .rfmax = 502000, .val = 0x19 }, - { .rfmax = 508000, .val = 0x1b }, - { .rfmax = 519000, .val = 0x1c }, - { .rfmax = 522000, .val = 0x1d }, - { .rfmax = 524000, .val = 0x1e }, - { .rfmax = 534000, .val = 0x1f }, - { .rfmax = 549000, .val = 0x20 }, - { .rfmax = 554000, .val = 0x22 }, - { .rfmax = 584000, .val = 0x24 }, - { .rfmax = 589000, .val = 0x26 }, - { .rfmax = 658000, .val = 0x27 }, - { .rfmax = 664000, .val = 0x2c }, - { .rfmax = 669000, .val = 0x2d }, - { .rfmax = 699000, .val = 0x2e }, - { .rfmax = 704000, .val = 0x30 }, - { .rfmax = 709000, .val = 0x31 }, - { .rfmax = 714000, .val = 0x32 }, - { .rfmax = 724000, .val = 0x33 }, - { .rfmax = 729000, .val = 0x36 }, - { .rfmax = 739000, .val = 0x38 }, - { .rfmax = 744000, .val = 0x39 }, - { .rfmax = 749000, .val = 0x3b }, - { .rfmax = 754000, .val = 0x3c }, - { .rfmax = 759000, .val = 0x3d }, - { .rfmax = 764000, .val = 0x3e }, - { .rfmax = 769000, .val = 0x3f }, - { .rfmax = 774000, .val = 0x40 }, - { .rfmax = 779000, .val = 0x41 }, - { .rfmax = 784000, .val = 0x43 }, - { .rfmax = 789000, .val = 0x46 }, - { .rfmax = 794000, .val = 0x48 }, - { .rfmax = 799000, .val = 0x4b }, - { .rfmax = 804000, .val = 0x4f }, - { .rfmax = 809000, .val = 0x54 }, - { .rfmax = 814000, .val = 0x59 }, - { .rfmax = 819000, .val = 0x5d }, - { .rfmax = 824000, .val = 0x61 }, - { .rfmax = 829000, .val = 0x68 }, - { .rfmax = 834000, .val = 0x6e }, - { .rfmax = 839000, .val = 0x75 }, - { .rfmax = 844000, .val = 0x7e }, - { .rfmax = 849000, .val = 0x82 }, - { .rfmax = 854000, .val = 0x84 }, - { .rfmax = 859000, .val = 0x8f }, - { .rfmax = 865000, .val = 0x9a }, - { .rfmax = 0, .val = 0x00 }, /* end */ -}; - -/*---------------------------------------------------------------------*/ - -struct tda18271_thermo_map { - u8 d; - u8 r0; - u8 r1; -}; - -static struct tda18271_thermo_map tda18271_thermometer[] = { - { .d = 0x00, .r0 = 60, .r1 = 92 }, - { .d = 0x01, .r0 = 62, .r1 = 94 }, - { .d = 0x02, .r0 = 66, .r1 = 98 }, - { .d = 0x03, .r0 = 64, .r1 = 96 }, - { .d = 0x04, .r0 = 74, .r1 = 106 }, - { .d = 0x05, .r0 = 72, .r1 = 104 }, - { .d = 0x06, .r0 = 68, .r1 = 100 }, - { .d = 0x07, .r0 = 70, .r1 = 102 }, - { .d = 0x08, .r0 = 90, .r1 = 122 }, - { .d = 0x09, .r0 = 88, .r1 = 120 }, - { .d = 0x0a, .r0 = 84, .r1 = 116 }, - { .d = 0x0b, .r0 = 86, .r1 = 118 }, - { .d = 0x0c, .r0 = 76, .r1 = 108 }, - { .d = 0x0d, .r0 = 78, .r1 = 110 }, - { .d = 0x0e, .r0 = 82, .r1 = 114 }, - { .d = 0x0f, .r0 = 80, .r1 = 112 }, - { .d = 0x00, .r0 = 0, .r1 = 0 }, /* end */ -}; - -int tda18271_lookup_thermometer(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - int val, i = 0; - - while (tda18271_thermometer[i].d < (regs[R_TM] & 0x0f)) { - if (tda18271_thermometer[i + 1].d == 0) - break; - i++; - } - - if ((regs[R_TM] & 0x20) == 0x20) - val = tda18271_thermometer[i].r1; - else - val = tda18271_thermometer[i].r0; - - tda_map("(%d) tm = %d\n", i, val); - - return val; -} - -/*---------------------------------------------------------------------*/ - -struct tda18271_cid_target_map { - u32 rfmax; - u8 target; - u16 limit; -}; - -static struct tda18271_cid_target_map tda18271_cid_target[] = { - { .rfmax = 46000, .target = 0x04, .limit = 1800 }, - { .rfmax = 52200, .target = 0x0a, .limit = 1500 }, - { .rfmax = 70100, .target = 0x01, .limit = 4000 }, - { .rfmax = 136800, .target = 0x18, .limit = 4000 }, - { .rfmax = 156700, .target = 0x18, .limit = 4000 }, - { .rfmax = 186250, .target = 0x0a, .limit = 4000 }, - { .rfmax = 230000, .target = 0x0a, .limit = 4000 }, - { .rfmax = 345000, .target = 0x18, .limit = 4000 }, - { .rfmax = 426000, .target = 0x0e, .limit = 4000 }, - { .rfmax = 489500, .target = 0x1e, .limit = 4000 }, - { .rfmax = 697500, .target = 0x32, .limit = 4000 }, - { .rfmax = 842000, .target = 0x3a, .limit = 4000 }, - { .rfmax = 0, .target = 0x00, .limit = 0 }, /* end */ -}; - -int tda18271_lookup_cid_target(struct dvb_frontend *fe, - u32 *freq, u8 *cid_target, u16 *count_limit) -{ - struct tda18271_priv *priv = fe->tuner_priv; - int i = 0; - - while ((tda18271_cid_target[i].rfmax * 1000) < *freq) { - if (tda18271_cid_target[i + 1].rfmax == 0) - break; - i++; - } - *cid_target = tda18271_cid_target[i].target; - *count_limit = tda18271_cid_target[i].limit; - - tda_map("(%d) cid_target = %02x, count_limit = %d\n", i, - tda18271_cid_target[i].target, tda18271_cid_target[i].limit); - - return 0; -} - -/*---------------------------------------------------------------------*/ - -static struct tda18271_rf_tracking_filter_cal tda18271_rf_band_template[] = { - { .rfmax = 47900, .rfband = 0x00, - .rf1_def = 46000, .rf2_def = 0, .rf3_def = 0 }, - { .rfmax = 61100, .rfband = 0x01, - .rf1_def = 52200, .rf2_def = 0, .rf3_def = 0 }, - { .rfmax = 152600, .rfband = 0x02, - .rf1_def = 70100, .rf2_def = 136800, .rf3_def = 0 }, - { .rfmax = 164700, .rfband = 0x03, - .rf1_def = 156700, .rf2_def = 0, .rf3_def = 0 }, - { .rfmax = 203500, .rfband = 0x04, - .rf1_def = 186250, .rf2_def = 0, .rf3_def = 0 }, - { .rfmax = 457800, .rfband = 0x05, - .rf1_def = 230000, .rf2_def = 345000, .rf3_def = 426000 }, - { .rfmax = 865000, .rfband = 0x06, - .rf1_def = 489500, .rf2_def = 697500, .rf3_def = 842000 }, - { .rfmax = 0, .rfband = 0x00, - .rf1_def = 0, .rf2_def = 0, .rf3_def = 0 }, /* end */ -}; - -int tda18271_lookup_rf_band(struct dvb_frontend *fe, u32 *freq, u8 *rf_band) -{ - struct tda18271_priv *priv = fe->tuner_priv; - struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; - int i = 0; - - while ((map[i].rfmax * 1000) < *freq) { - if (tda18271_debug & DBG_ADV) - tda_map("(%d) rfmax = %d < freq = %d, " - "rf1_def = %d, rf2_def = %d, rf3_def = %d, " - "rf1 = %d, rf2 = %d, rf3 = %d, " - "rf_a1 = %d, rf_a2 = %d, " - "rf_b1 = %d, rf_b2 = %d\n", - i, map[i].rfmax * 1000, *freq, - map[i].rf1_def, map[i].rf2_def, map[i].rf3_def, - map[i].rf1, map[i].rf2, map[i].rf3, - map[i].rf_a1, map[i].rf_a2, - map[i].rf_b1, map[i].rf_b2); - if (map[i].rfmax == 0) - return -EINVAL; - i++; - } - if (rf_band) - *rf_band = map[i].rfband; - - tda_map("(%d) rf_band = %02x\n", i, map[i].rfband); - - return i; -} - -/*---------------------------------------------------------------------*/ - -struct tda18271_map_layout { - struct tda18271_pll_map *main_pll; - struct tda18271_pll_map *cal_pll; - - struct tda18271_map *rf_cal; - struct tda18271_map *rf_cal_kmco; - struct tda18271_map *rf_cal_dc_over_dt; - - struct tda18271_map *bp_filter; - struct tda18271_map *rf_band; - struct tda18271_map *gain_taper; - struct tda18271_map *ir_measure; -}; - -/*---------------------------------------------------------------------*/ - -int tda18271_lookup_pll_map(struct dvb_frontend *fe, - enum tda18271_map_type map_type, - u32 *freq, u8 *post_div, u8 *div) -{ - struct tda18271_priv *priv = fe->tuner_priv; - struct tda18271_pll_map *map = NULL; - unsigned int i = 0; - char *map_name; - int ret = 0; - - BUG_ON(!priv->maps); - - switch (map_type) { - case MAIN_PLL: - map = priv->maps->main_pll; - map_name = "main_pll"; - break; - case CAL_PLL: - map = priv->maps->cal_pll; - map_name = "cal_pll"; - break; - default: - /* we should never get here */ - map_name = "undefined"; - break; - } - - if (!map) { - tda_warn("%s map is not set!\n", map_name); - ret = -EINVAL; - goto fail; - } - - while ((map[i].lomax * 1000) < *freq) { - if (map[i + 1].lomax == 0) { - tda_map("%s: frequency (%d) out of range\n", - map_name, *freq); - ret = -ERANGE; - break; - } - i++; - } - *post_div = map[i].pd; - *div = map[i].d; - - tda_map("(%d) %s: post div = 0x%02x, div = 0x%02x\n", - i, map_name, *post_div, *div); -fail: - return ret; -} - -int tda18271_lookup_map(struct dvb_frontend *fe, - enum tda18271_map_type map_type, - u32 *freq, u8 *val) -{ - struct tda18271_priv *priv = fe->tuner_priv; - struct tda18271_map *map = NULL; - unsigned int i = 0; - char *map_name; - int ret = 0; - - BUG_ON(!priv->maps); - - switch (map_type) { - case BP_FILTER: - map = priv->maps->bp_filter; - map_name = "bp_filter"; - break; - case RF_CAL_KMCO: - map = priv->maps->rf_cal_kmco; - map_name = "km"; - break; - case RF_BAND: - map = priv->maps->rf_band; - map_name = "rf_band"; - break; - case GAIN_TAPER: - map = priv->maps->gain_taper; - map_name = "gain_taper"; - break; - case RF_CAL: - map = priv->maps->rf_cal; - map_name = "rf_cal"; - break; - case IR_MEASURE: - map = priv->maps->ir_measure; - map_name = "ir_measure"; - break; - case RF_CAL_DC_OVER_DT: - map = priv->maps->rf_cal_dc_over_dt; - map_name = "rf_cal_dc_over_dt"; - break; - default: - /* we should never get here */ - map_name = "undefined"; - break; - } - - if (!map) { - tda_warn("%s map is not set!\n", map_name); - ret = -EINVAL; - goto fail; - } - - while ((map[i].rfmax * 1000) < *freq) { - if (map[i + 1].rfmax == 0) { - tda_map("%s: frequency (%d) out of range\n", - map_name, *freq); - ret = -ERANGE; - break; - } - i++; - } - *val = map[i].val; - - tda_map("(%d) %s: 0x%02x\n", i, map_name, *val); -fail: - return ret; -} - -/*---------------------------------------------------------------------*/ - -static struct tda18271_std_map tda18271c1_std_map = { - .fm_radio = { .if_freq = 1250, .fm_rfn = 1, .agc_mode = 3, .std = 0, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x18 */ - .atv_b = { .if_freq = 6750, .fm_rfn = 0, .agc_mode = 1, .std = 6, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ - .atv_dk = { .if_freq = 7750, .fm_rfn = 0, .agc_mode = 1, .std = 7, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ - .atv_gh = { .if_freq = 7750, .fm_rfn = 0, .agc_mode = 1, .std = 7, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ - .atv_i = { .if_freq = 7750, .fm_rfn = 0, .agc_mode = 1, .std = 7, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ - .atv_l = { .if_freq = 7750, .fm_rfn = 0, .agc_mode = 1, .std = 7, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ - .atv_lc = { .if_freq = 1250, .fm_rfn = 0, .agc_mode = 1, .std = 7, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ - .atv_mn = { .if_freq = 5750, .fm_rfn = 0, .agc_mode = 1, .std = 5, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0d */ - .atsc_6 = { .if_freq = 3250, .fm_rfn = 0, .agc_mode = 3, .std = 4, - .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ - .dvbt_6 = { .if_freq = 3300, .fm_rfn = 0, .agc_mode = 3, .std = 4, - .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ - .dvbt_7 = { .if_freq = 3800, .fm_rfn = 0, .agc_mode = 3, .std = 5, - .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1d */ - .dvbt_8 = { .if_freq = 4300, .fm_rfn = 0, .agc_mode = 3, .std = 6, - .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1e */ - .qam_6 = { .if_freq = 4000, .fm_rfn = 0, .agc_mode = 3, .std = 5, - .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1d */ - .qam_7 = { .if_freq = 4500, .fm_rfn = 0, .agc_mode = 3, .std = 6, - .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1e */ - .qam_8 = { .if_freq = 5000, .fm_rfn = 0, .agc_mode = 3, .std = 7, - .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1f */ -}; - -static struct tda18271_std_map tda18271c2_std_map = { - .fm_radio = { .if_freq = 1250, .fm_rfn = 1, .agc_mode = 3, .std = 0, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x18 */ - .atv_b = { .if_freq = 6000, .fm_rfn = 0, .agc_mode = 1, .std = 5, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0d */ - .atv_dk = { .if_freq = 6900, .fm_rfn = 0, .agc_mode = 1, .std = 6, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ - .atv_gh = { .if_freq = 7100, .fm_rfn = 0, .agc_mode = 1, .std = 6, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ - .atv_i = { .if_freq = 7250, .fm_rfn = 0, .agc_mode = 1, .std = 6, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ - .atv_l = { .if_freq = 6900, .fm_rfn = 0, .agc_mode = 1, .std = 6, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ - .atv_lc = { .if_freq = 1250, .fm_rfn = 0, .agc_mode = 1, .std = 6, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ - .atv_mn = { .if_freq = 5400, .fm_rfn = 0, .agc_mode = 1, .std = 4, - .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0c */ - .atsc_6 = { .if_freq = 3250, .fm_rfn = 0, .agc_mode = 3, .std = 4, - .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ - .dvbt_6 = { .if_freq = 3300, .fm_rfn = 0, .agc_mode = 3, .std = 4, - .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ - .dvbt_7 = { .if_freq = 3500, .fm_rfn = 0, .agc_mode = 3, .std = 4, - .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ - .dvbt_8 = { .if_freq = 4000, .fm_rfn = 0, .agc_mode = 3, .std = 5, - .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1d */ - .qam_6 = { .if_freq = 4000, .fm_rfn = 0, .agc_mode = 3, .std = 5, - .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1d */ - .qam_7 = { .if_freq = 4500, .fm_rfn = 0, .agc_mode = 3, .std = 6, - .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1e */ - .qam_8 = { .if_freq = 5000, .fm_rfn = 0, .agc_mode = 3, .std = 7, - .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1f */ -}; - -/*---------------------------------------------------------------------*/ - -static struct tda18271_map_layout tda18271c1_map_layout = { - .main_pll = tda18271c1_main_pll, - .cal_pll = tda18271c1_cal_pll, - - .rf_cal = tda18271c1_rf_cal, - .rf_cal_kmco = tda18271c1_km, - - .bp_filter = tda18271_bp_filter, - .rf_band = tda18271_rf_band, - .gain_taper = tda18271_gain_taper, - .ir_measure = tda18271_ir_measure, -}; - -static struct tda18271_map_layout tda18271c2_map_layout = { - .main_pll = tda18271c2_main_pll, - .cal_pll = tda18271c2_cal_pll, - - .rf_cal = tda18271c2_rf_cal, - .rf_cal_kmco = tda18271c2_km, - - .rf_cal_dc_over_dt = tda18271_rf_cal_dc_over_dt, - - .bp_filter = tda18271_bp_filter, - .rf_band = tda18271_rf_band, - .gain_taper = tda18271_gain_taper, - .ir_measure = tda18271_ir_measure, -}; - -int tda18271_assign_map_layout(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - int ret = 0; - - switch (priv->id) { - case TDA18271HDC1: - priv->maps = &tda18271c1_map_layout; - memcpy(&priv->std, &tda18271c1_std_map, - sizeof(struct tda18271_std_map)); - break; - case TDA18271HDC2: - priv->maps = &tda18271c2_map_layout; - memcpy(&priv->std, &tda18271c2_std_map, - sizeof(struct tda18271_std_map)); - break; - default: - ret = -EINVAL; - break; - } - memcpy(priv->rf_cal_state, &tda18271_rf_band_template, - sizeof(tda18271_rf_band_template)); - - return ret; -} - -/* - * 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/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h deleted file mode 100644 index 454c152ccaa0..000000000000 --- a/drivers/media/common/tuners/tda18271-priv.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - tda18271-priv.h - private header for the NXP TDA18271 silicon tuner - - Copyright (C) 2007, 2008 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 __TDA18271_PRIV_H__ -#define __TDA18271_PRIV_H__ - -#include -#include -#include -#include "tuner-i2c.h" -#include "tda18271.h" - -#define R_ID 0x00 /* ID byte */ -#define R_TM 0x01 /* Thermo byte */ -#define R_PL 0x02 /* Power level byte */ -#define R_EP1 0x03 /* Easy Prog byte 1 */ -#define R_EP2 0x04 /* Easy Prog byte 2 */ -#define R_EP3 0x05 /* Easy Prog byte 3 */ -#define R_EP4 0x06 /* Easy Prog byte 4 */ -#define R_EP5 0x07 /* Easy Prog byte 5 */ -#define R_CPD 0x08 /* Cal Post-Divider byte */ -#define R_CD1 0x09 /* Cal Divider byte 1 */ -#define R_CD2 0x0a /* Cal Divider byte 2 */ -#define R_CD3 0x0b /* Cal Divider byte 3 */ -#define R_MPD 0x0c /* Main Post-Divider byte */ -#define R_MD1 0x0d /* Main Divider byte 1 */ -#define R_MD2 0x0e /* Main Divider byte 2 */ -#define R_MD3 0x0f /* Main Divider byte 3 */ -#define R_EB1 0x10 /* Extended byte 1 */ -#define R_EB2 0x11 /* Extended byte 2 */ -#define R_EB3 0x12 /* Extended byte 3 */ -#define R_EB4 0x13 /* Extended byte 4 */ -#define R_EB5 0x14 /* Extended byte 5 */ -#define R_EB6 0x15 /* Extended byte 6 */ -#define R_EB7 0x16 /* Extended byte 7 */ -#define R_EB8 0x17 /* Extended byte 8 */ -#define R_EB9 0x18 /* Extended byte 9 */ -#define R_EB10 0x19 /* Extended byte 10 */ -#define R_EB11 0x1a /* Extended byte 11 */ -#define R_EB12 0x1b /* Extended byte 12 */ -#define R_EB13 0x1c /* Extended byte 13 */ -#define R_EB14 0x1d /* Extended byte 14 */ -#define R_EB15 0x1e /* Extended byte 15 */ -#define R_EB16 0x1f /* Extended byte 16 */ -#define R_EB17 0x20 /* Extended byte 17 */ -#define R_EB18 0x21 /* Extended byte 18 */ -#define R_EB19 0x22 /* Extended byte 19 */ -#define R_EB20 0x23 /* Extended byte 20 */ -#define R_EB21 0x24 /* Extended byte 21 */ -#define R_EB22 0x25 /* Extended byte 22 */ -#define R_EB23 0x26 /* Extended byte 23 */ - -#define TDA18271_NUM_REGS 39 - -/*---------------------------------------------------------------------*/ - -struct tda18271_rf_tracking_filter_cal { - u32 rfmax; - u8 rfband; - u32 rf1_def; - u32 rf2_def; - u32 rf3_def; - u32 rf1; - u32 rf2; - u32 rf3; - s32 rf_a1; - s32 rf_b1; - s32 rf_a2; - s32 rf_b2; -}; - -enum tda18271_pll { - TDA18271_MAIN_PLL, - TDA18271_CAL_PLL, -}; - -struct tda18271_map_layout; - -enum tda18271_ver { - TDA18271HDC1, - TDA18271HDC2, -}; - -struct tda18271_priv { - unsigned char tda18271_regs[TDA18271_NUM_REGS]; - - struct list_head hybrid_tuner_instance_list; - struct tuner_i2c_props i2c_props; - - enum tda18271_mode mode; - enum tda18271_role role; - enum tda18271_i2c_gate gate; - enum tda18271_ver id; - enum tda18271_output_options output_opt; - enum tda18271_small_i2c small_i2c; - - unsigned int config; /* interface to saa713x / tda829x */ - unsigned int cal_initialized:1; - - u8 tm_rfcal; - - struct tda18271_map_layout *maps; - struct tda18271_std_map std; - struct tda18271_rf_tracking_filter_cal rf_cal_state[8]; - - struct mutex lock; - - u16 if_freq; - - u32 frequency; - u32 bandwidth; -}; - -/*---------------------------------------------------------------------*/ - -extern int tda18271_debug; - -#define DBG_INFO 1 -#define DBG_MAP 2 -#define DBG_REG 4 -#define DBG_ADV 8 -#define DBG_CAL 16 - -__attribute__((format(printf, 4, 5))) -int _tda_printk(struct tda18271_priv *state, const char *level, - const char *func, const char *fmt, ...); - -#define tda_printk(st, lvl, fmt, arg...) \ - _tda_printk(st, lvl, __func__, fmt, ##arg) - -#define tda_dprintk(st, lvl, fmt, arg...) \ -do { \ - if (tda18271_debug & lvl) \ - tda_printk(st, KERN_DEBUG, fmt, ##arg); \ -} while (0) - -#define tda_info(fmt, arg...) pr_info(fmt, ##arg) -#define tda_warn(fmt, arg...) tda_printk(priv, KERN_WARNING, fmt, ##arg) -#define tda_err(fmt, arg...) tda_printk(priv, KERN_ERR, fmt, ##arg) -#define tda_dbg(fmt, arg...) tda_dprintk(priv, DBG_INFO, fmt, ##arg) -#define tda_map(fmt, arg...) tda_dprintk(priv, DBG_MAP, fmt, ##arg) -#define tda_reg(fmt, arg...) tda_dprintk(priv, DBG_REG, fmt, ##arg) -#define tda_cal(fmt, arg...) tda_dprintk(priv, DBG_CAL, fmt, ##arg) - -#define tda_fail(ret) \ -({ \ - int __ret; \ - __ret = (ret < 0); \ - if (__ret) \ - tda_printk(priv, KERN_ERR, \ - "error %d on line %d\n", ret, __LINE__); \ - __ret; \ -}) - -/*---------------------------------------------------------------------*/ - -enum tda18271_map_type { - /* tda18271_pll_map */ - MAIN_PLL, - CAL_PLL, - /* tda18271_map */ - RF_CAL, - RF_CAL_KMCO, - RF_CAL_DC_OVER_DT, - BP_FILTER, - RF_BAND, - GAIN_TAPER, - IR_MEASURE, -}; - -extern int tda18271_lookup_pll_map(struct dvb_frontend *fe, - enum tda18271_map_type map_type, - u32 *freq, u8 *post_div, u8 *div); -extern int tda18271_lookup_map(struct dvb_frontend *fe, - enum tda18271_map_type map_type, - u32 *freq, u8 *val); - -extern int tda18271_lookup_thermometer(struct dvb_frontend *fe); - -extern int tda18271_lookup_rf_band(struct dvb_frontend *fe, - u32 *freq, u8 *rf_band); - -extern int tda18271_lookup_cid_target(struct dvb_frontend *fe, - u32 *freq, u8 *cid_target, - u16 *count_limit); - -extern int tda18271_assign_map_layout(struct dvb_frontend *fe); - -/*---------------------------------------------------------------------*/ - -extern int tda18271_read_regs(struct dvb_frontend *fe); -extern int tda18271_read_extended(struct dvb_frontend *fe); -extern int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len); -extern int tda18271_init_regs(struct dvb_frontend *fe); - -extern int tda18271_charge_pump_source(struct dvb_frontend *fe, - enum tda18271_pll pll, int force); -extern int tda18271_set_standby_mode(struct dvb_frontend *fe, - int sm, int sm_lt, int sm_xt); - -extern int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq); -extern int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq); - -extern int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq); -extern int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq); -extern int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq); -extern int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq); -extern int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq); -extern int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq); - -#endif /* __TDA18271_PRIV_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/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 - - 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 -#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.c b/drivers/media/common/tuners/tda827x.c deleted file mode 100644 index a0d176267470..000000000000 --- a/drivers/media/common/tuners/tda827x.c +++ /dev/null @@ -1,917 +0,0 @@ -/* - * - * (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. - */ - -#include -#include -#include -#include -#include - -#include "tda827x.h" - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); - -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG "tda827x: " args); \ - } while (0) - -struct tda827x_priv { - int i2c_addr; - struct i2c_adapter *i2c_adap; - struct tda827x_config *cfg; - - unsigned int sgIF; - unsigned char lpsel; - - u32 frequency; - u32 bandwidth; -}; - -static void tda827x_set_std(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct tda827x_priv *priv = fe->tuner_priv; - char *mode; - - priv->lpsel = 0; - if (params->std & V4L2_STD_MN) { - priv->sgIF = 92; - priv->lpsel = 1; - mode = "MN"; - } else if (params->std & V4L2_STD_B) { - priv->sgIF = 108; - mode = "B"; - } else if (params->std & V4L2_STD_GH) { - priv->sgIF = 124; - mode = "GH"; - } else if (params->std & V4L2_STD_PAL_I) { - priv->sgIF = 124; - mode = "I"; - } else if (params->std & V4L2_STD_DK) { - priv->sgIF = 124; - mode = "DK"; - } else if (params->std & V4L2_STD_SECAM_L) { - priv->sgIF = 124; - mode = "L"; - } else if (params->std & V4L2_STD_SECAM_LC) { - priv->sgIF = 20; - mode = "LC"; - } else { - priv->sgIF = 124; - mode = "xx"; - } - - if (params->mode == V4L2_TUNER_RADIO) { - priv->sgIF = 88; /* if frequency is 5.5 MHz */ - dprintk("setting tda827x to radio FM\n"); - } else - dprintk("setting tda827x to system %s\n", mode); -} - - -/* ------------------------------------------------------------------ */ - -struct tda827x_data { - u32 lomax; - u8 spd; - u8 bs; - u8 bp; - u8 cp; - u8 gc3; - u8 div1p5; -}; - -static const struct tda827x_data tda827x_table[] = { - { .lomax = 62000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, - { .lomax = 66000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, - { .lomax = 76000000, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, - { .lomax = 84000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, - { .lomax = 93000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 98000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 109000000, .spd = 3, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 123000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 133000000, .spd = 2, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 151000000, .spd = 2, .bs = 1, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 154000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 181000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 0, .div1p5 = 0}, - { .lomax = 185000000, .spd = 2, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 217000000, .spd = 2, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 244000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 265000000, .spd = 1, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 302000000, .spd = 1, .bs = 1, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 324000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 370000000, .spd = 1, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 454000000, .spd = 1, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 493000000, .spd = 0, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 530000000, .spd = 0, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 554000000, .spd = 0, .bs = 1, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 604000000, .spd = 0, .bs = 1, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, - { .lomax = 696000000, .spd = 0, .bs = 2, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, - { .lomax = 740000000, .spd = 0, .bs = 2, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, - { .lomax = 820000000, .spd = 0, .bs = 3, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, - { .lomax = 865000000, .spd = 0, .bs = 3, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, - { .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0} -}; - -static int tuner_transfer(struct dvb_frontend *fe, - struct i2c_msg *msg, - const int size) -{ - int rc; - struct tda827x_priv *priv = fe->tuner_priv; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - rc = i2c_transfer(priv->i2c_adap, msg, size); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - if (rc >= 0 && rc != size) - return -EIO; - - return rc; -} - -static int tda827xo_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct tda827x_priv *priv = fe->tuner_priv; - u8 buf[14]; - int rc; - - struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, - .buf = buf, .len = sizeof(buf) }; - int i, tuner_freq, if_freq; - u32 N; - - dprintk("%s:\n", __func__); - if (c->bandwidth_hz == 0) { - if_freq = 5000000; - } else if (c->bandwidth_hz <= 6000000) { - if_freq = 4000000; - } else if (c->bandwidth_hz <= 7000000) { - if_freq = 4500000; - } else { /* 8 MHz */ - if_freq = 5000000; - } - tuner_freq = c->frequency; - - i = 0; - while (tda827x_table[i].lomax < tuner_freq) { - if (tda827x_table[i + 1].lomax == 0) - break; - i++; - } - - tuner_freq += if_freq; - - N = ((tuner_freq + 125000) / 250000) << (tda827x_table[i].spd + 2); - buf[0] = 0; - buf[1] = (N>>8) | 0x40; - buf[2] = N & 0xff; - buf[3] = 0; - buf[4] = 0x52; - buf[5] = (tda827x_table[i].spd << 6) + (tda827x_table[i].div1p5 << 5) + - (tda827x_table[i].bs << 3) + - tda827x_table[i].bp; - buf[6] = (tda827x_table[i].gc3 << 4) + 0x8f; - buf[7] = 0xbf; - buf[8] = 0x2a; - buf[9] = 0x05; - buf[10] = 0xff; - buf[11] = 0x00; - buf[12] = 0x00; - buf[13] = 0x40; - - msg.len = 14; - rc = tuner_transfer(fe, &msg, 1); - if (rc < 0) - goto err; - - msleep(500); - /* correct CP value */ - buf[0] = 0x30; - buf[1] = 0x50 + tda827x_table[i].cp; - msg.len = 2; - - rc = tuner_transfer(fe, &msg, 1); - if (rc < 0) - goto err; - - priv->frequency = c->frequency; - priv->bandwidth = c->bandwidth_hz; - - return 0; - -err: - printk(KERN_ERR "%s: could not write to tuner at addr: 0x%02x\n", - __func__, priv->i2c_addr << 1); - return rc; -} - -static int tda827xo_sleep(struct dvb_frontend *fe) -{ - struct tda827x_priv *priv = fe->tuner_priv; - static u8 buf[] = { 0x30, 0xd0 }; - struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, - .buf = buf, .len = sizeof(buf) }; - - dprintk("%s:\n", __func__); - tuner_transfer(fe, &msg, 1); - - if (priv->cfg && priv->cfg->sleep) - priv->cfg->sleep(fe); - - return 0; -} - -/* ------------------------------------------------------------------ */ - -static int tda827xo_set_analog_params(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - unsigned char tuner_reg[8]; - unsigned char reg2[2]; - u32 N; - int i; - struct tda827x_priv *priv = fe->tuner_priv; - struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0 }; - unsigned int freq = params->frequency; - - tda827x_set_std(fe, params); - - if (params->mode == V4L2_TUNER_RADIO) - freq = freq / 1000; - - N = freq + priv->sgIF; - - i = 0; - while (tda827x_table[i].lomax < N * 62500) { - if (tda827x_table[i + 1].lomax == 0) - break; - i++; - } - - N = N << tda827x_table[i].spd; - - tuner_reg[0] = 0; - tuner_reg[1] = (unsigned char)(N>>8); - tuner_reg[2] = (unsigned char) N; - tuner_reg[3] = 0x40; - tuner_reg[4] = 0x52 + (priv->lpsel << 5); - tuner_reg[5] = (tda827x_table[i].spd << 6) + - (tda827x_table[i].div1p5 << 5) + - (tda827x_table[i].bs << 3) + tda827x_table[i].bp; - tuner_reg[6] = 0x8f + (tda827x_table[i].gc3 << 4); - tuner_reg[7] = 0x8f; - - msg.buf = tuner_reg; - msg.len = 8; - tuner_transfer(fe, &msg, 1); - - msg.buf = reg2; - msg.len = 2; - reg2[0] = 0x80; - reg2[1] = 0; - tuner_transfer(fe, &msg, 1); - - reg2[0] = 0x60; - reg2[1] = 0xbf; - tuner_transfer(fe, &msg, 1); - - reg2[0] = 0x30; - reg2[1] = tuner_reg[4] + 0x80; - tuner_transfer(fe, &msg, 1); - - msleep(1); - reg2[0] = 0x30; - reg2[1] = tuner_reg[4] + 4; - tuner_transfer(fe, &msg, 1); - - msleep(1); - reg2[0] = 0x30; - reg2[1] = tuner_reg[4]; - tuner_transfer(fe, &msg, 1); - - msleep(550); - reg2[0] = 0x30; - reg2[1] = (tuner_reg[4] & 0xfc) + tda827x_table[i].cp; - tuner_transfer(fe, &msg, 1); - - reg2[0] = 0x60; - reg2[1] = 0x3f; - tuner_transfer(fe, &msg, 1); - - reg2[0] = 0x80; - reg2[1] = 0x08; /* Vsync en */ - tuner_transfer(fe, &msg, 1); - - priv->frequency = params->frequency; - - return 0; -} - -static void tda827xo_agcf(struct dvb_frontend *fe) -{ - struct tda827x_priv *priv = fe->tuner_priv; - unsigned char data[] = { 0x80, 0x0c }; - struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, - .buf = data, .len = 2}; - - tuner_transfer(fe, &msg, 1); -} - -/* ------------------------------------------------------------------ */ - -struct tda827xa_data { - u32 lomax; - u8 svco; - u8 spd; - u8 scr; - u8 sbs; - u8 gc3; -}; - -static struct tda827xa_data tda827xa_dvbt[] = { - { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 1}, - { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, - { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, - { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, - { .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, - { .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, - { .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, - { .lomax = 290000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, - { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1}, - { .lomax = 550000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, - { .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, - { .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, - { .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, - { .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, - { .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, - { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0}, - { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} -}; - -static struct tda827xa_data tda827xa_dvbc[] = { - { .lomax = 50125000, .svco = 2, .spd = 4, .scr = 2, .sbs = 0, .gc3 = 3}, - { .lomax = 58500000, .svco = 3, .spd = 4, .scr = 2, .sbs = 0, .gc3 = 3}, - { .lomax = 69250000, .svco = 0, .spd = 3, .scr = 2, .sbs = 0, .gc3 = 3}, - { .lomax = 83625000, .svco = 1, .spd = 3, .scr = 2, .sbs = 0, .gc3 = 3}, - { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 2, .sbs = 0, .gc3 = 3}, - { .lomax = 100250000, .svco = 2, .spd = 3, .scr = 2, .sbs = 1, .gc3 = 1}, - { .lomax = 117000000, .svco = 3, .spd = 3, .scr = 2, .sbs = 1, .gc3 = 1}, - { .lomax = 138500000, .svco = 0, .spd = 2, .scr = 2, .sbs = 1, .gc3 = 1}, - { .lomax = 167250000, .svco = 1, .spd = 2, .scr = 2, .sbs = 1, .gc3 = 1}, - { .lomax = 187000000, .svco = 2, .spd = 2, .scr = 2, .sbs = 1, .gc3 = 1}, - { .lomax = 200500000, .svco = 2, .spd = 2, .scr = 2, .sbs = 2, .gc3 = 1}, - { .lomax = 234000000, .svco = 3, .spd = 2, .scr = 2, .sbs = 2, .gc3 = 3}, - { .lomax = 277000000, .svco = 0, .spd = 1, .scr = 2, .sbs = 2, .gc3 = 3}, - { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 2, .sbs = 2, .gc3 = 1}, - { .lomax = 334500000, .svco = 1, .spd = 1, .scr = 2, .sbs = 3, .gc3 = 3}, - { .lomax = 401000000, .svco = 2, .spd = 1, .scr = 2, .sbs = 3, .gc3 = 3}, - { .lomax = 468000000, .svco = 3, .spd = 1, .scr = 2, .sbs = 3, .gc3 = 1}, - { .lomax = 535000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1}, - { .lomax = 554000000, .svco = 0, .spd = 0, .scr = 2, .sbs = 3, .gc3 = 1}, - { .lomax = 638000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1}, - { .lomax = 669000000, .svco = 1, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 1}, - { .lomax = 720000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1}, - { .lomax = 802000000, .svco = 2, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 1}, - { .lomax = 835000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1}, - { .lomax = 885000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1}, - { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 1}, - { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} -}; - -static struct tda827xa_data tda827xa_analog[] = { - { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 3}, - { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3}, - { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3}, - { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3}, - { .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, - { .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 3}, - { .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 3}, - { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, - { .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 3}, - { .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 3}, - { .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1}, - { .lomax = 554000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, - { .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, - { .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, - { .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, - { .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, - { .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, - { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0}, - { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} -}; - -static int tda827xa_sleep(struct dvb_frontend *fe) -{ - struct tda827x_priv *priv = fe->tuner_priv; - static u8 buf[] = { 0x30, 0x90 }; - struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, - .buf = buf, .len = sizeof(buf) }; - - dprintk("%s:\n", __func__); - - tuner_transfer(fe, &msg, 1); - - if (priv->cfg && priv->cfg->sleep) - priv->cfg->sleep(fe); - - return 0; -} - -static void tda827xa_lna_gain(struct dvb_frontend *fe, int high, - struct analog_parameters *params) -{ - struct tda827x_priv *priv = fe->tuner_priv; - unsigned char buf[] = {0x22, 0x01}; - int arg; - int gp_func; - struct i2c_msg msg = { .flags = 0, .buf = buf, .len = sizeof(buf) }; - - if (NULL == priv->cfg) { - dprintk("tda827x_config not defined, cannot set LNA gain!\n"); - return; - } - msg.addr = priv->cfg->switch_addr; - if (priv->cfg->config) { - if (high) - dprintk("setting LNA to high gain\n"); - else - dprintk("setting LNA to low gain\n"); - } - switch (priv->cfg->config) { - case 0: /* no LNA */ - break; - case 1: /* switch is GPIO 0 of tda8290 */ - case 2: - if (params == NULL) { - gp_func = 0; - arg = 0; - } else { - /* turn Vsync on */ - gp_func = 1; - if (params->std & V4L2_STD_MN) - arg = 1; - else - arg = 0; - } - if (fe->callback) - fe->callback(priv->i2c_adap->algo_data, - DVB_FRONTEND_COMPONENT_TUNER, - gp_func, arg); - buf[1] = high ? 0 : 1; - if (priv->cfg->config == 2) - buf[1] = high ? 1 : 0; - tuner_transfer(fe, &msg, 1); - break; - case 3: /* switch with GPIO of saa713x */ - if (fe->callback) - fe->callback(priv->i2c_adap->algo_data, - DVB_FRONTEND_COMPONENT_TUNER, 0, high); - break; - } -} - -static int tda827xa_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct tda827x_priv *priv = fe->tuner_priv; - struct tda827xa_data *frequency_map = tda827xa_dvbt; - u8 buf[11]; - - struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, - .buf = buf, .len = sizeof(buf) }; - - int i, tuner_freq, if_freq, rc; - u32 N; - - dprintk("%s:\n", __func__); - - tda827xa_lna_gain(fe, 1, NULL); - msleep(20); - - if (c->bandwidth_hz == 0) { - if_freq = 5000000; - } else if (c->bandwidth_hz <= 6000000) { - if_freq = 4000000; - } else if (c->bandwidth_hz <= 7000000) { - if_freq = 4500000; - } else { /* 8 MHz */ - if_freq = 5000000; - } - tuner_freq = c->frequency; - - switch (c->delivery_system) { - case SYS_DVBC_ANNEX_A: - case SYS_DVBC_ANNEX_C: - dprintk("%s select tda827xa_dvbc\n", __func__); - frequency_map = tda827xa_dvbc; - break; - default: - break; - } - - i = 0; - while (frequency_map[i].lomax < tuner_freq) { - if (frequency_map[i + 1].lomax == 0) - break; - i++; - } - - tuner_freq += if_freq; - - N = ((tuner_freq + 31250) / 62500) << frequency_map[i].spd; - buf[0] = 0; // subaddress - buf[1] = N >> 8; - buf[2] = N & 0xff; - buf[3] = 0; - buf[4] = 0x16; - buf[5] = (frequency_map[i].spd << 5) + (frequency_map[i].svco << 3) + - frequency_map[i].sbs; - buf[6] = 0x4b + (frequency_map[i].gc3 << 4); - buf[7] = 0x1c; - buf[8] = 0x06; - buf[9] = 0x24; - buf[10] = 0x00; - msg.len = 11; - rc = tuner_transfer(fe, &msg, 1); - if (rc < 0) - goto err; - - buf[0] = 0x90; - buf[1] = 0xff; - buf[2] = 0x60; - buf[3] = 0x00; - buf[4] = 0x59; // lpsel, for 6MHz + 2 - msg.len = 5; - rc = tuner_transfer(fe, &msg, 1); - if (rc < 0) - goto err; - - buf[0] = 0xa0; - buf[1] = 0x40; - msg.len = 2; - rc = tuner_transfer(fe, &msg, 1); - if (rc < 0) - goto err; - - msleep(11); - msg.flags = I2C_M_RD; - rc = tuner_transfer(fe, &msg, 1); - if (rc < 0) - goto err; - msg.flags = 0; - - buf[1] >>= 4; - dprintk("tda8275a AGC2 gain is: %d\n", buf[1]); - if ((buf[1]) < 2) { - tda827xa_lna_gain(fe, 0, NULL); - buf[0] = 0x60; - buf[1] = 0x0c; - rc = tuner_transfer(fe, &msg, 1); - if (rc < 0) - goto err; - } - - buf[0] = 0xc0; - buf[1] = 0x99; // lpsel, for 6MHz + 2 - rc = tuner_transfer(fe, &msg, 1); - if (rc < 0) - goto err; - - buf[0] = 0x60; - buf[1] = 0x3c; - rc = tuner_transfer(fe, &msg, 1); - if (rc < 0) - goto err; - - /* correct CP value */ - buf[0] = 0x30; - buf[1] = 0x10 + frequency_map[i].scr; - rc = tuner_transfer(fe, &msg, 1); - if (rc < 0) - goto err; - - msleep(163); - buf[0] = 0xc0; - buf[1] = 0x39; // lpsel, for 6MHz + 2 - rc = tuner_transfer(fe, &msg, 1); - if (rc < 0) - goto err; - - msleep(3); - /* freeze AGC1 */ - buf[0] = 0x50; - buf[1] = 0x4f + (frequency_map[i].gc3 << 4); - rc = tuner_transfer(fe, &msg, 1); - if (rc < 0) - goto err; - - priv->frequency = c->frequency; - priv->bandwidth = c->bandwidth_hz; - - return 0; - -err: - printk(KERN_ERR "%s: could not write to tuner at addr: 0x%02x\n", - __func__, priv->i2c_addr << 1); - return rc; -} - - -static int tda827xa_set_analog_params(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - unsigned char tuner_reg[11]; - u32 N; - int i; - struct tda827x_priv *priv = fe->tuner_priv; - struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, - .buf = tuner_reg, .len = sizeof(tuner_reg) }; - unsigned int freq = params->frequency; - - tda827x_set_std(fe, params); - - tda827xa_lna_gain(fe, 1, params); - msleep(10); - - if (params->mode == V4L2_TUNER_RADIO) - freq = freq / 1000; - - N = freq + priv->sgIF; - - i = 0; - while (tda827xa_analog[i].lomax < N * 62500) { - if (tda827xa_analog[i + 1].lomax == 0) - break; - i++; - } - - N = N << tda827xa_analog[i].spd; - - tuner_reg[0] = 0; - tuner_reg[1] = (unsigned char)(N>>8); - tuner_reg[2] = (unsigned char) N; - tuner_reg[3] = 0; - tuner_reg[4] = 0x16; - tuner_reg[5] = (tda827xa_analog[i].spd << 5) + - (tda827xa_analog[i].svco << 3) + - tda827xa_analog[i].sbs; - tuner_reg[6] = 0x8b + (tda827xa_analog[i].gc3 << 4); - tuner_reg[7] = 0x1c; - tuner_reg[8] = 4; - tuner_reg[9] = 0x20; - tuner_reg[10] = 0x00; - msg.len = 11; - tuner_transfer(fe, &msg, 1); - - tuner_reg[0] = 0x90; - tuner_reg[1] = 0xff; - tuner_reg[2] = 0xe0; - tuner_reg[3] = 0; - tuner_reg[4] = 0x99 + (priv->lpsel << 1); - msg.len = 5; - tuner_transfer(fe, &msg, 1); - - tuner_reg[0] = 0xa0; - tuner_reg[1] = 0xc0; - msg.len = 2; - tuner_transfer(fe, &msg, 1); - - tuner_reg[0] = 0x30; - tuner_reg[1] = 0x10 + tda827xa_analog[i].scr; - tuner_transfer(fe, &msg, 1); - - msg.flags = I2C_M_RD; - tuner_transfer(fe, &msg, 1); - msg.flags = 0; - tuner_reg[1] >>= 4; - dprintk("AGC2 gain is: %d\n", tuner_reg[1]); - if (tuner_reg[1] < 1) - tda827xa_lna_gain(fe, 0, params); - - msleep(100); - tuner_reg[0] = 0x60; - tuner_reg[1] = 0x3c; - tuner_transfer(fe, &msg, 1); - - msleep(163); - tuner_reg[0] = 0x50; - tuner_reg[1] = 0x8f + (tda827xa_analog[i].gc3 << 4); - tuner_transfer(fe, &msg, 1); - - tuner_reg[0] = 0x80; - tuner_reg[1] = 0x28; - tuner_transfer(fe, &msg, 1); - - tuner_reg[0] = 0xb0; - tuner_reg[1] = 0x01; - tuner_transfer(fe, &msg, 1); - - tuner_reg[0] = 0xc0; - tuner_reg[1] = 0x19 + (priv->lpsel << 1); - tuner_transfer(fe, &msg, 1); - - priv->frequency = params->frequency; - - return 0; -} - -static void tda827xa_agcf(struct dvb_frontend *fe) -{ - struct tda827x_priv *priv = fe->tuner_priv; - unsigned char data[] = {0x80, 0x2c}; - struct i2c_msg msg = {.addr = priv->i2c_addr, .flags = 0, - .buf = data, .len = 2}; - tuner_transfer(fe, &msg, 1); -} - -/* ------------------------------------------------------------------ */ - -static int tda827x_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - return 0; -} - -static int tda827x_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct tda827x_priv *priv = fe->tuner_priv; - *frequency = priv->frequency; - return 0; -} - -static int tda827x_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) -{ - struct tda827x_priv *priv = fe->tuner_priv; - *bandwidth = priv->bandwidth; - return 0; -} - -static int tda827x_init(struct dvb_frontend *fe) -{ - struct tda827x_priv *priv = fe->tuner_priv; - dprintk("%s:\n", __func__); - if (priv->cfg && priv->cfg->init) - priv->cfg->init(fe); - - return 0; -} - -static int tda827x_probe_version(struct dvb_frontend *fe); - -static int tda827x_initial_init(struct dvb_frontend *fe) -{ - int ret; - ret = tda827x_probe_version(fe); - if (ret) - return ret; - return fe->ops.tuner_ops.init(fe); -} - -static int tda827x_initial_sleep(struct dvb_frontend *fe) -{ - int ret; - ret = tda827x_probe_version(fe); - if (ret) - return ret; - return fe->ops.tuner_ops.sleep(fe); -} - -static struct dvb_tuner_ops tda827xo_tuner_ops = { - .info = { - .name = "Philips TDA827X", - .frequency_min = 55000000, - .frequency_max = 860000000, - .frequency_step = 250000 - }, - .release = tda827x_release, - .init = tda827x_initial_init, - .sleep = tda827x_initial_sleep, - .set_params = tda827xo_set_params, - .set_analog_params = tda827xo_set_analog_params, - .get_frequency = tda827x_get_frequency, - .get_bandwidth = tda827x_get_bandwidth, -}; - -static struct dvb_tuner_ops tda827xa_tuner_ops = { - .info = { - .name = "Philips TDA827XA", - .frequency_min = 44000000, - .frequency_max = 906000000, - .frequency_step = 62500 - }, - .release = tda827x_release, - .init = tda827x_init, - .sleep = tda827xa_sleep, - .set_params = tda827xa_set_params, - .set_analog_params = tda827xa_set_analog_params, - .get_frequency = tda827x_get_frequency, - .get_bandwidth = tda827x_get_bandwidth, -}; - -static int tda827x_probe_version(struct dvb_frontend *fe) -{ - u8 data; - int rc; - struct tda827x_priv *priv = fe->tuner_priv; - struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = I2C_M_RD, - .buf = &data, .len = 1 }; - - rc = tuner_transfer(fe, &msg, 1); - - if (rc < 0) { - printk("%s: could not read from tuner at addr: 0x%02x\n", - __func__, msg.addr << 1); - return rc; - } - if ((data & 0x3c) == 0) { - dprintk("tda827x tuner found\n"); - fe->ops.tuner_ops.init = tda827x_init; - fe->ops.tuner_ops.sleep = tda827xo_sleep; - if (priv->cfg) - priv->cfg->agcf = tda827xo_agcf; - } else { - dprintk("tda827xa tuner found\n"); - memcpy(&fe->ops.tuner_ops, &tda827xa_tuner_ops, sizeof(struct dvb_tuner_ops)); - if (priv->cfg) - priv->cfg->agcf = tda827xa_agcf; - } - return 0; -} - -struct dvb_frontend *tda827x_attach(struct dvb_frontend *fe, int addr, - struct i2c_adapter *i2c, - struct tda827x_config *cfg) -{ - struct tda827x_priv *priv = NULL; - - dprintk("%s:\n", __func__); - priv = kzalloc(sizeof(struct tda827x_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - - priv->i2c_addr = addr; - priv->i2c_adap = i2c; - priv->cfg = cfg; - memcpy(&fe->ops.tuner_ops, &tda827xo_tuner_ops, sizeof(struct dvb_tuner_ops)); - fe->tuner_priv = priv; - - dprintk("type set to %s\n", fe->ops.tuner_ops.info.name); - - return fe; -} -EXPORT_SYMBOL_GPL(tda827x_attach); - -MODULE_DESCRIPTION("DVB TDA827x driver"); -MODULE_AUTHOR("Hartmut Hackmann "); -MODULE_AUTHOR("Michael Krufky "); -MODULE_LICENSE("GPL"); - -/* - * 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/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 -#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.c b/drivers/media/common/tuners/tda8290.c deleted file mode 100644 index 8c4852114eeb..000000000000 --- a/drivers/media/common/tuners/tda8290.c +++ /dev/null @@ -1,874 +0,0 @@ -/* - - i2c tv tuner chip device driver - controls the philips tda8290+75 tuner chip combo. - - 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 "tda8290" module was split apart from the original "tuner" module. -*/ - -#include -#include -#include -#include -#include "tuner-i2c.h" -#include "tda8290.h" -#include "tda827x.h" -#include "tda18271.h" - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable verbose debug messages"); - -static int deemphasis_50; -module_param(deemphasis_50, int, 0644); -MODULE_PARM_DESC(deemphasis_50, "0 - 75us deemphasis; 1 - 50us deemphasis"); - -/* ---------------------------------------------------------------------- */ - -struct tda8290_priv { - struct tuner_i2c_props i2c_props; - - unsigned char tda8290_easy_mode; - - unsigned char tda827x_addr; - - unsigned char ver; -#define TDA8290 1 -#define TDA8295 2 -#define TDA8275 4 -#define TDA8275A 8 -#define TDA18271 16 - - struct tda827x_config cfg; -}; - -/*---------------------------------------------------------------------*/ - -static int tda8290_i2c_bridge(struct dvb_frontend *fe, int close) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - - unsigned char enable[2] = { 0x21, 0xC0 }; - unsigned char disable[2] = { 0x21, 0x00 }; - unsigned char *msg; - - if (close) { - msg = enable; - tuner_i2c_xfer_send(&priv->i2c_props, msg, 2); - /* let the bridge stabilize */ - msleep(20); - } else { - msg = disable; - tuner_i2c_xfer_send(&priv->i2c_props, msg, 2); - } - - return 0; -} - -static int tda8295_i2c_bridge(struct dvb_frontend *fe, int close) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - - unsigned char enable[2] = { 0x45, 0xc1 }; - unsigned char disable[2] = { 0x46, 0x00 }; - unsigned char buf[3] = { 0x45, 0x01, 0x00 }; - unsigned char *msg; - - if (close) { - msg = enable; - tuner_i2c_xfer_send(&priv->i2c_props, msg, 2); - /* let the bridge stabilize */ - msleep(20); - } else { - msg = disable; - tuner_i2c_xfer_send_recv(&priv->i2c_props, msg, 1, &msg[1], 1); - - buf[2] = msg[1]; - buf[2] &= ~0x04; - tuner_i2c_xfer_send(&priv->i2c_props, buf, 3); - msleep(5); - - msg[1] |= 0x04; - tuner_i2c_xfer_send(&priv->i2c_props, msg, 2); - } - - return 0; -} - -/*---------------------------------------------------------------------*/ - -static void set_audio(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - char* mode; - - if (params->std & V4L2_STD_MN) { - priv->tda8290_easy_mode = 0x01; - mode = "MN"; - } else if (params->std & V4L2_STD_B) { - priv->tda8290_easy_mode = 0x02; - mode = "B"; - } else if (params->std & V4L2_STD_GH) { - priv->tda8290_easy_mode = 0x04; - mode = "GH"; - } else if (params->std & V4L2_STD_PAL_I) { - priv->tda8290_easy_mode = 0x08; - mode = "I"; - } else if (params->std & V4L2_STD_DK) { - priv->tda8290_easy_mode = 0x10; - mode = "DK"; - } else if (params->std & V4L2_STD_SECAM_L) { - priv->tda8290_easy_mode = 0x20; - mode = "L"; - } else if (params->std & V4L2_STD_SECAM_LC) { - priv->tda8290_easy_mode = 0x40; - mode = "LC"; - } else { - priv->tda8290_easy_mode = 0x10; - mode = "xx"; - } - - if (params->mode == V4L2_TUNER_RADIO) { - /* Set TDA8295 to FM radio; Start TDA8290 with MN values */ - priv->tda8290_easy_mode = (priv->ver & TDA8295) ? 0x80 : 0x01; - tuner_dbg("setting to radio FM\n"); - } else { - tuner_dbg("setting tda829x to system %s\n", mode); - } -} - -static struct { - unsigned char seq[2]; -} fm_mode[] = { - { { 0x01, 0x81} }, /* Put device into expert mode */ - { { 0x03, 0x48} }, /* Disable NOTCH and VIDEO filters */ - { { 0x04, 0x04} }, /* Disable color carrier filter (SSIF) */ - { { 0x05, 0x04} }, /* ADC headroom */ - { { 0x06, 0x10} }, /* group delay flat */ - - { { 0x07, 0x00} }, /* use the same radio DTO values as a tda8295 */ - { { 0x08, 0x00} }, - { { 0x09, 0x80} }, - { { 0x0a, 0xda} }, - { { 0x0b, 0x4b} }, - { { 0x0c, 0x68} }, - - { { 0x0d, 0x00} }, /* PLL off, no video carrier detect */ - { { 0x14, 0x00} }, /* disable auto mute if no video */ -}; - -static void tda8290_set_params(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - - unsigned char soft_reset[] = { 0x00, 0x00 }; - unsigned char easy_mode[] = { 0x01, priv->tda8290_easy_mode }; - unsigned char expert_mode[] = { 0x01, 0x80 }; - unsigned char agc_out_on[] = { 0x02, 0x00 }; - unsigned char gainset_off[] = { 0x28, 0x14 }; - unsigned char if_agc_spd[] = { 0x0f, 0x88 }; - unsigned char adc_head_6[] = { 0x05, 0x04 }; - unsigned char adc_head_9[] = { 0x05, 0x02 }; - unsigned char adc_head_12[] = { 0x05, 0x01 }; - unsigned char pll_bw_nom[] = { 0x0d, 0x47 }; - unsigned char pll_bw_low[] = { 0x0d, 0x27 }; - unsigned char gainset_2[] = { 0x28, 0x64 }; - unsigned char agc_rst_on[] = { 0x0e, 0x0b }; - unsigned char agc_rst_off[] = { 0x0e, 0x09 }; - unsigned char if_agc_set[] = { 0x0f, 0x81 }; - unsigned char addr_adc_sat = 0x1a; - unsigned char addr_agc_stat = 0x1d; - unsigned char addr_pll_stat = 0x1b; - unsigned char adc_sat, agc_stat, - pll_stat; - int i; - - set_audio(fe, params); - - if (priv->cfg.config) - tuner_dbg("tda827xa config is 0x%02x\n", priv->cfg.config); - tuner_i2c_xfer_send(&priv->i2c_props, easy_mode, 2); - tuner_i2c_xfer_send(&priv->i2c_props, agc_out_on, 2); - tuner_i2c_xfer_send(&priv->i2c_props, soft_reset, 2); - msleep(1); - - if (params->mode == V4L2_TUNER_RADIO) { - unsigned char deemphasis[] = { 0x13, 1 }; - - /* FIXME: allow using a different deemphasis */ - - if (deemphasis_50) - deemphasis[1] = 2; - - for (i = 0; i < ARRAY_SIZE(fm_mode); i++) - tuner_i2c_xfer_send(&priv->i2c_props, fm_mode[i].seq, 2); - - tuner_i2c_xfer_send(&priv->i2c_props, deemphasis, 2); - } else { - expert_mode[1] = priv->tda8290_easy_mode + 0x80; - tuner_i2c_xfer_send(&priv->i2c_props, expert_mode, 2); - tuner_i2c_xfer_send(&priv->i2c_props, gainset_off, 2); - tuner_i2c_xfer_send(&priv->i2c_props, if_agc_spd, 2); - if (priv->tda8290_easy_mode & 0x60) - tuner_i2c_xfer_send(&priv->i2c_props, adc_head_9, 2); - else - tuner_i2c_xfer_send(&priv->i2c_props, adc_head_6, 2); - tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2); - } - - - tda8290_i2c_bridge(fe, 1); - - if (fe->ops.tuner_ops.set_analog_params) - fe->ops.tuner_ops.set_analog_params(fe, params); - - for (i = 0; i < 3; i++) { - tuner_i2c_xfer_send_recv(&priv->i2c_props, - &addr_pll_stat, 1, &pll_stat, 1); - if (pll_stat & 0x80) { - tuner_i2c_xfer_send_recv(&priv->i2c_props, - &addr_adc_sat, 1, - &adc_sat, 1); - tuner_i2c_xfer_send_recv(&priv->i2c_props, - &addr_agc_stat, 1, - &agc_stat, 1); - tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat); - break; - } else { - tuner_dbg("tda8290 not locked, no signal?\n"); - msleep(100); - } - } - /* adjust headroom resp. gain */ - if ((agc_stat > 115) || (!(pll_stat & 0x80) && (adc_sat < 20))) { - tuner_dbg("adjust gain, step 1. Agc: %d, ADC stat: %d, lock: %d\n", - agc_stat, adc_sat, pll_stat & 0x80); - tuner_i2c_xfer_send(&priv->i2c_props, gainset_2, 2); - msleep(100); - tuner_i2c_xfer_send_recv(&priv->i2c_props, - &addr_agc_stat, 1, &agc_stat, 1); - tuner_i2c_xfer_send_recv(&priv->i2c_props, - &addr_pll_stat, 1, &pll_stat, 1); - if ((agc_stat > 115) || !(pll_stat & 0x80)) { - tuner_dbg("adjust gain, step 2. Agc: %d, lock: %d\n", - agc_stat, pll_stat & 0x80); - if (priv->cfg.agcf) - priv->cfg.agcf(fe); - msleep(100); - tuner_i2c_xfer_send_recv(&priv->i2c_props, - &addr_agc_stat, 1, - &agc_stat, 1); - tuner_i2c_xfer_send_recv(&priv->i2c_props, - &addr_pll_stat, 1, - &pll_stat, 1); - if((agc_stat > 115) || !(pll_stat & 0x80)) { - tuner_dbg("adjust gain, step 3. Agc: %d\n", agc_stat); - tuner_i2c_xfer_send(&priv->i2c_props, adc_head_12, 2); - tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_low, 2); - msleep(100); - } - } - } - - /* l/ l' deadlock? */ - if(priv->tda8290_easy_mode & 0x60) { - tuner_i2c_xfer_send_recv(&priv->i2c_props, - &addr_adc_sat, 1, - &adc_sat, 1); - tuner_i2c_xfer_send_recv(&priv->i2c_props, - &addr_pll_stat, 1, - &pll_stat, 1); - if ((adc_sat > 20) || !(pll_stat & 0x80)) { - tuner_dbg("trying to resolve SECAM L deadlock\n"); - tuner_i2c_xfer_send(&priv->i2c_props, agc_rst_on, 2); - msleep(40); - tuner_i2c_xfer_send(&priv->i2c_props, agc_rst_off, 2); - } - } - - tda8290_i2c_bridge(fe, 0); - tuner_i2c_xfer_send(&priv->i2c_props, if_agc_set, 2); -} - -/*---------------------------------------------------------------------*/ - -static void tda8295_power(struct dvb_frontend *fe, int enable) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - unsigned char buf[] = { 0x30, 0x00 }; /* clb_stdbt */ - - tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1); - - if (enable) - buf[1] = 0x01; - else - buf[1] = 0x03; - - tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); -} - -static void tda8295_set_easy_mode(struct dvb_frontend *fe, int enable) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - unsigned char buf[] = { 0x01, 0x00 }; - - tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1); - - if (enable) - buf[1] = 0x01; /* rising edge sets regs 0x02 - 0x23 */ - else - buf[1] = 0x00; /* reset active bit */ - - tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); -} - -static void tda8295_set_video_std(struct dvb_frontend *fe) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - unsigned char buf[] = { 0x00, priv->tda8290_easy_mode }; - - tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); - - tda8295_set_easy_mode(fe, 1); - msleep(20); - tda8295_set_easy_mode(fe, 0); -} - -/*---------------------------------------------------------------------*/ - -static void tda8295_agc1_out(struct dvb_frontend *fe, int enable) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - unsigned char buf[] = { 0x02, 0x00 }; /* DIV_FUNC */ - - tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1); - - if (enable) - buf[1] &= ~0x40; - else - buf[1] |= 0x40; - - tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); -} - -static void tda8295_agc2_out(struct dvb_frontend *fe, int enable) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - unsigned char set_gpio_cf[] = { 0x44, 0x00 }; - unsigned char set_gpio_val[] = { 0x46, 0x00 }; - - tuner_i2c_xfer_send_recv(&priv->i2c_props, - &set_gpio_cf[0], 1, &set_gpio_cf[1], 1); - tuner_i2c_xfer_send_recv(&priv->i2c_props, - &set_gpio_val[0], 1, &set_gpio_val[1], 1); - - set_gpio_cf[1] &= 0xf0; /* clear GPIO_0 bits 3-0 */ - - if (enable) { - set_gpio_cf[1] |= 0x01; /* config GPIO_0 as Open Drain Out */ - set_gpio_val[1] &= 0xfe; /* set GPIO_0 pin low */ - } - tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_cf, 2); - tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_val, 2); -} - -static int tda8295_has_signal(struct dvb_frontend *fe) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - - unsigned char hvpll_stat = 0x26; - unsigned char ret; - - tuner_i2c_xfer_send_recv(&priv->i2c_props, &hvpll_stat, 1, &ret, 1); - return (ret & 0x01) ? 65535 : 0; -} - -/*---------------------------------------------------------------------*/ - -static void tda8295_set_params(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - - unsigned char blanking_mode[] = { 0x1d, 0x00 }; - - set_audio(fe, params); - - tuner_dbg("%s: freq = %d\n", __func__, params->frequency); - - tda8295_power(fe, 1); - tda8295_agc1_out(fe, 1); - - tuner_i2c_xfer_send_recv(&priv->i2c_props, - &blanking_mode[0], 1, &blanking_mode[1], 1); - - tda8295_set_video_std(fe); - - blanking_mode[1] = 0x03; - tuner_i2c_xfer_send(&priv->i2c_props, blanking_mode, 2); - msleep(20); - - tda8295_i2c_bridge(fe, 1); - - if (fe->ops.tuner_ops.set_analog_params) - fe->ops.tuner_ops.set_analog_params(fe, params); - - if (priv->cfg.agcf) - priv->cfg.agcf(fe); - - if (tda8295_has_signal(fe)) - tuner_dbg("tda8295 is locked\n"); - else - tuner_dbg("tda8295 not locked, no signal?\n"); - - tda8295_i2c_bridge(fe, 0); -} - -/*---------------------------------------------------------------------*/ - -static int tda8290_has_signal(struct dvb_frontend *fe) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - - unsigned char i2c_get_afc[1] = { 0x1B }; - unsigned char afc = 0; - - tuner_i2c_xfer_send_recv(&priv->i2c_props, - i2c_get_afc, ARRAY_SIZE(i2c_get_afc), &afc, 1); - return (afc & 0x80)? 65535:0; -} - -/*---------------------------------------------------------------------*/ - -static void tda8290_standby(struct dvb_frontend *fe) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - - unsigned char cb1[] = { 0x30, 0xD0 }; - unsigned char tda8290_standby[] = { 0x00, 0x02 }; - unsigned char tda8290_agc_tri[] = { 0x02, 0x20 }; - struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, .buf=cb1, .len = 2}; - - tda8290_i2c_bridge(fe, 1); - if (priv->ver & TDA8275A) - cb1[1] = 0x90; - i2c_transfer(priv->i2c_props.adap, &msg, 1); - tda8290_i2c_bridge(fe, 0); - tuner_i2c_xfer_send(&priv->i2c_props, tda8290_agc_tri, 2); - tuner_i2c_xfer_send(&priv->i2c_props, tda8290_standby, 2); -} - -static void tda8295_standby(struct dvb_frontend *fe) -{ - tda8295_agc1_out(fe, 0); /* Put AGC in tri-state */ - - tda8295_power(fe, 0); -} - -static void tda8290_init_if(struct dvb_frontend *fe) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - - unsigned char set_VS[] = { 0x30, 0x6F }; - unsigned char set_GP00_CF[] = { 0x20, 0x01 }; - unsigned char set_GP01_CF[] = { 0x20, 0x0B }; - - if ((priv->cfg.config == 1) || (priv->cfg.config == 2)) - tuner_i2c_xfer_send(&priv->i2c_props, set_GP00_CF, 2); - else - tuner_i2c_xfer_send(&priv->i2c_props, set_GP01_CF, 2); - tuner_i2c_xfer_send(&priv->i2c_props, set_VS, 2); -} - -static void tda8295_init_if(struct dvb_frontend *fe) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - - static unsigned char set_adc_ctl[] = { 0x33, 0x14 }; - static unsigned char set_adc_ctl2[] = { 0x34, 0x00 }; - static unsigned char set_pll_reg6[] = { 0x3e, 0x63 }; - static unsigned char set_pll_reg0[] = { 0x38, 0x23 }; - static unsigned char set_pll_reg7[] = { 0x3f, 0x01 }; - static unsigned char set_pll_reg10[] = { 0x42, 0x61 }; - static unsigned char set_gpio_reg0[] = { 0x44, 0x0b }; - - tda8295_power(fe, 1); - - tda8295_set_easy_mode(fe, 0); - tda8295_set_video_std(fe); - - tuner_i2c_xfer_send(&priv->i2c_props, set_adc_ctl, 2); - tuner_i2c_xfer_send(&priv->i2c_props, set_adc_ctl2, 2); - tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg6, 2); - tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg0, 2); - tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg7, 2); - tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg10, 2); - tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_reg0, 2); - - tda8295_agc1_out(fe, 0); - tda8295_agc2_out(fe, 0); -} - -static void tda8290_init_tuner(struct dvb_frontend *fe) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - unsigned char tda8275_init[] = { 0x00, 0x00, 0x00, 0x40, 0xdC, 0x04, 0xAf, - 0x3F, 0x2A, 0x04, 0xFF, 0x00, 0x00, 0x40 }; - unsigned char tda8275a_init[] = { 0x00, 0x00, 0x00, 0x00, 0xdC, 0x05, 0x8b, - 0x0c, 0x04, 0x20, 0xFF, 0x00, 0x00, 0x4b }; - struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, - .buf=tda8275_init, .len = 14}; - if (priv->ver & TDA8275A) - msg.buf = tda8275a_init; - - tda8290_i2c_bridge(fe, 1); - i2c_transfer(priv->i2c_props.adap, &msg, 1); - tda8290_i2c_bridge(fe, 0); -} - -/*---------------------------------------------------------------------*/ - -static void tda829x_release(struct dvb_frontend *fe) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - - /* only try to release the tuner if we've - * attached it from within this module */ - if (priv->ver & (TDA18271 | TDA8275 | TDA8275A)) - if (fe->ops.tuner_ops.release) - fe->ops.tuner_ops.release(fe); - - kfree(fe->analog_demod_priv); - fe->analog_demod_priv = NULL; -} - -static struct tda18271_config tda829x_tda18271_config = { - .gate = TDA18271_GATE_ANALOG, -}; - -static int tda829x_find_tuner(struct dvb_frontend *fe) -{ - struct tda8290_priv *priv = fe->analog_demod_priv; - struct analog_demod_ops *analog_ops = &fe->ops.analog_ops; - int i, ret, tuners_found; - u32 tuner_addrs; - u8 data; - struct i2c_msg msg = { .flags = I2C_M_RD, .buf = &data, .len = 1 }; - - if (!analog_ops->i2c_gate_ctrl) { - printk(KERN_ERR "tda8290: no gate control were provided!\n"); - - return -EINVAL; - } - - analog_ops->i2c_gate_ctrl(fe, 1); - - /* probe for tuner chip */ - tuners_found = 0; - tuner_addrs = 0; - for (i = 0x60; i <= 0x63; i++) { - msg.addr = i; - ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); - if (ret == 1) { - tuners_found++; - tuner_addrs = (tuner_addrs << 8) + i; - } - } - /* if there is more than one tuner, we expect the right one is - behind the bridge and we choose the highest address that doesn't - give a response now - */ - - analog_ops->i2c_gate_ctrl(fe, 0); - - if (tuners_found > 1) - for (i = 0; i < tuners_found; i++) { - msg.addr = tuner_addrs & 0xff; - ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); - if (ret == 1) - tuner_addrs = tuner_addrs >> 8; - else - break; - } - - if (tuner_addrs == 0) { - tuner_addrs = 0x60; - tuner_info("could not clearly identify tuner address, " - "defaulting to %x\n", tuner_addrs); - } else { - tuner_addrs = tuner_addrs & 0xff; - tuner_info("setting tuner address to %x\n", tuner_addrs); - } - priv->tda827x_addr = tuner_addrs; - msg.addr = tuner_addrs; - - analog_ops->i2c_gate_ctrl(fe, 1); - ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); - - if (ret != 1) { - tuner_warn("tuner access failed!\n"); - analog_ops->i2c_gate_ctrl(fe, 0); - return -EREMOTEIO; - } - - if ((data == 0x83) || (data == 0x84)) { - priv->ver |= TDA18271; - tda829x_tda18271_config.config = priv->cfg.config; - dvb_attach(tda18271_attach, fe, priv->tda827x_addr, - priv->i2c_props.adap, &tda829x_tda18271_config); - } else { - if ((data & 0x3c) == 0) - priv->ver |= TDA8275; - else - priv->ver |= TDA8275A; - - dvb_attach(tda827x_attach, fe, priv->tda827x_addr, - priv->i2c_props.adap, &priv->cfg); - priv->cfg.switch_addr = priv->i2c_props.addr; - } - if (fe->ops.tuner_ops.init) - fe->ops.tuner_ops.init(fe); - - if (fe->ops.tuner_ops.sleep) - fe->ops.tuner_ops.sleep(fe); - - analog_ops->i2c_gate_ctrl(fe, 0); - - return 0; -} - -static int tda8290_probe(struct tuner_i2c_props *i2c_props) -{ -#define TDA8290_ID 0x89 - u8 reg = 0x1f, id; - struct i2c_msg msg_read[] = { - { .addr = i2c_props->addr, .flags = 0, .len = 1, .buf = ® }, - { .addr = i2c_props->addr, .flags = I2C_M_RD, .len = 1, .buf = &id }, - }; - - /* detect tda8290 */ - if (i2c_transfer(i2c_props->adap, msg_read, 2) != 2) { - printk(KERN_WARNING "%s: couldn't read register 0x%02x\n", - __func__, reg); - return -ENODEV; - } - - if (id == TDA8290_ID) { - if (debug) - printk(KERN_DEBUG "%s: tda8290 detected @ %d-%04x\n", - __func__, i2c_adapter_id(i2c_props->adap), - i2c_props->addr); - return 0; - } - return -ENODEV; -} - -static int tda8295_probe(struct tuner_i2c_props *i2c_props) -{ -#define TDA8295_ID 0x8a -#define TDA8295C2_ID 0x8b - u8 reg = 0x2f, id; - struct i2c_msg msg_read[] = { - { .addr = i2c_props->addr, .flags = 0, .len = 1, .buf = ® }, - { .addr = i2c_props->addr, .flags = I2C_M_RD, .len = 1, .buf = &id }, - }; - - /* detect tda8295 */ - if (i2c_transfer(i2c_props->adap, msg_read, 2) != 2) { - printk(KERN_WARNING "%s: couldn't read register 0x%02x\n", - __func__, reg); - return -ENODEV; - } - - if ((id & 0xfe) == TDA8295_ID) { - if (debug) - printk(KERN_DEBUG "%s: %s detected @ %d-%04x\n", - __func__, (id == TDA8295_ID) ? - "tda8295c1" : "tda8295c2", - i2c_adapter_id(i2c_props->adap), - i2c_props->addr); - return 0; - } - - return -ENODEV; -} - -static struct analog_demod_ops tda8290_ops = { - .set_params = tda8290_set_params, - .has_signal = tda8290_has_signal, - .standby = tda8290_standby, - .release = tda829x_release, - .i2c_gate_ctrl = tda8290_i2c_bridge, -}; - -static struct analog_demod_ops tda8295_ops = { - .set_params = tda8295_set_params, - .has_signal = tda8295_has_signal, - .standby = tda8295_standby, - .release = tda829x_release, - .i2c_gate_ctrl = tda8295_i2c_bridge, -}; - -struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c_adap, u8 i2c_addr, - struct tda829x_config *cfg) -{ - struct tda8290_priv *priv = NULL; - char *name; - - priv = kzalloc(sizeof(struct tda8290_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - fe->analog_demod_priv = priv; - - priv->i2c_props.addr = i2c_addr; - priv->i2c_props.adap = i2c_adap; - priv->i2c_props.name = "tda829x"; - if (cfg) - priv->cfg.config = cfg->lna_cfg; - - if (tda8290_probe(&priv->i2c_props) == 0) { - priv->ver = TDA8290; - memcpy(&fe->ops.analog_ops, &tda8290_ops, - sizeof(struct analog_demod_ops)); - } - - if (tda8295_probe(&priv->i2c_props) == 0) { - priv->ver = TDA8295; - memcpy(&fe->ops.analog_ops, &tda8295_ops, - sizeof(struct analog_demod_ops)); - } - - if (!(cfg) || (TDA829X_PROBE_TUNER == cfg->probe_tuner)) { - tda8295_power(fe, 1); - if (tda829x_find_tuner(fe) < 0) - goto fail; - } - - switch (priv->ver) { - case TDA8290: - name = "tda8290"; - break; - case TDA8295: - name = "tda8295"; - break; - case TDA8290 | TDA8275: - name = "tda8290+75"; - break; - case TDA8295 | TDA8275: - name = "tda8295+75"; - break; - case TDA8290 | TDA8275A: - name = "tda8290+75a"; - break; - case TDA8295 | TDA8275A: - name = "tda8295+75a"; - break; - case TDA8290 | TDA18271: - name = "tda8290+18271"; - break; - case TDA8295 | TDA18271: - name = "tda8295+18271"; - break; - default: - goto fail; - } - tuner_info("type set to %s\n", name); - - fe->ops.analog_ops.info.name = name; - - if (priv->ver & TDA8290) { - if (priv->ver & (TDA8275 | TDA8275A)) - tda8290_init_tuner(fe); - tda8290_init_if(fe); - } else if (priv->ver & TDA8295) - tda8295_init_if(fe); - - return fe; - -fail: - memset(&fe->ops.analog_ops, 0, sizeof(struct analog_demod_ops)); - - tda829x_release(fe); - return NULL; -} -EXPORT_SYMBOL_GPL(tda829x_attach); - -int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr) -{ - struct tuner_i2c_props i2c_props = { - .adap = i2c_adap, - .addr = i2c_addr, - }; - - unsigned char soft_reset[] = { 0x00, 0x00 }; - unsigned char easy_mode_b[] = { 0x01, 0x02 }; - unsigned char easy_mode_g[] = { 0x01, 0x04 }; - unsigned char restore_9886[] = { 0x00, 0xd6, 0x30 }; - unsigned char addr_dto_lsb = 0x07; - unsigned char data; -#define PROBE_BUFFER_SIZE 8 - unsigned char buf[PROBE_BUFFER_SIZE]; - int i; - - /* rule out tda9887, which would return the same byte repeatedly */ - tuner_i2c_xfer_send_recv(&i2c_props, - soft_reset, 1, buf, PROBE_BUFFER_SIZE); - for (i = 1; i < PROBE_BUFFER_SIZE; i++) { - if (buf[i] != buf[0]) - break; - } - - /* all bytes are equal, not a tda829x - probably a tda9887 */ - if (i == PROBE_BUFFER_SIZE) - return -ENODEV; - - if ((tda8290_probe(&i2c_props) == 0) || - (tda8295_probe(&i2c_props) == 0)) - return 0; - - /* fall back to old probing method */ - tuner_i2c_xfer_send(&i2c_props, easy_mode_b, 2); - tuner_i2c_xfer_send(&i2c_props, soft_reset, 2); - tuner_i2c_xfer_send_recv(&i2c_props, &addr_dto_lsb, 1, &data, 1); - if (data == 0) { - tuner_i2c_xfer_send(&i2c_props, easy_mode_g, 2); - tuner_i2c_xfer_send(&i2c_props, soft_reset, 2); - tuner_i2c_xfer_send_recv(&i2c_props, - &addr_dto_lsb, 1, &data, 1); - if (data == 0x7b) { - return 0; - } - } - tuner_i2c_xfer_send(&i2c_props, restore_9886, 3); - return -ENODEV; -} -EXPORT_SYMBOL_GPL(tda829x_probe); - -MODULE_DESCRIPTION("Philips/NXP TDA8290/TDA8295 analog IF demodulator driver"); -MODULE_AUTHOR("Gerd Knorr, Hartmut Hackmann, Michael Krufky"); -MODULE_LICENSE("GPL"); - -/* - * 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/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 -#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.c b/drivers/media/common/tuners/tda9887.c deleted file mode 100644 index cdb645d57438..000000000000 --- a/drivers/media/common/tuners/tda9887.c +++ /dev/null @@ -1,717 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "tuner-i2c.h" -#include "tda9887.h" - - -/* Chips: - TDA9885 (PAL, NTSC) - TDA9886 (PAL, SECAM, NTSC) - TDA9887 (PAL, SECAM, NTSC, FM Radio) - - Used as part of several tuners -*/ - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable verbose debug messages"); - -static DEFINE_MUTEX(tda9887_list_mutex); -static LIST_HEAD(hybrid_tuner_instance_list); - -struct tda9887_priv { - struct tuner_i2c_props i2c_props; - struct list_head hybrid_tuner_instance_list; - - unsigned char data[4]; - unsigned int config; - unsigned int mode; - unsigned int audmode; - v4l2_std_id std; - - bool standby; -}; - -/* ---------------------------------------------------------------------- */ - -#define UNSET (-1U) - -struct tvnorm { - v4l2_std_id std; - char *name; - unsigned char b; - unsigned char c; - unsigned char e; -}; - -/* ---------------------------------------------------------------------- */ - -// -// TDA defines -// - -//// first reg (b) -#define cVideoTrapBypassOFF 0x00 // bit b0 -#define cVideoTrapBypassON 0x01 // bit b0 - -#define cAutoMuteFmInactive 0x00 // bit b1 -#define cAutoMuteFmActive 0x02 // bit b1 - -#define cIntercarrier 0x00 // bit b2 -#define cQSS 0x04 // bit b2 - -#define cPositiveAmTV 0x00 // bit b3:4 -#define cFmRadio 0x08 // bit b3:4 -#define cNegativeFmTV 0x10 // bit b3:4 - - -#define cForcedMuteAudioON 0x20 // bit b5 -#define cForcedMuteAudioOFF 0x00 // bit b5 - -#define cOutputPort1Active 0x00 // bit b6 -#define cOutputPort1Inactive 0x40 // bit b6 - -#define cOutputPort2Active 0x00 // bit b7 -#define cOutputPort2Inactive 0x80 // bit b7 - - -//// second reg (c) -#define cDeemphasisOFF 0x00 // bit c5 -#define cDeemphasisON 0x20 // bit c5 - -#define cDeemphasis75 0x00 // bit c6 -#define cDeemphasis50 0x40 // bit c6 - -#define cAudioGain0 0x00 // bit c7 -#define cAudioGain6 0x80 // bit c7 - -#define cTopMask 0x1f // bit c0:4 -#define cTopDefault 0x10 // bit c0:4 - -//// third reg (e) -#define cAudioIF_4_5 0x00 // bit e0:1 -#define cAudioIF_5_5 0x01 // bit e0:1 -#define cAudioIF_6_0 0x02 // bit e0:1 -#define cAudioIF_6_5 0x03 // bit e0:1 - - -#define cVideoIFMask 0x1c // bit e2:4 -/* Video IF selection in TV Mode (bit B3=0) */ -#define cVideoIF_58_75 0x00 // bit e2:4 -#define cVideoIF_45_75 0x04 // bit e2:4 -#define cVideoIF_38_90 0x08 // bit e2:4 -#define cVideoIF_38_00 0x0C // bit e2:4 -#define cVideoIF_33_90 0x10 // bit e2:4 -#define cVideoIF_33_40 0x14 // bit e2:4 -#define cRadioIF_45_75 0x18 // bit e2:4 -#define cRadioIF_38_90 0x1C // bit e2:4 - -/* IF1 selection in Radio Mode (bit B3=1) */ -#define cRadioIF_33_30 0x00 // bit e2,4 (also 0x10,0x14) -#define cRadioIF_41_30 0x04 // bit e2,4 - -/* Output of AFC pin in radio mode when bit E7=1 */ -#define cRadioAGC_SIF 0x00 // bit e3 -#define cRadioAGC_FM 0x08 // bit e3 - -#define cTunerGainNormal 0x00 // bit e5 -#define cTunerGainLow 0x20 // bit e5 - -#define cGating_18 0x00 // bit e6 -#define cGating_36 0x40 // bit e6 - -#define cAgcOutON 0x80 // bit e7 -#define cAgcOutOFF 0x00 // bit e7 - -/* ---------------------------------------------------------------------- */ - -static struct tvnorm tvnorms[] = { - { - .std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N, - .name = "PAL-BGHN", - .b = ( cNegativeFmTV | - cQSS ), - .c = ( cDeemphasisON | - cDeemphasis50 | - cTopDefault), - .e = ( cGating_36 | - cAudioIF_5_5 | - cVideoIF_38_90 ), - },{ - .std = V4L2_STD_PAL_I, - .name = "PAL-I", - .b = ( cNegativeFmTV | - cQSS ), - .c = ( cDeemphasisON | - cDeemphasis50 | - cTopDefault), - .e = ( cGating_36 | - cAudioIF_6_0 | - cVideoIF_38_90 ), - },{ - .std = V4L2_STD_PAL_DK, - .name = "PAL-DK", - .b = ( cNegativeFmTV | - cQSS ), - .c = ( cDeemphasisON | - cDeemphasis50 | - cTopDefault), - .e = ( cGating_36 | - cAudioIF_6_5 | - cVideoIF_38_90 ), - },{ - .std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc, - .name = "PAL-M/Nc", - .b = ( cNegativeFmTV | - cQSS ), - .c = ( cDeemphasisON | - cDeemphasis75 | - cTopDefault), - .e = ( cGating_36 | - cAudioIF_4_5 | - cVideoIF_45_75 ), - },{ - .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, - .name = "SECAM-BGH", - .b = ( cNegativeFmTV | - cQSS ), - .c = ( cTopDefault), - .e = ( cAudioIF_5_5 | - cVideoIF_38_90 ), - },{ - .std = V4L2_STD_SECAM_L, - .name = "SECAM-L", - .b = ( cPositiveAmTV | - cQSS ), - .c = ( cTopDefault), - .e = ( cGating_36 | - cAudioIF_6_5 | - cVideoIF_38_90 ), - },{ - .std = V4L2_STD_SECAM_LC, - .name = "SECAM-L'", - .b = ( cOutputPort2Inactive | - cPositiveAmTV | - cQSS ), - .c = ( cTopDefault), - .e = ( cGating_36 | - cAudioIF_6_5 | - cVideoIF_33_90 ), - },{ - .std = V4L2_STD_SECAM_DK, - .name = "SECAM-DK", - .b = ( cNegativeFmTV | - cQSS ), - .c = ( cDeemphasisON | - cDeemphasis50 | - cTopDefault), - .e = ( cGating_36 | - cAudioIF_6_5 | - cVideoIF_38_90 ), - },{ - .std = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR, - .name = "NTSC-M", - .b = ( cNegativeFmTV | - cQSS ), - .c = ( cDeemphasisON | - cDeemphasis75 | - cTopDefault), - .e = ( cGating_36 | - cAudioIF_4_5 | - cVideoIF_45_75 ), - },{ - .std = V4L2_STD_NTSC_M_JP, - .name = "NTSC-M-JP", - .b = ( cNegativeFmTV | - cQSS ), - .c = ( cDeemphasisON | - cDeemphasis50 | - cTopDefault), - .e = ( cGating_36 | - cAudioIF_4_5 | - cVideoIF_58_75 ), - } -}; - -static struct tvnorm radio_stereo = { - .name = "Radio Stereo", - .b = ( cFmRadio | - cQSS ), - .c = ( cDeemphasisOFF | - cAudioGain6 | - cTopDefault), - .e = ( cTunerGainLow | - cAudioIF_5_5 | - cRadioIF_38_90 ), -}; - -static struct tvnorm radio_mono = { - .name = "Radio Mono", - .b = ( cFmRadio | - cQSS ), - .c = ( cDeemphasisON | - cDeemphasis75 | - cTopDefault), - .e = ( cTunerGainLow | - cAudioIF_5_5 | - cRadioIF_38_90 ), -}; - -/* ---------------------------------------------------------------------- */ - -static void dump_read_message(struct dvb_frontend *fe, unsigned char *buf) -{ - struct tda9887_priv *priv = fe->analog_demod_priv; - - static char *afc[16] = { - "- 12.5 kHz", - "- 37.5 kHz", - "- 62.5 kHz", - "- 87.5 kHz", - "-112.5 kHz", - "-137.5 kHz", - "-162.5 kHz", - "-187.5 kHz [min]", - "+187.5 kHz [max]", - "+162.5 kHz", - "+137.5 kHz", - "+112.5 kHz", - "+ 87.5 kHz", - "+ 62.5 kHz", - "+ 37.5 kHz", - "+ 12.5 kHz", - }; - tuner_info("read: 0x%2x\n", buf[0]); - tuner_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no"); - tuner_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]); - tuner_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low"); - tuner_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out"); - tuner_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low"); -} - -static void dump_write_message(struct dvb_frontend *fe, unsigned char *buf) -{ - struct tda9887_priv *priv = fe->analog_demod_priv; - - static char *sound[4] = { - "AM/TV", - "FM/radio", - "FM/TV", - "FM/radio" - }; - static char *adjust[32] = { - "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9", - "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1", - "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7", - "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15" - }; - static char *deemph[4] = { - "no", "no", "75", "50" - }; - static char *carrier[4] = { - "4.5 MHz", - "5.5 MHz", - "6.0 MHz", - "6.5 MHz / AM" - }; - static char *vif[8] = { - "58.75 MHz", - "45.75 MHz", - "38.9 MHz", - "38.0 MHz", - "33.9 MHz", - "33.4 MHz", - "45.75 MHz + pin13", - "38.9 MHz + pin13", - }; - static char *rif[4] = { - "44 MHz", - "52 MHz", - "52 MHz", - "44 MHz", - }; - - tuner_info("write: byte B 0x%02x\n", buf[1]); - tuner_info(" B0 video mode : %s\n", - (buf[1] & 0x01) ? "video trap" : "sound trap"); - tuner_info(" B1 auto mute fm : %s\n", - (buf[1] & 0x02) ? "yes" : "no"); - tuner_info(" B2 carrier mode : %s\n", - (buf[1] & 0x04) ? "QSS" : "Intercarrier"); - tuner_info(" B3-4 tv sound/radio : %s\n", - sound[(buf[1] & 0x18) >> 3]); - tuner_info(" B5 force mute audio: %s\n", - (buf[1] & 0x20) ? "yes" : "no"); - tuner_info(" B6 output port 1 : %s\n", - (buf[1] & 0x40) ? "high (inactive)" : "low (active)"); - tuner_info(" B7 output port 2 : %s\n", - (buf[1] & 0x80) ? "high (inactive)" : "low (active)"); - - tuner_info("write: byte C 0x%02x\n", buf[2]); - tuner_info(" C0-4 top adjustment : %s dB\n", - adjust[buf[2] & 0x1f]); - tuner_info(" C5-6 de-emphasis : %s\n", - deemph[(buf[2] & 0x60) >> 5]); - tuner_info(" C7 audio gain : %s\n", - (buf[2] & 0x80) ? "-6" : "0"); - - tuner_info("write: byte E 0x%02x\n", buf[3]); - tuner_info(" E0-1 sound carrier : %s\n", - carrier[(buf[3] & 0x03)]); - tuner_info(" E6 l pll gating : %s\n", - (buf[3] & 0x40) ? "36" : "13"); - - if (buf[1] & 0x08) { - /* radio */ - tuner_info(" E2-4 video if : %s\n", - rif[(buf[3] & 0x0c) >> 2]); - tuner_info(" E7 vif agc output : %s\n", - (buf[3] & 0x80) - ? ((buf[3] & 0x10) ? "fm-agc radio" : - "sif-agc radio") - : "fm radio carrier afc"); - } else { - /* video */ - tuner_info(" E2-4 video if : %s\n", - vif[(buf[3] & 0x1c) >> 2]); - tuner_info(" E5 tuner gain : %s\n", - (buf[3] & 0x80) - ? ((buf[3] & 0x20) ? "external" : "normal") - : ((buf[3] & 0x20) ? "minimum" : "normal")); - tuner_info(" E7 vif agc output : %s\n", - (buf[3] & 0x80) ? ((buf[3] & 0x20) - ? "pin3 port, pin22 vif agc out" - : "pin22 port, pin3 vif acg ext in") - : "pin3+pin22 port"); - } - tuner_info("--\n"); -} - -/* ---------------------------------------------------------------------- */ - -static int tda9887_set_tvnorm(struct dvb_frontend *fe) -{ - struct tda9887_priv *priv = fe->analog_demod_priv; - struct tvnorm *norm = NULL; - char *buf = priv->data; - int i; - - if (priv->mode == V4L2_TUNER_RADIO) { - if (priv->audmode == V4L2_TUNER_MODE_MONO) - norm = &radio_mono; - else - norm = &radio_stereo; - } else { - for (i = 0; i < ARRAY_SIZE(tvnorms); i++) { - if (tvnorms[i].std & priv->std) { - norm = tvnorms+i; - break; - } - } - } - if (NULL == norm) { - tuner_dbg("Unsupported tvnorm entry - audio muted\n"); - return -1; - } - - tuner_dbg("configure for: %s\n", norm->name); - buf[1] = norm->b; - buf[2] = norm->c; - buf[3] = norm->e; - return 0; -} - -static unsigned int port1 = UNSET; -static unsigned int port2 = UNSET; -static unsigned int qss = UNSET; -static unsigned int adjust = UNSET; - -module_param(port1, int, 0644); -module_param(port2, int, 0644); -module_param(qss, int, 0644); -module_param(adjust, int, 0644); - -static int tda9887_set_insmod(struct dvb_frontend *fe) -{ - struct tda9887_priv *priv = fe->analog_demod_priv; - char *buf = priv->data; - - if (UNSET != port1) { - if (port1) - buf[1] |= cOutputPort1Inactive; - else - buf[1] &= ~cOutputPort1Inactive; - } - if (UNSET != port2) { - if (port2) - buf[1] |= cOutputPort2Inactive; - else - buf[1] &= ~cOutputPort2Inactive; - } - - if (UNSET != qss) { - if (qss) - buf[1] |= cQSS; - else - buf[1] &= ~cQSS; - } - - if (adjust < 0x20) { - buf[2] &= ~cTopMask; - buf[2] |= adjust; - } - return 0; -} - -static int tda9887_do_config(struct dvb_frontend *fe) -{ - struct tda9887_priv *priv = fe->analog_demod_priv; - char *buf = priv->data; - - if (priv->config & TDA9887_PORT1_ACTIVE) - buf[1] &= ~cOutputPort1Inactive; - if (priv->config & TDA9887_PORT1_INACTIVE) - buf[1] |= cOutputPort1Inactive; - if (priv->config & TDA9887_PORT2_ACTIVE) - buf[1] &= ~cOutputPort2Inactive; - if (priv->config & TDA9887_PORT2_INACTIVE) - buf[1] |= cOutputPort2Inactive; - - if (priv->config & TDA9887_QSS) - buf[1] |= cQSS; - if (priv->config & TDA9887_INTERCARRIER) - buf[1] &= ~cQSS; - - if (priv->config & TDA9887_AUTOMUTE) - buf[1] |= cAutoMuteFmActive; - if (priv->config & TDA9887_DEEMPHASIS_MASK) { - buf[2] &= ~0x60; - switch (priv->config & TDA9887_DEEMPHASIS_MASK) { - case TDA9887_DEEMPHASIS_NONE: - buf[2] |= cDeemphasisOFF; - break; - case TDA9887_DEEMPHASIS_50: - buf[2] |= cDeemphasisON | cDeemphasis50; - break; - case TDA9887_DEEMPHASIS_75: - buf[2] |= cDeemphasisON | cDeemphasis75; - break; - } - } - if (priv->config & TDA9887_TOP_SET) { - buf[2] &= ~cTopMask; - buf[2] |= (priv->config >> 8) & cTopMask; - } - if ((priv->config & TDA9887_INTERCARRIER_NTSC) && - (priv->std & V4L2_STD_NTSC)) - buf[1] &= ~cQSS; - if (priv->config & TDA9887_GATING_18) - buf[3] &= ~cGating_36; - - if (priv->mode == V4L2_TUNER_RADIO) { - if (priv->config & TDA9887_RIF_41_3) { - buf[3] &= ~cVideoIFMask; - buf[3] |= cRadioIF_41_30; - } - if (priv->config & TDA9887_GAIN_NORMAL) - buf[3] &= ~cTunerGainLow; - } - - return 0; -} - -/* ---------------------------------------------------------------------- */ - -static int tda9887_status(struct dvb_frontend *fe) -{ - struct tda9887_priv *priv = fe->analog_demod_priv; - unsigned char buf[1]; - int rc; - - memset(buf,0,sizeof(buf)); - if (1 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props,buf,1))) - tuner_info("i2c i/o error: rc == %d (should be 1)\n", rc); - dump_read_message(fe, buf); - return 0; -} - -static void tda9887_configure(struct dvb_frontend *fe) -{ - struct tda9887_priv *priv = fe->analog_demod_priv; - int rc; - - memset(priv->data,0,sizeof(priv->data)); - tda9887_set_tvnorm(fe); - - /* A note on the port settings: - These settings tend to depend on the specifics of the board. - By default they are set to inactive (bit value 1) by this driver, - overwriting any changes made by the tvnorm. This means that it - is the responsibility of the module using the tda9887 to set - these values in case of changes in the tvnorm. - In many cases port 2 should be made active (0) when selecting - SECAM-L, and port 2 should remain inactive (1) for SECAM-L'. - - For the other standards the tda9887 application note says that - the ports should be set to active (0), but, again, that may - differ depending on the precise hardware configuration. - */ - priv->data[1] |= cOutputPort1Inactive; - priv->data[1] |= cOutputPort2Inactive; - - tda9887_do_config(fe); - tda9887_set_insmod(fe); - - if (priv->standby) - priv->data[1] |= cForcedMuteAudioON; - - tuner_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n", - priv->data[1], priv->data[2], priv->data[3]); - if (debug > 1) - dump_write_message(fe, priv->data); - - if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,priv->data,4))) - tuner_info("i2c i/o error: rc == %d (should be 4)\n", rc); - - if (debug > 2) { - msleep_interruptible(1000); - tda9887_status(fe); - } -} - -/* ---------------------------------------------------------------------- */ - -static void tda9887_tuner_status(struct dvb_frontend *fe) -{ - struct tda9887_priv *priv = fe->analog_demod_priv; - tuner_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", - priv->data[1], priv->data[2], priv->data[3]); -} - -static int tda9887_get_afc(struct dvb_frontend *fe) -{ - struct tda9887_priv *priv = fe->analog_demod_priv; - static int AFC_BITS_2_kHz[] = { - -12500, -37500, -62500, -97500, - -112500, -137500, -162500, -187500, - 187500, 162500, 137500, 112500, - 97500 , 62500, 37500 , 12500 - }; - int afc=0; - __u8 reg = 0; - - if (1 == tuner_i2c_xfer_recv(&priv->i2c_props,®,1)) - afc = AFC_BITS_2_kHz[(reg>>1)&0x0f]; - - return afc; -} - -static void tda9887_standby(struct dvb_frontend *fe) -{ - struct tda9887_priv *priv = fe->analog_demod_priv; - - priv->standby = true; - - tda9887_configure(fe); -} - -static void tda9887_set_params(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct tda9887_priv *priv = fe->analog_demod_priv; - - priv->standby = false; - priv->mode = params->mode; - priv->audmode = params->audmode; - priv->std = params->std; - tda9887_configure(fe); -} - -static int tda9887_set_config(struct dvb_frontend *fe, void *priv_cfg) -{ - struct tda9887_priv *priv = fe->analog_demod_priv; - - priv->config = *(unsigned int *)priv_cfg; - tda9887_configure(fe); - - return 0; -} - -static void tda9887_release(struct dvb_frontend *fe) -{ - struct tda9887_priv *priv = fe->analog_demod_priv; - - mutex_lock(&tda9887_list_mutex); - - if (priv) - hybrid_tuner_release_state(priv); - - mutex_unlock(&tda9887_list_mutex); - - fe->analog_demod_priv = NULL; -} - -static struct analog_demod_ops tda9887_ops = { - .info = { - .name = "tda9887", - }, - .set_params = tda9887_set_params, - .standby = tda9887_standby, - .tuner_status = tda9887_tuner_status, - .get_afc = tda9887_get_afc, - .release = tda9887_release, - .set_config = tda9887_set_config, -}; - -struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c_adap, - u8 i2c_addr) -{ - struct tda9887_priv *priv = NULL; - int instance; - - mutex_lock(&tda9887_list_mutex); - - instance = hybrid_tuner_request_state(struct tda9887_priv, priv, - hybrid_tuner_instance_list, - i2c_adap, i2c_addr, "tda9887"); - switch (instance) { - case 0: - mutex_unlock(&tda9887_list_mutex); - return NULL; - case 1: - fe->analog_demod_priv = priv; - priv->standby = true; - tuner_info("tda988[5/6/7] found\n"); - break; - default: - fe->analog_demod_priv = priv; - break; - } - - mutex_unlock(&tda9887_list_mutex); - - memcpy(&fe->ops.analog_ops, &tda9887_ops, - sizeof(struct analog_demod_ops)); - - return fe; -} -EXPORT_SYMBOL_GPL(tda9887_attach); - -MODULE_LICENSE("GPL"); - -/* - * 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/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 -#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.c b/drivers/media/common/tuners/tea5761.c deleted file mode 100644 index bf78cb9fc52c..000000000000 --- a/drivers/media/common/tuners/tea5761.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * For Philips TEA5761 FM Chip - * I2C address is allways 0x20 (0x10 at 7-bit mode). - * - * Copyright (c) 2005-2007 Mauro Carvalho Chehab (mchehab@infradead.org) - * This code is placed under the terms of the GNUv2 General Public License - * - */ - -#include -#include -#include -#include -#include -#include "tuner-i2c.h" -#include "tea5761.h" - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable verbose debug messages"); - -struct tea5761_priv { - struct tuner_i2c_props i2c_props; - - u32 frequency; - bool standby; -}; - -/*****************************************************************************/ - -/*************************** - * TEA5761HN I2C registers * - ***************************/ - -/* INTREG - Read: bytes 0 and 1 / Write: byte 0 */ - - /* first byte for reading */ -#define TEA5761_INTREG_IFFLAG 0x10 -#define TEA5761_INTREG_LEVFLAG 0x8 -#define TEA5761_INTREG_FRRFLAG 0x2 -#define TEA5761_INTREG_BLFLAG 0x1 - - /* second byte for reading / byte for writing */ -#define TEA5761_INTREG_IFMSK 0x10 -#define TEA5761_INTREG_LEVMSK 0x8 -#define TEA5761_INTREG_FRMSK 0x2 -#define TEA5761_INTREG_BLMSK 0x1 - -/* FRQSET - Read: bytes 2 and 3 / Write: byte 1 and 2 */ - - /* First byte */ -#define TEA5761_FRQSET_SEARCH_UP 0x80 /* 1=Station search from botton to up */ -#define TEA5761_FRQSET_SEARCH_MODE 0x40 /* 1=Search mode */ - - /* Bits 0-5 for divider MSB */ - - /* Second byte */ - /* Bits 0-7 for divider LSB */ - -/* TNCTRL - Read: bytes 4 and 5 / Write: Bytes 3 and 4 */ - - /* first byte */ - -#define TEA5761_TNCTRL_PUPD_0 0x40 /* Power UP/Power Down MSB */ -#define TEA5761_TNCTRL_BLIM 0X20 /* 1= Japan Frequencies, 0= European frequencies */ -#define TEA5761_TNCTRL_SWPM 0x10 /* 1= software port is FRRFLAG */ -#define TEA5761_TNCTRL_IFCTC 0x08 /* 1= IF count time 15.02 ms, 0= IF count time 2.02 ms */ -#define TEA5761_TNCTRL_AFM 0x04 -#define TEA5761_TNCTRL_SMUTE 0x02 /* 1= Soft mute */ -#define TEA5761_TNCTRL_SNC 0x01 - - /* second byte */ - -#define TEA5761_TNCTRL_MU 0x80 /* 1=Hard mute */ -#define TEA5761_TNCTRL_SSL_1 0x40 -#define TEA5761_TNCTRL_SSL_0 0x20 -#define TEA5761_TNCTRL_HLSI 0x10 -#define TEA5761_TNCTRL_MST 0x08 /* 1 = mono */ -#define TEA5761_TNCTRL_SWP 0x04 -#define TEA5761_TNCTRL_DTC 0x02 /* 1 = deemphasis 50 us, 0 = deemphasis 75 us */ -#define TEA5761_TNCTRL_AHLSI 0x01 - -/* FRQCHECK - Read: bytes 6 and 7 */ - /* First byte */ - - /* Bits 0-5 for divider MSB */ - - /* Second byte */ - /* Bits 0-7 for divider LSB */ - -/* TUNCHECK - Read: bytes 8 and 9 */ - - /* First byte */ -#define TEA5761_TUNCHECK_IF_MASK 0x7e /* IF count */ -#define TEA5761_TUNCHECK_TUNTO 0x01 - - /* Second byte */ -#define TEA5761_TUNCHECK_LEV_MASK 0xf0 /* Level Count */ -#define TEA5761_TUNCHECK_LD 0x08 -#define TEA5761_TUNCHECK_STEREO 0x04 - -/* TESTREG - Read: bytes 10 and 11 / Write: bytes 5 and 6 */ - - /* All zero = no test mode */ - -/* MANID - Read: bytes 12 and 13 */ - - /* First byte - should be 0x10 */ -#define TEA5767_MANID_VERSION_MASK 0xf0 /* Version = 1 */ -#define TEA5767_MANID_ID_MSB_MASK 0x0f /* Manufacurer ID - should be 0 */ - - /* Second byte - Should be 0x2b */ - -#define TEA5767_MANID_ID_LSB_MASK 0xfe /* Manufacturer ID - should be 0x15 */ -#define TEA5767_MANID_IDAV 0x01 /* 1 = Chip has ID, 0 = Chip has no ID */ - -/* Chip ID - Read: bytes 14 and 15 */ - - /* First byte - should be 0x57 */ - - /* Second byte - should be 0x61 */ - -/*****************************************************************************/ - -#define FREQ_OFFSET 0 /* for TEA5767, it is 700 to give the right freq */ -static void tea5761_status_dump(unsigned char *buffer) -{ - unsigned int div, frq; - - div = ((buffer[2] & 0x3f) << 8) | buffer[3]; - - frq = 1000 * (div * 32768 / 1000 + FREQ_OFFSET + 225) / 4; /* Freq in KHz */ - - printk(KERN_INFO "tea5761: Frequency %d.%03d KHz (divider = 0x%04x)\n", - frq / 1000, frq % 1000, div); -} - -/* Freq should be specifyed at 62.5 Hz */ -static int __set_radio_freq(struct dvb_frontend *fe, - unsigned int freq, - bool mono) -{ - struct tea5761_priv *priv = fe->tuner_priv; - unsigned int frq = freq; - unsigned char buffer[7] = {0, 0, 0, 0, 0, 0, 0 }; - unsigned div; - int rc; - - tuner_dbg("radio freq counter %d\n", frq); - - if (priv->standby) { - tuner_dbg("TEA5761 set to standby mode\n"); - buffer[5] |= TEA5761_TNCTRL_MU; - } else { - buffer[4] |= TEA5761_TNCTRL_PUPD_0; - } - - - if (mono) { - tuner_dbg("TEA5761 set to mono\n"); - buffer[5] |= TEA5761_TNCTRL_MST; - } else { - tuner_dbg("TEA5761 set to stereo\n"); - } - - div = (1000 * (frq * 4 / 16 + 700 + 225) ) >> 15; - buffer[1] = (div >> 8) & 0x3f; - buffer[2] = div & 0xff; - - if (debug) - tea5761_status_dump(buffer); - - if (7 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 7))) - tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); - - priv->frequency = frq * 125 / 2; - - return 0; -} - -static int set_radio_freq(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct tea5761_priv *priv = fe->analog_demod_priv; - - priv->standby = false; - - return __set_radio_freq(fe, params->frequency, - params->audmode == V4L2_TUNER_MODE_MONO); -} - -static int set_radio_sleep(struct dvb_frontend *fe) -{ - struct tea5761_priv *priv = fe->analog_demod_priv; - - priv->standby = true; - - return __set_radio_freq(fe, priv->frequency, false); -} - -static int tea5761_read_status(struct dvb_frontend *fe, char *buffer) -{ - struct tea5761_priv *priv = fe->tuner_priv; - int rc; - - memset(buffer, 0, 16); - if (16 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 16))) { - tuner_warn("i2c i/o error: rc == %d (should be 16)\n", rc); - return -EREMOTEIO; - } - - return 0; -} - -static inline int tea5761_signal(struct dvb_frontend *fe, const char *buffer) -{ - struct tea5761_priv *priv = fe->tuner_priv; - - int signal = ((buffer[9] & TEA5761_TUNCHECK_LEV_MASK) << (13 - 4)); - - tuner_dbg("Signal strength: %d\n", signal); - - return signal; -} - -static inline int tea5761_stereo(struct dvb_frontend *fe, const char *buffer) -{ - struct tea5761_priv *priv = fe->tuner_priv; - - int stereo = buffer[9] & TEA5761_TUNCHECK_STEREO; - - tuner_dbg("Radio ST GET = %02x\n", stereo); - - return (stereo ? V4L2_TUNER_SUB_STEREO : 0); -} - -static int tea5761_get_status(struct dvb_frontend *fe, u32 *status) -{ - unsigned char buffer[16]; - - *status = 0; - - if (0 == tea5761_read_status(fe, buffer)) { - if (tea5761_signal(fe, buffer)) - *status = TUNER_STATUS_LOCKED; - if (tea5761_stereo(fe, buffer)) - *status |= TUNER_STATUS_STEREO; - } - - return 0; -} - -static int tea5761_get_rf_strength(struct dvb_frontend *fe, u16 *strength) -{ - unsigned char buffer[16]; - - *strength = 0; - - if (0 == tea5761_read_status(fe, buffer)) - *strength = tea5761_signal(fe, buffer); - - return 0; -} - -int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) -{ - unsigned char buffer[16]; - int rc; - struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr }; - - if (16 != (rc = tuner_i2c_xfer_recv(&i2c, buffer, 16))) { - printk(KERN_WARNING "it is not a TEA5761. Received %i chars.\n", rc); - return -EINVAL; - } - - if ((buffer[13] != 0x2b) || (buffer[14] != 0x57) || (buffer[15] != 0x061)) { - printk(KERN_WARNING "Manufacturer ID= 0x%02x, Chip ID = %02x%02x." - " It is not a TEA5761\n", - buffer[13], buffer[14], buffer[15]); - return -EINVAL; - } - printk(KERN_WARNING "tea5761: TEA%02x%02x detected. " - "Manufacturer ID= 0x%02x\n", - buffer[14], buffer[15], buffer[13]); - - return 0; -} - -static int tea5761_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - - return 0; -} - -static int tea5761_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct tea5761_priv *priv = fe->tuner_priv; - *frequency = priv->frequency; - return 0; -} - -static struct dvb_tuner_ops tea5761_tuner_ops = { - .info = { - .name = "tea5761", // Philips TEA5761HN FM Radio - }, - .set_analog_params = set_radio_freq, - .sleep = set_radio_sleep, - .release = tea5761_release, - .get_frequency = tea5761_get_frequency, - .get_status = tea5761_get_status, - .get_rf_strength = tea5761_get_rf_strength, -}; - -struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe, - struct i2c_adapter* i2c_adap, - u8 i2c_addr) -{ - struct tea5761_priv *priv = NULL; - - if (tea5761_autodetection(i2c_adap, i2c_addr) != 0) - return NULL; - - priv = kzalloc(sizeof(struct tea5761_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - fe->tuner_priv = priv; - - priv->i2c_props.addr = i2c_addr; - priv->i2c_props.adap = i2c_adap; - priv->i2c_props.name = "tea5761"; - - memcpy(&fe->ops.tuner_ops, &tea5761_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - tuner_info("type set to %s\n", "Philips TEA5761HN FM Radio"); - - return fe; -} - - -EXPORT_SYMBOL_GPL(tea5761_attach); -EXPORT_SYMBOL_GPL(tea5761_autodetection); - -MODULE_DESCRIPTION("Philips TEA5761 FM tuner driver"); -MODULE_AUTHOR("Mauro Carvalho Chehab "); -MODULE_LICENSE("GPL"); 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 -#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.c b/drivers/media/common/tuners/tea5767.c deleted file mode 100644 index 36e85d81acb2..000000000000 --- a/drivers/media/common/tuners/tea5767.c +++ /dev/null @@ -1,475 +0,0 @@ -/* - * For Philips TEA5767 FM Chip used on some TV Cards like Prolink Pixelview - * I2C address is allways 0xC0. - * - * - * Copyright (c) 2005 Mauro Carvalho Chehab (mchehab@infradead.org) - * This code is placed under the terms of the GNU General Public License - * - * tea5767 autodetection thanks to Torsten Seeboth and Atsushi Nakagawa - * from their contributions on DScaler. - */ - -#include -#include -#include -#include -#include "tuner-i2c.h" -#include "tea5767.h" - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable verbose debug messages"); - -/*****************************************************************************/ - -struct tea5767_priv { - struct tuner_i2c_props i2c_props; - u32 frequency; - struct tea5767_ctrl ctrl; -}; - -/*****************************************************************************/ - -/****************************** - * Write mode register values * - ******************************/ - -/* First register */ -#define TEA5767_MUTE 0x80 /* Mutes output */ -#define TEA5767_SEARCH 0x40 /* Activates station search */ -/* Bits 0-5 for divider MSB */ - -/* Second register */ -/* Bits 0-7 for divider LSB */ - -/* Third register */ - -/* Station search from botton to up */ -#define TEA5767_SEARCH_UP 0x80 - -/* Searches with ADC output = 10 */ -#define TEA5767_SRCH_HIGH_LVL 0x60 - -/* Searches with ADC output = 10 */ -#define TEA5767_SRCH_MID_LVL 0x40 - -/* Searches with ADC output = 5 */ -#define TEA5767_SRCH_LOW_LVL 0x20 - -/* if on, div=4*(Frf+Fif)/Fref otherwise, div=4*(Frf-Fif)/Freq) */ -#define TEA5767_HIGH_LO_INJECT 0x10 - -/* Disable stereo */ -#define TEA5767_MONO 0x08 - -/* Disable right channel and turns to mono */ -#define TEA5767_MUTE_RIGHT 0x04 - -/* Disable left channel and turns to mono */ -#define TEA5767_MUTE_LEFT 0x02 - -#define TEA5767_PORT1_HIGH 0x01 - -/* Fourth register */ -#define TEA5767_PORT2_HIGH 0x80 -/* Chips stops working. Only I2C bus remains on */ -#define TEA5767_STDBY 0x40 - -/* Japan freq (76-108 MHz. If disabled, 87.5-108 MHz */ -#define TEA5767_JAPAN_BAND 0x20 - -/* Unselected means 32.768 KHz freq as reference. Otherwise Xtal at 13 MHz */ -#define TEA5767_XTAL_32768 0x10 - -/* Cuts weak signals */ -#define TEA5767_SOFT_MUTE 0x08 - -/* Activates high cut control */ -#define TEA5767_HIGH_CUT_CTRL 0x04 - -/* Activates stereo noise control */ -#define TEA5767_ST_NOISE_CTL 0x02 - -/* If activate PORT 1 indicates SEARCH or else it is used as PORT1 */ -#define TEA5767_SRCH_IND 0x01 - -/* Fifth register */ - -/* By activating, it will use Xtal at 13 MHz as reference for divider */ -#define TEA5767_PLLREF_ENABLE 0x80 - -/* By activating, deemphasis=50, or else, deemphasis of 50us */ -#define TEA5767_DEEMPH_75 0X40 - -/***************************** - * Read mode register values * - *****************************/ - -/* First register */ -#define TEA5767_READY_FLAG_MASK 0x80 -#define TEA5767_BAND_LIMIT_MASK 0X40 -/* Bits 0-5 for divider MSB after search or preset */ - -/* Second register */ -/* Bits 0-7 for divider LSB after search or preset */ - -/* Third register */ -#define TEA5767_STEREO_MASK 0x80 -#define TEA5767_IF_CNTR_MASK 0x7f - -/* Fourth register */ -#define TEA5767_ADC_LEVEL_MASK 0xf0 - -/* should be 0 */ -#define TEA5767_CHIP_ID_MASK 0x0f - -/* Fifth register */ -/* Reserved for future extensions */ -#define TEA5767_RESERVED_MASK 0xff - -/*****************************************************************************/ - -static void tea5767_status_dump(struct tea5767_priv *priv, - unsigned char *buffer) -{ - unsigned int div, frq; - - if (TEA5767_READY_FLAG_MASK & buffer[0]) - tuner_info("Ready Flag ON\n"); - else - tuner_info("Ready Flag OFF\n"); - - if (TEA5767_BAND_LIMIT_MASK & buffer[0]) - tuner_info("Tuner at band limit\n"); - else - tuner_info("Tuner not at band limit\n"); - - div = ((buffer[0] & 0x3f) << 8) | buffer[1]; - - switch (priv->ctrl.xtal_freq) { - case TEA5767_HIGH_LO_13MHz: - frq = (div * 50000 - 700000 - 225000) / 4; /* Freq in KHz */ - break; - case TEA5767_LOW_LO_13MHz: - frq = (div * 50000 + 700000 + 225000) / 4; /* Freq in KHz */ - break; - case TEA5767_LOW_LO_32768: - frq = (div * 32768 + 700000 + 225000) / 4; /* Freq in KHz */ - break; - case TEA5767_HIGH_LO_32768: - default: - frq = (div * 32768 - 700000 - 225000) / 4; /* Freq in KHz */ - break; - } - buffer[0] = (div >> 8) & 0x3f; - buffer[1] = div & 0xff; - - tuner_info("Frequency %d.%03d KHz (divider = 0x%04x)\n", - frq / 1000, frq % 1000, div); - - if (TEA5767_STEREO_MASK & buffer[2]) - tuner_info("Stereo\n"); - else - tuner_info("Mono\n"); - - tuner_info("IF Counter = %d\n", buffer[2] & TEA5767_IF_CNTR_MASK); - - tuner_info("ADC Level = %d\n", - (buffer[3] & TEA5767_ADC_LEVEL_MASK) >> 4); - - tuner_info("Chip ID = %d\n", (buffer[3] & TEA5767_CHIP_ID_MASK)); - - tuner_info("Reserved = 0x%02x\n", - (buffer[4] & TEA5767_RESERVED_MASK)); -} - -/* Freq should be specifyed at 62.5 Hz */ -static int set_radio_freq(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct tea5767_priv *priv = fe->tuner_priv; - unsigned int frq = params->frequency; - unsigned char buffer[5]; - unsigned div; - int rc; - - tuner_dbg("radio freq = %d.%03d MHz\n", frq/16000,(frq/16)%1000); - - buffer[2] = 0; - - if (priv->ctrl.port1) - buffer[2] |= TEA5767_PORT1_HIGH; - - if (params->audmode == V4L2_TUNER_MODE_MONO) { - tuner_dbg("TEA5767 set to mono\n"); - buffer[2] |= TEA5767_MONO; - } else { - tuner_dbg("TEA5767 set to stereo\n"); - } - - - buffer[3] = 0; - - if (priv->ctrl.port2) - buffer[3] |= TEA5767_PORT2_HIGH; - - if (priv->ctrl.high_cut) - buffer[3] |= TEA5767_HIGH_CUT_CTRL; - - if (priv->ctrl.st_noise) - buffer[3] |= TEA5767_ST_NOISE_CTL; - - if (priv->ctrl.soft_mute) - buffer[3] |= TEA5767_SOFT_MUTE; - - if (priv->ctrl.japan_band) - buffer[3] |= TEA5767_JAPAN_BAND; - - buffer[4] = 0; - - if (priv->ctrl.deemph_75) - buffer[4] |= TEA5767_DEEMPH_75; - - if (priv->ctrl.pllref) - buffer[4] |= TEA5767_PLLREF_ENABLE; - - - /* Rounds freq to next decimal value - for 62.5 KHz step */ - /* frq = 20*(frq/16)+radio_frq[frq%16]; */ - - switch (priv->ctrl.xtal_freq) { - case TEA5767_HIGH_LO_13MHz: - tuner_dbg("radio HIGH LO inject xtal @ 13 MHz\n"); - buffer[2] |= TEA5767_HIGH_LO_INJECT; - div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000; - break; - case TEA5767_LOW_LO_13MHz: - tuner_dbg("radio LOW LO inject xtal @ 13 MHz\n"); - - div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000; - break; - case TEA5767_LOW_LO_32768: - tuner_dbg("radio LOW LO inject xtal @ 32,768 MHz\n"); - buffer[3] |= TEA5767_XTAL_32768; - /* const 700=4000*175 Khz - to adjust freq to right value */ - div = ((frq * (4000 / 16) - 700000 - 225000) + 16384) >> 15; - break; - case TEA5767_HIGH_LO_32768: - default: - tuner_dbg("radio HIGH LO inject xtal @ 32,768 MHz\n"); - - buffer[2] |= TEA5767_HIGH_LO_INJECT; - buffer[3] |= TEA5767_XTAL_32768; - div = ((frq * (4000 / 16) + 700000 + 225000) + 16384) >> 15; - break; - } - buffer[0] = (div >> 8) & 0x3f; - buffer[1] = div & 0xff; - - if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) - tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); - - if (debug) { - if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) - tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); - else - tea5767_status_dump(priv, buffer); - } - - priv->frequency = frq * 125 / 2; - - return 0; -} - -static int tea5767_read_status(struct dvb_frontend *fe, char *buffer) -{ - struct tea5767_priv *priv = fe->tuner_priv; - int rc; - - memset(buffer, 0, 5); - if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) { - tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); - return -EREMOTEIO; - } - - return 0; -} - -static inline int tea5767_signal(struct dvb_frontend *fe, const char *buffer) -{ - struct tea5767_priv *priv = fe->tuner_priv; - - int signal = ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8); - - tuner_dbg("Signal strength: %d\n", signal); - - return signal; -} - -static inline int tea5767_stereo(struct dvb_frontend *fe, const char *buffer) -{ - struct tea5767_priv *priv = fe->tuner_priv; - - int stereo = buffer[2] & TEA5767_STEREO_MASK; - - tuner_dbg("Radio ST GET = %02x\n", stereo); - - return (stereo ? V4L2_TUNER_SUB_STEREO : 0); -} - -static int tea5767_get_status(struct dvb_frontend *fe, u32 *status) -{ - unsigned char buffer[5]; - - *status = 0; - - if (0 == tea5767_read_status(fe, buffer)) { - if (tea5767_signal(fe, buffer)) - *status = TUNER_STATUS_LOCKED; - if (tea5767_stereo(fe, buffer)) - *status |= TUNER_STATUS_STEREO; - } - - return 0; -} - -static int tea5767_get_rf_strength(struct dvb_frontend *fe, u16 *strength) -{ - unsigned char buffer[5]; - - *strength = 0; - - if (0 == tea5767_read_status(fe, buffer)) - *strength = tea5767_signal(fe, buffer); - - return 0; -} - -static int tea5767_standby(struct dvb_frontend *fe) -{ - unsigned char buffer[5]; - struct tea5767_priv *priv = fe->tuner_priv; - unsigned div, rc; - - div = (87500 * 4 + 700 + 225 + 25) / 50; /* Set frequency to 87.5 MHz */ - buffer[0] = (div >> 8) & 0x3f; - buffer[1] = div & 0xff; - buffer[2] = TEA5767_PORT1_HIGH; - buffer[3] = TEA5767_PORT2_HIGH | TEA5767_HIGH_CUT_CTRL | - TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND | TEA5767_STDBY; - buffer[4] = 0; - - if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) - tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); - - return 0; -} - -int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) -{ - struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr }; - unsigned char buffer[7] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - int rc; - - if ((rc = tuner_i2c_xfer_recv(&i2c, buffer, 7))< 5) { - printk(KERN_WARNING "It is not a TEA5767. Received %i bytes.\n", rc); - return -EINVAL; - } - - /* If all bytes are the same then it's a TV tuner and not a tea5767 */ - if (buffer[0] == buffer[1] && buffer[0] == buffer[2] && - buffer[0] == buffer[3] && buffer[0] == buffer[4]) { - printk(KERN_WARNING "All bytes are equal. It is not a TEA5767\n"); - return -EINVAL; - } - - /* Status bytes: - * Byte 4: bit 3:1 : CI (Chip Identification) == 0 - * bit 0 : internally set to 0 - * Byte 5: bit 7:0 : == 0 - */ - if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) { - printk(KERN_WARNING "Chip ID is not zero. It is not a TEA5767\n"); - return -EINVAL; - } - - - return 0; -} - -static int tea5767_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - - return 0; -} - -static int tea5767_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct tea5767_priv *priv = fe->tuner_priv; - *frequency = priv->frequency; - - return 0; -} - -static int tea5767_set_config (struct dvb_frontend *fe, void *priv_cfg) -{ - struct tea5767_priv *priv = fe->tuner_priv; - - memcpy(&priv->ctrl, priv_cfg, sizeof(priv->ctrl)); - - return 0; -} - -static struct dvb_tuner_ops tea5767_tuner_ops = { - .info = { - .name = "tea5767", // Philips TEA5767HN FM Radio - }, - - .set_analog_params = set_radio_freq, - .set_config = tea5767_set_config, - .sleep = tea5767_standby, - .release = tea5767_release, - .get_frequency = tea5767_get_frequency, - .get_status = tea5767_get_status, - .get_rf_strength = tea5767_get_rf_strength, -}; - -struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, - struct i2c_adapter* i2c_adap, - u8 i2c_addr) -{ - struct tea5767_priv *priv = NULL; - - priv = kzalloc(sizeof(struct tea5767_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - fe->tuner_priv = priv; - - priv->i2c_props.addr = i2c_addr; - priv->i2c_props.adap = i2c_adap; - priv->i2c_props.name = "tea5767"; - - priv->ctrl.xtal_freq = TEA5767_HIGH_LO_32768; - priv->ctrl.port1 = 1; - priv->ctrl.port2 = 1; - priv->ctrl.high_cut = 1; - priv->ctrl.st_noise = 1; - priv->ctrl.japan_band = 1; - - memcpy(&fe->ops.tuner_ops, &tea5767_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - tuner_info("type set to %s\n", "Philips TEA5767HN FM Radio"); - - return fe; -} - -EXPORT_SYMBOL_GPL(tea5767_attach); -EXPORT_SYMBOL_GPL(tea5767_autodetection); - -MODULE_DESCRIPTION("Philips TEA5767 FM tuner driver"); -MODULE_AUTHOR("Mauro Carvalho Chehab "); -MODULE_LICENSE("GPL"); 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 -#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 - * - * 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 "); -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 - * - * 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 - * - * 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-i2c.h b/drivers/media/common/tuners/tuner-i2c.h deleted file mode 100644 index 18f005634c67..000000000000 --- a/drivers/media/common/tuners/tuner-i2c.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - tuner-i2c.h - i2c interface for different tuners - - Copyright (C) 2007 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 __TUNER_I2C_H__ -#define __TUNER_I2C_H__ - -#include -#include - -struct tuner_i2c_props { - u8 addr; - struct i2c_adapter *adap; - - /* used for tuner instance management */ - int count; - char *name; -}; - -static inline int tuner_i2c_xfer_send(struct tuner_i2c_props *props, char *buf, int len) -{ - struct i2c_msg msg = { .addr = props->addr, .flags = 0, - .buf = buf, .len = len }; - int ret = i2c_transfer(props->adap, &msg, 1); - - return (ret == 1) ? len : ret; -} - -static inline int tuner_i2c_xfer_recv(struct tuner_i2c_props *props, char *buf, int len) -{ - struct i2c_msg msg = { .addr = props->addr, .flags = I2C_M_RD, - .buf = buf, .len = len }; - int ret = i2c_transfer(props->adap, &msg, 1); - - return (ret == 1) ? len : ret; -} - -static inline int tuner_i2c_xfer_send_recv(struct tuner_i2c_props *props, - char *obuf, int olen, - char *ibuf, int ilen) -{ - struct i2c_msg msg[2] = { { .addr = props->addr, .flags = 0, - .buf = obuf, .len = olen }, - { .addr = props->addr, .flags = I2C_M_RD, - .buf = ibuf, .len = ilen } }; - int ret = i2c_transfer(props->adap, msg, 2); - - return (ret == 2) ? ilen : ret; -} - -/* Callers must declare as a global for the module: - * - * static LIST_HEAD(hybrid_tuner_instance_list); - * - * hybrid_tuner_instance_list should be the third argument - * passed into hybrid_tuner_request_state(). - * - * state structure must contain the following: - * - * struct list_head hybrid_tuner_instance_list; - * struct tuner_i2c_props i2c_props; - * - * hybrid_tuner_instance_list (both within state structure and globally) - * is only required if the driver is using hybrid_tuner_request_state - * and hybrid_tuner_release_state to manage state sharing between - * multiple instances of hybrid tuners. - */ - -#define tuner_printk(kernlvl, i2cprops, fmt, arg...) do { \ - printk(kernlvl "%s %d-%04x: " fmt, i2cprops.name, \ - i2cprops.adap ? \ - i2c_adapter_id(i2cprops.adap) : -1, \ - i2cprops.addr, ##arg); \ - } while (0) - -/* TO DO: convert all callers of these macros to pass in - * struct tuner_i2c_props, then remove the macro wrappers */ - -#define __tuner_warn(i2cprops, fmt, arg...) do { \ - tuner_printk(KERN_WARNING, i2cprops, fmt, ##arg); \ - } while (0) - -#define __tuner_info(i2cprops, fmt, arg...) do { \ - tuner_printk(KERN_INFO, i2cprops, fmt, ##arg); \ - } while (0) - -#define __tuner_err(i2cprops, fmt, arg...) do { \ - tuner_printk(KERN_ERR, i2cprops, fmt, ##arg); \ - } while (0) - -#define __tuner_dbg(i2cprops, fmt, arg...) do { \ - if ((debug)) \ - tuner_printk(KERN_DEBUG, i2cprops, fmt, ##arg); \ - } while (0) - -#define tuner_warn(fmt, arg...) __tuner_warn(priv->i2c_props, fmt, ##arg) -#define tuner_info(fmt, arg...) __tuner_info(priv->i2c_props, fmt, ##arg) -#define tuner_err(fmt, arg...) __tuner_err(priv->i2c_props, fmt, ##arg) -#define tuner_dbg(fmt, arg...) __tuner_dbg(priv->i2c_props, fmt, ##arg) - -/****************************************************************************/ - -/* The return value of hybrid_tuner_request_state indicates the number of - * instances using this tuner object. - * - * 0 - no instances, indicates an error - kzalloc must have failed - * - * 1 - one instance, indicates that the tuner object was created successfully - * - * 2 (or more) instances, indicates that an existing tuner object was found - */ - -#define hybrid_tuner_request_state(type, state, list, i2cadap, i2caddr, devname)\ -({ \ - int __ret = 0; \ - list_for_each_entry(state, &list, hybrid_tuner_instance_list) { \ - if (((i2cadap) && (state->i2c_props.adap)) && \ - ((i2c_adapter_id(state->i2c_props.adap) == \ - i2c_adapter_id(i2cadap)) && \ - (i2caddr == state->i2c_props.addr))) { \ - __tuner_info(state->i2c_props, \ - "attaching existing instance\n"); \ - state->i2c_props.count++; \ - __ret = state->i2c_props.count; \ - break; \ - } \ - } \ - if (0 == __ret) { \ - state = kzalloc(sizeof(type), GFP_KERNEL); \ - if (NULL == state) \ - goto __fail; \ - state->i2c_props.addr = i2caddr; \ - state->i2c_props.adap = i2cadap; \ - state->i2c_props.name = devname; \ - __tuner_info(state->i2c_props, \ - "creating new instance\n"); \ - list_add_tail(&state->hybrid_tuner_instance_list, &list);\ - state->i2c_props.count++; \ - __ret = state->i2c_props.count; \ - } \ -__fail: \ - __ret; \ -}) - -#define hybrid_tuner_release_state(state) \ -({ \ - int __ret; \ - state->i2c_props.count--; \ - __ret = state->i2c_props.count; \ - if (!state->i2c_props.count) { \ - __tuner_info(state->i2c_props, "destroying instance\n");\ - list_del(&state->hybrid_tuner_instance_list); \ - kfree(state); \ - } \ - __ret; \ -}) - -#define hybrid_tuner_report_instance_count(state) \ -({ \ - int __ret = 0; \ - if (state) \ - __ret = state->i2c_props.count; \ - __ret; \ -}) - -#endif /* __TUNER_I2C_H__ */ diff --git a/drivers/media/common/tuners/tuner-simple.c b/drivers/media/common/tuners/tuner-simple.c deleted file mode 100644 index 39e7e583c8c0..000000000000 --- a/drivers/media/common/tuners/tuner-simple.c +++ /dev/null @@ -1,1155 +0,0 @@ -/* - * i2c tv tuner chip device driver - * controls all those simple 4-control-bytes style tuners. - * - * This "tuner-simple" module was split apart from the original "tuner" module. - */ -#include -#include -#include -#include -#include -#include -#include "tuner-i2c.h" -#include "tuner-simple.h" - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "enable verbose debug messages"); - -#define TUNER_SIMPLE_MAX 64 -static unsigned int simple_devcount; - -static int offset; -module_param(offset, int, 0664); -MODULE_PARM_DESC(offset, "Allows to specify an offset for tuner"); - -static unsigned int atv_input[TUNER_SIMPLE_MAX] = \ - { [0 ... (TUNER_SIMPLE_MAX-1)] = 0 }; -static unsigned int dtv_input[TUNER_SIMPLE_MAX] = \ - { [0 ... (TUNER_SIMPLE_MAX-1)] = 0 }; -module_param_array(atv_input, int, NULL, 0644); -module_param_array(dtv_input, int, NULL, 0644); -MODULE_PARM_DESC(atv_input, "specify atv rf input, 0 for autoselect"); -MODULE_PARM_DESC(dtv_input, "specify dtv rf input, 0 for autoselect"); - -/* ---------------------------------------------------------------------- */ - -/* tv standard selection for Temic 4046 FM5 - this value takes the low bits of control byte 2 - from datasheet Rev.01, Feb.00 - standard BG I L L2 D - picture IF 38.9 38.9 38.9 33.95 38.9 - sound 1 33.4 32.9 32.4 40.45 32.4 - sound 2 33.16 - NICAM 33.05 32.348 33.05 33.05 - */ -#define TEMIC_SET_PAL_I 0x05 -#define TEMIC_SET_PAL_DK 0x09 -#define TEMIC_SET_PAL_L 0x0a /* SECAM ? */ -#define TEMIC_SET_PAL_L2 0x0b /* change IF ! */ -#define TEMIC_SET_PAL_BG 0x0c - -/* tv tuner system standard selection for Philips FQ1216ME - this value takes the low bits of control byte 2 - from datasheet "1999 Nov 16" (supersedes "1999 Mar 23") - standard BG DK I L L` - picture carrier 38.90 38.90 38.90 38.90 33.95 - colour 34.47 34.47 34.47 34.47 38.38 - sound 1 33.40 32.40 32.90 32.40 40.45 - sound 2 33.16 - - - - - NICAM 33.05 33.05 32.35 33.05 39.80 - */ -#define PHILIPS_SET_PAL_I 0x01 /* Bit 2 always zero !*/ -#define PHILIPS_SET_PAL_BGDK 0x09 -#define PHILIPS_SET_PAL_L2 0x0a -#define PHILIPS_SET_PAL_L 0x0b - -/* system switching for Philips FI1216MF MK2 - from datasheet "1996 Jul 09", - standard BG L L' - picture carrier 38.90 38.90 33.95 - colour 34.47 34.37 38.38 - sound 1 33.40 32.40 40.45 - sound 2 33.16 - - - NICAM 33.05 33.05 39.80 - */ -#define PHILIPS_MF_SET_STD_BG 0x01 /* Bit 2 must be zero, Bit 3 is system output */ -#define PHILIPS_MF_SET_STD_L 0x03 /* Used on Secam France */ -#define PHILIPS_MF_SET_STD_LC 0x02 /* Used on SECAM L' */ - -/* Control byte */ - -#define TUNER_RATIO_MASK 0x06 /* Bit cb1:cb2 */ -#define TUNER_RATIO_SELECT_50 0x00 -#define TUNER_RATIO_SELECT_32 0x02 -#define TUNER_RATIO_SELECT_166 0x04 -#define TUNER_RATIO_SELECT_62 0x06 - -#define TUNER_CHARGE_PUMP 0x40 /* Bit cb6 */ - -/* Status byte */ - -#define TUNER_POR 0x80 -#define TUNER_FL 0x40 -#define TUNER_MODE 0x38 -#define TUNER_AFC 0x07 -#define TUNER_SIGNAL 0x07 -#define TUNER_STEREO 0x10 - -#define TUNER_PLL_LOCKED 0x40 -#define TUNER_STEREO_MK3 0x04 - -static DEFINE_MUTEX(tuner_simple_list_mutex); -static LIST_HEAD(hybrid_tuner_instance_list); - -struct tuner_simple_priv { - unsigned int nr; - u16 last_div; - - struct tuner_i2c_props i2c_props; - struct list_head hybrid_tuner_instance_list; - - unsigned int type; - struct tunertype *tun; - - u32 frequency; - u32 bandwidth; -}; - -/* ---------------------------------------------------------------------- */ - -static int tuner_read_status(struct dvb_frontend *fe) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - unsigned char byte; - - if (1 != tuner_i2c_xfer_recv(&priv->i2c_props, &byte, 1)) - return 0; - - return byte; -} - -static inline int tuner_signal(const int status) -{ - return (status & TUNER_SIGNAL) << 13; -} - -static inline int tuner_stereo(const int type, const int status) -{ - switch (type) { - case TUNER_PHILIPS_FM1216ME_MK3: - case TUNER_PHILIPS_FM1236_MK3: - case TUNER_PHILIPS_FM1256_IH3: - case TUNER_LG_NTSC_TAPE: - case TUNER_TCL_MF02GIP_5N: - return ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3); - case TUNER_PHILIPS_FM1216MK5: - return status | TUNER_STEREO; - default: - return status & TUNER_STEREO; - } -} - -static inline int tuner_islocked(const int status) -{ - return (status & TUNER_FL); -} - -static inline int tuner_afcstatus(const int status) -{ - return (status & TUNER_AFC) - 2; -} - - -static int simple_get_status(struct dvb_frontend *fe, u32 *status) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - int tuner_status; - - if (priv->i2c_props.adap == NULL) - return -EINVAL; - - tuner_status = tuner_read_status(fe); - - *status = 0; - - if (tuner_islocked(tuner_status)) - *status = TUNER_STATUS_LOCKED; - if (tuner_stereo(priv->type, tuner_status)) - *status |= TUNER_STATUS_STEREO; - - tuner_dbg("AFC Status: %d\n", tuner_afcstatus(tuner_status)); - - return 0; -} - -static int simple_get_rf_strength(struct dvb_frontend *fe, u16 *strength) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - int signal; - - if (priv->i2c_props.adap == NULL) - return -EINVAL; - - signal = tuner_signal(tuner_read_status(fe)); - - *strength = signal; - - tuner_dbg("Signal strength: %d\n", signal); - - return 0; -} - -/* ---------------------------------------------------------------------- */ - -static inline char *tuner_param_name(enum param_type type) -{ - char *name; - - switch (type) { - case TUNER_PARAM_TYPE_RADIO: - name = "radio"; - break; - case TUNER_PARAM_TYPE_PAL: - name = "pal"; - break; - case TUNER_PARAM_TYPE_SECAM: - name = "secam"; - break; - case TUNER_PARAM_TYPE_NTSC: - name = "ntsc"; - break; - case TUNER_PARAM_TYPE_DIGITAL: - name = "digital"; - break; - default: - name = "unknown"; - break; - } - return name; -} - -static struct tuner_params *simple_tuner_params(struct dvb_frontend *fe, - enum param_type desired_type) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - struct tunertype *tun = priv->tun; - int i; - - for (i = 0; i < tun->count; i++) - if (desired_type == tun->params[i].type) - break; - - /* use default tuner params if desired_type not available */ - if (i == tun->count) { - tuner_dbg("desired params (%s) undefined for tuner %d\n", - tuner_param_name(desired_type), priv->type); - i = 0; - } - - tuner_dbg("using tuner params #%d (%s)\n", i, - tuner_param_name(tun->params[i].type)); - - return &tun->params[i]; -} - -static int simple_config_lookup(struct dvb_frontend *fe, - struct tuner_params *t_params, - unsigned *frequency, u8 *config, u8 *cb) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - int i; - - for (i = 0; i < t_params->count; i++) { - if (*frequency > t_params->ranges[i].limit) - continue; - break; - } - if (i == t_params->count) { - tuner_dbg("frequency out of range (%d > %d)\n", - *frequency, t_params->ranges[i - 1].limit); - *frequency = t_params->ranges[--i].limit; - } - *config = t_params->ranges[i].config; - *cb = t_params->ranges[i].cb; - - tuner_dbg("freq = %d.%02d (%d), range = %d, " - "config = 0x%02x, cb = 0x%02x\n", - *frequency / 16, *frequency % 16 * 100 / 16, *frequency, - i, *config, *cb); - - return i; -} - -/* ---------------------------------------------------------------------- */ - -static void simple_set_rf_input(struct dvb_frontend *fe, - u8 *config, u8 *cb, unsigned int rf) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - - switch (priv->type) { - case TUNER_PHILIPS_TUV1236D: - switch (rf) { - case 1: - *cb |= 0x08; - break; - default: - *cb &= ~0x08; - break; - } - break; - case TUNER_PHILIPS_FCV1236D: - switch (rf) { - case 1: - *cb |= 0x01; - break; - default: - *cb &= ~0x01; - break; - } - break; - default: - break; - } -} - -static int simple_std_setup(struct dvb_frontend *fe, - struct analog_parameters *params, - u8 *config, u8 *cb) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - int rc; - - /* tv norm specific stuff for multi-norm tuners */ - switch (priv->type) { - case TUNER_PHILIPS_SECAM: /* FI1216MF */ - /* 0x01 -> ??? no change ??? */ - /* 0x02 -> PAL BDGHI / SECAM L */ - /* 0x04 -> ??? PAL others / SECAM others ??? */ - *cb &= ~0x03; - if (params->std & V4L2_STD_SECAM_L) - /* also valid for V4L2_STD_SECAM */ - *cb |= PHILIPS_MF_SET_STD_L; - else if (params->std & V4L2_STD_SECAM_LC) - *cb |= PHILIPS_MF_SET_STD_LC; - else /* V4L2_STD_B|V4L2_STD_GH */ - *cb |= PHILIPS_MF_SET_STD_BG; - break; - - case TUNER_TEMIC_4046FM5: - *cb &= ~0x0f; - - if (params->std & V4L2_STD_PAL_BG) { - *cb |= TEMIC_SET_PAL_BG; - - } else if (params->std & V4L2_STD_PAL_I) { - *cb |= TEMIC_SET_PAL_I; - - } else if (params->std & V4L2_STD_PAL_DK) { - *cb |= TEMIC_SET_PAL_DK; - - } else if (params->std & V4L2_STD_SECAM_L) { - *cb |= TEMIC_SET_PAL_L; - - } - break; - - case TUNER_PHILIPS_FQ1216ME: - *cb &= ~0x0f; - - if (params->std & (V4L2_STD_PAL_BG|V4L2_STD_PAL_DK)) { - *cb |= PHILIPS_SET_PAL_BGDK; - - } else if (params->std & V4L2_STD_PAL_I) { - *cb |= PHILIPS_SET_PAL_I; - - } else if (params->std & V4L2_STD_SECAM_L) { - *cb |= PHILIPS_SET_PAL_L; - - } - break; - - case TUNER_PHILIPS_FCV1236D: - /* 0x00 -> ATSC antenna input 1 */ - /* 0x01 -> ATSC antenna input 2 */ - /* 0x02 -> NTSC antenna input 1 */ - /* 0x03 -> NTSC antenna input 2 */ - *cb &= ~0x03; - if (!(params->std & V4L2_STD_ATSC)) - *cb |= 2; - break; - - case TUNER_MICROTUNE_4042FI5: - /* Set the charge pump for fast tuning */ - *config |= TUNER_CHARGE_PUMP; - break; - - case TUNER_PHILIPS_TUV1236D: - { - struct tuner_i2c_props i2c = priv->i2c_props; - /* 0x40 -> ATSC antenna input 1 */ - /* 0x48 -> ATSC antenna input 2 */ - /* 0x00 -> NTSC antenna input 1 */ - /* 0x08 -> NTSC antenna input 2 */ - u8 buffer[4] = { 0x14, 0x00, 0x17, 0x00}; - *cb &= ~0x40; - if (params->std & V4L2_STD_ATSC) { - *cb |= 0x40; - buffer[1] = 0x04; - } - /* set to the correct mode (analog or digital) */ - i2c.addr = 0x0a; - rc = tuner_i2c_xfer_send(&i2c, &buffer[0], 2); - if (2 != rc) - tuner_warn("i2c i/o error: rc == %d " - "(should be 2)\n", rc); - rc = tuner_i2c_xfer_send(&i2c, &buffer[2], 2); - if (2 != rc) - tuner_warn("i2c i/o error: rc == %d " - "(should be 2)\n", rc); - break; - } - } - if (atv_input[priv->nr]) - simple_set_rf_input(fe, config, cb, atv_input[priv->nr]); - - return 0; -} - -static int simple_set_aux_byte(struct dvb_frontend *fe, u8 config, u8 aux) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - int rc; - u8 buffer[2]; - - buffer[0] = (config & ~0x38) | 0x18; - buffer[1] = aux; - - tuner_dbg("setting aux byte: 0x%02x 0x%02x\n", buffer[0], buffer[1]); - - rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 2); - if (2 != rc) - tuner_warn("i2c i/o error: rc == %d (should be 2)\n", rc); - - return rc == 2 ? 0 : rc; -} - -static int simple_post_tune(struct dvb_frontend *fe, u8 *buffer, - u16 div, u8 config, u8 cb) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - int rc; - - switch (priv->type) { - case TUNER_LG_TDVS_H06XF: - simple_set_aux_byte(fe, config, 0x20); - break; - case TUNER_PHILIPS_FQ1216LME_MK3: - simple_set_aux_byte(fe, config, 0x60); /* External AGC */ - break; - case TUNER_MICROTUNE_4042FI5: - { - /* FIXME - this may also work for other tuners */ - unsigned long timeout = jiffies + msecs_to_jiffies(1); - u8 status_byte = 0; - - /* Wait until the PLL locks */ - for (;;) { - if (time_after(jiffies, timeout)) - return 0; - rc = tuner_i2c_xfer_recv(&priv->i2c_props, - &status_byte, 1); - if (1 != rc) { - tuner_warn("i2c i/o read error: rc == %d " - "(should be 1)\n", rc); - break; - } - if (status_byte & TUNER_PLL_LOCKED) - break; - udelay(10); - } - - /* Set the charge pump for optimized phase noise figure */ - config &= ~TUNER_CHARGE_PUMP; - buffer[0] = (div>>8) & 0x7f; - buffer[1] = div & 0xff; - buffer[2] = config; - buffer[3] = cb; - tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n", - buffer[0], buffer[1], buffer[2], buffer[3]); - - rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); - if (4 != rc) - tuner_warn("i2c i/o error: rc == %d " - "(should be 4)\n", rc); - break; - } - } - - return 0; -} - -static int simple_radio_bandswitch(struct dvb_frontend *fe, u8 *buffer) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - - switch (priv->type) { - case TUNER_TENA_9533_DI: - case TUNER_YMEC_TVF_5533MF: - tuner_dbg("This tuner doesn't have FM. " - "Most cards have a TEA5767 for FM\n"); - return 0; - case TUNER_PHILIPS_FM1216ME_MK3: - case TUNER_PHILIPS_FM1236_MK3: - case TUNER_PHILIPS_FMD1216ME_MK3: - case TUNER_PHILIPS_FMD1216MEX_MK3: - case TUNER_LG_NTSC_TAPE: - case TUNER_PHILIPS_FM1256_IH3: - case TUNER_TCL_MF02GIP_5N: - buffer[3] = 0x19; - break; - case TUNER_PHILIPS_FM1216MK5: - buffer[2] = 0x88; - buffer[3] = 0x09; - break; - case TUNER_TNF_5335MF: - buffer[3] = 0x11; - break; - case TUNER_LG_PAL_FM: - buffer[3] = 0xa5; - break; - case TUNER_THOMSON_DTT761X: - buffer[3] = 0x39; - break; - case TUNER_PHILIPS_FQ1216LME_MK3: - case TUNER_PHILIPS_FQ1236_MK5: - tuner_err("This tuner doesn't have FM\n"); - /* Set the low band for sanity, since it covers 88-108 MHz */ - buffer[3] = 0x01; - break; - case TUNER_MICROTUNE_4049FM5: - default: - buffer[3] = 0xa4; - break; - } - - return 0; -} - -/* ---------------------------------------------------------------------- */ - -static int simple_set_tv_freq(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - u8 config, cb; - u16 div; - u8 buffer[4]; - int rc, IFPCoff, i; - enum param_type desired_type; - struct tuner_params *t_params; - - /* IFPCoff = Video Intermediate Frequency - Vif: - 940 =16*58.75 NTSC/J (Japan) - 732 =16*45.75 M/N STD - 704 =16*44 ATSC (at DVB code) - 632 =16*39.50 I U.K. - 622.4=16*38.90 B/G D/K I, L STD - 592 =16*37.00 D China - 590 =16.36.875 B Australia - 543.2=16*33.95 L' STD - 171.2=16*10.70 FM Radio (at set_radio_freq) - */ - - if (params->std == V4L2_STD_NTSC_M_JP) { - IFPCoff = 940; - desired_type = TUNER_PARAM_TYPE_NTSC; - } else if ((params->std & V4L2_STD_MN) && - !(params->std & ~V4L2_STD_MN)) { - IFPCoff = 732; - desired_type = TUNER_PARAM_TYPE_NTSC; - } else if (params->std == V4L2_STD_SECAM_LC) { - IFPCoff = 543; - desired_type = TUNER_PARAM_TYPE_SECAM; - } else { - IFPCoff = 623; - desired_type = TUNER_PARAM_TYPE_PAL; - } - - t_params = simple_tuner_params(fe, desired_type); - - i = simple_config_lookup(fe, t_params, ¶ms->frequency, - &config, &cb); - - div = params->frequency + IFPCoff + offset; - - tuner_dbg("Freq= %d.%02d MHz, V_IF=%d.%02d MHz, " - "Offset=%d.%02d MHz, div=%0d\n", - params->frequency / 16, params->frequency % 16 * 100 / 16, - IFPCoff / 16, IFPCoff % 16 * 100 / 16, - offset / 16, offset % 16 * 100 / 16, div); - - /* tv norm specific stuff for multi-norm tuners */ - simple_std_setup(fe, params, &config, &cb); - - if (t_params->cb_first_if_lower_freq && div < priv->last_div) { - buffer[0] = config; - buffer[1] = cb; - buffer[2] = (div>>8) & 0x7f; - buffer[3] = div & 0xff; - } else { - buffer[0] = (div>>8) & 0x7f; - buffer[1] = div & 0xff; - buffer[2] = config; - buffer[3] = cb; - } - priv->last_div = div; - if (t_params->has_tda9887) { - struct v4l2_priv_tun_config tda9887_cfg; - int tda_config = 0; - int is_secam_l = (params->std & (V4L2_STD_SECAM_L | - V4L2_STD_SECAM_LC)) && - !(params->std & ~(V4L2_STD_SECAM_L | - V4L2_STD_SECAM_LC)); - - tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &tda_config; - - if (params->std == V4L2_STD_SECAM_LC) { - if (t_params->port1_active ^ t_params->port1_invert_for_secam_lc) - tda_config |= TDA9887_PORT1_ACTIVE; - if (t_params->port2_active ^ t_params->port2_invert_for_secam_lc) - tda_config |= TDA9887_PORT2_ACTIVE; - } else { - if (t_params->port1_active) - tda_config |= TDA9887_PORT1_ACTIVE; - if (t_params->port2_active) - tda_config |= TDA9887_PORT2_ACTIVE; - } - if (t_params->intercarrier_mode) - tda_config |= TDA9887_INTERCARRIER; - if (is_secam_l) { - if (i == 0 && t_params->default_top_secam_low) - tda_config |= TDA9887_TOP(t_params->default_top_secam_low); - else if (i == 1 && t_params->default_top_secam_mid) - tda_config |= TDA9887_TOP(t_params->default_top_secam_mid); - else if (t_params->default_top_secam_high) - tda_config |= TDA9887_TOP(t_params->default_top_secam_high); - } else { - if (i == 0 && t_params->default_top_low) - tda_config |= TDA9887_TOP(t_params->default_top_low); - else if (i == 1 && t_params->default_top_mid) - tda_config |= TDA9887_TOP(t_params->default_top_mid); - else if (t_params->default_top_high) - tda_config |= TDA9887_TOP(t_params->default_top_high); - } - if (t_params->default_pll_gating_18) - tda_config |= TDA9887_GATING_18; - i2c_clients_command(priv->i2c_props.adap, TUNER_SET_CONFIG, - &tda9887_cfg); - } - tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n", - buffer[0], buffer[1], buffer[2], buffer[3]); - - rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); - if (4 != rc) - tuner_warn("i2c i/o error: rc == %d (should be 4)\n", rc); - - simple_post_tune(fe, &buffer[0], div, config, cb); - - return 0; -} - -static int simple_set_radio_freq(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct tunertype *tun; - struct tuner_simple_priv *priv = fe->tuner_priv; - u8 buffer[4]; - u16 div; - int rc, j; - struct tuner_params *t_params; - unsigned int freq = params->frequency; - - tun = priv->tun; - - for (j = tun->count-1; j > 0; j--) - if (tun->params[j].type == TUNER_PARAM_TYPE_RADIO) - break; - /* default t_params (j=0) will be used if desired type wasn't found */ - t_params = &tun->params[j]; - - /* Select Radio 1st IF used */ - switch (t_params->radio_if) { - case 0: /* 10.7 MHz */ - freq += (unsigned int)(10.7*16000); - break; - case 1: /* 33.3 MHz */ - freq += (unsigned int)(33.3*16000); - break; - case 2: /* 41.3 MHz */ - freq += (unsigned int)(41.3*16000); - break; - default: - tuner_warn("Unsupported radio_if value %d\n", - t_params->radio_if); - return 0; - } - - buffer[2] = (t_params->ranges[0].config & ~TUNER_RATIO_MASK) | - TUNER_RATIO_SELECT_50; /* 50 kHz step */ - - /* Bandswitch byte */ - simple_radio_bandswitch(fe, &buffer[0]); - - /* Convert from 1/16 kHz V4L steps to 1/20 MHz (=50 kHz) PLL steps - freq * (1 Mhz / 16000 V4L steps) * (20 PLL steps / 1 MHz) = - freq * (1/800) */ - div = (freq + 400) / 800; - - if (t_params->cb_first_if_lower_freq && div < priv->last_div) { - buffer[0] = buffer[2]; - buffer[1] = buffer[3]; - buffer[2] = (div>>8) & 0x7f; - buffer[3] = div & 0xff; - } else { - buffer[0] = (div>>8) & 0x7f; - buffer[1] = div & 0xff; - } - - tuner_dbg("radio 0x%02x 0x%02x 0x%02x 0x%02x\n", - buffer[0], buffer[1], buffer[2], buffer[3]); - priv->last_div = div; - - if (t_params->has_tda9887) { - int config = 0; - struct v4l2_priv_tun_config tda9887_cfg; - - tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &config; - - if (t_params->port1_active && - !t_params->port1_fm_high_sensitivity) - config |= TDA9887_PORT1_ACTIVE; - if (t_params->port2_active && - !t_params->port2_fm_high_sensitivity) - config |= TDA9887_PORT2_ACTIVE; - if (t_params->intercarrier_mode) - config |= TDA9887_INTERCARRIER; -/* if (t_params->port1_set_for_fm_mono) - config &= ~TDA9887_PORT1_ACTIVE;*/ - if (t_params->fm_gain_normal) - config |= TDA9887_GAIN_NORMAL; - if (t_params->radio_if == 2) - config |= TDA9887_RIF_41_3; - i2c_clients_command(priv->i2c_props.adap, TUNER_SET_CONFIG, - &tda9887_cfg); - } - rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); - if (4 != rc) - tuner_warn("i2c i/o error: rc == %d (should be 4)\n", rc); - - /* Write AUX byte */ - switch (priv->type) { - case TUNER_PHILIPS_FM1216ME_MK3: - buffer[2] = 0x98; - buffer[3] = 0x20; /* set TOP AGC */ - rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); - if (4 != rc) - tuner_warn("i2c i/o error: rc == %d (should be 4)\n", rc); - break; - } - - return 0; -} - -static int simple_set_params(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - int ret = -EINVAL; - - if (priv->i2c_props.adap == NULL) - return -EINVAL; - - switch (params->mode) { - case V4L2_TUNER_RADIO: - ret = simple_set_radio_freq(fe, params); - priv->frequency = params->frequency * 125 / 2; - break; - case V4L2_TUNER_ANALOG_TV: - case V4L2_TUNER_DIGITAL_TV: - ret = simple_set_tv_freq(fe, params); - priv->frequency = params->frequency * 62500; - break; - } - priv->bandwidth = 0; - - return ret; -} - -static void simple_set_dvb(struct dvb_frontend *fe, u8 *buf, - const u32 delsys, - const u32 frequency, - const u32 bandwidth) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - - switch (priv->type) { - case TUNER_PHILIPS_FMD1216ME_MK3: - case TUNER_PHILIPS_FMD1216MEX_MK3: - if (bandwidth == 8000000 && - frequency >= 158870000) - buf[3] |= 0x08; - break; - case TUNER_PHILIPS_TD1316: - /* determine band */ - buf[3] |= (frequency < 161000000) ? 1 : - (frequency < 444000000) ? 2 : 4; - - /* setup PLL filter */ - if (bandwidth == 8000000) - buf[3] |= 1 << 3; - break; - case TUNER_PHILIPS_TUV1236D: - case TUNER_PHILIPS_FCV1236D: - { - unsigned int new_rf; - - if (dtv_input[priv->nr]) - new_rf = dtv_input[priv->nr]; - else - switch (delsys) { - case SYS_DVBC_ANNEX_B: - new_rf = 1; - break; - case SYS_ATSC: - default: - new_rf = 0; - break; - } - simple_set_rf_input(fe, &buf[2], &buf[3], new_rf); - break; - } - default: - break; - } -} - -static u32 simple_dvb_configure(struct dvb_frontend *fe, u8 *buf, - const u32 delsys, - const u32 freq, - const u32 bw) -{ - /* This function returns the tuned frequency on success, 0 on error */ - struct tuner_simple_priv *priv = fe->tuner_priv; - struct tunertype *tun = priv->tun; - static struct tuner_params *t_params; - u8 config, cb; - u32 div; - int ret; - u32 frequency = freq / 62500; - - if (!tun->stepsize) { - /* tuner-core was loaded before the digital tuner was - * configured and somehow picked the wrong tuner type */ - tuner_err("attempt to treat tuner %d (%s) as digital tuner " - "without stepsize defined.\n", - priv->type, priv->tun->name); - return 0; /* failure */ - } - - t_params = simple_tuner_params(fe, TUNER_PARAM_TYPE_DIGITAL); - ret = simple_config_lookup(fe, t_params, &frequency, &config, &cb); - if (ret < 0) - return 0; /* failure */ - - div = ((frequency + t_params->iffreq) * 62500 + offset + - tun->stepsize/2) / tun->stepsize; - - buf[0] = div >> 8; - buf[1] = div & 0xff; - buf[2] = config; - buf[3] = cb; - - simple_set_dvb(fe, buf, delsys, freq, bw); - - tuner_dbg("%s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n", - tun->name, div, buf[0], buf[1], buf[2], buf[3]); - - /* calculate the frequency we set it to */ - return (div * tun->stepsize) - t_params->iffreq; -} - -static int simple_dvb_calc_regs(struct dvb_frontend *fe, - u8 *buf, int buf_len) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - u32 delsys = c->delivery_system; - u32 bw = c->bandwidth_hz; - struct tuner_simple_priv *priv = fe->tuner_priv; - u32 frequency; - - if (buf_len < 5) - return -EINVAL; - - frequency = simple_dvb_configure(fe, buf+1, delsys, c->frequency, bw); - if (frequency == 0) - return -EINVAL; - - buf[0] = priv->i2c_props.addr; - - priv->frequency = frequency; - priv->bandwidth = c->bandwidth_hz; - - return 5; -} - -static int simple_dvb_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; - u32 freq = c->frequency; - struct tuner_simple_priv *priv = fe->tuner_priv; - u32 frequency; - u32 prev_freq, prev_bw; - int ret; - u8 buf[5]; - - if (priv->i2c_props.adap == NULL) - return -EINVAL; - - prev_freq = priv->frequency; - prev_bw = priv->bandwidth; - - frequency = simple_dvb_configure(fe, buf+1, delsys, freq, bw); - if (frequency == 0) - return -EINVAL; - - buf[0] = priv->i2c_props.addr; - - priv->frequency = frequency; - priv->bandwidth = bw; - - /* put analog demod in standby when tuning digital */ - if (fe->ops.analog_ops.standby) - fe->ops.analog_ops.standby(fe); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - /* buf[0] contains the i2c address, but * - * we already have it in i2c_props.addr */ - ret = tuner_i2c_xfer_send(&priv->i2c_props, buf+1, 4); - if (ret != 4) - goto fail; - - return 0; -fail: - /* calc_regs sets frequency and bandwidth. if we failed, unset them */ - priv->frequency = prev_freq; - priv->bandwidth = prev_bw; - - return ret; -} - -static int simple_init(struct dvb_frontend *fe) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - - if (priv->i2c_props.adap == NULL) - return -EINVAL; - - if (priv->tun->initdata) { - int ret; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - ret = tuner_i2c_xfer_send(&priv->i2c_props, - priv->tun->initdata + 1, - priv->tun->initdata[0]); - if (ret != priv->tun->initdata[0]) - return ret; - } - - return 0; -} - -static int simple_sleep(struct dvb_frontend *fe) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - - if (priv->i2c_props.adap == NULL) - return -EINVAL; - - if (priv->tun->sleepdata) { - int ret; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - ret = tuner_i2c_xfer_send(&priv->i2c_props, - priv->tun->sleepdata + 1, - priv->tun->sleepdata[0]); - if (ret != priv->tun->sleepdata[0]) - return ret; - } - - return 0; -} - -static int simple_release(struct dvb_frontend *fe) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - - mutex_lock(&tuner_simple_list_mutex); - - if (priv) - hybrid_tuner_release_state(priv); - - mutex_unlock(&tuner_simple_list_mutex); - - fe->tuner_priv = NULL; - - return 0; -} - -static int simple_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - *frequency = priv->frequency; - return 0; -} - -static int simple_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) -{ - struct tuner_simple_priv *priv = fe->tuner_priv; - *bandwidth = priv->bandwidth; - return 0; -} - -static struct dvb_tuner_ops simple_tuner_ops = { - .init = simple_init, - .sleep = simple_sleep, - .set_analog_params = simple_set_params, - .set_params = simple_dvb_set_params, - .calc_regs = simple_dvb_calc_regs, - .release = simple_release, - .get_frequency = simple_get_frequency, - .get_bandwidth = simple_get_bandwidth, - .get_status = simple_get_status, - .get_rf_strength = simple_get_rf_strength, -}; - -struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c_adap, - u8 i2c_addr, - unsigned int type) -{ - struct tuner_simple_priv *priv = NULL; - int instance; - - if (type >= tuner_count) { - printk(KERN_WARNING "%s: invalid tuner type: %d (max: %d)\n", - __func__, type, tuner_count-1); - return NULL; - } - - /* If i2c_adap is set, check that the tuner is at the correct address. - * Otherwise, if i2c_adap is NULL, the tuner will be programmed directly - * by the digital demod via calc_regs. - */ - if (i2c_adap != NULL) { - u8 b[1]; - struct i2c_msg msg = { - .addr = i2c_addr, .flags = I2C_M_RD, - .buf = b, .len = 1, - }; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - if (1 != i2c_transfer(i2c_adap, &msg, 1)) - printk(KERN_WARNING "tuner-simple %d-%04x: " - "unable to probe %s, proceeding anyway.", - i2c_adapter_id(i2c_adap), i2c_addr, - tuners[type].name); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - } - - mutex_lock(&tuner_simple_list_mutex); - - instance = hybrid_tuner_request_state(struct tuner_simple_priv, priv, - hybrid_tuner_instance_list, - i2c_adap, i2c_addr, - "tuner-simple"); - switch (instance) { - case 0: - mutex_unlock(&tuner_simple_list_mutex); - return NULL; - case 1: - fe->tuner_priv = priv; - - priv->type = type; - priv->tun = &tuners[type]; - priv->nr = simple_devcount++; - break; - default: - fe->tuner_priv = priv; - break; - } - - mutex_unlock(&tuner_simple_list_mutex); - - memcpy(&fe->ops.tuner_ops, &simple_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - if (type != priv->type) - tuner_warn("couldn't set type to %d. Using %d (%s) instead\n", - type, priv->type, priv->tun->name); - else - tuner_info("type set to %d (%s)\n", - priv->type, priv->tun->name); - - if ((debug) || ((atv_input[priv->nr] > 0) || - (dtv_input[priv->nr] > 0))) { - if (0 == atv_input[priv->nr]) - tuner_info("tuner %d atv rf input will be " - "autoselected\n", priv->nr); - else - tuner_info("tuner %d atv rf input will be " - "set to input %d (insmod option)\n", - priv->nr, atv_input[priv->nr]); - if (0 == dtv_input[priv->nr]) - tuner_info("tuner %d dtv rf input will be " - "autoselected\n", priv->nr); - else - tuner_info("tuner %d dtv rf input will be " - "set to input %d (insmod option)\n", - priv->nr, dtv_input[priv->nr]); - } - - strlcpy(fe->ops.tuner_ops.info.name, priv->tun->name, - sizeof(fe->ops.tuner_ops.info.name)); - - return fe; -} -EXPORT_SYMBOL_GPL(simple_tuner_attach); - -MODULE_DESCRIPTION("Simple 4-control-bytes style tuner driver"); -MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); -MODULE_LICENSE("GPL"); - -/* - * 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/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 -#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-types.c b/drivers/media/common/tuners/tuner-types.c deleted file mode 100644 index 2da4440c16ee..000000000000 --- a/drivers/media/common/tuners/tuner-types.c +++ /dev/null @@ -1,1883 +0,0 @@ -/* - * - * i2c tv tuner chip device type database. - * - */ - -#include -#include -#include -#include - -/* ---------------------------------------------------------------------- */ - -/* - * The floats in the tuner struct are computed at compile time - * by gcc and cast back to integers. Thus we don't violate the - * "no float in kernel" rule. - * - * A tuner_range may be referenced by multiple tuner_params structs. - * There are many duplicates in here. Reusing tuner_range structs, - * rather than defining new ones for each tuner, will cut down on - * memory usage, and is preferred when possible. - * - * Each tuner_params array may contain one or more elements, one - * for each video standard. - * - * FIXME: tuner_params struct contains an element, tda988x. We must - * set this for all tuners that contain a tda988x chip, and then we - * can remove this setting from the various card structs. - * - * FIXME: Right now, all tuners are using the first tuner_params[] - * array element for analog mode. In the future, we will be merging - * similar tuner definitions together, such that each tuner definition - * will have a tuner_params struct for each available video standard. - * At that point, the tuner_params[] array element will be chosen - * based on the video standard in use. - */ - -/* The following was taken from dvb-pll.c: */ - -/* Set AGC TOP value to 103 dBuV: - * 0x80 = Control Byte - * 0x40 = 250 uA charge pump (irrelevant) - * 0x18 = Aux Byte to follow - * 0x06 = 64.5 kHz divider (irrelevant) - * 0x01 = Disable Vt (aka sleep) - * - * 0x00 = AGC Time constant 2s Iagc = 300 nA (vs 0x80 = 9 nA) - * 0x50 = AGC Take over point = 103 dBuV - */ -static u8 tua603x_agc103[] = { 2, 0x80|0x40|0x18|0x06|0x01, 0x00|0x50 }; - -/* 0x04 = 166.67 kHz divider - * - * 0x80 = AGC Time constant 50ms Iagc = 9 uA - * 0x20 = AGC Take over point = 112 dBuV - */ -static u8 tua603x_agc112[] = { 2, 0x80|0x40|0x18|0x04|0x01, 0x80|0x20 }; - -/* 0-9 */ -/* ------------ TUNER_TEMIC_PAL - TEMIC PAL ------------ */ - -static struct tuner_range tuner_temic_pal_ranges[] = { - { 16 * 140.25 /*MHz*/, 0x8e, 0x02, }, - { 16 * 463.25 /*MHz*/, 0x8e, 0x04, }, - { 16 * 999.99 , 0x8e, 0x01, }, -}; - -static struct tuner_params tuner_temic_pal_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_temic_pal_ranges, - .count = ARRAY_SIZE(tuner_temic_pal_ranges), - }, -}; - -/* ------------ TUNER_PHILIPS_PAL_I - Philips PAL_I ------------ */ - -static struct tuner_range tuner_philips_pal_i_ranges[] = { - { 16 * 140.25 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 463.25 /*MHz*/, 0x8e, 0x90, }, - { 16 * 999.99 , 0x8e, 0x30, }, -}; - -static struct tuner_params tuner_philips_pal_i_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_philips_pal_i_ranges, - .count = ARRAY_SIZE(tuner_philips_pal_i_ranges), - }, -}; - -/* ------------ TUNER_PHILIPS_NTSC - Philips NTSC ------------ */ - -static struct tuner_range tuner_philips_ntsc_ranges[] = { - { 16 * 157.25 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 451.25 /*MHz*/, 0x8e, 0x90, }, - { 16 * 999.99 , 0x8e, 0x30, }, -}; - -static struct tuner_params tuner_philips_ntsc_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_philips_ntsc_ranges, - .count = ARRAY_SIZE(tuner_philips_ntsc_ranges), - .cb_first_if_lower_freq = 1, - }, -}; - -/* ------------ TUNER_PHILIPS_SECAM - Philips SECAM ------------ */ - -static struct tuner_range tuner_philips_secam_ranges[] = { - { 16 * 168.25 /*MHz*/, 0x8e, 0xa7, }, - { 16 * 447.25 /*MHz*/, 0x8e, 0x97, }, - { 16 * 999.99 , 0x8e, 0x37, }, -}; - -static struct tuner_params tuner_philips_secam_params[] = { - { - .type = TUNER_PARAM_TYPE_SECAM, - .ranges = tuner_philips_secam_ranges, - .count = ARRAY_SIZE(tuner_philips_secam_ranges), - .cb_first_if_lower_freq = 1, - }, -}; - -/* ------------ TUNER_PHILIPS_PAL - Philips PAL ------------ */ - -static struct tuner_range tuner_philips_pal_ranges[] = { - { 16 * 168.25 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 447.25 /*MHz*/, 0x8e, 0x90, }, - { 16 * 999.99 , 0x8e, 0x30, }, -}; - -static struct tuner_params tuner_philips_pal_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_philips_pal_ranges, - .count = ARRAY_SIZE(tuner_philips_pal_ranges), - .cb_first_if_lower_freq = 1, - }, -}; - -/* ------------ TUNER_TEMIC_NTSC - TEMIC NTSC ------------ */ - -static struct tuner_range tuner_temic_ntsc_ranges[] = { - { 16 * 157.25 /*MHz*/, 0x8e, 0x02, }, - { 16 * 463.25 /*MHz*/, 0x8e, 0x04, }, - { 16 * 999.99 , 0x8e, 0x01, }, -}; - -static struct tuner_params tuner_temic_ntsc_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_temic_ntsc_ranges, - .count = ARRAY_SIZE(tuner_temic_ntsc_ranges), - }, -}; - -/* ------------ TUNER_TEMIC_PAL_I - TEMIC PAL_I ------------ */ - -static struct tuner_range tuner_temic_pal_i_ranges[] = { - { 16 * 170.00 /*MHz*/, 0x8e, 0x02, }, - { 16 * 450.00 /*MHz*/, 0x8e, 0x04, }, - { 16 * 999.99 , 0x8e, 0x01, }, -}; - -static struct tuner_params tuner_temic_pal_i_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_temic_pal_i_ranges, - .count = ARRAY_SIZE(tuner_temic_pal_i_ranges), - }, -}; - -/* ------------ TUNER_TEMIC_4036FY5_NTSC - TEMIC NTSC ------------ */ - -static struct tuner_range tuner_temic_4036fy5_ntsc_ranges[] = { - { 16 * 157.25 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 463.25 /*MHz*/, 0x8e, 0x90, }, - { 16 * 999.99 , 0x8e, 0x30, }, -}; - -static struct tuner_params tuner_temic_4036fy5_ntsc_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_temic_4036fy5_ntsc_ranges, - .count = ARRAY_SIZE(tuner_temic_4036fy5_ntsc_ranges), - }, -}; - -/* ------------ TUNER_ALPS_TSBH1_NTSC - TEMIC NTSC ------------ */ - -static struct tuner_range tuner_alps_tsb_1_ranges[] = { - { 16 * 137.25 /*MHz*/, 0x8e, 0x01, }, - { 16 * 385.25 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x08, }, -}; - -static struct tuner_params tuner_alps_tsbh1_ntsc_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_alps_tsb_1_ranges, - .count = ARRAY_SIZE(tuner_alps_tsb_1_ranges), - }, -}; - -/* 10-19 */ -/* ------------ TUNER_ALPS_TSBE1_PAL - TEMIC PAL ------------ */ - -static struct tuner_params tuner_alps_tsb_1_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_alps_tsb_1_ranges, - .count = ARRAY_SIZE(tuner_alps_tsb_1_ranges), - }, -}; - -/* ------------ TUNER_ALPS_TSBB5_PAL_I - Alps PAL_I ------------ */ - -static struct tuner_range tuner_alps_tsb_5_pal_ranges[] = { - { 16 * 133.25 /*MHz*/, 0x8e, 0x01, }, - { 16 * 351.25 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x08, }, -}; - -static struct tuner_params tuner_alps_tsbb5_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_alps_tsb_5_pal_ranges, - .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges), - }, -}; - -/* ------------ TUNER_ALPS_TSBE5_PAL - Alps PAL ------------ */ - -static struct tuner_params tuner_alps_tsbe5_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_alps_tsb_5_pal_ranges, - .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges), - }, -}; - -/* ------------ TUNER_ALPS_TSBC5_PAL - Alps PAL ------------ */ - -static struct tuner_params tuner_alps_tsbc5_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_alps_tsb_5_pal_ranges, - .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges), - }, -}; - -/* ------------ TUNER_TEMIC_4006FH5_PAL - TEMIC PAL ------------ */ - -static struct tuner_range tuner_lg_pal_ranges[] = { - { 16 * 170.00 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 450.00 /*MHz*/, 0x8e, 0x90, }, - { 16 * 999.99 , 0x8e, 0x30, }, -}; - -static struct tuner_params tuner_temic_4006fh5_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_lg_pal_ranges, - .count = ARRAY_SIZE(tuner_lg_pal_ranges), - }, -}; - -/* ------------ TUNER_ALPS_TSHC6_NTSC - Alps NTSC ------------ */ - -static struct tuner_range tuner_alps_tshc6_ntsc_ranges[] = { - { 16 * 137.25 /*MHz*/, 0x8e, 0x14, }, - { 16 * 385.25 /*MHz*/, 0x8e, 0x12, }, - { 16 * 999.99 , 0x8e, 0x11, }, -}; - -static struct tuner_params tuner_alps_tshc6_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_alps_tshc6_ntsc_ranges, - .count = ARRAY_SIZE(tuner_alps_tshc6_ntsc_ranges), - }, -}; - -/* ------------ TUNER_TEMIC_PAL_DK - TEMIC PAL ------------ */ - -static struct tuner_range tuner_temic_pal_dk_ranges[] = { - { 16 * 168.25 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 456.25 /*MHz*/, 0x8e, 0x90, }, - { 16 * 999.99 , 0x8e, 0x30, }, -}; - -static struct tuner_params tuner_temic_pal_dk_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_temic_pal_dk_ranges, - .count = ARRAY_SIZE(tuner_temic_pal_dk_ranges), - }, -}; - -/* ------------ TUNER_PHILIPS_NTSC_M - Philips NTSC ------------ */ - -static struct tuner_range tuner_philips_ntsc_m_ranges[] = { - { 16 * 160.00 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 454.00 /*MHz*/, 0x8e, 0x90, }, - { 16 * 999.99 , 0x8e, 0x30, }, -}; - -static struct tuner_params tuner_philips_ntsc_m_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_philips_ntsc_m_ranges, - .count = ARRAY_SIZE(tuner_philips_ntsc_m_ranges), - }, -}; - -/* ------------ TUNER_TEMIC_4066FY5_PAL_I - TEMIC PAL_I ------------ */ - -static struct tuner_range tuner_temic_40x6f_5_pal_ranges[] = { - { 16 * 169.00 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 454.00 /*MHz*/, 0x8e, 0x90, }, - { 16 * 999.99 , 0x8e, 0x30, }, -}; - -static struct tuner_params tuner_temic_4066fy5_pal_i_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_temic_40x6f_5_pal_ranges, - .count = ARRAY_SIZE(tuner_temic_40x6f_5_pal_ranges), - }, -}; - -/* ------------ TUNER_TEMIC_4006FN5_MULTI_PAL - TEMIC PAL ------------ */ - -static struct tuner_params tuner_temic_4006fn5_multi_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_temic_40x6f_5_pal_ranges, - .count = ARRAY_SIZE(tuner_temic_40x6f_5_pal_ranges), - }, -}; - -/* 20-29 */ -/* ------------ TUNER_TEMIC_4009FR5_PAL - TEMIC PAL ------------ */ - -static struct tuner_range tuner_temic_4009f_5_pal_ranges[] = { - { 16 * 141.00 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 464.00 /*MHz*/, 0x8e, 0x90, }, - { 16 * 999.99 , 0x8e, 0x30, }, -}; - -static struct tuner_params tuner_temic_4009f_5_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_temic_4009f_5_pal_ranges, - .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), - }, -}; - -/* ------------ TUNER_TEMIC_4039FR5_NTSC - TEMIC NTSC ------------ */ - -static struct tuner_range tuner_temic_4x3x_f_5_ntsc_ranges[] = { - { 16 * 158.00 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 453.00 /*MHz*/, 0x8e, 0x90, }, - { 16 * 999.99 , 0x8e, 0x30, }, -}; - -static struct tuner_params tuner_temic_4039fr5_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_temic_4x3x_f_5_ntsc_ranges, - .count = ARRAY_SIZE(tuner_temic_4x3x_f_5_ntsc_ranges), - }, -}; - -/* ------------ TUNER_TEMIC_4046FM5 - TEMIC PAL ------------ */ - -static struct tuner_params tuner_temic_4046fm5_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_temic_40x6f_5_pal_ranges, - .count = ARRAY_SIZE(tuner_temic_40x6f_5_pal_ranges), - }, -}; - -/* ------------ TUNER_PHILIPS_PAL_DK - Philips PAL ------------ */ - -static struct tuner_params tuner_philips_pal_dk_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_lg_pal_ranges, - .count = ARRAY_SIZE(tuner_lg_pal_ranges), - }, -}; - -/* ------------ TUNER_PHILIPS_FQ1216ME - Philips PAL ------------ */ - -static struct tuner_params tuner_philips_fq1216me_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_lg_pal_ranges, - .count = ARRAY_SIZE(tuner_lg_pal_ranges), - .has_tda9887 = 1, - .port1_active = 1, - .port2_active = 1, - .port2_invert_for_secam_lc = 1, - }, -}; - -/* ------------ TUNER_LG_PAL_I_FM - LGINNOTEK PAL_I ------------ */ - -static struct tuner_params tuner_lg_pal_i_fm_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_lg_pal_ranges, - .count = ARRAY_SIZE(tuner_lg_pal_ranges), - }, -}; - -/* ------------ TUNER_LG_PAL_I - LGINNOTEK PAL_I ------------ */ - -static struct tuner_params tuner_lg_pal_i_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_lg_pal_ranges, - .count = ARRAY_SIZE(tuner_lg_pal_ranges), - }, -}; - -/* ------------ TUNER_LG_NTSC_FM - LGINNOTEK NTSC ------------ */ - -static struct tuner_range tuner_lg_ntsc_fm_ranges[] = { - { 16 * 210.00 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 497.00 /*MHz*/, 0x8e, 0x90, }, - { 16 * 999.99 , 0x8e, 0x30, }, -}; - -static struct tuner_params tuner_lg_ntsc_fm_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_lg_ntsc_fm_ranges, - .count = ARRAY_SIZE(tuner_lg_ntsc_fm_ranges), - }, -}; - -/* ------------ TUNER_LG_PAL_FM - LGINNOTEK PAL ------------ */ - -static struct tuner_params tuner_lg_pal_fm_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_lg_pal_ranges, - .count = ARRAY_SIZE(tuner_lg_pal_ranges), - }, -}; - -/* ------------ TUNER_LG_PAL - LGINNOTEK PAL ------------ */ - -static struct tuner_params tuner_lg_pal_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_lg_pal_ranges, - .count = ARRAY_SIZE(tuner_lg_pal_ranges), - }, -}; - -/* 30-39 */ -/* ------------ TUNER_TEMIC_4009FN5_MULTI_PAL_FM - TEMIC PAL ------------ */ - -static struct tuner_params tuner_temic_4009_fn5_multi_pal_fm_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_temic_4009f_5_pal_ranges, - .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), - }, -}; - -/* ------------ TUNER_SHARP_2U5JF5540_NTSC - SHARP NTSC ------------ */ - -static struct tuner_range tuner_sharp_2u5jf5540_ntsc_ranges[] = { - { 16 * 137.25 /*MHz*/, 0x8e, 0x01, }, - { 16 * 317.25 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x08, }, -}; - -static struct tuner_params tuner_sharp_2u5jf5540_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_sharp_2u5jf5540_ntsc_ranges, - .count = ARRAY_SIZE(tuner_sharp_2u5jf5540_ntsc_ranges), - }, -}; - -/* ------------ TUNER_Samsung_PAL_TCPM9091PD27 - Samsung PAL ------------ */ - -static struct tuner_range tuner_samsung_pal_tcpm9091pd27_ranges[] = { - { 16 * 169 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 464 /*MHz*/, 0x8e, 0x90, }, - { 16 * 999.99 , 0x8e, 0x30, }, -}; - -static struct tuner_params tuner_samsung_pal_tcpm9091pd27_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_samsung_pal_tcpm9091pd27_ranges, - .count = ARRAY_SIZE(tuner_samsung_pal_tcpm9091pd27_ranges), - }, -}; - -/* ------------ TUNER_TEMIC_4106FH5 - TEMIC PAL ------------ */ - -static struct tuner_params tuner_temic_4106fh5_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_temic_4009f_5_pal_ranges, - .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), - }, -}; - -/* ------------ TUNER_TEMIC_4012FY5 - TEMIC PAL ------------ */ - -static struct tuner_params tuner_temic_4012fy5_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_temic_pal_ranges, - .count = ARRAY_SIZE(tuner_temic_pal_ranges), - }, -}; - -/* ------------ TUNER_TEMIC_4136FY5 - TEMIC NTSC ------------ */ - -static struct tuner_params tuner_temic_4136_fy5_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_temic_4x3x_f_5_ntsc_ranges, - .count = ARRAY_SIZE(tuner_temic_4x3x_f_5_ntsc_ranges), - }, -}; - -/* ------------ TUNER_LG_PAL_NEW_TAPC - LGINNOTEK PAL ------------ */ - -static struct tuner_range tuner_lg_new_tapc_ranges[] = { - { 16 * 170.00 /*MHz*/, 0x8e, 0x01, }, - { 16 * 450.00 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x08, }, -}; - -static struct tuner_params tuner_lg_pal_new_tapc_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_lg_new_tapc_ranges, - .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges), - }, -}; - -/* ------------ TUNER_PHILIPS_FM1216ME_MK3 - Philips PAL ------------ */ - -static struct tuner_range tuner_fm1216me_mk3_pal_ranges[] = { - { 16 * 158.00 /*MHz*/, 0x8e, 0x01, }, - { 16 * 442.00 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x04, }, -}; - -static struct tuner_params tuner_fm1216me_mk3_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_fm1216me_mk3_pal_ranges, - .count = ARRAY_SIZE(tuner_fm1216me_mk3_pal_ranges), - .cb_first_if_lower_freq = 1, - .has_tda9887 = 1, - .port1_active = 1, - .port2_active = 1, - .port2_invert_for_secam_lc = 1, - .port1_fm_high_sensitivity = 1, - .default_top_mid = -2, - .default_top_secam_mid = -2, - .default_top_secam_high = -2, - }, -}; - -/* ------------ TUNER_PHILIPS_FM1216MK5 - Philips PAL ------------ */ - -static struct tuner_range tuner_fm1216mk5_pal_ranges[] = { - { 16 * 158.00 /*MHz*/, 0xce, 0x01, }, - { 16 * 441.00 /*MHz*/, 0xce, 0x02, }, - { 16 * 864.00 , 0xce, 0x04, }, -}; - -static struct tuner_params tuner_fm1216mk5_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_fm1216mk5_pal_ranges, - .count = ARRAY_SIZE(tuner_fm1216mk5_pal_ranges), - .cb_first_if_lower_freq = 1, - .has_tda9887 = 1, - .port1_active = 1, - .port2_active = 1, - .port2_invert_for_secam_lc = 1, - .port1_fm_high_sensitivity = 1, - .default_top_mid = -2, - .default_top_secam_mid = -2, - .default_top_secam_high = -2, - }, -}; - -/* ------------ TUNER_LG_NTSC_NEW_TAPC - LGINNOTEK NTSC ------------ */ - -static struct tuner_params tuner_lg_ntsc_new_tapc_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_lg_new_tapc_ranges, - .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges), - }, -}; - -/* 40-49 */ -/* ------------ TUNER_HITACHI_NTSC - HITACHI NTSC ------------ */ - -static struct tuner_params tuner_hitachi_ntsc_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_lg_new_tapc_ranges, - .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges), - }, -}; - -/* ------------ TUNER_PHILIPS_PAL_MK - Philips PAL ------------ */ - -static struct tuner_range tuner_philips_pal_mk_pal_ranges[] = { - { 16 * 140.25 /*MHz*/, 0x8e, 0x01, }, - { 16 * 463.25 /*MHz*/, 0x8e, 0xc2, }, - { 16 * 999.99 , 0x8e, 0xcf, }, -}; - -static struct tuner_params tuner_philips_pal_mk_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_philips_pal_mk_pal_ranges, - .count = ARRAY_SIZE(tuner_philips_pal_mk_pal_ranges), - }, -}; - -/* ---- TUNER_PHILIPS_FCV1236D - Philips FCV1236D (ATSC/NTSC) ---- */ - -static struct tuner_range tuner_philips_fcv1236d_ntsc_ranges[] = { - { 16 * 157.25 /*MHz*/, 0x8e, 0xa2, }, - { 16 * 451.25 /*MHz*/, 0x8e, 0x92, }, - { 16 * 999.99 , 0x8e, 0x32, }, -}; - -static struct tuner_range tuner_philips_fcv1236d_atsc_ranges[] = { - { 16 * 159.00 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 453.00 /*MHz*/, 0x8e, 0x90, }, - { 16 * 999.99 , 0x8e, 0x30, }, -}; - -static struct tuner_params tuner_philips_fcv1236d_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_philips_fcv1236d_ntsc_ranges, - .count = ARRAY_SIZE(tuner_philips_fcv1236d_ntsc_ranges), - }, - { - .type = TUNER_PARAM_TYPE_DIGITAL, - .ranges = tuner_philips_fcv1236d_atsc_ranges, - .count = ARRAY_SIZE(tuner_philips_fcv1236d_atsc_ranges), - .iffreq = 16 * 44.00, - }, -}; - -/* ------------ TUNER_PHILIPS_FM1236_MK3 - Philips NTSC ------------ */ - -static struct tuner_range tuner_fm1236_mk3_ntsc_ranges[] = { - { 16 * 160.00 /*MHz*/, 0x8e, 0x01, }, - { 16 * 442.00 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x04, }, -}; - -static struct tuner_params tuner_fm1236_mk3_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_fm1236_mk3_ntsc_ranges, - .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), - .cb_first_if_lower_freq = 1, - .has_tda9887 = 1, - .port1_active = 1, - .port2_active = 1, - .port1_fm_high_sensitivity = 1, - }, -}; - -/* ------------ TUNER_PHILIPS_4IN1 - Philips NTSC ------------ */ - -static struct tuner_params tuner_philips_4in1_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_fm1236_mk3_ntsc_ranges, - .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), - }, -}; - -/* ------------ TUNER_MICROTUNE_4049FM5 - Microtune PAL ------------ */ - -static struct tuner_params tuner_microtune_4049_fm5_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_temic_4009f_5_pal_ranges, - .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), - .has_tda9887 = 1, - .port1_invert_for_secam_lc = 1, - .default_pll_gating_18 = 1, - .fm_gain_normal=1, - .radio_if = 1, /* 33.3 MHz */ - }, -}; - -/* ------------ TUNER_PANASONIC_VP27 - Panasonic NTSC ------------ */ - -static struct tuner_range tuner_panasonic_vp27_ntsc_ranges[] = { - { 16 * 160.00 /*MHz*/, 0xce, 0x01, }, - { 16 * 454.00 /*MHz*/, 0xce, 0x02, }, - { 16 * 999.99 , 0xce, 0x08, }, -}; - -static struct tuner_params tuner_panasonic_vp27_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_panasonic_vp27_ntsc_ranges, - .count = ARRAY_SIZE(tuner_panasonic_vp27_ntsc_ranges), - .has_tda9887 = 1, - .intercarrier_mode = 1, - .default_top_low = -3, - .default_top_mid = -3, - .default_top_high = -3, - }, -}; - -/* ------------ TUNER_TNF_8831BGFF - Philips PAL ------------ */ - -static struct tuner_range tuner_tnf_8831bgff_pal_ranges[] = { - { 16 * 161.25 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 463.25 /*MHz*/, 0x8e, 0x90, }, - { 16 * 999.99 , 0x8e, 0x30, }, -}; - -static struct tuner_params tuner_tnf_8831bgff_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_tnf_8831bgff_pal_ranges, - .count = ARRAY_SIZE(tuner_tnf_8831bgff_pal_ranges), - }, -}; - -/* ------------ TUNER_MICROTUNE_4042FI5 - Microtune NTSC ------------ */ - -static struct tuner_range tuner_microtune_4042fi5_ntsc_ranges[] = { - { 16 * 162.00 /*MHz*/, 0x8e, 0xa2, }, - { 16 * 457.00 /*MHz*/, 0x8e, 0x94, }, - { 16 * 999.99 , 0x8e, 0x31, }, -}; - -static struct tuner_range tuner_microtune_4042fi5_atsc_ranges[] = { - { 16 * 162.00 /*MHz*/, 0x8e, 0xa1, }, - { 16 * 457.00 /*MHz*/, 0x8e, 0x91, }, - { 16 * 999.99 , 0x8e, 0x31, }, -}; - -static struct tuner_params tuner_microtune_4042fi5_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_microtune_4042fi5_ntsc_ranges, - .count = ARRAY_SIZE(tuner_microtune_4042fi5_ntsc_ranges), - }, - { - .type = TUNER_PARAM_TYPE_DIGITAL, - .ranges = tuner_microtune_4042fi5_atsc_ranges, - .count = ARRAY_SIZE(tuner_microtune_4042fi5_atsc_ranges), - .iffreq = 16 * 44.00 /*MHz*/, - }, -}; - -/* 50-59 */ -/* ------------ TUNER_TCL_2002N - TCL NTSC ------------ */ - -static struct tuner_range tuner_tcl_2002n_ntsc_ranges[] = { - { 16 * 172.00 /*MHz*/, 0x8e, 0x01, }, - { 16 * 448.00 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x08, }, -}; - -static struct tuner_params tuner_tcl_2002n_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_tcl_2002n_ntsc_ranges, - .count = ARRAY_SIZE(tuner_tcl_2002n_ntsc_ranges), - .cb_first_if_lower_freq = 1, - }, -}; - -/* ------------ TUNER_PHILIPS_FM1256_IH3 - Philips PAL ------------ */ - -static struct tuner_params tuner_philips_fm1256_ih3_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_fm1236_mk3_ntsc_ranges, - .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), - .radio_if = 1, /* 33.3 MHz */ - }, -}; - -/* ------------ TUNER_THOMSON_DTT7610 - THOMSON ATSC ------------ */ - -/* single range used for both ntsc and atsc */ -static struct tuner_range tuner_thomson_dtt7610_ntsc_ranges[] = { - { 16 * 157.25 /*MHz*/, 0x8e, 0x39, }, - { 16 * 454.00 /*MHz*/, 0x8e, 0x3a, }, - { 16 * 999.99 , 0x8e, 0x3c, }, -}; - -static struct tuner_params tuner_thomson_dtt7610_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_thomson_dtt7610_ntsc_ranges, - .count = ARRAY_SIZE(tuner_thomson_dtt7610_ntsc_ranges), - }, - { - .type = TUNER_PARAM_TYPE_DIGITAL, - .ranges = tuner_thomson_dtt7610_ntsc_ranges, - .count = ARRAY_SIZE(tuner_thomson_dtt7610_ntsc_ranges), - .iffreq = 16 * 44.00 /*MHz*/, - }, -}; - -/* ------------ TUNER_PHILIPS_FQ1286 - Philips NTSC ------------ */ - -static struct tuner_range tuner_philips_fq1286_ntsc_ranges[] = { - { 16 * 160.00 /*MHz*/, 0x8e, 0x41, }, - { 16 * 454.00 /*MHz*/, 0x8e, 0x42, }, - { 16 * 999.99 , 0x8e, 0x04, }, -}; - -static struct tuner_params tuner_philips_fq1286_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_philips_fq1286_ntsc_ranges, - .count = ARRAY_SIZE(tuner_philips_fq1286_ntsc_ranges), - }, -}; - -/* ------------ TUNER_TCL_2002MB - TCL PAL ------------ */ - -static struct tuner_range tuner_tcl_2002mb_pal_ranges[] = { - { 16 * 170.00 /*MHz*/, 0xce, 0x01, }, - { 16 * 450.00 /*MHz*/, 0xce, 0x02, }, - { 16 * 999.99 , 0xce, 0x08, }, -}; - -static struct tuner_params tuner_tcl_2002mb_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_tcl_2002mb_pal_ranges, - .count = ARRAY_SIZE(tuner_tcl_2002mb_pal_ranges), - }, -}; - -/* ------------ TUNER_PHILIPS_FQ1216AME_MK4 - Philips PAL ------------ */ - -static struct tuner_range tuner_philips_fq12_6a___mk4_pal_ranges[] = { - { 16 * 160.00 /*MHz*/, 0xce, 0x01, }, - { 16 * 442.00 /*MHz*/, 0xce, 0x02, }, - { 16 * 999.99 , 0xce, 0x04, }, -}; - -static struct tuner_params tuner_philips_fq1216ame_mk4_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_philips_fq12_6a___mk4_pal_ranges, - .count = ARRAY_SIZE(tuner_philips_fq12_6a___mk4_pal_ranges), - .has_tda9887 = 1, - .port1_active = 1, - .port2_invert_for_secam_lc = 1, - .default_top_mid = -2, - .default_top_secam_low = -2, - .default_top_secam_mid = -2, - .default_top_secam_high = -2, - }, -}; - -/* ------------ TUNER_PHILIPS_FQ1236A_MK4 - Philips NTSC ------------ */ - -static struct tuner_params tuner_philips_fq1236a_mk4_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_fm1236_mk3_ntsc_ranges, - .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), - }, -}; - -/* ------------ TUNER_YMEC_TVF_8531MF - Philips NTSC ------------ */ - -static struct tuner_params tuner_ymec_tvf_8531mf_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_philips_ntsc_m_ranges, - .count = ARRAY_SIZE(tuner_philips_ntsc_m_ranges), - }, -}; - -/* ------------ TUNER_YMEC_TVF_5533MF - Philips NTSC ------------ */ - -static struct tuner_range tuner_ymec_tvf_5533mf_ntsc_ranges[] = { - { 16 * 160.00 /*MHz*/, 0x8e, 0x01, }, - { 16 * 454.00 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x04, }, -}; - -static struct tuner_params tuner_ymec_tvf_5533mf_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_ymec_tvf_5533mf_ntsc_ranges, - .count = ARRAY_SIZE(tuner_ymec_tvf_5533mf_ntsc_ranges), - }, -}; - -/* 60-69 */ -/* ------------ TUNER_THOMSON_DTT761X - THOMSON ATSC ------------ */ -/* DTT 7611 7611A 7612 7613 7613A 7614 7615 7615A */ - -static struct tuner_range tuner_thomson_dtt761x_ntsc_ranges[] = { - { 16 * 145.25 /*MHz*/, 0x8e, 0x39, }, - { 16 * 415.25 /*MHz*/, 0x8e, 0x3a, }, - { 16 * 999.99 , 0x8e, 0x3c, }, -}; - -static struct tuner_range tuner_thomson_dtt761x_atsc_ranges[] = { - { 16 * 147.00 /*MHz*/, 0x8e, 0x39, }, - { 16 * 417.00 /*MHz*/, 0x8e, 0x3a, }, - { 16 * 999.99 , 0x8e, 0x3c, }, -}; - -static struct tuner_params tuner_thomson_dtt761x_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_thomson_dtt761x_ntsc_ranges, - .count = ARRAY_SIZE(tuner_thomson_dtt761x_ntsc_ranges), - .has_tda9887 = 1, - .fm_gain_normal = 1, - .radio_if = 2, /* 41.3 MHz */ - }, - { - .type = TUNER_PARAM_TYPE_DIGITAL, - .ranges = tuner_thomson_dtt761x_atsc_ranges, - .count = ARRAY_SIZE(tuner_thomson_dtt761x_atsc_ranges), - .iffreq = 16 * 44.00, /*MHz*/ - }, -}; - -/* ------------ TUNER_TENA_9533_DI - Philips PAL ------------ */ - -static struct tuner_range tuner_tena_9533_di_pal_ranges[] = { - { 16 * 160.25 /*MHz*/, 0x8e, 0x01, }, - { 16 * 464.25 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x04, }, -}; - -static struct tuner_params tuner_tena_9533_di_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_tena_9533_di_pal_ranges, - .count = ARRAY_SIZE(tuner_tena_9533_di_pal_ranges), - }, -}; - -/* ------------ TUNER_TENA_TNF_5337 - Tena tnf5337MFD STD M/N ------------ */ - -static struct tuner_range tuner_tena_tnf_5337_ntsc_ranges[] = { - { 16 * 166.25 /*MHz*/, 0x86, 0x01, }, - { 16 * 466.25 /*MHz*/, 0x86, 0x02, }, - { 16 * 999.99 , 0x86, 0x08, }, -}; - -static struct tuner_params tuner_tena_tnf_5337_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_tena_tnf_5337_ntsc_ranges, - .count = ARRAY_SIZE(tuner_tena_tnf_5337_ntsc_ranges), - }, -}; - -/* ------------ TUNER_PHILIPS_FMD1216ME(X)_MK3 - Philips PAL ------------ */ - -static struct tuner_range tuner_philips_fmd1216me_mk3_pal_ranges[] = { - { 16 * 160.00 /*MHz*/, 0x86, 0x51, }, - { 16 * 442.00 /*MHz*/, 0x86, 0x52, }, - { 16 * 999.99 , 0x86, 0x54, }, -}; - -static struct tuner_range tuner_philips_fmd1216me_mk3_dvb_ranges[] = { - { 16 * 143.87 /*MHz*/, 0xbc, 0x41 }, - { 16 * 158.87 /*MHz*/, 0xf4, 0x41 }, - { 16 * 329.87 /*MHz*/, 0xbc, 0x42 }, - { 16 * 441.87 /*MHz*/, 0xf4, 0x42 }, - { 16 * 625.87 /*MHz*/, 0xbc, 0x44 }, - { 16 * 803.87 /*MHz*/, 0xf4, 0x44 }, - { 16 * 999.99 , 0xfc, 0x44 }, -}; - -static struct tuner_params tuner_philips_fmd1216me_mk3_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_philips_fmd1216me_mk3_pal_ranges, - .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_pal_ranges), - .has_tda9887 = 1, - .port1_active = 1, - .port2_active = 1, - .port2_fm_high_sensitivity = 1, - .port2_invert_for_secam_lc = 1, - .port1_set_for_fm_mono = 1, - }, - { - .type = TUNER_PARAM_TYPE_DIGITAL, - .ranges = tuner_philips_fmd1216me_mk3_dvb_ranges, - .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_dvb_ranges), - .iffreq = 16 * 36.125, /*MHz*/ - }, -}; - -static struct tuner_params tuner_philips_fmd1216mex_mk3_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_philips_fmd1216me_mk3_pal_ranges, - .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_pal_ranges), - .has_tda9887 = 1, - .port1_active = 1, - .port2_active = 1, - .port2_fm_high_sensitivity = 1, - .port2_invert_for_secam_lc = 1, - .port1_set_for_fm_mono = 1, - .radio_if = 1, - .fm_gain_normal = 1, - }, - { - .type = TUNER_PARAM_TYPE_DIGITAL, - .ranges = tuner_philips_fmd1216me_mk3_dvb_ranges, - .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_dvb_ranges), - .iffreq = 16 * 36.125, /*MHz*/ - }, -}; - -/* ------ TUNER_LG_TDVS_H06XF - LG INNOTEK / INFINEON ATSC ----- */ - -static struct tuner_range tuner_tua6034_ntsc_ranges[] = { - { 16 * 165.00 /*MHz*/, 0x8e, 0x01 }, - { 16 * 450.00 /*MHz*/, 0x8e, 0x02 }, - { 16 * 999.99 , 0x8e, 0x04 }, -}; - -static struct tuner_range tuner_tua6034_atsc_ranges[] = { - { 16 * 165.00 /*MHz*/, 0xce, 0x01 }, - { 16 * 450.00 /*MHz*/, 0xce, 0x02 }, - { 16 * 999.99 , 0xce, 0x04 }, -}; - -static struct tuner_params tuner_lg_tdvs_h06xf_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_tua6034_ntsc_ranges, - .count = ARRAY_SIZE(tuner_tua6034_ntsc_ranges), - }, - { - .type = TUNER_PARAM_TYPE_DIGITAL, - .ranges = tuner_tua6034_atsc_ranges, - .count = ARRAY_SIZE(tuner_tua6034_atsc_ranges), - .iffreq = 16 * 44.00, - }, -}; - -/* ------------ TUNER_YMEC_TVF66T5_B_DFF - Philips PAL ------------ */ - -static struct tuner_range tuner_ymec_tvf66t5_b_dff_pal_ranges[] = { - { 16 * 160.25 /*MHz*/, 0x8e, 0x01, }, - { 16 * 464.25 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x08, }, -}; - -static struct tuner_params tuner_ymec_tvf66t5_b_dff_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_ymec_tvf66t5_b_dff_pal_ranges, - .count = ARRAY_SIZE(tuner_ymec_tvf66t5_b_dff_pal_ranges), - }, -}; - -/* ------------ TUNER_LG_NTSC_TALN_MINI - LGINNOTEK NTSC ------------ */ - -static struct tuner_range tuner_lg_taln_ntsc_ranges[] = { - { 16 * 137.25 /*MHz*/, 0x8e, 0x01, }, - { 16 * 373.25 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x08, }, -}; - -static struct tuner_range tuner_lg_taln_pal_secam_ranges[] = { - { 16 * 150.00 /*MHz*/, 0x8e, 0x01, }, - { 16 * 425.00 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x08, }, -}; - -static struct tuner_params tuner_lg_taln_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_lg_taln_ntsc_ranges, - .count = ARRAY_SIZE(tuner_lg_taln_ntsc_ranges), - },{ - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_lg_taln_pal_secam_ranges, - .count = ARRAY_SIZE(tuner_lg_taln_pal_secam_ranges), - }, -}; - -/* ------------ TUNER_PHILIPS_TD1316 - Philips PAL ------------ */ - -static struct tuner_range tuner_philips_td1316_pal_ranges[] = { - { 16 * 160.00 /*MHz*/, 0xc8, 0xa1, }, - { 16 * 442.00 /*MHz*/, 0xc8, 0xa2, }, - { 16 * 999.99 , 0xc8, 0xa4, }, -}; - -static struct tuner_range tuner_philips_td1316_dvb_ranges[] = { - { 16 * 93.834 /*MHz*/, 0xca, 0x60, }, - { 16 * 123.834 /*MHz*/, 0xca, 0xa0, }, - { 16 * 163.834 /*MHz*/, 0xca, 0xc0, }, - { 16 * 253.834 /*MHz*/, 0xca, 0x60, }, - { 16 * 383.834 /*MHz*/, 0xca, 0xa0, }, - { 16 * 443.834 /*MHz*/, 0xca, 0xc0, }, - { 16 * 583.834 /*MHz*/, 0xca, 0x60, }, - { 16 * 793.834 /*MHz*/, 0xca, 0xa0, }, - { 16 * 999.999 , 0xca, 0xe0, }, -}; - -static struct tuner_params tuner_philips_td1316_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_philips_td1316_pal_ranges, - .count = ARRAY_SIZE(tuner_philips_td1316_pal_ranges), - }, - { - .type = TUNER_PARAM_TYPE_DIGITAL, - .ranges = tuner_philips_td1316_dvb_ranges, - .count = ARRAY_SIZE(tuner_philips_td1316_dvb_ranges), - .iffreq = 16 * 36.166667 /*MHz*/, - }, -}; - -/* ------------ TUNER_PHILIPS_TUV1236D - Philips ATSC ------------ */ - -static struct tuner_range tuner_tuv1236d_ntsc_ranges[] = { - { 16 * 157.25 /*MHz*/, 0xce, 0x01, }, - { 16 * 454.00 /*MHz*/, 0xce, 0x02, }, - { 16 * 999.99 , 0xce, 0x04, }, -}; - -static struct tuner_range tuner_tuv1236d_atsc_ranges[] = { - { 16 * 157.25 /*MHz*/, 0xc6, 0x41, }, - { 16 * 454.00 /*MHz*/, 0xc6, 0x42, }, - { 16 * 999.99 , 0xc6, 0x44, }, -}; - -static struct tuner_params tuner_tuv1236d_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_tuv1236d_ntsc_ranges, - .count = ARRAY_SIZE(tuner_tuv1236d_ntsc_ranges), - }, - { - .type = TUNER_PARAM_TYPE_DIGITAL, - .ranges = tuner_tuv1236d_atsc_ranges, - .count = ARRAY_SIZE(tuner_tuv1236d_atsc_ranges), - .iffreq = 16 * 44.00, - }, -}; - -/* ------------ TUNER_TNF_xxx5 - Texas Instruments--------- */ -/* This is known to work with Tenna TVF58t5-MFF and TVF5835 MFF - * but it is expected to work also with other Tenna/Ymec - * models based on TI SN 761677 chip on both PAL and NTSC - */ - -static struct tuner_range tuner_tnf_5335_d_if_pal_ranges[] = { - { 16 * 168.25 /*MHz*/, 0x8e, 0x01, }, - { 16 * 471.25 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x08, }, -}; - -static struct tuner_range tuner_tnf_5335mf_ntsc_ranges[] = { - { 16 * 169.25 /*MHz*/, 0x8e, 0x01, }, - { 16 * 469.25 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x08, }, -}; - -static struct tuner_params tuner_tnf_5335mf_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_tnf_5335mf_ntsc_ranges, - .count = ARRAY_SIZE(tuner_tnf_5335mf_ntsc_ranges), - }, - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_tnf_5335_d_if_pal_ranges, - .count = ARRAY_SIZE(tuner_tnf_5335_d_if_pal_ranges), - }, -}; - -/* 70-79 */ -/* ------------ TUNER_SAMSUNG_TCPN_2121P30A - Samsung NTSC ------------ */ - -/* '+ 4' turns on the Low Noise Amplifier */ -static struct tuner_range tuner_samsung_tcpn_2121p30a_ntsc_ranges[] = { - { 16 * 130.00 /*MHz*/, 0xce, 0x01 + 4, }, - { 16 * 364.50 /*MHz*/, 0xce, 0x02 + 4, }, - { 16 * 999.99 , 0xce, 0x08 + 4, }, -}; - -static struct tuner_params tuner_samsung_tcpn_2121p30a_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_samsung_tcpn_2121p30a_ntsc_ranges, - .count = ARRAY_SIZE(tuner_samsung_tcpn_2121p30a_ntsc_ranges), - }, -}; - -/* ------------ TUNER_THOMSON_FE6600 - DViCO Hybrid PAL ------------ */ - -static struct tuner_range tuner_thomson_fe6600_pal_ranges[] = { - { 16 * 160.00 /*MHz*/, 0xfe, 0x11, }, - { 16 * 442.00 /*MHz*/, 0xf6, 0x12, }, - { 16 * 999.99 , 0xf6, 0x18, }, -}; - -static struct tuner_range tuner_thomson_fe6600_dvb_ranges[] = { - { 16 * 250.00 /*MHz*/, 0xb4, 0x12, }, - { 16 * 455.00 /*MHz*/, 0xfe, 0x11, }, - { 16 * 775.50 /*MHz*/, 0xbc, 0x18, }, - { 16 * 999.99 , 0xf4, 0x18, }, -}; - -static struct tuner_params tuner_thomson_fe6600_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_thomson_fe6600_pal_ranges, - .count = ARRAY_SIZE(tuner_thomson_fe6600_pal_ranges), - }, - { - .type = TUNER_PARAM_TYPE_DIGITAL, - .ranges = tuner_thomson_fe6600_dvb_ranges, - .count = ARRAY_SIZE(tuner_thomson_fe6600_dvb_ranges), - .iffreq = 16 * 36.125 /*MHz*/, - }, -}; - -/* ------------ TUNER_SAMSUNG_TCPG_6121P30A - Samsung PAL ------------ */ - -/* '+ 4' turns on the Low Noise Amplifier */ -static struct tuner_range tuner_samsung_tcpg_6121p30a_pal_ranges[] = { - { 16 * 146.25 /*MHz*/, 0xce, 0x01 + 4, }, - { 16 * 428.50 /*MHz*/, 0xce, 0x02 + 4, }, - { 16 * 999.99 , 0xce, 0x08 + 4, }, -}; - -static struct tuner_params tuner_samsung_tcpg_6121p30a_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_samsung_tcpg_6121p30a_pal_ranges, - .count = ARRAY_SIZE(tuner_samsung_tcpg_6121p30a_pal_ranges), - .has_tda9887 = 1, - .port1_active = 1, - .port2_active = 1, - .port2_invert_for_secam_lc = 1, - }, -}; - -/* ------------ TUNER_TCL_MF02GIP-5N-E - TCL MF02GIP-5N ------------ */ - -static struct tuner_range tuner_tcl_mf02gip_5n_ntsc_ranges[] = { - { 16 * 172.00 /*MHz*/, 0x8e, 0x01, }, - { 16 * 448.00 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x04, }, -}; - -static struct tuner_params tuner_tcl_mf02gip_5n_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_tcl_mf02gip_5n_ntsc_ranges, - .count = ARRAY_SIZE(tuner_tcl_mf02gip_5n_ntsc_ranges), - .cb_first_if_lower_freq = 1, - }, -}; - -/* 80-89 */ -/* --------- TUNER_PHILIPS_FQ1216LME_MK3 -- active loopthrough, no FM ------- */ - -static struct tuner_params tuner_fq1216lme_mk3_params[] = { - { - .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_fm1216me_mk3_pal_ranges, - .count = ARRAY_SIZE(tuner_fm1216me_mk3_pal_ranges), - .cb_first_if_lower_freq = 1, /* not specified, but safe to do */ - .has_tda9887 = 1, /* TDA9886 */ - .port1_active = 1, - .port2_active = 1, - .port2_invert_for_secam_lc = 1, - .default_top_low = 4, - .default_top_mid = 4, - .default_top_high = 4, - .default_top_secam_low = 4, - .default_top_secam_mid = 4, - .default_top_secam_high = 4, - }, -}; - -/* ----- TUNER_PARTSNIC_PTI_5NF05 - Partsnic (Daewoo) PTI-5NF05 NTSC ----- */ - -static struct tuner_range tuner_partsnic_pti_5nf05_ranges[] = { - /* The datasheet specified channel ranges and the bandswitch byte */ - /* The control byte value of 0x8e is just a guess */ - { 16 * 133.25 /*MHz*/, 0x8e, 0x01, }, /* Channels 2 - B */ - { 16 * 367.25 /*MHz*/, 0x8e, 0x02, }, /* Channels C - W+11 */ - { 16 * 999.99 , 0x8e, 0x08, }, /* Channels W+12 - 69 */ -}; - -static struct tuner_params tuner_partsnic_pti_5nf05_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_partsnic_pti_5nf05_ranges, - .count = ARRAY_SIZE(tuner_partsnic_pti_5nf05_ranges), - .cb_first_if_lower_freq = 1, /* not specified but safe to do */ - }, -}; - -/* --------- TUNER_PHILIPS_CU1216L - DVB-C NIM ------------------------- */ - -static struct tuner_range tuner_cu1216l_ranges[] = { - { 16 * 160.25 /*MHz*/, 0xce, 0x01 }, - { 16 * 444.25 /*MHz*/, 0xce, 0x02 }, - { 16 * 999.99 , 0xce, 0x04 }, -}; - -static struct tuner_params tuner_philips_cu1216l_params[] = { - { - .type = TUNER_PARAM_TYPE_DIGITAL, - .ranges = tuner_cu1216l_ranges, - .count = ARRAY_SIZE(tuner_cu1216l_ranges), - .iffreq = 16 * 36.125, /*MHz*/ - }, -}; - -/* ---------------------- TUNER_SONY_BTF_PXN01Z ------------------------ */ - -static struct tuner_range tuner_sony_btf_pxn01z_ranges[] = { - { 16 * 137.25 /*MHz*/, 0x8e, 0x01, }, - { 16 * 367.25 /*MHz*/, 0x8e, 0x02, }, - { 16 * 999.99 , 0x8e, 0x04, }, -}; - -static struct tuner_params tuner_sony_btf_pxn01z_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_sony_btf_pxn01z_ranges, - .count = ARRAY_SIZE(tuner_sony_btf_pxn01z_ranges), - }, -}; - -/* ------------ TUNER_PHILIPS_FQ1236_MK5 - Philips NTSC ------------ */ - -static struct tuner_params tuner_philips_fq1236_mk5_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_fm1236_mk3_ntsc_ranges, - .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), - .has_tda9887 = 1, /* TDA9885, no FM radio */ - }, -}; - -/* --------------------------------------------------------------------- */ - -struct tunertype tuners[] = { - /* 0-9 */ - [TUNER_TEMIC_PAL] = { /* TEMIC PAL */ - .name = "Temic PAL (4002 FH5)", - .params = tuner_temic_pal_params, - .count = ARRAY_SIZE(tuner_temic_pal_params), - }, - [TUNER_PHILIPS_PAL_I] = { /* Philips PAL_I */ - .name = "Philips PAL_I (FI1246 and compatibles)", - .params = tuner_philips_pal_i_params, - .count = ARRAY_SIZE(tuner_philips_pal_i_params), - }, - [TUNER_PHILIPS_NTSC] = { /* Philips NTSC */ - .name = "Philips NTSC (FI1236,FM1236 and compatibles)", - .params = tuner_philips_ntsc_params, - .count = ARRAY_SIZE(tuner_philips_ntsc_params), - }, - [TUNER_PHILIPS_SECAM] = { /* Philips SECAM */ - .name = "Philips (SECAM+PAL_BG) (FI1216MF, FM1216MF, FR1216MF)", - .params = tuner_philips_secam_params, - .count = ARRAY_SIZE(tuner_philips_secam_params), - }, - [TUNER_ABSENT] = { /* Tuner Absent */ - .name = "NoTuner", - }, - [TUNER_PHILIPS_PAL] = { /* Philips PAL */ - .name = "Philips PAL_BG (FI1216 and compatibles)", - .params = tuner_philips_pal_params, - .count = ARRAY_SIZE(tuner_philips_pal_params), - }, - [TUNER_TEMIC_NTSC] = { /* TEMIC NTSC */ - .name = "Temic NTSC (4032 FY5)", - .params = tuner_temic_ntsc_params, - .count = ARRAY_SIZE(tuner_temic_ntsc_params), - }, - [TUNER_TEMIC_PAL_I] = { /* TEMIC PAL_I */ - .name = "Temic PAL_I (4062 FY5)", - .params = tuner_temic_pal_i_params, - .count = ARRAY_SIZE(tuner_temic_pal_i_params), - }, - [TUNER_TEMIC_4036FY5_NTSC] = { /* TEMIC NTSC */ - .name = "Temic NTSC (4036 FY5)", - .params = tuner_temic_4036fy5_ntsc_params, - .count = ARRAY_SIZE(tuner_temic_4036fy5_ntsc_params), - }, - [TUNER_ALPS_TSBH1_NTSC] = { /* TEMIC NTSC */ - .name = "Alps HSBH1", - .params = tuner_alps_tsbh1_ntsc_params, - .count = ARRAY_SIZE(tuner_alps_tsbh1_ntsc_params), - }, - - /* 10-19 */ - [TUNER_ALPS_TSBE1_PAL] = { /* TEMIC PAL */ - .name = "Alps TSBE1", - .params = tuner_alps_tsb_1_params, - .count = ARRAY_SIZE(tuner_alps_tsb_1_params), - }, - [TUNER_ALPS_TSBB5_PAL_I] = { /* Alps PAL_I */ - .name = "Alps TSBB5", - .params = tuner_alps_tsbb5_params, - .count = ARRAY_SIZE(tuner_alps_tsbb5_params), - }, - [TUNER_ALPS_TSBE5_PAL] = { /* Alps PAL */ - .name = "Alps TSBE5", - .params = tuner_alps_tsbe5_params, - .count = ARRAY_SIZE(tuner_alps_tsbe5_params), - }, - [TUNER_ALPS_TSBC5_PAL] = { /* Alps PAL */ - .name = "Alps TSBC5", - .params = tuner_alps_tsbc5_params, - .count = ARRAY_SIZE(tuner_alps_tsbc5_params), - }, - [TUNER_TEMIC_4006FH5_PAL] = { /* TEMIC PAL */ - .name = "Temic PAL_BG (4006FH5)", - .params = tuner_temic_4006fh5_params, - .count = ARRAY_SIZE(tuner_temic_4006fh5_params), - }, - [TUNER_ALPS_TSHC6_NTSC] = { /* Alps NTSC */ - .name = "Alps TSCH6", - .params = tuner_alps_tshc6_params, - .count = ARRAY_SIZE(tuner_alps_tshc6_params), - }, - [TUNER_TEMIC_PAL_DK] = { /* TEMIC PAL */ - .name = "Temic PAL_DK (4016 FY5)", - .params = tuner_temic_pal_dk_params, - .count = ARRAY_SIZE(tuner_temic_pal_dk_params), - }, - [TUNER_PHILIPS_NTSC_M] = { /* Philips NTSC */ - .name = "Philips NTSC_M (MK2)", - .params = tuner_philips_ntsc_m_params, - .count = ARRAY_SIZE(tuner_philips_ntsc_m_params), - }, - [TUNER_TEMIC_4066FY5_PAL_I] = { /* TEMIC PAL_I */ - .name = "Temic PAL_I (4066 FY5)", - .params = tuner_temic_4066fy5_pal_i_params, - .count = ARRAY_SIZE(tuner_temic_4066fy5_pal_i_params), - }, - [TUNER_TEMIC_4006FN5_MULTI_PAL] = { /* TEMIC PAL */ - .name = "Temic PAL* auto (4006 FN5)", - .params = tuner_temic_4006fn5_multi_params, - .count = ARRAY_SIZE(tuner_temic_4006fn5_multi_params), - }, - - /* 20-29 */ - [TUNER_TEMIC_4009FR5_PAL] = { /* TEMIC PAL */ - .name = "Temic PAL_BG (4009 FR5) or PAL_I (4069 FR5)", - .params = tuner_temic_4009f_5_params, - .count = ARRAY_SIZE(tuner_temic_4009f_5_params), - }, - [TUNER_TEMIC_4039FR5_NTSC] = { /* TEMIC NTSC */ - .name = "Temic NTSC (4039 FR5)", - .params = tuner_temic_4039fr5_params, - .count = ARRAY_SIZE(tuner_temic_4039fr5_params), - }, - [TUNER_TEMIC_4046FM5] = { /* TEMIC PAL */ - .name = "Temic PAL/SECAM multi (4046 FM5)", - .params = tuner_temic_4046fm5_params, - .count = ARRAY_SIZE(tuner_temic_4046fm5_params), - }, - [TUNER_PHILIPS_PAL_DK] = { /* Philips PAL */ - .name = "Philips PAL_DK (FI1256 and compatibles)", - .params = tuner_philips_pal_dk_params, - .count = ARRAY_SIZE(tuner_philips_pal_dk_params), - }, - [TUNER_PHILIPS_FQ1216ME] = { /* Philips PAL */ - .name = "Philips PAL/SECAM multi (FQ1216ME)", - .params = tuner_philips_fq1216me_params, - .count = ARRAY_SIZE(tuner_philips_fq1216me_params), - }, - [TUNER_LG_PAL_I_FM] = { /* LGINNOTEK PAL_I */ - .name = "LG PAL_I+FM (TAPC-I001D)", - .params = tuner_lg_pal_i_fm_params, - .count = ARRAY_SIZE(tuner_lg_pal_i_fm_params), - }, - [TUNER_LG_PAL_I] = { /* LGINNOTEK PAL_I */ - .name = "LG PAL_I (TAPC-I701D)", - .params = tuner_lg_pal_i_params, - .count = ARRAY_SIZE(tuner_lg_pal_i_params), - }, - [TUNER_LG_NTSC_FM] = { /* LGINNOTEK NTSC */ - .name = "LG NTSC+FM (TPI8NSR01F)", - .params = tuner_lg_ntsc_fm_params, - .count = ARRAY_SIZE(tuner_lg_ntsc_fm_params), - }, - [TUNER_LG_PAL_FM] = { /* LGINNOTEK PAL */ - .name = "LG PAL_BG+FM (TPI8PSB01D)", - .params = tuner_lg_pal_fm_params, - .count = ARRAY_SIZE(tuner_lg_pal_fm_params), - }, - [TUNER_LG_PAL] = { /* LGINNOTEK PAL */ - .name = "LG PAL_BG (TPI8PSB11D)", - .params = tuner_lg_pal_params, - .count = ARRAY_SIZE(tuner_lg_pal_params), - }, - - /* 30-39 */ - [TUNER_TEMIC_4009FN5_MULTI_PAL_FM] = { /* TEMIC PAL */ - .name = "Temic PAL* auto + FM (4009 FN5)", - .params = tuner_temic_4009_fn5_multi_pal_fm_params, - .count = ARRAY_SIZE(tuner_temic_4009_fn5_multi_pal_fm_params), - }, - [TUNER_SHARP_2U5JF5540_NTSC] = { /* SHARP NTSC */ - .name = "SHARP NTSC_JP (2U5JF5540)", - .params = tuner_sharp_2u5jf5540_params, - .count = ARRAY_SIZE(tuner_sharp_2u5jf5540_params), - }, - [TUNER_Samsung_PAL_TCPM9091PD27] = { /* Samsung PAL */ - .name = "Samsung PAL TCPM9091PD27", - .params = tuner_samsung_pal_tcpm9091pd27_params, - .count = ARRAY_SIZE(tuner_samsung_pal_tcpm9091pd27_params), - }, - [TUNER_MT2032] = { /* Microtune PAL|NTSC */ - .name = "MT20xx universal", - /* see mt20xx.c for details */ }, - [TUNER_TEMIC_4106FH5] = { /* TEMIC PAL */ - .name = "Temic PAL_BG (4106 FH5)", - .params = tuner_temic_4106fh5_params, - .count = ARRAY_SIZE(tuner_temic_4106fh5_params), - }, - [TUNER_TEMIC_4012FY5] = { /* TEMIC PAL */ - .name = "Temic PAL_DK/SECAM_L (4012 FY5)", - .params = tuner_temic_4012fy5_params, - .count = ARRAY_SIZE(tuner_temic_4012fy5_params), - }, - [TUNER_TEMIC_4136FY5] = { /* TEMIC NTSC */ - .name = "Temic NTSC (4136 FY5)", - .params = tuner_temic_4136_fy5_params, - .count = ARRAY_SIZE(tuner_temic_4136_fy5_params), - }, - [TUNER_LG_PAL_NEW_TAPC] = { /* LGINNOTEK PAL */ - .name = "LG PAL (newer TAPC series)", - .params = tuner_lg_pal_new_tapc_params, - .count = ARRAY_SIZE(tuner_lg_pal_new_tapc_params), - }, - [TUNER_PHILIPS_FM1216ME_MK3] = { /* Philips PAL */ - .name = "Philips PAL/SECAM multi (FM1216ME MK3)", - .params = tuner_fm1216me_mk3_params, - .count = ARRAY_SIZE(tuner_fm1216me_mk3_params), - }, - [TUNER_LG_NTSC_NEW_TAPC] = { /* LGINNOTEK NTSC */ - .name = "LG NTSC (newer TAPC series)", - .params = tuner_lg_ntsc_new_tapc_params, - .count = ARRAY_SIZE(tuner_lg_ntsc_new_tapc_params), - }, - - /* 40-49 */ - [TUNER_HITACHI_NTSC] = { /* HITACHI NTSC */ - .name = "HITACHI V7-J180AT", - .params = tuner_hitachi_ntsc_params, - .count = ARRAY_SIZE(tuner_hitachi_ntsc_params), - }, - [TUNER_PHILIPS_PAL_MK] = { /* Philips PAL */ - .name = "Philips PAL_MK (FI1216 MK)", - .params = tuner_philips_pal_mk_params, - .count = ARRAY_SIZE(tuner_philips_pal_mk_params), - }, - [TUNER_PHILIPS_FCV1236D] = { /* Philips ATSC */ - .name = "Philips FCV1236D ATSC/NTSC dual in", - .params = tuner_philips_fcv1236d_params, - .count = ARRAY_SIZE(tuner_philips_fcv1236d_params), - .min = 16 * 53.00, - .max = 16 * 803.00, - .stepsize = 62500, - }, - [TUNER_PHILIPS_FM1236_MK3] = { /* Philips NTSC */ - .name = "Philips NTSC MK3 (FM1236MK3 or FM1236/F)", - .params = tuner_fm1236_mk3_params, - .count = ARRAY_SIZE(tuner_fm1236_mk3_params), - }, - [TUNER_PHILIPS_4IN1] = { /* Philips NTSC */ - .name = "Philips 4 in 1 (ATI TV Wonder Pro/Conexant)", - .params = tuner_philips_4in1_params, - .count = ARRAY_SIZE(tuner_philips_4in1_params), - }, - [TUNER_MICROTUNE_4049FM5] = { /* Microtune PAL */ - .name = "Microtune 4049 FM5", - .params = tuner_microtune_4049_fm5_params, - .count = ARRAY_SIZE(tuner_microtune_4049_fm5_params), - }, - [TUNER_PANASONIC_VP27] = { /* Panasonic NTSC */ - .name = "Panasonic VP27s/ENGE4324D", - .params = tuner_panasonic_vp27_params, - .count = ARRAY_SIZE(tuner_panasonic_vp27_params), - }, - [TUNER_LG_NTSC_TAPE] = { /* LGINNOTEK NTSC */ - .name = "LG NTSC (TAPE series)", - .params = tuner_fm1236_mk3_params, - .count = ARRAY_SIZE(tuner_fm1236_mk3_params), - }, - [TUNER_TNF_8831BGFF] = { /* Philips PAL */ - .name = "Tenna TNF 8831 BGFF)", - .params = tuner_tnf_8831bgff_params, - .count = ARRAY_SIZE(tuner_tnf_8831bgff_params), - }, - [TUNER_MICROTUNE_4042FI5] = { /* Microtune NTSC */ - .name = "Microtune 4042 FI5 ATSC/NTSC dual in", - .params = tuner_microtune_4042fi5_params, - .count = ARRAY_SIZE(tuner_microtune_4042fi5_params), - .min = 16 * 57.00, - .max = 16 * 858.00, - .stepsize = 62500, - }, - - /* 50-59 */ - [TUNER_TCL_2002N] = { /* TCL NTSC */ - .name = "TCL 2002N", - .params = tuner_tcl_2002n_params, - .count = ARRAY_SIZE(tuner_tcl_2002n_params), - }, - [TUNER_PHILIPS_FM1256_IH3] = { /* Philips PAL */ - .name = "Philips PAL/SECAM_D (FM 1256 I-H3)", - .params = tuner_philips_fm1256_ih3_params, - .count = ARRAY_SIZE(tuner_philips_fm1256_ih3_params), - }, - [TUNER_THOMSON_DTT7610] = { /* THOMSON ATSC */ - .name = "Thomson DTT 7610 (ATSC/NTSC)", - .params = tuner_thomson_dtt7610_params, - .count = ARRAY_SIZE(tuner_thomson_dtt7610_params), - .min = 16 * 44.00, - .max = 16 * 958.00, - .stepsize = 62500, - }, - [TUNER_PHILIPS_FQ1286] = { /* Philips NTSC */ - .name = "Philips FQ1286", - .params = tuner_philips_fq1286_params, - .count = ARRAY_SIZE(tuner_philips_fq1286_params), - }, - [TUNER_PHILIPS_TDA8290] = { /* Philips PAL|NTSC */ - .name = "Philips/NXP TDA 8290/8295 + 8275/8275A/18271", - /* see tda8290.c for details */ }, - [TUNER_TCL_2002MB] = { /* TCL PAL */ - .name = "TCL 2002MB", - .params = tuner_tcl_2002mb_params, - .count = ARRAY_SIZE(tuner_tcl_2002mb_params), - }, - [TUNER_PHILIPS_FQ1216AME_MK4] = { /* Philips PAL */ - .name = "Philips PAL/SECAM multi (FQ1216AME MK4)", - .params = tuner_philips_fq1216ame_mk4_params, - .count = ARRAY_SIZE(tuner_philips_fq1216ame_mk4_params), - }, - [TUNER_PHILIPS_FQ1236A_MK4] = { /* Philips NTSC */ - .name = "Philips FQ1236A MK4", - .params = tuner_philips_fq1236a_mk4_params, - .count = ARRAY_SIZE(tuner_philips_fq1236a_mk4_params), - }, - [TUNER_YMEC_TVF_8531MF] = { /* Philips NTSC */ - .name = "Ymec TVision TVF-8531MF/8831MF/8731MF", - .params = tuner_ymec_tvf_8531mf_params, - .count = ARRAY_SIZE(tuner_ymec_tvf_8531mf_params), - }, - [TUNER_YMEC_TVF_5533MF] = { /* Philips NTSC */ - .name = "Ymec TVision TVF-5533MF", - .params = tuner_ymec_tvf_5533mf_params, - .count = ARRAY_SIZE(tuner_ymec_tvf_5533mf_params), - }, - - /* 60-69 */ - [TUNER_THOMSON_DTT761X] = { /* THOMSON ATSC */ - /* DTT 7611 7611A 7612 7613 7613A 7614 7615 7615A */ - .name = "Thomson DTT 761X (ATSC/NTSC)", - .params = tuner_thomson_dtt761x_params, - .count = ARRAY_SIZE(tuner_thomson_dtt761x_params), - .min = 16 * 57.00, - .max = 16 * 863.00, - .stepsize = 62500, - .initdata = tua603x_agc103, - }, - [TUNER_TENA_9533_DI] = { /* Philips PAL */ - .name = "Tena TNF9533-D/IF/TNF9533-B/DF", - .params = tuner_tena_9533_di_params, - .count = ARRAY_SIZE(tuner_tena_9533_di_params), - }, - [TUNER_TEA5767] = { /* Philips RADIO */ - .name = "Philips TEA5767HN FM Radio", - /* see tea5767.c for details */ - }, - [TUNER_PHILIPS_FMD1216ME_MK3] = { /* Philips PAL */ - .name = "Philips FMD1216ME MK3 Hybrid Tuner", - .params = tuner_philips_fmd1216me_mk3_params, - .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_params), - .min = 16 * 50.87, - .max = 16 * 858.00, - .stepsize = 166667, - .initdata = tua603x_agc112, - .sleepdata = (u8[]){ 4, 0x9c, 0x60, 0x85, 0x54 }, - }, - [TUNER_LG_TDVS_H06XF] = { /* LGINNOTEK ATSC */ - .name = "LG TDVS-H06xF", /* H061F, H062F & H064F */ - .params = tuner_lg_tdvs_h06xf_params, - .count = ARRAY_SIZE(tuner_lg_tdvs_h06xf_params), - .min = 16 * 54.00, - .max = 16 * 863.00, - .stepsize = 62500, - .initdata = tua603x_agc103, - }, - [TUNER_YMEC_TVF66T5_B_DFF] = { /* Philips PAL */ - .name = "Ymec TVF66T5-B/DFF", - .params = tuner_ymec_tvf66t5_b_dff_params, - .count = ARRAY_SIZE(tuner_ymec_tvf66t5_b_dff_params), - }, - [TUNER_LG_TALN] = { /* LGINNOTEK NTSC / PAL / SECAM */ - .name = "LG TALN series", - .params = tuner_lg_taln_params, - .count = ARRAY_SIZE(tuner_lg_taln_params), - }, - [TUNER_PHILIPS_TD1316] = { /* Philips PAL */ - .name = "Philips TD1316 Hybrid Tuner", - .params = tuner_philips_td1316_params, - .count = ARRAY_SIZE(tuner_philips_td1316_params), - .min = 16 * 87.00, - .max = 16 * 895.00, - .stepsize = 166667, - }, - [TUNER_PHILIPS_TUV1236D] = { /* Philips ATSC */ - .name = "Philips TUV1236D ATSC/NTSC dual in", - .params = tuner_tuv1236d_params, - .count = ARRAY_SIZE(tuner_tuv1236d_params), - .min = 16 * 54.00, - .max = 16 * 864.00, - .stepsize = 62500, - }, - [TUNER_TNF_5335MF] = { /* Tenna PAL/NTSC */ - .name = "Tena TNF 5335 and similar models", - .params = tuner_tnf_5335mf_params, - .count = ARRAY_SIZE(tuner_tnf_5335mf_params), - }, - - /* 70-79 */ - [TUNER_SAMSUNG_TCPN_2121P30A] = { /* Samsung NTSC */ - .name = "Samsung TCPN 2121P30A", - .params = tuner_samsung_tcpn_2121p30a_params, - .count = ARRAY_SIZE(tuner_samsung_tcpn_2121p30a_params), - }, - [TUNER_XC2028] = { /* Xceive 2028 */ - .name = "Xceive xc2028/xc3028 tuner", - /* see tuner-xc2028.c for details */ - }, - [TUNER_THOMSON_FE6600] = { /* Thomson PAL / DVB-T */ - .name = "Thomson FE6600", - .params = tuner_thomson_fe6600_params, - .count = ARRAY_SIZE(tuner_thomson_fe6600_params), - .min = 16 * 44.25, - .max = 16 * 858.00, - .stepsize = 166667, - }, - [TUNER_SAMSUNG_TCPG_6121P30A] = { /* Samsung PAL */ - .name = "Samsung TCPG 6121P30A", - .params = tuner_samsung_tcpg_6121p30a_params, - .count = ARRAY_SIZE(tuner_samsung_tcpg_6121p30a_params), - }, - [TUNER_TDA9887] = { /* Philips TDA 9887 IF PLL Demodulator. - This chip is part of some modern tuners */ - .name = "Philips TDA988[5,6,7] IF PLL Demodulator", - /* see tda9887.c for details */ - }, - [TUNER_TEA5761] = { /* Philips RADIO */ - .name = "Philips TEA5761 FM Radio", - /* see tea5767.c for details */ - }, - [TUNER_XC5000] = { /* Xceive 5000 */ - .name = "Xceive 5000 tuner", - /* see xc5000.c for details */ - }, - [TUNER_XC4000] = { /* Xceive 4000 */ - .name = "Xceive 4000 tuner", - /* see xc4000.c for details */ - }, - [TUNER_TCL_MF02GIP_5N] = { /* TCL tuner MF02GIP-5N-E */ - .name = "TCL tuner MF02GIP-5N-E", - .params = tuner_tcl_mf02gip_5n_params, - .count = ARRAY_SIZE(tuner_tcl_mf02gip_5n_params), - }, - [TUNER_PHILIPS_FMD1216MEX_MK3] = { /* Philips PAL */ - .name = "Philips FMD1216MEX MK3 Hybrid Tuner", - .params = tuner_philips_fmd1216mex_mk3_params, - .count = ARRAY_SIZE(tuner_philips_fmd1216mex_mk3_params), - .min = 16 * 50.87, - .max = 16 * 858.00, - .stepsize = 166667, - .initdata = tua603x_agc112, - .sleepdata = (u8[]){ 4, 0x9c, 0x60, 0x85, 0x54 }, - }, - [TUNER_PHILIPS_FM1216MK5] = { /* Philips PAL */ - .name = "Philips PAL/SECAM multi (FM1216 MK5)", - .params = tuner_fm1216mk5_params, - .count = ARRAY_SIZE(tuner_fm1216mk5_params), - }, - - /* 80-89 */ - [TUNER_PHILIPS_FQ1216LME_MK3] = { /* PAL/SECAM, Loop-thru, no FM */ - .name = "Philips FQ1216LME MK3 PAL/SECAM w/active loopthrough", - .params = tuner_fq1216lme_mk3_params, - .count = ARRAY_SIZE(tuner_fq1216lme_mk3_params), - }, - - [TUNER_PARTSNIC_PTI_5NF05] = { - .name = "Partsnic (Daewoo) PTI-5NF05", - .params = tuner_partsnic_pti_5nf05_params, - .count = ARRAY_SIZE(tuner_partsnic_pti_5nf05_params), - }, - [TUNER_PHILIPS_CU1216L] = { - .name = "Philips CU1216L", - .params = tuner_philips_cu1216l_params, - .count = ARRAY_SIZE(tuner_philips_cu1216l_params), - .stepsize = 62500, - }, - [TUNER_NXP_TDA18271] = { - .name = "NXP TDA18271", - /* see tda18271-fe.c for details */ - }, - [TUNER_SONY_BTF_PXN01Z] = { - .name = "Sony BTF-Pxn01Z", - .params = tuner_sony_btf_pxn01z_params, - .count = ARRAY_SIZE(tuner_sony_btf_pxn01z_params), - }, - [TUNER_PHILIPS_FQ1236_MK5] = { /* NTSC, TDA9885, no FM radio */ - .name = "Philips FQ1236 MK5", - .params = tuner_philips_fq1236_mk5_params, - .count = ARRAY_SIZE(tuner_philips_fq1236_mk5_params), - }, - [TUNER_TENA_TNF_5337] = { /* Tena 5337 MFD */ - .name = "Tena TNF5337 MFD", - .params = tuner_tena_tnf_5337_params, - .count = ARRAY_SIZE(tuner_tena_tnf_5337_params), - }, - [TUNER_XC5000C] = { /* Xceive 5000C */ - .name = "Xceive 5000C tuner", - /* see xc5000.c for details */ - }, -}; -EXPORT_SYMBOL(tuners); - -unsigned const int tuner_count = ARRAY_SIZE(tuners); -EXPORT_SYMBOL(tuner_count); - -MODULE_DESCRIPTION("Simple tuner device type database"); -MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); -MODULE_LICENSE("GPL"); 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 7bcb6b0ff1df..000000000000 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ /dev/null @@ -1,1509 +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 -#include -#include -#include -#include -#include -#include -#include -#include -#include "tuner-i2c.h" -#include "tuner-xc2028.h" -#include "tuner-xc2028-types.h" - -#include -#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; -}; - -enum xc2028_state { - XC2028_NO_FIRMWARE = 0, - XC2028_WAITING_FIRMWARE, - XC2028_ACTIVE, - XC2028_SLEEP, - XC2028_NODEV, -}; - -struct xc2028_data { - struct list_head hybrid_tuner_instance_list; - struct tuner_i2c_props i2c_props; - __u32 frequency; - - enum xc2028_state state; - const char *fname; - - 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 int check_device_status(struct xc2028_data *priv) -{ - switch (priv->state) { - case XC2028_NO_FIRMWARE: - case XC2028_WAITING_FIRMWARE: - return -EAGAIN; - case XC2028_ACTIVE: - case XC2028_SLEEP: - return 0; - case XC2028_NODEV: - return -ENODEV; - } - 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; - priv->state = XC2028_NO_FIRMWARE; - - memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); -} - -static int load_all_firmwares(struct dvb_frontend *fe, - const struct firmware *fw) -{ - struct xc2028_data *priv = fe->tuner_priv; - const unsigned char *p, *endp; - int rc = 0; - int n, n_array; - char name[33]; - - tuner_dbg("%s called\n", __func__); - - 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", - priv->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, priv->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: - if (rc == 0) - tuner_dbg("Firmware files loaded.\n"); - else - priv->state = XC2028_NODEV; - - 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, retry_count = 0; - u16 version, hwmodel; - v4l2_std_id std0; - - tuner_dbg("%s called\n", __func__); - - rc = check_device_status(priv); - 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 and if the tuner - * is not at sleep mode - */ - if ((priv->state == XC2028_ACTIVE) && - (((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; - priv->state = XC2028_ACTIVE; - - return 0; - -fail: - priv->state = XC2028_SLEEP; - - 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, i; - - tuner_dbg("%s called\n", __func__); - - rc = check_device_status(priv); - if (rc < 0) - return rc; - - mutex_lock(&priv->lock); - - /* Sync Lock Indicator */ - for (i = 0; i < 3; i++) { - rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock); - if (rc < 0) - goto ret; - - if (frq_lock) - break; - msleep(6); - } - - /* Frequency didn't lock */ - if (frq_lock == 2) - goto ret; - - /* Get SNR of the video signal */ - rc = xc2028_get_reg(priv, XREG_SNR, &signal); - if (rc < 0) - goto ret; - - /* Signal level is 3 bits only */ - - signal = ((1 << 12) - 1) | ((signal & 0x07) << 12); - -ret: - mutex_unlock(&priv->lock); - - *strength = signal; - - tuner_dbg("signal strength is %d\n", signal); - - return rc; -} - -static int xc2028_get_afc(struct dvb_frontend *fe, s32 *afc) -{ - struct xc2028_data *priv = fe->tuner_priv; - int i, rc; - u16 frq_lock = 0; - s16 afc_reg = 0; - - rc = check_device_status(priv); - if (rc < 0) - return rc; - - mutex_lock(&priv->lock); - - /* Sync Lock Indicator */ - for (i = 0; i < 3; i++) { - rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock); - if (rc < 0) - goto ret; - - if (frq_lock) - break; - msleep(6); - } - - /* Frequency didn't lock */ - if (frq_lock == 2) - goto ret; - - /* Get AFC */ - rc = xc2028_get_reg(priv, XREG_FREQ_ERROR, &afc_reg); - if (rc < 0) - goto ret; - - *afc = afc_reg * 15625; /* Hz */ - - tuner_dbg("AFC is %d Hz\n", *afc); - -ret: - mutex_unlock(&priv->lock); - - 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 - */ - -#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= %*ph (freq=%d.%03d)\n", 4, buf, - 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; - int rc; - unsigned int type = 0; - u16 demod = 0; - - tuner_dbg("%s called\n", __func__); - - rc = check_device_status(priv); - if (rc < 0) - return rc; - - 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 - */ - 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 - */ - } - - 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; - - rc = check_device_status(priv); - if (rc < 0) - return rc; - - /* 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->state = XC2028_SLEEP; - - 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) { - free_firmware(priv); - kfree(priv->ctrl.fname); - priv->ctrl.fname = NULL; - } - - 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; - int rc; - - tuner_dbg("%s called\n", __func__); - - rc = check_device_status(priv); - if (rc < 0) - return rc; - - *frequency = priv->frequency; - - return 0; -} - -static void load_firmware_cb(const struct firmware *fw, - void *context) -{ - struct dvb_frontend *fe = context; - struct xc2028_data *priv = fe->tuner_priv; - int rc; - - tuner_dbg("request_firmware_nowait(): %s\n", fw ? "OK" : "error"); - if (!fw) { - tuner_err("Could not load firmware %s.\n", priv->fname); - priv->state = XC2028_NODEV; - return; - } - - rc = load_all_firmwares(fe, fw); - - release_firmware(fw); - - if (rc < 0) - return; - priv->state = XC2028_SLEEP; -} - -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); - - /* - * Copy the config data. - * For the firmware name, keep a local copy of the string, - * in order to avoid troubles during device release. - */ - if (priv->ctrl.fname) - kfree(priv->ctrl.fname); - memcpy(&priv->ctrl, p, sizeof(priv->ctrl)); - if (p->fname) { - priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL); - if (priv->ctrl.fname == NULL) - rc = -ENOMEM; - } - - /* - * If firmware name changed, frees firmware. As free_firmware will - * reset the status to NO_FIRMWARE, this forces a new request_firmware - */ - if (!firmware_name[0] && p->fname && - priv->fname && strcmp(p->fname, priv->fname)) - free_firmware(priv); - - if (priv->ctrl.max_len < 9) - priv->ctrl.max_len = 13; - - if (priv->state == XC2028_NO_FIRMWARE) { - if (!firmware_name[0]) - priv->fname = priv->ctrl.fname; - else - priv->fname = firmware_name; - - rc = request_firmware_nowait(THIS_MODULE, 1, - priv->fname, - priv->i2c_props.adap->dev.parent, - GFP_KERNEL, - fe, load_firmware_cb); - if (rc < 0) { - tuner_err("Failed to request firmware %s\n", - priv->fname); - priv->state = XC2028_NODEV; - } else - priv->state = XC2028_WAITING_FIRMWARE; - } - 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, - .get_afc = xc2028_get_afc, - .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 "); -MODULE_AUTHOR("Mauro Carvalho Chehab "); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE); -MODULE_FIRMWARE(XC3028L_DEFAULT_FIRMWARE); 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.c b/drivers/media/common/tuners/xc4000.c deleted file mode 100644 index 4937712278f6..000000000000 --- a/drivers/media/common/tuners/xc4000.c +++ /dev/null @@ -1,1757 +0,0 @@ -/* - * Driver for Xceive XC4000 "QAM/8VSB single chip tuner" - * - * Copyright (c) 2007 Xceive Corporation - * Copyright (c) 2007 Steven Toth - * Copyright (c) 2009 Devin Heitmueller - * Copyright (c) 2009 Davide Ferri - * Copyright (c) 2010 Istvan Varga - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include "dvb_frontend.h" - -#include "xc4000.h" -#include "tuner-i2c.h" -#include "tuner-xc2028-types.h" - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debugging level (0 to 2, default: 0 (off))."); - -static int no_poweroff; -module_param(no_poweroff, int, 0644); -MODULE_PARM_DESC(no_poweroff, "Power management (1: disabled, 2: enabled, " - "0 (default): use device-specific default mode)."); - -static int audio_std; -module_param(audio_std, int, 0644); -MODULE_PARM_DESC(audio_std, "Audio standard. XC4000 audio decoder explicitly " - "needs to know what audio standard is needed for some video standards " - "with audio A2 or NICAM. The valid settings are a sum of:\n" - " 1: use NICAM/B or A2/B instead of NICAM/A or A2/A\n" - " 2: use A2 instead of NICAM or BTSC\n" - " 4: use SECAM/K3 instead of K1\n" - " 8: use PAL-D/K audio for SECAM-D/K\n" - "16: use FM radio input 1 instead of input 2\n" - "32: use mono audio (the lower three bits are ignored)"); - -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."); - -static DEFINE_MUTEX(xc4000_list_mutex); -static LIST_HEAD(hybrid_tuner_instance_list); - -#define dprintk(level, fmt, arg...) if (debug >= level) \ - printk(KERN_INFO "%s: " fmt, "xc4000", ## arg) - -/* 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 xc4000_priv { - struct tuner_i2c_props i2c_props; - struct list_head hybrid_tuner_instance_list; - struct firmware_description *firm; - int firm_size; - u32 if_khz; - u32 freq_hz; - u32 bandwidth; - u8 video_standard; - u8 rf_mode; - u8 default_pm; - u8 dvb_amplitude; - u8 set_smoothedcvbs; - u8 ignore_i2c_write_errors; - __u16 firm_version; - struct firmware_properties cur_fw; - __u16 hwmodel; - __u16 hwvers; - struct mutex lock; -}; - -#define XC4000_AUDIO_STD_B 1 -#define XC4000_AUDIO_STD_A2 2 -#define XC4000_AUDIO_STD_K3 4 -#define XC4000_AUDIO_STD_L 8 -#define XC4000_AUDIO_STD_INPUT1 16 -#define XC4000_AUDIO_STD_MONO 32 - -#define XC4000_DEFAULT_FIRMWARE "dvb-fe-xc4000-1.4.fw" - -/* Misc Defines */ -#define MAX_TV_STANDARD 24 -#define XC_MAX_I2C_WRITE_LENGTH 64 -#define XC_POWERED_DOWN 0x80000000U - -/* Signal Types */ -#define XC_RF_MODE_AIR 0 -#define XC_RF_MODE_CABLE 1 - -/* Product id */ -#define XC_PRODUCT_ID_FW_NOT_LOADED 0x2000 -#define XC_PRODUCT_ID_XC4000 0x0FA0 -#define XC_PRODUCT_ID_XC4100 0x1004 - -/* Registers (Write-only) */ -#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_DIRECTSITTING_MODE 0x05 -#define XREG_SEEK_MODE 0x06 -#define XREG_POWER_DOWN 0x08 -#define XREG_SIGNALSOURCE 0x0A -#define XREG_SMOOTHEDCVBS 0x0E -#define XREG_AMPLITUDE 0x10 - -/* Registers (Read-only) */ -#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_SIGNAL_LEVEL 0x0A -#define XREG_NOISE_LEVEL 0x0B - -/* - 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 { - const char *Name; - u16 audio_mode; - u16 video_mode; - u16 int_freq; -}; - -/* Tuner standards */ -#define XC4000_MN_NTSC_PAL_BTSC 0 -#define XC4000_MN_NTSC_PAL_A2 1 -#define XC4000_MN_NTSC_PAL_EIAJ 2 -#define XC4000_MN_NTSC_PAL_Mono 3 -#define XC4000_BG_PAL_A2 4 -#define XC4000_BG_PAL_NICAM 5 -#define XC4000_BG_PAL_MONO 6 -#define XC4000_I_PAL_NICAM 7 -#define XC4000_I_PAL_NICAM_MONO 8 -#define XC4000_DK_PAL_A2 9 -#define XC4000_DK_PAL_NICAM 10 -#define XC4000_DK_PAL_MONO 11 -#define XC4000_DK_SECAM_A2DK1 12 -#define XC4000_DK_SECAM_A2LDK3 13 -#define XC4000_DK_SECAM_A2MONO 14 -#define XC4000_DK_SECAM_NICAM 15 -#define XC4000_L_SECAM_NICAM 16 -#define XC4000_LC_SECAM_NICAM 17 -#define XC4000_DTV6 18 -#define XC4000_DTV8 19 -#define XC4000_DTV7_8 20 -#define XC4000_DTV7 21 -#define XC4000_FM_Radio_INPUT2 22 -#define XC4000_FM_Radio_INPUT1 23 - -static struct XC_TV_STANDARD xc4000_standard[MAX_TV_STANDARD] = { - {"M/N-NTSC/PAL-BTSC", 0x0000, 0x80A0, 4500}, - {"M/N-NTSC/PAL-A2", 0x0000, 0x80A0, 4600}, - {"M/N-NTSC/PAL-EIAJ", 0x0040, 0x80A0, 4500}, - {"M/N-NTSC/PAL-Mono", 0x0078, 0x80A0, 4500}, - {"B/G-PAL-A2", 0x0000, 0x8159, 5640}, - {"B/G-PAL-NICAM", 0x0004, 0x8159, 5740}, - {"B/G-PAL-MONO", 0x0078, 0x8159, 5500}, - {"I-PAL-NICAM", 0x0080, 0x8049, 6240}, - {"I-PAL-NICAM-MONO", 0x0078, 0x8049, 6000}, - {"D/K-PAL-A2", 0x0000, 0x8049, 6380}, - {"D/K-PAL-NICAM", 0x0080, 0x8049, 6200}, - {"D/K-PAL-MONO", 0x0078, 0x8049, 6500}, - {"D/K-SECAM-A2 DK1", 0x0000, 0x8049, 6340}, - {"D/K-SECAM-A2 L/DK3", 0x0000, 0x8049, 6000}, - {"D/K-SECAM-A2 MONO", 0x0078, 0x8049, 6500}, - {"D/K-SECAM-NICAM", 0x0080, 0x8049, 6200}, - {"L-SECAM-NICAM", 0x8080, 0x0009, 6200}, - {"L'-SECAM-NICAM", 0x8080, 0x4009, 6200}, - {"DTV6", 0x00C0, 0x8002, 0}, - {"DTV8", 0x00C0, 0x800B, 0}, - {"DTV7/8", 0x00C0, 0x801B, 0}, - {"DTV7", 0x00C0, 0x8007, 0}, - {"FM Radio-INPUT2", 0x0008, 0x9800, 10700}, - {"FM Radio-INPUT1", 0x0008, 0x9000, 10700} -}; - -static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val); -static int xc4000_tuner_reset(struct dvb_frontend *fe); -static void xc_debug_dump(struct xc4000_priv *priv); - -static int xc_send_i2c_data(struct xc4000_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) { - if (priv->ignore_i2c_write_errors == 0) { - printk(KERN_ERR "xc4000: I2C write failed (len=%i)\n", - len); - if (len == 4) { - printk(KERN_ERR "bytes %*ph\n", 4, buf); - } - return -EREMOTEIO; - } - } - return 0; -} - -static int xc4000_tuner_reset(struct dvb_frontend *fe) -{ - struct xc4000_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, - XC4000_TUNER_RESET, 0); - if (ret) { - printk(KERN_ERR "xc4000: reset failed\n"); - return -EREMOTEIO; - } - } else { - printk(KERN_ERR "xc4000: no tuner reset callback function, " - "fatal\n"); - return -EINVAL; - } - return 0; -} - -static int xc_write_reg(struct xc4000_priv *priv, u16 regAddr, u16 i2cData) -{ - u8 buf[4]; - 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); - - return result; -} - -static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence) -{ - struct xc4000_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 */ - /* NOTE: this is ignored, as the reset callback was */ - /* already called by check_firmware() */ - index += 2; - } else if (len & 0x8000) { - /* WAIT command */ - msleep(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 != 0) - return result; - - pos += nbytes_to_send - 2; - } - index += len; - } - } - return 0; -} - -static int xc_set_tv_standard(struct xc4000_priv *priv, - u16 video_mode, u16 audio_mode) -{ - int ret; - dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, video_mode, audio_mode); - dprintk(1, "%s() Standard = %s\n", - __func__, - xc4000_standard[priv->video_standard].Name); - - /* Don't complain when the request fails because of i2c stretching */ - priv->ignore_i2c_write_errors = 1; - - ret = xc_write_reg(priv, XREG_VIDEO_MODE, video_mode); - if (ret == 0) - ret = xc_write_reg(priv, XREG_AUDIO_MODE, audio_mode); - - priv->ignore_i2c_write_errors = 0; - - return ret; -} - -static int xc_set_signal_source(struct xc4000_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 xc4000_tuner_ops; - -static int xc_set_rf_frequency(struct xc4000_priv *priv, u32 freq_hz) -{ - u16 freq_code; - - dprintk(1, "%s(%u)\n", __func__, freq_hz); - - if ((freq_hz > xc4000_tuner_ops.info.frequency_max) || - (freq_hz < xc4000_tuner_ops.info.frequency_min)) - return -EINVAL; - - freq_code = (u16)(freq_hz / 15625); - - /* WAS: 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) */ - /* WAS: XREG_FINERFREQ */ - return xc_write_reg(priv, XREG_RF_FREQ, freq_code); -} - -static int xc_get_adc_envelope(struct xc4000_priv *priv, u16 *adc_envelope) -{ - return xc4000_readreg(priv, XREG_ADC_ENV, adc_envelope); -} - -static int xc_get_frequency_error(struct xc4000_priv *priv, u32 *freq_error_hz) -{ - int result; - u16 regData; - u32 tmp; - - result = xc4000_readreg(priv, XREG_FREQ_ERROR, ®Data); - if (result != 0) - return result; - - tmp = (u32)regData & 0xFFFFU; - tmp = (tmp < 0x8000U ? tmp : 0x10000U - tmp); - (*freq_error_hz) = tmp * 15625; - return result; -} - -static int xc_get_lock_status(struct xc4000_priv *priv, u16 *lock_status) -{ - return xc4000_readreg(priv, XREG_LOCK, lock_status); -} - -static int xc_get_version(struct xc4000_priv *priv, - u8 *hw_majorversion, u8 *hw_minorversion, - u8 *fw_majorversion, u8 *fw_minorversion) -{ - u16 data; - int result; - - result = xc4000_readreg(priv, XREG_VERSION, &data); - if (result != 0) - 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_hsync_freq(struct xc4000_priv *priv, u32 *hsync_freq_hz) -{ - u16 regData; - int result; - - result = xc4000_readreg(priv, XREG_HSYNC_FREQ, ®Data); - if (result != 0) - return result; - - (*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100; - return result; -} - -static int xc_get_frame_lines(struct xc4000_priv *priv, u16 *frame_lines) -{ - return xc4000_readreg(priv, XREG_FRAME_LINES, frame_lines); -} - -static int xc_get_quality(struct xc4000_priv *priv, u16 *quality) -{ - return xc4000_readreg(priv, XREG_QUALITY, quality); -} - -static int xc_get_signal_level(struct xc4000_priv *priv, u16 *signal) -{ - return xc4000_readreg(priv, XREG_SIGNAL_LEVEL, signal); -} - -static int xc_get_noise_level(struct xc4000_priv *priv, u16 *noise) -{ - return xc4000_readreg(priv, XREG_NOISE_LEVEL, noise); -} - -static u16 xc_wait_for_lock(struct xc4000_priv *priv) -{ - u16 lock_state = 0; - int watchdog_count = 40; - - while ((lock_state == 0) && (watchdog_count > 0)) { - xc_get_lock_status(priv, &lock_state); - if (lock_state != 1) { - msleep(5); - watchdog_count--; - } - } - return lock_state; -} - -static int xc_tune_channel(struct xc4000_priv *priv, u32 freq_hz) -{ - int found = 1; - int result; - - dprintk(1, "%s(%u)\n", __func__, freq_hz); - - /* Don't complain when the request fails because of i2c stretching */ - priv->ignore_i2c_write_errors = 1; - result = xc_set_rf_frequency(priv, freq_hz); - priv->ignore_i2c_write_errors = 0; - - if (result != 0) - return 0; - - /* wait for lock only in analog TV mode */ - if ((priv->cur_fw.type & (FM | DTV6 | DTV7 | DTV78 | DTV8)) == 0) { - if (xc_wait_for_lock(priv) != 1) - found = 0; - } - - /* Wait for stats to stabilize. - * Frame Lines needs two frame times after initial lock - * before it is valid. - */ - msleep(debug ? 100 : 10); - - if (debug) - xc_debug_dump(priv); - - return found; -} - -static int xc4000_readreg(struct xc4000_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_ERR "xc4000: I2C read failed\n"); - return -EREMOTEIO; - } - - *val = (bval[0] << 8) | bval[1]; - 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(KERN_CONT "BASE "); - if (type & INIT1) - printk(KERN_CONT "INIT1 "); - if (type & F8MHZ) - printk(KERN_CONT "F8MHZ "); - if (type & MTS) - printk(KERN_CONT "MTS "); - if (type & D2620) - printk(KERN_CONT "D2620 "); - if (type & D2633) - printk(KERN_CONT "D2633 "); - if (type & DTV6) - printk(KERN_CONT "DTV6 "); - if (type & QAM) - printk(KERN_CONT "QAM "); - if (type & DTV7) - printk(KERN_CONT "DTV7 "); - if (type & DTV78) - printk(KERN_CONT "DTV78 "); - if (type & DTV8) - printk(KERN_CONT "DTV8 "); - if (type & FM) - printk(KERN_CONT "FM "); - if (type & INPUT1) - printk(KERN_CONT "INPUT1 "); - if (type & LCD) - printk(KERN_CONT "LCD "); - if (type & NOGD) - printk(KERN_CONT "NOGD "); - if (type & MONO) - printk(KERN_CONT "MONO "); - if (type & ATSC) - printk(KERN_CONT "ATSC "); - if (type & IF) - printk(KERN_CONT "IF "); - if (type & LG60) - printk(KERN_CONT "LG60 "); - if (type & ATI638) - printk(KERN_CONT "ATI638 "); - if (type & OREN538) - printk(KERN_CONT "OREN538 "); - if (type & OREN36) - printk(KERN_CONT "OREN36 "); - if (type & TOYOTA388) - printk(KERN_CONT "TOYOTA388 "); - if (type & TOYOTA794) - printk(KERN_CONT "TOYOTA794 "); - if (type & DIBCOM52) - printk(KERN_CONT "DIBCOM52 "); - if (type & ZARLINK456) - printk(KERN_CONT "ZARLINK456 "); - if (type & CHINA) - printk(KERN_CONT "CHINA "); - if (type & F6MHZ) - printk(KERN_CONT "F6MHZ "); - if (type & INPUT2) - printk(KERN_CONT "INPUT2 "); - if (type & SCODE) - printk(KERN_CONT "SCODE "); - if (type & HAS_IF) - printk(KERN_CONT "HAS_IF_%d ", int_freq); -} - -static int seek_firmware(struct dvb_frontend *fe, unsigned int type, - v4l2_std_id *id) -{ - struct xc4000_priv *priv = fe->tuner_priv; - int i, best_i = -1; - unsigned int best_nr_diffs = 255U; - - if (!priv->firm) { - printk(KERN_ERR "Error! firmware not loaded\n"); - return -EINVAL; - } - - if (((type & ~SCODE) == 0) && (*id == 0)) - *id = V4L2_STD_PAL; - - /* Seek for generic video standard match */ - for (i = 0; i < priv->firm_size; i++) { - v4l2_std_id id_diff_mask = - (priv->firm[i].id ^ (*id)) & (*id); - unsigned int type_diff_mask = - (priv->firm[i].type ^ type) - & (BASE_TYPES | DTV_TYPES | LCD | NOGD | MONO | SCODE); - unsigned int nr_diffs; - - if (type_diff_mask - & (BASE | INIT1 | FM | DTV6 | DTV7 | DTV78 | DTV8 | SCODE)) - continue; - - nr_diffs = hweight64(id_diff_mask) + hweight32(type_diff_mask); - if (!nr_diffs) /* Supports all the requested standards */ - goto found; - - if (nr_diffs < best_nr_diffs) { - best_nr_diffs = nr_diffs; - best_i = i; - } - } - - /* FIXME: Would make sense to seek for type "hint" match ? */ - if (best_i < 0) { - i = -ENOENT; - goto ret; - } - - if (best_nr_diffs > 0U) { - printk(KERN_WARNING - "Selecting best matching firmware (%u bits differ) for " - "type=(%x), id %016llx:\n", - best_nr_diffs, type, (unsigned long long)*id); - i = best_i; - } - -found: - *id = priv->firm[i].id; - -ret: - if (debug) { - printk(KERN_DEBUG "%s firmware for type=", - (i < 0) ? "Can't find" : "Found"); - dump_firm_type(type); - printk(KERN_DEBUG "(%x), id %016llx.\n", type, (unsigned long long)*id); - } - return i; -} - -static int load_firmware(struct dvb_frontend *fe, unsigned int type, - v4l2_std_id *id) -{ - struct xc4000_priv *priv = fe->tuner_priv; - int pos, rc; - unsigned char *p; - - pos = seek_firmware(fe, type, id); - if (pos < 0) - return pos; - - p = priv->firm[pos].ptr; - - /* Don't complain when the request fails because of i2c stretching */ - priv->ignore_i2c_write_errors = 1; - - rc = xc_load_i2c_sequence(fe, p); - - priv->ignore_i2c_write_errors = 0; - - return rc; -} - -static int xc4000_fwupload(struct dvb_frontend *fe) -{ - struct xc4000_priv *priv = fe->tuner_priv; - const struct firmware *fw = NULL; - const unsigned char *p, *endp; - int rc = 0; - int n, n_array; - char name[33]; - const char *fname; - - if (firmware_name[0] != '\0') - fname = firmware_name; - else - fname = XC4000_DEFAULT_FIRMWARE; - - dprintk(1, "Reading firmware %s\n", fname); - rc = request_firmware(&fw, fname, priv->i2c_props.adap->dev.parent); - if (rc < 0) { - if (rc == -ENOENT) - printk(KERN_ERR "Error: firmware %s not found.\n", fname); - else - printk(KERN_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) { - printk(KERN_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; - - dprintk(1, "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) { - printk(KERN_ERR "Not enough memory to load firmware file.\n"); - rc = -ENOMEM; - goto done; - } - 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) { - printk(KERN_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) { - printk(KERN_ERR "Firmware type (%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) { - printk(KERN_ERR "Not enough memory to load firmware file.\n"); - rc = -ENOMEM; - goto done; - } - - if (debug) { - printk(KERN_DEBUG "Reading firmware type "); - dump_firm_type_and_int_freq(type, int_freq); - printk(KERN_DEBUG "(%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) { - printk(KERN_ERR "Firmware file is incomplete!\n"); - goto corrupt; - } - - goto done; - -header: - printk(KERN_ERR "Firmware header is incomplete!\n"); -corrupt: - rc = -EINVAL; - printk(KERN_ERR "Error: firmware file is corrupted!\n"); - -done: - release_firmware(fw); - if (rc == 0) - dprintk(1, "Firmware files loaded.\n"); - - return rc; -} - -static int load_scode(struct dvb_frontend *fe, unsigned int type, - v4l2_std_id *id, __u16 int_freq, int scode) -{ - struct xc4000_priv *priv = fe->tuner_priv; - int pos, rc; - unsigned char *p; - u8 scode_buf[13]; - u8 indirect_mode[5]; - - dprintk(1, "%s called int_freq=%d\n", __func__, int_freq); - - 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].size != 12 * 16 || scode >= 16) - return -EINVAL; - p += 12 * scode; - - if (debug) { - tuner_info("Loading SCODE for type="); - dump_firm_type_and_int_freq(priv->firm[pos].type, - priv->firm[pos].int_freq); - printk(KERN_CONT "(%x), id %016llx.\n", priv->firm[pos].type, - (unsigned long long)*id); - } - - scode_buf[0] = 0x00; - memcpy(&scode_buf[1], p, 12); - - /* Enter direct-mode */ - rc = xc_write_reg(priv, XREG_DIRECTSITTING_MODE, 0); - if (rc < 0) { - printk(KERN_ERR "failed to put device into direct mode!\n"); - return -EIO; - } - - rc = xc_send_i2c_data(priv, scode_buf, 13); - if (rc != 0) { - /* Even if the send failed, make sure we set back to indirect - mode */ - printk(KERN_ERR "Failed to set scode %d\n", rc); - } - - /* Switch back to indirect-mode */ - memset(indirect_mode, 0, sizeof(indirect_mode)); - indirect_mode[4] = 0x88; - xc_send_i2c_data(priv, indirect_mode, sizeof(indirect_mode)); - msleep(10); - - return 0; -} - -static int check_firmware(struct dvb_frontend *fe, unsigned int type, - v4l2_std_id std, __u16 int_freq) -{ - struct xc4000_priv *priv = fe->tuner_priv; - struct firmware_properties new_fw; - int rc = 0, is_retry = 0; - u16 hwmodel; - v4l2_std_id std0; - u8 hw_major, hw_minor, fw_major, fw_minor; - - dprintk(1, "%s called\n", __func__); - - if (!priv->firm) { - rc = xc4000_fwupload(fe); - if (rc < 0) - return rc; - } - -retry: - new_fw.type = type; - new_fw.id = std; - new_fw.std_req = std; - new_fw.scode_table = SCODE; - new_fw.scode_nr = 0; - new_fw.int_freq = int_freq; - - dprintk(1, "checking firmware, user requested type="); - if (debug) { - dump_firm_type(new_fw.type); - printk(KERN_CONT "(%x), id %016llx, ", new_fw.type, - (unsigned long long)new_fw.std_req); - if (!int_freq) - printk(KERN_CONT "scode_tbl "); - else - printk(KERN_CONT "int_freq %d, ", new_fw.int_freq); - printk(KERN_CONT "scode_nr %d\n", new_fw.scode_nr); - } - - /* No need to reload base firmware if it matches */ - if (priv->cur_fw.type & BASE) { - dprintk(1, "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 = xc4000_tuner_reset(fe); - if (rc < 0) - goto fail; - - /* BASE firmwares are all std0 */ - std0 = 0; - rc = load_firmware(fe, BASE, &std0); - if (rc < 0) { - printk(KERN_ERR "Error %d while loading base firmware\n", rc); - goto fail; - } - - /* Load INIT1, if needed */ - dprintk(1, "Load init1 firmware, if exists\n"); - - rc = load_firmware(fe, BASE | INIT1, &std0); - if (rc == -ENOENT) - rc = load_firmware(fe, BASE | INIT1, &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) { - dprintk(1, "Std-specific firmware already loaded.\n"); - goto skip_std_specific; - } - - /* Reloading std-specific firmware forces a SCODE update */ - priv->cur_fw.scode_table = 0; - - /* Load the standard firmware */ - rc = load_firmware(fe, new_fw.type, &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) { - dprintk(1, "SCODE firmware already loaded.\n"); - goto check_device; - } - - /* Load SCODE firmware, if exists */ - rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id, - new_fw.int_freq, new_fw.scode_nr); - if (rc != 0) - dprintk(1, "load scode failed %d\n", rc); - -check_device: - rc = xc4000_readreg(priv, XREG_PRODUCT_ID, &hwmodel); - - if (xc_get_version(priv, &hw_major, &hw_minor, &fw_major, - &fw_minor) != 0) { - printk(KERN_ERR "Unable to read tuner registers.\n"); - goto fail; - } - - dprintk(1, "Device is Xceive %d version %d.%d, " - "firmware version %d.%d\n", - hwmodel, hw_major, hw_minor, fw_major, fw_minor); - - /* Check firmware version against what we downloaded. */ - if (priv->firm_version != ((fw_major << 8) | fw_minor)) { - printk(KERN_WARNING - "Incorrect readback of firmware version %d.%d.\n", - fw_major, fw_minor); - goto fail; - } - - /* Check that the tuner hardware model remains consistent over time. */ - if (priv->hwmodel == 0 && - (hwmodel == XC_PRODUCT_ID_XC4000 || - hwmodel == XC_PRODUCT_ID_XC4100)) { - priv->hwmodel = hwmodel; - priv->hwvers = (hw_major << 8) | hw_minor; - } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel || - priv->hwvers != ((hw_major << 8) | hw_minor)) { - printk(KERN_WARNING - "Read invalid device hardware information - tuner " - "hung?\n"); - goto fail; - } - - 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 (!is_retry) { - msleep(50); - is_retry = 1; - dprintk(1, "Retrying firmware load\n"); - goto retry; - } - - if (rc == -ENOENT) - rc = -EINVAL; - return rc; -} - -static void xc_debug_dump(struct xc4000_priv *priv) -{ - u16 adc_envelope; - u32 freq_error_hz = 0; - u16 lock_status; - u32 hsync_freq_hz = 0; - u16 frame_lines; - u16 quality; - u16 signal = 0; - u16 noise = 0; - u8 hw_majorversion = 0, hw_minorversion = 0; - u8 fw_majorversion = 0, fw_minorversion = 0; - - 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); - dprintk(1, "*** HW: V%02x.%02x, FW: V%02x.%02x\n", - hw_majorversion, hw_minorversion, - fw_majorversion, fw_minorversion); - - if (priv->video_standard < XC4000_DTV6) { - 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); - - xc_get_signal_level(priv, &signal); - dprintk(1, "*** Signal level = -%ddB (%d)\n", signal >> 8, signal); - - xc_get_noise_level(priv, &noise); - dprintk(1, "*** Noise level = %ddB (%d)\n", noise >> 8, noise); -} - -static int xc4000_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 xc4000_priv *priv = fe->tuner_priv; - unsigned int type; - int ret = -EREMOTEIO; - - dprintk(1, "%s() frequency=%d (Hz)\n", __func__, c->frequency); - - mutex_lock(&priv->lock); - - switch (delsys) { - case SYS_ATSC: - dprintk(1, "%s() VSB modulation\n", __func__); - priv->rf_mode = XC_RF_MODE_AIR; - priv->freq_hz = c->frequency - 1750000; - priv->video_standard = XC4000_DTV6; - type = DTV6; - break; - case SYS_DVBC_ANNEX_B: - dprintk(1, "%s() QAM modulation\n", __func__); - priv->rf_mode = XC_RF_MODE_CABLE; - priv->freq_hz = c->frequency - 1750000; - priv->video_standard = XC4000_DTV6; - type = DTV6; - break; - case SYS_DVBT: - case SYS_DVBT2: - dprintk(1, "%s() OFDM\n", __func__); - if (bw == 0) { - if (c->frequency < 400000000) { - priv->freq_hz = c->frequency - 2250000; - } else { - priv->freq_hz = c->frequency - 2750000; - } - priv->video_standard = XC4000_DTV7_8; - type = DTV78; - } else if (bw <= 6000000) { - priv->video_standard = XC4000_DTV6; - priv->freq_hz = c->frequency - 1750000; - type = DTV6; - } else if (bw <= 7000000) { - priv->video_standard = XC4000_DTV7; - priv->freq_hz = c->frequency - 2250000; - type = DTV7; - } else { - priv->video_standard = XC4000_DTV8; - priv->freq_hz = c->frequency - 2750000; - type = DTV8; - } - priv->rf_mode = XC_RF_MODE_AIR; - break; - default: - printk(KERN_ERR "xc4000 delivery system not supported!\n"); - ret = -EINVAL; - goto fail; - } - - dprintk(1, "%s() frequency=%d (compensated)\n", - __func__, priv->freq_hz); - - /* Make sure the correct firmware type is loaded */ - if (check_firmware(fe, type, 0, priv->if_khz) != 0) - goto fail; - - priv->bandwidth = c->bandwidth_hz; - - ret = xc_set_signal_source(priv, priv->rf_mode); - if (ret != 0) { - printk(KERN_ERR "xc4000: xc_set_signal_source(%d) failed\n", - priv->rf_mode); - goto fail; - } else { - u16 video_mode, audio_mode; - video_mode = xc4000_standard[priv->video_standard].video_mode; - audio_mode = xc4000_standard[priv->video_standard].audio_mode; - if (type == DTV6 && priv->firm_version != 0x0102) - video_mode |= 0x0001; - ret = xc_set_tv_standard(priv, video_mode, audio_mode); - if (ret != 0) { - printk(KERN_ERR "xc4000: xc_set_tv_standard failed\n"); - /* DJH - do not return when it fails... */ - /* goto fail; */ - } - } - - if (xc_write_reg(priv, XREG_D_CODE, 0) == 0) - ret = 0; - if (priv->dvb_amplitude != 0) { - if (xc_write_reg(priv, XREG_AMPLITUDE, - (priv->firm_version != 0x0102 || - priv->dvb_amplitude != 134 ? - priv->dvb_amplitude : 132)) != 0) - ret = -EREMOTEIO; - } - if (priv->set_smoothedcvbs != 0) { - if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0) - ret = -EREMOTEIO; - } - if (ret != 0) { - printk(KERN_ERR "xc4000: setting registers failed\n"); - /* goto fail; */ - } - - xc_tune_channel(priv, priv->freq_hz); - - ret = 0; - -fail: - mutex_unlock(&priv->lock); - - return ret; -} - -static int xc4000_set_analog_params(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct xc4000_priv *priv = fe->tuner_priv; - unsigned int type = 0; - int ret = -EREMOTEIO; - - if (params->mode == V4L2_TUNER_RADIO) { - dprintk(1, "%s() frequency=%d (in units of 62.5Hz)\n", - __func__, params->frequency); - - mutex_lock(&priv->lock); - - params->std = 0; - priv->freq_hz = params->frequency * 125L / 2; - - if (audio_std & XC4000_AUDIO_STD_INPUT1) { - priv->video_standard = XC4000_FM_Radio_INPUT1; - type = FM | INPUT1; - } else { - priv->video_standard = XC4000_FM_Radio_INPUT2; - type = FM | INPUT2; - } - - goto tune_channel; - } - - dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n", - __func__, params->frequency); - - mutex_lock(&priv->lock); - - /* params->frequency is in units of 62.5khz */ - priv->freq_hz = params->frequency * 62500; - - params->std &= V4L2_STD_ALL; - /* if std is not defined, choose one */ - if (!params->std) - params->std = V4L2_STD_PAL_BG; - - if (audio_std & XC4000_AUDIO_STD_MONO) - type = MONO; - - if (params->std & V4L2_STD_MN) { - params->std = V4L2_STD_MN; - if (audio_std & XC4000_AUDIO_STD_MONO) { - priv->video_standard = XC4000_MN_NTSC_PAL_Mono; - } else if (audio_std & XC4000_AUDIO_STD_A2) { - params->std |= V4L2_STD_A2; - priv->video_standard = XC4000_MN_NTSC_PAL_A2; - } else { - params->std |= V4L2_STD_BTSC; - priv->video_standard = XC4000_MN_NTSC_PAL_BTSC; - } - goto tune_channel; - } - - if (params->std & V4L2_STD_PAL_BG) { - params->std = V4L2_STD_PAL_BG; - if (audio_std & XC4000_AUDIO_STD_MONO) { - priv->video_standard = XC4000_BG_PAL_MONO; - } else if (!(audio_std & XC4000_AUDIO_STD_A2)) { - if (!(audio_std & XC4000_AUDIO_STD_B)) { - params->std |= V4L2_STD_NICAM_A; - priv->video_standard = XC4000_BG_PAL_NICAM; - } else { - params->std |= V4L2_STD_NICAM_B; - priv->video_standard = XC4000_BG_PAL_NICAM; - } - } else { - if (!(audio_std & XC4000_AUDIO_STD_B)) { - params->std |= V4L2_STD_A2_A; - priv->video_standard = XC4000_BG_PAL_A2; - } else { - params->std |= V4L2_STD_A2_B; - priv->video_standard = XC4000_BG_PAL_A2; - } - } - goto tune_channel; - } - - if (params->std & V4L2_STD_PAL_I) { - /* default to NICAM audio standard */ - params->std = V4L2_STD_PAL_I | V4L2_STD_NICAM; - if (audio_std & XC4000_AUDIO_STD_MONO) - priv->video_standard = XC4000_I_PAL_NICAM_MONO; - else - priv->video_standard = XC4000_I_PAL_NICAM; - goto tune_channel; - } - - if (params->std & V4L2_STD_PAL_DK) { - params->std = V4L2_STD_PAL_DK; - if (audio_std & XC4000_AUDIO_STD_MONO) { - priv->video_standard = XC4000_DK_PAL_MONO; - } else if (audio_std & XC4000_AUDIO_STD_A2) { - params->std |= V4L2_STD_A2; - priv->video_standard = XC4000_DK_PAL_A2; - } else { - params->std |= V4L2_STD_NICAM; - priv->video_standard = XC4000_DK_PAL_NICAM; - } - goto tune_channel; - } - - if (params->std & V4L2_STD_SECAM_DK) { - /* default to A2 audio standard */ - params->std = V4L2_STD_SECAM_DK | V4L2_STD_A2; - if (audio_std & XC4000_AUDIO_STD_L) { - type = 0; - priv->video_standard = XC4000_DK_SECAM_NICAM; - } else if (audio_std & XC4000_AUDIO_STD_MONO) { - priv->video_standard = XC4000_DK_SECAM_A2MONO; - } else if (audio_std & XC4000_AUDIO_STD_K3) { - params->std |= V4L2_STD_SECAM_K3; - priv->video_standard = XC4000_DK_SECAM_A2LDK3; - } else { - priv->video_standard = XC4000_DK_SECAM_A2DK1; - } - goto tune_channel; - } - - if (params->std & V4L2_STD_SECAM_L) { - /* default to NICAM audio standard */ - type = 0; - params->std = V4L2_STD_SECAM_L | V4L2_STD_NICAM; - priv->video_standard = XC4000_L_SECAM_NICAM; - goto tune_channel; - } - - if (params->std & V4L2_STD_SECAM_LC) { - /* default to NICAM audio standard */ - type = 0; - params->std = V4L2_STD_SECAM_LC | V4L2_STD_NICAM; - priv->video_standard = XC4000_LC_SECAM_NICAM; - goto tune_channel; - } - -tune_channel: - /* FIXME: it could be air. */ - priv->rf_mode = XC_RF_MODE_CABLE; - - if (check_firmware(fe, type, params->std, - xc4000_standard[priv->video_standard].int_freq) != 0) - goto fail; - - ret = xc_set_signal_source(priv, priv->rf_mode); - if (ret != 0) { - printk(KERN_ERR - "xc4000: xc_set_signal_source(%d) failed\n", - priv->rf_mode); - goto fail; - } else { - u16 video_mode, audio_mode; - video_mode = xc4000_standard[priv->video_standard].video_mode; - audio_mode = xc4000_standard[priv->video_standard].audio_mode; - if (priv->video_standard < XC4000_BG_PAL_A2) { - if (type & NOGD) - video_mode &= 0xFF7F; - } else if (priv->video_standard < XC4000_I_PAL_NICAM) { - if (priv->firm_version == 0x0102) - video_mode &= 0xFEFF; - if (audio_std & XC4000_AUDIO_STD_B) - video_mode |= 0x0080; - } - ret = xc_set_tv_standard(priv, video_mode, audio_mode); - if (ret != 0) { - printk(KERN_ERR "xc4000: xc_set_tv_standard failed\n"); - goto fail; - } - } - - if (xc_write_reg(priv, XREG_D_CODE, 0) == 0) - ret = 0; - if (xc_write_reg(priv, XREG_AMPLITUDE, 1) != 0) - ret = -EREMOTEIO; - if (priv->set_smoothedcvbs != 0) { - if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0) - ret = -EREMOTEIO; - } - if (ret != 0) { - printk(KERN_ERR "xc4000: setting registers failed\n"); - goto fail; - } - - xc_tune_channel(priv, priv->freq_hz); - - ret = 0; - -fail: - mutex_unlock(&priv->lock); - - return ret; -} - -static int xc4000_get_signal(struct dvb_frontend *fe, u16 *strength) -{ - struct xc4000_priv *priv = fe->tuner_priv; - u16 value = 0; - int rc; - - mutex_lock(&priv->lock); - rc = xc4000_readreg(priv, XREG_SIGNAL_LEVEL, &value); - mutex_unlock(&priv->lock); - - if (rc < 0) - goto ret; - - /* Informations from real testing of DVB-T and radio part, - coeficient for one dB is 0xff. - */ - tuner_dbg("Signal strength: -%ddB (%05d)\n", value >> 8, value); - - /* all known digital modes */ - if ((priv->video_standard == XC4000_DTV6) || - (priv->video_standard == XC4000_DTV7) || - (priv->video_standard == XC4000_DTV7_8) || - (priv->video_standard == XC4000_DTV8)) - goto digital; - - /* Analog mode has NOISE LEVEL important, signal - depends only on gain of antenna and amplifiers, - but it doesn't tell anything about real quality - of reception. - */ - mutex_lock(&priv->lock); - rc = xc4000_readreg(priv, XREG_NOISE_LEVEL, &value); - mutex_unlock(&priv->lock); - - tuner_dbg("Noise level: %ddB (%05d)\n", value >> 8, value); - - /* highest noise level: 32dB */ - if (value >= 0x2000) { - value = 0; - } else { - value = ~value << 3; - } - - goto ret; - - /* Digital mode has SIGNAL LEVEL important and real - noise level is stored in demodulator registers. - */ -digital: - /* best signal: -50dB */ - if (value <= 0x3200) { - value = 0xffff; - /* minimum: -114dB - should be 0x7200 but real zero is 0x713A */ - } else if (value >= 0x713A) { - value = 0; - } else { - value = ~(value - 0x3200) << 2; - } - -ret: - *strength = value; - - return rc; -} - -static int xc4000_get_frequency(struct dvb_frontend *fe, u32 *freq) -{ - struct xc4000_priv *priv = fe->tuner_priv; - - *freq = priv->freq_hz; - - if (debug) { - mutex_lock(&priv->lock); - if ((priv->cur_fw.type - & (BASE | FM | DTV6 | DTV7 | DTV78 | DTV8)) == BASE) { - u16 snr = 0; - if (xc4000_readreg(priv, XREG_SNR, &snr) == 0) { - mutex_unlock(&priv->lock); - dprintk(1, "%s() freq = %u, SNR = %d\n", - __func__, *freq, snr); - return 0; - } - } - mutex_unlock(&priv->lock); - } - - dprintk(1, "%s()\n", __func__); - - return 0; -} - -static int xc4000_get_bandwidth(struct dvb_frontend *fe, u32 *bw) -{ - struct xc4000_priv *priv = fe->tuner_priv; - dprintk(1, "%s()\n", __func__); - - *bw = priv->bandwidth; - return 0; -} - -static int xc4000_get_status(struct dvb_frontend *fe, u32 *status) -{ - struct xc4000_priv *priv = fe->tuner_priv; - u16 lock_status = 0; - - mutex_lock(&priv->lock); - - if (priv->cur_fw.type & BASE) - xc_get_lock_status(priv, &lock_status); - - *status = (lock_status == 1 ? - TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO : 0); - if (priv->cur_fw.type & (DTV6 | DTV7 | DTV78 | DTV8)) - *status &= (~TUNER_STATUS_STEREO); - - mutex_unlock(&priv->lock); - - dprintk(2, "%s() lock_status = %d\n", __func__, lock_status); - - return 0; -} - -static int xc4000_sleep(struct dvb_frontend *fe) -{ - struct xc4000_priv *priv = fe->tuner_priv; - int ret = 0; - - dprintk(1, "%s()\n", __func__); - - mutex_lock(&priv->lock); - - /* Avoid firmware reload on slow devices */ - if ((no_poweroff == 2 || - (no_poweroff == 0 && priv->default_pm != 0)) && - (priv->cur_fw.type & BASE) != 0) { - /* force reset and firmware reload */ - priv->cur_fw.type = XC_POWERED_DOWN; - - if (xc_write_reg(priv, XREG_POWER_DOWN, 0) != 0) { - printk(KERN_ERR - "xc4000: %s() unable to shutdown tuner\n", - __func__); - ret = -EREMOTEIO; - } - msleep(20); - } - - mutex_unlock(&priv->lock); - - return ret; -} - -static int xc4000_init(struct dvb_frontend *fe) -{ - dprintk(1, "%s()\n", __func__); - - return 0; -} - -static int xc4000_release(struct dvb_frontend *fe) -{ - struct xc4000_priv *priv = fe->tuner_priv; - - dprintk(1, "%s()\n", __func__); - - mutex_lock(&xc4000_list_mutex); - - if (priv) - hybrid_tuner_release_state(priv); - - mutex_unlock(&xc4000_list_mutex); - - fe->tuner_priv = NULL; - - return 0; -} - -static const struct dvb_tuner_ops xc4000_tuner_ops = { - .info = { - .name = "Xceive XC4000", - .frequency_min = 1000000, - .frequency_max = 1023000000, - .frequency_step = 50000, - }, - - .release = xc4000_release, - .init = xc4000_init, - .sleep = xc4000_sleep, - - .set_params = xc4000_set_params, - .set_analog_params = xc4000_set_analog_params, - .get_frequency = xc4000_get_frequency, - .get_rf_strength = xc4000_get_signal, - .get_bandwidth = xc4000_get_bandwidth, - .get_status = xc4000_get_status -}; - -struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct xc4000_config *cfg) -{ - struct xc4000_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(&xc4000_list_mutex); - - instance = hybrid_tuner_request_state(struct xc4000_priv, priv, - hybrid_tuner_instance_list, - i2c, cfg->i2c_address, "xc4000"); - switch (instance) { - case 0: - goto fail; - break; - case 1: - /* new tuner instance */ - priv->bandwidth = 6000000; - /* set default configuration */ - priv->if_khz = 4560; - priv->default_pm = 0; - priv->dvb_amplitude = 134; - priv->set_smoothedcvbs = 1; - mutex_init(&priv->lock); - fe->tuner_priv = priv; - break; - default: - /* existing tuner instance */ - fe->tuner_priv = priv; - break; - } - - if (cfg->if_khz != 0) { - /* copy configuration if provided by the caller */ - priv->if_khz = cfg->if_khz; - priv->default_pm = cfg->default_pm; - priv->dvb_amplitude = cfg->dvb_amplitude; - priv->set_smoothedcvbs = cfg->set_smoothedcvbs; - } - - /* Check if firmware has been loaded. It is possible that another - instance of the driver has loaded the firmware. - */ - - if (instance == 1) { - if (xc4000_readreg(priv, XREG_PRODUCT_ID, &id) != 0) - goto fail; - } else { - id = ((priv->cur_fw.type & BASE) != 0 ? - priv->hwmodel : XC_PRODUCT_ID_FW_NOT_LOADED); - } - - switch (id) { - case XC_PRODUCT_ID_XC4000: - case XC_PRODUCT_ID_XC4100: - printk(KERN_INFO - "xc4000: Successfully identified at address 0x%02x\n", - cfg->i2c_address); - printk(KERN_INFO - "xc4000: Firmware has been loaded previously\n"); - break; - case XC_PRODUCT_ID_FW_NOT_LOADED: - printk(KERN_INFO - "xc4000: Successfully identified at address 0x%02x\n", - cfg->i2c_address); - printk(KERN_INFO - "xc4000: Firmware has not been loaded previously\n"); - break; - default: - printk(KERN_ERR - "xc4000: Device not found at addr 0x%02x (0x%x)\n", - cfg->i2c_address, id); - goto fail; - } - - mutex_unlock(&xc4000_list_mutex); - - memcpy(&fe->ops.tuner_ops, &xc4000_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - if (instance == 1) { - int ret; - mutex_lock(&priv->lock); - ret = xc4000_fwupload(fe); - mutex_unlock(&priv->lock); - if (ret != 0) - goto fail2; - } - - return fe; -fail: - mutex_unlock(&xc4000_list_mutex); -fail2: - xc4000_release(fe); - return NULL; -} -EXPORT_SYMBOL(xc4000_attach); - -MODULE_AUTHOR("Steven Toth, Davide Ferri"); -MODULE_DESCRIPTION("Xceive xc4000 silicon tuner driver"); -MODULE_LICENSE("GPL"); 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 - * - * 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 - -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 dc93cf338f36..000000000000 --- a/drivers/media/common/tuners/xc5000.c +++ /dev/null @@ -1,1366 +0,0 @@ -/* - * Driver for Xceive XC5000 "QAM/8VSB single chip tuner" - * - * Copyright (c) 2007 Xceive Corporation - * Copyright (c) 2007 Steven Toth - * Copyright (c) 2009 Devin Heitmueller - * - * 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 -#include -#include -#include -#include -#include - -#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; - u16 pll_register_no; - u8 init_status_supported; - u8 fw_checksum_supported; -}; - -/* 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 -#define XREG_TOTALGAIN 0x0F -#define XREG_FW_CHECKSUM 0x12 -#define XREG_INIT_STATUS 0x13 - -/* - 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; - u16 pll_reg; - u8 init_status_supported; - u8 fw_checksum_supported; -}; - -#define XC5000A_FIRMWARE "dvb-fe-xc5000-1.6.114.fw" -static const struct xc5000_fw_cfg xc5000a_1_6_114 = { - .name = XC5000A_FIRMWARE, - .size = 12401, - .pll_reg = 0x806c, -}; - -#define XC5000C_FIRMWARE "dvb-fe-xc5000c-4.1.30.7.fw" -static const struct xc5000_fw_cfg xc5000c_41_024_5 = { - .name = XC5000C_FIRMWARE, - .size = 16497, - .pll_reg = 0x13, - .init_status_supported = 1, - .fw_checksum_supported = 1, -}; - -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, int force); -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, ®Data); - 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, ®Data); - 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 int xc_get_analogsnr(struct xc5000_priv *priv, u16 *snr) -{ - return xc5000_readreg(priv, XREG_SNR, snr); -} - -static int xc_get_totalgain(struct xc5000_priv *priv, u16 *totalgain) -{ - return xc5000_readreg(priv, XREG_TOTALGAIN, totalgain); -} - -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); - priv->pll_register_no = desired_fw->pll_reg; - priv->init_status_supported = desired_fw->init_status_supported; - priv->fw_checksum_supported = desired_fw->fw_checksum_supported; - - /* 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; - u16 snr; - u16 totalgain; - u8 hw_majorversion = 0, hw_minorversion = 0; - u8 fw_majorversion = 0, fw_minorversion = 0; - u16 fw_buildversion = 0; - u16 regval; - - /* 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%d.%d, FW: V %d.%d.%d\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 & 0x07); - - xc_get_analogsnr(priv, &snr); - dprintk(1, "*** Unweighted analog SNR = %d dB\n", snr & 0x3f); - - xc_get_totalgain(priv, &totalgain); - dprintk(1, "*** Total gain = %d.%d dB\n", totalgain / 256, - (totalgain % 256) * 100 / 256); - - if (priv->pll_register_no) { - xc5000_readreg(priv, priv->pll_register_no, ®val); - dprintk(1, "*** PLL lock status = 0x%04x\n", regval); - } -} - -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 (xc_load_fw_and_init_tuner(fe, 0) != 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_ISDBT: - /* All ISDB-T are currently for 6 MHz bw */ - if (!bw) - bw = 6000000; - /* fall to OFDM handling */ - case SYS_DMBTH: - 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; - u16 pll_lock_status; - 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); - - if (priv->pll_register_no != 0) { - msleep(20); - xc5000_readreg(priv, priv->pll_register_no, &pll_lock_status); - if (pll_lock_status > 63) { - /* PLL is unlocked, force reload of the firmware */ - dprintk(1, "xc5000: PLL not locked (0x%x). Reloading...\n", - pll_lock_status); - if (xc_load_fw_and_init_tuner(fe, 1) != XC_RESULT_SUCCESS) { - printk(KERN_ERR "xc5000: Unable to reload fw\n"); - return -EREMOTEIO; - } - goto tune_channel; - } - } - - 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 (xc_load_fw_and_init_tuner(fe, 0) != 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, int force) -{ - struct xc5000_priv *priv = fe->tuner_priv; - int ret = XC_RESULT_SUCCESS; - u16 pll_lock_status; - u16 fw_ck; - - if (force || xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { - -fw_retry: - - ret = xc5000_fwupload(fe); - if (ret != XC_RESULT_SUCCESS) - return ret; - - msleep(20); - - if (priv->fw_checksum_supported) { - if (xc5000_readreg(priv, XREG_FW_CHECKSUM, &fw_ck) - != XC_RESULT_SUCCESS) { - dprintk(1, "%s() FW checksum reading failed.\n", - __func__); - goto fw_retry; - } - - if (fw_ck == 0) { - dprintk(1, "%s() FW checksum failed = 0x%04x\n", - __func__, fw_ck); - goto fw_retry; - } - } - - /* Start the tuner self-calibration process */ - ret |= xc_initialize(priv); - - if (ret != XC_RESULT_SUCCESS) - goto fw_retry; - - /* 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); - - if (priv->init_status_supported) { - if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck) != XC_RESULT_SUCCESS) { - dprintk(1, "%s() FW failed reading init status.\n", - __func__); - goto fw_retry; - } - - if (fw_ck == 0) { - dprintk(1, "%s() FW init status failed = 0x%04x\n", __func__, fw_ck); - goto fw_retry; - } - } - - if (priv->pll_register_no) { - xc5000_readreg(priv, priv->pll_register_no, - &pll_lock_status); - if (pll_lock_status > 63) { - /* PLL is unlocked, force reload of the firmware */ - printk(KERN_ERR "xc5000: PLL not running after fwload.\n"); - goto fw_retry; - } - } - - /* 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, 0) != 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"); -MODULE_FIRMWARE(XC5000A_FIRMWARE); -MODULE_FIRMWARE(XC5000C_FIRMWARE); 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 - * - * 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 - -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/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index a378c5293764..208bc496b90a 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -3,7 +3,7 @@ # ccflags-y += -I$(srctree)/drivers/media/dvb-core/ -ccflags-y += -I$(srctree)/drivers/media/common/tuners/ +ccflags-y += -I$(srctree)/drivers/media/tuners/ stb0899-objs = stb0899_drv.o stb0899_algo.o stv0900-objs = stv0900_core.o stv0900_sw.o diff --git a/drivers/media/pci/bt8xx/Makefile b/drivers/media/pci/bt8xx/Makefile index 36591ae505f4..c008d0c135d6 100644 --- a/drivers/media/pci/bt8xx/Makefile +++ b/drivers/media/pci/bt8xx/Makefile @@ -3,4 +3,4 @@ obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends ccflags-y += -Idrivers/media/video/bt8xx -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/pci/ddbridge/Makefile b/drivers/media/pci/ddbridge/Makefile index 9d083c98ce58..7446c8b677b5 100644 --- a/drivers/media/pci/ddbridge/Makefile +++ b/drivers/media/pci/ddbridge/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o ccflags-y += -Idrivers/media/dvb-core/ ccflags-y += -Idrivers/media/dvb-frontends/ -ccflags-y += -Idrivers/media/common/tuners/ +ccflags-y += -Idrivers/media/tuners/ # For the staging CI driver cxd2099 ccflags-y += -Idrivers/staging/media/cxd2099/ diff --git a/drivers/media/pci/ngene/Makefile b/drivers/media/pci/ngene/Makefile index 63997089f9d1..5c0b5d6b9d69 100644 --- a/drivers/media/pci/ngene/Makefile +++ b/drivers/media/pci/ngene/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_DVB_NGENE) += ngene.o ccflags-y += -Idrivers/media/dvb-core/ ccflags-y += -Idrivers/media/dvb-frontends/ -ccflags-y += -Idrivers/media/common/tuners/ +ccflags-y += -Idrivers/media/tuners/ # For the staging CI driver cxd2099 ccflags-y += -Idrivers/staging/media/cxd2099/ diff --git a/drivers/media/pci/ttpci/Makefile b/drivers/media/pci/ttpci/Makefile index 22a235f3cc48..98905963ff08 100644 --- a/drivers/media/pci/ttpci/Makefile +++ b/drivers/media/pci/ttpci/Makefile @@ -18,4 +18,4 @@ obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig new file mode 100644 index 000000000000..94c6ff7a5da3 --- /dev/null +++ b/drivers/media/tuners/Kconfig @@ -0,0 +1,243 @@ +config MEDIA_ATTACH + bool "Load and attach frontend and tuner driver modules as needed" + depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT + depends on MODULES + default y if !EXPERT + 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 + depends on (MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT) && I2C + default y + 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 && MEDIA_RADIO_SUPPORT && EXPERIMENTAL + select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMISE && MEDIA_RADIO_SUPPORT + 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 + depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT + +config MEDIA_TUNER_SIMPLE + tristate "Simple tuner support" + depends on MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && 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 MEDIA_SUPPORT && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + NXP TDA18218 silicon tuner driver. + +config MEDIA_TUNER_FC0011 + tristate "Fitipower FC0011 silicon tuner" + depends on MEDIA_SUPPORT && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Fitipower FC0011 silicon tuner driver. + +config MEDIA_TUNER_FC0012 + tristate "Fitipower FC0012 silicon tuner" + depends on MEDIA_SUPPORT && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Fitipower FC0012 silicon tuner driver. + +config MEDIA_TUNER_FC0013 + tristate "Fitipower FC0013 silicon tuner" + depends on MEDIA_SUPPORT && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Fitipower FC0013 silicon tuner driver. + +config MEDIA_TUNER_TDA18212 + tristate "NXP TDA18212 silicon tuner" + depends on MEDIA_SUPPORT && 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 MEDIA_SUPPORT && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Infineon TUA 9001 silicon tuner driver. +endmenu diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile new file mode 100644 index 000000000000..112aeee90202 --- /dev/null +++ b/drivers/media/tuners/Makefile @@ -0,0 +1,37 @@ +# +# 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-core +ccflags-y += -I$(srctree)/drivers/media/dvb-frontends diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c new file mode 100644 index 000000000000..e4882546c283 --- /dev/null +++ b/drivers/media/tuners/fc0011.c @@ -0,0 +1,524 @@ +/* + * Fitipower FC0011 tuner driver + * + * Copyright (C) 2012 Michael Buesch + * + * Derived from FC0012 tuner driver: + * Copyright (C) 2012 Hans-Frieder Vogt + * + * 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 "fc0011.h" + + +/* Tuner registers */ +enum { + FC11_REG_0, + FC11_REG_FA, /* FA */ + FC11_REG_FP, /* FP */ + FC11_REG_XINHI, /* XIN high 8 bit */ + FC11_REG_XINLO, /* XIN low 8 bit */ + FC11_REG_VCO, /* VCO */ + FC11_REG_VCOSEL, /* VCO select */ + FC11_REG_7, /* Unknown tuner reg 7 */ + FC11_REG_8, /* Unknown tuner reg 8 */ + FC11_REG_9, + FC11_REG_10, /* Unknown tuner reg 10 */ + FC11_REG_11, /* Unknown tuner reg 11 */ + FC11_REG_12, + FC11_REG_RCCAL, /* RC calibrate */ + FC11_REG_VCOCAL, /* VCO calibrate */ + FC11_REG_15, + FC11_REG_16, /* Unknown tuner reg 16 */ + FC11_REG_17, + + FC11_NR_REGS, /* Number of registers */ +}; + +enum FC11_REG_VCOSEL_bits { + FC11_VCOSEL_2 = 0x08, /* VCO select 2 */ + FC11_VCOSEL_1 = 0x10, /* VCO select 1 */ + FC11_VCOSEL_CLKOUT = 0x20, /* Fix clock out */ + FC11_VCOSEL_BW7M = 0x40, /* 7MHz bw */ + FC11_VCOSEL_BW6M = 0x80, /* 6MHz bw */ +}; + +enum FC11_REG_RCCAL_bits { + FC11_RCCAL_FORCE = 0x10, /* force */ +}; + +enum FC11_REG_VCOCAL_bits { + FC11_VCOCAL_RUN = 0, /* VCO calibration run */ + FC11_VCOCAL_VALUEMASK = 0x3F, /* VCO calibration value mask */ + FC11_VCOCAL_OK = 0x40, /* VCO calibration Ok */ + FC11_VCOCAL_RESET = 0x80, /* VCO calibration reset */ +}; + + +struct fc0011_priv { + struct i2c_adapter *i2c; + u8 addr; + + u32 frequency; + u32 bandwidth; +}; + + +static int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = { reg, val }; + struct i2c_msg msg = { .addr = priv->addr, + .flags = 0, .buf = buf, .len = 2 }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + dev_err(&priv->i2c->dev, + "I2C write reg failed, reg: %02x, val: %02x\n", + reg, val); + return -EIO; + } + + return 0; +} + +static int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val) +{ + u8 dummy; + struct i2c_msg msg[2] = { + { .addr = priv->addr, + .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->addr, + .flags = I2C_M_RD, .buf = val ? : &dummy, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + dev_err(&priv->i2c->dev, + "I2C read failed, reg: %02x\n", reg); + return -EIO; + } + + return 0; +} + +static int fc0011_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + + return 0; +} + +static int fc0011_init(struct dvb_frontend *fe) +{ + struct fc0011_priv *priv = fe->tuner_priv; + int err; + + if (WARN_ON(!fe->callback)) + return -EINVAL; + + err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC0011_FE_CALLBACK_POWER, priv->addr); + if (err) { + dev_err(&priv->i2c->dev, "Power-on callback failed\n"); + return err; + } + err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC0011_FE_CALLBACK_RESET, priv->addr); + if (err) { + dev_err(&priv->i2c->dev, "Reset callback failed\n"); + return err; + } + + return 0; +} + +/* Initiate VCO calibration */ +static int fc0011_vcocal_trigger(struct fc0011_priv *priv) +{ + int err; + + err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET); + if (err) + return err; + err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); + if (err) + return err; + + return 0; +} + +/* Read VCO calibration value */ +static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value) +{ + int err; + + err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); + if (err) + return err; + usleep_range(10000, 20000); + err = fc0011_readreg(priv, FC11_REG_VCOCAL, value); + if (err) + return err; + + return 0; +} + +static int fc0011_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct fc0011_priv *priv = fe->tuner_priv; + int err; + unsigned int i, vco_retries; + u32 freq = p->frequency / 1000; + u32 bandwidth = p->bandwidth_hz / 1000; + u32 fvco, xin, xdiv, xdivr; + u16 frac; + u8 fa, fp, vco_sel, vco_cal; + u8 regs[FC11_NR_REGS] = { }; + + regs[FC11_REG_7] = 0x0F; + regs[FC11_REG_8] = 0x3E; + regs[FC11_REG_10] = 0xB8; + regs[FC11_REG_11] = 0x80; + regs[FC11_REG_RCCAL] = 0x04; + err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); + err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); + err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); + err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); + err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); + if (err) + return -EIO; + + /* Set VCO freq and VCO div */ + if (freq < 54000) { + fvco = freq * 64; + regs[FC11_REG_VCO] = 0x82; + } else if (freq < 108000) { + fvco = freq * 32; + regs[FC11_REG_VCO] = 0x42; + } else if (freq < 216000) { + fvco = freq * 16; + regs[FC11_REG_VCO] = 0x22; + } else if (freq < 432000) { + fvco = freq * 8; + regs[FC11_REG_VCO] = 0x12; + } else { + fvco = freq * 4; + regs[FC11_REG_VCO] = 0x0A; + } + + /* Calc XIN. The PLL reference frequency is 18 MHz. */ + xdiv = fvco / 18000; + frac = fvco - xdiv * 18000; + frac = (frac << 15) / 18000; + if (frac >= 16384) + frac += 32786; + if (!frac) + xin = 0; + else if (frac < 511) + xin = 512; + else if (frac < 65026) + xin = frac; + else + xin = 65024; + regs[FC11_REG_XINHI] = xin >> 8; + regs[FC11_REG_XINLO] = xin; + + /* Calc FP and FA */ + xdivr = xdiv; + if (fvco - xdiv * 18000 >= 9000) + xdivr += 1; /* round */ + fp = xdivr / 8; + fa = xdivr - fp * 8; + if (fa < 2) { + fp -= 1; + fa += 8; + } + if (fp > 0x1F) { + fp &= 0x1F; + fa &= 0xF; + } + if (fa >= fp) { + dev_warn(&priv->i2c->dev, + "fa %02X >= fp %02X, but trying to continue\n", + (unsigned int)(u8)fa, (unsigned int)(u8)fp); + } + regs[FC11_REG_FA] = fa; + regs[FC11_REG_FP] = fp; + + /* Select bandwidth */ + switch (bandwidth) { + case 8000: + break; + case 7000: + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M; + break; + default: + dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. " + "Using 6000 kHz.\n", + bandwidth); + bandwidth = 6000; + /* fallthrough */ + case 6000: + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M; + break; + } + + /* Pre VCO select */ + if (fvco < 2320000) { + vco_sel = 0; + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + } else if (fvco < 3080000) { + vco_sel = 1; + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + } else { + vco_sel = 2; + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; + } + + /* Fix for low freqs */ + if (freq < 45000) { + regs[FC11_REG_FA] = 0x6; + regs[FC11_REG_FP] = 0x11; + } + + /* Clock out fix */ + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT; + + /* Write the cached registers */ + for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) { + err = fc0011_writereg(priv, i, regs[i]); + if (err) + return err; + } + + /* VCO calibration */ + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + err = fc0011_vcocal_read(priv, &vco_cal); + if (err) + return err; + vco_retries = 0; + while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 3) { + /* Reset the tuner and try again */ + err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC0011_FE_CALLBACK_RESET, priv->addr); + if (err) { + dev_err(&priv->i2c->dev, "Failed to reset tuner\n"); + return err; + } + /* Reinit tuner config */ + err = 0; + for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) + err |= fc0011_writereg(priv, i, regs[i]); + err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); + err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); + err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); + err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); + err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); + if (err) + return -EIO; + /* VCO calibration */ + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + err = fc0011_vcocal_read(priv, &vco_cal); + if (err) + return err; + vco_retries++; + } + if (!(vco_cal & FC11_VCOCAL_OK)) { + dev_err(&priv->i2c->dev, + "Failed to read VCO calibration value (got %02X)\n", + (unsigned int)vco_cal); + return -EIO; + } + vco_cal &= FC11_VCOCAL_VALUEMASK; + + switch (vco_sel) { + case 0: + if (vco_cal < 8) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } else { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + } + break; + case 1: + if (vco_cal < 5) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } else if (vco_cal <= 48) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + } else { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } + break; + case 2: + if (vco_cal > 53) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } else { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + } + break; + } + err = fc0011_vcocal_read(priv, NULL); + if (err) + return err; + usleep_range(10000, 50000); + + err = fc0011_readreg(priv, FC11_REG_RCCAL, ®s[FC11_REG_RCCAL]); + if (err) + return err; + regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE; + err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); + if (err) + return err; + err = fc0011_writereg(priv, FC11_REG_16, 0xB); + if (err) + return err; + + dev_dbg(&priv->i2c->dev, "Tuned to " + "fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X " + "vcocal=%02X(%u) bw=%u\n", + (unsigned int)regs[FC11_REG_FA], + (unsigned int)regs[FC11_REG_FP], + (unsigned int)regs[FC11_REG_XINHI], + (unsigned int)regs[FC11_REG_XINLO], + (unsigned int)regs[FC11_REG_VCO], + (unsigned int)regs[FC11_REG_VCOSEL], + (unsigned int)vco_cal, vco_retries, + (unsigned int)bandwidth); + + priv->frequency = p->frequency; + priv->bandwidth = p->bandwidth_hz; + + return 0; +} + +static int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct fc0011_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency; + + return 0; +} + +static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + *frequency = 0; + + return 0; +} + +static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct fc0011_priv *priv = fe->tuner_priv; + + *bandwidth = priv->bandwidth; + + return 0; +} + +static const struct dvb_tuner_ops fc0011_tuner_ops = { + .info = { + .name = "Fitipower FC0011", + + .frequency_min = 45000000, + .frequency_max = 1000000000, + }, + + .release = fc0011_release, + .init = fc0011_init, + + .set_params = fc0011_set_params, + + .get_frequency = fc0011_get_frequency, + .get_if_frequency = fc0011_get_if_frequency, + .get_bandwidth = fc0011_get_bandwidth, +}; + +struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct fc0011_config *config) +{ + struct fc0011_priv *priv; + + priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL); + if (!priv) + return NULL; + + priv->i2c = i2c; + priv->addr = config->i2c_address; + + fe->tuner_priv = priv; + fe->ops.tuner_ops = fc0011_tuner_ops; + + dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n"); + + return fe; +} +EXPORT_SYMBOL(fc0011_attach); + +MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver"); +MODULE_AUTHOR("Michael Buesch "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/fc0011.h b/drivers/media/tuners/fc0011.h new file mode 100644 index 000000000000..0ee581f122d2 --- /dev/null +++ b/drivers/media/tuners/fc0011.h @@ -0,0 +1,41 @@ +#ifndef LINUX_FC0011_H_ +#define LINUX_FC0011_H_ + +#include "dvb_frontend.h" + + +/** struct fc0011_config - fc0011 hardware config + * + * @i2c_address: I2C bus address. + */ +struct fc0011_config { + u8 i2c_address; +}; + +/** enum fc0011_fe_callback_commands - Frontend callbacks + * + * @FC0011_FE_CALLBACK_POWER: Power on tuner hardware. + * @FC0011_FE_CALLBACK_RESET: Request a tuner reset. + */ +enum fc0011_fe_callback_commands { + FC0011_FE_CALLBACK_POWER, + FC0011_FE_CALLBACK_RESET, +}; + +#if defined(CONFIG_MEDIA_TUNER_FC0011) ||\ + defined(CONFIG_MEDIA_TUNER_FC0011_MODULE) +struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct fc0011_config *config); +#else +static inline +struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct fc0011_config *config) +{ + dev_err(&i2c->dev, "fc0011 driver disabled in Kconfig\n"); + return NULL; +} +#endif + +#endif /* LINUX_FC0011_H_ */ diff --git a/drivers/media/tuners/fc0012-priv.h b/drivers/media/tuners/fc0012-priv.h new file mode 100644 index 000000000000..4577c917e616 --- /dev/null +++ b/drivers/media/tuners/fc0012-priv.h @@ -0,0 +1,43 @@ +/* + * Fitipower FC0012 tuner driver - private includes + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * 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/tuners/fc0012.c b/drivers/media/tuners/fc0012.c new file mode 100644 index 000000000000..308135abd54c --- /dev/null +++ b/drivers/media/tuners/fc0012.c @@ -0,0 +1,467 @@ +/* + * Fitipower FC0012 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * 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 "fc0012.h" +#include "fc0012-priv.h" + +static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = {reg, val}; + struct i2c_msg msg = { + .addr = priv->addr, .flags = 0, .buf = buf, .len = 2 + }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + err("I2C write reg failed, reg: %02x, val: %02x", reg, val); + return -EREMOTEIO; + } + return 0; +} + +static int fc0012_readreg(struct fc0012_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + err("I2C read reg failed, reg: %02x", reg); + return -EREMOTEIO; + } + return 0; +} + +static int fc0012_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int fc0012_init(struct dvb_frontend *fe) +{ + struct fc0012_priv *priv = fe->tuner_priv; + int i, ret = 0; + unsigned char reg[] = { + 0x00, /* dummy reg. 0 */ + 0x05, /* reg. 0x01 */ + 0x10, /* reg. 0x02 */ + 0x00, /* reg. 0x03 */ + 0x00, /* reg. 0x04 */ + 0x0f, /* reg. 0x05: may also be 0x0a */ + 0x00, /* reg. 0x06: divider 2, VCO slow */ + 0x00, /* reg. 0x07: may also be 0x0f */ + 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, + Loop Bw 1/8 */ + 0x6e, /* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */ + 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ + 0x82, /* reg. 0x0b: Output Clock is same as clock frequency, + may also be 0x83 */ + 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ + 0x02, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, 0x02 for DVB-T */ + 0x00, /* reg. 0x0e */ + 0x00, /* reg. 0x0f */ + 0x00, /* reg. 0x10: may also be 0x0d */ + 0x00, /* reg. 0x11 */ + 0x1f, /* reg. 0x12: Set to maximum gain */ + 0x08, /* reg. 0x13: Set to Middle Gain: 0x08, + Low Gain: 0x00, High Gain: 0x10, enable IX2: 0x80 */ + 0x00, /* reg. 0x14 */ + 0x04, /* reg. 0x15: Enable LNA COMPS */ + }; + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + case FC_XTAL_28_8_MHZ: + reg[0x07] |= 0x20; + break; + case FC_XTAL_36_MHZ: + default: + break; + } + + if (priv->dual_master) + reg[0x0c] |= 0x02; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + for (i = 1; i < sizeof(reg); i++) { + ret = fc0012_writereg(priv, i, reg[i]); + if (ret) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (ret) + err("fc0012_writereg failed: %d", ret); + + return ret; +} + +static int fc0012_sleep(struct dvb_frontend *fe) +{ + /* nothing to do here */ + return 0; +} + +static int fc0012_set_params(struct dvb_frontend *fe) +{ + struct fc0012_priv *priv = fe->tuner_priv; + int i, ret = 0; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 freq = p->frequency / 1000; + u32 delsys = p->delivery_system; + unsigned char reg[7], am, pm, multi, tmp; + unsigned long f_vco; + unsigned short xtal_freq_khz_2, xin, xdiv; + int vco_select = false; + + if (fe->callback) { + ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1)); + if (ret) + goto exit; + } + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + xtal_freq_khz_2 = 27000 / 2; + break; + case FC_XTAL_36_MHZ: + xtal_freq_khz_2 = 36000 / 2; + break; + case FC_XTAL_28_8_MHZ: + default: + xtal_freq_khz_2 = 28800 / 2; + break; + } + + /* select frequency divider and the frequency of VCO */ + if (freq < 37084) { /* freq * 96 < 3560000 */ + multi = 96; + reg[5] = 0x82; + reg[6] = 0x00; + } else if (freq < 55625) { /* freq * 64 < 3560000 */ + multi = 64; + reg[5] = 0x82; + reg[6] = 0x02; + } else if (freq < 74167) { /* freq * 48 < 3560000 */ + multi = 48; + reg[5] = 0x42; + reg[6] = 0x00; + } else if (freq < 111250) { /* freq * 32 < 3560000 */ + multi = 32; + reg[5] = 0x42; + reg[6] = 0x02; + } else if (freq < 148334) { /* freq * 24 < 3560000 */ + multi = 24; + reg[5] = 0x22; + reg[6] = 0x00; + } else if (freq < 222500) { /* freq * 16 < 3560000 */ + multi = 16; + reg[5] = 0x22; + reg[6] = 0x02; + } else if (freq < 296667) { /* freq * 12 < 3560000 */ + multi = 12; + reg[5] = 0x12; + reg[6] = 0x00; + } else if (freq < 445000) { /* freq * 8 < 3560000 */ + multi = 8; + reg[5] = 0x12; + reg[6] = 0x02; + } else if (freq < 593334) { /* freq * 6 < 3560000 */ + multi = 6; + reg[5] = 0x0a; + reg[6] = 0x00; + } else { + multi = 4; + reg[5] = 0x0a; + reg[6] = 0x02; + } + + f_vco = freq * multi; + + if (f_vco >= 3060000) { + reg[6] |= 0x08; + vco_select = true; + } + + if (freq >= 45000) { + /* From divided value (XDIV) determined the FA and FP value */ + xdiv = (unsigned short)(f_vco / xtal_freq_khz_2); + if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2)) + xdiv++; + + pm = (unsigned char)(xdiv / 8); + am = (unsigned char)(xdiv - (8 * pm)); + + if (am < 2) { + reg[1] = am + 8; + reg[2] = pm - 1; + } else { + reg[1] = am; + reg[2] = pm; + } + } else { + /* fix for frequency less than 45 MHz */ + reg[1] = 0x06; + reg[2] = 0x11; + } + + /* fix clock out */ + reg[6] |= 0x20; + + /* From VCO frequency determines the XIN ( fractional part of Delta + Sigma PLL) and divided value (XDIV) */ + xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2); + xin = (xin << 15) / xtal_freq_khz_2; + if (xin >= 16384) + xin += 32768; + + reg[3] = xin >> 8; /* xin with 9 bit resolution */ + reg[4] = xin & 0xff; + + if (delsys == SYS_DVBT) { + reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ + switch (p->bandwidth_hz) { + case 6000000: + reg[6] |= 0x80; + break; + case 7000000: + reg[6] |= 0x40; + break; + case 8000000: + default: + break; + } + } else { + err("%s: modulation type not supported!", __func__); + return -EINVAL; + } + + /* modified for Realtek demod */ + reg[5] |= 0x07; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + for (i = 1; i <= 6; i++) { + ret = fc0012_writereg(priv, i, reg[i]); + if (ret) + goto exit; + } + + /* VCO Calibration */ + ret = fc0012_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + + /* VCO Re-Calibration if needed */ + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + + if (!ret) { + msleep(10); + ret = fc0012_readreg(priv, 0x0e, &tmp); + } + if (ret) + goto exit; + + /* vco selection */ + tmp &= 0x3f; + + if (vco_select) { + if (tmp > 0x3c) { + reg[6] &= ~0x08; + ret = fc0012_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + } + } else { + if (tmp < 0x02) { + reg[6] |= 0x08; + ret = fc0012_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + } + } + + priv->frequency = p->frequency; + priv->bandwidth = p->bandwidth_hz; + +exit: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} + +static int fc0012_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct fc0012_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int fc0012_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + /* CHECK: always ? */ + *frequency = 0; + return 0; +} + +static int fc0012_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct fc0012_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +#define INPUT_ADC_LEVEL -8 + +static int fc0012_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct fc0012_priv *priv = fe->tuner_priv; + int ret; + unsigned char tmp; + int int_temp, lna_gain, int_lna, tot_agc_gain, power; + const int fc0012_lna_gain_table[] = { + /* low gain */ + -63, -58, -99, -73, + -63, -65, -54, -60, + /* middle gain */ + 71, 70, 68, 67, + 65, 63, 61, 58, + /* high gain */ + 197, 191, 188, 186, + 184, 182, 181, 179, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + ret = fc0012_writereg(priv, 0x12, 0x00); + if (ret) + goto err; + + ret = fc0012_readreg(priv, 0x12, &tmp); + if (ret) + goto err; + int_temp = tmp; + + ret = fc0012_readreg(priv, 0x13, &tmp); + if (ret) + goto err; + lna_gain = tmp & 0x1f; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (lna_gain < ARRAY_SIZE(fc0012_lna_gain_table)) { + int_lna = fc0012_lna_gain_table[lna_gain]; + tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 + + (int_temp & 0x1f)) * 2; + power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10; + + if (power >= 45) + *strength = 255; /* 100% */ + else if (power < -95) + *strength = 0; + else + *strength = (power + 95) * 255 / 140; + + *strength |= *strength << 8; + } else { + ret = -1; + } + + goto exit; + +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ +exit: + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} + +static const struct dvb_tuner_ops fc0012_tuner_ops = { + .info = { + .name = "Fitipower FC0012", + + .frequency_min = 37000000, /* estimate */ + .frequency_max = 862000000, /* estimate */ + .frequency_step = 0, + }, + + .release = fc0012_release, + + .init = fc0012_init, + .sleep = fc0012_sleep, + + .set_params = fc0012_set_params, + + .get_frequency = fc0012_get_frequency, + .get_if_frequency = fc0012_get_if_frequency, + .get_bandwidth = fc0012_get_bandwidth, + + .get_rf_strength = fc0012_get_rf_strength, +}; + +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) +{ + struct fc0012_priv *priv = NULL; + + priv = kzalloc(sizeof(struct fc0012_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c = i2c; + priv->dual_master = dual_master; + priv->addr = i2c_address; + priv->xtal_freq = xtal_freq; + + info("Fitipower FC0012 successfully attached."); + + fe->tuner_priv = priv; + + memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + return fe; +} +EXPORT_SYMBOL(fc0012_attach); + +MODULE_DESCRIPTION("Fitipower FC0012 silicon tuner driver"); +MODULE_AUTHOR("Hans-Frieder Vogt "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.6"); diff --git a/drivers/media/tuners/fc0012.h b/drivers/media/tuners/fc0012.h new file mode 100644 index 000000000000..4dbd5efe8845 --- /dev/null +++ b/drivers/media/tuners/fc0012.h @@ -0,0 +1,44 @@ +/* + * Fitipower FC0012 tuner driver - include + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * 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/tuners/fc0013-priv.h b/drivers/media/tuners/fc0013-priv.h new file mode 100644 index 000000000000..bfd49dedea22 --- /dev/null +++ b/drivers/media/tuners/fc0013-priv.h @@ -0,0 +1,44 @@ +/* + * Fitipower FC0013 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * 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/tuners/fc0013.c b/drivers/media/tuners/fc0013.c new file mode 100644 index 000000000000..bd8f0f1e8f3b --- /dev/null +++ b/drivers/media/tuners/fc0013.c @@ -0,0 +1,634 @@ +/* + * Fitipower FC0013 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt + * partially based on driver code from Fitipower + * Copyright (C) 2010 Fitipower Integrated Technology 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. + * + */ + +#include "fc0013.h" +#include "fc0013-priv.h" + +static int fc0013_writereg(struct fc0013_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = {reg, val}; + struct i2c_msg msg = { + .addr = priv->addr, .flags = 0, .buf = buf, .len = 2 + }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + err("I2C write reg failed, reg: %02x, val: %02x", reg, val); + return -EREMOTEIO; + } + return 0; +} + +static int fc0013_readreg(struct fc0013_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + err("I2C read reg failed, reg: %02x", reg); + return -EREMOTEIO; + } + return 0; +} + +static int fc0013_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int fc0013_init(struct dvb_frontend *fe) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int i, ret = 0; + unsigned char reg[] = { + 0x00, /* reg. 0x00: dummy */ + 0x09, /* reg. 0x01 */ + 0x16, /* reg. 0x02 */ + 0x00, /* reg. 0x03 */ + 0x00, /* reg. 0x04 */ + 0x17, /* reg. 0x05 */ + 0x02, /* reg. 0x06 */ + 0x0a, /* reg. 0x07: CHECK */ + 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, + Loop Bw 1/8 */ + 0x6f, /* reg. 0x09: enable LoopThrough */ + 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ + 0x82, /* reg. 0x0b: CHECK */ + 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ + 0x01, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */ + 0x00, /* reg. 0x0e */ + 0x00, /* reg. 0x0f */ + 0x00, /* reg. 0x10 */ + 0x00, /* reg. 0x11 */ + 0x00, /* reg. 0x12 */ + 0x00, /* reg. 0x13 */ + 0x50, /* reg. 0x14: DVB-t High Gain, UHF. + Middle Gain: 0x48, Low Gain: 0x40 */ + 0x01, /* reg. 0x15 */ + }; + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + case FC_XTAL_28_8_MHZ: + reg[0x07] |= 0x20; + break; + case FC_XTAL_36_MHZ: + default: + break; + } + + if (priv->dual_master) + reg[0x0c] |= 0x02; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + for (i = 1; i < sizeof(reg); i++) { + ret = fc0013_writereg(priv, i, reg[i]); + if (ret) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (ret) + err("fc0013_writereg failed: %d", ret); + + return ret; +} + +static int fc0013_sleep(struct dvb_frontend *fe) +{ + /* nothing to do here */ + return 0; +} + +int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int ret; + u8 rc_cal; + int val; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + /* push rc_cal value, get rc_cal value */ + ret = fc0013_writereg(priv, 0x10, 0x00); + if (ret) + goto error_out; + + /* get rc_cal value */ + ret = fc0013_readreg(priv, 0x10, &rc_cal); + if (ret) + goto error_out; + + rc_cal &= 0x0f; + + val = (int)rc_cal + rc_val; + + /* forcing rc_cal */ + ret = fc0013_writereg(priv, 0x0d, 0x11); + if (ret) + goto error_out; + + /* modify rc_cal value */ + if (val > 15) + ret = fc0013_writereg(priv, 0x10, 0x0f); + else if (val < 0) + ret = fc0013_writereg(priv, 0x10, 0x00); + else + ret = fc0013_writereg(priv, 0x10, (u8)val); + +error_out: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + return ret; +} +EXPORT_SYMBOL(fc0013_rc_cal_add); + +int fc0013_rc_cal_reset(struct dvb_frontend *fe) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + ret = fc0013_writereg(priv, 0x0d, 0x01); + if (!ret) + ret = fc0013_writereg(priv, 0x10, 0x00); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + return ret; +} +EXPORT_SYMBOL(fc0013_rc_cal_reset); + +static int fc0013_set_vhf_track(struct fc0013_priv *priv, u32 freq) +{ + int ret; + u8 tmp; + + ret = fc0013_readreg(priv, 0x1d, &tmp); + if (ret) + goto error_out; + tmp &= 0xe3; + if (freq <= 177500) { /* VHF Track: 7 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c); + } else if (freq <= 184500) { /* VHF Track: 6 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x18); + } else if (freq <= 191500) { /* VHF Track: 5 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x14); + } else if (freq <= 198500) { /* VHF Track: 4 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x10); + } else if (freq <= 205500) { /* VHF Track: 3 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x0c); + } else if (freq <= 219500) { /* VHF Track: 2 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x08); + } else if (freq < 300000) { /* VHF Track: 1 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x04); + } else { /* UHF and GPS */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c); + } + if (ret) + goto error_out; +error_out: + return ret; +} + +static int fc0013_set_params(struct dvb_frontend *fe) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int i, ret = 0; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 freq = p->frequency / 1000; + u32 delsys = p->delivery_system; + unsigned char reg[7], am, pm, multi, tmp; + unsigned long f_vco; + unsigned short xtal_freq_khz_2, xin, xdiv; + int vco_select = false; + + if (fe->callback) { + ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1)); + if (ret) + goto exit; + } + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + xtal_freq_khz_2 = 27000 / 2; + break; + case FC_XTAL_36_MHZ: + xtal_freq_khz_2 = 36000 / 2; + break; + case FC_XTAL_28_8_MHZ: + default: + xtal_freq_khz_2 = 28800 / 2; + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + /* set VHF track */ + ret = fc0013_set_vhf_track(priv, freq); + if (ret) + goto exit; + + if (freq < 300000) { + /* enable VHF filter */ + ret = fc0013_readreg(priv, 0x07, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x07, tmp | 0x10); + if (ret) + goto exit; + + /* disable UHF & disable GPS */ + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x14, tmp & 0x1f); + if (ret) + goto exit; + } else if (freq <= 862000) { + /* disable VHF filter */ + ret = fc0013_readreg(priv, 0x07, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x07, tmp & 0xef); + if (ret) + goto exit; + + /* enable UHF & disable GPS */ + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x40); + if (ret) + goto exit; + } else { + /* disable VHF filter */ + ret = fc0013_readreg(priv, 0x07, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x07, tmp & 0xef); + if (ret) + goto exit; + + /* disable UHF & enable GPS */ + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x20); + if (ret) + goto exit; + } + + /* select frequency divider and the frequency of VCO */ + if (freq < 37084) { /* freq * 96 < 3560000 */ + multi = 96; + reg[5] = 0x82; + reg[6] = 0x00; + } else if (freq < 55625) { /* freq * 64 < 3560000 */ + multi = 64; + reg[5] = 0x02; + reg[6] = 0x02; + } else if (freq < 74167) { /* freq * 48 < 3560000 */ + multi = 48; + reg[5] = 0x42; + reg[6] = 0x00; + } else if (freq < 111250) { /* freq * 32 < 3560000 */ + multi = 32; + reg[5] = 0x82; + reg[6] = 0x02; + } else if (freq < 148334) { /* freq * 24 < 3560000 */ + multi = 24; + reg[5] = 0x22; + reg[6] = 0x00; + } else if (freq < 222500) { /* freq * 16 < 3560000 */ + multi = 16; + reg[5] = 0x42; + reg[6] = 0x02; + } else if (freq < 296667) { /* freq * 12 < 3560000 */ + multi = 12; + reg[5] = 0x12; + reg[6] = 0x00; + } else if (freq < 445000) { /* freq * 8 < 3560000 */ + multi = 8; + reg[5] = 0x22; + reg[6] = 0x02; + } else if (freq < 593334) { /* freq * 6 < 3560000 */ + multi = 6; + reg[5] = 0x0a; + reg[6] = 0x00; + } else if (freq < 950000) { /* freq * 4 < 3800000 */ + multi = 4; + reg[5] = 0x12; + reg[6] = 0x02; + } else { + multi = 2; + reg[5] = 0x0a; + reg[6] = 0x02; + } + + f_vco = freq * multi; + + if (f_vco >= 3060000) { + reg[6] |= 0x08; + vco_select = true; + } + + if (freq >= 45000) { + /* From divided value (XDIV) determined the FA and FP value */ + xdiv = (unsigned short)(f_vco / xtal_freq_khz_2); + if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2)) + xdiv++; + + pm = (unsigned char)(xdiv / 8); + am = (unsigned char)(xdiv - (8 * pm)); + + if (am < 2) { + reg[1] = am + 8; + reg[2] = pm - 1; + } else { + reg[1] = am; + reg[2] = pm; + } + } else { + /* fix for frequency less than 45 MHz */ + reg[1] = 0x06; + reg[2] = 0x11; + } + + /* fix clock out */ + reg[6] |= 0x20; + + /* From VCO frequency determines the XIN ( fractional part of Delta + Sigma PLL) and divided value (XDIV) */ + xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2); + xin = (xin << 15) / xtal_freq_khz_2; + if (xin >= 16384) + xin += 32768; + + reg[3] = xin >> 8; + reg[4] = xin & 0xff; + + if (delsys == SYS_DVBT) { + reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ + switch (p->bandwidth_hz) { + case 6000000: + reg[6] |= 0x80; + break; + case 7000000: + reg[6] |= 0x40; + break; + case 8000000: + default: + break; + } + } else { + err("%s: modulation type not supported!", __func__); + return -EINVAL; + } + + /* modified for Realtek demod */ + reg[5] |= 0x07; + + for (i = 1; i <= 6; i++) { + ret = fc0013_writereg(priv, i, reg[i]); + if (ret) + goto exit; + } + + ret = fc0013_readreg(priv, 0x11, &tmp); + if (ret) + goto exit; + if (multi == 64) + ret = fc0013_writereg(priv, 0x11, tmp | 0x04); + else + ret = fc0013_writereg(priv, 0x11, tmp & 0xfb); + if (ret) + goto exit; + + /* VCO Calibration */ + ret = fc0013_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + + /* VCO Re-Calibration if needed */ + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + + if (!ret) { + msleep(10); + ret = fc0013_readreg(priv, 0x0e, &tmp); + } + if (ret) + goto exit; + + /* vco selection */ + tmp &= 0x3f; + + if (vco_select) { + if (tmp > 0x3c) { + reg[6] &= ~0x08; + ret = fc0013_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + } + } else { + if (tmp < 0x02) { + reg[6] |= 0x08; + ret = fc0013_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + } + } + + priv->frequency = p->frequency; + priv->bandwidth = p->bandwidth_hz; + +exit: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} + +static int fc0013_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct fc0013_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int fc0013_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + /* always ? */ + *frequency = 0; + return 0; +} + +static int fc0013_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct fc0013_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +#define INPUT_ADC_LEVEL -8 + +static int fc0013_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int ret; + unsigned char tmp; + int int_temp, lna_gain, int_lna, tot_agc_gain, power; + const int fc0013_lna_gain_table[] = { + /* low gain */ + -63, -58, -99, -73, + -63, -65, -54, -60, + /* middle gain */ + 71, 70, 68, 67, + 65, 63, 61, 58, + /* high gain */ + 197, 191, 188, 186, + 184, 182, 181, 179, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + ret = fc0013_writereg(priv, 0x13, 0x00); + if (ret) + goto err; + + ret = fc0013_readreg(priv, 0x13, &tmp); + if (ret) + goto err; + int_temp = tmp; + + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto err; + lna_gain = tmp & 0x1f; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (lna_gain < ARRAY_SIZE(fc0013_lna_gain_table)) { + int_lna = fc0013_lna_gain_table[lna_gain]; + tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 + + (int_temp & 0x1f)) * 2; + power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10; + + if (power >= 45) + *strength = 255; /* 100% */ + else if (power < -95) + *strength = 0; + else + *strength = (power + 95) * 255 / 140; + + *strength |= *strength << 8; + } else { + ret = -1; + } + + goto exit; + +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ +exit: + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} + +static const struct dvb_tuner_ops fc0013_tuner_ops = { + .info = { + .name = "Fitipower FC0013", + + .frequency_min = 37000000, /* estimate */ + .frequency_max = 1680000000, /* CHECK */ + .frequency_step = 0, + }, + + .release = fc0013_release, + + .init = fc0013_init, + .sleep = fc0013_sleep, + + .set_params = fc0013_set_params, + + .get_frequency = fc0013_get_frequency, + .get_if_frequency = fc0013_get_if_frequency, + .get_bandwidth = fc0013_get_bandwidth, + + .get_rf_strength = fc0013_get_rf_strength, +}; + +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) +{ + struct fc0013_priv *priv = NULL; + + priv = kzalloc(sizeof(struct fc0013_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c = i2c; + priv->dual_master = dual_master; + priv->addr = i2c_address; + priv->xtal_freq = xtal_freq; + + info("Fitipower FC0013 successfully attached."); + + fe->tuner_priv = priv; + + memcpy(&fe->ops.tuner_ops, &fc0013_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + return fe; +} +EXPORT_SYMBOL(fc0013_attach); + +MODULE_DESCRIPTION("Fitipower FC0013 silicon tuner driver"); +MODULE_AUTHOR("Hans-Frieder Vogt "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.2"); diff --git a/drivers/media/tuners/fc0013.h b/drivers/media/tuners/fc0013.h new file mode 100644 index 000000000000..594efd64aeec --- /dev/null +++ b/drivers/media/tuners/fc0013.h @@ -0,0 +1,57 @@ +/* + * Fitipower FC0013 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * 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/tuners/fc001x-common.h b/drivers/media/tuners/fc001x-common.h new file mode 100644 index 000000000000..718818156934 --- /dev/null +++ b/drivers/media/tuners/fc001x-common.h @@ -0,0 +1,39 @@ +/* + * Fitipower FC0012 & FC0013 tuner driver - common defines + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * 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/tuners/max2165.c b/drivers/media/tuners/max2165.c new file mode 100644 index 000000000000..ba84936aafd6 --- /dev/null +++ b/drivers/media/tuners/max2165.c @@ -0,0 +1,433 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" + +#include "max2165.h" +#include "max2165_priv.h" +#include "tuner-i2c.h" + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "max2165: " args); \ + } while (0) + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +static int max2165_write_reg(struct max2165_priv *priv, u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; + + msg.addr = priv->config->i2c_address; + + if (debug >= 2) + dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, data); + + ret = i2c_transfer(priv->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", + __func__, reg, data, ret); + + return (ret != 1) ? -EIO : 0; +} + +static int max2165_read_reg(struct max2165_priv *priv, u8 reg, u8 *p_data) +{ + int ret; + u8 dev_addr = priv->config->i2c_address; + + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { .addr = dev_addr, .flags = 0, .buf = b0, .len = 1 }, + { .addr = dev_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 }, + }; + + ret = i2c_transfer(priv->i2c, msg, 2); + if (ret != 2) { + dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ret); + return -EIO; + } + + *p_data = b1[0]; + if (debug >= 2) + dprintk("%s: reg=0x%02X, data=0x%02X\n", + __func__, reg, b1[0]); + return 0; +} + +static int max2165_mask_write_reg(struct max2165_priv *priv, u8 reg, + u8 mask, u8 data) +{ + int ret; + u8 v; + + data &= mask; + ret = max2165_read_reg(priv, reg, &v); + if (ret != 0) + return ret; + v &= ~mask; + v |= data; + ret = max2165_write_reg(priv, reg, v); + + return ret; +} + +static int max2165_read_rom_table(struct max2165_priv *priv) +{ + u8 dat[3]; + int i; + + for (i = 0; i < 3; i++) { + max2165_write_reg(priv, REG_ROM_TABLE_ADDR, i + 1); + max2165_read_reg(priv, REG_ROM_TABLE_DATA, &dat[i]); + } + + priv->tf_ntch_low_cfg = dat[0] >> 4; + priv->tf_ntch_hi_cfg = dat[0] & 0x0F; + priv->tf_balun_low_ref = dat[1] & 0x0F; + priv->tf_balun_hi_ref = dat[1] >> 4; + priv->bb_filter_7mhz_cfg = dat[2] & 0x0F; + priv->bb_filter_8mhz_cfg = dat[2] >> 4; + + dprintk("tf_ntch_low_cfg = 0x%X\n", priv->tf_ntch_low_cfg); + dprintk("tf_ntch_hi_cfg = 0x%X\n", priv->tf_ntch_hi_cfg); + dprintk("tf_balun_low_ref = 0x%X\n", priv->tf_balun_low_ref); + dprintk("tf_balun_hi_ref = 0x%X\n", priv->tf_balun_hi_ref); + dprintk("bb_filter_7mhz_cfg = 0x%X\n", priv->bb_filter_7mhz_cfg); + dprintk("bb_filter_8mhz_cfg = 0x%X\n", priv->bb_filter_8mhz_cfg); + + return 0; +} + +static int max2165_set_osc(struct max2165_priv *priv, u8 osc /*MHz*/) +{ + u8 v; + + v = (osc / 2); + if (v == 2) + v = 0x7; + else + v -= 8; + + max2165_mask_write_reg(priv, REG_PLL_CFG, 0x07, v); + + return 0; +} + +static int max2165_set_bandwidth(struct max2165_priv *priv, u32 bw) +{ + u8 val; + + if (bw == 8000000) + val = priv->bb_filter_8mhz_cfg; + else + val = priv->bb_filter_7mhz_cfg; + + max2165_mask_write_reg(priv, REG_BASEBAND_CTRL, 0xF0, val << 4); + + return 0; +} + +int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction) +{ + u32 remainder; + u32 q, f = 0; + int i; + + if (0 == divisor) + return -EINVAL; + + q = dividend / divisor; + remainder = dividend - q * divisor; + + for (i = 0; i < 31; i++) { + remainder <<= 1; + if (remainder >= divisor) { + f += 1; + remainder -= divisor; + } + f <<= 1; + } + + *quotient = q; + *fraction = f; + + return 0; +} + +static int max2165_set_rf(struct max2165_priv *priv, u32 freq) +{ + u8 tf; + u8 tf_ntch; + u32 t; + u32 quotient, fraction; + int ret; + + /* Set PLL divider according to RF frequency */ + ret = fixpt_div32(freq / 1000, priv->config->osc_clk * 1000, + "ient, &fraction); + if (ret != 0) + return ret; + + /* 20-bit fraction */ + fraction >>= 12; + + max2165_write_reg(priv, REG_NDIV_INT, quotient); + max2165_mask_write_reg(priv, REG_NDIV_FRAC2, 0x0F, fraction >> 16); + max2165_write_reg(priv, REG_NDIV_FRAC1, fraction >> 8); + max2165_write_reg(priv, REG_NDIV_FRAC0, fraction); + + /* Norch Filter */ + tf_ntch = (freq < 725000000) ? + priv->tf_ntch_low_cfg : priv->tf_ntch_hi_cfg; + + /* Tracking filter balun */ + t = priv->tf_balun_low_ref; + t += (priv->tf_balun_hi_ref - priv->tf_balun_low_ref) + * (freq / 1000 - 470000) / (780000 - 470000); + + tf = t; + dprintk("tf = %X\n", tf); + tf |= tf_ntch << 4; + + max2165_write_reg(priv, REG_TRACK_FILTER, tf); + + return 0; +} + +static void max2165_debug_status(struct max2165_priv *priv) +{ + u8 status, autotune; + u8 auto_vco_success, auto_vco_active; + u8 pll_locked; + u8 dc_offset_low, dc_offset_hi; + u8 signal_lv_over_threshold; + u8 vco, vco_sub_band, adc; + + max2165_read_reg(priv, REG_STATUS, &status); + max2165_read_reg(priv, REG_AUTOTUNE, &autotune); + + auto_vco_success = (status >> 6) & 0x01; + auto_vco_active = (status >> 5) & 0x01; + pll_locked = (status >> 4) & 0x01; + dc_offset_low = (status >> 3) & 0x01; + dc_offset_hi = (status >> 2) & 0x01; + signal_lv_over_threshold = status & 0x01; + + vco = autotune >> 6; + vco_sub_band = (autotune >> 3) & 0x7; + adc = autotune & 0x7; + + dprintk("auto VCO active: %d, auto VCO success: %d\n", + auto_vco_active, auto_vco_success); + dprintk("PLL locked: %d\n", pll_locked); + dprintk("DC offset low: %d, DC offset high: %d\n", + dc_offset_low, dc_offset_hi); + dprintk("Signal lvl over threshold: %d\n", signal_lv_over_threshold); + dprintk("VCO: %d, VCO Sub-band: %d, ADC: %d\n", vco, vco_sub_band, adc); +} + +static int max2165_set_params(struct dvb_frontend *fe) +{ + struct max2165_priv *priv = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + + switch (c->bandwidth_hz) { + case 7000000: + case 8000000: + priv->frequency = c->frequency; + break; + default: + printk(KERN_INFO "MAX2165: bandwidth %d Hz not supported.\n", + c->bandwidth_hz); + return -EINVAL; + } + + dprintk("%s() frequency=%d\n", __func__, c->frequency); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + max2165_set_bandwidth(priv, c->bandwidth_hz); + ret = max2165_set_rf(priv, priv->frequency); + mdelay(50); + max2165_debug_status(priv); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret != 0) + return -EREMOTEIO; + + return 0; +} + +static int max2165_get_frequency(struct dvb_frontend *fe, u32 *freq) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + *freq = priv->frequency; + return 0; +} + +static int max2165_get_bandwidth(struct dvb_frontend *fe, u32 *bw) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + *bw = priv->bandwidth; + return 0; +} + +static int max2165_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct max2165_priv *priv = fe->tuner_priv; + u16 lock_status = 0; + + dprintk("%s()\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + max2165_debug_status(priv); + *status = lock_status; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int max2165_sleep(struct dvb_frontend *fe) +{ + dprintk("%s()\n", __func__); + return 0; +} + +static int max2165_init(struct dvb_frontend *fe) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* Setup initial values */ + /* Fractional Mode on */ + max2165_write_reg(priv, REG_NDIV_FRAC2, 0x18); + /* LNA on */ + max2165_write_reg(priv, REG_LNA, 0x01); + max2165_write_reg(priv, REG_PLL_CFG, 0x7A); + max2165_write_reg(priv, REG_TEST, 0x08); + max2165_write_reg(priv, REG_SHUTDOWN, 0x40); + max2165_write_reg(priv, REG_VCO_CTRL, 0x84); + max2165_write_reg(priv, REG_BASEBAND_CTRL, 0xC3); + max2165_write_reg(priv, REG_DC_OFFSET_CTRL, 0x75); + max2165_write_reg(priv, REG_DC_OFFSET_DAC, 0x00); + max2165_write_reg(priv, REG_ROM_TABLE_ADDR, 0x00); + + max2165_set_osc(priv, priv->config->osc_clk); + + max2165_read_rom_table(priv); + + max2165_set_bandwidth(priv, 8000000); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int max2165_release(struct dvb_frontend *fe) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + kfree(priv); + fe->tuner_priv = NULL; + + return 0; +} + +static const struct dvb_tuner_ops max2165_tuner_ops = { + .info = { + .name = "Maxim MAX2165", + .frequency_min = 470000000, + .frequency_max = 780000000, + .frequency_step = 50000, + }, + + .release = max2165_release, + .init = max2165_init, + .sleep = max2165_sleep, + + .set_params = max2165_set_params, + .set_analog_params = NULL, + .get_frequency = max2165_get_frequency, + .get_bandwidth = max2165_get_bandwidth, + .get_status = max2165_get_status +}; + +struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct max2165_config *cfg) +{ + struct max2165_priv *priv = NULL; + + dprintk("%s(%d-%04x)\n", __func__, + i2c ? i2c_adapter_id(i2c) : -1, + cfg ? cfg->i2c_address : -1); + + priv = kzalloc(sizeof(struct max2165_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + memcpy(&fe->ops.tuner_ops, &max2165_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + priv->config = cfg; + priv->i2c = i2c; + fe->tuner_priv = priv; + + max2165_init(fe); + max2165_debug_status(priv); + + return fe; +} +EXPORT_SYMBOL(max2165_attach); + +MODULE_AUTHOR("David T. L. Wong "); +MODULE_DESCRIPTION("Maxim MAX2165 silicon tuner driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/max2165.h b/drivers/media/tuners/max2165.h new file mode 100644 index 000000000000..c063c36a93d3 --- /dev/null +++ b/drivers/media/tuners/max2165.h @@ -0,0 +1,48 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong + * + * 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/tuners/max2165_priv.h b/drivers/media/tuners/max2165_priv.h new file mode 100644 index 000000000000..91bbe021a08d --- /dev/null +++ b/drivers/media/tuners/max2165_priv.h @@ -0,0 +1,60 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong + * + * 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/tuners/mc44s803.c b/drivers/media/tuners/mc44s803.c new file mode 100644 index 000000000000..5ddce7e326f7 --- /dev/null +++ b/drivers/media/tuners/mc44s803.c @@ -0,0 +1,372 @@ +/* + * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner + * + * Copyright (c) 2009 Jochen Friedrich + * + * 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 +#include +#include +#include +#include + +#include "dvb_frontend.h" + +#include "mc44s803.h" +#include "mc44s803_priv.h" + +#define mc_printk(level, format, arg...) \ + printk(level "mc44s803: " format , ## arg) + +/* Writes a single register */ +static int mc44s803_writereg(struct mc44s803_priv *priv, u32 val) +{ + u8 buf[3]; + struct i2c_msg msg = { + .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 3 + }; + + buf[0] = (val & 0xff0000) >> 16; + buf[1] = (val & 0xff00) >> 8; + buf[2] = (val & 0xff); + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + mc_printk(KERN_WARNING, "I2C write failed\n"); + return -EREMOTEIO; + } + return 0; +} + +/* Reads a single register */ +static int mc44s803_readreg(struct mc44s803_priv *priv, u8 reg, u32 *val) +{ + u32 wval; + u8 buf[3]; + int ret; + struct i2c_msg msg[] = { + { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, + .buf = buf, .len = 3 }, + }; + + wval = MC44S803_REG_SM(MC44S803_REG_DATAREG, MC44S803_ADDR) | + MC44S803_REG_SM(reg, MC44S803_D); + + ret = mc44s803_writereg(priv, wval); + if (ret) + return ret; + + if (i2c_transfer(priv->i2c, msg, 1) != 1) { + mc_printk(KERN_WARNING, "I2C read failed\n"); + return -EREMOTEIO; + } + + *val = (buf[0] << 16) | (buf[1] << 8) | buf[2]; + + return 0; +} + +static int mc44s803_release(struct dvb_frontend *fe) +{ + struct mc44s803_priv *priv = fe->tuner_priv; + + fe->tuner_priv = NULL; + kfree(priv); + + return 0; +} + +static int mc44s803_init(struct dvb_frontend *fe) +{ + struct mc44s803_priv *priv = fe->tuner_priv; + u32 val; + int err; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + +/* Reset chip */ + val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR) | + MC44S803_REG_SM(1, MC44S803_RS); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + +/* Power Up and Start Osc */ + + val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) | + MC44S803_REG_SM(0xC0, MC44S803_REFOSC) | + MC44S803_REG_SM(1, MC44S803_OSCSEL); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + val = MC44S803_REG_SM(MC44S803_REG_POWER, MC44S803_ADDR) | + MC44S803_REG_SM(0x200, MC44S803_POWER); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + msleep(10); + + val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) | + MC44S803_REG_SM(0x40, MC44S803_REFOSC) | + MC44S803_REG_SM(1, MC44S803_OSCSEL); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + msleep(20); + +/* Setup Mixer */ + + val = MC44S803_REG_SM(MC44S803_REG_MIXER, MC44S803_ADDR) | + MC44S803_REG_SM(1, MC44S803_TRI_STATE) | + MC44S803_REG_SM(0x7F, MC44S803_MIXER_RES); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + +/* Setup Cirquit Adjust */ + + val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) | + MC44S803_REG_SM(1, MC44S803_G1) | + MC44S803_REG_SM(1, MC44S803_G3) | + MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) | + MC44S803_REG_SM(1, MC44S803_G6) | + MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) | + MC44S803_REG_SM(0x3, MC44S803_LP) | + MC44S803_REG_SM(1, MC44S803_CLRF) | + MC44S803_REG_SM(1, MC44S803_CLIF); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) | + MC44S803_REG_SM(1, MC44S803_G1) | + MC44S803_REG_SM(1, MC44S803_G3) | + MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) | + MC44S803_REG_SM(1, MC44S803_G6) | + MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) | + MC44S803_REG_SM(0x3, MC44S803_LP); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + +/* Setup Digtune */ + + val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | + MC44S803_REG_SM(3, MC44S803_XOD); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + +/* Setup AGC */ + + val = MC44S803_REG_SM(MC44S803_REG_LNAAGC, MC44S803_ADDR) | + MC44S803_REG_SM(1, MC44S803_AT1) | + MC44S803_REG_SM(1, MC44S803_AT2) | + MC44S803_REG_SM(1, MC44S803_AGC_AN_DIG) | + MC44S803_REG_SM(1, MC44S803_AGC_READ_EN) | + MC44S803_REG_SM(1, MC44S803_LNA0); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + return 0; + +exit: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + mc_printk(KERN_WARNING, "I/O Error\n"); + return err; +} + +static int mc44s803_set_params(struct dvb_frontend *fe) +{ + struct mc44s803_priv *priv = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 r1, r2, n1, n2, lo1, lo2, freq, val; + int err; + + priv->frequency = c->frequency; + + r1 = MC44S803_OSC / 1000000; + r2 = MC44S803_OSC / 100000; + + n1 = (c->frequency + MC44S803_IF1 + 500000) / 1000000; + freq = MC44S803_OSC / r1 * n1; + lo1 = ((60 * n1) + (r1 / 2)) / r1; + freq = freq - c->frequency; + + n2 = (freq - MC44S803_IF2 + 50000) / 100000; + lo2 = ((60 * n2) + (r2 / 2)) / r2; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + val = MC44S803_REG_SM(MC44S803_REG_REFDIV, MC44S803_ADDR) | + MC44S803_REG_SM(r1-1, MC44S803_R1) | + MC44S803_REG_SM(r2-1, MC44S803_R2) | + MC44S803_REG_SM(1, MC44S803_REFBUF_EN); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + val = MC44S803_REG_SM(MC44S803_REG_LO1, MC44S803_ADDR) | + MC44S803_REG_SM(n1-2, MC44S803_LO1); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + val = MC44S803_REG_SM(MC44S803_REG_LO2, MC44S803_ADDR) | + MC44S803_REG_SM(n2-2, MC44S803_LO2); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | + MC44S803_REG_SM(1, MC44S803_DA) | + MC44S803_REG_SM(lo1, MC44S803_LO_REF) | + MC44S803_REG_SM(1, MC44S803_AT); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | + MC44S803_REG_SM(2, MC44S803_DA) | + MC44S803_REG_SM(lo2, MC44S803_LO_REF) | + MC44S803_REG_SM(1, MC44S803_AT); + + err = mc44s803_writereg(priv, val); + if (err) + goto exit; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; + +exit: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + mc_printk(KERN_WARNING, "I/O Error\n"); + return err; +} + +static int mc44s803_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct mc44s803_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static const struct dvb_tuner_ops mc44s803_tuner_ops = { + .info = { + .name = "Freescale MC44S803", + .frequency_min = 48000000, + .frequency_max = 1000000000, + .frequency_step = 100000, + }, + + .release = mc44s803_release, + .init = mc44s803_init, + .set_params = mc44s803_set_params, + .get_frequency = mc44s803_get_frequency +}; + +/* This functions tries to identify a MC44S803 tuner by reading the ID + register. This is hasty. */ +struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct mc44s803_config *cfg) +{ + struct mc44s803_priv *priv; + u32 reg; + u8 id; + int ret; + + reg = 0; + + priv = kzalloc(sizeof(struct mc44s803_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->cfg = cfg; + priv->i2c = i2c; + priv->fe = fe; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + ret = mc44s803_readreg(priv, MC44S803_REG_ID, ®); + if (ret) + goto error; + + id = MC44S803_REG_MS(reg, MC44S803_ID); + + if (id != 0x14) { + mc_printk(KERN_ERR, "unsupported ID " + "(%x should be 0x14)\n", id); + goto error; + } + + mc_printk(KERN_INFO, "successfully identified (ID = %x)\n", id); + memcpy(&fe->ops.tuner_ops, &mc44s803_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + return fe; + +error: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + kfree(priv); + return NULL; +} +EXPORT_SYMBOL(mc44s803_attach); + +MODULE_AUTHOR("Jochen Friedrich"); +MODULE_DESCRIPTION("Freescale MC44S803 silicon tuner driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/mc44s803.h b/drivers/media/tuners/mc44s803.h new file mode 100644 index 000000000000..34f3892d3f6d --- /dev/null +++ b/drivers/media/tuners/mc44s803.h @@ -0,0 +1,46 @@ +/* + * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner + * + * Copyright (c) 2009 Jochen Friedrich + * + * 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/tuners/mc44s803_priv.h b/drivers/media/tuners/mc44s803_priv.h new file mode 100644 index 000000000000..14a92780906d --- /dev/null +++ b/drivers/media/tuners/mc44s803_priv.h @@ -0,0 +1,208 @@ +/* + * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner + * + * Copyright (c) 2009 Jochen Friedrich + * + * 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_PRIV_H +#define MC44S803_PRIV_H + +/* This driver is based on the information available in the datasheet + http://www.freescale.com/files/rf_if/doc/data_sheet/MC44S803.pdf + + SPI or I2C Address : 0xc0-0xc6 + + Reg.No | Function + ------------------------------------------- + 00 | Power Down + 01 | Reference Oszillator + 02 | Reference Dividers + 03 | Mixer and Reference Buffer + 04 | Reset/Serial Out + 05 | LO 1 + 06 | LO 2 + 07 | Circuit Adjust + 08 | Test + 09 | Digital Tune + 0A | LNA AGC + 0B | Data Register Address + 0C | Regulator Test + 0D | VCO Test + 0E | LNA Gain/Input Power + 0F | ID Bits + +*/ + +#define MC44S803_OSC 26000000 /* 26 MHz */ +#define MC44S803_IF1 1086000000 /* 1086 MHz */ +#define MC44S803_IF2 36125000 /* 36.125 MHz */ + +#define MC44S803_REG_POWER 0 +#define MC44S803_REG_REFOSC 1 +#define MC44S803_REG_REFDIV 2 +#define MC44S803_REG_MIXER 3 +#define MC44S803_REG_RESET 4 +#define MC44S803_REG_LO1 5 +#define MC44S803_REG_LO2 6 +#define MC44S803_REG_CIRCADJ 7 +#define MC44S803_REG_TEST 8 +#define MC44S803_REG_DIGTUNE 9 +#define MC44S803_REG_LNAAGC 0x0A +#define MC44S803_REG_DATAREG 0x0B +#define MC44S803_REG_REGTEST 0x0C +#define MC44S803_REG_VCOTEST 0x0D +#define MC44S803_REG_LNAGAIN 0x0E +#define MC44S803_REG_ID 0x0F + +/* Register definitions */ +#define MC44S803_ADDR 0x0F +#define MC44S803_ADDR_S 0 +/* REG_POWER */ +#define MC44S803_POWER 0xFFFFF0 +#define MC44S803_POWER_S 4 +/* REG_REFOSC */ +#define MC44S803_REFOSC 0x1FF0 +#define MC44S803_REFOSC_S 4 +#define MC44S803_OSCSEL 0x2000 +#define MC44S803_OSCSEL_S 13 +/* REG_REFDIV */ +#define MC44S803_R2 0x1FF0 +#define MC44S803_R2_S 4 +#define MC44S803_REFBUF_EN 0x2000 +#define MC44S803_REFBUF_EN_S 13 +#define MC44S803_R1 0x7C000 +#define MC44S803_R1_S 14 +/* REG_MIXER */ +#define MC44S803_R3 0x70 +#define MC44S803_R3_S 4 +#define MC44S803_MUX3 0x80 +#define MC44S803_MUX3_S 7 +#define MC44S803_MUX4 0x100 +#define MC44S803_MUX4_S 8 +#define MC44S803_OSC_SCR 0x200 +#define MC44S803_OSC_SCR_S 9 +#define MC44S803_TRI_STATE 0x400 +#define MC44S803_TRI_STATE_S 10 +#define MC44S803_BUF_GAIN 0x800 +#define MC44S803_BUF_GAIN_S 11 +#define MC44S803_BUF_IO 0x1000 +#define MC44S803_BUF_IO_S 12 +#define MC44S803_MIXER_RES 0xFE000 +#define MC44S803_MIXER_RES_S 13 +/* REG_RESET */ +#define MC44S803_RS 0x10 +#define MC44S803_RS_S 4 +#define MC44S803_SO 0x20 +#define MC44S803_SO_S 5 +/* REG_LO1 */ +#define MC44S803_LO1 0xFFF0 +#define MC44S803_LO1_S 4 +/* REG_LO2 */ +#define MC44S803_LO2 0x7FFF0 +#define MC44S803_LO2_S 4 +/* REG_CIRCADJ */ +#define MC44S803_G1 0x20 +#define MC44S803_G1_S 5 +#define MC44S803_G3 0x80 +#define MC44S803_G3_S 7 +#define MC44S803_CIRCADJ_RES 0x300 +#define MC44S803_CIRCADJ_RES_S 8 +#define MC44S803_G6 0x400 +#define MC44S803_G6_S 10 +#define MC44S803_G7 0x800 +#define MC44S803_G7_S 11 +#define MC44S803_S1 0x1000 +#define MC44S803_S1_S 12 +#define MC44S803_LP 0x7E000 +#define MC44S803_LP_S 13 +#define MC44S803_CLRF 0x80000 +#define MC44S803_CLRF_S 19 +#define MC44S803_CLIF 0x100000 +#define MC44S803_CLIF_S 20 +/* REG_TEST */ +/* REG_DIGTUNE */ +#define MC44S803_DA 0xF0 +#define MC44S803_DA_S 4 +#define MC44S803_XOD 0x300 +#define MC44S803_XOD_S 8 +#define MC44S803_RST 0x10000 +#define MC44S803_RST_S 16 +#define MC44S803_LO_REF 0x1FFF00 +#define MC44S803_LO_REF_S 8 +#define MC44S803_AT 0x200000 +#define MC44S803_AT_S 21 +#define MC44S803_MT 0x400000 +#define MC44S803_MT_S 22 +/* REG_LNAAGC */ +#define MC44S803_G 0x3F0 +#define MC44S803_G_S 4 +#define MC44S803_AT1 0x400 +#define MC44S803_AT1_S 10 +#define MC44S803_AT2 0x800 +#define MC44S803_AT2_S 11 +#define MC44S803_HL_GR_EN 0x8000 +#define MC44S803_HL_GR_EN_S 15 +#define MC44S803_AGC_AN_DIG 0x10000 +#define MC44S803_AGC_AN_DIG_S 16 +#define MC44S803_ATTEN_EN 0x20000 +#define MC44S803_ATTEN_EN_S 17 +#define MC44S803_AGC_READ_EN 0x40000 +#define MC44S803_AGC_READ_EN_S 18 +#define MC44S803_LNA0 0x80000 +#define MC44S803_LNA0_S 19 +#define MC44S803_AGC_SEL 0x100000 +#define MC44S803_AGC_SEL_S 20 +#define MC44S803_AT0 0x200000 +#define MC44S803_AT0_S 21 +#define MC44S803_B 0xC00000 +#define MC44S803_B_S 22 +/* REG_DATAREG */ +#define MC44S803_D 0xF0 +#define MC44S803_D_S 4 +/* REG_REGTEST */ +/* REG_VCOTEST */ +/* REG_LNAGAIN */ +#define MC44S803_IF_PWR 0x700 +#define MC44S803_IF_PWR_S 8 +#define MC44S803_RF_PWR 0x3800 +#define MC44S803_RF_PWR_S 11 +#define MC44S803_LNA_GAIN 0xFC000 +#define MC44S803_LNA_GAIN_S 14 +/* REG_ID */ +#define MC44S803_ID 0x3E00 +#define MC44S803_ID_S 9 + +/* Some macros to read/write fields */ + +/* First shift, then mask */ +#define MC44S803_REG_SM(_val, _reg) \ + (((_val) << _reg##_S) & (_reg)) + +/* First mask, then shift */ +#define MC44S803_REG_MS(_val, _reg) \ + (((_val) & (_reg)) >> _reg##_S) + +struct mc44s803_priv { + struct mc44s803_config *cfg; + struct i2c_adapter *i2c; + struct dvb_frontend *fe; + + u32 frequency; +}; + +#endif diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c new file mode 100644 index 000000000000..13381de58a84 --- /dev/null +++ b/drivers/media/tuners/mt2060.c @@ -0,0 +1,403 @@ +/* + * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" + * + * Copyright (c) 2006 Olivier DANET + * + * 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 +#include +#include +#include +#include + +#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 = ®, .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/tuners/mt2060.h b/drivers/media/tuners/mt2060.h new file mode 100644 index 000000000000..cb60caffb6b6 --- /dev/null +++ b/drivers/media/tuners/mt2060.h @@ -0,0 +1,43 @@ +/* + * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" + * + * Copyright (c) 2006 Olivier DANET + * + * 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/tuners/mt2060_priv.h b/drivers/media/tuners/mt2060_priv.h new file mode 100644 index 000000000000..2b60de6c707d --- /dev/null +++ b/drivers/media/tuners/mt2060_priv.h @@ -0,0 +1,104 @@ +/* + * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" + * + * Copyright (c) 2006 Olivier DANET + * + * 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_PRIV_H +#define MT2060_PRIV_H + +// Uncomment the #define below to enable spurs checking. The results where quite unconvincing. +// #define MT2060_SPURCHECK + +/* This driver is based on the information available in the datasheet of the + "Comtech SDVBT-3K6M" tuner ( K1000737843.pdf ) which features the MT2060 register map : + + I2C Address : 0x60 + + Reg.No | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 | ( defaults ) + -------------------------------------------------------------------------------- + 00 | [ PART ] | [ REV ] | R = 0x63 + 01 | [ LNABAND ] | [ NUM1(5:2) ] | RW = 0x3F + 02 | [ DIV1 ] | RW = 0x74 + 03 | FM1CA | FM1SS | [ NUM1(1:0) ] | [ NUM2(3:0) ] | RW = 0x00 + 04 | NUM2(11:4) ] | RW = 0x08 + 05 | [ DIV2 ] |NUM2(12)| RW = 0x93 + 06 | L1LK | [ TAD1 ] | L2LK | [ TAD2 ] | R + 07 | [ FMF ] | R + 08 | ? | FMCAL | ? | ? | ? | ? | ? | TEMP | R + 09 | 0 | 0 | [ FMGC ] | 0 | GP02 | GP01 | 0 | RW = 0x20 + 0A | ?? + 0B | 0 | 0 | 1 | 1 | 0 | 0 | [ VGAG ] | RW = 0x30 + 0C | V1CSE | 1 | 1 | 1 | 1 | 1 | 1 | 1 | RW = 0xFF + 0D | 1 | 0 | [ V1CS ] | RW = 0xB0 + 0E | ?? + 0F | ?? + 10 | ?? + 11 | [ LOTO ] | 0 | 0 | 1 | 0 | RW = 0x42 + + PART : Part code : 6 for MT2060 + REV : Revision code : 3 for current revision + LNABAND : Input frequency range : ( See code for details ) + NUM1 / DIV1 / NUM2 / DIV2 : Frequencies programming ( See code for details ) + FM1CA : Calibration Start Bit + FM1SS : Calibration Single Step bit + L1LK : LO1 Lock Detect + TAD1 : Tune Line ADC ( ? ) + L2LK : LO2 Lock Detect + TAD2 : Tune Line ADC ( ? ) + FMF : Estimated first IF Center frequency Offset ( ? ) + FM1CAL : Calibration done bit + TEMP : On chip temperature sensor + FMCG : Mixer 1 Cap Gain ( ? ) + GP01 / GP02 : Programmable digital outputs. Unconnected pins ? + V1CSE : LO1 VCO Automatic Capacitor Select Enable ( ? ) + V1CS : LO1 Capacitor Selection Value ( ? ) + LOTO : LO Timeout ( ? ) + VGAG : Tuner Output gain +*/ + +#define I2C_ADDRESS 0x60 + +#define REG_PART_REV 0 +#define REG_LO1C1 1 +#define REG_LO1C2 2 +#define REG_LO2C1 3 +#define REG_LO2C2 4 +#define REG_LO2C3 5 +#define REG_LO_STATUS 6 +#define REG_FM_FREQ 7 +#define REG_MISC_STAT 8 +#define REG_MISC_CTRL 9 +#define REG_RESERVED_A 0x0A +#define REG_VGAG 0x0B +#define REG_LO1B1 0x0C +#define REG_LO1B2 0x0D +#define REG_LOTO 0x11 + +#define PART_REV 0x63 // The current driver works only with PART=6 and REV=3 chips + +struct mt2060_priv { + struct mt2060_config *cfg; + struct i2c_adapter *i2c; + + u32 frequency; + u16 if1_freq; + u8 fmfreq; +}; + +#endif diff --git a/drivers/media/tuners/mt2063.c b/drivers/media/tuners/mt2063.c new file mode 100644 index 000000000000..0ed9091ff48e --- /dev/null +++ b/drivers/media/tuners/mt2063.c @@ -0,0 +1,2307 @@ +/* + * Driver for mt2063 Micronas tuner + * + * Copyright (c) 2011 Mauro Carvalho Chehab + * + * This driver came from a driver originally written by: + * Henry Wang + * Made publicly available by Terratec, at: + * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz + * The original driver's license is GPL, as declared with MODULE_LICENSE() + * + * 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 under version 2 of the License. + * + * 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 +#include +#include +#include +#include + +#include "mt2063.h" + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Set Verbosity level"); + +#define dprintk(level, fmt, arg...) do { \ +if (debug >= level) \ + printk(KERN_DEBUG "mt2063 %s: " fmt, __func__, ## arg); \ +} while (0) + + +/* positive error codes used internally */ + +/* Info: Unavoidable LO-related spur may be present in the output */ +#define MT2063_SPUR_PRESENT_ERR (0x00800000) + +/* Info: Mask of bits used for # of LO-related spurs that were avoided during tuning */ +#define MT2063_SPUR_CNT_MASK (0x001f0000) +#define MT2063_SPUR_SHIFT (16) + +/* Info: Upconverter frequency is out of range (may be reason for MT_UPC_UNLOCK) */ +#define MT2063_UPC_RANGE (0x04000000) + +/* Info: Downconverter frequency is out of range (may be reason for MT_DPC_UNLOCK) */ +#define MT2063_DNC_RANGE (0x08000000) + +/* + * Constant defining the version of the following structure + * and therefore the API for this code. + * + * When compiling the tuner driver, the preprocessor will + * check against this version number to make sure that + * it matches the version that the tuner driver knows about. + */ + +/* DECT Frequency Avoidance */ +#define MT2063_DECT_AVOID_US_FREQS 0x00000001 + +#define MT2063_DECT_AVOID_EURO_FREQS 0x00000002 + +#define MT2063_EXCLUDE_US_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_US_FREQS) != 0) + +#define MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_EURO_FREQS) != 0) + +enum MT2063_DECT_Avoid_Type { + MT2063_NO_DECT_AVOIDANCE = 0, /* Do not create DECT exclusion zones. */ + MT2063_AVOID_US_DECT = MT2063_DECT_AVOID_US_FREQS, /* Avoid US DECT frequencies. */ + MT2063_AVOID_EURO_DECT = MT2063_DECT_AVOID_EURO_FREQS, /* Avoid European DECT frequencies. */ + MT2063_AVOID_BOTH /* Avoid both regions. Not typically used. */ +}; + +#define MT2063_MAX_ZONES 48 + +struct MT2063_ExclZone_t { + u32 min_; + u32 max_; + struct MT2063_ExclZone_t *next_; +}; + +/* + * Structure of data needed for Spur Avoidance + */ +struct MT2063_AvoidSpursData_t { + u32 f_ref; + u32 f_in; + u32 f_LO1; + u32 f_if1_Center; + u32 f_if1_Request; + u32 f_if1_bw; + u32 f_LO2; + u32 f_out; + u32 f_out_bw; + u32 f_LO1_Step; + u32 f_LO2_Step; + u32 f_LO1_FracN_Avoid; + u32 f_LO2_FracN_Avoid; + u32 f_zif_bw; + u32 f_min_LO_Separation; + u32 maxH1; + u32 maxH2; + enum MT2063_DECT_Avoid_Type avoidDECT; + u32 bSpurPresent; + u32 bSpurAvoided; + u32 nSpursFound; + u32 nZones; + struct MT2063_ExclZone_t *freeZones; + struct MT2063_ExclZone_t *usedZones; + struct MT2063_ExclZone_t MT2063_ExclZones[MT2063_MAX_ZONES]; +}; + +/* + * Parameter for function MT2063_SetPowerMask that specifies the power down + * of various sections of the MT2063. + */ +enum MT2063_Mask_Bits { + MT2063_REG_SD = 0x0040, /* Shutdown regulator */ + MT2063_SRO_SD = 0x0020, /* Shutdown SRO */ + MT2063_AFC_SD = 0x0010, /* Shutdown AFC A/D */ + MT2063_PD_SD = 0x0002, /* Enable power detector shutdown */ + MT2063_PDADC_SD = 0x0001, /* Enable power detector A/D shutdown */ + MT2063_VCO_SD = 0x8000, /* Enable VCO shutdown */ + MT2063_LTX_SD = 0x4000, /* Enable LTX shutdown */ + MT2063_LT1_SD = 0x2000, /* Enable LT1 shutdown */ + MT2063_LNA_SD = 0x1000, /* Enable LNA shutdown */ + MT2063_UPC_SD = 0x0800, /* Enable upconverter shutdown */ + MT2063_DNC_SD = 0x0400, /* Enable downconverter shutdown */ + MT2063_VGA_SD = 0x0200, /* Enable VGA shutdown */ + MT2063_AMP_SD = 0x0100, /* Enable AMP shutdown */ + MT2063_ALL_SD = 0xFF73, /* All shutdown bits for this tuner */ + MT2063_NONE_SD = 0x0000 /* No shutdown bits */ +}; + +/* + * Possible values for MT2063_DNC_OUTPUT + */ +enum MT2063_DNC_Output_Enable { + MT2063_DNC_NONE = 0, + MT2063_DNC_1, + MT2063_DNC_2, + MT2063_DNC_BOTH +}; + +/* + * Two-wire serial bus subaddresses of the tuner registers. + * Also known as the tuner's register addresses. + */ +enum MT2063_Register_Offsets { + MT2063_REG_PART_REV = 0, /* 0x00: Part/Rev Code */ + MT2063_REG_LO1CQ_1, /* 0x01: LO1C Queued Byte 1 */ + MT2063_REG_LO1CQ_2, /* 0x02: LO1C Queued Byte 2 */ + MT2063_REG_LO2CQ_1, /* 0x03: LO2C Queued Byte 1 */ + MT2063_REG_LO2CQ_2, /* 0x04: LO2C Queued Byte 2 */ + MT2063_REG_LO2CQ_3, /* 0x05: LO2C Queued Byte 3 */ + MT2063_REG_RSVD_06, /* 0x06: Reserved */ + MT2063_REG_LO_STATUS, /* 0x07: LO Status */ + MT2063_REG_FIFFC, /* 0x08: FIFF Center */ + MT2063_REG_CLEARTUNE, /* 0x09: ClearTune Filter */ + MT2063_REG_ADC_OUT, /* 0x0A: ADC_OUT */ + MT2063_REG_LO1C_1, /* 0x0B: LO1C Byte 1 */ + MT2063_REG_LO1C_2, /* 0x0C: LO1C Byte 2 */ + MT2063_REG_LO2C_1, /* 0x0D: LO2C Byte 1 */ + MT2063_REG_LO2C_2, /* 0x0E: LO2C Byte 2 */ + MT2063_REG_LO2C_3, /* 0x0F: LO2C Byte 3 */ + MT2063_REG_RSVD_10, /* 0x10: Reserved */ + MT2063_REG_PWR_1, /* 0x11: PWR Byte 1 */ + MT2063_REG_PWR_2, /* 0x12: PWR Byte 2 */ + MT2063_REG_TEMP_STATUS, /* 0x13: Temp Status */ + MT2063_REG_XO_STATUS, /* 0x14: Crystal Status */ + MT2063_REG_RF_STATUS, /* 0x15: RF Attn Status */ + MT2063_REG_FIF_STATUS, /* 0x16: FIF Attn Status */ + MT2063_REG_LNA_OV, /* 0x17: LNA Attn Override */ + MT2063_REG_RF_OV, /* 0x18: RF Attn Override */ + MT2063_REG_FIF_OV, /* 0x19: FIF Attn Override */ + MT2063_REG_LNA_TGT, /* 0x1A: Reserved */ + MT2063_REG_PD1_TGT, /* 0x1B: Pwr Det 1 Target */ + MT2063_REG_PD2_TGT, /* 0x1C: Pwr Det 2 Target */ + MT2063_REG_RSVD_1D, /* 0x1D: Reserved */ + MT2063_REG_RSVD_1E, /* 0x1E: Reserved */ + MT2063_REG_RSVD_1F, /* 0x1F: Reserved */ + MT2063_REG_RSVD_20, /* 0x20: Reserved */ + MT2063_REG_BYP_CTRL, /* 0x21: Bypass Control */ + MT2063_REG_RSVD_22, /* 0x22: Reserved */ + MT2063_REG_RSVD_23, /* 0x23: Reserved */ + MT2063_REG_RSVD_24, /* 0x24: Reserved */ + MT2063_REG_RSVD_25, /* 0x25: Reserved */ + MT2063_REG_RSVD_26, /* 0x26: Reserved */ + MT2063_REG_RSVD_27, /* 0x27: Reserved */ + MT2063_REG_FIFF_CTRL, /* 0x28: FIFF Control */ + MT2063_REG_FIFF_OFFSET, /* 0x29: FIFF Offset */ + MT2063_REG_CTUNE_CTRL, /* 0x2A: Reserved */ + MT2063_REG_CTUNE_OV, /* 0x2B: Reserved */ + MT2063_REG_CTRL_2C, /* 0x2C: Reserved */ + MT2063_REG_FIFF_CTRL2, /* 0x2D: Fiff Control */ + MT2063_REG_RSVD_2E, /* 0x2E: Reserved */ + MT2063_REG_DNC_GAIN, /* 0x2F: DNC Control */ + MT2063_REG_VGA_GAIN, /* 0x30: VGA Gain Ctrl */ + MT2063_REG_RSVD_31, /* 0x31: Reserved */ + MT2063_REG_TEMP_SEL, /* 0x32: Temperature Selection */ + MT2063_REG_RSVD_33, /* 0x33: Reserved */ + MT2063_REG_RSVD_34, /* 0x34: Reserved */ + MT2063_REG_RSVD_35, /* 0x35: Reserved */ + MT2063_REG_RSVD_36, /* 0x36: Reserved */ + MT2063_REG_RSVD_37, /* 0x37: Reserved */ + MT2063_REG_RSVD_38, /* 0x38: Reserved */ + MT2063_REG_RSVD_39, /* 0x39: Reserved */ + MT2063_REG_RSVD_3A, /* 0x3A: Reserved */ + MT2063_REG_RSVD_3B, /* 0x3B: Reserved */ + MT2063_REG_RSVD_3C, /* 0x3C: Reserved */ + MT2063_REG_END_REGS +}; + +struct mt2063_state { + struct i2c_adapter *i2c; + + bool init; + + const struct mt2063_config *config; + struct dvb_tuner_ops ops; + struct dvb_frontend *frontend; + struct tuner_state status; + + u32 frequency; + u32 srate; + u32 bandwidth; + u32 reference; + + u32 tuner_id; + struct MT2063_AvoidSpursData_t AS_Data; + u32 f_IF1_actual; + u32 rcvr_mode; + u32 ctfilt_sw; + u32 CTFiltMax[31]; + u32 num_regs; + u8 reg[MT2063_REG_END_REGS]; +}; + +/* + * mt2063_write - Write data into the I2C bus + */ +static u32 mt2063_write(struct mt2063_state *state, u8 reg, u8 *data, u32 len) +{ + struct dvb_frontend *fe = state->frontend; + int ret; + u8 buf[60]; + struct i2c_msg msg = { + .addr = state->config->tuner_address, + .flags = 0, + .buf = buf, + .len = len + 1 + }; + + dprintk(2, "\n"); + + msg.buf[0] = reg; + memcpy(msg.buf + 1, data, len); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + ret = i2c_transfer(state->i2c, &msg, 1); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret < 0) + printk(KERN_ERR "%s error ret=%d\n", __func__, ret); + + return ret; +} + +/* + * mt2063_write - Write register data into the I2C bus, caching the value + */ +static u32 mt2063_setreg(struct mt2063_state *state, u8 reg, u8 val) +{ + u32 status; + + dprintk(2, "\n"); + + if (reg >= MT2063_REG_END_REGS) + return -ERANGE; + + status = mt2063_write(state, reg, &val, 1); + if (status < 0) + return status; + + state->reg[reg] = val; + + return 0; +} + +/* + * mt2063_read - Read data from the I2C bus + */ +static u32 mt2063_read(struct mt2063_state *state, + u8 subAddress, u8 *pData, u32 cnt) +{ + u32 status = 0; /* Status to be returned */ + struct dvb_frontend *fe = state->frontend; + u32 i = 0; + + dprintk(2, "addr 0x%02x, cnt %d\n", subAddress, cnt); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + for (i = 0; i < cnt; i++) { + u8 b0[] = { subAddress + i }; + struct i2c_msg msg[] = { + { + .addr = state->config->tuner_address, + .flags = 0, + .buf = b0, + .len = 1 + }, { + .addr = state->config->tuner_address, + .flags = I2C_M_RD, + .buf = pData + i, + .len = 1 + } + }; + + status = i2c_transfer(state->i2c, msg, 2); + dprintk(2, "addr 0x%02x, ret = %d, val = 0x%02x\n", + subAddress + i, status, *(pData + i)); + if (status < 0) + break; + } + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (status < 0) + printk(KERN_ERR "Can't read from address 0x%02x,\n", + subAddress + i); + + return status; +} + +/* + * FIXME: Is this really needed? + */ +static int MT2063_Sleep(struct dvb_frontend *fe) +{ + /* + * ToDo: Add code here to implement a OS blocking + */ + msleep(100); + + return 0; +} + +/* + * Microtune spur avoidance + */ + +/* Implement ceiling, floor functions. */ +#define ceil(n, d) (((n) < 0) ? (-((-(n))/(d))) : (n)/(d) + ((n)%(d) != 0)) +#define floor(n, d) (((n) < 0) ? (-((-(n))/(d))) - ((n)%(d) != 0) : (n)/(d)) + +struct MT2063_FIFZone_t { + s32 min_; + s32 max_; +}; + +static struct MT2063_ExclZone_t *InsertNode(struct MT2063_AvoidSpursData_t + *pAS_Info, + struct MT2063_ExclZone_t *pPrevNode) +{ + struct MT2063_ExclZone_t *pNode; + + dprintk(2, "\n"); + + /* Check for a node in the free list */ + if (pAS_Info->freeZones != NULL) { + /* Use one from the free list */ + pNode = pAS_Info->freeZones; + pAS_Info->freeZones = pNode->next_; + } else { + /* Grab a node from the array */ + pNode = &pAS_Info->MT2063_ExclZones[pAS_Info->nZones]; + } + + if (pPrevNode != NULL) { + pNode->next_ = pPrevNode->next_; + pPrevNode->next_ = pNode; + } else { /* insert at the beginning of the list */ + + pNode->next_ = pAS_Info->usedZones; + pAS_Info->usedZones = pNode; + } + + pAS_Info->nZones++; + return pNode; +} + +static struct MT2063_ExclZone_t *RemoveNode(struct MT2063_AvoidSpursData_t + *pAS_Info, + struct MT2063_ExclZone_t *pPrevNode, + struct MT2063_ExclZone_t + *pNodeToRemove) +{ + struct MT2063_ExclZone_t *pNext = pNodeToRemove->next_; + + dprintk(2, "\n"); + + /* Make previous node point to the subsequent node */ + if (pPrevNode != NULL) + pPrevNode->next_ = pNext; + + /* Add pNodeToRemove to the beginning of the freeZones */ + pNodeToRemove->next_ = pAS_Info->freeZones; + pAS_Info->freeZones = pNodeToRemove; + + /* Decrement node count */ + pAS_Info->nZones--; + + return pNext; +} + +/* + * MT_AddExclZone() + * + * Add (and merge) an exclusion zone into the list. + * If the range (f_min, f_max) is totally outside the + * 1st IF BW, ignore the entry. + * If the range (f_min, f_max) is negative, ignore the entry. + */ +static void MT2063_AddExclZone(struct MT2063_AvoidSpursData_t *pAS_Info, + u32 f_min, u32 f_max) +{ + struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones; + struct MT2063_ExclZone_t *pPrev = NULL; + struct MT2063_ExclZone_t *pNext = NULL; + + dprintk(2, "\n"); + + /* Check to see if this overlaps the 1st IF filter */ + if ((f_max > (pAS_Info->f_if1_Center - (pAS_Info->f_if1_bw / 2))) + && (f_min < (pAS_Info->f_if1_Center + (pAS_Info->f_if1_bw / 2))) + && (f_min < f_max)) { + /* + * 1 2 3 4 5 6 + * + * New entry: |---| |--| |--| |-| |---| |--| + * or or or or or + * Existing: |--| |--| |--| |---| |-| |--| + */ + + /* Check for our place in the list */ + while ((pNode != NULL) && (pNode->max_ < f_min)) { + pPrev = pNode; + pNode = pNode->next_; + } + + if ((pNode != NULL) && (pNode->min_ < f_max)) { + /* Combine me with pNode */ + if (f_min < pNode->min_) + pNode->min_ = f_min; + if (f_max > pNode->max_) + pNode->max_ = f_max; + } else { + pNode = InsertNode(pAS_Info, pPrev); + pNode->min_ = f_min; + pNode->max_ = f_max; + } + + /* Look for merging possibilities */ + pNext = pNode->next_; + while ((pNext != NULL) && (pNext->min_ < pNode->max_)) { + if (pNext->max_ > pNode->max_) + pNode->max_ = pNext->max_; + /* Remove pNext, return ptr to pNext->next */ + pNext = RemoveNode(pAS_Info, pNode, pNext); + } + } +} + +/* + * Reset all exclusion zones. + * Add zones to protect the PLL FracN regions near zero + */ +static void MT2063_ResetExclZones(struct MT2063_AvoidSpursData_t *pAS_Info) +{ + u32 center; + + dprintk(2, "\n"); + + pAS_Info->nZones = 0; /* this clears the used list */ + pAS_Info->usedZones = NULL; /* reset ptr */ + pAS_Info->freeZones = NULL; /* reset ptr */ + + center = + pAS_Info->f_ref * + ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 + + pAS_Info->f_in) / pAS_Info->f_ref) - pAS_Info->f_in; + while (center < + pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 + + pAS_Info->f_LO1_FracN_Avoid) { + /* Exclude LO1 FracN */ + MT2063_AddExclZone(pAS_Info, + center - pAS_Info->f_LO1_FracN_Avoid, + center - 1); + MT2063_AddExclZone(pAS_Info, center + 1, + center + pAS_Info->f_LO1_FracN_Avoid); + center += pAS_Info->f_ref; + } + + center = + pAS_Info->f_ref * + ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 - + pAS_Info->f_out) / pAS_Info->f_ref) + pAS_Info->f_out; + while (center < + pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 + + pAS_Info->f_LO2_FracN_Avoid) { + /* Exclude LO2 FracN */ + MT2063_AddExclZone(pAS_Info, + center - pAS_Info->f_LO2_FracN_Avoid, + center - 1); + MT2063_AddExclZone(pAS_Info, center + 1, + center + pAS_Info->f_LO2_FracN_Avoid); + center += pAS_Info->f_ref; + } + + if (MT2063_EXCLUDE_US_DECT_FREQUENCIES(pAS_Info->avoidDECT)) { + /* Exclude LO1 values that conflict with DECT channels */ + MT2063_AddExclZone(pAS_Info, 1920836000 - pAS_Info->f_in, 1922236000 - pAS_Info->f_in); /* Ctr = 1921.536 */ + MT2063_AddExclZone(pAS_Info, 1922564000 - pAS_Info->f_in, 1923964000 - pAS_Info->f_in); /* Ctr = 1923.264 */ + MT2063_AddExclZone(pAS_Info, 1924292000 - pAS_Info->f_in, 1925692000 - pAS_Info->f_in); /* Ctr = 1924.992 */ + MT2063_AddExclZone(pAS_Info, 1926020000 - pAS_Info->f_in, 1927420000 - pAS_Info->f_in); /* Ctr = 1926.720 */ + MT2063_AddExclZone(pAS_Info, 1927748000 - pAS_Info->f_in, 1929148000 - pAS_Info->f_in); /* Ctr = 1928.448 */ + } + + if (MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(pAS_Info->avoidDECT)) { + MT2063_AddExclZone(pAS_Info, 1896644000 - pAS_Info->f_in, 1898044000 - pAS_Info->f_in); /* Ctr = 1897.344 */ + MT2063_AddExclZone(pAS_Info, 1894916000 - pAS_Info->f_in, 1896316000 - pAS_Info->f_in); /* Ctr = 1895.616 */ + MT2063_AddExclZone(pAS_Info, 1893188000 - pAS_Info->f_in, 1894588000 - pAS_Info->f_in); /* Ctr = 1893.888 */ + MT2063_AddExclZone(pAS_Info, 1891460000 - pAS_Info->f_in, 1892860000 - pAS_Info->f_in); /* Ctr = 1892.16 */ + MT2063_AddExclZone(pAS_Info, 1889732000 - pAS_Info->f_in, 1891132000 - pAS_Info->f_in); /* Ctr = 1890.432 */ + MT2063_AddExclZone(pAS_Info, 1888004000 - pAS_Info->f_in, 1889404000 - pAS_Info->f_in); /* Ctr = 1888.704 */ + MT2063_AddExclZone(pAS_Info, 1886276000 - pAS_Info->f_in, 1887676000 - pAS_Info->f_in); /* Ctr = 1886.976 */ + MT2063_AddExclZone(pAS_Info, 1884548000 - pAS_Info->f_in, 1885948000 - pAS_Info->f_in); /* Ctr = 1885.248 */ + MT2063_AddExclZone(pAS_Info, 1882820000 - pAS_Info->f_in, 1884220000 - pAS_Info->f_in); /* Ctr = 1883.52 */ + MT2063_AddExclZone(pAS_Info, 1881092000 - pAS_Info->f_in, 1882492000 - pAS_Info->f_in); /* Ctr = 1881.792 */ + } +} + +/* + * MT_ChooseFirstIF - Choose the best available 1st IF + * If f_Desired is not excluded, choose that first. + * Otherwise, return the value closest to f_Center that is + * not excluded + */ +static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info) +{ + /* + * Update "f_Desired" to be the nearest "combinational-multiple" of + * "f_LO1_Step". + * The resulting number, F_LO1 must be a multiple of f_LO1_Step. + * And F_LO1 is the arithmetic sum of f_in + f_Center. + * Neither f_in, nor f_Center must be a multiple of f_LO1_Step. + * However, the sum must be. + */ + const u32 f_Desired = + pAS_Info->f_LO1_Step * + ((pAS_Info->f_if1_Request + pAS_Info->f_in + + pAS_Info->f_LO1_Step / 2) / pAS_Info->f_LO1_Step) - + pAS_Info->f_in; + const u32 f_Step = + (pAS_Info->f_LO1_Step > + pAS_Info->f_LO2_Step) ? pAS_Info->f_LO1_Step : pAS_Info-> + f_LO2_Step; + u32 f_Center; + s32 i; + s32 j = 0; + u32 bDesiredExcluded = 0; + u32 bZeroExcluded = 0; + s32 tmpMin, tmpMax; + s32 bestDiff; + struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones; + struct MT2063_FIFZone_t zones[MT2063_MAX_ZONES]; + + dprintk(2, "\n"); + + if (pAS_Info->nZones == 0) + return f_Desired; + + /* + * f_Center needs to be an integer multiple of f_Step away + * from f_Desired + */ + if (pAS_Info->f_if1_Center > f_Desired) + f_Center = + f_Desired + + f_Step * + ((pAS_Info->f_if1_Center - f_Desired + + f_Step / 2) / f_Step); + else + f_Center = + f_Desired - + f_Step * + ((f_Desired - pAS_Info->f_if1_Center + + f_Step / 2) / f_Step); + + /* + * Take MT_ExclZones, center around f_Center and change the + * resolution to f_Step + */ + while (pNode != NULL) { + /* floor function */ + tmpMin = + floor((s32) (pNode->min_ - f_Center), (s32) f_Step); + + /* ceil function */ + tmpMax = + ceil((s32) (pNode->max_ - f_Center), (s32) f_Step); + + if ((pNode->min_ < f_Desired) && (pNode->max_ > f_Desired)) + bDesiredExcluded = 1; + + if ((tmpMin < 0) && (tmpMax > 0)) + bZeroExcluded = 1; + + /* See if this zone overlaps the previous */ + if ((j > 0) && (tmpMin < zones[j - 1].max_)) + zones[j - 1].max_ = tmpMax; + else { + /* Add new zone */ + zones[j].min_ = tmpMin; + zones[j].max_ = tmpMax; + j++; + } + pNode = pNode->next_; + } + + /* + * If the desired is okay, return with it + */ + if (bDesiredExcluded == 0) + return f_Desired; + + /* + * If the desired is excluded and the center is okay, return with it + */ + if (bZeroExcluded == 0) + return f_Center; + + /* Find the value closest to 0 (f_Center) */ + bestDiff = zones[0].min_; + for (i = 0; i < j; i++) { + if (abs(zones[i].min_) < abs(bestDiff)) + bestDiff = zones[i].min_; + if (abs(zones[i].max_) < abs(bestDiff)) + bestDiff = zones[i].max_; + } + + if (bestDiff < 0) + return f_Center - ((u32) (-bestDiff) * f_Step); + + return f_Center + (bestDiff * f_Step); +} + +/** + * gcd() - Uses Euclid's algorithm + * + * @u, @v: Unsigned values whose GCD is desired. + * + * Returns THE greatest common divisor of u and v, if either value is 0, + * the other value is returned as the result. + */ +static u32 MT2063_gcd(u32 u, u32 v) +{ + u32 r; + + while (v != 0) { + r = u % v; + u = v; + v = r; + } + + return u; +} + +/** + * IsSpurInBand() - Checks to see if a spur will be present within the IF's + * bandwidth. (fIFOut +/- fIFBW, -fIFOut +/- fIFBW) + * + * ma mb mc md + * <--+-+-+-------------------+-------------------+-+-+--> + * | ^ 0 ^ | + * ^ b=-fIFOut+fIFBW/2 -b=+fIFOut-fIFBW/2 ^ + * a=-fIFOut-fIFBW/2 -a=+fIFOut+fIFBW/2 + * + * Note that some equations are doubled to prevent round-off + * problems when calculating fIFBW/2 + * + * @pAS_Info: Avoid Spurs information block + * @fm: If spur, amount f_IF1 has to move negative + * @fp: If spur, amount f_IF1 has to move positive + * + * Returns 1 if an LO spur would be present, otherwise 0. + */ +static u32 IsSpurInBand(struct MT2063_AvoidSpursData_t *pAS_Info, + u32 *fm, u32 * fp) +{ + /* + ** Calculate LO frequency settings. + */ + u32 n, n0; + const u32 f_LO1 = pAS_Info->f_LO1; + const u32 f_LO2 = pAS_Info->f_LO2; + const u32 d = pAS_Info->f_out + pAS_Info->f_out_bw / 2; + const u32 c = d - pAS_Info->f_out_bw; + const u32 f = pAS_Info->f_zif_bw / 2; + const u32 f_Scale = (f_LO1 / (UINT_MAX / 2 / pAS_Info->maxH1)) + 1; + s32 f_nsLO1, f_nsLO2; + s32 f_Spur; + u32 ma, mb, mc, md, me, mf; + u32 lo_gcd, gd_Scale, gc_Scale, gf_Scale, hgds, hgfs, hgcs; + + dprintk(2, "\n"); + + *fm = 0; + + /* + ** For each edge (d, c & f), calculate a scale, based on the gcd + ** of f_LO1, f_LO2 and the edge value. Use the larger of this + ** gcd-based scale factor or f_Scale. + */ + lo_gcd = MT2063_gcd(f_LO1, f_LO2); + gd_Scale = max((u32) MT2063_gcd(lo_gcd, d), f_Scale); + hgds = gd_Scale / 2; + gc_Scale = max((u32) MT2063_gcd(lo_gcd, c), f_Scale); + hgcs = gc_Scale / 2; + gf_Scale = max((u32) MT2063_gcd(lo_gcd, f), f_Scale); + hgfs = gf_Scale / 2; + + n0 = DIV_ROUND_UP(f_LO2 - d, f_LO1 - f_LO2); + + /* Check out all multiples of LO1 from n0 to m_maxLOSpurHarmonic */ + for (n = n0; n <= pAS_Info->maxH1; ++n) { + md = (n * ((f_LO1 + hgds) / gd_Scale) - + ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale); + + /* If # fLO2 harmonics > m_maxLOSpurHarmonic, then no spurs present */ + if (md >= pAS_Info->maxH1) + break; + + ma = (n * ((f_LO1 + hgds) / gd_Scale) + + ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale); + + /* If no spurs between +/- (f_out + f_IFBW/2), then try next harmonic */ + if (md == ma) + continue; + + mc = (n * ((f_LO1 + hgcs) / gc_Scale) - + ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale); + if (mc != md) { + f_nsLO1 = (s32) (n * (f_LO1 / gc_Scale)); + f_nsLO2 = (s32) (mc * (f_LO2 / gc_Scale)); + f_Spur = + (gc_Scale * (f_nsLO1 - f_nsLO2)) + + n * (f_LO1 % gc_Scale) - mc * (f_LO2 % gc_Scale); + + *fp = ((f_Spur - (s32) c) / (mc - n)) + 1; + *fm = (((s32) d - f_Spur) / (mc - n)) + 1; + return 1; + } + + /* Location of Zero-IF-spur to be checked */ + me = (n * ((f_LO1 + hgfs) / gf_Scale) + + ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale); + mf = (n * ((f_LO1 + hgfs) / gf_Scale) - + ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale); + if (me != mf) { + f_nsLO1 = n * (f_LO1 / gf_Scale); + f_nsLO2 = me * (f_LO2 / gf_Scale); + f_Spur = + (gf_Scale * (f_nsLO1 - f_nsLO2)) + + n * (f_LO1 % gf_Scale) - me * (f_LO2 % gf_Scale); + + *fp = ((f_Spur + (s32) f) / (me - n)) + 1; + *fm = (((s32) f - f_Spur) / (me - n)) + 1; + return 1; + } + + mb = (n * ((f_LO1 + hgcs) / gc_Scale) + + ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale); + if (ma != mb) { + f_nsLO1 = n * (f_LO1 / gc_Scale); + f_nsLO2 = ma * (f_LO2 / gc_Scale); + f_Spur = + (gc_Scale * (f_nsLO1 - f_nsLO2)) + + n * (f_LO1 % gc_Scale) - ma * (f_LO2 % gc_Scale); + + *fp = (((s32) d + f_Spur) / (ma - n)) + 1; + *fm = (-(f_Spur + (s32) c) / (ma - n)) + 1; + return 1; + } + } + + /* No spurs found */ + return 0; +} + +/* + * MT_AvoidSpurs() - Main entry point to avoid spurs. + * Checks for existing spurs in present LO1, LO2 freqs + * and if present, chooses spur-free LO1, LO2 combination + * that tunes the same input/output frequencies. + */ +static u32 MT2063_AvoidSpurs(struct MT2063_AvoidSpursData_t *pAS_Info) +{ + u32 status = 0; + u32 fm, fp; /* restricted range on LO's */ + pAS_Info->bSpurAvoided = 0; + pAS_Info->nSpursFound = 0; + + dprintk(2, "\n"); + + if (pAS_Info->maxH1 == 0) + return 0; + + /* + * Avoid LO Generated Spurs + * + * Make sure that have no LO-related spurs within the IF output + * bandwidth. + * + * If there is an LO spur in this band, start at the current IF1 frequency + * and work out until we find a spur-free frequency or run up against the + * 1st IF SAW band edge. Use temporary copies of fLO1 and fLO2 so that they + * will be unchanged if a spur-free setting is not found. + */ + pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp); + if (pAS_Info->bSpurPresent) { + u32 zfIF1 = pAS_Info->f_LO1 - pAS_Info->f_in; /* current attempt at a 1st IF */ + u32 zfLO1 = pAS_Info->f_LO1; /* current attempt at an LO1 freq */ + u32 zfLO2 = pAS_Info->f_LO2; /* current attempt at an LO2 freq */ + u32 delta_IF1; + u32 new_IF1; + + /* + ** Spur was found, attempt to find a spur-free 1st IF + */ + do { + pAS_Info->nSpursFound++; + + /* Raise f_IF1_upper, if needed */ + MT2063_AddExclZone(pAS_Info, zfIF1 - fm, zfIF1 + fp); + + /* Choose next IF1 that is closest to f_IF1_CENTER */ + new_IF1 = MT2063_ChooseFirstIF(pAS_Info); + + if (new_IF1 > zfIF1) { + pAS_Info->f_LO1 += (new_IF1 - zfIF1); + pAS_Info->f_LO2 += (new_IF1 - zfIF1); + } else { + pAS_Info->f_LO1 -= (zfIF1 - new_IF1); + pAS_Info->f_LO2 -= (zfIF1 - new_IF1); + } + zfIF1 = new_IF1; + + if (zfIF1 > pAS_Info->f_if1_Center) + delta_IF1 = zfIF1 - pAS_Info->f_if1_Center; + else + delta_IF1 = pAS_Info->f_if1_Center - zfIF1; + + pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp); + /* + * Continue while the new 1st IF is still within the 1st IF bandwidth + * and there is a spur in the band (again) + */ + } while ((2 * delta_IF1 + pAS_Info->f_out_bw <= pAS_Info->f_if1_bw) && pAS_Info->bSpurPresent); + + /* + * Use the LO-spur free values found. If the search went all + * the way to the 1st IF band edge and always found spurs, just + * leave the original choice. It's as "good" as any other. + */ + if (pAS_Info->bSpurPresent == 1) { + status |= MT2063_SPUR_PRESENT_ERR; + pAS_Info->f_LO1 = zfLO1; + pAS_Info->f_LO2 = zfLO2; + } else + pAS_Info->bSpurAvoided = 1; + } + + status |= + ((pAS_Info-> + nSpursFound << MT2063_SPUR_SHIFT) & MT2063_SPUR_CNT_MASK); + + return status; +} + +/* + * Constants used by the tuning algorithm + */ +#define MT2063_REF_FREQ (16000000UL) /* Reference oscillator Frequency (in Hz) */ +#define MT2063_IF1_BW (22000000UL) /* The IF1 filter bandwidth (in Hz) */ +#define MT2063_TUNE_STEP_SIZE (50000UL) /* Tune in steps of 50 kHz */ +#define MT2063_SPUR_STEP_HZ (250000UL) /* Step size (in Hz) to move IF1 when avoiding spurs */ +#define MT2063_ZIF_BW (2000000UL) /* Zero-IF spur-free bandwidth (in Hz) */ +#define MT2063_MAX_HARMONICS_1 (15UL) /* Highest intra-tuner LO Spur Harmonic to be avoided */ +#define MT2063_MAX_HARMONICS_2 (5UL) /* Highest inter-tuner LO Spur Harmonic to be avoided */ +#define MT2063_MIN_LO_SEP (1000000UL) /* Minimum inter-tuner LO frequency separation */ +#define MT2063_LO1_FRACN_AVOID (0UL) /* LO1 FracN numerator avoid region (in Hz) */ +#define MT2063_LO2_FRACN_AVOID (199999UL) /* LO2 FracN numerator avoid region (in Hz) */ +#define MT2063_MIN_FIN_FREQ (44000000UL) /* Minimum input frequency (in Hz) */ +#define MT2063_MAX_FIN_FREQ (1100000000UL) /* Maximum input frequency (in Hz) */ +#define MT2063_MIN_FOUT_FREQ (36000000UL) /* Minimum output frequency (in Hz) */ +#define MT2063_MAX_FOUT_FREQ (57000000UL) /* Maximum output frequency (in Hz) */ +#define MT2063_MIN_DNC_FREQ (1293000000UL) /* Minimum LO2 frequency (in Hz) */ +#define MT2063_MAX_DNC_FREQ (1614000000UL) /* Maximum LO2 frequency (in Hz) */ +#define MT2063_MIN_UPC_FREQ (1396000000UL) /* Minimum LO1 frequency (in Hz) */ +#define MT2063_MAX_UPC_FREQ (2750000000UL) /* Maximum LO1 frequency (in Hz) */ + +/* + * Define the supported Part/Rev codes for the MT2063 + */ +#define MT2063_B0 (0x9B) +#define MT2063_B1 (0x9C) +#define MT2063_B2 (0x9D) +#define MT2063_B3 (0x9E) + +/** + * mt2063_lockStatus - Checks to see if LO1 and LO2 are locked + * + * @state: struct mt2063_state pointer + * + * This function returns 0, if no lock, 1 if locked and a value < 1 if error + */ +static unsigned int mt2063_lockStatus(struct mt2063_state *state) +{ + const u32 nMaxWait = 100; /* wait a maximum of 100 msec */ + const u32 nPollRate = 2; /* poll status bits every 2 ms */ + const u32 nMaxLoops = nMaxWait / nPollRate; + const u8 LO1LK = 0x80; + u8 LO2LK = 0x08; + u32 status; + u32 nDelays = 0; + + dprintk(2, "\n"); + + /* LO2 Lock bit was in a different place for B0 version */ + if (state->tuner_id == MT2063_B0) + LO2LK = 0x40; + + do { + status = mt2063_read(state, MT2063_REG_LO_STATUS, + &state->reg[MT2063_REG_LO_STATUS], 1); + + if (status < 0) + return status; + + if ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) == + (LO1LK | LO2LK)) { + return TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO; + } + msleep(nPollRate); /* Wait between retries */ + } while (++nDelays < nMaxLoops); + + /* + * Got no lock or partial lock + */ + return 0; +} + +/* + * Constants for setting receiver modes. + * (6 modes defined at this time, enumerated by mt2063_delivery_sys) + * (DNC1GC & DNC2GC are the values, which are used, when the specific + * DNC Output is selected, the other is always off) + * + * enum mt2063_delivery_sys + * -------------+---------------------------------------------- + * Mode 0 : | MT2063_CABLE_QAM + * Mode 1 : | MT2063_CABLE_ANALOG + * Mode 2 : | MT2063_OFFAIR_COFDM + * Mode 3 : | MT2063_OFFAIR_COFDM_SAWLESS + * Mode 4 : | MT2063_OFFAIR_ANALOG + * Mode 5 : | MT2063_OFFAIR_8VSB + * --------------+---------------------------------------------- + * + * |<---------- Mode -------------->| + * Reg Field | 0 | 1 | 2 | 3 | 4 | 5 | + * ------------+-----+-----+-----+-----+-----+-----+ + * RFAGCen | OFF | OFF | OFF | OFF | OFF | OFF + * LNARin | 0 | 0 | 3 | 3 | 3 | 3 + * FIFFQen | 1 | 1 | 1 | 1 | 1 | 1 + * FIFFq | 0 | 0 | 0 | 0 | 0 | 0 + * DNC1gc | 0 | 0 | 0 | 0 | 0 | 0 + * DNC2gc | 0 | 0 | 0 | 0 | 0 | 0 + * GCU Auto | 1 | 1 | 1 | 1 | 1 | 1 + * LNA max Atn | 31 | 31 | 31 | 31 | 31 | 31 + * LNA Target | 44 | 43 | 43 | 43 | 43 | 43 + * ign RF Ovl | 0 | 0 | 0 | 0 | 0 | 0 + * RF max Atn | 31 | 31 | 31 | 31 | 31 | 31 + * PD1 Target | 36 | 36 | 38 | 38 | 36 | 38 + * ign FIF Ovl | 0 | 0 | 0 | 0 | 0 | 0 + * FIF max Atn | 5 | 5 | 5 | 5 | 5 | 5 + * PD2 Target | 40 | 33 | 42 | 42 | 33 | 42 + */ + +enum mt2063_delivery_sys { + MT2063_CABLE_QAM = 0, + MT2063_CABLE_ANALOG, + MT2063_OFFAIR_COFDM, + MT2063_OFFAIR_COFDM_SAWLESS, + MT2063_OFFAIR_ANALOG, + MT2063_OFFAIR_8VSB, + MT2063_NUM_RCVR_MODES +}; + +static const char *mt2063_mode_name[] = { + [MT2063_CABLE_QAM] = "digital cable", + [MT2063_CABLE_ANALOG] = "analog cable", + [MT2063_OFFAIR_COFDM] = "digital offair", + [MT2063_OFFAIR_COFDM_SAWLESS] = "digital offair without SAW", + [MT2063_OFFAIR_ANALOG] = "analog offair", + [MT2063_OFFAIR_8VSB] = "analog offair 8vsb", +}; + +static const u8 RFAGCEN[] = { 0, 0, 0, 0, 0, 0 }; +static const u8 LNARIN[] = { 0, 0, 3, 3, 3, 3 }; +static const u8 FIFFQEN[] = { 1, 1, 1, 1, 1, 1 }; +static const u8 FIFFQ[] = { 0, 0, 0, 0, 0, 0 }; +static const u8 DNC1GC[] = { 0, 0, 0, 0, 0, 0 }; +static const u8 DNC2GC[] = { 0, 0, 0, 0, 0, 0 }; +static const u8 ACLNAMAX[] = { 31, 31, 31, 31, 31, 31 }; +static const u8 LNATGT[] = { 44, 43, 43, 43, 43, 43 }; +static const u8 RFOVDIS[] = { 0, 0, 0, 0, 0, 0 }; +static const u8 ACRFMAX[] = { 31, 31, 31, 31, 31, 31 }; +static const u8 PD1TGT[] = { 36, 36, 38, 38, 36, 38 }; +static const u8 FIFOVDIS[] = { 0, 0, 0, 0, 0, 0 }; +static const u8 ACFIFMAX[] = { 29, 29, 29, 29, 29, 29 }; +static const u8 PD2TGT[] = { 40, 33, 38, 42, 30, 38 }; + +/* + * mt2063_set_dnc_output_enable() + */ +static u32 mt2063_get_dnc_output_enable(struct mt2063_state *state, + enum MT2063_DNC_Output_Enable *pValue) +{ + dprintk(2, "\n"); + + if ((state->reg[MT2063_REG_DNC_GAIN] & 0x03) == 0x03) { /* if DNC1 is off */ + if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */ + *pValue = MT2063_DNC_NONE; + else + *pValue = MT2063_DNC_2; + } else { /* DNC1 is on */ + if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */ + *pValue = MT2063_DNC_1; + else + *pValue = MT2063_DNC_BOTH; + } + return 0; +} + +/* + * mt2063_set_dnc_output_enable() + */ +static u32 mt2063_set_dnc_output_enable(struct mt2063_state *state, + enum MT2063_DNC_Output_Enable nValue) +{ + u32 status = 0; /* Status to be returned */ + u8 val = 0; + + dprintk(2, "\n"); + + /* selects, which DNC output is used */ + switch (nValue) { + case MT2063_DNC_NONE: + val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */ + if (state->reg[MT2063_REG_DNC_GAIN] != + val) + status |= + mt2063_setreg(state, + MT2063_REG_DNC_GAIN, + val); + + val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */ + if (state->reg[MT2063_REG_VGA_GAIN] != + val) + status |= + mt2063_setreg(state, + MT2063_REG_VGA_GAIN, + val); + + val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */ + if (state->reg[MT2063_REG_RSVD_20] != + val) + status |= + mt2063_setreg(state, + MT2063_REG_RSVD_20, + val); + + break; + case MT2063_DNC_1: + val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */ + if (state->reg[MT2063_REG_DNC_GAIN] != + val) + status |= + mt2063_setreg(state, + MT2063_REG_DNC_GAIN, + val); + + val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */ + if (state->reg[MT2063_REG_VGA_GAIN] != + val) + status |= + mt2063_setreg(state, + MT2063_REG_VGA_GAIN, + val); + + val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */ + if (state->reg[MT2063_REG_RSVD_20] != + val) + status |= + mt2063_setreg(state, + MT2063_REG_RSVD_20, + val); + + break; + case MT2063_DNC_2: + val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */ + if (state->reg[MT2063_REG_DNC_GAIN] != + val) + status |= + mt2063_setreg(state, + MT2063_REG_DNC_GAIN, + val); + + val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */ + if (state->reg[MT2063_REG_VGA_GAIN] != + val) + status |= + mt2063_setreg(state, + MT2063_REG_VGA_GAIN, + val); + + val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */ + if (state->reg[MT2063_REG_RSVD_20] != + val) + status |= + mt2063_setreg(state, + MT2063_REG_RSVD_20, + val); + + break; + case MT2063_DNC_BOTH: + val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */ + if (state->reg[MT2063_REG_DNC_GAIN] != + val) + status |= + mt2063_setreg(state, + MT2063_REG_DNC_GAIN, + val); + + val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */ + if (state->reg[MT2063_REG_VGA_GAIN] != + val) + status |= + mt2063_setreg(state, + MT2063_REG_VGA_GAIN, + val); + + val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */ + if (state->reg[MT2063_REG_RSVD_20] != + val) + status |= + mt2063_setreg(state, + MT2063_REG_RSVD_20, + val); + + break; + default: + break; + } + + return status; +} + +/* + * MT2063_SetReceiverMode() - Set the MT2063 receiver mode, according with + * the selected enum mt2063_delivery_sys type. + * + * (DNC1GC & DNC2GC are the values, which are used, when the specific + * DNC Output is selected, the other is always off) + * + * @state: ptr to mt2063_state structure + * @Mode: desired reciever delivery system + * + * Note: Register cache must be valid for it to work + */ + +static u32 MT2063_SetReceiverMode(struct mt2063_state *state, + enum mt2063_delivery_sys Mode) +{ + u32 status = 0; /* Status to be returned */ + u8 val; + u32 longval; + + dprintk(2, "\n"); + + if (Mode >= MT2063_NUM_RCVR_MODES) + status = -ERANGE; + + /* RFAGCen */ + if (status >= 0) { + val = + (state-> + reg[MT2063_REG_PD1_TGT] & (u8) ~0x40) | (RFAGCEN[Mode] + ? 0x40 : + 0x00); + if (state->reg[MT2063_REG_PD1_TGT] != val) + status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val); + } + + /* LNARin */ + if (status >= 0) { + u8 val = (state->reg[MT2063_REG_CTRL_2C] & (u8) ~0x03) | + (LNARIN[Mode] & 0x03); + if (state->reg[MT2063_REG_CTRL_2C] != val) + status |= mt2063_setreg(state, MT2063_REG_CTRL_2C, val); + } + + /* FIFFQEN and FIFFQ */ + if (status >= 0) { + val = + (state-> + reg[MT2063_REG_FIFF_CTRL2] & (u8) ~0xF0) | + (FIFFQEN[Mode] << 7) | (FIFFQ[Mode] << 4); + if (state->reg[MT2063_REG_FIFF_CTRL2] != val) { + status |= + mt2063_setreg(state, MT2063_REG_FIFF_CTRL2, val); + /* trigger FIFF calibration, needed after changing FIFFQ */ + val = + (state->reg[MT2063_REG_FIFF_CTRL] | (u8) 0x01); + status |= + mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val); + val = + (state-> + reg[MT2063_REG_FIFF_CTRL] & (u8) ~0x01); + status |= + mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val); + } + } + + /* DNC1GC & DNC2GC */ + status |= mt2063_get_dnc_output_enable(state, &longval); + status |= mt2063_set_dnc_output_enable(state, longval); + + /* acLNAmax */ + if (status >= 0) { + u8 val = (state->reg[MT2063_REG_LNA_OV] & (u8) ~0x1F) | + (ACLNAMAX[Mode] & 0x1F); + if (state->reg[MT2063_REG_LNA_OV] != val) + status |= mt2063_setreg(state, MT2063_REG_LNA_OV, val); + } + + /* LNATGT */ + if (status >= 0) { + u8 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x3F) | + (LNATGT[Mode] & 0x3F); + if (state->reg[MT2063_REG_LNA_TGT] != val) + status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val); + } + + /* ACRF */ + if (status >= 0) { + u8 val = (state->reg[MT2063_REG_RF_OV] & (u8) ~0x1F) | + (ACRFMAX[Mode] & 0x1F); + if (state->reg[MT2063_REG_RF_OV] != val) + status |= mt2063_setreg(state, MT2063_REG_RF_OV, val); + } + + /* PD1TGT */ + if (status >= 0) { + u8 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x3F) | + (PD1TGT[Mode] & 0x3F); + if (state->reg[MT2063_REG_PD1_TGT] != val) + status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val); + } + + /* FIFATN */ + if (status >= 0) { + u8 val = ACFIFMAX[Mode]; + if (state->reg[MT2063_REG_PART_REV] != MT2063_B3 && val > 5) + val = 5; + val = (state->reg[MT2063_REG_FIF_OV] & (u8) ~0x1F) | + (val & 0x1F); + if (state->reg[MT2063_REG_FIF_OV] != val) + status |= mt2063_setreg(state, MT2063_REG_FIF_OV, val); + } + + /* PD2TGT */ + if (status >= 0) { + u8 val = (state->reg[MT2063_REG_PD2_TGT] & (u8) ~0x3F) | + (PD2TGT[Mode] & 0x3F); + if (state->reg[MT2063_REG_PD2_TGT] != val) + status |= mt2063_setreg(state, MT2063_REG_PD2_TGT, val); + } + + /* Ignore ATN Overload */ + if (status >= 0) { + val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x80) | + (RFOVDIS[Mode] ? 0x80 : 0x00); + if (state->reg[MT2063_REG_LNA_TGT] != val) + status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val); + } + + /* Ignore FIF Overload */ + if (status >= 0) { + val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x80) | + (FIFOVDIS[Mode] ? 0x80 : 0x00); + if (state->reg[MT2063_REG_PD1_TGT] != val) + status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val); + } + + if (status >= 0) { + state->rcvr_mode = Mode; + dprintk(1, "mt2063 mode changed to %s\n", + mt2063_mode_name[state->rcvr_mode]); + } + + return status; +} + +/* + * MT2063_ClearPowerMaskBits () - Clears the power-down mask bits for various + * sections of the MT2063 + * + * @Bits: Mask bits to be cleared. + * + * See definition of MT2063_Mask_Bits type for description + * of each of the power bits. + */ +static u32 MT2063_ClearPowerMaskBits(struct mt2063_state *state, + enum MT2063_Mask_Bits Bits) +{ + u32 status = 0; + + dprintk(2, "\n"); + Bits = (enum MT2063_Mask_Bits)(Bits & MT2063_ALL_SD); /* Only valid bits for this tuner */ + if ((Bits & 0xFF00) != 0) { + state->reg[MT2063_REG_PWR_2] &= ~(u8) (Bits >> 8); + status |= + mt2063_write(state, + MT2063_REG_PWR_2, + &state->reg[MT2063_REG_PWR_2], 1); + } + if ((Bits & 0xFF) != 0) { + state->reg[MT2063_REG_PWR_1] &= ~(u8) (Bits & 0xFF); + status |= + mt2063_write(state, + MT2063_REG_PWR_1, + &state->reg[MT2063_REG_PWR_1], 1); + } + + return status; +} + +/* + * MT2063_SoftwareShutdown() - Enables or disables software shutdown function. + * When Shutdown is 1, any section whose power + * mask is set will be shutdown. + */ +static u32 MT2063_SoftwareShutdown(struct mt2063_state *state, u8 Shutdown) +{ + u32 status; + + dprintk(2, "\n"); + if (Shutdown == 1) + state->reg[MT2063_REG_PWR_1] |= 0x04; + else + state->reg[MT2063_REG_PWR_1] &= ~0x04; + + status = mt2063_write(state, + MT2063_REG_PWR_1, + &state->reg[MT2063_REG_PWR_1], 1); + + if (Shutdown != 1) { + state->reg[MT2063_REG_BYP_CTRL] = + (state->reg[MT2063_REG_BYP_CTRL] & 0x9F) | 0x40; + status |= + mt2063_write(state, + MT2063_REG_BYP_CTRL, + &state->reg[MT2063_REG_BYP_CTRL], + 1); + state->reg[MT2063_REG_BYP_CTRL] = + (state->reg[MT2063_REG_BYP_CTRL] & 0x9F); + status |= + mt2063_write(state, + MT2063_REG_BYP_CTRL, + &state->reg[MT2063_REG_BYP_CTRL], + 1); + } + + return status; +} + +static u32 MT2063_Round_fLO(u32 f_LO, u32 f_LO_Step, u32 f_ref) +{ + return f_ref * (f_LO / f_ref) + + f_LO_Step * (((f_LO % f_ref) + (f_LO_Step / 2)) / f_LO_Step); +} + +/** + * fLO_FractionalTerm() - Calculates the portion contributed by FracN / denom. + * This function preserves maximum precision without + * risk of overflow. It accurately calculates + * f_ref * num / denom to within 1 HZ with fixed math. + * + * @num : Fractional portion of the multiplier + * @denom: denominator portion of the ratio + * @f_Ref: SRO frequency. + * + * This calculation handles f_ref as two separate 14-bit fields. + * Therefore, a maximum value of 2^28-1 may safely be used for f_ref. + * This is the genesis of the magic number "14" and the magic mask value of + * 0x03FFF. + * + * This routine successfully handles denom values up to and including 2^18. + * Returns: f_ref * num / denom + */ +static u32 MT2063_fLO_FractionalTerm(u32 f_ref, u32 num, u32 denom) +{ + u32 t1 = (f_ref >> 14) * num; + u32 term1 = t1 / denom; + u32 loss = t1 % denom; + u32 term2 = + (((f_ref & 0x00003FFF) * num + (loss << 14)) + (denom / 2)) / denom; + return (term1 << 14) + term2; +} + +/* + * CalcLO1Mult()- Calculates Integer divider value and the numerator + * value for a FracN PLL. + * + * This function assumes that the f_LO and f_Ref are + * evenly divisible by f_LO_Step. + * + * @Div: OUTPUT: Whole number portion of the multiplier + * @FracN: OUTPUT: Fractional portion of the multiplier + * @f_LO: desired LO frequency. + * @f_LO_Step: Minimum step size for the LO (in Hz). + * @f_Ref: SRO frequency. + * @f_Avoid: Range of PLL frequencies to avoid near integer multiples + * of f_Ref (in Hz). + * + * Returns: Recalculated LO frequency. + */ +static u32 MT2063_CalcLO1Mult(u32 *Div, + u32 *FracN, + u32 f_LO, + u32 f_LO_Step, u32 f_Ref) +{ + /* Calculate the whole number portion of the divider */ + *Div = f_LO / f_Ref; + + /* Calculate the numerator value (round to nearest f_LO_Step) */ + *FracN = + (64 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) + + (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step); + + return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN, 64); +} + +/** + * CalcLO2Mult() - Calculates Integer divider value and the numerator + * value for a FracN PLL. + * + * This function assumes that the f_LO and f_Ref are + * evenly divisible by f_LO_Step. + * + * @Div: OUTPUT: Whole number portion of the multiplier + * @FracN: OUTPUT: Fractional portion of the multiplier + * @f_LO: desired LO frequency. + * @f_LO_Step: Minimum step size for the LO (in Hz). + * @f_Ref: SRO frequency. + * @f_Avoid: Range of PLL frequencies to avoid near + * integer multiples of f_Ref (in Hz). + * + * Returns: Recalculated LO frequency. + */ +static u32 MT2063_CalcLO2Mult(u32 *Div, + u32 *FracN, + u32 f_LO, + u32 f_LO_Step, u32 f_Ref) +{ + /* Calculate the whole number portion of the divider */ + *Div = f_LO / f_Ref; + + /* Calculate the numerator value (round to nearest f_LO_Step) */ + *FracN = + (8191 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) + + (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step); + + return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN, + 8191); +} + +/* + * FindClearTuneFilter() - Calculate the corrrect ClearTune filter to be + * used for a given input frequency. + * + * @state: ptr to tuner data structure + * @f_in: RF input center frequency (in Hz). + * + * Returns: ClearTune filter number (0-31) + */ +static u32 FindClearTuneFilter(struct mt2063_state *state, u32 f_in) +{ + u32 RFBand; + u32 idx; /* index loop */ + + /* + ** Find RF Band setting + */ + RFBand = 31; /* def when f_in > all */ + for (idx = 0; idx < 31; ++idx) { + if (state->CTFiltMax[idx] >= f_in) { + RFBand = idx; + break; + } + } + return RFBand; +} + +/* + * MT2063_Tune() - Change the tuner's tuned frequency to RFin. + */ +static u32 MT2063_Tune(struct mt2063_state *state, u32 f_in) +{ /* RF input center frequency */ + + u32 status = 0; + u32 LO1; /* 1st LO register value */ + u32 Num1; /* Numerator for LO1 reg. value */ + u32 f_IF1; /* 1st IF requested */ + u32 LO2; /* 2nd LO register value */ + u32 Num2; /* Numerator for LO2 reg. value */ + u32 ofLO1, ofLO2; /* last time's LO frequencies */ + u8 fiffc = 0x80; /* FIFF center freq from tuner */ + u32 fiffof; /* Offset from FIFF center freq */ + const u8 LO1LK = 0x80; /* Mask for LO1 Lock bit */ + u8 LO2LK = 0x08; /* Mask for LO2 Lock bit */ + u8 val; + u32 RFBand; + + dprintk(2, "\n"); + /* Check the input and output frequency ranges */ + if ((f_in < MT2063_MIN_FIN_FREQ) || (f_in > MT2063_MAX_FIN_FREQ)) + return -EINVAL; + + if ((state->AS_Data.f_out < MT2063_MIN_FOUT_FREQ) + || (state->AS_Data.f_out > MT2063_MAX_FOUT_FREQ)) + return -EINVAL; + + /* + * Save original LO1 and LO2 register values + */ + ofLO1 = state->AS_Data.f_LO1; + ofLO2 = state->AS_Data.f_LO2; + + /* + * Find and set RF Band setting + */ + if (state->ctfilt_sw == 1) { + val = (state->reg[MT2063_REG_CTUNE_CTRL] | 0x08); + if (state->reg[MT2063_REG_CTUNE_CTRL] != val) { + status |= + mt2063_setreg(state, MT2063_REG_CTUNE_CTRL, val); + } + val = state->reg[MT2063_REG_CTUNE_OV]; + RFBand = FindClearTuneFilter(state, f_in); + state->reg[MT2063_REG_CTUNE_OV] = + (u8) ((state->reg[MT2063_REG_CTUNE_OV] & ~0x1F) + | RFBand); + if (state->reg[MT2063_REG_CTUNE_OV] != val) { + status |= + mt2063_setreg(state, MT2063_REG_CTUNE_OV, val); + } + } + + /* + * Read the FIFF Center Frequency from the tuner + */ + if (status >= 0) { + status |= + mt2063_read(state, + MT2063_REG_FIFFC, + &state->reg[MT2063_REG_FIFFC], 1); + fiffc = state->reg[MT2063_REG_FIFFC]; + } + /* + * Assign in the requested values + */ + state->AS_Data.f_in = f_in; + /* Request a 1st IF such that LO1 is on a step size */ + state->AS_Data.f_if1_Request = + MT2063_Round_fLO(state->AS_Data.f_if1_Request + f_in, + state->AS_Data.f_LO1_Step, + state->AS_Data.f_ref) - f_in; + + /* + * Calculate frequency settings. f_IF1_FREQ + f_in is the + * desired LO1 frequency + */ + MT2063_ResetExclZones(&state->AS_Data); + + f_IF1 = MT2063_ChooseFirstIF(&state->AS_Data); + + state->AS_Data.f_LO1 = + MT2063_Round_fLO(f_IF1 + f_in, state->AS_Data.f_LO1_Step, + state->AS_Data.f_ref); + + state->AS_Data.f_LO2 = + MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in, + state->AS_Data.f_LO2_Step, state->AS_Data.f_ref); + + /* + * Check for any LO spurs in the output bandwidth and adjust + * the LO settings to avoid them if needed + */ + status |= MT2063_AvoidSpurs(&state->AS_Data); + /* + * MT_AvoidSpurs spurs may have changed the LO1 & LO2 values. + * Recalculate the LO frequencies and the values to be placed + * in the tuning registers. + */ + state->AS_Data.f_LO1 = + MT2063_CalcLO1Mult(&LO1, &Num1, state->AS_Data.f_LO1, + state->AS_Data.f_LO1_Step, state->AS_Data.f_ref); + state->AS_Data.f_LO2 = + MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in, + state->AS_Data.f_LO2_Step, state->AS_Data.f_ref); + state->AS_Data.f_LO2 = + MT2063_CalcLO2Mult(&LO2, &Num2, state->AS_Data.f_LO2, + state->AS_Data.f_LO2_Step, state->AS_Data.f_ref); + + /* + * Check the upconverter and downconverter frequency ranges + */ + if ((state->AS_Data.f_LO1 < MT2063_MIN_UPC_FREQ) + || (state->AS_Data.f_LO1 > MT2063_MAX_UPC_FREQ)) + status |= MT2063_UPC_RANGE; + if ((state->AS_Data.f_LO2 < MT2063_MIN_DNC_FREQ) + || (state->AS_Data.f_LO2 > MT2063_MAX_DNC_FREQ)) + status |= MT2063_DNC_RANGE; + /* LO2 Lock bit was in a different place for B0 version */ + if (state->tuner_id == MT2063_B0) + LO2LK = 0x40; + + /* + * If we have the same LO frequencies and we're already locked, + * then skip re-programming the LO registers. + */ + if ((ofLO1 != state->AS_Data.f_LO1) + || (ofLO2 != state->AS_Data.f_LO2) + || ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) != + (LO1LK | LO2LK))) { + /* + * Calculate the FIFFOF register value + * + * IF1_Actual + * FIFFOF = ------------ - 8 * FIFFC - 4992 + * f_ref/64 + */ + fiffof = + (state->AS_Data.f_LO1 - + f_in) / (state->AS_Data.f_ref / 64) - 8 * (u32) fiffc - + 4992; + if (fiffof > 0xFF) + fiffof = 0xFF; + + /* + * Place all of the calculated values into the local tuner + * register fields. + */ + if (status >= 0) { + state->reg[MT2063_REG_LO1CQ_1] = (u8) (LO1 & 0xFF); /* DIV1q */ + state->reg[MT2063_REG_LO1CQ_2] = (u8) (Num1 & 0x3F); /* NUM1q */ + state->reg[MT2063_REG_LO2CQ_1] = (u8) (((LO2 & 0x7F) << 1) /* DIV2q */ + |(Num2 >> 12)); /* NUM2q (hi) */ + state->reg[MT2063_REG_LO2CQ_2] = (u8) ((Num2 & 0x0FF0) >> 4); /* NUM2q (mid) */ + state->reg[MT2063_REG_LO2CQ_3] = (u8) (0xE0 | (Num2 & 0x000F)); /* NUM2q (lo) */ + + /* + * Now write out the computed register values + * IMPORTANT: There is a required order for writing + * (0x05 must follow all the others). + */ + status |= mt2063_write(state, MT2063_REG_LO1CQ_1, &state->reg[MT2063_REG_LO1CQ_1], 5); /* 0x01 - 0x05 */ + if (state->tuner_id == MT2063_B0) { + /* Re-write the one-shot bits to trigger the tune operation */ + status |= mt2063_write(state, MT2063_REG_LO2CQ_3, &state->reg[MT2063_REG_LO2CQ_3], 1); /* 0x05 */ + } + /* Write out the FIFF offset only if it's changing */ + if (state->reg[MT2063_REG_FIFF_OFFSET] != + (u8) fiffof) { + state->reg[MT2063_REG_FIFF_OFFSET] = + (u8) fiffof; + status |= + mt2063_write(state, + MT2063_REG_FIFF_OFFSET, + &state-> + reg[MT2063_REG_FIFF_OFFSET], + 1); + } + } + + /* + * Check for LO's locking + */ + + if (status < 0) + return status; + + status = mt2063_lockStatus(state); + if (status < 0) + return status; + if (!status) + return -EINVAL; /* Couldn't lock */ + + /* + * If we locked OK, assign calculated data to mt2063_state structure + */ + state->f_IF1_actual = state->AS_Data.f_LO1 - f_in; + } + + return status; +} + +static const u8 MT2063B0_defaults[] = { + /* Reg, Value */ + 0x19, 0x05, + 0x1B, 0x1D, + 0x1C, 0x1F, + 0x1D, 0x0F, + 0x1E, 0x3F, + 0x1F, 0x0F, + 0x20, 0x3F, + 0x22, 0x21, + 0x23, 0x3F, + 0x24, 0x20, + 0x25, 0x3F, + 0x27, 0xEE, + 0x2C, 0x27, /* bit at 0x20 is cleared below */ + 0x30, 0x03, + 0x2C, 0x07, /* bit at 0x20 is cleared here */ + 0x2D, 0x87, + 0x2E, 0xAA, + 0x28, 0xE1, /* Set the FIFCrst bit here */ + 0x28, 0xE0, /* Clear the FIFCrst bit here */ + 0x00 +}; + +/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */ +static const u8 MT2063B1_defaults[] = { + /* Reg, Value */ + 0x05, 0xF0, + 0x11, 0x10, /* New Enable AFCsd */ + 0x19, 0x05, + 0x1A, 0x6C, + 0x1B, 0x24, + 0x1C, 0x28, + 0x1D, 0x8F, + 0x1E, 0x14, + 0x1F, 0x8F, + 0x20, 0x57, + 0x22, 0x21, /* New - ver 1.03 */ + 0x23, 0x3C, /* New - ver 1.10 */ + 0x24, 0x20, /* New - ver 1.03 */ + 0x2C, 0x24, /* bit at 0x20 is cleared below */ + 0x2D, 0x87, /* FIFFQ=0 */ + 0x2F, 0xF3, + 0x30, 0x0C, /* New - ver 1.11 */ + 0x31, 0x1B, /* New - ver 1.11 */ + 0x2C, 0x04, /* bit at 0x20 is cleared here */ + 0x28, 0xE1, /* Set the FIFCrst bit here */ + 0x28, 0xE0, /* Clear the FIFCrst bit here */ + 0x00 +}; + +/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */ +static const u8 MT2063B3_defaults[] = { + /* Reg, Value */ + 0x05, 0xF0, + 0x19, 0x3D, + 0x2C, 0x24, /* bit at 0x20 is cleared below */ + 0x2C, 0x04, /* bit at 0x20 is cleared here */ + 0x28, 0xE1, /* Set the FIFCrst bit here */ + 0x28, 0xE0, /* Clear the FIFCrst bit here */ + 0x00 +}; + +static int mt2063_init(struct dvb_frontend *fe) +{ + u32 status; + struct mt2063_state *state = fe->tuner_priv; + u8 all_resets = 0xF0; /* reset/load bits */ + const u8 *def = NULL; + char *step; + u32 FCRUN; + s32 maxReads; + u32 fcu_osc; + u32 i; + + dprintk(2, "\n"); + + state->rcvr_mode = MT2063_CABLE_QAM; + + /* Read the Part/Rev code from the tuner */ + status = mt2063_read(state, MT2063_REG_PART_REV, + &state->reg[MT2063_REG_PART_REV], 1); + if (status < 0) { + printk(KERN_ERR "Can't read mt2063 part ID\n"); + return status; + } + + /* Check the part/rev code */ + switch (state->reg[MT2063_REG_PART_REV]) { + case MT2063_B0: + step = "B0"; + break; + case MT2063_B1: + step = "B1"; + break; + case MT2063_B2: + step = "B2"; + break; + case MT2063_B3: + step = "B3"; + break; + default: + printk(KERN_ERR "mt2063: Unknown mt2063 device ID (0x%02x)\n", + state->reg[MT2063_REG_PART_REV]); + return -ENODEV; /* Wrong tuner Part/Rev code */ + } + + /* Check the 2nd byte of the Part/Rev code from the tuner */ + status = mt2063_read(state, MT2063_REG_RSVD_3B, + &state->reg[MT2063_REG_RSVD_3B], 1); + + /* b7 != 0 ==> NOT MT2063 */ + if (status < 0 || ((state->reg[MT2063_REG_RSVD_3B] & 0x80) != 0x00)) { + printk(KERN_ERR "mt2063: Unknown part ID (0x%02x%02x)\n", + state->reg[MT2063_REG_PART_REV], + state->reg[MT2063_REG_RSVD_3B]); + return -ENODEV; /* Wrong tuner Part/Rev code */ + } + + printk(KERN_INFO "mt2063: detected a mt2063 %s\n", step); + + /* Reset the tuner */ + status = mt2063_write(state, MT2063_REG_LO2CQ_3, &all_resets, 1); + if (status < 0) + return status; + + /* change all of the default values that vary from the HW reset values */ + /* def = (state->reg[PART_REV] == MT2063_B0) ? MT2063B0_defaults : MT2063B1_defaults; */ + switch (state->reg[MT2063_REG_PART_REV]) { + case MT2063_B3: + def = MT2063B3_defaults; + break; + + case MT2063_B1: + def = MT2063B1_defaults; + break; + + case MT2063_B0: + def = MT2063B0_defaults; + break; + + default: + return -ENODEV; + break; + } + + while (status >= 0 && *def) { + u8 reg = *def++; + u8 val = *def++; + status = mt2063_write(state, reg, &val, 1); + } + if (status < 0) + return status; + + /* Wait for FIFF location to complete. */ + FCRUN = 1; + maxReads = 10; + while (status >= 0 && (FCRUN != 0) && (maxReads-- > 0)) { + msleep(2); + status = mt2063_read(state, + MT2063_REG_XO_STATUS, + &state-> + reg[MT2063_REG_XO_STATUS], 1); + FCRUN = (state->reg[MT2063_REG_XO_STATUS] & 0x40) >> 6; + } + + if (FCRUN != 0 || status < 0) + return -ENODEV; + + status = mt2063_read(state, + MT2063_REG_FIFFC, + &state->reg[MT2063_REG_FIFFC], 1); + if (status < 0) + return status; + + /* Read back all the registers from the tuner */ + status = mt2063_read(state, + MT2063_REG_PART_REV, + state->reg, MT2063_REG_END_REGS); + if (status < 0) + return status; + + /* Initialize the tuner state. */ + state->tuner_id = state->reg[MT2063_REG_PART_REV]; + state->AS_Data.f_ref = MT2063_REF_FREQ; + state->AS_Data.f_if1_Center = (state->AS_Data.f_ref / 8) * + ((u32) state->reg[MT2063_REG_FIFFC] + 640); + state->AS_Data.f_if1_bw = MT2063_IF1_BW; + state->AS_Data.f_out = 43750000UL; + state->AS_Data.f_out_bw = 6750000UL; + state->AS_Data.f_zif_bw = MT2063_ZIF_BW; + state->AS_Data.f_LO1_Step = state->AS_Data.f_ref / 64; + state->AS_Data.f_LO2_Step = MT2063_TUNE_STEP_SIZE; + state->AS_Data.maxH1 = MT2063_MAX_HARMONICS_1; + state->AS_Data.maxH2 = MT2063_MAX_HARMONICS_2; + state->AS_Data.f_min_LO_Separation = MT2063_MIN_LO_SEP; + state->AS_Data.f_if1_Request = state->AS_Data.f_if1_Center; + state->AS_Data.f_LO1 = 2181000000UL; + state->AS_Data.f_LO2 = 1486249786UL; + state->f_IF1_actual = state->AS_Data.f_if1_Center; + state->AS_Data.f_in = state->AS_Data.f_LO1 - state->f_IF1_actual; + state->AS_Data.f_LO1_FracN_Avoid = MT2063_LO1_FRACN_AVOID; + state->AS_Data.f_LO2_FracN_Avoid = MT2063_LO2_FRACN_AVOID; + state->num_regs = MT2063_REG_END_REGS; + state->AS_Data.avoidDECT = MT2063_AVOID_BOTH; + state->ctfilt_sw = 0; + + state->CTFiltMax[0] = 69230000; + state->CTFiltMax[1] = 105770000; + state->CTFiltMax[2] = 140350000; + state->CTFiltMax[3] = 177110000; + state->CTFiltMax[4] = 212860000; + state->CTFiltMax[5] = 241130000; + state->CTFiltMax[6] = 274370000; + state->CTFiltMax[7] = 309820000; + state->CTFiltMax[8] = 342450000; + state->CTFiltMax[9] = 378870000; + state->CTFiltMax[10] = 416210000; + state->CTFiltMax[11] = 456500000; + state->CTFiltMax[12] = 495790000; + state->CTFiltMax[13] = 534530000; + state->CTFiltMax[14] = 572610000; + state->CTFiltMax[15] = 598970000; + state->CTFiltMax[16] = 635910000; + state->CTFiltMax[17] = 672130000; + state->CTFiltMax[18] = 714840000; + state->CTFiltMax[19] = 739660000; + state->CTFiltMax[20] = 770410000; + state->CTFiltMax[21] = 814660000; + state->CTFiltMax[22] = 846950000; + state->CTFiltMax[23] = 867820000; + state->CTFiltMax[24] = 915980000; + state->CTFiltMax[25] = 947450000; + state->CTFiltMax[26] = 983110000; + state->CTFiltMax[27] = 1021630000; + state->CTFiltMax[28] = 1061870000; + state->CTFiltMax[29] = 1098330000; + state->CTFiltMax[30] = 1138990000; + + /* + ** Fetch the FCU osc value and use it and the fRef value to + ** scale all of the Band Max values + */ + + state->reg[MT2063_REG_CTUNE_CTRL] = 0x0A; + status = mt2063_write(state, MT2063_REG_CTUNE_CTRL, + &state->reg[MT2063_REG_CTUNE_CTRL], 1); + if (status < 0) + return status; + + /* Read the ClearTune filter calibration value */ + status = mt2063_read(state, MT2063_REG_FIFFC, + &state->reg[MT2063_REG_FIFFC], 1); + if (status < 0) + return status; + + fcu_osc = state->reg[MT2063_REG_FIFFC]; + + state->reg[MT2063_REG_CTUNE_CTRL] = 0x00; + status = mt2063_write(state, MT2063_REG_CTUNE_CTRL, + &state->reg[MT2063_REG_CTUNE_CTRL], 1); + if (status < 0) + return status; + + /* Adjust each of the values in the ClearTune filter cross-over table */ + for (i = 0; i < 31; i++) + state->CTFiltMax[i] = (state->CTFiltMax[i] / 768) * (fcu_osc + 640); + + status = MT2063_SoftwareShutdown(state, 1); + if (status < 0) + return status; + status = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD); + if (status < 0) + return status; + + state->init = true; + + return 0; +} + +static int mt2063_get_status(struct dvb_frontend *fe, u32 *tuner_status) +{ + struct mt2063_state *state = fe->tuner_priv; + int status; + + dprintk(2, "\n"); + + if (!state->init) + return -ENODEV; + + *tuner_status = 0; + status = mt2063_lockStatus(state); + if (status < 0) + return status; + if (status) + *tuner_status = TUNER_STATUS_LOCKED; + + dprintk(1, "Tuner status: %d", *tuner_status); + + return 0; +} + +static int mt2063_release(struct dvb_frontend *fe) +{ + struct mt2063_state *state = fe->tuner_priv; + + dprintk(2, "\n"); + + fe->tuner_priv = NULL; + kfree(state); + + return 0; +} + +static int mt2063_set_analog_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct mt2063_state *state = fe->tuner_priv; + s32 pict_car; + s32 pict2chanb_vsb; + s32 ch_bw; + s32 if_mid; + s32 rcvr_mode; + int status; + + dprintk(2, "\n"); + + if (!state->init) { + status = mt2063_init(fe); + if (status < 0) + return status; + } + + switch (params->mode) { + case V4L2_TUNER_RADIO: + pict_car = 38900000; + ch_bw = 8000000; + pict2chanb_vsb = -(ch_bw / 2); + rcvr_mode = MT2063_OFFAIR_ANALOG; + break; + case V4L2_TUNER_ANALOG_TV: + rcvr_mode = MT2063_CABLE_ANALOG; + if (params->std & ~V4L2_STD_MN) { + pict_car = 38900000; + ch_bw = 6000000; + pict2chanb_vsb = -1250000; + } else if (params->std & V4L2_STD_PAL_G) { + pict_car = 38900000; + ch_bw = 7000000; + pict2chanb_vsb = -1250000; + } else { /* PAL/SECAM standards */ + pict_car = 38900000; + ch_bw = 8000000; + pict2chanb_vsb = -1250000; + } + break; + default: + return -EINVAL; + } + if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2)); + + state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */ + state->AS_Data.f_out = if_mid; + state->AS_Data.f_out_bw = ch_bw + 750000; + status = MT2063_SetReceiverMode(state, rcvr_mode); + if (status < 0) + return status; + + dprintk(1, "Tuning to frequency: %d, bandwidth %d, foffset %d\n", + params->frequency, ch_bw, pict2chanb_vsb); + + status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2)))); + if (status < 0) + return status; + + state->frequency = params->frequency; + return 0; +} + +/* + * As defined on EN 300 429, the DVB-C roll-off factor is 0.15. + * So, the amount of the needed bandwith is given by: + * Bw = Symbol_rate * (1 + 0.15) + * As such, the maximum symbol rate supported by 6 MHz is given by: + * max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds + */ +#define MAX_SYMBOL_RATE_6MHz 5217391 + +static int mt2063_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct mt2063_state *state = fe->tuner_priv; + int status; + s32 pict_car; + s32 pict2chanb_vsb; + s32 ch_bw; + s32 if_mid; + s32 rcvr_mode; + + if (!state->init) { + status = mt2063_init(fe); + if (status < 0) + return status; + } + + dprintk(2, "\n"); + + if (c->bandwidth_hz == 0) + return -EINVAL; + if (c->bandwidth_hz <= 6000000) + ch_bw = 6000000; + else if (c->bandwidth_hz <= 7000000) + ch_bw = 7000000; + else + ch_bw = 8000000; + + switch (c->delivery_system) { + case SYS_DVBT: + rcvr_mode = MT2063_OFFAIR_COFDM; + pict_car = 36125000; + pict2chanb_vsb = -(ch_bw / 2); + break; + case SYS_DVBC_ANNEX_A: + case SYS_DVBC_ANNEX_C: + rcvr_mode = MT2063_CABLE_QAM; + pict_car = 36125000; + pict2chanb_vsb = -(ch_bw / 2); + break; + default: + return -EINVAL; + } + if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2)); + + state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */ + state->AS_Data.f_out = if_mid; + state->AS_Data.f_out_bw = ch_bw + 750000; + status = MT2063_SetReceiverMode(state, rcvr_mode); + if (status < 0) + return status; + + dprintk(1, "Tuning to frequency: %d, bandwidth %d, foffset %d\n", + c->frequency, ch_bw, pict2chanb_vsb); + + status = MT2063_Tune(state, (c->frequency + (pict2chanb_vsb + (ch_bw / 2)))); + + if (status < 0) + return status; + + state->frequency = c->frequency; + return 0; +} + +static int mt2063_get_if_frequency(struct dvb_frontend *fe, u32 *freq) +{ + struct mt2063_state *state = fe->tuner_priv; + + dprintk(2, "\n"); + + if (!state->init) + return -ENODEV; + + *freq = state->AS_Data.f_out; + + dprintk(1, "IF frequency: %d\n", *freq); + + return 0; +} + +static int mt2063_get_bandwidth(struct dvb_frontend *fe, u32 *bw) +{ + struct mt2063_state *state = fe->tuner_priv; + + dprintk(2, "\n"); + + if (!state->init) + return -ENODEV; + + *bw = state->AS_Data.f_out_bw - 750000; + + dprintk(1, "bandwidth: %d\n", *bw); + + return 0; +} + +static struct dvb_tuner_ops mt2063_ops = { + .info = { + .name = "MT2063 Silicon Tuner", + .frequency_min = 45000000, + .frequency_max = 865000000, + .frequency_step = 0, + }, + + .init = mt2063_init, + .sleep = MT2063_Sleep, + .get_status = mt2063_get_status, + .set_analog_params = mt2063_set_analog_params, + .set_params = mt2063_set_params, + .get_if_frequency = mt2063_get_if_frequency, + .get_bandwidth = mt2063_get_bandwidth, + .release = mt2063_release, +}; + +struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe, + struct mt2063_config *config, + struct i2c_adapter *i2c) +{ + struct mt2063_state *state = NULL; + + dprintk(2, "\n"); + + state = kzalloc(sizeof(struct mt2063_state), GFP_KERNEL); + if (state == NULL) + goto error; + + state->config = config; + state->i2c = i2c; + state->frontend = fe; + state->reference = config->refclock / 1000; /* kHz */ + fe->tuner_priv = state; + fe->ops.tuner_ops = mt2063_ops; + + printk(KERN_INFO "%s: Attaching MT2063\n", __func__); + return fe; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL_GPL(mt2063_attach); + +/* + * Ancillary routines visible outside mt2063 + * FIXME: Remove them in favor of using standard tuner callbacks + */ +unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe) +{ + struct mt2063_state *state = fe->tuner_priv; + int err = 0; + + dprintk(2, "\n"); + + err = MT2063_SoftwareShutdown(state, 1); + if (err < 0) + printk(KERN_ERR "%s: Couldn't shutdown\n", __func__); + + return err; +} +EXPORT_SYMBOL_GPL(tuner_MT2063_SoftwareShutdown); + +unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe) +{ + struct mt2063_state *state = fe->tuner_priv; + int err = 0; + + dprintk(2, "\n"); + + err = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD); + if (err < 0) + printk(KERN_ERR "%s: Invalid parameter\n", __func__); + + return err; +} +EXPORT_SYMBOL_GPL(tuner_MT2063_ClearPowerMaskBits); + +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_DESCRIPTION("MT2063 Silicon tuner"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/mt2063.h b/drivers/media/tuners/mt2063.h new file mode 100644 index 000000000000..3f5cfd93713f --- /dev/null +++ b/drivers/media/tuners/mt2063.h @@ -0,0 +1,32 @@ +#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/tuners/mt20xx.c b/drivers/media/tuners/mt20xx.c new file mode 100644 index 000000000000..0e74e97e0d1a --- /dev/null +++ b/drivers/media/tuners/mt20xx.c @@ -0,0 +1,670 @@ +/* + * i2c tv tuner chip device driver + * controls microtune tuners, mt2032 + mt2050 at the moment. + * + * This "mt20xx" module was split apart from the original "tuner" module. + */ +#include +#include +#include +#include +#include "tuner-i2c.h" +#include "mt20xx.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable verbose debug messages"); + +/* ---------------------------------------------------------------------- */ + +static unsigned int optimize_vco = 1; +module_param(optimize_vco, int, 0644); + +static unsigned int tv_antenna = 1; +module_param(tv_antenna, int, 0644); + +static unsigned int radio_antenna; +module_param(radio_antenna, int, 0644); + +/* ---------------------------------------------------------------------- */ + +#define MT2032 0x04 +#define MT2030 0x06 +#define MT2040 0x07 +#define MT2050 0x42 + +static char *microtune_part[] = { + [ MT2030 ] = "MT2030", + [ MT2032 ] = "MT2032", + [ MT2040 ] = "MT2040", + [ MT2050 ] = "MT2050", +}; + +struct microtune_priv { + struct tuner_i2c_props i2c_props; + + unsigned int xogc; + //unsigned int radio_if2; + + u32 frequency; +}; + +static int microtune_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + + return 0; +} + +static int microtune_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct microtune_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +// IsSpurInBand()? +static int mt2032_spurcheck(struct dvb_frontend *fe, + int f1, int f2, int spectrum_from,int spectrum_to) +{ + struct microtune_priv *priv = fe->tuner_priv; + int n1=1,n2,f; + + f1=f1/1000; //scale to kHz to avoid 32bit overflows + f2=f2/1000; + spectrum_from/=1000; + spectrum_to/=1000; + + tuner_dbg("spurcheck f1=%d f2=%d from=%d to=%d\n", + f1,f2,spectrum_from,spectrum_to); + + do { + n2=-n1; + f=n1*(f1-f2); + do { + n2--; + f=f-f2; + tuner_dbg("spurtest n1=%d n2=%d ftest=%d\n",n1,n2,f); + + if( (f>spectrum_from) && (f(f2-spectrum_to)) || (n2>-5)); + n1++; + } while (n1<5); + + return 1; +} + +static int mt2032_compute_freq(struct dvb_frontend *fe, + unsigned int rfin, + unsigned int if1, unsigned int if2, + unsigned int spectrum_from, + unsigned int spectrum_to, + unsigned char *buf, + int *ret_sel, + unsigned int xogc) //all in Hz +{ + struct microtune_priv *priv = fe->tuner_priv; + unsigned int fref,lo1,lo1n,lo1a,s,sel,lo1freq, desired_lo1, + desired_lo2,lo2,lo2n,lo2a,lo2num,lo2freq; + + fref= 5250 *1000; //5.25MHz + desired_lo1=rfin+if1; + + lo1=(2*(desired_lo1/1000)+(fref/1000)) / (2*fref/1000); + lo1n=lo1/8; + lo1a=lo1-(lo1n*8); + + s=rfin/1000/1000+1090; + + if(optimize_vco) { + if(s>1890) sel=0; + else if(s>1720) sel=1; + else if(s>1530) sel=2; + else if(s>1370) sel=3; + else sel=4; // >1090 + } + else { + if(s>1790) sel=0; // <1958 + else if(s>1617) sel=1; + else if(s>1449) sel=2; + else if(s>1291) sel=3; + else sel=4; // >1090 + } + *ret_sel=sel; + + lo1freq=(lo1a+8*lo1n)*fref; + + tuner_dbg("mt2032: rfin=%d lo1=%d lo1n=%d lo1a=%d sel=%d, lo1freq=%d\n", + rfin,lo1,lo1n,lo1a,sel,lo1freq); + + desired_lo2=lo1freq-rfin-if2; + lo2=(desired_lo2)/fref; + lo2n=lo2/8; + lo2a=lo2-(lo2n*8); + lo2num=((desired_lo2/1000)%(fref/1000))* 3780/(fref/1000); //scale to fit in 32bit arith + lo2freq=(lo2a+8*lo2n)*fref + lo2num*(fref/1000)/3780*1000; + + tuner_dbg("mt2032: rfin=%d lo2=%d lo2n=%d lo2a=%d num=%d lo2freq=%d\n", + rfin,lo2,lo2n,lo2a,lo2num,lo2freq); + + if (lo1a > 7 || lo1n < 17 || lo1n > 48 || lo2a > 7 || lo2n < 17 || + lo2n > 30) { + tuner_info("mt2032: frequency parameters out of range: %d %d %d %d\n", + lo1a, lo1n, lo2a,lo2n); + return(-1); + } + + mt2032_spurcheck(fe, lo1freq, desired_lo2, spectrum_from, spectrum_to); + // should recalculate lo1 (one step up/down) + + // set up MT2032 register map for transfer over i2c + buf[0]=lo1n-1; + buf[1]=lo1a | (sel<<4); + buf[2]=0x86; // LOGC + buf[3]=0x0f; //reserved + buf[4]=0x1f; + buf[5]=(lo2n-1) | (lo2a<<5); + if(rfin >400*1000*1000) + buf[6]=0xe4; + else + buf[6]=0xf4; // set PKEN per rev 1.2 + buf[7]=8+xogc; + buf[8]=0xc3; //reserved + buf[9]=0x4e; //reserved + buf[10]=0xec; //reserved + buf[11]=(lo2num&0xff); + buf[12]=(lo2num>>8) |0x80; // Lo2RST + + return 0; +} + +static int mt2032_check_lo_lock(struct dvb_frontend *fe) +{ + struct microtune_priv *priv = fe->tuner_priv; + int try,lock=0; + unsigned char buf[2]; + + for(try=0;try<10;try++) { + buf[0]=0x0e; + tuner_i2c_xfer_send(&priv->i2c_props,buf,1); + tuner_i2c_xfer_recv(&priv->i2c_props,buf,1); + tuner_dbg("mt2032 Reg.E=0x%02x\n",buf[0]); + lock=buf[0] &0x06; + + if (lock==6) + break; + + tuner_dbg("mt2032: pll wait 1ms for lock (0x%2x)\n",buf[0]); + udelay(1000); + } + return lock; +} + +static int mt2032_optimize_vco(struct dvb_frontend *fe,int sel,int lock) +{ + struct microtune_priv *priv = fe->tuner_priv; + unsigned char buf[2]; + int tad1; + + buf[0]=0x0f; + tuner_i2c_xfer_send(&priv->i2c_props,buf,1); + tuner_i2c_xfer_recv(&priv->i2c_props,buf,1); + tuner_dbg("mt2032 Reg.F=0x%02x\n",buf[0]); + tad1=buf[0]&0x07; + + if(tad1 ==0) return lock; + if(tad1 ==1) return lock; + + if(tad1==2) { + if(sel==0) + return lock; + else sel--; + } + else { + if(sel<4) + sel++; + else + return lock; + } + + tuner_dbg("mt2032 optimize_vco: sel=%d\n",sel); + + buf[0]=0x0f; + buf[1]=sel; + tuner_i2c_xfer_send(&priv->i2c_props,buf,2); + lock=mt2032_check_lo_lock(fe); + return lock; +} + + +static void mt2032_set_if_freq(struct dvb_frontend *fe, unsigned int rfin, + unsigned int if1, unsigned int if2, + unsigned int from, unsigned int to) +{ + unsigned char buf[21]; + int lint_try,ret,sel,lock=0; + struct microtune_priv *priv = fe->tuner_priv; + + tuner_dbg("mt2032_set_if_freq rfin=%d if1=%d if2=%d from=%d to=%d\n", + rfin,if1,if2,from,to); + + buf[0]=0; + ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,1); + tuner_i2c_xfer_recv(&priv->i2c_props,buf,21); + + buf[0]=0; + ret=mt2032_compute_freq(fe,rfin,if1,if2,from,to,&buf[1],&sel,priv->xogc); + if (ret<0) + return; + + // send only the relevant registers per Rev. 1.2 + buf[0]=0; + ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,4); + buf[5]=5; + ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+5,4); + buf[11]=11; + ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+11,3); + if(ret!=3) + tuner_warn("i2c i/o error: rc == %d (should be 3)\n",ret); + + // wait for PLLs to lock (per manual), retry LINT if not. + for(lint_try=0; lint_try<2; lint_try++) { + lock=mt2032_check_lo_lock(fe); + + if(optimize_vco) + lock=mt2032_optimize_vco(fe,sel,lock); + if(lock==6) break; + + tuner_dbg("mt2032: re-init PLLs by LINT\n"); + buf[0]=7; + buf[1]=0x80 +8+priv->xogc; // set LINT to re-init PLLs + tuner_i2c_xfer_send(&priv->i2c_props,buf,2); + mdelay(10); + buf[1]=8+priv->xogc; + tuner_i2c_xfer_send(&priv->i2c_props,buf,2); + } + + if (lock!=6) + tuner_warn("MT2032 Fatal Error: PLLs didn't lock.\n"); + + buf[0]=2; + buf[1]=0x20; // LOGC for optimal phase noise + ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); + if (ret!=2) + tuner_warn("i2c i/o error: rc == %d (should be 2)\n",ret); +} + + +static int mt2032_set_tv_freq(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + int if2,from,to; + + // signal bandwidth and picture carrier + if (params->std & V4L2_STD_525_60) { + // NTSC + from = 40750*1000; + to = 46750*1000; + if2 = 45750*1000; + } else { + // PAL + from = 32900*1000; + to = 39900*1000; + if2 = 38900*1000; + } + + mt2032_set_if_freq(fe, params->frequency*62500, + 1090*1000*1000, if2, from, to); + + return 0; +} + +static int mt2032_set_radio_freq(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct microtune_priv *priv = fe->tuner_priv; + int if2; + + if (params->std & V4L2_STD_525_60) { + tuner_dbg("pinnacle ntsc\n"); + if2 = 41300 * 1000; + } else { + tuner_dbg("pinnacle pal\n"); + if2 = 33300 * 1000; + } + + // per Manual for FM tuning: first if center freq. 1085 MHz + mt2032_set_if_freq(fe, params->frequency * 125 / 2, + 1085*1000*1000,if2,if2,if2); + + return 0; +} + +static int mt2032_set_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct microtune_priv *priv = fe->tuner_priv; + int ret = -EINVAL; + + switch (params->mode) { + case V4L2_TUNER_RADIO: + ret = mt2032_set_radio_freq(fe, params); + priv->frequency = params->frequency * 125 / 2; + break; + case V4L2_TUNER_ANALOG_TV: + case V4L2_TUNER_DIGITAL_TV: + ret = mt2032_set_tv_freq(fe, params); + priv->frequency = params->frequency * 62500; + break; + } + + return ret; +} + +static struct dvb_tuner_ops mt2032_tuner_ops = { + .set_analog_params = mt2032_set_params, + .release = microtune_release, + .get_frequency = microtune_get_frequency, +}; + +// Initialization as described in "MT203x Programming Procedures", Rev 1.2, Feb.2001 +static int mt2032_init(struct dvb_frontend *fe) +{ + struct microtune_priv *priv = fe->tuner_priv; + unsigned char buf[21]; + int ret,xogc,xok=0; + + // Initialize Registers per spec. + buf[1]=2; // Index to register 2 + buf[2]=0xff; + buf[3]=0x0f; + buf[4]=0x1f; + ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+1,4); + + buf[5]=6; // Index register 6 + buf[6]=0xe4; + buf[7]=0x8f; + buf[8]=0xc3; + buf[9]=0x4e; + buf[10]=0xec; + ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+5,6); + + buf[12]=13; // Index register 13 + buf[13]=0x32; + ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+12,2); + + // Adjust XOGC (register 7), wait for XOK + xogc=7; + do { + tuner_dbg("mt2032: xogc = 0x%02x\n",xogc&0x07); + mdelay(10); + buf[0]=0x0e; + tuner_i2c_xfer_send(&priv->i2c_props,buf,1); + tuner_i2c_xfer_recv(&priv->i2c_props,buf,1); + xok=buf[0]&0x01; + tuner_dbg("mt2032: xok = 0x%02x\n",xok); + if (xok == 1) break; + + xogc--; + tuner_dbg("mt2032: xogc = 0x%02x\n",xogc&0x07); + if (xogc == 3) { + xogc=4; // min. 4 per spec + break; + } + buf[0]=0x07; + buf[1]=0x88 + xogc; + ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); + if (ret!=2) + tuner_warn("i2c i/o error: rc == %d (should be 2)\n",ret); + } while (xok != 1 ); + priv->xogc=xogc; + + memcpy(&fe->ops.tuner_ops, &mt2032_tuner_ops, sizeof(struct dvb_tuner_ops)); + + return(1); +} + +static void mt2050_set_antenna(struct dvb_frontend *fe, unsigned char antenna) +{ + struct microtune_priv *priv = fe->tuner_priv; + unsigned char buf[2]; + + buf[0] = 6; + buf[1] = antenna ? 0x11 : 0x10; + tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); + tuner_dbg("mt2050: enabled antenna connector %d\n", antenna); +} + +static void mt2050_set_if_freq(struct dvb_frontend *fe,unsigned int freq, unsigned int if2) +{ + struct microtune_priv *priv = fe->tuner_priv; + unsigned int if1=1218*1000*1000; + unsigned int f_lo1,f_lo2,lo1,lo2,f_lo1_modulo,f_lo2_modulo,num1,num2,div1a,div1b,div2a,div2b; + int ret; + unsigned char buf[6]; + + tuner_dbg("mt2050_set_if_freq freq=%d if1=%d if2=%d\n", + freq,if1,if2); + + f_lo1=freq+if1; + f_lo1=(f_lo1/1000000)*1000000; + + f_lo2=f_lo1-freq-if2; + f_lo2=(f_lo2/50000)*50000; + + lo1=f_lo1/4000000; + lo2=f_lo2/4000000; + + f_lo1_modulo= f_lo1-(lo1*4000000); + f_lo2_modulo= f_lo2-(lo2*4000000); + + num1=4*f_lo1_modulo/4000000; + num2=4096*(f_lo2_modulo/1000)/4000; + + // todo spurchecks + + div1a=(lo1/12)-1; + div1b=lo1-(div1a+1)*12; + + div2a=(lo2/8)-1; + div2b=lo2-(div2a+1)*8; + + if (debug > 1) { + tuner_dbg("lo1 lo2 = %d %d\n", lo1, lo2); + tuner_dbg("num1 num2 div1a div1b div2a div2b= %x %x %x %x %x %x\n", + num1,num2,div1a,div1b,div2a,div2b); + } + + buf[0]=1; + buf[1]= 4*div1b + num1; + if(freq<275*1000*1000) buf[1] = buf[1]|0x80; + + buf[2]=div1a; + buf[3]=32*div2b + num2/256; + buf[4]=num2-(num2/256)*256; + buf[5]=div2a; + if(num2!=0) buf[5]=buf[5]|0x40; + + if (debug > 1) { + int i; + tuner_dbg("bufs is: "); + for(i=0;i<6;i++) + printk("%x ",buf[i]); + printk("\n"); + } + + ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,6); + if (ret!=6) + tuner_warn("i2c i/o error: rc == %d (should be 6)\n",ret); +} + +static int mt2050_set_tv_freq(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + unsigned int if2; + + if (params->std & V4L2_STD_525_60) { + // NTSC + if2 = 45750*1000; + } else { + // PAL + if2 = 38900*1000; + } + if (V4L2_TUNER_DIGITAL_TV == params->mode) { + // DVB (pinnacle 300i) + if2 = 36150*1000; + } + mt2050_set_if_freq(fe, params->frequency*62500, if2); + mt2050_set_antenna(fe, tv_antenna); + + return 0; +} + +static int mt2050_set_radio_freq(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct microtune_priv *priv = fe->tuner_priv; + int if2; + + if (params->std & V4L2_STD_525_60) { + tuner_dbg("pinnacle ntsc\n"); + if2 = 41300 * 1000; + } else { + tuner_dbg("pinnacle pal\n"); + if2 = 33300 * 1000; + } + + mt2050_set_if_freq(fe, params->frequency * 125 / 2, if2); + mt2050_set_antenna(fe, radio_antenna); + + return 0; +} + +static int mt2050_set_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct microtune_priv *priv = fe->tuner_priv; + int ret = -EINVAL; + + switch (params->mode) { + case V4L2_TUNER_RADIO: + ret = mt2050_set_radio_freq(fe, params); + priv->frequency = params->frequency * 125 / 2; + break; + case V4L2_TUNER_ANALOG_TV: + case V4L2_TUNER_DIGITAL_TV: + ret = mt2050_set_tv_freq(fe, params); + priv->frequency = params->frequency * 62500; + break; + } + + return ret; +} + +static struct dvb_tuner_ops mt2050_tuner_ops = { + .set_analog_params = mt2050_set_params, + .release = microtune_release, + .get_frequency = microtune_get_frequency, +}; + +static int mt2050_init(struct dvb_frontend *fe) +{ + struct microtune_priv *priv = fe->tuner_priv; + unsigned char buf[2]; + + buf[0] = 6; + buf[1] = 0x10; + tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); /* power */ + + buf[0] = 0x0f; + buf[1] = 0x0f; + tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); /* m1lo */ + + buf[0] = 0x0d; + tuner_i2c_xfer_send(&priv->i2c_props, buf, 1); + tuner_i2c_xfer_recv(&priv->i2c_props, buf, 1); + + tuner_dbg("mt2050: sro is %x\n", buf[0]); + + memcpy(&fe->ops.tuner_ops, &mt2050_tuner_ops, sizeof(struct dvb_tuner_ops)); + + return 0; +} + +struct dvb_frontend *microtune_attach(struct dvb_frontend *fe, + struct i2c_adapter* i2c_adap, + u8 i2c_addr) +{ + struct microtune_priv *priv = NULL; + char *name; + unsigned char buf[21]; + int company_code; + + priv = kzalloc(sizeof(struct microtune_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + fe->tuner_priv = priv; + + priv->i2c_props.addr = i2c_addr; + priv->i2c_props.adap = i2c_adap; + priv->i2c_props.name = "mt20xx"; + + //priv->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */ + + memset(buf,0,sizeof(buf)); + + name = "unknown"; + + tuner_i2c_xfer_send(&priv->i2c_props,buf,1); + tuner_i2c_xfer_recv(&priv->i2c_props,buf,21); + if (debug) { + int i; + tuner_dbg("MT20xx hexdump:"); + for(i=0;i<21;i++) { + printk(" %02x",buf[i]); + if(((i+1)%8)==0) printk(" "); + } + printk("\n"); + } + company_code = buf[0x11] << 8 | buf[0x12]; + tuner_info("microtune: companycode=%04x part=%02x rev=%02x\n", + company_code,buf[0x13],buf[0x14]); + + + if (buf[0x13] < ARRAY_SIZE(microtune_part) && + NULL != microtune_part[buf[0x13]]) + name = microtune_part[buf[0x13]]; + switch (buf[0x13]) { + case MT2032: + mt2032_init(fe); + break; + case MT2050: + mt2050_init(fe); + break; + default: + tuner_info("microtune %s found, not (yet?) supported, sorry :-/\n", + name); + return NULL; + } + + strlcpy(fe->ops.tuner_ops.info.name, name, + sizeof(fe->ops.tuner_ops.info.name)); + tuner_info("microtune %s found, OK\n",name); + return fe; +} + +EXPORT_SYMBOL_GPL(microtune_attach); + +MODULE_DESCRIPTION("Microtune tuner driver"); +MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); +MODULE_LICENSE("GPL"); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/tuners/mt20xx.h b/drivers/media/tuners/mt20xx.h new file mode 100644 index 000000000000..259553a24903 --- /dev/null +++ b/drivers/media/tuners/mt20xx.h @@ -0,0 +1,37 @@ +/* + 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 +#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/tuners/mt2131.c b/drivers/media/tuners/mt2131.c new file mode 100644 index 000000000000..f83b0c1ea6c8 --- /dev/null +++ b/drivers/media/tuners/mt2131.c @@ -0,0 +1,301 @@ +/* + * Driver for Microtune MT2131 "QAM/8VSB single chip tuner" + * + * Copyright (c) 2006 Steven Toth + * + * 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 +#include +#include +#include +#include + +#include "dvb_frontend.h" + +#include "mt2131.h" +#include "mt2131_priv.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +#define dprintk(level,fmt, arg...) if (debug >= level) \ + printk(KERN_INFO "%s: " fmt, "mt2131", ## arg) + +static u8 mt2131_config1[] = { + 0x01, + 0x50, 0x00, 0x50, 0x80, 0x00, 0x49, 0xfa, 0x88, + 0x08, 0x77, 0x41, 0x04, 0x00, 0x00, 0x00, 0x32, + 0x7f, 0xda, 0x4c, 0x00, 0x10, 0xaa, 0x78, 0x80, + 0xff, 0x68, 0xa0, 0xff, 0xdd, 0x00, 0x00 +}; + +static u8 mt2131_config2[] = { + 0x10, + 0x7f, 0xc8, 0x0a, 0x5f, 0x00, 0x04 +}; + +static int mt2131_readreg(struct mt2131_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->cfg->i2c_address, .flags = 0, + .buf = ®, .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 "mt2131 I2C read failed\n"); + return -EREMOTEIO; + } + return 0; +} + +static int mt2131_writereg(struct mt2131_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 "mt2131 I2C write failed\n"); + return -EREMOTEIO; + } + return 0; +} + +static int mt2131_writeregs(struct mt2131_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 "mt2131 I2C write failed (len=%i)\n", + (int)len); + return -EREMOTEIO; + } + return 0; +} + +static int mt2131_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct mt2131_priv *priv; + int ret=0, i; + u32 freq; + u8 if_band_center; + u32 f_lo1, f_lo2; + u32 div1, num1, div2, num2; + u8 b[8]; + u8 lockval = 0; + + priv = fe->tuner_priv; + + freq = c->frequency / 1000; /* Hz -> kHz */ + dprintk(1, "%s() freq=%d\n", __func__, freq); + + f_lo1 = freq + MT2131_IF1 * 1000; + f_lo1 = (f_lo1 / 250) * 250; + f_lo2 = f_lo1 - freq - MT2131_IF2; + + priv->frequency = (f_lo1 - f_lo2 - MT2131_IF2) * 1000; + + /* Frequency LO1 = 16MHz * (DIV1 + NUM1/8192 ) */ + num1 = f_lo1 * 64 / (MT2131_FREF / 128); + div1 = num1 / 8192; + num1 &= 0x1fff; + + /* Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) */ + num2 = f_lo2 * 64 / (MT2131_FREF / 128); + div2 = num2 / 8192; + num2 &= 0x1fff; + + if (freq <= 82500) if_band_center = 0x00; else + if (freq <= 137500) if_band_center = 0x01; else + if (freq <= 192500) if_band_center = 0x02; else + if (freq <= 247500) if_band_center = 0x03; else + if (freq <= 302500) if_band_center = 0x04; else + if (freq <= 357500) if_band_center = 0x05; else + if (freq <= 412500) if_band_center = 0x06; else + if (freq <= 467500) if_band_center = 0x07; else + if (freq <= 522500) if_band_center = 0x08; else + if (freq <= 577500) if_band_center = 0x09; else + if (freq <= 632500) if_band_center = 0x0A; else + if (freq <= 687500) if_band_center = 0x0B; else + if (freq <= 742500) if_band_center = 0x0C; else + if (freq <= 797500) if_band_center = 0x0D; else + if (freq <= 852500) if_band_center = 0x0E; else + if (freq <= 907500) if_band_center = 0x0F; else + if (freq <= 962500) if_band_center = 0x10; else + if (freq <= 1017500) if_band_center = 0x11; else + if (freq <= 1072500) if_band_center = 0x12; else if_band_center = 0x13; + + b[0] = 1; + b[1] = (num1 >> 5) & 0xFF; + b[2] = (num1 & 0x1F); + b[3] = div1; + b[4] = (num2 >> 5) & 0xFF; + b[5] = num2 & 0x1F; + b[6] = div2; + + dprintk(1, "IF1: %dMHz IF2: %dMHz\n", MT2131_IF1, MT2131_IF2); + dprintk(1, "PLL freq=%dkHz band=%d\n", (int)freq, (int)if_band_center); + dprintk(1, "PLL f_lo1=%dkHz f_lo2=%dkHz\n", (int)f_lo1, (int)f_lo2); + dprintk(1, "PLL div1=%d num1=%d div2=%d num2=%d\n", + (int)div1, (int)num1, (int)div2, (int)num2); + dprintk(1, "PLL [1..6]: %2x %2x %2x %2x %2x %2x\n", + (int)b[1], (int)b[2], (int)b[3], (int)b[4], (int)b[5], + (int)b[6]); + + ret = mt2131_writeregs(priv,b,7); + if (ret < 0) + return ret; + + mt2131_writereg(priv, 0x0b, if_band_center); + + /* Wait for lock */ + i = 0; + do { + mt2131_readreg(priv, 0x08, &lockval); + if ((lockval & 0x88) == 0x88) + break; + msleep(4); + i++; + } while (i < 10); + + return ret; +} + +static int mt2131_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct mt2131_priv *priv = fe->tuner_priv; + dprintk(1, "%s()\n", __func__); + *frequency = priv->frequency; + return 0; +} + +static int mt2131_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct mt2131_priv *priv = fe->tuner_priv; + u8 lock_status = 0; + u8 afc_status = 0; + + *status = 0; + + mt2131_readreg(priv, 0x08, &lock_status); + if ((lock_status & 0x88) == 0x88) + *status = TUNER_STATUS_LOCKED; + + mt2131_readreg(priv, 0x09, &afc_status); + dprintk(1, "%s() - LO Status = 0x%x, AFC Status = 0x%x\n", + __func__, lock_status, afc_status); + + return 0; +} + +static int mt2131_init(struct dvb_frontend *fe) +{ + struct mt2131_priv *priv = fe->tuner_priv; + int ret; + dprintk(1, "%s()\n", __func__); + + if ((ret = mt2131_writeregs(priv, mt2131_config1, + sizeof(mt2131_config1))) < 0) + return ret; + + mt2131_writereg(priv, 0x0b, 0x09); + mt2131_writereg(priv, 0x15, 0x47); + mt2131_writereg(priv, 0x07, 0xf2); + mt2131_writereg(priv, 0x0b, 0x01); + + if ((ret = mt2131_writeregs(priv, mt2131_config2, + sizeof(mt2131_config2))) < 0) + return ret; + + return ret; +} + +static int mt2131_release(struct dvb_frontend *fe) +{ + dprintk(1, "%s()\n", __func__); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static const struct dvb_tuner_ops mt2131_tuner_ops = { + .info = { + .name = "Microtune MT2131", + .frequency_min = 48000000, + .frequency_max = 860000000, + .frequency_step = 50000, + }, + + .release = mt2131_release, + .init = mt2131_init, + + .set_params = mt2131_set_params, + .get_frequency = mt2131_get_frequency, + .get_status = mt2131_get_status +}; + +struct dvb_frontend * mt2131_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct mt2131_config *cfg, u16 if1) +{ + struct mt2131_priv *priv = NULL; + u8 id = 0; + + dprintk(1, "%s()\n", __func__); + + priv = kzalloc(sizeof(struct mt2131_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->cfg = cfg; + priv->i2c = i2c; + + if (mt2131_readreg(priv, 0, &id) != 0) { + kfree(priv); + return NULL; + } + if ( (id != 0x3E) && (id != 0x3F) ) { + printk(KERN_ERR "MT2131: Device not found at addr 0x%02x\n", + cfg->i2c_address); + kfree(priv); + return NULL; + } + + printk(KERN_INFO "MT2131: successfully identified at address 0x%02x\n", + cfg->i2c_address); + memcpy(&fe->ops.tuner_ops, &mt2131_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + return fe; +} +EXPORT_SYMBOL(mt2131_attach); + +MODULE_AUTHOR("Steven Toth"); +MODULE_DESCRIPTION("Microtune MT2131 silicon tuner driver"); +MODULE_LICENSE("GPL"); + +/* + * Local variables: + * c-basic-offset: 8 + */ diff --git a/drivers/media/tuners/mt2131.h b/drivers/media/tuners/mt2131.h new file mode 100644 index 000000000000..6632de640df0 --- /dev/null +++ b/drivers/media/tuners/mt2131.h @@ -0,0 +1,54 @@ +/* + * Driver for Microtune MT2131 "QAM/8VSB single chip tuner" + * + * Copyright (c) 2006 Steven Toth + * + * 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/tuners/mt2131_priv.h b/drivers/media/tuners/mt2131_priv.h new file mode 100644 index 000000000000..62aeedf5c550 --- /dev/null +++ b/drivers/media/tuners/mt2131_priv.h @@ -0,0 +1,48 @@ +/* + * Driver for Microtune MT2131 "QAM/8VSB single chip tuner" + * + * Copyright (c) 2006 Steven Toth + * + * 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/tuners/mt2266.c b/drivers/media/tuners/mt2266.c new file mode 100644 index 000000000000..bca4d75e42d4 --- /dev/null +++ b/drivers/media/tuners/mt2266.c @@ -0,0 +1,353 @@ +/* + * Driver for Microtune MT2266 "Direct conversion low power broadband tuner" + * + * Copyright (c) 2007 Olivier DANET + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "mt2266.h" + +#define I2C_ADDRESS 0x60 + +#define REG_PART_REV 0 +#define REG_TUNE 1 +#define REG_BAND 6 +#define REG_BANDWIDTH 8 +#define REG_LOCK 0x12 + +#define PART_REV 0x85 + +struct mt2266_priv { + struct mt2266_config *cfg; + struct i2c_adapter *i2c; + + u32 frequency; + u32 bandwidth; + u8 band; +}; + +#define MT2266_VHF 1 +#define MT2266_UHF 0 + +/* Here, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ + +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 "MT2266: " args); printk("\n"); }} while (0) + +// Reads a single register +static int mt2266_readreg(struct mt2266_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->cfg->i2c_address, .flags = 0, .buf = ®, .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 "MT2266 I2C read failed\n"); + return -EREMOTEIO; + } + return 0; +} + +// Writes a single register +static int mt2266_writereg(struct mt2266_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 "MT2266 I2C write failed\n"); + return -EREMOTEIO; + } + return 0; +} + +// Writes a set of consecutive registers +static int mt2266_writeregs(struct mt2266_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 "MT2266 I2C write failed (len=%i)\n",(int)len); + return -EREMOTEIO; + } + return 0; +} + +// Initialisation sequences +static u8 mt2266_init1[] = { REG_TUNE, 0x00, 0x00, 0x28, + 0x00, 0x52, 0x99, 0x3f }; + +static u8 mt2266_init2[] = { + 0x17, 0x6d, 0x71, 0x61, 0xc0, 0xbf, 0xff, 0xdc, 0x00, 0x0a, 0xd4, + 0x03, 0x64, 0x64, 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7f, 0x5e, 0x3f, 0xff, 0xff, + 0xff, 0x00, 0x77, 0x0f, 0x2d +}; + +static u8 mt2266_init_8mhz[] = { REG_BANDWIDTH, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22 }; + +static u8 mt2266_init_7mhz[] = { REG_BANDWIDTH, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32 }; + +static u8 mt2266_init_6mhz[] = { REG_BANDWIDTH, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7 }; + +static u8 mt2266_uhf[] = { 0x1d, 0xdc, 0x00, 0x0a, 0xd4, 0x03, 0x64, 0x64, + 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14 }; + +static u8 mt2266_vhf[] = { 0x1d, 0xfe, 0x00, 0x00, 0xb4, 0x03, 0xa5, 0xa5, + 0xa5, 0xa5, 0x82, 0xaa, 0xf1, 0x17, 0x80, 0x1f }; + +#define FREF 30000 // Quartz oscillator 30 MHz + +static int mt2266_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct mt2266_priv *priv; + int ret=0; + u32 freq; + u32 tune; + u8 lnaband; + u8 b[10]; + int i; + u8 band; + + priv = fe->tuner_priv; + + freq = priv->frequency / 1000; /* Hz -> kHz */ + if (freq < 470000 && freq > 230000) + return -EINVAL; /* Gap between VHF and UHF bands */ + + priv->frequency = c->frequency; + tune = 2 * freq * (8192/16) / (FREF/16); + band = (freq < 300000) ? MT2266_VHF : MT2266_UHF; + if (band == MT2266_VHF) + tune *= 2; + + switch (c->bandwidth_hz) { + case 6000000: + mt2266_writeregs(priv, mt2266_init_6mhz, + sizeof(mt2266_init_6mhz)); + break; + case 8000000: + mt2266_writeregs(priv, mt2266_init_8mhz, + sizeof(mt2266_init_8mhz)); + break; + case 7000000: + default: + mt2266_writeregs(priv, mt2266_init_7mhz, + sizeof(mt2266_init_7mhz)); + break; + } + priv->bandwidth = c->bandwidth_hz; + + if (band == MT2266_VHF && priv->band == MT2266_UHF) { + dprintk("Switch from UHF to VHF"); + mt2266_writereg(priv, 0x05, 0x04); + mt2266_writereg(priv, 0x19, 0x61); + mt2266_writeregs(priv, mt2266_vhf, sizeof(mt2266_vhf)); + } else if (band == MT2266_UHF && priv->band == MT2266_VHF) { + dprintk("Switch from VHF to UHF"); + mt2266_writereg(priv, 0x05, 0x52); + mt2266_writereg(priv, 0x19, 0x61); + mt2266_writeregs(priv, mt2266_uhf, sizeof(mt2266_uhf)); + } + msleep(10); + + if (freq <= 495000) + lnaband = 0xEE; + else if (freq <= 525000) + lnaband = 0xDD; + else if (freq <= 550000) + lnaband = 0xCC; + else if (freq <= 580000) + lnaband = 0xBB; + else if (freq <= 605000) + lnaband = 0xAA; + else if (freq <= 630000) + lnaband = 0x99; + else if (freq <= 655000) + lnaband = 0x88; + else if (freq <= 685000) + lnaband = 0x77; + else if (freq <= 710000) + lnaband = 0x66; + else if (freq <= 735000) + lnaband = 0x55; + else if (freq <= 765000) + lnaband = 0x44; + else if (freq <= 802000) + lnaband = 0x33; + else if (freq <= 840000) + lnaband = 0x22; + else + lnaband = 0x11; + + b[0] = REG_TUNE; + b[1] = (tune >> 8) & 0x1F; + b[2] = tune & 0xFF; + b[3] = tune >> 13; + mt2266_writeregs(priv,b,4); + + dprintk("set_parms: tune=%d band=%d %s", + (int) tune, (int) lnaband, + (band == MT2266_UHF) ? "UHF" : "VHF"); + dprintk("set_parms: [1..3]: %2x %2x %2x", + (int) b[1], (int) b[2], (int)b[3]); + + if (band == MT2266_UHF) { + b[0] = 0x05; + b[1] = (priv->band == MT2266_VHF) ? 0x52 : 0x62; + b[2] = lnaband; + mt2266_writeregs(priv, b, 3); + } + + /* Wait for pll lock or timeout */ + i = 0; + do { + mt2266_readreg(priv,REG_LOCK,b); + if (b[0] & 0x40) + break; + msleep(10); + i++; + } while (i<10); + dprintk("Lock when i=%i",(int)i); + + if (band == MT2266_UHF && priv->band == MT2266_VHF) + mt2266_writereg(priv, 0x05, 0x62); + + priv->band = band; + + return ret; +} + +static void mt2266_calibrate(struct mt2266_priv *priv) +{ + mt2266_writereg(priv, 0x11, 0x03); + mt2266_writereg(priv, 0x11, 0x01); + mt2266_writeregs(priv, mt2266_init1, sizeof(mt2266_init1)); + mt2266_writeregs(priv, mt2266_init2, sizeof(mt2266_init2)); + mt2266_writereg(priv, 0x33, 0x5e); + mt2266_writereg(priv, 0x10, 0x10); + mt2266_writereg(priv, 0x10, 0x00); + mt2266_writeregs(priv, mt2266_init_8mhz, sizeof(mt2266_init_8mhz)); + msleep(25); + mt2266_writereg(priv, 0x17, 0x6d); + mt2266_writereg(priv, 0x1c, 0x00); + msleep(75); + mt2266_writereg(priv, 0x17, 0x6d); + mt2266_writereg(priv, 0x1c, 0xff); +} + +static int mt2266_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct mt2266_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int mt2266_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct mt2266_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +static int mt2266_init(struct dvb_frontend *fe) +{ + int ret; + struct mt2266_priv *priv = fe->tuner_priv; + ret = mt2266_writereg(priv, 0x17, 0x6d); + if (ret < 0) + return ret; + ret = mt2266_writereg(priv, 0x1c, 0xff); + if (ret < 0) + return ret; + return 0; +} + +static int mt2266_sleep(struct dvb_frontend *fe) +{ + struct mt2266_priv *priv = fe->tuner_priv; + mt2266_writereg(priv, 0x17, 0x6d); + mt2266_writereg(priv, 0x1c, 0x00); + return 0; +} + +static int mt2266_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static const struct dvb_tuner_ops mt2266_tuner_ops = { + .info = { + .name = "Microtune MT2266", + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_step = 50000, + }, + .release = mt2266_release, + .init = mt2266_init, + .sleep = mt2266_sleep, + .set_params = mt2266_set_params, + .get_frequency = mt2266_get_frequency, + .get_bandwidth = mt2266_get_bandwidth +}; + +struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg) +{ + struct mt2266_priv *priv = NULL; + u8 id = 0; + + priv = kzalloc(sizeof(struct mt2266_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->cfg = cfg; + priv->i2c = i2c; + priv->band = MT2266_UHF; + + if (mt2266_readreg(priv, 0, &id)) { + kfree(priv); + return NULL; + } + if (id != PART_REV) { + kfree(priv); + return NULL; + } + printk(KERN_INFO "MT2266: successfully identified\n"); + memcpy(&fe->ops.tuner_ops, &mt2266_tuner_ops, sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + mt2266_calibrate(priv); + return fe; +} +EXPORT_SYMBOL(mt2266_attach); + +MODULE_AUTHOR("Olivier DANET"); +MODULE_DESCRIPTION("Microtune MT2266 silicon tuner driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/mt2266.h b/drivers/media/tuners/mt2266.h new file mode 100644 index 000000000000..4d083882d044 --- /dev/null +++ b/drivers/media/tuners/mt2266.h @@ -0,0 +1,37 @@ +/* + * Driver for Microtune MT2266 "Direct conversion low power broadband tuner" + * + * Copyright (c) 2007 Olivier DANET + * + * 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/tuners/mxl5005s.c b/drivers/media/tuners/mxl5005s.c new file mode 100644 index 000000000000..6133315fb0e3 --- /dev/null +++ b/drivers/media/tuners/mxl5005s.c @@ -0,0 +1,4109 @@ +/* + MaxLinear MXL5005S VSB/QAM/DVBT tuner driver + + Copyright (C) 2008 MaxLinear + Copyright (C) 2006 Steven Toth + Functions: + mxl5005s_reset() + mxl5005s_writereg() + mxl5005s_writeregs() + mxl5005s_init() + mxl5005s_reconfigure() + mxl5005s_AssignTunerMode() + mxl5005s_set_params() + mxl5005s_get_frequency() + mxl5005s_get_bandwidth() + mxl5005s_release() + mxl5005s_attach() + + Copyright (C) 2008 Realtek + Copyright (C) 2008 Jan Hoogenraad + Functions: + mxl5005s_SetRfFreqHz() + + 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. + +*/ + +/* + History of this driver (Steven Toth): + I was given a public release of a linux driver that included + support for the MaxLinear MXL5005S silicon tuner. Analysis of + the tuner driver showed clearly three things. + + 1. The tuner driver didn't support the LinuxTV tuner API + so the code Realtek added had to be removed. + + 2. A significant amount of the driver is reference driver code + from MaxLinear, I felt it was important to identify and + preserve this. + + 3. New code has to be added to interface correctly with the + LinuxTV API, as a regular kernel module. + + Other than the reference driver enum's, I've clearly marked + sections of the code and retained the copyright of the + respective owners. +*/ +#include +#include +#include +#include +#include +#include +#include "dvb_frontend.h" +#include "mxl5005s.h" + +static int debug; + +#define dprintk(level, arg...) do { \ + if (level <= debug) \ + printk(arg); \ + } while (0) + +#define TUNER_REGS_NUM 104 +#define INITCTRL_NUM 40 + +#ifdef _MXL_PRODUCTION +#define CHCTRL_NUM 39 +#else +#define CHCTRL_NUM 36 +#endif + +#define MXLCTRL_NUM 189 +#define MASTER_CONTROL_ADDR 9 + +/* Enumeration of Master Control Register State */ +enum master_control_state { + MC_LOAD_START = 1, + MC_POWER_DOWN, + MC_SYNTH_RESET, + MC_SEQ_OFF +}; + +/* Enumeration of MXL5005 Tuner Modulation Type */ +enum { + MXL_DEFAULT_MODULATION = 0, + MXL_DVBT, + MXL_ATSC, + MXL_QAM, + MXL_ANALOG_CABLE, + MXL_ANALOG_OTA +}; + +/* MXL5005 Tuner Register Struct */ +struct TunerReg { + u16 Reg_Num; /* Tuner Register Address */ + u16 Reg_Val; /* Current sw programmed value waiting to be written */ +}; + +enum { + /* Initialization Control Names */ + DN_IQTN_AMP_CUT = 1, /* 1 */ + BB_MODE, /* 2 */ + BB_BUF, /* 3 */ + BB_BUF_OA, /* 4 */ + BB_ALPF_BANDSELECT, /* 5 */ + BB_IQSWAP, /* 6 */ + BB_DLPF_BANDSEL, /* 7 */ + RFSYN_CHP_GAIN, /* 8 */ + RFSYN_EN_CHP_HIGAIN, /* 9 */ + AGC_IF, /* 10 */ + AGC_RF, /* 11 */ + IF_DIVVAL, /* 12 */ + IF_VCO_BIAS, /* 13 */ + CHCAL_INT_MOD_IF, /* 14 */ + CHCAL_FRAC_MOD_IF, /* 15 */ + DRV_RES_SEL, /* 16 */ + I_DRIVER, /* 17 */ + EN_AAF, /* 18 */ + EN_3P, /* 19 */ + EN_AUX_3P, /* 20 */ + SEL_AAF_BAND, /* 21 */ + SEQ_ENCLK16_CLK_OUT, /* 22 */ + SEQ_SEL4_16B, /* 23 */ + XTAL_CAPSELECT, /* 24 */ + IF_SEL_DBL, /* 25 */ + RFSYN_R_DIV, /* 26 */ + SEQ_EXTSYNTHCALIF, /* 27 */ + SEQ_EXTDCCAL, /* 28 */ + AGC_EN_RSSI, /* 29 */ + RFA_ENCLKRFAGC, /* 30 */ + RFA_RSSI_REFH, /* 31 */ + RFA_RSSI_REF, /* 32 */ + RFA_RSSI_REFL, /* 33 */ + RFA_FLR, /* 34 */ + RFA_CEIL, /* 35 */ + SEQ_EXTIQFSMPULSE, /* 36 */ + OVERRIDE_1, /* 37 */ + BB_INITSTATE_DLPF_TUNE, /* 38 */ + TG_R_DIV, /* 39 */ + EN_CHP_LIN_B, /* 40 */ + + /* Channel Change Control Names */ + DN_POLY = 51, /* 51 */ + DN_RFGAIN, /* 52 */ + DN_CAP_RFLPF, /* 53 */ + DN_EN_VHFUHFBAR, /* 54 */ + DN_GAIN_ADJUST, /* 55 */ + DN_IQTNBUF_AMP, /* 56 */ + DN_IQTNGNBFBIAS_BST, /* 57 */ + RFSYN_EN_OUTMUX, /* 58 */ + RFSYN_SEL_VCO_OUT, /* 59 */ + RFSYN_SEL_VCO_HI, /* 60 */ + RFSYN_SEL_DIVM, /* 61 */ + RFSYN_RF_DIV_BIAS, /* 62 */ + DN_SEL_FREQ, /* 63 */ + RFSYN_VCO_BIAS, /* 64 */ + CHCAL_INT_MOD_RF, /* 65 */ + CHCAL_FRAC_MOD_RF, /* 66 */ + RFSYN_LPF_R, /* 67 */ + CHCAL_EN_INT_RF, /* 68 */ + TG_LO_DIVVAL, /* 69 */ + TG_LO_SELVAL, /* 70 */ + TG_DIV_VAL, /* 71 */ + TG_VCO_BIAS, /* 72 */ + SEQ_EXTPOWERUP, /* 73 */ + OVERRIDE_2, /* 74 */ + OVERRIDE_3, /* 75 */ + OVERRIDE_4, /* 76 */ + SEQ_FSM_PULSE, /* 77 */ + GPIO_4B, /* 78 */ + GPIO_3B, /* 79 */ + GPIO_4, /* 80 */ + GPIO_3, /* 81 */ + GPIO_1B, /* 82 */ + DAC_A_ENABLE, /* 83 */ + DAC_B_ENABLE, /* 84 */ + DAC_DIN_A, /* 85 */ + DAC_DIN_B, /* 86 */ +#ifdef _MXL_PRODUCTION + RFSYN_EN_DIV, /* 87 */ + RFSYN_DIVM, /* 88 */ + DN_BYPASS_AGC_I2C /* 89 */ +#endif +}; + +/* + * The following context is source code provided by MaxLinear. + * MaxLinear source code - Common_MXL.h (?) + */ + +/* Constants */ +#define MXL5005S_REG_WRITING_TABLE_LEN_MAX 104 +#define MXL5005S_LATCH_BYTE 0xfe + +/* Register address, MSB, and LSB */ +#define MXL5005S_BB_IQSWAP_ADDR 59 +#define MXL5005S_BB_IQSWAP_MSB 0 +#define MXL5005S_BB_IQSWAP_LSB 0 + +#define MXL5005S_BB_DLPF_BANDSEL_ADDR 53 +#define MXL5005S_BB_DLPF_BANDSEL_MSB 4 +#define MXL5005S_BB_DLPF_BANDSEL_LSB 3 + +/* Standard modes */ +enum { + MXL5005S_STANDARD_DVBT, + MXL5005S_STANDARD_ATSC, +}; +#define MXL5005S_STANDARD_MODE_NUM 2 + +/* Bandwidth modes */ +enum { + MXL5005S_BANDWIDTH_6MHZ = 6000000, + MXL5005S_BANDWIDTH_7MHZ = 7000000, + MXL5005S_BANDWIDTH_8MHZ = 8000000, +}; +#define MXL5005S_BANDWIDTH_MODE_NUM 3 + +/* MXL5005 Tuner Control Struct */ +struct TunerControl { + u16 Ctrl_Num; /* Control Number */ + u16 size; /* Number of bits to represent Value */ + u16 addr[25]; /* Array of Tuner Register Address for each bit pos */ + u16 bit[25]; /* Array of bit pos in Reg Addr for each bit pos */ + u16 val[25]; /* Binary representation of Value */ +}; + +/* MXL5005 Tuner Struct */ +struct mxl5005s_state { + u8 Mode; /* 0: Analog Mode ; 1: Digital Mode */ + u8 IF_Mode; /* for Analog Mode, 0: zero IF; 1: low IF */ + u32 Chan_Bandwidth; /* filter channel bandwidth (6, 7, 8) */ + u32 IF_OUT; /* Desired IF Out Frequency */ + u16 IF_OUT_LOAD; /* IF Out Load Resistor (200/300 Ohms) */ + u32 RF_IN; /* RF Input Frequency */ + u32 Fxtal; /* XTAL Frequency */ + u8 AGC_Mode; /* AGC Mode 0: Dual AGC; 1: Single AGC */ + u16 TOP; /* Value: take over point */ + u8 CLOCK_OUT; /* 0: turn off clk out; 1: turn on clock out */ + u8 DIV_OUT; /* 4MHz or 16MHz */ + u8 CAPSELECT; /* 0: disable On-Chip pulling cap; 1: enable */ + u8 EN_RSSI; /* 0: disable RSSI; 1: enable RSSI */ + + /* Modulation Type; */ + /* 0 - Default; 1 - DVB-T; 2 - ATSC; 3 - QAM; 4 - Analog Cable */ + u8 Mod_Type; + + /* Tracking Filter Type */ + /* 0 - Default; 1 - Off; 2 - Type C; 3 - Type C-H */ + u8 TF_Type; + + /* Calculated Settings */ + u32 RF_LO; /* Synth RF LO Frequency */ + u32 IF_LO; /* Synth IF LO Frequency */ + u32 TG_LO; /* Synth TG_LO Frequency */ + + /* Pointers to ControlName Arrays */ + u16 Init_Ctrl_Num; /* Number of INIT Control Names */ + struct TunerControl + Init_Ctrl[INITCTRL_NUM]; /* INIT Control Names Array Pointer */ + + u16 CH_Ctrl_Num; /* Number of CH Control Names */ + struct TunerControl + CH_Ctrl[CHCTRL_NUM]; /* CH Control Name Array Pointer */ + + u16 MXL_Ctrl_Num; /* Number of MXL Control Names */ + struct TunerControl + MXL_Ctrl[MXLCTRL_NUM]; /* MXL Control Name Array Pointer */ + + /* Pointer to Tuner Register Array */ + u16 TunerRegs_Num; /* Number of Tuner Registers */ + struct TunerReg + TunerRegs[TUNER_REGS_NUM]; /* Tuner Register Array Pointer */ + + /* Linux driver framework specific */ + struct mxl5005s_config *config; + struct dvb_frontend *frontend; + struct i2c_adapter *i2c; + + /* Cache values */ + u32 current_mode; + +}; + +static u16 MXL_GetMasterControl(u8 *MasterReg, int state); +static u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value); +static u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value); +static void MXL_RegWriteBit(struct dvb_frontend *fe, u8 address, u8 bit, + u8 bitVal); +static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum, + u8 *RegVal, int *count); +static u32 MXL_Ceiling(u32 value, u32 resolution); +static u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal); +static u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum, + u32 value, u16 controlGroup); +static u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val); +static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum, + u8 *RegVal, int *count); +static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq); +static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe); +static void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe); +static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum, + u8 *RegVal, int *count); +static int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable, + u8 *datatable, u8 len); +static u16 MXL_IFSynthInit(struct dvb_frontend *fe); +static int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type, + u32 bandwidth); +static int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type, + u32 bandwidth); + +/* ---------------------------------------------------------------- + * Begin: Custom code salvaged from the Realtek driver. + * Copyright (C) 2008 Realtek + * Copyright (C) 2008 Jan Hoogenraad + * This code is placed under the terms of the GNU General Public License + * + * Released by Realtek under GPLv2. + * Thanks to Realtek for a lot of support we received ! + * + * Revision: 080314 - original version + */ + +static int mxl5005s_SetRfFreqHz(struct dvb_frontend *fe, unsigned long RfFreqHz) +{ + struct mxl5005s_state *state = fe->tuner_priv; + unsigned char AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; + unsigned char ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; + int TableLen; + + u32 IfDivval = 0; + unsigned char MasterControlByte; + + dprintk(1, "%s() freq=%ld\n", __func__, RfFreqHz); + + /* Set MxL5005S tuner RF frequency according to example code. */ + + /* Tuner RF frequency setting stage 0 */ + MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET); + AddrTable[0] = MASTER_CONTROL_ADDR; + ByteTable[0] |= state->config->AgcMasterByte; + + mxl5005s_writeregs(fe, AddrTable, ByteTable, 1); + + /* Tuner RF frequency setting stage 1 */ + MXL_TuneRF(fe, RfFreqHz); + + MXL_ControlRead(fe, IF_DIVVAL, &IfDivval); + + MXL_ControlWrite(fe, SEQ_FSM_PULSE, 0); + MXL_ControlWrite(fe, SEQ_EXTPOWERUP, 1); + MXL_ControlWrite(fe, IF_DIVVAL, 8); + MXL_GetCHRegister(fe, AddrTable, ByteTable, &TableLen); + + MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START); + AddrTable[TableLen] = MASTER_CONTROL_ADDR ; + ByteTable[TableLen] = MasterControlByte | + state->config->AgcMasterByte; + TableLen += 1; + + mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen); + + /* Wait 30 ms. */ + msleep(150); + + /* Tuner RF frequency setting stage 2 */ + MXL_ControlWrite(fe, SEQ_FSM_PULSE, 1); + MXL_ControlWrite(fe, IF_DIVVAL, IfDivval); + MXL_GetCHRegister_ZeroIF(fe, AddrTable, ByteTable, &TableLen); + + MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START); + AddrTable[TableLen] = MASTER_CONTROL_ADDR ; + ByteTable[TableLen] = MasterControlByte | + state->config->AgcMasterByte ; + TableLen += 1; + + mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen); + + msleep(100); + + return 0; +} +/* End: Custom code taken from the Realtek driver */ + +/* ---------------------------------------------------------------- + * Begin: Reference driver code found in the Realtek driver. + * Copyright (C) 2008 MaxLinear + */ +static u16 MXL5005_RegisterInit(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + state->TunerRegs_Num = TUNER_REGS_NUM ; + + state->TunerRegs[0].Reg_Num = 9 ; + state->TunerRegs[0].Reg_Val = 0x40 ; + + state->TunerRegs[1].Reg_Num = 11 ; + state->TunerRegs[1].Reg_Val = 0x19 ; + + state->TunerRegs[2].Reg_Num = 12 ; + state->TunerRegs[2].Reg_Val = 0x60 ; + + state->TunerRegs[3].Reg_Num = 13 ; + state->TunerRegs[3].Reg_Val = 0x00 ; + + state->TunerRegs[4].Reg_Num = 14 ; + state->TunerRegs[4].Reg_Val = 0x00 ; + + state->TunerRegs[5].Reg_Num = 15 ; + state->TunerRegs[5].Reg_Val = 0xC0 ; + + state->TunerRegs[6].Reg_Num = 16 ; + state->TunerRegs[6].Reg_Val = 0x00 ; + + state->TunerRegs[7].Reg_Num = 17 ; + state->TunerRegs[7].Reg_Val = 0x00 ; + + state->TunerRegs[8].Reg_Num = 18 ; + state->TunerRegs[8].Reg_Val = 0x00 ; + + state->TunerRegs[9].Reg_Num = 19 ; + state->TunerRegs[9].Reg_Val = 0x34 ; + + state->TunerRegs[10].Reg_Num = 21 ; + state->TunerRegs[10].Reg_Val = 0x00 ; + + state->TunerRegs[11].Reg_Num = 22 ; + state->TunerRegs[11].Reg_Val = 0x6B ; + + state->TunerRegs[12].Reg_Num = 23 ; + state->TunerRegs[12].Reg_Val = 0x35 ; + + state->TunerRegs[13].Reg_Num = 24 ; + state->TunerRegs[13].Reg_Val = 0x70 ; + + state->TunerRegs[14].Reg_Num = 25 ; + state->TunerRegs[14].Reg_Val = 0x3E ; + + state->TunerRegs[15].Reg_Num = 26 ; + state->TunerRegs[15].Reg_Val = 0x82 ; + + state->TunerRegs[16].Reg_Num = 31 ; + state->TunerRegs[16].Reg_Val = 0x00 ; + + state->TunerRegs[17].Reg_Num = 32 ; + state->TunerRegs[17].Reg_Val = 0x40 ; + + state->TunerRegs[18].Reg_Num = 33 ; + state->TunerRegs[18].Reg_Val = 0x53 ; + + state->TunerRegs[19].Reg_Num = 34 ; + state->TunerRegs[19].Reg_Val = 0x81 ; + + state->TunerRegs[20].Reg_Num = 35 ; + state->TunerRegs[20].Reg_Val = 0xC9 ; + + state->TunerRegs[21].Reg_Num = 36 ; + state->TunerRegs[21].Reg_Val = 0x01 ; + + state->TunerRegs[22].Reg_Num = 37 ; + state->TunerRegs[22].Reg_Val = 0x00 ; + + state->TunerRegs[23].Reg_Num = 41 ; + state->TunerRegs[23].Reg_Val = 0x00 ; + + state->TunerRegs[24].Reg_Num = 42 ; + state->TunerRegs[24].Reg_Val = 0xF8 ; + + state->TunerRegs[25].Reg_Num = 43 ; + state->TunerRegs[25].Reg_Val = 0x43 ; + + state->TunerRegs[26].Reg_Num = 44 ; + state->TunerRegs[26].Reg_Val = 0x20 ; + + state->TunerRegs[27].Reg_Num = 45 ; + state->TunerRegs[27].Reg_Val = 0x80 ; + + state->TunerRegs[28].Reg_Num = 46 ; + state->TunerRegs[28].Reg_Val = 0x88 ; + + state->TunerRegs[29].Reg_Num = 47 ; + state->TunerRegs[29].Reg_Val = 0x86 ; + + state->TunerRegs[30].Reg_Num = 48 ; + state->TunerRegs[30].Reg_Val = 0x00 ; + + state->TunerRegs[31].Reg_Num = 49 ; + state->TunerRegs[31].Reg_Val = 0x00 ; + + state->TunerRegs[32].Reg_Num = 53 ; + state->TunerRegs[32].Reg_Val = 0x94 ; + + state->TunerRegs[33].Reg_Num = 54 ; + state->TunerRegs[33].Reg_Val = 0xFA ; + + state->TunerRegs[34].Reg_Num = 55 ; + state->TunerRegs[34].Reg_Val = 0x92 ; + + state->TunerRegs[35].Reg_Num = 56 ; + state->TunerRegs[35].Reg_Val = 0x80 ; + + state->TunerRegs[36].Reg_Num = 57 ; + state->TunerRegs[36].Reg_Val = 0x41 ; + + state->TunerRegs[37].Reg_Num = 58 ; + state->TunerRegs[37].Reg_Val = 0xDB ; + + state->TunerRegs[38].Reg_Num = 59 ; + state->TunerRegs[38].Reg_Val = 0x00 ; + + state->TunerRegs[39].Reg_Num = 60 ; + state->TunerRegs[39].Reg_Val = 0x00 ; + + state->TunerRegs[40].Reg_Num = 61 ; + state->TunerRegs[40].Reg_Val = 0x00 ; + + state->TunerRegs[41].Reg_Num = 62 ; + state->TunerRegs[41].Reg_Val = 0x00 ; + + state->TunerRegs[42].Reg_Num = 65 ; + state->TunerRegs[42].Reg_Val = 0xF8 ; + + state->TunerRegs[43].Reg_Num = 66 ; + state->TunerRegs[43].Reg_Val = 0xE4 ; + + state->TunerRegs[44].Reg_Num = 67 ; + state->TunerRegs[44].Reg_Val = 0x90 ; + + state->TunerRegs[45].Reg_Num = 68 ; + state->TunerRegs[45].Reg_Val = 0xC0 ; + + state->TunerRegs[46].Reg_Num = 69 ; + state->TunerRegs[46].Reg_Val = 0x01 ; + + state->TunerRegs[47].Reg_Num = 70 ; + state->TunerRegs[47].Reg_Val = 0x50 ; + + state->TunerRegs[48].Reg_Num = 71 ; + state->TunerRegs[48].Reg_Val = 0x06 ; + + state->TunerRegs[49].Reg_Num = 72 ; + state->TunerRegs[49].Reg_Val = 0x00 ; + + state->TunerRegs[50].Reg_Num = 73 ; + state->TunerRegs[50].Reg_Val = 0x20 ; + + state->TunerRegs[51].Reg_Num = 76 ; + state->TunerRegs[51].Reg_Val = 0xBB ; + + state->TunerRegs[52].Reg_Num = 77 ; + state->TunerRegs[52].Reg_Val = 0x13 ; + + state->TunerRegs[53].Reg_Num = 81 ; + state->TunerRegs[53].Reg_Val = 0x04 ; + + state->TunerRegs[54].Reg_Num = 82 ; + state->TunerRegs[54].Reg_Val = 0x75 ; + + state->TunerRegs[55].Reg_Num = 83 ; + state->TunerRegs[55].Reg_Val = 0x00 ; + + state->TunerRegs[56].Reg_Num = 84 ; + state->TunerRegs[56].Reg_Val = 0x00 ; + + state->TunerRegs[57].Reg_Num = 85 ; + state->TunerRegs[57].Reg_Val = 0x00 ; + + state->TunerRegs[58].Reg_Num = 91 ; + state->TunerRegs[58].Reg_Val = 0x70 ; + + state->TunerRegs[59].Reg_Num = 92 ; + state->TunerRegs[59].Reg_Val = 0x00 ; + + state->TunerRegs[60].Reg_Num = 93 ; + state->TunerRegs[60].Reg_Val = 0x00 ; + + state->TunerRegs[61].Reg_Num = 94 ; + state->TunerRegs[61].Reg_Val = 0x00 ; + + state->TunerRegs[62].Reg_Num = 95 ; + state->TunerRegs[62].Reg_Val = 0x0C ; + + state->TunerRegs[63].Reg_Num = 96 ; + state->TunerRegs[63].Reg_Val = 0x00 ; + + state->TunerRegs[64].Reg_Num = 97 ; + state->TunerRegs[64].Reg_Val = 0x00 ; + + state->TunerRegs[65].Reg_Num = 98 ; + state->TunerRegs[65].Reg_Val = 0xE2 ; + + state->TunerRegs[66].Reg_Num = 99 ; + state->TunerRegs[66].Reg_Val = 0x00 ; + + state->TunerRegs[67].Reg_Num = 100 ; + state->TunerRegs[67].Reg_Val = 0x00 ; + + state->TunerRegs[68].Reg_Num = 101 ; + state->TunerRegs[68].Reg_Val = 0x12 ; + + state->TunerRegs[69].Reg_Num = 102 ; + state->TunerRegs[69].Reg_Val = 0x80 ; + + state->TunerRegs[70].Reg_Num = 103 ; + state->TunerRegs[70].Reg_Val = 0x32 ; + + state->TunerRegs[71].Reg_Num = 104 ; + state->TunerRegs[71].Reg_Val = 0xB4 ; + + state->TunerRegs[72].Reg_Num = 105 ; + state->TunerRegs[72].Reg_Val = 0x60 ; + + state->TunerRegs[73].Reg_Num = 106 ; + state->TunerRegs[73].Reg_Val = 0x83 ; + + state->TunerRegs[74].Reg_Num = 107 ; + state->TunerRegs[74].Reg_Val = 0x84 ; + + state->TunerRegs[75].Reg_Num = 108 ; + state->TunerRegs[75].Reg_Val = 0x9C ; + + state->TunerRegs[76].Reg_Num = 109 ; + state->TunerRegs[76].Reg_Val = 0x02 ; + + state->TunerRegs[77].Reg_Num = 110 ; + state->TunerRegs[77].Reg_Val = 0x81 ; + + state->TunerRegs[78].Reg_Num = 111 ; + state->TunerRegs[78].Reg_Val = 0xC0 ; + + state->TunerRegs[79].Reg_Num = 112 ; + state->TunerRegs[79].Reg_Val = 0x10 ; + + state->TunerRegs[80].Reg_Num = 131 ; + state->TunerRegs[80].Reg_Val = 0x8A ; + + state->TunerRegs[81].Reg_Num = 132 ; + state->TunerRegs[81].Reg_Val = 0x10 ; + + state->TunerRegs[82].Reg_Num = 133 ; + state->TunerRegs[82].Reg_Val = 0x24 ; + + state->TunerRegs[83].Reg_Num = 134 ; + state->TunerRegs[83].Reg_Val = 0x00 ; + + state->TunerRegs[84].Reg_Num = 135 ; + state->TunerRegs[84].Reg_Val = 0x00 ; + + state->TunerRegs[85].Reg_Num = 136 ; + state->TunerRegs[85].Reg_Val = 0x7E ; + + state->TunerRegs[86].Reg_Num = 137 ; + state->TunerRegs[86].Reg_Val = 0x40 ; + + state->TunerRegs[87].Reg_Num = 138 ; + state->TunerRegs[87].Reg_Val = 0x38 ; + + state->TunerRegs[88].Reg_Num = 146 ; + state->TunerRegs[88].Reg_Val = 0xF6 ; + + state->TunerRegs[89].Reg_Num = 147 ; + state->TunerRegs[89].Reg_Val = 0x1A ; + + state->TunerRegs[90].Reg_Num = 148 ; + state->TunerRegs[90].Reg_Val = 0x62 ; + + state->TunerRegs[91].Reg_Num = 149 ; + state->TunerRegs[91].Reg_Val = 0x33 ; + + state->TunerRegs[92].Reg_Num = 150 ; + state->TunerRegs[92].Reg_Val = 0x80 ; + + state->TunerRegs[93].Reg_Num = 156 ; + state->TunerRegs[93].Reg_Val = 0x56 ; + + state->TunerRegs[94].Reg_Num = 157 ; + state->TunerRegs[94].Reg_Val = 0x17 ; + + state->TunerRegs[95].Reg_Num = 158 ; + state->TunerRegs[95].Reg_Val = 0xA9 ; + + state->TunerRegs[96].Reg_Num = 159 ; + state->TunerRegs[96].Reg_Val = 0x00 ; + + state->TunerRegs[97].Reg_Num = 160 ; + state->TunerRegs[97].Reg_Val = 0x00 ; + + state->TunerRegs[98].Reg_Num = 161 ; + state->TunerRegs[98].Reg_Val = 0x00 ; + + state->TunerRegs[99].Reg_Num = 162 ; + state->TunerRegs[99].Reg_Val = 0x40 ; + + state->TunerRegs[100].Reg_Num = 166 ; + state->TunerRegs[100].Reg_Val = 0xAE ; + + state->TunerRegs[101].Reg_Num = 167 ; + state->TunerRegs[101].Reg_Val = 0x1B ; + + state->TunerRegs[102].Reg_Num = 168 ; + state->TunerRegs[102].Reg_Val = 0xF2 ; + + state->TunerRegs[103].Reg_Num = 195 ; + state->TunerRegs[103].Reg_Val = 0x00 ; + + return 0 ; +} + +static u16 MXL5005_ControlInit(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + state->Init_Ctrl_Num = INITCTRL_NUM; + + state->Init_Ctrl[0].Ctrl_Num = DN_IQTN_AMP_CUT ; + state->Init_Ctrl[0].size = 1 ; + state->Init_Ctrl[0].addr[0] = 73; + state->Init_Ctrl[0].bit[0] = 7; + state->Init_Ctrl[0].val[0] = 0; + + state->Init_Ctrl[1].Ctrl_Num = BB_MODE ; + state->Init_Ctrl[1].size = 1 ; + state->Init_Ctrl[1].addr[0] = 53; + state->Init_Ctrl[1].bit[0] = 2; + state->Init_Ctrl[1].val[0] = 1; + + state->Init_Ctrl[2].Ctrl_Num = BB_BUF ; + state->Init_Ctrl[2].size = 2 ; + state->Init_Ctrl[2].addr[0] = 53; + state->Init_Ctrl[2].bit[0] = 1; + state->Init_Ctrl[2].val[0] = 0; + state->Init_Ctrl[2].addr[1] = 57; + state->Init_Ctrl[2].bit[1] = 0; + state->Init_Ctrl[2].val[1] = 1; + + state->Init_Ctrl[3].Ctrl_Num = BB_BUF_OA ; + state->Init_Ctrl[3].size = 1 ; + state->Init_Ctrl[3].addr[0] = 53; + state->Init_Ctrl[3].bit[0] = 0; + state->Init_Ctrl[3].val[0] = 0; + + state->Init_Ctrl[4].Ctrl_Num = BB_ALPF_BANDSELECT ; + state->Init_Ctrl[4].size = 3 ; + state->Init_Ctrl[4].addr[0] = 53; + state->Init_Ctrl[4].bit[0] = 5; + state->Init_Ctrl[4].val[0] = 0; + state->Init_Ctrl[4].addr[1] = 53; + state->Init_Ctrl[4].bit[1] = 6; + state->Init_Ctrl[4].val[1] = 0; + state->Init_Ctrl[4].addr[2] = 53; + state->Init_Ctrl[4].bit[2] = 7; + state->Init_Ctrl[4].val[2] = 1; + + state->Init_Ctrl[5].Ctrl_Num = BB_IQSWAP ; + state->Init_Ctrl[5].size = 1 ; + state->Init_Ctrl[5].addr[0] = 59; + state->Init_Ctrl[5].bit[0] = 0; + state->Init_Ctrl[5].val[0] = 0; + + state->Init_Ctrl[6].Ctrl_Num = BB_DLPF_BANDSEL ; + state->Init_Ctrl[6].size = 2 ; + state->Init_Ctrl[6].addr[0] = 53; + state->Init_Ctrl[6].bit[0] = 3; + state->Init_Ctrl[6].val[0] = 0; + state->Init_Ctrl[6].addr[1] = 53; + state->Init_Ctrl[6].bit[1] = 4; + state->Init_Ctrl[6].val[1] = 1; + + state->Init_Ctrl[7].Ctrl_Num = RFSYN_CHP_GAIN ; + state->Init_Ctrl[7].size = 4 ; + state->Init_Ctrl[7].addr[0] = 22; + state->Init_Ctrl[7].bit[0] = 4; + state->Init_Ctrl[7].val[0] = 0; + state->Init_Ctrl[7].addr[1] = 22; + state->Init_Ctrl[7].bit[1] = 5; + state->Init_Ctrl[7].val[1] = 1; + state->Init_Ctrl[7].addr[2] = 22; + state->Init_Ctrl[7].bit[2] = 6; + state->Init_Ctrl[7].val[2] = 1; + state->Init_Ctrl[7].addr[3] = 22; + state->Init_Ctrl[7].bit[3] = 7; + state->Init_Ctrl[7].val[3] = 0; + + state->Init_Ctrl[8].Ctrl_Num = RFSYN_EN_CHP_HIGAIN ; + state->Init_Ctrl[8].size = 1 ; + state->Init_Ctrl[8].addr[0] = 22; + state->Init_Ctrl[8].bit[0] = 2; + state->Init_Ctrl[8].val[0] = 0; + + state->Init_Ctrl[9].Ctrl_Num = AGC_IF ; + state->Init_Ctrl[9].size = 4 ; + state->Init_Ctrl[9].addr[0] = 76; + state->Init_Ctrl[9].bit[0] = 0; + state->Init_Ctrl[9].val[0] = 1; + state->Init_Ctrl[9].addr[1] = 76; + state->Init_Ctrl[9].bit[1] = 1; + state->Init_Ctrl[9].val[1] = 1; + state->Init_Ctrl[9].addr[2] = 76; + state->Init_Ctrl[9].bit[2] = 2; + state->Init_Ctrl[9].val[2] = 0; + state->Init_Ctrl[9].addr[3] = 76; + state->Init_Ctrl[9].bit[3] = 3; + state->Init_Ctrl[9].val[3] = 1; + + state->Init_Ctrl[10].Ctrl_Num = AGC_RF ; + state->Init_Ctrl[10].size = 4 ; + state->Init_Ctrl[10].addr[0] = 76; + state->Init_Ctrl[10].bit[0] = 4; + state->Init_Ctrl[10].val[0] = 1; + state->Init_Ctrl[10].addr[1] = 76; + state->Init_Ctrl[10].bit[1] = 5; + state->Init_Ctrl[10].val[1] = 1; + state->Init_Ctrl[10].addr[2] = 76; + state->Init_Ctrl[10].bit[2] = 6; + state->Init_Ctrl[10].val[2] = 0; + state->Init_Ctrl[10].addr[3] = 76; + state->Init_Ctrl[10].bit[3] = 7; + state->Init_Ctrl[10].val[3] = 1; + + state->Init_Ctrl[11].Ctrl_Num = IF_DIVVAL ; + state->Init_Ctrl[11].size = 5 ; + state->Init_Ctrl[11].addr[0] = 43; + state->Init_Ctrl[11].bit[0] = 3; + state->Init_Ctrl[11].val[0] = 0; + state->Init_Ctrl[11].addr[1] = 43; + state->Init_Ctrl[11].bit[1] = 4; + state->Init_Ctrl[11].val[1] = 0; + state->Init_Ctrl[11].addr[2] = 43; + state->Init_Ctrl[11].bit[2] = 5; + state->Init_Ctrl[11].val[2] = 0; + state->Init_Ctrl[11].addr[3] = 43; + state->Init_Ctrl[11].bit[3] = 6; + state->Init_Ctrl[11].val[3] = 1; + state->Init_Ctrl[11].addr[4] = 43; + state->Init_Ctrl[11].bit[4] = 7; + state->Init_Ctrl[11].val[4] = 0; + + state->Init_Ctrl[12].Ctrl_Num = IF_VCO_BIAS ; + state->Init_Ctrl[12].size = 6 ; + state->Init_Ctrl[12].addr[0] = 44; + state->Init_Ctrl[12].bit[0] = 2; + state->Init_Ctrl[12].val[0] = 0; + state->Init_Ctrl[12].addr[1] = 44; + state->Init_Ctrl[12].bit[1] = 3; + state->Init_Ctrl[12].val[1] = 0; + state->Init_Ctrl[12].addr[2] = 44; + state->Init_Ctrl[12].bit[2] = 4; + state->Init_Ctrl[12].val[2] = 0; + state->Init_Ctrl[12].addr[3] = 44; + state->Init_Ctrl[12].bit[3] = 5; + state->Init_Ctrl[12].val[3] = 1; + state->Init_Ctrl[12].addr[4] = 44; + state->Init_Ctrl[12].bit[4] = 6; + state->Init_Ctrl[12].val[4] = 0; + state->Init_Ctrl[12].addr[5] = 44; + state->Init_Ctrl[12].bit[5] = 7; + state->Init_Ctrl[12].val[5] = 0; + + state->Init_Ctrl[13].Ctrl_Num = CHCAL_INT_MOD_IF ; + state->Init_Ctrl[13].size = 7 ; + state->Init_Ctrl[13].addr[0] = 11; + state->Init_Ctrl[13].bit[0] = 0; + state->Init_Ctrl[13].val[0] = 1; + state->Init_Ctrl[13].addr[1] = 11; + state->Init_Ctrl[13].bit[1] = 1; + state->Init_Ctrl[13].val[1] = 0; + state->Init_Ctrl[13].addr[2] = 11; + state->Init_Ctrl[13].bit[2] = 2; + state->Init_Ctrl[13].val[2] = 0; + state->Init_Ctrl[13].addr[3] = 11; + state->Init_Ctrl[13].bit[3] = 3; + state->Init_Ctrl[13].val[3] = 1; + state->Init_Ctrl[13].addr[4] = 11; + state->Init_Ctrl[13].bit[4] = 4; + state->Init_Ctrl[13].val[4] = 1; + state->Init_Ctrl[13].addr[5] = 11; + state->Init_Ctrl[13].bit[5] = 5; + state->Init_Ctrl[13].val[5] = 0; + state->Init_Ctrl[13].addr[6] = 11; + state->Init_Ctrl[13].bit[6] = 6; + state->Init_Ctrl[13].val[6] = 0; + + state->Init_Ctrl[14].Ctrl_Num = CHCAL_FRAC_MOD_IF ; + state->Init_Ctrl[14].size = 16 ; + state->Init_Ctrl[14].addr[0] = 13; + state->Init_Ctrl[14].bit[0] = 0; + state->Init_Ctrl[14].val[0] = 0; + state->Init_Ctrl[14].addr[1] = 13; + state->Init_Ctrl[14].bit[1] = 1; + state->Init_Ctrl[14].val[1] = 0; + state->Init_Ctrl[14].addr[2] = 13; + state->Init_Ctrl[14].bit[2] = 2; + state->Init_Ctrl[14].val[2] = 0; + state->Init_Ctrl[14].addr[3] = 13; + state->Init_Ctrl[14].bit[3] = 3; + state->Init_Ctrl[14].val[3] = 0; + state->Init_Ctrl[14].addr[4] = 13; + state->Init_Ctrl[14].bit[4] = 4; + state->Init_Ctrl[14].val[4] = 0; + state->Init_Ctrl[14].addr[5] = 13; + state->Init_Ctrl[14].bit[5] = 5; + state->Init_Ctrl[14].val[5] = 0; + state->Init_Ctrl[14].addr[6] = 13; + state->Init_Ctrl[14].bit[6] = 6; + state->Init_Ctrl[14].val[6] = 0; + state->Init_Ctrl[14].addr[7] = 13; + state->Init_Ctrl[14].bit[7] = 7; + state->Init_Ctrl[14].val[7] = 0; + state->Init_Ctrl[14].addr[8] = 12; + state->Init_Ctrl[14].bit[8] = 0; + state->Init_Ctrl[14].val[8] = 0; + state->Init_Ctrl[14].addr[9] = 12; + state->Init_Ctrl[14].bit[9] = 1; + state->Init_Ctrl[14].val[9] = 0; + state->Init_Ctrl[14].addr[10] = 12; + state->Init_Ctrl[14].bit[10] = 2; + state->Init_Ctrl[14].val[10] = 0; + state->Init_Ctrl[14].addr[11] = 12; + state->Init_Ctrl[14].bit[11] = 3; + state->Init_Ctrl[14].val[11] = 0; + state->Init_Ctrl[14].addr[12] = 12; + state->Init_Ctrl[14].bit[12] = 4; + state->Init_Ctrl[14].val[12] = 0; + state->Init_Ctrl[14].addr[13] = 12; + state->Init_Ctrl[14].bit[13] = 5; + state->Init_Ctrl[14].val[13] = 1; + state->Init_Ctrl[14].addr[14] = 12; + state->Init_Ctrl[14].bit[14] = 6; + state->Init_Ctrl[14].val[14] = 1; + state->Init_Ctrl[14].addr[15] = 12; + state->Init_Ctrl[14].bit[15] = 7; + state->Init_Ctrl[14].val[15] = 0; + + state->Init_Ctrl[15].Ctrl_Num = DRV_RES_SEL ; + state->Init_Ctrl[15].size = 3 ; + state->Init_Ctrl[15].addr[0] = 147; + state->Init_Ctrl[15].bit[0] = 2; + state->Init_Ctrl[15].val[0] = 0; + state->Init_Ctrl[15].addr[1] = 147; + state->Init_Ctrl[15].bit[1] = 3; + state->Init_Ctrl[15].val[1] = 1; + state->Init_Ctrl[15].addr[2] = 147; + state->Init_Ctrl[15].bit[2] = 4; + state->Init_Ctrl[15].val[2] = 1; + + state->Init_Ctrl[16].Ctrl_Num = I_DRIVER ; + state->Init_Ctrl[16].size = 2 ; + state->Init_Ctrl[16].addr[0] = 147; + state->Init_Ctrl[16].bit[0] = 0; + state->Init_Ctrl[16].val[0] = 0; + state->Init_Ctrl[16].addr[1] = 147; + state->Init_Ctrl[16].bit[1] = 1; + state->Init_Ctrl[16].val[1] = 1; + + state->Init_Ctrl[17].Ctrl_Num = EN_AAF ; + state->Init_Ctrl[17].size = 1 ; + state->Init_Ctrl[17].addr[0] = 147; + state->Init_Ctrl[17].bit[0] = 7; + state->Init_Ctrl[17].val[0] = 0; + + state->Init_Ctrl[18].Ctrl_Num = EN_3P ; + state->Init_Ctrl[18].size = 1 ; + state->Init_Ctrl[18].addr[0] = 147; + state->Init_Ctrl[18].bit[0] = 6; + state->Init_Ctrl[18].val[0] = 0; + + state->Init_Ctrl[19].Ctrl_Num = EN_AUX_3P ; + state->Init_Ctrl[19].size = 1 ; + state->Init_Ctrl[19].addr[0] = 156; + state->Init_Ctrl[19].bit[0] = 0; + state->Init_Ctrl[19].val[0] = 0; + + state->Init_Ctrl[20].Ctrl_Num = SEL_AAF_BAND ; + state->Init_Ctrl[20].size = 1 ; + state->Init_Ctrl[20].addr[0] = 147; + state->Init_Ctrl[20].bit[0] = 5; + state->Init_Ctrl[20].val[0] = 0; + + state->Init_Ctrl[21].Ctrl_Num = SEQ_ENCLK16_CLK_OUT ; + state->Init_Ctrl[21].size = 1 ; + state->Init_Ctrl[21].addr[0] = 137; + state->Init_Ctrl[21].bit[0] = 4; + state->Init_Ctrl[21].val[0] = 0; + + state->Init_Ctrl[22].Ctrl_Num = SEQ_SEL4_16B ; + state->Init_Ctrl[22].size = 1 ; + state->Init_Ctrl[22].addr[0] = 137; + state->Init_Ctrl[22].bit[0] = 7; + state->Init_Ctrl[22].val[0] = 0; + + state->Init_Ctrl[23].Ctrl_Num = XTAL_CAPSELECT ; + state->Init_Ctrl[23].size = 1 ; + state->Init_Ctrl[23].addr[0] = 91; + state->Init_Ctrl[23].bit[0] = 5; + state->Init_Ctrl[23].val[0] = 1; + + state->Init_Ctrl[24].Ctrl_Num = IF_SEL_DBL ; + state->Init_Ctrl[24].size = 1 ; + state->Init_Ctrl[24].addr[0] = 43; + state->Init_Ctrl[24].bit[0] = 0; + state->Init_Ctrl[24].val[0] = 1; + + state->Init_Ctrl[25].Ctrl_Num = RFSYN_R_DIV ; + state->Init_Ctrl[25].size = 2 ; + state->Init_Ctrl[25].addr[0] = 22; + state->Init_Ctrl[25].bit[0] = 0; + state->Init_Ctrl[25].val[0] = 1; + state->Init_Ctrl[25].addr[1] = 22; + state->Init_Ctrl[25].bit[1] = 1; + state->Init_Ctrl[25].val[1] = 1; + + state->Init_Ctrl[26].Ctrl_Num = SEQ_EXTSYNTHCALIF ; + state->Init_Ctrl[26].size = 1 ; + state->Init_Ctrl[26].addr[0] = 134; + state->Init_Ctrl[26].bit[0] = 2; + state->Init_Ctrl[26].val[0] = 0; + + state->Init_Ctrl[27].Ctrl_Num = SEQ_EXTDCCAL ; + state->Init_Ctrl[27].size = 1 ; + state->Init_Ctrl[27].addr[0] = 137; + state->Init_Ctrl[27].bit[0] = 3; + state->Init_Ctrl[27].val[0] = 0; + + state->Init_Ctrl[28].Ctrl_Num = AGC_EN_RSSI ; + state->Init_Ctrl[28].size = 1 ; + state->Init_Ctrl[28].addr[0] = 77; + state->Init_Ctrl[28].bit[0] = 7; + state->Init_Ctrl[28].val[0] = 0; + + state->Init_Ctrl[29].Ctrl_Num = RFA_ENCLKRFAGC ; + state->Init_Ctrl[29].size = 1 ; + state->Init_Ctrl[29].addr[0] = 166; + state->Init_Ctrl[29].bit[0] = 7; + state->Init_Ctrl[29].val[0] = 1; + + state->Init_Ctrl[30].Ctrl_Num = RFA_RSSI_REFH ; + state->Init_Ctrl[30].size = 3 ; + state->Init_Ctrl[30].addr[0] = 166; + state->Init_Ctrl[30].bit[0] = 0; + state->Init_Ctrl[30].val[0] = 0; + state->Init_Ctrl[30].addr[1] = 166; + state->Init_Ctrl[30].bit[1] = 1; + state->Init_Ctrl[30].val[1] = 1; + state->Init_Ctrl[30].addr[2] = 166; + state->Init_Ctrl[30].bit[2] = 2; + state->Init_Ctrl[30].val[2] = 1; + + state->Init_Ctrl[31].Ctrl_Num = RFA_RSSI_REF ; + state->Init_Ctrl[31].size = 3 ; + state->Init_Ctrl[31].addr[0] = 166; + state->Init_Ctrl[31].bit[0] = 3; + state->Init_Ctrl[31].val[0] = 1; + state->Init_Ctrl[31].addr[1] = 166; + state->Init_Ctrl[31].bit[1] = 4; + state->Init_Ctrl[31].val[1] = 0; + state->Init_Ctrl[31].addr[2] = 166; + state->Init_Ctrl[31].bit[2] = 5; + state->Init_Ctrl[31].val[2] = 1; + + state->Init_Ctrl[32].Ctrl_Num = RFA_RSSI_REFL ; + state->Init_Ctrl[32].size = 3 ; + state->Init_Ctrl[32].addr[0] = 167; + state->Init_Ctrl[32].bit[0] = 0; + state->Init_Ctrl[32].val[0] = 1; + state->Init_Ctrl[32].addr[1] = 167; + state->Init_Ctrl[32].bit[1] = 1; + state->Init_Ctrl[32].val[1] = 1; + state->Init_Ctrl[32].addr[2] = 167; + state->Init_Ctrl[32].bit[2] = 2; + state->Init_Ctrl[32].val[2] = 0; + + state->Init_Ctrl[33].Ctrl_Num = RFA_FLR ; + state->Init_Ctrl[33].size = 4 ; + state->Init_Ctrl[33].addr[0] = 168; + state->Init_Ctrl[33].bit[0] = 0; + state->Init_Ctrl[33].val[0] = 0; + state->Init_Ctrl[33].addr[1] = 168; + state->Init_Ctrl[33].bit[1] = 1; + state->Init_Ctrl[33].val[1] = 1; + state->Init_Ctrl[33].addr[2] = 168; + state->Init_Ctrl[33].bit[2] = 2; + state->Init_Ctrl[33].val[2] = 0; + state->Init_Ctrl[33].addr[3] = 168; + state->Init_Ctrl[33].bit[3] = 3; + state->Init_Ctrl[33].val[3] = 0; + + state->Init_Ctrl[34].Ctrl_Num = RFA_CEIL ; + state->Init_Ctrl[34].size = 4 ; + state->Init_Ctrl[34].addr[0] = 168; + state->Init_Ctrl[34].bit[0] = 4; + state->Init_Ctrl[34].val[0] = 1; + state->Init_Ctrl[34].addr[1] = 168; + state->Init_Ctrl[34].bit[1] = 5; + state->Init_Ctrl[34].val[1] = 1; + state->Init_Ctrl[34].addr[2] = 168; + state->Init_Ctrl[34].bit[2] = 6; + state->Init_Ctrl[34].val[2] = 1; + state->Init_Ctrl[34].addr[3] = 168; + state->Init_Ctrl[34].bit[3] = 7; + state->Init_Ctrl[34].val[3] = 1; + + state->Init_Ctrl[35].Ctrl_Num = SEQ_EXTIQFSMPULSE ; + state->Init_Ctrl[35].size = 1 ; + state->Init_Ctrl[35].addr[0] = 135; + state->Init_Ctrl[35].bit[0] = 0; + state->Init_Ctrl[35].val[0] = 0; + + state->Init_Ctrl[36].Ctrl_Num = OVERRIDE_1 ; + state->Init_Ctrl[36].size = 1 ; + state->Init_Ctrl[36].addr[0] = 56; + state->Init_Ctrl[36].bit[0] = 3; + state->Init_Ctrl[36].val[0] = 0; + + state->Init_Ctrl[37].Ctrl_Num = BB_INITSTATE_DLPF_TUNE ; + state->Init_Ctrl[37].size = 7 ; + state->Init_Ctrl[37].addr[0] = 59; + state->Init_Ctrl[37].bit[0] = 1; + state->Init_Ctrl[37].val[0] = 0; + state->Init_Ctrl[37].addr[1] = 59; + state->Init_Ctrl[37].bit[1] = 2; + state->Init_Ctrl[37].val[1] = 0; + state->Init_Ctrl[37].addr[2] = 59; + state->Init_Ctrl[37].bit[2] = 3; + state->Init_Ctrl[37].val[2] = 0; + state->Init_Ctrl[37].addr[3] = 59; + state->Init_Ctrl[37].bit[3] = 4; + state->Init_Ctrl[37].val[3] = 0; + state->Init_Ctrl[37].addr[4] = 59; + state->Init_Ctrl[37].bit[4] = 5; + state->Init_Ctrl[37].val[4] = 0; + state->Init_Ctrl[37].addr[5] = 59; + state->Init_Ctrl[37].bit[5] = 6; + state->Init_Ctrl[37].val[5] = 0; + state->Init_Ctrl[37].addr[6] = 59; + state->Init_Ctrl[37].bit[6] = 7; + state->Init_Ctrl[37].val[6] = 0; + + state->Init_Ctrl[38].Ctrl_Num = TG_R_DIV ; + state->Init_Ctrl[38].size = 6 ; + state->Init_Ctrl[38].addr[0] = 32; + state->Init_Ctrl[38].bit[0] = 2; + state->Init_Ctrl[38].val[0] = 0; + state->Init_Ctrl[38].addr[1] = 32; + state->Init_Ctrl[38].bit[1] = 3; + state->Init_Ctrl[38].val[1] = 0; + state->Init_Ctrl[38].addr[2] = 32; + state->Init_Ctrl[38].bit[2] = 4; + state->Init_Ctrl[38].val[2] = 0; + state->Init_Ctrl[38].addr[3] = 32; + state->Init_Ctrl[38].bit[3] = 5; + state->Init_Ctrl[38].val[3] = 0; + state->Init_Ctrl[38].addr[4] = 32; + state->Init_Ctrl[38].bit[4] = 6; + state->Init_Ctrl[38].val[4] = 1; + state->Init_Ctrl[38].addr[5] = 32; + state->Init_Ctrl[38].bit[5] = 7; + state->Init_Ctrl[38].val[5] = 0; + + state->Init_Ctrl[39].Ctrl_Num = EN_CHP_LIN_B ; + state->Init_Ctrl[39].size = 1 ; + state->Init_Ctrl[39].addr[0] = 25; + state->Init_Ctrl[39].bit[0] = 3; + state->Init_Ctrl[39].val[0] = 1; + + + state->CH_Ctrl_Num = CHCTRL_NUM ; + + state->CH_Ctrl[0].Ctrl_Num = DN_POLY ; + state->CH_Ctrl[0].size = 2 ; + state->CH_Ctrl[0].addr[0] = 68; + state->CH_Ctrl[0].bit[0] = 6; + state->CH_Ctrl[0].val[0] = 1; + state->CH_Ctrl[0].addr[1] = 68; + state->CH_Ctrl[0].bit[1] = 7; + state->CH_Ctrl[0].val[1] = 1; + + state->CH_Ctrl[1].Ctrl_Num = DN_RFGAIN ; + state->CH_Ctrl[1].size = 2 ; + state->CH_Ctrl[1].addr[0] = 70; + state->CH_Ctrl[1].bit[0] = 6; + state->CH_Ctrl[1].val[0] = 1; + state->CH_Ctrl[1].addr[1] = 70; + state->CH_Ctrl[1].bit[1] = 7; + state->CH_Ctrl[1].val[1] = 0; + + state->CH_Ctrl[2].Ctrl_Num = DN_CAP_RFLPF ; + state->CH_Ctrl[2].size = 9 ; + state->CH_Ctrl[2].addr[0] = 69; + state->CH_Ctrl[2].bit[0] = 5; + state->CH_Ctrl[2].val[0] = 0; + state->CH_Ctrl[2].addr[1] = 69; + state->CH_Ctrl[2].bit[1] = 6; + state->CH_Ctrl[2].val[1] = 0; + state->CH_Ctrl[2].addr[2] = 69; + state->CH_Ctrl[2].bit[2] = 7; + state->CH_Ctrl[2].val[2] = 0; + state->CH_Ctrl[2].addr[3] = 68; + state->CH_Ctrl[2].bit[3] = 0; + state->CH_Ctrl[2].val[3] = 0; + state->CH_Ctrl[2].addr[4] = 68; + state->CH_Ctrl[2].bit[4] = 1; + state->CH_Ctrl[2].val[4] = 0; + state->CH_Ctrl[2].addr[5] = 68; + state->CH_Ctrl[2].bit[5] = 2; + state->CH_Ctrl[2].val[5] = 0; + state->CH_Ctrl[2].addr[6] = 68; + state->CH_Ctrl[2].bit[6] = 3; + state->CH_Ctrl[2].val[6] = 0; + state->CH_Ctrl[2].addr[7] = 68; + state->CH_Ctrl[2].bit[7] = 4; + state->CH_Ctrl[2].val[7] = 0; + state->CH_Ctrl[2].addr[8] = 68; + state->CH_Ctrl[2].bit[8] = 5; + state->CH_Ctrl[2].val[8] = 0; + + state->CH_Ctrl[3].Ctrl_Num = DN_EN_VHFUHFBAR ; + state->CH_Ctrl[3].size = 1 ; + state->CH_Ctrl[3].addr[0] = 70; + state->CH_Ctrl[3].bit[0] = 5; + state->CH_Ctrl[3].val[0] = 0; + + state->CH_Ctrl[4].Ctrl_Num = DN_GAIN_ADJUST ; + state->CH_Ctrl[4].size = 3 ; + state->CH_Ctrl[4].addr[0] = 73; + state->CH_Ctrl[4].bit[0] = 4; + state->CH_Ctrl[4].val[0] = 0; + state->CH_Ctrl[4].addr[1] = 73; + state->CH_Ctrl[4].bit[1] = 5; + state->CH_Ctrl[4].val[1] = 1; + state->CH_Ctrl[4].addr[2] = 73; + state->CH_Ctrl[4].bit[2] = 6; + state->CH_Ctrl[4].val[2] = 0; + + state->CH_Ctrl[5].Ctrl_Num = DN_IQTNBUF_AMP ; + state->CH_Ctrl[5].size = 4 ; + state->CH_Ctrl[5].addr[0] = 70; + state->CH_Ctrl[5].bit[0] = 0; + state->CH_Ctrl[5].val[0] = 0; + state->CH_Ctrl[5].addr[1] = 70; + state->CH_Ctrl[5].bit[1] = 1; + state->CH_Ctrl[5].val[1] = 0; + state->CH_Ctrl[5].addr[2] = 70; + state->CH_Ctrl[5].bit[2] = 2; + state->CH_Ctrl[5].val[2] = 0; + state->CH_Ctrl[5].addr[3] = 70; + state->CH_Ctrl[5].bit[3] = 3; + state->CH_Ctrl[5].val[3] = 0; + + state->CH_Ctrl[6].Ctrl_Num = DN_IQTNGNBFBIAS_BST ; + state->CH_Ctrl[6].size = 1 ; + state->CH_Ctrl[6].addr[0] = 70; + state->CH_Ctrl[6].bit[0] = 4; + state->CH_Ctrl[6].val[0] = 1; + + state->CH_Ctrl[7].Ctrl_Num = RFSYN_EN_OUTMUX ; + state->CH_Ctrl[7].size = 1 ; + state->CH_Ctrl[7].addr[0] = 111; + state->CH_Ctrl[7].bit[0] = 4; + state->CH_Ctrl[7].val[0] = 0; + + state->CH_Ctrl[8].Ctrl_Num = RFSYN_SEL_VCO_OUT ; + state->CH_Ctrl[8].size = 1 ; + state->CH_Ctrl[8].addr[0] = 111; + state->CH_Ctrl[8].bit[0] = 7; + state->CH_Ctrl[8].val[0] = 1; + + state->CH_Ctrl[9].Ctrl_Num = RFSYN_SEL_VCO_HI ; + state->CH_Ctrl[9].size = 1 ; + state->CH_Ctrl[9].addr[0] = 111; + state->CH_Ctrl[9].bit[0] = 6; + state->CH_Ctrl[9].val[0] = 1; + + state->CH_Ctrl[10].Ctrl_Num = RFSYN_SEL_DIVM ; + state->CH_Ctrl[10].size = 1 ; + state->CH_Ctrl[10].addr[0] = 111; + state->CH_Ctrl[10].bit[0] = 5; + state->CH_Ctrl[10].val[0] = 0; + + state->CH_Ctrl[11].Ctrl_Num = RFSYN_RF_DIV_BIAS ; + state->CH_Ctrl[11].size = 2 ; + state->CH_Ctrl[11].addr[0] = 110; + state->CH_Ctrl[11].bit[0] = 0; + state->CH_Ctrl[11].val[0] = 1; + state->CH_Ctrl[11].addr[1] = 110; + state->CH_Ctrl[11].bit[1] = 1; + state->CH_Ctrl[11].val[1] = 0; + + state->CH_Ctrl[12].Ctrl_Num = DN_SEL_FREQ ; + state->CH_Ctrl[12].size = 3 ; + state->CH_Ctrl[12].addr[0] = 69; + state->CH_Ctrl[12].bit[0] = 2; + state->CH_Ctrl[12].val[0] = 0; + state->CH_Ctrl[12].addr[1] = 69; + state->CH_Ctrl[12].bit[1] = 3; + state->CH_Ctrl[12].val[1] = 0; + state->CH_Ctrl[12].addr[2] = 69; + state->CH_Ctrl[12].bit[2] = 4; + state->CH_Ctrl[12].val[2] = 0; + + state->CH_Ctrl[13].Ctrl_Num = RFSYN_VCO_BIAS ; + state->CH_Ctrl[13].size = 6 ; + state->CH_Ctrl[13].addr[0] = 110; + state->CH_Ctrl[13].bit[0] = 2; + state->CH_Ctrl[13].val[0] = 0; + state->CH_Ctrl[13].addr[1] = 110; + state->CH_Ctrl[13].bit[1] = 3; + state->CH_Ctrl[13].val[1] = 0; + state->CH_Ctrl[13].addr[2] = 110; + state->CH_Ctrl[13].bit[2] = 4; + state->CH_Ctrl[13].val[2] = 0; + state->CH_Ctrl[13].addr[3] = 110; + state->CH_Ctrl[13].bit[3] = 5; + state->CH_Ctrl[13].val[3] = 0; + state->CH_Ctrl[13].addr[4] = 110; + state->CH_Ctrl[13].bit[4] = 6; + state->CH_Ctrl[13].val[4] = 0; + state->CH_Ctrl[13].addr[5] = 110; + state->CH_Ctrl[13].bit[5] = 7; + state->CH_Ctrl[13].val[5] = 1; + + state->CH_Ctrl[14].Ctrl_Num = CHCAL_INT_MOD_RF ; + state->CH_Ctrl[14].size = 7 ; + state->CH_Ctrl[14].addr[0] = 14; + state->CH_Ctrl[14].bit[0] = 0; + state->CH_Ctrl[14].val[0] = 0; + state->CH_Ctrl[14].addr[1] = 14; + state->CH_Ctrl[14].bit[1] = 1; + state->CH_Ctrl[14].val[1] = 0; + state->CH_Ctrl[14].addr[2] = 14; + state->CH_Ctrl[14].bit[2] = 2; + state->CH_Ctrl[14].val[2] = 0; + state->CH_Ctrl[14].addr[3] = 14; + state->CH_Ctrl[14].bit[3] = 3; + state->CH_Ctrl[14].val[3] = 0; + state->CH_Ctrl[14].addr[4] = 14; + state->CH_Ctrl[14].bit[4] = 4; + state->CH_Ctrl[14].val[4] = 0; + state->CH_Ctrl[14].addr[5] = 14; + state->CH_Ctrl[14].bit[5] = 5; + state->CH_Ctrl[14].val[5] = 0; + state->CH_Ctrl[14].addr[6] = 14; + state->CH_Ctrl[14].bit[6] = 6; + state->CH_Ctrl[14].val[6] = 0; + + state->CH_Ctrl[15].Ctrl_Num = CHCAL_FRAC_MOD_RF ; + state->CH_Ctrl[15].size = 18 ; + state->CH_Ctrl[15].addr[0] = 17; + state->CH_Ctrl[15].bit[0] = 6; + state->CH_Ctrl[15].val[0] = 0; + state->CH_Ctrl[15].addr[1] = 17; + state->CH_Ctrl[15].bit[1] = 7; + state->CH_Ctrl[15].val[1] = 0; + state->CH_Ctrl[15].addr[2] = 16; + state->CH_Ctrl[15].bit[2] = 0; + state->CH_Ctrl[15].val[2] = 0; + state->CH_Ctrl[15].addr[3] = 16; + state->CH_Ctrl[15].bit[3] = 1; + state->CH_Ctrl[15].val[3] = 0; + state->CH_Ctrl[15].addr[4] = 16; + state->CH_Ctrl[15].bit[4] = 2; + state->CH_Ctrl[15].val[4] = 0; + state->CH_Ctrl[15].addr[5] = 16; + state->CH_Ctrl[15].bit[5] = 3; + state->CH_Ctrl[15].val[5] = 0; + state->CH_Ctrl[15].addr[6] = 16; + state->CH_Ctrl[15].bit[6] = 4; + state->CH_Ctrl[15].val[6] = 0; + state->CH_Ctrl[15].addr[7] = 16; + state->CH_Ctrl[15].bit[7] = 5; + state->CH_Ctrl[15].val[7] = 0; + state->CH_Ctrl[15].addr[8] = 16; + state->CH_Ctrl[15].bit[8] = 6; + state->CH_Ctrl[15].val[8] = 0; + state->CH_Ctrl[15].addr[9] = 16; + state->CH_Ctrl[15].bit[9] = 7; + state->CH_Ctrl[15].val[9] = 0; + state->CH_Ctrl[15].addr[10] = 15; + state->CH_Ctrl[15].bit[10] = 0; + state->CH_Ctrl[15].val[10] = 0; + state->CH_Ctrl[15].addr[11] = 15; + state->CH_Ctrl[15].bit[11] = 1; + state->CH_Ctrl[15].val[11] = 0; + state->CH_Ctrl[15].addr[12] = 15; + state->CH_Ctrl[15].bit[12] = 2; + state->CH_Ctrl[15].val[12] = 0; + state->CH_Ctrl[15].addr[13] = 15; + state->CH_Ctrl[15].bit[13] = 3; + state->CH_Ctrl[15].val[13] = 0; + state->CH_Ctrl[15].addr[14] = 15; + state->CH_Ctrl[15].bit[14] = 4; + state->CH_Ctrl[15].val[14] = 0; + state->CH_Ctrl[15].addr[15] = 15; + state->CH_Ctrl[15].bit[15] = 5; + state->CH_Ctrl[15].val[15] = 0; + state->CH_Ctrl[15].addr[16] = 15; + state->CH_Ctrl[15].bit[16] = 6; + state->CH_Ctrl[15].val[16] = 1; + state->CH_Ctrl[15].addr[17] = 15; + state->CH_Ctrl[15].bit[17] = 7; + state->CH_Ctrl[15].val[17] = 1; + + state->CH_Ctrl[16].Ctrl_Num = RFSYN_LPF_R ; + state->CH_Ctrl[16].size = 5 ; + state->CH_Ctrl[16].addr[0] = 112; + state->CH_Ctrl[16].bit[0] = 0; + state->CH_Ctrl[16].val[0] = 0; + state->CH_Ctrl[16].addr[1] = 112; + state->CH_Ctrl[16].bit[1] = 1; + state->CH_Ctrl[16].val[1] = 0; + state->CH_Ctrl[16].addr[2] = 112; + state->CH_Ctrl[16].bit[2] = 2; + state->CH_Ctrl[16].val[2] = 0; + state->CH_Ctrl[16].addr[3] = 112; + state->CH_Ctrl[16].bit[3] = 3; + state->CH_Ctrl[16].val[3] = 0; + state->CH_Ctrl[16].addr[4] = 112; + state->CH_Ctrl[16].bit[4] = 4; + state->CH_Ctrl[16].val[4] = 1; + + state->CH_Ctrl[17].Ctrl_Num = CHCAL_EN_INT_RF ; + state->CH_Ctrl[17].size = 1 ; + state->CH_Ctrl[17].addr[0] = 14; + state->CH_Ctrl[17].bit[0] = 7; + state->CH_Ctrl[17].val[0] = 0; + + state->CH_Ctrl[18].Ctrl_Num = TG_LO_DIVVAL ; + state->CH_Ctrl[18].size = 4 ; + state->CH_Ctrl[18].addr[0] = 107; + state->CH_Ctrl[18].bit[0] = 3; + state->CH_Ctrl[18].val[0] = 0; + state->CH_Ctrl[18].addr[1] = 107; + state->CH_Ctrl[18].bit[1] = 4; + state->CH_Ctrl[18].val[1] = 0; + state->CH_Ctrl[18].addr[2] = 107; + state->CH_Ctrl[18].bit[2] = 5; + state->CH_Ctrl[18].val[2] = 0; + state->CH_Ctrl[18].addr[3] = 107; + state->CH_Ctrl[18].bit[3] = 6; + state->CH_Ctrl[18].val[3] = 0; + + state->CH_Ctrl[19].Ctrl_Num = TG_LO_SELVAL ; + state->CH_Ctrl[19].size = 3 ; + state->CH_Ctrl[19].addr[0] = 107; + state->CH_Ctrl[19].bit[0] = 7; + state->CH_Ctrl[19].val[0] = 1; + state->CH_Ctrl[19].addr[1] = 106; + state->CH_Ctrl[19].bit[1] = 0; + state->CH_Ctrl[19].val[1] = 1; + state->CH_Ctrl[19].addr[2] = 106; + state->CH_Ctrl[19].bit[2] = 1; + state->CH_Ctrl[19].val[2] = 1; + + state->CH_Ctrl[20].Ctrl_Num = TG_DIV_VAL ; + state->CH_Ctrl[20].size = 11 ; + state->CH_Ctrl[20].addr[0] = 109; + state->CH_Ctrl[20].bit[0] = 2; + state->CH_Ctrl[20].val[0] = 0; + state->CH_Ctrl[20].addr[1] = 109; + state->CH_Ctrl[20].bit[1] = 3; + state->CH_Ctrl[20].val[1] = 0; + state->CH_Ctrl[20].addr[2] = 109; + state->CH_Ctrl[20].bit[2] = 4; + state->CH_Ctrl[20].val[2] = 0; + state->CH_Ctrl[20].addr[3] = 109; + state->CH_Ctrl[20].bit[3] = 5; + state->CH_Ctrl[20].val[3] = 0; + state->CH_Ctrl[20].addr[4] = 109; + state->CH_Ctrl[20].bit[4] = 6; + state->CH_Ctrl[20].val[4] = 0; + state->CH_Ctrl[20].addr[5] = 109; + state->CH_Ctrl[20].bit[5] = 7; + state->CH_Ctrl[20].val[5] = 0; + state->CH_Ctrl[20].addr[6] = 108; + state->CH_Ctrl[20].bit[6] = 0; + state->CH_Ctrl[20].val[6] = 0; + state->CH_Ctrl[20].addr[7] = 108; + state->CH_Ctrl[20].bit[7] = 1; + state->CH_Ctrl[20].val[7] = 0; + state->CH_Ctrl[20].addr[8] = 108; + state->CH_Ctrl[20].bit[8] = 2; + state->CH_Ctrl[20].val[8] = 1; + state->CH_Ctrl[20].addr[9] = 108; + state->CH_Ctrl[20].bit[9] = 3; + state->CH_Ctrl[20].val[9] = 1; + state->CH_Ctrl[20].addr[10] = 108; + state->CH_Ctrl[20].bit[10] = 4; + state->CH_Ctrl[20].val[10] = 1; + + state->CH_Ctrl[21].Ctrl_Num = TG_VCO_BIAS ; + state->CH_Ctrl[21].size = 6 ; + state->CH_Ctrl[21].addr[0] = 106; + state->CH_Ctrl[21].bit[0] = 2; + state->CH_Ctrl[21].val[0] = 0; + state->CH_Ctrl[21].addr[1] = 106; + state->CH_Ctrl[21].bit[1] = 3; + state->CH_Ctrl[21].val[1] = 0; + state->CH_Ctrl[21].addr[2] = 106; + state->CH_Ctrl[21].bit[2] = 4; + state->CH_Ctrl[21].val[2] = 0; + state->CH_Ctrl[21].addr[3] = 106; + state->CH_Ctrl[21].bit[3] = 5; + state->CH_Ctrl[21].val[3] = 0; + state->CH_Ctrl[21].addr[4] = 106; + state->CH_Ctrl[21].bit[4] = 6; + state->CH_Ctrl[21].val[4] = 0; + state->CH_Ctrl[21].addr[5] = 106; + state->CH_Ctrl[21].bit[5] = 7; + state->CH_Ctrl[21].val[5] = 1; + + state->CH_Ctrl[22].Ctrl_Num = SEQ_EXTPOWERUP ; + state->CH_Ctrl[22].size = 1 ; + state->CH_Ctrl[22].addr[0] = 138; + state->CH_Ctrl[22].bit[0] = 4; + state->CH_Ctrl[22].val[0] = 1; + + state->CH_Ctrl[23].Ctrl_Num = OVERRIDE_2 ; + state->CH_Ctrl[23].size = 1 ; + state->CH_Ctrl[23].addr[0] = 17; + state->CH_Ctrl[23].bit[0] = 5; + state->CH_Ctrl[23].val[0] = 0; + + state->CH_Ctrl[24].Ctrl_Num = OVERRIDE_3 ; + state->CH_Ctrl[24].size = 1 ; + state->CH_Ctrl[24].addr[0] = 111; + state->CH_Ctrl[24].bit[0] = 3; + state->CH_Ctrl[24].val[0] = 0; + + state->CH_Ctrl[25].Ctrl_Num = OVERRIDE_4 ; + state->CH_Ctrl[25].size = 1 ; + state->CH_Ctrl[25].addr[0] = 112; + state->CH_Ctrl[25].bit[0] = 7; + state->CH_Ctrl[25].val[0] = 0; + + state->CH_Ctrl[26].Ctrl_Num = SEQ_FSM_PULSE ; + state->CH_Ctrl[26].size = 1 ; + state->CH_Ctrl[26].addr[0] = 136; + state->CH_Ctrl[26].bit[0] = 7; + state->CH_Ctrl[26].val[0] = 0; + + state->CH_Ctrl[27].Ctrl_Num = GPIO_4B ; + state->CH_Ctrl[27].size = 1 ; + state->CH_Ctrl[27].addr[0] = 149; + state->CH_Ctrl[27].bit[0] = 7; + state->CH_Ctrl[27].val[0] = 0; + + state->CH_Ctrl[28].Ctrl_Num = GPIO_3B ; + state->CH_Ctrl[28].size = 1 ; + state->CH_Ctrl[28].addr[0] = 149; + state->CH_Ctrl[28].bit[0] = 6; + state->CH_Ctrl[28].val[0] = 0; + + state->CH_Ctrl[29].Ctrl_Num = GPIO_4 ; + state->CH_Ctrl[29].size = 1 ; + state->CH_Ctrl[29].addr[0] = 149; + state->CH_Ctrl[29].bit[0] = 5; + state->CH_Ctrl[29].val[0] = 1; + + state->CH_Ctrl[30].Ctrl_Num = GPIO_3 ; + state->CH_Ctrl[30].size = 1 ; + state->CH_Ctrl[30].addr[0] = 149; + state->CH_Ctrl[30].bit[0] = 4; + state->CH_Ctrl[30].val[0] = 1; + + state->CH_Ctrl[31].Ctrl_Num = GPIO_1B ; + state->CH_Ctrl[31].size = 1 ; + state->CH_Ctrl[31].addr[0] = 149; + state->CH_Ctrl[31].bit[0] = 3; + state->CH_Ctrl[31].val[0] = 0; + + state->CH_Ctrl[32].Ctrl_Num = DAC_A_ENABLE ; + state->CH_Ctrl[32].size = 1 ; + state->CH_Ctrl[32].addr[0] = 93; + state->CH_Ctrl[32].bit[0] = 1; + state->CH_Ctrl[32].val[0] = 0; + + state->CH_Ctrl[33].Ctrl_Num = DAC_B_ENABLE ; + state->CH_Ctrl[33].size = 1 ; + state->CH_Ctrl[33].addr[0] = 93; + state->CH_Ctrl[33].bit[0] = 0; + state->CH_Ctrl[33].val[0] = 0; + + state->CH_Ctrl[34].Ctrl_Num = DAC_DIN_A ; + state->CH_Ctrl[34].size = 6 ; + state->CH_Ctrl[34].addr[0] = 92; + state->CH_Ctrl[34].bit[0] = 2; + state->CH_Ctrl[34].val[0] = 0; + state->CH_Ctrl[34].addr[1] = 92; + state->CH_Ctrl[34].bit[1] = 3; + state->CH_Ctrl[34].val[1] = 0; + state->CH_Ctrl[34].addr[2] = 92; + state->CH_Ctrl[34].bit[2] = 4; + state->CH_Ctrl[34].val[2] = 0; + state->CH_Ctrl[34].addr[3] = 92; + state->CH_Ctrl[34].bit[3] = 5; + state->CH_Ctrl[34].val[3] = 0; + state->CH_Ctrl[34].addr[4] = 92; + state->CH_Ctrl[34].bit[4] = 6; + state->CH_Ctrl[34].val[4] = 0; + state->CH_Ctrl[34].addr[5] = 92; + state->CH_Ctrl[34].bit[5] = 7; + state->CH_Ctrl[34].val[5] = 0; + + state->CH_Ctrl[35].Ctrl_Num = DAC_DIN_B ; + state->CH_Ctrl[35].size = 6 ; + state->CH_Ctrl[35].addr[0] = 93; + state->CH_Ctrl[35].bit[0] = 2; + state->CH_Ctrl[35].val[0] = 0; + state->CH_Ctrl[35].addr[1] = 93; + state->CH_Ctrl[35].bit[1] = 3; + state->CH_Ctrl[35].val[1] = 0; + state->CH_Ctrl[35].addr[2] = 93; + state->CH_Ctrl[35].bit[2] = 4; + state->CH_Ctrl[35].val[2] = 0; + state->CH_Ctrl[35].addr[3] = 93; + state->CH_Ctrl[35].bit[3] = 5; + state->CH_Ctrl[35].val[3] = 0; + state->CH_Ctrl[35].addr[4] = 93; + state->CH_Ctrl[35].bit[4] = 6; + state->CH_Ctrl[35].val[4] = 0; + state->CH_Ctrl[35].addr[5] = 93; + state->CH_Ctrl[35].bit[5] = 7; + state->CH_Ctrl[35].val[5] = 0; + +#ifdef _MXL_PRODUCTION + state->CH_Ctrl[36].Ctrl_Num = RFSYN_EN_DIV ; + state->CH_Ctrl[36].size = 1 ; + state->CH_Ctrl[36].addr[0] = 109; + state->CH_Ctrl[36].bit[0] = 1; + state->CH_Ctrl[36].val[0] = 1; + + state->CH_Ctrl[37].Ctrl_Num = RFSYN_DIVM ; + state->CH_Ctrl[37].size = 2 ; + state->CH_Ctrl[37].addr[0] = 112; + state->CH_Ctrl[37].bit[0] = 5; + state->CH_Ctrl[37].val[0] = 0; + state->CH_Ctrl[37].addr[1] = 112; + state->CH_Ctrl[37].bit[1] = 6; + state->CH_Ctrl[37].val[1] = 0; + + state->CH_Ctrl[38].Ctrl_Num = DN_BYPASS_AGC_I2C ; + state->CH_Ctrl[38].size = 1 ; + state->CH_Ctrl[38].addr[0] = 65; + state->CH_Ctrl[38].bit[0] = 1; + state->CH_Ctrl[38].val[0] = 0; +#endif + + return 0 ; +} + +static void InitTunerControls(struct dvb_frontend *fe) +{ + MXL5005_RegisterInit(fe); + MXL5005_ControlInit(fe); +#ifdef _MXL_INTERNAL + MXL5005_MXLControlInit(fe); +#endif +} + +static u16 MXL5005_TunerConfig(struct dvb_frontend *fe, + u8 Mode, /* 0: Analog Mode ; 1: Digital Mode */ + u8 IF_mode, /* for Analog Mode, 0: zero IF; 1: low IF */ + u32 Bandwidth, /* filter channel bandwidth (6, 7, 8) */ + u32 IF_out, /* Desired IF Out Frequency */ + u32 Fxtal, /* XTAL Frequency */ + u8 AGC_Mode, /* AGC Mode - Dual AGC: 0, Single AGC: 1 */ + u16 TOP, /* 0: Dual AGC; Value: take over point */ + u16 IF_OUT_LOAD, /* IF Out Load Resistor (200 / 300 Ohms) */ + u8 CLOCK_OUT, /* 0: turn off clk out; 1: turn on clock out */ + u8 DIV_OUT, /* 0: Div-1; 1: Div-4 */ + u8 CAPSELECT, /* 0: disable On-Chip pulling cap; 1: enable */ + u8 EN_RSSI, /* 0: disable RSSI; 1: enable RSSI */ + + /* Modulation Type; */ + /* 0 - Default; 1 - DVB-T; 2 - ATSC; 3 - QAM; 4 - Analog Cable */ + u8 Mod_Type, + + /* Tracking Filter */ + /* 0 - Default; 1 - Off; 2 - Type C; 3 - Type C-H */ + u8 TF_Type + ) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u16 status = 0; + + state->Mode = Mode; + state->IF_Mode = IF_mode; + state->Chan_Bandwidth = Bandwidth; + state->IF_OUT = IF_out; + state->Fxtal = Fxtal; + state->AGC_Mode = AGC_Mode; + state->TOP = TOP; + state->IF_OUT_LOAD = IF_OUT_LOAD; + state->CLOCK_OUT = CLOCK_OUT; + state->DIV_OUT = DIV_OUT; + state->CAPSELECT = CAPSELECT; + state->EN_RSSI = EN_RSSI; + state->Mod_Type = Mod_Type; + state->TF_Type = TF_Type; + + /* Initialize all the controls and registers */ + InitTunerControls(fe); + + /* Synthesizer LO frequency calculation */ + MXL_SynthIFLO_Calc(fe); + + return status; +} + +static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + if (state->Mode == 1) /* Digital Mode */ + state->IF_LO = state->IF_OUT; + else /* Analog Mode */ { + if (state->IF_Mode == 0) /* Analog Zero IF mode */ + state->IF_LO = state->IF_OUT + 400000; + else /* Analog Low IF mode */ + state->IF_LO = state->IF_OUT + state->Chan_Bandwidth/2; + } +} + +static void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + + if (state->Mode == 1) /* Digital Mode */ { + /* remove 20.48MHz setting for 2.6.10 */ + state->RF_LO = state->RF_IN; + /* change for 2.6.6 */ + state->TG_LO = state->RF_IN - 750000; + } else /* Analog Mode */ { + if (state->IF_Mode == 0) /* Analog Zero IF mode */ { + state->RF_LO = state->RF_IN - 400000; + state->TG_LO = state->RF_IN - 1750000; + } else /* Analog Low IF mode */ { + state->RF_LO = state->RF_IN - state->Chan_Bandwidth/2; + state->TG_LO = state->RF_IN - + state->Chan_Bandwidth + 500000; + } + } +} + +static u16 MXL_OverwriteICDefault(struct dvb_frontend *fe) +{ + u16 status = 0; + + status += MXL_ControlWrite(fe, OVERRIDE_1, 1); + status += MXL_ControlWrite(fe, OVERRIDE_2, 1); + status += MXL_ControlWrite(fe, OVERRIDE_3, 1); + status += MXL_ControlWrite(fe, OVERRIDE_4, 1); + + return status; +} + +static u16 MXL_BlockInit(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u16 status = 0; + + status += MXL_OverwriteICDefault(fe); + + /* Downconverter Control Dig Ana */ + status += MXL_ControlWrite(fe, DN_IQTN_AMP_CUT, state->Mode ? 1 : 0); + + /* Filter Control Dig Ana */ + status += MXL_ControlWrite(fe, BB_MODE, state->Mode ? 0 : 1); + status += MXL_ControlWrite(fe, BB_BUF, state->Mode ? 3 : 2); + status += MXL_ControlWrite(fe, BB_BUF_OA, state->Mode ? 1 : 0); + status += MXL_ControlWrite(fe, BB_IQSWAP, state->Mode ? 0 : 1); + status += MXL_ControlWrite(fe, BB_INITSTATE_DLPF_TUNE, 0); + + /* Initialize Low-Pass Filter */ + if (state->Mode) { /* Digital Mode */ + switch (state->Chan_Bandwidth) { + case 8000000: + status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 0); + break; + case 7000000: + status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 2); + break; + case 6000000: + status += MXL_ControlWrite(fe, + BB_DLPF_BANDSEL, 3); + break; + } + } else { /* Analog Mode */ + switch (state->Chan_Bandwidth) { + case 8000000: /* Low Zero */ + status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT, + (state->IF_Mode ? 0 : 3)); + break; + case 7000000: + status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT, + (state->IF_Mode ? 1 : 4)); + break; + case 6000000: + status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT, + (state->IF_Mode ? 2 : 5)); + break; + } + } + + /* Charge Pump Control Dig Ana */ + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, state->Mode ? 5 : 8); + status += MXL_ControlWrite(fe, + RFSYN_EN_CHP_HIGAIN, state->Mode ? 1 : 1); + status += MXL_ControlWrite(fe, EN_CHP_LIN_B, state->Mode ? 0 : 0); + + /* AGC TOP Control */ + if (state->AGC_Mode == 0) /* Dual AGC */ { + status += MXL_ControlWrite(fe, AGC_IF, 15); + status += MXL_ControlWrite(fe, AGC_RF, 15); + } else /* Single AGC Mode Dig Ana */ + status += MXL_ControlWrite(fe, AGC_RF, state->Mode ? 15 : 12); + + if (state->TOP == 55) /* TOP == 5.5 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x0); + + if (state->TOP == 72) /* TOP == 7.2 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x1); + + if (state->TOP == 92) /* TOP == 9.2 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x2); + + if (state->TOP == 110) /* TOP == 11.0 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x3); + + if (state->TOP == 129) /* TOP == 12.9 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x4); + + if (state->TOP == 147) /* TOP == 14.7 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x5); + + if (state->TOP == 168) /* TOP == 16.8 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x6); + + if (state->TOP == 194) /* TOP == 19.4 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x7); + + if (state->TOP == 212) /* TOP == 21.2 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x9); + + if (state->TOP == 232) /* TOP == 23.2 */ + status += MXL_ControlWrite(fe, AGC_IF, 0xA); + + if (state->TOP == 252) /* TOP == 25.2 */ + status += MXL_ControlWrite(fe, AGC_IF, 0xB); + + if (state->TOP == 271) /* TOP == 27.1 */ + status += MXL_ControlWrite(fe, AGC_IF, 0xC); + + if (state->TOP == 292) /* TOP == 29.2 */ + status += MXL_ControlWrite(fe, AGC_IF, 0xD); + + if (state->TOP == 317) /* TOP == 31.7 */ + status += MXL_ControlWrite(fe, AGC_IF, 0xE); + + if (state->TOP == 349) /* TOP == 34.9 */ + status += MXL_ControlWrite(fe, AGC_IF, 0xF); + + /* IF Synthesizer Control */ + status += MXL_IFSynthInit(fe); + + /* IF UpConverter Control */ + if (state->IF_OUT_LOAD == 200) { + status += MXL_ControlWrite(fe, DRV_RES_SEL, 6); + status += MXL_ControlWrite(fe, I_DRIVER, 2); + } + if (state->IF_OUT_LOAD == 300) { + status += MXL_ControlWrite(fe, DRV_RES_SEL, 4); + status += MXL_ControlWrite(fe, I_DRIVER, 1); + } + + /* Anti-Alias Filtering Control + * initialise Anti-Aliasing Filter + */ + if (state->Mode) { /* Digital Mode */ + if (state->IF_OUT >= 4000000UL && state->IF_OUT <= 6280000UL) { + status += MXL_ControlWrite(fe, EN_AAF, 1); + status += MXL_ControlWrite(fe, EN_3P, 1); + status += MXL_ControlWrite(fe, EN_AUX_3P, 1); + status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0); + } + if ((state->IF_OUT == 36125000UL) || + (state->IF_OUT == 36150000UL)) { + status += MXL_ControlWrite(fe, EN_AAF, 1); + status += MXL_ControlWrite(fe, EN_3P, 1); + status += MXL_ControlWrite(fe, EN_AUX_3P, 1); + status += MXL_ControlWrite(fe, SEL_AAF_BAND, 1); + } + if (state->IF_OUT > 36150000UL) { + status += MXL_ControlWrite(fe, EN_AAF, 0); + status += MXL_ControlWrite(fe, EN_3P, 1); + status += MXL_ControlWrite(fe, EN_AUX_3P, 1); + status += MXL_ControlWrite(fe, SEL_AAF_BAND, 1); + } + } else { /* Analog Mode */ + if (state->IF_OUT >= 4000000UL && state->IF_OUT <= 5000000UL) { + status += MXL_ControlWrite(fe, EN_AAF, 1); + status += MXL_ControlWrite(fe, EN_3P, 1); + status += MXL_ControlWrite(fe, EN_AUX_3P, 1); + status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0); + } + if (state->IF_OUT > 5000000UL) { + status += MXL_ControlWrite(fe, EN_AAF, 0); + status += MXL_ControlWrite(fe, EN_3P, 0); + status += MXL_ControlWrite(fe, EN_AUX_3P, 0); + status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0); + } + } + + /* Demod Clock Out */ + if (state->CLOCK_OUT) + status += MXL_ControlWrite(fe, SEQ_ENCLK16_CLK_OUT, 1); + else + status += MXL_ControlWrite(fe, SEQ_ENCLK16_CLK_OUT, 0); + + if (state->DIV_OUT == 1) + status += MXL_ControlWrite(fe, SEQ_SEL4_16B, 1); + if (state->DIV_OUT == 0) + status += MXL_ControlWrite(fe, SEQ_SEL4_16B, 0); + + /* Crystal Control */ + if (state->CAPSELECT) + status += MXL_ControlWrite(fe, XTAL_CAPSELECT, 1); + else + status += MXL_ControlWrite(fe, XTAL_CAPSELECT, 0); + + if (state->Fxtal >= 12000000UL && state->Fxtal <= 16000000UL) + status += MXL_ControlWrite(fe, IF_SEL_DBL, 1); + if (state->Fxtal > 16000000UL && state->Fxtal <= 32000000UL) + status += MXL_ControlWrite(fe, IF_SEL_DBL, 0); + + if (state->Fxtal >= 12000000UL && state->Fxtal <= 22000000UL) + status += MXL_ControlWrite(fe, RFSYN_R_DIV, 3); + if (state->Fxtal > 22000000UL && state->Fxtal <= 32000000UL) + status += MXL_ControlWrite(fe, RFSYN_R_DIV, 0); + + /* Misc Controls */ + if (state->Mode == 0 && state->IF_Mode == 1) /* Analog LowIF mode */ + status += MXL_ControlWrite(fe, SEQ_EXTIQFSMPULSE, 0); + else + status += MXL_ControlWrite(fe, SEQ_EXTIQFSMPULSE, 1); + + /* status += MXL_ControlRead(fe, IF_DIVVAL, &IF_DIVVAL_Val); */ + + /* Set TG_R_DIV */ + status += MXL_ControlWrite(fe, TG_R_DIV, + MXL_Ceiling(state->Fxtal, 1000000)); + + /* Apply Default value to BB_INITSTATE_DLPF_TUNE */ + + /* RSSI Control */ + if (state->EN_RSSI) { + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + + /* RSSI reference point */ + status += MXL_ControlWrite(fe, RFA_RSSI_REF, 2); + status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 3); + status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1); + + /* TOP point */ + status += MXL_ControlWrite(fe, RFA_FLR, 0); + status += MXL_ControlWrite(fe, RFA_CEIL, 12); + } + + /* Modulation type bit settings + * Override the control values preset + */ + if (state->Mod_Type == MXL_DVBT) /* DVB-T Mode */ { + state->AGC_Mode = 1; /* Single AGC Mode */ + + /* Enable RSSI */ + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + + /* RSSI reference point */ + status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); + status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); + status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1); + + /* TOP point */ + status += MXL_ControlWrite(fe, RFA_FLR, 2); + status += MXL_ControlWrite(fe, RFA_CEIL, 13); + if (state->IF_OUT <= 6280000UL) /* Low IF */ + status += MXL_ControlWrite(fe, BB_IQSWAP, 0); + else /* High IF */ + status += MXL_ControlWrite(fe, BB_IQSWAP, 1); + + } + if (state->Mod_Type == MXL_ATSC) /* ATSC Mode */ { + state->AGC_Mode = 1; /* Single AGC Mode */ + + /* Enable RSSI */ + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + + /* RSSI reference point */ + status += MXL_ControlWrite(fe, RFA_RSSI_REF, 2); + status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 4); + status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1); + + /* TOP point */ + status += MXL_ControlWrite(fe, RFA_FLR, 2); + status += MXL_ControlWrite(fe, RFA_CEIL, 13); + status += MXL_ControlWrite(fe, BB_INITSTATE_DLPF_TUNE, 1); + /* Low Zero */ + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 5); + + if (state->IF_OUT <= 6280000UL) /* Low IF */ + status += MXL_ControlWrite(fe, BB_IQSWAP, 0); + else /* High IF */ + status += MXL_ControlWrite(fe, BB_IQSWAP, 1); + } + if (state->Mod_Type == MXL_QAM) /* QAM Mode */ { + state->Mode = MXL_DIGITAL_MODE; + + /* state->AGC_Mode = 1; */ /* Single AGC Mode */ + + /* Disable RSSI */ /* change here for v2.6.5 */ + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + + /* RSSI reference point */ + status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); + status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); + status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2); + /* change here for v2.6.5 */ + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); + + if (state->IF_OUT <= 6280000UL) /* Low IF */ + status += MXL_ControlWrite(fe, BB_IQSWAP, 0); + else /* High IF */ + status += MXL_ControlWrite(fe, BB_IQSWAP, 1); + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2); + + } + if (state->Mod_Type == MXL_ANALOG_CABLE) { + /* Analog Cable Mode */ + /* state->Mode = MXL_DIGITAL_MODE; */ + + state->AGC_Mode = 1; /* Single AGC Mode */ + + /* Disable RSSI */ + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + /* change for 2.6.3 */ + status += MXL_ControlWrite(fe, AGC_IF, 1); + status += MXL_ControlWrite(fe, AGC_RF, 15); + status += MXL_ControlWrite(fe, BB_IQSWAP, 1); + } + + if (state->Mod_Type == MXL_ANALOG_OTA) { + /* Analog OTA Terrestrial mode add for 2.6.7 */ + /* state->Mode = MXL_ANALOG_MODE; */ + + /* Enable RSSI */ + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + + /* RSSI reference point */ + status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); + status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); + status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2); + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); + status += MXL_ControlWrite(fe, BB_IQSWAP, 1); + } + + /* RSSI disable */ + if (state->EN_RSSI == 0) { + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + } + + return status; +} + +static u16 MXL_IFSynthInit(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u16 status = 0 ; + u32 Fref = 0 ; + u32 Kdbl, intModVal ; + u32 fracModVal ; + Kdbl = 2 ; + + if (state->Fxtal >= 12000000UL && state->Fxtal <= 16000000UL) + Kdbl = 2 ; + if (state->Fxtal > 16000000UL && state->Fxtal <= 32000000UL) + Kdbl = 1 ; + + /* IF Synthesizer Control */ + if (state->Mode == 0 && state->IF_Mode == 1) /* Analog Low IF mode */ { + if (state->IF_LO == 41000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 328000000UL ; + } + if (state->IF_LO == 47000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 376000000UL ; + } + if (state->IF_LO == 54000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 324000000UL ; + } + if (state->IF_LO == 60000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 360000000UL ; + } + if (state->IF_LO == 39250000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 314000000UL ; + } + if (state->IF_LO == 39650000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 317200000UL ; + } + if (state->IF_LO == 40150000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 321200000UL ; + } + if (state->IF_LO == 40650000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 325200000UL ; + } + } + + if (state->Mode || (state->Mode == 0 && state->IF_Mode == 0)) { + if (state->IF_LO == 57000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 342000000UL ; + } + if (state->IF_LO == 44000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 352000000UL ; + } + if (state->IF_LO == 43750000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 350000000UL ; + } + if (state->IF_LO == 36650000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 366500000UL ; + } + if (state->IF_LO == 36150000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 361500000UL ; + } + if (state->IF_LO == 36000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 360000000UL ; + } + if (state->IF_LO == 35250000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 352500000UL ; + } + if (state->IF_LO == 34750000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 347500000UL ; + } + if (state->IF_LO == 6280000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 376800000UL ; + } + if (state->IF_LO == 5000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 360000000UL ; + } + if (state->IF_LO == 4500000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 360000000UL ; + } + if (state->IF_LO == 4570000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 365600000UL ; + } + if (state->IF_LO == 4000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x05); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 360000000UL ; + } + if (state->IF_LO == 57400000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 344400000UL ; + } + if (state->IF_LO == 44400000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 355200000UL ; + } + if (state->IF_LO == 44150000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 353200000UL ; + } + if (state->IF_LO == 37050000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 370500000UL ; + } + if (state->IF_LO == 36550000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 365500000UL ; + } + if (state->IF_LO == 36125000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 361250000UL ; + } + if (state->IF_LO == 6000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 360000000UL ; + } + if (state->IF_LO == 5400000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 324000000UL ; + } + if (state->IF_LO == 5380000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 322800000UL ; + } + if (state->IF_LO == 5200000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 374400000UL ; + } + if (state->IF_LO == 4900000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 352800000UL ; + } + if (state->IF_LO == 4400000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 352000000UL ; + } + if (state->IF_LO == 4063000UL) /* add for 2.6.8 */ { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x05); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 365670000UL ; + } + } + /* CHCAL_INT_MOD_IF */ + /* CHCAL_FRAC_MOD_IF */ + intModVal = Fref / (state->Fxtal * Kdbl/2); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_IF, intModVal); + + fracModVal = (2<<15)*(Fref/1000 - (state->Fxtal/1000 * Kdbl/2) * + intModVal); + + fracModVal = fracModVal / ((state->Fxtal * Kdbl/2)/1000); + status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_IF, fracModVal); + + return status ; +} + +static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u16 status = 0; + u32 divider_val, E3, E4, E5, E5A; + u32 Fmax, Fmin, FmaxBin, FminBin; + u32 Kdbl_RF = 2; + u32 tg_divval; + u32 tg_lo; + + u32 Fref_TG; + u32 Fvco; + + state->RF_IN = RF_Freq; + + MXL_SynthRFTGLO_Calc(fe); + + if (state->Fxtal >= 12000000UL && state->Fxtal <= 22000000UL) + Kdbl_RF = 2; + if (state->Fxtal > 22000000 && state->Fxtal <= 32000000) + Kdbl_RF = 1; + + /* Downconverter Controls + * Look-Up Table Implementation for: + * DN_POLY + * DN_RFGAIN + * DN_CAP_RFLPF + * DN_EN_VHFUHFBAR + * DN_GAIN_ADJUST + * Change the boundary reference from RF_IN to RF_LO + */ + if (state->RF_LO < 40000000UL) + return -1; + + if (state->RF_LO >= 40000000UL && state->RF_LO <= 75000000UL) { + status += MXL_ControlWrite(fe, DN_POLY, 2); + status += MXL_ControlWrite(fe, DN_RFGAIN, 3); + status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 423); + status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); + status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 1); + } + if (state->RF_LO > 75000000UL && state->RF_LO <= 100000000UL) { + status += MXL_ControlWrite(fe, DN_POLY, 3); + status += MXL_ControlWrite(fe, DN_RFGAIN, 3); + status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 222); + status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); + status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 1); + } + if (state->RF_LO > 100000000UL && state->RF_LO <= 150000000UL) { + status += MXL_ControlWrite(fe, DN_POLY, 3); + status += MXL_ControlWrite(fe, DN_RFGAIN, 3); + status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 147); + status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); + status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 2); + } + if (state->RF_LO > 150000000UL && state->RF_LO <= 200000000UL) { + status += MXL_ControlWrite(fe, DN_POLY, 3); + status += MXL_ControlWrite(fe, DN_RFGAIN, 3); + status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 9); + status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); + status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 2); + } + if (state->RF_LO > 200000000UL && state->RF_LO <= 300000000UL) { + status += MXL_ControlWrite(fe, DN_POLY, 3); + status += MXL_ControlWrite(fe, DN_RFGAIN, 3); + status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0); + status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); + status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3); + } + if (state->RF_LO > 300000000UL && state->RF_LO <= 650000000UL) { + status += MXL_ControlWrite(fe, DN_POLY, 3); + status += MXL_ControlWrite(fe, DN_RFGAIN, 1); + status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0); + status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 0); + status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3); + } + if (state->RF_LO > 650000000UL && state->RF_LO <= 900000000UL) { + status += MXL_ControlWrite(fe, DN_POLY, 3); + status += MXL_ControlWrite(fe, DN_RFGAIN, 2); + status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0); + status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 0); + status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3); + } + if (state->RF_LO > 900000000UL) + return -1; + + /* DN_IQTNBUF_AMP */ + /* DN_IQTNGNBFBIAS_BST */ + if (state->RF_LO >= 40000000UL && state->RF_LO <= 75000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 75000000UL && state->RF_LO <= 100000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 100000000UL && state->RF_LO <= 150000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 150000000UL && state->RF_LO <= 200000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 200000000UL && state->RF_LO <= 300000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 300000000UL && state->RF_LO <= 400000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 400000000UL && state->RF_LO <= 450000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 450000000UL && state->RF_LO <= 500000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 500000000UL && state->RF_LO <= 550000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 550000000UL && state->RF_LO <= 600000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 600000000UL && state->RF_LO <= 650000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 650000000UL && state->RF_LO <= 700000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 700000000UL && state->RF_LO <= 750000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 750000000UL && state->RF_LO <= 800000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 800000000UL && state->RF_LO <= 850000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 10); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 1); + } + if (state->RF_LO > 850000000UL && state->RF_LO <= 900000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 10); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 1); + } + + /* + * Set RF Synth and LO Path Control + * + * Look-Up table implementation for: + * RFSYN_EN_OUTMUX + * RFSYN_SEL_VCO_OUT + * RFSYN_SEL_VCO_HI + * RFSYN_SEL_DIVM + * RFSYN_RF_DIV_BIAS + * DN_SEL_FREQ + * + * Set divider_val, Fmax, Fmix to use in Equations + */ + FminBin = 28000000UL ; + FmaxBin = 42500000UL ; + if (state->RF_LO >= 40000000UL && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); + divider_val = 64 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 42500000UL ; + FmaxBin = 56000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); + divider_val = 64 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 56000000UL ; + FmaxBin = 85000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); + divider_val = 32 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 85000000UL ; + FmaxBin = 112000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); + divider_val = 32 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 112000000UL ; + FmaxBin = 170000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 2); + divider_val = 16 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 170000000UL ; + FmaxBin = 225000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 2); + divider_val = 16 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 225000000UL ; + FmaxBin = 300000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 4); + divider_val = 8 ; + Fmax = 340000000UL ; + Fmin = FminBin ; + } + FminBin = 300000000UL ; + FmaxBin = 340000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + divider_val = 8 ; + Fmax = FmaxBin ; + Fmin = 225000000UL ; + } + FminBin = 340000000UL ; + FmaxBin = 450000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 2); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + divider_val = 8 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 450000000UL ; + FmaxBin = 680000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 1); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + divider_val = 4 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 680000000UL ; + FmaxBin = 900000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 1); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + divider_val = 4 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + + /* CHCAL_INT_MOD_RF + * CHCAL_FRAC_MOD_RF + * RFSYN_LPF_R + * CHCAL_EN_INT_RF + */ + /* Equation E3 RFSYN_VCO_BIAS */ + E3 = (((Fmax-state->RF_LO)/1000)*32)/((Fmax-Fmin)/1000) + 8 ; + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, E3); + + /* Equation E4 CHCAL_INT_MOD_RF */ + E4 = (state->RF_LO*divider_val/1000)/(2*state->Fxtal*Kdbl_RF/1000); + MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, E4); + + /* Equation E5 CHCAL_FRAC_MOD_RF CHCAL_EN_INT_RF */ + E5 = ((2<<17)*(state->RF_LO/10000*divider_val - + (E4*(2*state->Fxtal*Kdbl_RF)/10000))) / + (2*state->Fxtal*Kdbl_RF/10000); + + status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_RF, E5); + + /* Equation E5A RFSYN_LPF_R */ + E5A = (((Fmax - state->RF_LO)/1000)*4/((Fmax-Fmin)/1000)) + 1 ; + status += MXL_ControlWrite(fe, RFSYN_LPF_R, E5A); + + /* Euqation E5B CHCAL_EN_INIT_RF */ + status += MXL_ControlWrite(fe, CHCAL_EN_INT_RF, ((E5 == 0) ? 1 : 0)); + /*if (E5 == 0) + * status += MXL_ControlWrite(fe, CHCAL_EN_INT_RF, 1); + *else + * status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_RF, E5); + */ + + /* + * Set TG Synth + * + * Look-Up table implementation for: + * TG_LO_DIVVAL + * TG_LO_SELVAL + * + * Set divider_val, Fmax, Fmix to use in Equations + */ + if (state->TG_LO < 33000000UL) + return -1; + + FminBin = 33000000UL ; + FmaxBin = 50000000UL ; + if (state->TG_LO >= FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x6); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x0); + divider_val = 36 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 50000000UL ; + FmaxBin = 67000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x1); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x0); + divider_val = 24 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 67000000UL ; + FmaxBin = 100000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0xC); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2); + divider_val = 18 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 100000000UL ; + FmaxBin = 150000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2); + divider_val = 12 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 150000000UL ; + FmaxBin = 200000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2); + divider_val = 8 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 200000000UL ; + FmaxBin = 300000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x3); + divider_val = 6 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 300000000UL ; + FmaxBin = 400000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x3); + divider_val = 4 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 400000000UL ; + FmaxBin = 600000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x7); + divider_val = 3 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 600000000UL ; + FmaxBin = 900000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x7); + divider_val = 2 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + + /* TG_DIV_VAL */ + tg_divval = (state->TG_LO*divider_val/100000) * + (MXL_Ceiling(state->Fxtal, 1000000) * 100) / + (state->Fxtal/1000); + + status += MXL_ControlWrite(fe, TG_DIV_VAL, tg_divval); + + if (state->TG_LO > 600000000UL) + status += MXL_ControlWrite(fe, TG_DIV_VAL, tg_divval + 1); + + Fmax = 1800000000UL ; + Fmin = 1200000000UL ; + + /* prevent overflow of 32 bit unsigned integer, use + * following equation. Edit for v2.6.4 + */ + /* Fref_TF = Fref_TG * 1000 */ + Fref_TG = (state->Fxtal/1000) / MXL_Ceiling(state->Fxtal, 1000000); + + /* Fvco = Fvco/10 */ + Fvco = (state->TG_LO/10000) * divider_val * Fref_TG; + + tg_lo = (((Fmax/10 - Fvco)/100)*32) / ((Fmax-Fmin)/1000)+8; + + /* below equation is same as above but much harder to debug. + * + * static u32 MXL_GetXtalInt(u32 Xtal_Freq) + * { + * if ((Xtal_Freq % 1000000) == 0) + * return (Xtal_Freq / 10000); + * else + * return (((Xtal_Freq / 1000000) + 1)*100); + * } + * + * u32 Xtal_Int = MXL_GetXtalInt(state->Fxtal); + * tg_lo = ( ((Fmax/10000 * Xtal_Int)/100) - + * ((state->TG_LO/10000)*divider_val * + * (state->Fxtal/10000)/100) )*32/((Fmax-Fmin)/10000 * + * Xtal_Int/100) + 8; + */ + + status += MXL_ControlWrite(fe, TG_VCO_BIAS , tg_lo); + + /* add for 2.6.5 Special setting for QAM */ + if (state->Mod_Type == MXL_QAM) { + if (state->config->qam_gain != 0) + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, + state->config->qam_gain); + else if (state->RF_IN < 680000000) + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); + else + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2); + } + + /* Off Chip Tracking Filter Control */ + if (state->TF_Type == MXL_TF_OFF) { + /* Tracking Filter Off State; turn off all the banks */ + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 3, 1); /* Bank1 Off */ + status += MXL_SetGPIO(fe, 1, 1); /* Bank2 Off */ + status += MXL_SetGPIO(fe, 4, 1); /* Bank3 Off */ + } + + if (state->TF_Type == MXL_TF_C) /* Tracking Filter type C */ { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_ControlWrite(fe, DAC_DIN_A, 0); + + if (state->RF_IN >= 43000000 && state->RF_IN < 150000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + status += MXL_SetGPIO(fe, 3, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 1); + } + if (state->RF_IN >= 150000000 && state->RF_IN < 280000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 4, 1); + } + if (state->RF_IN >= 280000000 && state->RF_IN < 360000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 4, 0); + } + if (state->RF_IN >= 360000000 && state->RF_IN < 560000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 0); + } + if (state->RF_IN >= 560000000 && state->RF_IN < 580000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_ControlWrite(fe, DAC_DIN_B, 29); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 0); + } + if (state->RF_IN >= 580000000 && state->RF_IN < 630000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 0); + } + if (state->RF_IN >= 630000000 && state->RF_IN < 700000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_ControlWrite(fe, DAC_DIN_B, 16); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 1); + } + if (state->RF_IN >= 700000000 && state->RF_IN < 760000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_ControlWrite(fe, DAC_DIN_B, 7); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 1); + } + if (state->RF_IN >= 760000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 1); + } + } + + if (state->TF_Type == MXL_TF_C_H) { + + /* Tracking Filter type C-H for Hauppauge only */ + status += MXL_ControlWrite(fe, DAC_DIN_A, 0); + + if (state->RF_IN >= 43000000 && state->RF_IN < 150000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + } + if (state->RF_IN >= 150000000 && state->RF_IN < 280000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 0); + status += MXL_SetGPIO(fe, 1, 1); + } + if (state->RF_IN >= 280000000 && state->RF_IN < 360000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 0); + status += MXL_SetGPIO(fe, 1, 0); + } + if (state->RF_IN >= 360000000 && state->RF_IN < 560000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 0); + } + if (state->RF_IN >= 560000000 && state->RF_IN < 580000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 0); + } + if (state->RF_IN >= 580000000 && state->RF_IN < 630000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 0); + } + if (state->RF_IN >= 630000000 && state->RF_IN < 700000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + } + if (state->RF_IN >= 700000000 && state->RF_IN < 760000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + } + if (state->RF_IN >= 760000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + } + } + + if (state->TF_Type == MXL_TF_D) { /* Tracking Filter type D */ + + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + + if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 250000000 && state->RF_IN < 310000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 310000000 && state->RF_IN < 360000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 360000000 && state->RF_IN < 470000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 640000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + } + + if (state->TF_Type == MXL_TF_D_L) { + + /* Tracking Filter type D-L for Lumanate ONLY change 2.6.3 */ + status += MXL_ControlWrite(fe, DAC_DIN_A, 0); + + /* if UHF and terrestrial => Turn off Tracking Filter */ + if (state->RF_IN >= 471000000 && + (state->RF_IN - 471000000)%6000000 != 0) { + /* Turn off all the banks */ + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_ControlWrite(fe, AGC_IF, 10); + } else { + /* if VHF or cable => Turn on Tracking Filter */ + if (state->RF_IN >= 43000000 && + state->RF_IN < 140000000) { + + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 140000000 && + state->RF_IN < 240000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 240000000 && + state->RF_IN < 340000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 340000000 && + state->RF_IN < 430000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 430000000 && + state->RF_IN < 470000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 470000000 && + state->RF_IN < 570000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 570000000 && + state->RF_IN < 620000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 620000000 && + state->RF_IN < 760000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 760000000 && + state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + } + } + + if (state->TF_Type == MXL_TF_E) /* Tracking Filter type E */ { + + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + + if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 250000000 && state->RF_IN < 310000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 310000000 && state->RF_IN < 360000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 360000000 && state->RF_IN < 470000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 640000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + } + + if (state->TF_Type == MXL_TF_F) { + + /* Tracking Filter type F */ + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + + if (state->RF_IN >= 43000000 && state->RF_IN < 160000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 160000000 && state->RF_IN < 210000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 210000000 && state->RF_IN < 300000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 300000000 && state->RF_IN < 390000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 390000000 && state->RF_IN < 515000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 515000000 && state->RF_IN < 650000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 650000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + } + + if (state->TF_Type == MXL_TF_E_2) { + + /* Tracking Filter type E_2 */ + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + + if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 250000000 && state->RF_IN < 350000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 400000000 && state->RF_IN < 570000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 570000000 && state->RF_IN < 770000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 770000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + } + + if (state->TF_Type == MXL_TF_G) { + + /* Tracking Filter type G add for v2.6.8 */ + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + + if (state->RF_IN >= 50000000 && state->RF_IN < 190000000) { + + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 190000000 && state->RF_IN < 280000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 280000000 && state->RF_IN < 350000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 400000000 && state->RF_IN < 470000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 640000000 && state->RF_IN < 820000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 820000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + } + + if (state->TF_Type == MXL_TF_E_NA) { + + /* Tracking Filter type E-NA for Empia ONLY change for 2.6.8 */ + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + + /* if UHF and terrestrial=> Turn off Tracking Filter */ + if (state->RF_IN >= 471000000 && + (state->RF_IN - 471000000)%6000000 != 0) { + + /* Turn off all the banks */ + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + + /* 2.6.12 Turn on RSSI */ + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + + /* RSSI reference point */ + status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); + status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); + status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2); + + /* following parameter is from analog OTA mode, + * can be change to seek better performance */ + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); + } else { + /* if VHF or Cable => Turn on Tracking Filter */ + + /* 2.6.12 Turn off RSSI */ + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); + + /* change back from above condition */ + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 5); + + + if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { + + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 250000000 && state->RF_IN < 350000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 400000000 && state->RF_IN < 570000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 570000000 && state->RF_IN < 770000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 770000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + } + } + return status ; +} + +static u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val) +{ + u16 status = 0; + + if (GPIO_Num == 1) + status += MXL_ControlWrite(fe, GPIO_1B, GPIO_Val ? 0 : 1); + + /* GPIO2 is not available */ + + if (GPIO_Num == 3) { + if (GPIO_Val == 1) { + status += MXL_ControlWrite(fe, GPIO_3, 0); + status += MXL_ControlWrite(fe, GPIO_3B, 0); + } + if (GPIO_Val == 0) { + status += MXL_ControlWrite(fe, GPIO_3, 1); + status += MXL_ControlWrite(fe, GPIO_3B, 1); + } + if (GPIO_Val == 3) { /* tri-state */ + status += MXL_ControlWrite(fe, GPIO_3, 0); + status += MXL_ControlWrite(fe, GPIO_3B, 1); + } + } + if (GPIO_Num == 4) { + if (GPIO_Val == 1) { + status += MXL_ControlWrite(fe, GPIO_4, 0); + status += MXL_ControlWrite(fe, GPIO_4B, 0); + } + if (GPIO_Val == 0) { + status += MXL_ControlWrite(fe, GPIO_4, 1); + status += MXL_ControlWrite(fe, GPIO_4B, 1); + } + if (GPIO_Val == 3) { /* tri-state */ + status += MXL_ControlWrite(fe, GPIO_4, 0); + status += MXL_ControlWrite(fe, GPIO_4B, 1); + } + } + + return status; +} + +static u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value) +{ + u16 status = 0; + + /* Will write ALL Matching Control Name */ + /* Write Matching INIT Control */ + status += MXL_ControlWrite_Group(fe, ControlNum, value, 1); + /* Write Matching CH Control */ + status += MXL_ControlWrite_Group(fe, ControlNum, value, 2); +#ifdef _MXL_INTERNAL + /* Write Matching MXL Control */ + status += MXL_ControlWrite_Group(fe, ControlNum, value, 3); +#endif + return status; +} + +static u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum, + u32 value, u16 controlGroup) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u16 i, j, k; + u32 highLimit; + u32 ctrlVal; + + if (controlGroup == 1) /* Initial Control */ { + + for (i = 0; i < state->Init_Ctrl_Num; i++) { + + if (controlNum == state->Init_Ctrl[i].Ctrl_Num) { + + highLimit = 1 << state->Init_Ctrl[i].size; + if (value < highLimit) { + for (j = 0; j < state->Init_Ctrl[i].size; j++) { + state->Init_Ctrl[i].val[j] = (u8)((value >> j) & 0x01); + MXL_RegWriteBit(fe, (u8)(state->Init_Ctrl[i].addr[j]), + (u8)(state->Init_Ctrl[i].bit[j]), + (u8)((value>>j) & 0x01)); + } + ctrlVal = 0; + for (k = 0; k < state->Init_Ctrl[i].size; k++) + ctrlVal += state->Init_Ctrl[i].val[k] * (1 << k); + } else + return -1; + } + } + } + if (controlGroup == 2) /* Chan change Control */ { + + for (i = 0; i < state->CH_Ctrl_Num; i++) { + + if (controlNum == state->CH_Ctrl[i].Ctrl_Num) { + + highLimit = 1 << state->CH_Ctrl[i].size; + if (value < highLimit) { + for (j = 0; j < state->CH_Ctrl[i].size; j++) { + state->CH_Ctrl[i].val[j] = (u8)((value >> j) & 0x01); + MXL_RegWriteBit(fe, (u8)(state->CH_Ctrl[i].addr[j]), + (u8)(state->CH_Ctrl[i].bit[j]), + (u8)((value>>j) & 0x01)); + } + ctrlVal = 0; + for (k = 0; k < state->CH_Ctrl[i].size; k++) + ctrlVal += state->CH_Ctrl[i].val[k] * (1 << k); + } else + return -1; + } + } + } +#ifdef _MXL_INTERNAL + if (controlGroup == 3) /* Maxlinear Control */ { + + for (i = 0; i < state->MXL_Ctrl_Num; i++) { + + if (controlNum == state->MXL_Ctrl[i].Ctrl_Num) { + + highLimit = (1 << state->MXL_Ctrl[i].size); + if (value < highLimit) { + for (j = 0; j < state->MXL_Ctrl[i].size; j++) { + state->MXL_Ctrl[i].val[j] = (u8)((value >> j) & 0x01); + MXL_RegWriteBit(fe, (u8)(state->MXL_Ctrl[i].addr[j]), + (u8)(state->MXL_Ctrl[i].bit[j]), + (u8)((value>>j) & 0x01)); + } + ctrlVal = 0; + for (k = 0; k < state->MXL_Ctrl[i].size; k++) + ctrlVal += state-> + MXL_Ctrl[i].val[k] * + (1 << k); + } else + return -1; + } + } + } +#endif + return 0 ; /* successful return */ +} + +static u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal) +{ + struct mxl5005s_state *state = fe->tuner_priv; + int i ; + + for (i = 0; i < 104; i++) { + if (RegNum == state->TunerRegs[i].Reg_Num) { + *RegVal = (u8)(state->TunerRegs[i].Reg_Val); + return 0; + } + } + + return 1; +} + +static u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u32 ctrlVal ; + u16 i, k ; + + for (i = 0; i < state->Init_Ctrl_Num ; i++) { + + if (controlNum == state->Init_Ctrl[i].Ctrl_Num) { + + ctrlVal = 0; + for (k = 0; k < state->Init_Ctrl[i].size; k++) + ctrlVal += state->Init_Ctrl[i].val[k] * (1<CH_Ctrl_Num ; i++) { + + if (controlNum == state->CH_Ctrl[i].Ctrl_Num) { + + ctrlVal = 0; + for (k = 0; k < state->CH_Ctrl[i].size; k++) + ctrlVal += state->CH_Ctrl[i].val[k] * (1 << k); + *value = ctrlVal; + return 0; + + } + } + +#ifdef _MXL_INTERNAL + for (i = 0; i < state->MXL_Ctrl_Num ; i++) { + + if (controlNum == state->MXL_Ctrl[i].Ctrl_Num) { + + ctrlVal = 0; + for (k = 0; k < state->MXL_Ctrl[i].size; k++) + ctrlVal += state->MXL_Ctrl[i].val[k] * (1<tuner_priv; + int i ; + + const u8 AND_MAP[8] = { + 0xFE, 0xFD, 0xFB, 0xF7, + 0xEF, 0xDF, 0xBF, 0x7F } ; + + const u8 OR_MAP[8] = { + 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80 } ; + + for (i = 0; i < state->TunerRegs_Num; i++) { + if (state->TunerRegs[i].Reg_Num == address) { + if (bitVal) + state->TunerRegs[i].Reg_Val |= OR_MAP[bit]; + else + state->TunerRegs[i].Reg_Val &= AND_MAP[bit]; + break ; + } + } +} + +static u32 MXL_Ceiling(u32 value, u32 resolution) +{ + return value / resolution + (value % resolution > 0 ? 1 : 0); +} + +/* Retrieve the Initialzation Registers */ +static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum, + u8 *RegVal, int *count) +{ + u16 status = 0; + int i ; + + u8 RegAddr[] = { + 11, 12, 13, 22, 32, 43, 44, 53, 56, 59, 73, + 76, 77, 91, 134, 135, 137, 147, + 156, 166, 167, 168, 25 }; + + *count = ARRAY_SIZE(RegAddr); + + status += MXL_BlockInit(fe); + + for (i = 0 ; i < *count; i++) { + RegNum[i] = RegAddr[i]; + status += MXL_RegRead(fe, RegNum[i], &RegVal[i]); + } + + return status; +} + +static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum, u8 *RegVal, + int *count) +{ + u16 status = 0; + int i ; + +/* add 77, 166, 167, 168 register for 2.6.12 */ +#ifdef _MXL_PRODUCTION + u8 RegAddr[] = {14, 15, 16, 17, 22, 43, 65, 68, 69, 70, 73, 92, 93, 106, + 107, 108, 109, 110, 111, 112, 136, 138, 149, 77, 166, 167, 168 } ; +#else + u8 RegAddr[] = {14, 15, 16, 17, 22, 43, 68, 69, 70, 73, 92, 93, 106, + 107, 108, 109, 110, 111, 112, 136, 138, 149, 77, 166, 167, 168 } ; + /* + u8 RegAddr[171]; + for (i = 0; i <= 170; i++) + RegAddr[i] = i; + */ +#endif + + *count = ARRAY_SIZE(RegAddr); + + for (i = 0 ; i < *count; i++) { + RegNum[i] = RegAddr[i]; + status += MXL_RegRead(fe, RegNum[i], &RegVal[i]); + } + + return status; +} + +static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum, + u8 *RegVal, int *count) +{ + u16 status = 0; + int i; + + u8 RegAddr[] = {43, 136}; + + *count = ARRAY_SIZE(RegAddr); + + for (i = 0; i < *count; i++) { + RegNum[i] = RegAddr[i]; + status += MXL_RegRead(fe, RegNum[i], &RegVal[i]); + } + + return status; +} + +static u16 MXL_GetMasterControl(u8 *MasterReg, int state) +{ + if (state == 1) /* Load_Start */ + *MasterReg = 0xF3; + if (state == 2) /* Power_Down */ + *MasterReg = 0x41; + if (state == 3) /* Synth_Reset */ + *MasterReg = 0xB1; + if (state == 4) /* Seq_Off */ + *MasterReg = 0xF1; + + return 0; +} + +#ifdef _MXL_PRODUCTION +static u16 MXL_VCORange_Test(struct dvb_frontend *fe, int VCO_Range) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u16 status = 0 ; + + if (VCO_Range == 1) { + status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + if (state->Mode == 0 && state->IF_Mode == 1) { + /* Analog Low IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 180224); + } + if (state->Mode == 0 && state->IF_Mode == 0) { + /* Analog Zero IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 222822); + } + if (state->Mode == 1) /* Digital Mode */ { + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 229376); + } + } + + if (VCO_Range == 2) { + status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 41); + if (state->Mode == 0 && state->IF_Mode == 1) { + /* Analog Low IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 206438); + } + if (state->Mode == 0 && state->IF_Mode == 0) { + /* Analog Zero IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 206438); + } + if (state->Mode == 1) /* Digital Mode */ { + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 41); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 16384); + } + } + + if (VCO_Range == 3) { + status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); + if (state->Mode == 0 && state->IF_Mode == 1) { + /* Analog Low IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 44); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 173670); + } + if (state->Mode == 0 && state->IF_Mode == 0) { + /* Analog Zero IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 44); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 173670); + } + if (state->Mode == 1) /* Digital Mode */ { + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 245760); + } + } + + if (VCO_Range == 4) { + status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); + if (state->Mode == 0 && state->IF_Mode == 1) { + /* Analog Low IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 206438); + } + if (state->Mode == 0 && state->IF_Mode == 0) { + /* Analog Zero IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 206438); + } + if (state->Mode == 1) /* Digital Mode */ { + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 212992); + } + } + + return status; +} + +static u16 MXL_Hystersis_Test(struct dvb_frontend *fe, int Hystersis) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u16 status = 0; + + if (Hystersis == 1) + status += MXL_ControlWrite(fe, DN_BYPASS_AGC_I2C, 1); + + return status; +} +#endif +/* End: Reference driver code found in the Realtek driver that + * is copyright MaxLinear */ + +/* ---------------------------------------------------------------- + * Begin: Everything after here is new code to adapt the + * proprietary Realtek driver into a Linux API tuner. + * Copyright (C) 2008 Steven Toth + */ +static int mxl5005s_reset(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + int ret = 0; + + u8 buf[2] = { 0xff, 0x00 }; + struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0, + .buf = buf, .len = 2 }; + + dprintk(2, "%s()\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "mxl5005s I2C reset failed\n"); + ret = -EREMOTEIO; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return ret; +} + +/* Write a single byte to a single reg, latch the value if required by + * following the transaction with the latch byte. + */ +static int mxl5005s_writereg(struct dvb_frontend *fe, u8 reg, u8 val, int latch) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u8 buf[3] = { reg, val, MXL5005S_LATCH_BYTE }; + struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0, + .buf = buf, .len = 3 }; + + if (latch == 0) + msg.len = 2; + + dprintk(2, "%s(0x%x, 0x%x, 0x%x)\n", __func__, reg, val, msg.addr); + + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "mxl5005s I2C write failed\n"); + return -EREMOTEIO; + } + return 0; +} + +static int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable, + u8 *datatable, u8 len) +{ + int ret = 0, i; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + for (i = 0 ; i < len-1; i++) { + ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 0); + if (ret < 0) + break; + } + + ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 1); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return ret; +} + +static int mxl5005s_init(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + + dprintk(1, "%s()\n", __func__); + state->current_mode = MXL_QAM; + return mxl5005s_reconfigure(fe, MXL_QAM, MXL5005S_BANDWIDTH_6MHZ); +} + +static int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type, + u32 bandwidth) +{ + struct mxl5005s_state *state = fe->tuner_priv; + + u8 AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; + u8 ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; + int TableLen; + + dprintk(1, "%s(type=%d, bw=%d)\n", __func__, mod_type, bandwidth); + + mxl5005s_reset(fe); + + /* Tuner initialization stage 0 */ + MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET); + AddrTable[0] = MASTER_CONTROL_ADDR; + ByteTable[0] |= state->config->AgcMasterByte; + + mxl5005s_writeregs(fe, AddrTable, ByteTable, 1); + + mxl5005s_AssignTunerMode(fe, mod_type, bandwidth); + + /* Tuner initialization stage 1 */ + MXL_GetInitRegister(fe, AddrTable, ByteTable, &TableLen); + + mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen); + + return 0; +} + +static int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type, + u32 bandwidth) +{ + struct mxl5005s_state *state = fe->tuner_priv; + struct mxl5005s_config *c = state->config; + + InitTunerControls(fe); + + /* Set MxL5005S parameters. */ + MXL5005_TunerConfig( + fe, + c->mod_mode, + c->if_mode, + bandwidth, + c->if_freq, + c->xtal_freq, + c->agc_mode, + c->top, + c->output_load, + c->clock_out, + c->div_out, + c->cap_select, + c->rssi_enable, + mod_type, + c->tracking_filter); + + return 0; +} + +static int mxl5005s_set_params(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 delsys = c->delivery_system; + u32 bw = c->bandwidth_hz; + u32 req_mode, req_bw = 0; + int ret; + + dprintk(1, "%s()\n", __func__); + + switch (delsys) { + case SYS_ATSC: + req_mode = MXL_ATSC; + req_bw = MXL5005S_BANDWIDTH_6MHZ; + break; + case SYS_DVBC_ANNEX_B: + req_mode = MXL_QAM; + req_bw = MXL5005S_BANDWIDTH_6MHZ; + break; + default: /* Assume DVB-T */ + req_mode = MXL_DVBT; + switch (bw) { + case 6000000: + req_bw = MXL5005S_BANDWIDTH_6MHZ; + break; + case 7000000: + req_bw = MXL5005S_BANDWIDTH_7MHZ; + break; + case 8000000: + case 0: + req_bw = MXL5005S_BANDWIDTH_8MHZ; + break; + default: + return -EINVAL; + } + } + + /* Change tuner for new modulation type if reqd */ + if (req_mode != state->current_mode || + req_bw != state->Chan_Bandwidth) { + state->current_mode = req_mode; + ret = mxl5005s_reconfigure(fe, req_mode, req_bw); + + } else + ret = 0; + + if (ret == 0) { + dprintk(1, "%s() freq=%d\n", __func__, c->frequency); + ret = mxl5005s_SetRfFreqHz(fe, c->frequency); + } + + return ret; +} + +static int mxl5005s_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct mxl5005s_state *state = fe->tuner_priv; + dprintk(1, "%s()\n", __func__); + + *frequency = state->RF_IN; + + return 0; +} + +static int mxl5005s_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct mxl5005s_state *state = fe->tuner_priv; + dprintk(1, "%s()\n", __func__); + + *bandwidth = state->Chan_Bandwidth; + + return 0; +} + +static int mxl5005s_release(struct dvb_frontend *fe) +{ + dprintk(1, "%s()\n", __func__); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static const struct dvb_tuner_ops mxl5005s_tuner_ops = { + .info = { + .name = "MaxLinear MXL5005S", + .frequency_min = 48000000, + .frequency_max = 860000000, + .frequency_step = 50000, + }, + + .release = mxl5005s_release, + .init = mxl5005s_init, + + .set_params = mxl5005s_set_params, + .get_frequency = mxl5005s_get_frequency, + .get_bandwidth = mxl5005s_get_bandwidth, +}; + +struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct mxl5005s_config *config) +{ + struct mxl5005s_state *state = NULL; + dprintk(1, "%s()\n", __func__); + + state = kzalloc(sizeof(struct mxl5005s_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + state->frontend = fe; + state->config = config; + state->i2c = i2c; + + printk(KERN_INFO "MXL5005S: Attached at address 0x%02x\n", + config->i2c_address); + + memcpy(&fe->ops.tuner_ops, &mxl5005s_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = state; + return fe; +} +EXPORT_SYMBOL(mxl5005s_attach); + +MODULE_DESCRIPTION("MaxLinear MXL5005S silicon tuner driver"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/mxl5005s.h b/drivers/media/tuners/mxl5005s.h new file mode 100644 index 000000000000..fc8a1ffc53b4 --- /dev/null +++ b/drivers/media/tuners/mxl5005s.h @@ -0,0 +1,135 @@ +/* + MaxLinear MXL5005S VSB/QAM/DVBT tuner driver + + Copyright (C) 2008 MaxLinear + Copyright (C) 2008 Steven Toth + + 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 __MXL5005S_H +#define __MXL5005S_H + +#include +#include "dvb_frontend.h" + +struct mxl5005s_config { + + /* 7 bit i2c address */ + u8 i2c_address; + +#define IF_FREQ_4570000HZ 4570000 +#define IF_FREQ_4571429HZ 4571429 +#define IF_FREQ_5380000HZ 5380000 +#define IF_FREQ_36000000HZ 36000000 +#define IF_FREQ_36125000HZ 36125000 +#define IF_FREQ_36166667HZ 36166667 +#define IF_FREQ_44000000HZ 44000000 + u32 if_freq; + +#define CRYSTAL_FREQ_4000000HZ 4000000 +#define CRYSTAL_FREQ_16000000HZ 16000000 +#define CRYSTAL_FREQ_25000000HZ 25000000 +#define CRYSTAL_FREQ_28800000HZ 28800000 + u32 xtal_freq; + +#define MXL_DUAL_AGC 0 +#define MXL_SINGLE_AGC 1 + u8 agc_mode; + +#define MXL_TF_DEFAULT 0 +#define MXL_TF_OFF 1 +#define MXL_TF_C 2 +#define MXL_TF_C_H 3 +#define MXL_TF_D 4 +#define MXL_TF_D_L 5 +#define MXL_TF_E 6 +#define MXL_TF_F 7 +#define MXL_TF_E_2 8 +#define MXL_TF_E_NA 9 +#define MXL_TF_G 10 + u8 tracking_filter; + +#define MXL_RSSI_DISABLE 0 +#define MXL_RSSI_ENABLE 1 + u8 rssi_enable; + +#define MXL_CAP_SEL_DISABLE 0 +#define MXL_CAP_SEL_ENABLE 1 + u8 cap_select; + +#define MXL_DIV_OUT_1 0 +#define MXL_DIV_OUT_4 1 + u8 div_out; + +#define MXL_CLOCK_OUT_DISABLE 0 +#define MXL_CLOCK_OUT_ENABLE 1 + u8 clock_out; + +#define MXL5005S_IF_OUTPUT_LOAD_200_OHM 200 +#define MXL5005S_IF_OUTPUT_LOAD_300_OHM 300 + u32 output_load; + +#define MXL5005S_TOP_5P5 55 +#define MXL5005S_TOP_7P2 72 +#define MXL5005S_TOP_9P2 92 +#define MXL5005S_TOP_11P0 110 +#define MXL5005S_TOP_12P9 129 +#define MXL5005S_TOP_14P7 147 +#define MXL5005S_TOP_16P8 168 +#define MXL5005S_TOP_19P4 194 +#define MXL5005S_TOP_21P2 212 +#define MXL5005S_TOP_23P2 232 +#define MXL5005S_TOP_25P2 252 +#define MXL5005S_TOP_27P1 271 +#define MXL5005S_TOP_29P2 292 +#define MXL5005S_TOP_31P7 317 +#define MXL5005S_TOP_34P9 349 + u32 top; + +#define MXL_ANALOG_MODE 0 +#define MXL_DIGITAL_MODE 1 + u8 mod_mode; + +#define MXL_ZERO_IF 0 +#define MXL_LOW_IF 1 + u8 if_mode; + + /* Some boards need to override the built-in logic for determining + the gain when in QAM mode (the HVR-1600 is one such case) */ + u8 qam_gain; + + /* Stuff I don't know what to do with */ + u8 AgcMasterByte; +}; + +#if defined(CONFIG_MEDIA_TUNER_MXL5005S) || \ + (defined(CONFIG_MEDIA_TUNER_MXL5005S_MODULE) && defined(MODULE)) +extern struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct mxl5005s_config *config); +#else +static inline struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct mxl5005s_config *config) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_TUNER_MXL5005S */ + +#endif /* __MXL5005S_H */ + diff --git a/drivers/media/tuners/mxl5007t.c b/drivers/media/tuners/mxl5007t.c new file mode 100644 index 000000000000..69e453ef0a1a --- /dev/null +++ b/drivers/media/tuners/mxl5007t.c @@ -0,0 +1,928 @@ +/* + * mxl5007t.c - driver for the MaxLinear MxL5007T silicon tuner + * + * Copyright (C) 2008, 2009 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. + */ + +#include +#include +#include +#include "tuner-i2c.h" +#include "mxl5007t.h" + +static DEFINE_MUTEX(mxl5007t_list_mutex); +static LIST_HEAD(hybrid_tuner_instance_list); + +static int mxl5007t_debug; +module_param_named(debug, mxl5007t_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debug level"); + +/* ------------------------------------------------------------------------- */ + +#define mxl_printk(kern, fmt, arg...) \ + printk(kern "%s: " fmt "\n", __func__, ##arg) + +#define mxl_err(fmt, arg...) \ + mxl_printk(KERN_ERR, "%d: " fmt, __LINE__, ##arg) + +#define mxl_warn(fmt, arg...) \ + mxl_printk(KERN_WARNING, fmt, ##arg) + +#define mxl_info(fmt, arg...) \ + mxl_printk(KERN_INFO, fmt, ##arg) + +#define mxl_debug(fmt, arg...) \ +({ \ + if (mxl5007t_debug) \ + mxl_printk(KERN_DEBUG, fmt, ##arg); \ +}) + +#define mxl_fail(ret) \ +({ \ + int __ret; \ + __ret = (ret < 0); \ + if (__ret) \ + mxl_printk(KERN_ERR, "error %d on line %d", \ + ret, __LINE__); \ + __ret; \ +}) + +/* ------------------------------------------------------------------------- */ + +#define MHz 1000000 + +enum mxl5007t_mode { + MxL_MODE_ISDBT = 0, + MxL_MODE_DVBT = 1, + MxL_MODE_ATSC = 2, + MxL_MODE_CABLE = 0x10, +}; + +enum mxl5007t_chip_version { + MxL_UNKNOWN_ID = 0x00, + MxL_5007_V1_F1 = 0x11, + MxL_5007_V1_F2 = 0x12, + MxL_5007_V4 = 0x14, + MxL_5007_V2_100_F1 = 0x21, + MxL_5007_V2_100_F2 = 0x22, + MxL_5007_V2_200_F1 = 0x23, + MxL_5007_V2_200_F2 = 0x24, +}; + +struct reg_pair_t { + u8 reg; + u8 val; +}; + +/* ------------------------------------------------------------------------- */ + +static struct reg_pair_t init_tab[] = { + { 0x02, 0x06 }, + { 0x03, 0x48 }, + { 0x05, 0x04 }, + { 0x06, 0x10 }, + { 0x2e, 0x15 }, /* OVERRIDE */ + { 0x30, 0x10 }, /* OVERRIDE */ + { 0x45, 0x58 }, /* OVERRIDE */ + { 0x48, 0x19 }, /* OVERRIDE */ + { 0x52, 0x03 }, /* OVERRIDE */ + { 0x53, 0x44 }, /* OVERRIDE */ + { 0x6a, 0x4b }, /* OVERRIDE */ + { 0x76, 0x00 }, /* OVERRIDE */ + { 0x78, 0x18 }, /* OVERRIDE */ + { 0x7a, 0x17 }, /* OVERRIDE */ + { 0x85, 0x06 }, /* OVERRIDE */ + { 0x01, 0x01 }, /* TOP_MASTER_ENABLE */ + { 0, 0 } +}; + +static struct reg_pair_t init_tab_cable[] = { + { 0x02, 0x06 }, + { 0x03, 0x48 }, + { 0x05, 0x04 }, + { 0x06, 0x10 }, + { 0x09, 0x3f }, + { 0x0a, 0x3f }, + { 0x0b, 0x3f }, + { 0x2e, 0x15 }, /* OVERRIDE */ + { 0x30, 0x10 }, /* OVERRIDE */ + { 0x45, 0x58 }, /* OVERRIDE */ + { 0x48, 0x19 }, /* OVERRIDE */ + { 0x52, 0x03 }, /* OVERRIDE */ + { 0x53, 0x44 }, /* OVERRIDE */ + { 0x6a, 0x4b }, /* OVERRIDE */ + { 0x76, 0x00 }, /* OVERRIDE */ + { 0x78, 0x18 }, /* OVERRIDE */ + { 0x7a, 0x17 }, /* OVERRIDE */ + { 0x85, 0x06 }, /* OVERRIDE */ + { 0x01, 0x01 }, /* TOP_MASTER_ENABLE */ + { 0, 0 } +}; + +/* ------------------------------------------------------------------------- */ + +static struct reg_pair_t reg_pair_rftune[] = { + { 0x0f, 0x00 }, /* abort tune */ + { 0x0c, 0x15 }, + { 0x0d, 0x40 }, + { 0x0e, 0x0e }, + { 0x1f, 0x87 }, /* OVERRIDE */ + { 0x20, 0x1f }, /* OVERRIDE */ + { 0x21, 0x87 }, /* OVERRIDE */ + { 0x22, 0x1f }, /* OVERRIDE */ + { 0x80, 0x01 }, /* freq dependent */ + { 0x0f, 0x01 }, /* start tune */ + { 0, 0 } +}; + +/* ------------------------------------------------------------------------- */ + +struct mxl5007t_state { + struct list_head hybrid_tuner_instance_list; + struct tuner_i2c_props i2c_props; + + struct mutex lock; + + struct mxl5007t_config *config; + + enum mxl5007t_chip_version chip_id; + + struct reg_pair_t tab_init[ARRAY_SIZE(init_tab)]; + struct reg_pair_t tab_init_cable[ARRAY_SIZE(init_tab_cable)]; + struct reg_pair_t tab_rftune[ARRAY_SIZE(reg_pair_rftune)]; + + enum mxl5007t_if_freq if_freq; + + u32 frequency; + u32 bandwidth; +}; + +/* ------------------------------------------------------------------------- */ + +/* called by _init and _rftun to manipulate the register arrays */ + +static void set_reg_bits(struct reg_pair_t *reg_pair, u8 reg, u8 mask, u8 val) +{ + unsigned int i = 0; + + while (reg_pair[i].reg || reg_pair[i].val) { + if (reg_pair[i].reg == reg) { + reg_pair[i].val &= ~mask; + reg_pair[i].val |= val; + } + i++; + + } + return; +} + +static void copy_reg_bits(struct reg_pair_t *reg_pair1, + struct reg_pair_t *reg_pair2) +{ + unsigned int i, j; + + i = j = 0; + + while (reg_pair1[i].reg || reg_pair1[i].val) { + while (reg_pair2[j].reg || reg_pair2[j].val) { + if (reg_pair1[i].reg != reg_pair2[j].reg) { + j++; + continue; + } + reg_pair2[j].val = reg_pair1[i].val; + break; + } + i++; + } + return; +} + +/* ------------------------------------------------------------------------- */ + +static void mxl5007t_set_mode_bits(struct mxl5007t_state *state, + enum mxl5007t_mode mode, + s32 if_diff_out_level) +{ + switch (mode) { + case MxL_MODE_ATSC: + set_reg_bits(state->tab_init, 0x06, 0x1f, 0x12); + break; + case MxL_MODE_DVBT: + set_reg_bits(state->tab_init, 0x06, 0x1f, 0x11); + break; + case MxL_MODE_ISDBT: + set_reg_bits(state->tab_init, 0x06, 0x1f, 0x10); + break; + case MxL_MODE_CABLE: + set_reg_bits(state->tab_init_cable, 0x09, 0xff, 0xc1); + set_reg_bits(state->tab_init_cable, 0x0a, 0xff, + 8 - if_diff_out_level); + set_reg_bits(state->tab_init_cable, 0x0b, 0xff, 0x17); + break; + default: + mxl_fail(-EINVAL); + } + return; +} + +static void mxl5007t_set_if_freq_bits(struct mxl5007t_state *state, + enum mxl5007t_if_freq if_freq, + int invert_if) +{ + u8 val; + + switch (if_freq) { + case MxL_IF_4_MHZ: + val = 0x00; + break; + case MxL_IF_4_5_MHZ: + val = 0x02; + break; + case MxL_IF_4_57_MHZ: + val = 0x03; + break; + case MxL_IF_5_MHZ: + val = 0x04; + break; + case MxL_IF_5_38_MHZ: + val = 0x05; + break; + case MxL_IF_6_MHZ: + val = 0x06; + break; + case MxL_IF_6_28_MHZ: + val = 0x07; + break; + case MxL_IF_9_1915_MHZ: + val = 0x08; + break; + case MxL_IF_35_25_MHZ: + val = 0x09; + break; + case MxL_IF_36_15_MHZ: + val = 0x0a; + break; + case MxL_IF_44_MHZ: + val = 0x0b; + break; + default: + mxl_fail(-EINVAL); + return; + } + set_reg_bits(state->tab_init, 0x02, 0x0f, val); + + /* set inverted IF or normal IF */ + set_reg_bits(state->tab_init, 0x02, 0x10, invert_if ? 0x10 : 0x00); + + state->if_freq = if_freq; + + return; +} + +static void mxl5007t_set_xtal_freq_bits(struct mxl5007t_state *state, + enum mxl5007t_xtal_freq xtal_freq) +{ + switch (xtal_freq) { + case MxL_XTAL_16_MHZ: + /* select xtal freq & ref freq */ + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x00); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x00); + break; + case MxL_XTAL_20_MHZ: + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x10); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x01); + break; + case MxL_XTAL_20_25_MHZ: + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x20); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x02); + break; + case MxL_XTAL_20_48_MHZ: + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x30); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x03); + break; + case MxL_XTAL_24_MHZ: + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x40); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x04); + break; + case MxL_XTAL_25_MHZ: + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x50); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x05); + break; + case MxL_XTAL_25_14_MHZ: + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x60); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x06); + break; + case MxL_XTAL_27_MHZ: + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x70); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x07); + break; + case MxL_XTAL_28_8_MHZ: + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x80); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x08); + break; + case MxL_XTAL_32_MHZ: + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x90); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x09); + break; + case MxL_XTAL_40_MHZ: + set_reg_bits(state->tab_init, 0x03, 0xf0, 0xa0); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0a); + break; + case MxL_XTAL_44_MHZ: + set_reg_bits(state->tab_init, 0x03, 0xf0, 0xb0); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0b); + break; + case MxL_XTAL_48_MHZ: + set_reg_bits(state->tab_init, 0x03, 0xf0, 0xc0); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0c); + break; + case MxL_XTAL_49_3811_MHZ: + set_reg_bits(state->tab_init, 0x03, 0xf0, 0xd0); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0d); + break; + default: + mxl_fail(-EINVAL); + return; + } + + return; +} + +static struct reg_pair_t *mxl5007t_calc_init_regs(struct mxl5007t_state *state, + enum mxl5007t_mode mode) +{ + struct mxl5007t_config *cfg = state->config; + + memcpy(&state->tab_init, &init_tab, sizeof(init_tab)); + memcpy(&state->tab_init_cable, &init_tab_cable, sizeof(init_tab_cable)); + + mxl5007t_set_mode_bits(state, mode, cfg->if_diff_out_level); + mxl5007t_set_if_freq_bits(state, cfg->if_freq_hz, cfg->invert_if); + mxl5007t_set_xtal_freq_bits(state, cfg->xtal_freq_hz); + + set_reg_bits(state->tab_init, 0x04, 0x01, cfg->loop_thru_enable); + set_reg_bits(state->tab_init, 0x03, 0x08, cfg->clk_out_enable << 3); + set_reg_bits(state->tab_init, 0x03, 0x07, cfg->clk_out_amp); + + if (mode >= MxL_MODE_CABLE) { + copy_reg_bits(state->tab_init, state->tab_init_cable); + return state->tab_init_cable; + } else + return state->tab_init; +} + +/* ------------------------------------------------------------------------- */ + +enum mxl5007t_bw_mhz { + MxL_BW_6MHz = 6, + MxL_BW_7MHz = 7, + MxL_BW_8MHz = 8, +}; + +static void mxl5007t_set_bw_bits(struct mxl5007t_state *state, + enum mxl5007t_bw_mhz bw) +{ + u8 val; + + switch (bw) { + case MxL_BW_6MHz: + val = 0x15; /* set DIG_MODEINDEX, DIG_MODEINDEX_A, + * and DIG_MODEINDEX_CSF */ + break; + case MxL_BW_7MHz: + val = 0x2a; + break; + case MxL_BW_8MHz: + val = 0x3f; + break; + default: + mxl_fail(-EINVAL); + return; + } + set_reg_bits(state->tab_rftune, 0x0c, 0x3f, val); + + return; +} + +static struct +reg_pair_t *mxl5007t_calc_rf_tune_regs(struct mxl5007t_state *state, + u32 rf_freq, enum mxl5007t_bw_mhz bw) +{ + u32 dig_rf_freq = 0; + u32 temp; + u32 frac_divider = 1000000; + unsigned int i; + + memcpy(&state->tab_rftune, ®_pair_rftune, sizeof(reg_pair_rftune)); + + mxl5007t_set_bw_bits(state, bw); + + /* Convert RF frequency into 16 bits => + * 10 bit integer (MHz) + 6 bit fraction */ + dig_rf_freq = rf_freq / MHz; + + temp = rf_freq % MHz; + + for (i = 0; i < 6; i++) { + dig_rf_freq <<= 1; + frac_divider /= 2; + if (temp > frac_divider) { + temp -= frac_divider; + dig_rf_freq++; + } + } + + /* add to have shift center point by 7.8124 kHz */ + if (temp > 7812) + dig_rf_freq++; + + set_reg_bits(state->tab_rftune, 0x0d, 0xff, (u8) dig_rf_freq); + set_reg_bits(state->tab_rftune, 0x0e, 0xff, (u8) (dig_rf_freq >> 8)); + + if (rf_freq >= 333000000) + set_reg_bits(state->tab_rftune, 0x80, 0x40, 0x40); + + return state->tab_rftune; +} + +/* ------------------------------------------------------------------------- */ + +static int mxl5007t_write_reg(struct mxl5007t_state *state, u8 reg, u8 val) +{ + u8 buf[] = { reg, val }; + struct i2c_msg msg = { .addr = state->i2c_props.addr, .flags = 0, + .buf = buf, .len = 2 }; + int ret; + + ret = i2c_transfer(state->i2c_props.adap, &msg, 1); + if (ret != 1) { + mxl_err("failed!"); + return -EREMOTEIO; + } + return 0; +} + +static int mxl5007t_write_regs(struct mxl5007t_state *state, + struct reg_pair_t *reg_pair) +{ + unsigned int i = 0; + int ret = 0; + + while ((ret == 0) && (reg_pair[i].reg || reg_pair[i].val)) { + ret = mxl5007t_write_reg(state, + reg_pair[i].reg, reg_pair[i].val); + i++; + } + return ret; +} + +static int mxl5007t_read_reg(struct mxl5007t_state *state, u8 reg, u8 *val) +{ + u8 buf[2] = { 0xfb, reg }; + struct i2c_msg msg[] = { + { .addr = state->i2c_props.addr, .flags = 0, + .buf = buf, .len = 2 }, + { .addr = state->i2c_props.addr, .flags = I2C_M_RD, + .buf = val, .len = 1 }, + }; + int ret; + + ret = i2c_transfer(state->i2c_props.adap, msg, 2); + if (ret != 2) { + mxl_err("failed!"); + return -EREMOTEIO; + } + return 0; +} + +static int mxl5007t_soft_reset(struct mxl5007t_state *state) +{ + u8 d = 0xff; + struct i2c_msg msg = { + .addr = state->i2c_props.addr, .flags = 0, + .buf = &d, .len = 1 + }; + int ret = i2c_transfer(state->i2c_props.adap, &msg, 1); + + if (ret != 1) { + mxl_err("failed!"); + return -EREMOTEIO; + } + return 0; +} + +static int mxl5007t_tuner_init(struct mxl5007t_state *state, + enum mxl5007t_mode mode) +{ + struct reg_pair_t *init_regs; + int ret; + + ret = mxl5007t_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + + /* calculate initialization reg array */ + init_regs = mxl5007t_calc_init_regs(state, mode); + + ret = mxl5007t_write_regs(state, init_regs); + if (mxl_fail(ret)) + goto fail; + mdelay(1); +fail: + return ret; +} + +static int mxl5007t_tuner_rf_tune(struct mxl5007t_state *state, u32 rf_freq_hz, + enum mxl5007t_bw_mhz bw) +{ + struct reg_pair_t *rf_tune_regs; + int ret; + + /* calculate channel change reg array */ + rf_tune_regs = mxl5007t_calc_rf_tune_regs(state, rf_freq_hz, bw); + + ret = mxl5007t_write_regs(state, rf_tune_regs); + if (mxl_fail(ret)) + goto fail; + msleep(3); +fail: + return ret; +} + +/* ------------------------------------------------------------------------- */ + +static int mxl5007t_synth_lock_status(struct mxl5007t_state *state, + int *rf_locked, int *ref_locked) +{ + u8 d; + int ret; + + *rf_locked = 0; + *ref_locked = 0; + + ret = mxl5007t_read_reg(state, 0xd8, &d); + if (mxl_fail(ret)) + goto fail; + + if ((d & 0x0c) == 0x0c) + *rf_locked = 1; + + if ((d & 0x03) == 0x03) + *ref_locked = 1; +fail: + return ret; +} + +/* ------------------------------------------------------------------------- */ + +static int mxl5007t_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct mxl5007t_state *state = fe->tuner_priv; + int rf_locked, ref_locked, ret; + + *status = 0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = mxl5007t_synth_lock_status(state, &rf_locked, &ref_locked); + if (mxl_fail(ret)) + goto fail; + mxl_debug("%s%s", rf_locked ? "rf locked " : "", + ref_locked ? "ref locked" : ""); + + if ((rf_locked) || (ref_locked)) + *status |= TUNER_STATUS_LOCKED; +fail: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return ret; +} + +/* ------------------------------------------------------------------------- */ + +static int mxl5007t_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 delsys = c->delivery_system; + struct mxl5007t_state *state = fe->tuner_priv; + enum mxl5007t_bw_mhz bw; + enum mxl5007t_mode mode; + int ret; + u32 freq = c->frequency; + + switch (delsys) { + case SYS_ATSC: + mode = MxL_MODE_ATSC; + bw = MxL_BW_6MHz; + break; + case SYS_DVBC_ANNEX_B: + mode = MxL_MODE_CABLE; + bw = MxL_BW_6MHz; + break; + case SYS_DVBT: + case SYS_DVBT2: + mode = MxL_MODE_DVBT; + switch (c->bandwidth_hz) { + case 6000000: + bw = MxL_BW_6MHz; + break; + case 7000000: + bw = MxL_BW_7MHz; + break; + case 8000000: + bw = MxL_BW_8MHz; + break; + default: + return -EINVAL; + } + break; + default: + mxl_err("modulation type not supported!"); + return -EINVAL; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + mutex_lock(&state->lock); + + ret = mxl5007t_tuner_init(state, mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl5007t_tuner_rf_tune(state, freq, bw); + if (mxl_fail(ret)) + goto fail; + + state->frequency = freq; + state->bandwidth = c->bandwidth_hz; +fail: + mutex_unlock(&state->lock); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return ret; +} + +/* ------------------------------------------------------------------------- */ + +static int mxl5007t_init(struct dvb_frontend *fe) +{ + struct mxl5007t_state *state = fe->tuner_priv; + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* wake from standby */ + ret = mxl5007t_write_reg(state, 0x01, 0x01); + mxl_fail(ret); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return ret; +} + +static int mxl5007t_sleep(struct dvb_frontend *fe) +{ + struct mxl5007t_state *state = fe->tuner_priv; + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* enter standby mode */ + ret = mxl5007t_write_reg(state, 0x01, 0x00); + mxl_fail(ret); + ret = mxl5007t_write_reg(state, 0x0f, 0x00); + mxl_fail(ret); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return ret; +} + +/* ------------------------------------------------------------------------- */ + +static int mxl5007t_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct mxl5007t_state *state = fe->tuner_priv; + *frequency = state->frequency; + return 0; +} + +static int mxl5007t_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct mxl5007t_state *state = fe->tuner_priv; + *bandwidth = state->bandwidth; + return 0; +} + +static int mxl5007t_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct mxl5007t_state *state = fe->tuner_priv; + + *frequency = 0; + + switch (state->if_freq) { + case MxL_IF_4_MHZ: + *frequency = 4000000; + break; + case MxL_IF_4_5_MHZ: + *frequency = 4500000; + break; + case MxL_IF_4_57_MHZ: + *frequency = 4570000; + break; + case MxL_IF_5_MHZ: + *frequency = 5000000; + break; + case MxL_IF_5_38_MHZ: + *frequency = 5380000; + break; + case MxL_IF_6_MHZ: + *frequency = 6000000; + break; + case MxL_IF_6_28_MHZ: + *frequency = 6280000; + break; + case MxL_IF_9_1915_MHZ: + *frequency = 9191500; + break; + case MxL_IF_35_25_MHZ: + *frequency = 35250000; + break; + case MxL_IF_36_15_MHZ: + *frequency = 36150000; + break; + case MxL_IF_44_MHZ: + *frequency = 44000000; + break; + } + return 0; +} + +static int mxl5007t_release(struct dvb_frontend *fe) +{ + struct mxl5007t_state *state = fe->tuner_priv; + + mutex_lock(&mxl5007t_list_mutex); + + if (state) + hybrid_tuner_release_state(state); + + mutex_unlock(&mxl5007t_list_mutex); + + fe->tuner_priv = NULL; + + return 0; +} + +/* ------------------------------------------------------------------------- */ + +static struct dvb_tuner_ops mxl5007t_tuner_ops = { + .info = { + .name = "MaxLinear MxL5007T", + }, + .init = mxl5007t_init, + .sleep = mxl5007t_sleep, + .set_params = mxl5007t_set_params, + .get_status = mxl5007t_get_status, + .get_frequency = mxl5007t_get_frequency, + .get_bandwidth = mxl5007t_get_bandwidth, + .release = mxl5007t_release, + .get_if_frequency = mxl5007t_get_if_frequency, +}; + +static int mxl5007t_get_chip_id(struct mxl5007t_state *state) +{ + char *name; + int ret; + u8 id; + + ret = mxl5007t_read_reg(state, 0xd9, &id); + if (mxl_fail(ret)) + goto fail; + + switch (id) { + case MxL_5007_V1_F1: + name = "MxL5007.v1.f1"; + break; + case MxL_5007_V1_F2: + name = "MxL5007.v1.f2"; + break; + case MxL_5007_V2_100_F1: + name = "MxL5007.v2.100.f1"; + break; + case MxL_5007_V2_100_F2: + name = "MxL5007.v2.100.f2"; + break; + case MxL_5007_V2_200_F1: + name = "MxL5007.v2.200.f1"; + break; + case MxL_5007_V2_200_F2: + name = "MxL5007.v2.200.f2"; + break; + case MxL_5007_V4: + name = "MxL5007T.v4"; + break; + default: + name = "MxL5007T"; + printk(KERN_WARNING "%s: unknown rev (%02x)\n", __func__, id); + id = MxL_UNKNOWN_ID; + } + state->chip_id = id; + mxl_info("%s detected @ %d-%04x", name, + i2c_adapter_id(state->i2c_props.adap), + state->i2c_props.addr); + return 0; +fail: + mxl_warn("unable to identify device @ %d-%04x", + i2c_adapter_id(state->i2c_props.adap), + state->i2c_props.addr); + + state->chip_id = MxL_UNKNOWN_ID; + return ret; +} + +struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 addr, + struct mxl5007t_config *cfg) +{ + struct mxl5007t_state *state = NULL; + int instance, ret; + + mutex_lock(&mxl5007t_list_mutex); + instance = hybrid_tuner_request_state(struct mxl5007t_state, state, + hybrid_tuner_instance_list, + i2c, addr, "mxl5007t"); + switch (instance) { + case 0: + goto fail; + case 1: + /* new tuner instance */ + state->config = cfg; + + mutex_init(&state->lock); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = mxl5007t_get_chip_id(state); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + /* check return value of mxl5007t_get_chip_id */ + if (mxl_fail(ret)) + goto fail; + break; + default: + /* existing tuner instance */ + break; + } + fe->tuner_priv = state; + mutex_unlock(&mxl5007t_list_mutex); + + memcpy(&fe->ops.tuner_ops, &mxl5007t_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + return fe; +fail: + mutex_unlock(&mxl5007t_list_mutex); + + mxl5007t_release(fe); + return NULL; +} +EXPORT_SYMBOL_GPL(mxl5007t_attach); +MODULE_DESCRIPTION("MaxLinear MxL5007T Silicon IC tuner driver"); +MODULE_AUTHOR("Michael Krufky "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.2"); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/tuners/mxl5007t.h b/drivers/media/tuners/mxl5007t.h new file mode 100644 index 000000000000..aa3eea0b5262 --- /dev/null +++ b/drivers/media/tuners/mxl5007t.h @@ -0,0 +1,104 @@ +/* + * mxl5007t.h - driver for the MaxLinear MxL5007T silicon tuner + * + * Copyright (C) 2008 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 __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/tuners/qt1010.c b/drivers/media/tuners/qt1010.c new file mode 100644 index 000000000000..bdc39e11030e --- /dev/null +++ b/drivers/media/tuners/qt1010.c @@ -0,0 +1,480 @@ +/* + * Driver for Quantek QT1010 silicon tuner + * + * Copyright (C) 2006 Antti Palosaari + * Aapo Tahkola + * + * 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 "qt1010.h" +#include "qt1010_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 "QT1010: " args); \ + } while (0) + +/* read single register */ +static int qt1010_readreg(struct qt1010_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->cfg->i2c_address, + .flags = 0, .buf = ®, .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 "qt1010 I2C read failed\n"); + return -EREMOTEIO; + } + return 0; +} + +/* write single register */ +static int qt1010_writereg(struct qt1010_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 "qt1010 I2C write failed\n"); + return -EREMOTEIO; + } + return 0; +} + +/* dump all registers */ +static void qt1010_dump_regs(struct qt1010_priv *priv) +{ + u8 reg, val; + + for (reg = 0; ; reg++) { + if (reg % 16 == 0) { + if (reg) + printk(KERN_CONT "\n"); + printk(KERN_DEBUG "%02x:", reg); + } + if (qt1010_readreg(priv, reg, &val) == 0) + printk(KERN_CONT " %02x", val); + else + printk(KERN_CONT " --"); + if (reg == 0x2f) + break; + } + printk(KERN_CONT "\n"); +} + +static int qt1010_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct qt1010_priv *priv; + int err; + u32 freq, div, mod1, mod2; + u8 i, tmpval, reg05; + qt1010_i2c_oper_t rd[48] = { + { QT1010_WR, 0x01, 0x80 }, + { QT1010_WR, 0x02, 0x3f }, + { QT1010_WR, 0x05, 0xff }, /* 02 c write */ + { QT1010_WR, 0x06, 0x44 }, + { QT1010_WR, 0x07, 0xff }, /* 04 c write */ + { QT1010_WR, 0x08, 0x08 }, + { QT1010_WR, 0x09, 0xff }, /* 06 c write */ + { QT1010_WR, 0x0a, 0xff }, /* 07 c write */ + { QT1010_WR, 0x0b, 0xff }, /* 08 c write */ + { QT1010_WR, 0x0c, 0xe1 }, + { QT1010_WR, 0x1a, 0xff }, /* 10 c write */ + { QT1010_WR, 0x1b, 0x00 }, + { QT1010_WR, 0x1c, 0x89 }, + { QT1010_WR, 0x11, 0xff }, /* 13 c write */ + { QT1010_WR, 0x12, 0xff }, /* 14 c write */ + { QT1010_WR, 0x22, 0xff }, /* 15 c write */ + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x1e, 0xd0 }, + { QT1010_RD, 0x22, 0xff }, /* 16 c read */ + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_RD, 0x05, 0xff }, /* 20 c read */ + { QT1010_RD, 0x22, 0xff }, /* 21 c read */ + { QT1010_WR, 0x23, 0xd0 }, + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x1e, 0xe0 }, + { QT1010_RD, 0x23, 0xff }, /* 25 c read */ + { QT1010_RD, 0x23, 0xff }, /* 26 c read */ + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x24, 0xd0 }, + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x1e, 0xf0 }, + { QT1010_RD, 0x24, 0xff }, /* 31 c read */ + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x14, 0x7f }, + { QT1010_WR, 0x15, 0x7f }, + { QT1010_WR, 0x05, 0xff }, /* 35 c write */ + { QT1010_WR, 0x06, 0x00 }, + { QT1010_WR, 0x15, 0x1f }, + { QT1010_WR, 0x16, 0xff }, + { QT1010_WR, 0x18, 0xff }, + { QT1010_WR, 0x1f, 0xff }, /* 40 c write */ + { QT1010_WR, 0x20, 0xff }, /* 41 c write */ + { QT1010_WR, 0x21, 0x53 }, + { QT1010_WR, 0x25, 0xff }, /* 43 c write */ + { QT1010_WR, 0x26, 0x15 }, + { QT1010_WR, 0x00, 0xff }, /* 45 c write */ + { QT1010_WR, 0x02, 0x00 }, + { QT1010_WR, 0x01, 0x00 } + }; + +#define FREQ1 32000000 /* 32 MHz */ +#define FREQ2 4000000 /* 4 MHz Quartz oscillator in the stick? */ + + priv = fe->tuner_priv; + freq = c->frequency; + div = (freq + QT1010_OFFSET) / QT1010_STEP; + freq = (div * QT1010_STEP) - QT1010_OFFSET; + mod1 = (freq + QT1010_OFFSET) % FREQ1; + mod2 = (freq + QT1010_OFFSET) % FREQ2; + priv->frequency = freq; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + /* reg 05 base value */ + if (freq < 290000000) reg05 = 0x14; /* 290 MHz */ + else if (freq < 610000000) reg05 = 0x34; /* 610 MHz */ + else if (freq < 802000000) reg05 = 0x54; /* 802 MHz */ + else reg05 = 0x74; + + /* 0x5 */ + rd[2].val = reg05; + + /* 07 - set frequency: 32 MHz scale */ + rd[4].val = (freq + QT1010_OFFSET) / FREQ1; + + /* 09 - changes every 8/24 MHz */ + if (mod1 < 8000000) rd[6].val = 0x1d; + else rd[6].val = 0x1c; + + /* 0a - set frequency: 4 MHz scale (max 28 MHz) */ + if (mod1 < 1*FREQ2) rd[7].val = 0x09; /* +0 MHz */ + else if (mod1 < 2*FREQ2) rd[7].val = 0x08; /* +4 MHz */ + else if (mod1 < 3*FREQ2) rd[7].val = 0x0f; /* +8 MHz */ + else if (mod1 < 4*FREQ2) rd[7].val = 0x0e; /* +12 MHz */ + else if (mod1 < 5*FREQ2) rd[7].val = 0x0d; /* +16 MHz */ + else if (mod1 < 6*FREQ2) rd[7].val = 0x0c; /* +20 MHz */ + else if (mod1 < 7*FREQ2) rd[7].val = 0x0b; /* +24 MHz */ + else rd[7].val = 0x0a; /* +28 MHz */ + + /* 0b - changes every 2/2 MHz */ + if (mod2 < 2000000) rd[8].val = 0x45; + else rd[8].val = 0x44; + + /* 1a - set frequency: 125 kHz scale (max 3875 kHz)*/ + tmpval = 0x78; /* byte, overflows intentionally */ + rd[10].val = tmpval-((mod2/QT1010_STEP)*0x08); + + /* 11 */ + rd[13].val = 0xfd; /* TODO: correct value calculation */ + + /* 12 */ + rd[14].val = 0x91; /* TODO: correct value calculation */ + + /* 22 */ + if (freq < 450000000) rd[15].val = 0xd0; /* 450 MHz */ + else if (freq < 482000000) rd[15].val = 0xd1; /* 482 MHz */ + else if (freq < 514000000) rd[15].val = 0xd4; /* 514 MHz */ + else if (freq < 546000000) rd[15].val = 0xd7; /* 546 MHz */ + else if (freq < 610000000) rd[15].val = 0xda; /* 610 MHz */ + else rd[15].val = 0xd0; + + /* 05 */ + rd[35].val = (reg05 & 0xf0); + + /* 1f */ + if (mod1 < 8000000) tmpval = 0x00; + else if (mod1 < 12000000) tmpval = 0x01; + else if (mod1 < 16000000) tmpval = 0x02; + else if (mod1 < 24000000) tmpval = 0x03; + else if (mod1 < 28000000) tmpval = 0x04; + else tmpval = 0x05; + rd[40].val = (priv->reg1f_init_val + 0x0e + tmpval); + + /* 20 */ + if (mod1 < 8000000) tmpval = 0x00; + else if (mod1 < 12000000) tmpval = 0x01; + else if (mod1 < 20000000) tmpval = 0x02; + else if (mod1 < 24000000) tmpval = 0x03; + else if (mod1 < 28000000) tmpval = 0x04; + else tmpval = 0x05; + rd[41].val = (priv->reg20_init_val + 0x0d + tmpval); + + /* 25 */ + rd[43].val = priv->reg25_init_val; + + /* 00 */ + rd[45].val = 0x92; /* TODO: correct value calculation */ + + dprintk("freq:%u 05:%02x 07:%02x 09:%02x 0a:%02x 0b:%02x " \ + "1a:%02x 11:%02x 12:%02x 22:%02x 05:%02x 1f:%02x " \ + "20:%02x 25:%02x 00:%02x", \ + freq, rd[2].val, rd[4].val, rd[6].val, rd[7].val, rd[8].val, \ + rd[10].val, rd[13].val, rd[14].val, rd[15].val, rd[35].val, \ + rd[40].val, rd[41].val, rd[43].val, rd[45].val); + + for (i = 0; i < ARRAY_SIZE(rd); i++) { + if (rd[i].oper == QT1010_WR) { + err = qt1010_writereg(priv, rd[i].reg, rd[i].val); + } else { /* read is required to proper locking */ + err = qt1010_readreg(priv, rd[i].reg, &tmpval); + } + if (err) return err; + } + + if (debug) + qt1010_dump_regs(priv); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + return 0; +} + +static int qt1010_init_meas1(struct qt1010_priv *priv, + u8 oper, u8 reg, u8 reg_init_val, u8 *retval) +{ + u8 i, val1, val2; + int err; + + qt1010_i2c_oper_t i2c_data[] = { + { QT1010_WR, reg, reg_init_val }, + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x1e, oper }, + { QT1010_RD, reg, 0xff } + }; + + for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { + if (i2c_data[i].oper == QT1010_WR) { + err = qt1010_writereg(priv, i2c_data[i].reg, + i2c_data[i].val); + } else { + err = qt1010_readreg(priv, i2c_data[i].reg, &val2); + } + if (err) return err; + } + + do { + val1 = val2; + err = qt1010_readreg(priv, reg, &val2); + if (err) return err; + dprintk("compare reg:%02x %02x %02x", reg, val1, val2); + } while (val1 != val2); + *retval = val1; + + return qt1010_writereg(priv, 0x1e, 0x00); +} + +static int qt1010_init_meas2(struct qt1010_priv *priv, + u8 reg_init_val, u8 *retval) +{ + u8 i, val; + int err; + qt1010_i2c_oper_t i2c_data[] = { + { QT1010_WR, 0x07, reg_init_val }, + { QT1010_WR, 0x22, 0xd0 }, + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x1e, 0xd0 }, + { QT1010_RD, 0x22, 0xff }, + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x22, 0xff } + }; + for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { + if (i2c_data[i].oper == QT1010_WR) { + err = qt1010_writereg(priv, i2c_data[i].reg, + i2c_data[i].val); + } else { + err = qt1010_readreg(priv, i2c_data[i].reg, &val); + } + if (err) return err; + } + *retval = val; + return 0; +} + +static int qt1010_init(struct dvb_frontend *fe) +{ + struct qt1010_priv *priv = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int err = 0; + u8 i, tmpval, *valptr = NULL; + + qt1010_i2c_oper_t i2c_data[] = { + { QT1010_WR, 0x01, 0x80 }, + { QT1010_WR, 0x0d, 0x84 }, + { QT1010_WR, 0x0e, 0xb7 }, + { QT1010_WR, 0x2a, 0x23 }, + { QT1010_WR, 0x2c, 0xdc }, + { QT1010_M1, 0x25, 0x40 }, /* get reg 25 init value */ + { QT1010_M1, 0x81, 0xff }, /* get reg 25 init value */ + { QT1010_WR, 0x2b, 0x70 }, + { QT1010_WR, 0x2a, 0x23 }, + { QT1010_M1, 0x26, 0x08 }, + { QT1010_M1, 0x82, 0xff }, + { QT1010_WR, 0x05, 0x14 }, + { QT1010_WR, 0x06, 0x44 }, + { QT1010_WR, 0x07, 0x28 }, + { QT1010_WR, 0x08, 0x0b }, + { QT1010_WR, 0x11, 0xfd }, + { QT1010_M1, 0x22, 0x0d }, + { QT1010_M1, 0xd0, 0xff }, + { QT1010_WR, 0x06, 0x40 }, + { QT1010_WR, 0x16, 0xf0 }, + { QT1010_WR, 0x02, 0x38 }, + { QT1010_WR, 0x03, 0x18 }, + { QT1010_WR, 0x20, 0xe0 }, + { QT1010_M1, 0x1f, 0x20 }, /* get reg 1f init value */ + { QT1010_M1, 0x84, 0xff }, /* get reg 1f init value */ + { QT1010_RD, 0x20, 0x20 }, /* get reg 20 init value */ + { QT1010_WR, 0x03, 0x19 }, + { QT1010_WR, 0x02, 0x3f }, + { QT1010_WR, 0x21, 0x53 }, + { QT1010_RD, 0x21, 0xff }, + { QT1010_WR, 0x11, 0xfd }, + { QT1010_WR, 0x05, 0x34 }, + { QT1010_WR, 0x06, 0x44 }, + { QT1010_WR, 0x08, 0x08 } + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { + switch (i2c_data[i].oper) { + case QT1010_WR: + err = qt1010_writereg(priv, i2c_data[i].reg, + i2c_data[i].val); + break; + case QT1010_RD: + if (i2c_data[i].val == 0x20) + valptr = &priv->reg20_init_val; + else + valptr = &tmpval; + err = qt1010_readreg(priv, i2c_data[i].reg, valptr); + break; + case QT1010_M1: + if (i2c_data[i].val == 0x25) + valptr = &priv->reg25_init_val; + else if (i2c_data[i].val == 0x1f) + valptr = &priv->reg1f_init_val; + else + valptr = &tmpval; + err = qt1010_init_meas1(priv, i2c_data[i+1].reg, + i2c_data[i].reg, + i2c_data[i].val, valptr); + i++; + break; + } + if (err) return err; + } + + for (i = 0x31; i < 0x3a; i++) /* 0x31 - 0x39 */ + if ((err = qt1010_init_meas2(priv, i, &tmpval))) + return err; + + c->frequency = 545000000; /* Sigmatek DVB-110 545000000 */ + /* MSI Megasky 580 GL861 533000000 */ + return qt1010_set_params(fe); +} + +static int qt1010_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int qt1010_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct qt1010_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int qt1010_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + *frequency = 36125000; + return 0; +} + +static const struct dvb_tuner_ops qt1010_tuner_ops = { + .info = { + .name = "Quantek QT1010", + .frequency_min = QT1010_MIN_FREQ, + .frequency_max = QT1010_MAX_FREQ, + .frequency_step = QT1010_STEP, + }, + + .release = qt1010_release, + .init = qt1010_init, + /* TODO: implement sleep */ + + .set_params = qt1010_set_params, + .get_frequency = qt1010_get_frequency, + .get_if_frequency = qt1010_get_if_frequency, +}; + +struct dvb_frontend * qt1010_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct qt1010_config *cfg) +{ + struct qt1010_priv *priv = NULL; + u8 id; + + priv = kzalloc(sizeof(struct qt1010_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->cfg = cfg; + priv->i2c = i2c; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + + /* Try to detect tuner chip. Probably this is not correct register. */ + if (qt1010_readreg(priv, 0x29, &id) != 0 || (id != 0x39)) { + kfree(priv); + return NULL; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + printk(KERN_INFO "Quantek QT1010 successfully identified.\n"); + memcpy(&fe->ops.tuner_ops, &qt1010_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + return fe; +} +EXPORT_SYMBOL(qt1010_attach); + +MODULE_DESCRIPTION("Quantek QT1010 silicon tuner driver"); +MODULE_AUTHOR("Antti Palosaari "); +MODULE_AUTHOR("Aapo Tahkola "); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/qt1010.h b/drivers/media/tuners/qt1010.h new file mode 100644 index 000000000000..807fb7b6146b --- /dev/null +++ b/drivers/media/tuners/qt1010.h @@ -0,0 +1,53 @@ +/* + * Driver for Quantek QT1010 silicon tuner + * + * Copyright (C) 2006 Antti Palosaari + * Aapo Tahkola + * + * 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/tuners/qt1010_priv.h b/drivers/media/tuners/qt1010_priv.h new file mode 100644 index 000000000000..2c42d3f01636 --- /dev/null +++ b/drivers/media/tuners/qt1010_priv.h @@ -0,0 +1,104 @@ +/* + * Driver for Quantek QT1010 silicon tuner + * + * Copyright (C) 2006 Antti Palosaari + * Aapo Tahkola + * + * 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/tuners/tda18212.c b/drivers/media/tuners/tda18212.c new file mode 100644 index 000000000000..5d9f02842501 --- /dev/null +++ b/drivers/media/tuners/tda18212.c @@ -0,0 +1,319 @@ +/* + * NXP TDA18212HN silicon tuner driver + * + * Copyright (C) 2011 Antti Palosaari + * + * 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 "tda18212.h" + +struct tda18212_priv { + struct tda18212_config *cfg; + struct i2c_adapter *i2c; + + u32 if_frequency; +}; + +/* 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 { + dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \ + "len=%d\n", KBUILD_MODNAME, 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 = ®, + }, { + .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 { + dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \ + "len=%d\n", KBUILD_MODNAME, 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 }, + }; + + dev_dbg(&priv->i2c->dev, + "%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", + __func__, 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: + dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, 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 uninitialized_var(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 */ + + dev_dbg(&priv->i2c->dev, "%s: ret=%d chip id=%02x\n", __func__, ret, + val); + if (ret || val != 0xc7) { + kfree(priv); + return NULL; + } + + dev_info(&priv->i2c->dev, + "%s: NXP TDA18212HN successfully identified\n", + KBUILD_MODNAME); + + 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 "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/tda18212.h b/drivers/media/tuners/tda18212.h new file mode 100644 index 000000000000..9bd5da4aabb7 --- /dev/null +++ b/drivers/media/tuners/tda18212.h @@ -0,0 +1,52 @@ +/* + * NXP TDA18212HN silicon tuner driver + * + * Copyright (C) 2011 Antti Palosaari + * + * 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/tuners/tda18218.c b/drivers/media/tuners/tda18218.c new file mode 100644 index 000000000000..8a6f9ca788f0 --- /dev/null +++ b/drivers/media/tuners/tda18218.c @@ -0,0 +1,342 @@ +/* + * NXP TDA18218HN silicon tuner driver + * + * Copyright (C) 2010 Antti Palosaari + * + * 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 "tda18218.h" +#include "tda18218_priv.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +/* write multiple registers */ +static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len) +{ + int ret = 0; + u8 buf[1+len], quotient, remainder, i, msg_len, msg_len_max; + struct i2c_msg msg[1] = { + { + .addr = priv->cfg->i2c_address, + .flags = 0, + .buf = buf, + } + }; + + msg_len_max = priv->cfg->i2c_wr_max - 1; + quotient = len / msg_len_max; + remainder = len % msg_len_max; + msg_len = msg_len_max; + for (i = 0; (i <= quotient && remainder); i++) { + if (i == quotient) /* set len of the last msg */ + msg_len = remainder; + + msg[0].len = msg_len + 1; + buf[0] = reg + i * msg_len_max; + memcpy(&buf[1], &val[i * msg_len_max], msg_len); + + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret != 1) + break; + } + + if (ret == 1) { + ret = 0; + } else { + warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len); + ret = -EREMOTEIO; + } + + return ret; +} + +/* read multiple registers */ +static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len) +{ + int ret; + u8 buf[reg+len]; /* we must start read always from reg 0x00 */ + struct i2c_msg msg[2] = { + { + .addr = priv->cfg->i2c_address, + .flags = 0, + .len = 1, + .buf = "\x00", + }, { + .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[reg], len); + ret = 0; + } else { + warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len); + ret = -EREMOTEIO; + } + + return ret; +} + +/* write single register */ +static int tda18218_wr_reg(struct tda18218_priv *priv, u8 reg, u8 val) +{ + return tda18218_wr_regs(priv, reg, &val, 1); +} + +/* read single register */ + +static int tda18218_rd_reg(struct tda18218_priv *priv, u8 reg, u8 *val) +{ + return tda18218_rd_regs(priv, reg, val, 1); +} + +static int tda18218_set_params(struct dvb_frontend *fe) +{ + struct tda18218_priv *priv = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 bw = c->bandwidth_hz; + int ret; + u8 buf[3], i, BP_Filter, LP_Fc; + u32 LO_Frac; + /* TODO: find out correct AGC algorithm */ + u8 agc[][2] = { + { R20_AGC11, 0x60 }, + { R23_AGC21, 0x02 }, + { R20_AGC11, 0xa0 }, + { R23_AGC21, 0x09 }, + { R20_AGC11, 0xe0 }, + { R23_AGC21, 0x0c }, + { R20_AGC11, 0x40 }, + { R23_AGC21, 0x01 }, + { R20_AGC11, 0x80 }, + { R23_AGC21, 0x08 }, + { R20_AGC11, 0xc0 }, + { R23_AGC21, 0x0b }, + { R24_AGC22, 0x1c }, + { R24_AGC22, 0x0c }, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + /* low-pass filter cut-off frequency */ + if (bw <= 6000000) { + LP_Fc = 0; + priv->if_frequency = 3000000; + } else if (bw <= 7000000) { + LP_Fc = 1; + priv->if_frequency = 3500000; + } else { + LP_Fc = 2; + priv->if_frequency = 4000000; + } + + LO_Frac = c->frequency + priv->if_frequency; + + /* band-pass filter */ + if (LO_Frac < 188000000) + BP_Filter = 3; + else if (LO_Frac < 253000000) + BP_Filter = 4; + else if (LO_Frac < 343000000) + BP_Filter = 5; + else + BP_Filter = 6; + + buf[0] = (priv->regs[R1A_IF1] & ~7) | BP_Filter; /* BP_Filter */ + buf[1] = (priv->regs[R1B_IF2] & ~3) | LP_Fc; /* LP_Fc */ + buf[2] = priv->regs[R1C_AGC2B]; + ret = tda18218_wr_regs(priv, R1A_IF1, buf, 3); + if (ret) + goto error; + + buf[0] = (LO_Frac / 1000) >> 12; /* LO_Frac_0 */ + buf[1] = (LO_Frac / 1000) >> 4; /* LO_Frac_1 */ + buf[2] = (LO_Frac / 1000) << 4 | + (priv->regs[R0C_MD5] & 0x0f); /* LO_Frac_2 */ + ret = tda18218_wr_regs(priv, R0A_MD3, buf, 3); + if (ret) + goto error; + + buf[0] = priv->regs[R0F_MD8] | (1 << 6); /* Freq_prog_Start */ + ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1); + if (ret) + goto error; + + buf[0] = priv->regs[R0F_MD8] & ~(1 << 6); /* Freq_prog_Start */ + ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1); + if (ret) + goto error; + + /* trigger AGC */ + for (i = 0; i < ARRAY_SIZE(agc); i++) { + ret = tda18218_wr_reg(priv, agc[i][0], agc[i][1]); + if (ret) + goto error; + } + +error: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (ret) + dbg("%s: failed ret:%d", __func__, ret); + + return ret; +} + +static int tda18218_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tda18218_priv *priv = fe->tuner_priv; + *frequency = priv->if_frequency; + dbg("%s: if=%d", __func__, *frequency); + return 0; +} + +static int tda18218_sleep(struct dvb_frontend *fe) +{ + struct tda18218_priv *priv = fe->tuner_priv; + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + /* standby */ + ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0)); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (ret) + dbg("%s: failed ret:%d", __func__, ret); + + return ret; +} + +static int tda18218_init(struct dvb_frontend *fe) +{ + struct tda18218_priv *priv = fe->tuner_priv; + int ret; + + /* TODO: calibrations */ + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + ret = tda18218_wr_regs(priv, R00_ID, priv->regs, TDA18218_NUM_REGS); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (ret) + dbg("%s: failed ret:%d", __func__, ret); + + return ret; +} + +static int tda18218_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static const struct dvb_tuner_ops tda18218_tuner_ops = { + .info = { + .name = "NXP TDA18218", + + .frequency_min = 174000000, + .frequency_max = 864000000, + .frequency_step = 1000, + }, + + .release = tda18218_release, + .init = tda18218_init, + .sleep = tda18218_sleep, + + .set_params = tda18218_set_params, + + .get_if_frequency = tda18218_get_if_frequency, +}; + +struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct tda18218_config *cfg) +{ + struct tda18218_priv *priv = NULL; + u8 uninitialized_var(val); + int ret; + /* chip default registers values */ + static u8 def_regs[] = { + 0xc0, 0x88, 0x00, 0x8e, 0x03, 0x00, 0x00, 0xd0, 0x00, 0x40, + 0x00, 0x00, 0x07, 0xff, 0x84, 0x09, 0x00, 0x13, 0x00, 0x00, + 0x01, 0x84, 0x09, 0xf0, 0x19, 0x0a, 0x8e, 0x69, 0x98, 0x01, + 0x00, 0x58, 0x10, 0x40, 0x8c, 0x00, 0x0c, 0x48, 0x85, 0xc9, + 0xa7, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x00, 0x39, 0x00, + 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf6 + }; + + priv = kzalloc(sizeof(struct tda18218_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 = tda18218_rd_reg(priv, R00_ID, &val); + dbg("%s: ret:%d chip ID:%02x", __func__, ret, val); + if (ret || val != def_regs[R00_ID]) { + kfree(priv); + return NULL; + } + + info("NXP TDA18218HN successfully identified."); + + memcpy(&fe->ops.tuner_ops, &tda18218_tuner_ops, + sizeof(struct dvb_tuner_ops)); + memcpy(priv->regs, def_regs, sizeof(def_regs)); + + /* loop-through enabled chip default register values */ + if (priv->cfg->loop_through) { + priv->regs[R17_PD1] = 0xb0; + priv->regs[R18_PD2] = 0x59; + } + + /* standby */ + ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0)); + if (ret) + dbg("%s: failed ret:%d", __func__, ret); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + return fe; +} +EXPORT_SYMBOL(tda18218_attach); + +MODULE_DESCRIPTION("NXP TDA18218HN silicon tuner driver"); +MODULE_AUTHOR("Antti Palosaari "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/tda18218.h b/drivers/media/tuners/tda18218.h new file mode 100644 index 000000000000..b4180d180029 --- /dev/null +++ b/drivers/media/tuners/tda18218.h @@ -0,0 +1,45 @@ +/* + * NXP TDA18218HN silicon tuner driver + * + * Copyright (C) 2010 Antti Palosaari + * + * 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/tuners/tda18218_priv.h b/drivers/media/tuners/tda18218_priv.h new file mode 100644 index 000000000000..dc52b72e1407 --- /dev/null +++ b/drivers/media/tuners/tda18218_priv.h @@ -0,0 +1,108 @@ +/* + * NXP TDA18218HN silicon tuner driver + * + * Copyright (C) 2010 Antti Palosaari + * + * 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/tuners/tda18271-common.c b/drivers/media/tuners/tda18271-common.c new file mode 100644 index 000000000000..39c645787b62 --- /dev/null +++ b/drivers/media/tuners/tda18271-common.c @@ -0,0 +1,703 @@ +/* + tda18271-common.c - driver for the Philips / NXP TDA18271 silicon tuner + + Copyright (C) 2007, 2008 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. +*/ + +#include "tda18271-priv.h" + +static int tda18271_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct tda18271_priv *priv = fe->tuner_priv; + enum tda18271_i2c_gate gate; + int ret = 0; + + switch (priv->gate) { + case TDA18271_GATE_DIGITAL: + case TDA18271_GATE_ANALOG: + gate = priv->gate; + break; + case TDA18271_GATE_AUTO: + default: + switch (priv->mode) { + case TDA18271_DIGITAL: + gate = TDA18271_GATE_DIGITAL; + break; + case TDA18271_ANALOG: + default: + gate = TDA18271_GATE_ANALOG; + break; + } + } + + switch (gate) { + case TDA18271_GATE_ANALOG: + if (fe->ops.analog_ops.i2c_gate_ctrl) + ret = fe->ops.analog_ops.i2c_gate_ctrl(fe, enable); + break; + case TDA18271_GATE_DIGITAL: + if (fe->ops.i2c_gate_ctrl) + ret = fe->ops.i2c_gate_ctrl(fe, enable); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +}; + +/*---------------------------------------------------------------------*/ + +static void tda18271_dump_regs(struct dvb_frontend *fe, int extended) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + + tda_reg("=== TDA18271 REG DUMP ===\n"); + tda_reg("ID_BYTE = 0x%02x\n", 0xff & regs[R_ID]); + tda_reg("THERMO_BYTE = 0x%02x\n", 0xff & regs[R_TM]); + tda_reg("POWER_LEVEL_BYTE = 0x%02x\n", 0xff & regs[R_PL]); + tda_reg("EASY_PROG_BYTE_1 = 0x%02x\n", 0xff & regs[R_EP1]); + tda_reg("EASY_PROG_BYTE_2 = 0x%02x\n", 0xff & regs[R_EP2]); + tda_reg("EASY_PROG_BYTE_3 = 0x%02x\n", 0xff & regs[R_EP3]); + tda_reg("EASY_PROG_BYTE_4 = 0x%02x\n", 0xff & regs[R_EP4]); + tda_reg("EASY_PROG_BYTE_5 = 0x%02x\n", 0xff & regs[R_EP5]); + tda_reg("CAL_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_CPD]); + tda_reg("CAL_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_CD1]); + tda_reg("CAL_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_CD2]); + tda_reg("CAL_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_CD3]); + tda_reg("MAIN_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_MPD]); + tda_reg("MAIN_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_MD1]); + tda_reg("MAIN_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_MD2]); + tda_reg("MAIN_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_MD3]); + + /* only dump extended regs if DBG_ADV is set */ + if (!(tda18271_debug & DBG_ADV)) + return; + + /* W indicates write-only registers. + * Register dump for write-only registers shows last value written. */ + + tda_reg("EXTENDED_BYTE_1 = 0x%02x\n", 0xff & regs[R_EB1]); + tda_reg("EXTENDED_BYTE_2 = 0x%02x\n", 0xff & regs[R_EB2]); + tda_reg("EXTENDED_BYTE_3 = 0x%02x\n", 0xff & regs[R_EB3]); + tda_reg("EXTENDED_BYTE_4 = 0x%02x\n", 0xff & regs[R_EB4]); + tda_reg("EXTENDED_BYTE_5 = 0x%02x\n", 0xff & regs[R_EB5]); + tda_reg("EXTENDED_BYTE_6 = 0x%02x\n", 0xff & regs[R_EB6]); + tda_reg("EXTENDED_BYTE_7 = 0x%02x\n", 0xff & regs[R_EB7]); + tda_reg("EXTENDED_BYTE_8 = 0x%02x\n", 0xff & regs[R_EB8]); + tda_reg("EXTENDED_BYTE_9 W = 0x%02x\n", 0xff & regs[R_EB9]); + tda_reg("EXTENDED_BYTE_10 = 0x%02x\n", 0xff & regs[R_EB10]); + tda_reg("EXTENDED_BYTE_11 = 0x%02x\n", 0xff & regs[R_EB11]); + tda_reg("EXTENDED_BYTE_12 = 0x%02x\n", 0xff & regs[R_EB12]); + tda_reg("EXTENDED_BYTE_13 = 0x%02x\n", 0xff & regs[R_EB13]); + tda_reg("EXTENDED_BYTE_14 = 0x%02x\n", 0xff & regs[R_EB14]); + tda_reg("EXTENDED_BYTE_15 = 0x%02x\n", 0xff & regs[R_EB15]); + tda_reg("EXTENDED_BYTE_16 W = 0x%02x\n", 0xff & regs[R_EB16]); + tda_reg("EXTENDED_BYTE_17 W = 0x%02x\n", 0xff & regs[R_EB17]); + tda_reg("EXTENDED_BYTE_18 = 0x%02x\n", 0xff & regs[R_EB18]); + tda_reg("EXTENDED_BYTE_19 W = 0x%02x\n", 0xff & regs[R_EB19]); + tda_reg("EXTENDED_BYTE_20 W = 0x%02x\n", 0xff & regs[R_EB20]); + tda_reg("EXTENDED_BYTE_21 = 0x%02x\n", 0xff & regs[R_EB21]); + tda_reg("EXTENDED_BYTE_22 = 0x%02x\n", 0xff & regs[R_EB22]); + tda_reg("EXTENDED_BYTE_23 = 0x%02x\n", 0xff & regs[R_EB23]); +} + +int tda18271_read_regs(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + unsigned char buf = 0x00; + int ret; + struct i2c_msg msg[] = { + { .addr = priv->i2c_props.addr, .flags = 0, + .buf = &buf, .len = 1 }, + { .addr = priv->i2c_props.addr, .flags = I2C_M_RD, + .buf = regs, .len = 16 } + }; + + tda18271_i2c_gate_ctrl(fe, 1); + + /* read all registers */ + ret = i2c_transfer(priv->i2c_props.adap, msg, 2); + + tda18271_i2c_gate_ctrl(fe, 0); + + if (ret != 2) + tda_err("ERROR: i2c_transfer returned: %d\n", ret); + + if (tda18271_debug & DBG_REG) + tda18271_dump_regs(fe, 0); + + return (ret == 2 ? 0 : ret); +} + +int tda18271_read_extended(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + unsigned char regdump[TDA18271_NUM_REGS]; + unsigned char buf = 0x00; + int ret, i; + struct i2c_msg msg[] = { + { .addr = priv->i2c_props.addr, .flags = 0, + .buf = &buf, .len = 1 }, + { .addr = priv->i2c_props.addr, .flags = I2C_M_RD, + .buf = regdump, .len = TDA18271_NUM_REGS } + }; + + tda18271_i2c_gate_ctrl(fe, 1); + + /* read all registers */ + ret = i2c_transfer(priv->i2c_props.adap, msg, 2); + + tda18271_i2c_gate_ctrl(fe, 0); + + if (ret != 2) + tda_err("ERROR: i2c_transfer returned: %d\n", ret); + + for (i = 0; i < TDA18271_NUM_REGS; i++) { + /* don't update write-only registers */ + if ((i != R_EB9) && + (i != R_EB16) && + (i != R_EB17) && + (i != R_EB19) && + (i != R_EB20)) + regs[i] = regdump[i]; + } + + if (tda18271_debug & DBG_REG) + tda18271_dump_regs(fe, 1); + + return (ret == 2 ? 0 : ret); +} + +int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + unsigned char buf[TDA18271_NUM_REGS + 1]; + struct i2c_msg msg = { .addr = priv->i2c_props.addr, .flags = 0, + .buf = buf }; + int i, ret = 1, max; + + BUG_ON((len == 0) || (idx + len > sizeof(buf))); + + + switch (priv->small_i2c) { + case TDA18271_03_BYTE_CHUNK_INIT: + max = 3; + break; + case TDA18271_08_BYTE_CHUNK_INIT: + max = 8; + break; + case TDA18271_16_BYTE_CHUNK_INIT: + max = 16; + break; + case TDA18271_39_BYTE_CHUNK_INIT: + default: + max = 39; + } + + tda18271_i2c_gate_ctrl(fe, 1); + while (len) { + if (max > len) + max = len; + + buf[0] = idx; + for (i = 1; i <= max; i++) + buf[i] = regs[idx - 1 + i]; + + msg.len = max + 1; + + /* write registers */ + ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); + if (ret != 1) + break; + + idx += max; + len -= max; + } + tda18271_i2c_gate_ctrl(fe, 0); + + if (ret != 1) + tda_err("ERROR: idx = 0x%x, len = %d, " + "i2c_transfer returned: %d\n", idx, max, ret); + + return (ret == 1 ? 0 : ret); +} + +/*---------------------------------------------------------------------*/ + +int tda18271_charge_pump_source(struct dvb_frontend *fe, + enum tda18271_pll pll, int force) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + + int r_cp = (pll == TDA18271_CAL_PLL) ? R_EB7 : R_EB4; + + regs[r_cp] &= ~0x20; + regs[r_cp] |= ((force & 1) << 5); + + return tda18271_write_regs(fe, r_cp, 1); +} + +int tda18271_init_regs(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + + tda_dbg("initializing registers for device @ %d-%04x\n", + i2c_adapter_id(priv->i2c_props.adap), + priv->i2c_props.addr); + + /* initialize registers */ + switch (priv->id) { + case TDA18271HDC1: + regs[R_ID] = 0x83; + break; + case TDA18271HDC2: + regs[R_ID] = 0x84; + break; + }; + + regs[R_TM] = 0x08; + regs[R_PL] = 0x80; + regs[R_EP1] = 0xc6; + regs[R_EP2] = 0xdf; + regs[R_EP3] = 0x16; + regs[R_EP4] = 0x60; + regs[R_EP5] = 0x80; + regs[R_CPD] = 0x80; + regs[R_CD1] = 0x00; + regs[R_CD2] = 0x00; + regs[R_CD3] = 0x00; + regs[R_MPD] = 0x00; + regs[R_MD1] = 0x00; + regs[R_MD2] = 0x00; + regs[R_MD3] = 0x00; + + switch (priv->id) { + case TDA18271HDC1: + regs[R_EB1] = 0xff; + break; + case TDA18271HDC2: + regs[R_EB1] = 0xfc; + break; + }; + + regs[R_EB2] = 0x01; + regs[R_EB3] = 0x84; + regs[R_EB4] = 0x41; + regs[R_EB5] = 0x01; + regs[R_EB6] = 0x84; + regs[R_EB7] = 0x40; + regs[R_EB8] = 0x07; + regs[R_EB9] = 0x00; + regs[R_EB10] = 0x00; + regs[R_EB11] = 0x96; + + switch (priv->id) { + case TDA18271HDC1: + regs[R_EB12] = 0x0f; + break; + case TDA18271HDC2: + regs[R_EB12] = 0x33; + break; + }; + + regs[R_EB13] = 0xc1; + regs[R_EB14] = 0x00; + regs[R_EB15] = 0x8f; + regs[R_EB16] = 0x00; + regs[R_EB17] = 0x00; + + switch (priv->id) { + case TDA18271HDC1: + regs[R_EB18] = 0x00; + break; + case TDA18271HDC2: + regs[R_EB18] = 0x8c; + break; + }; + + regs[R_EB19] = 0x00; + regs[R_EB20] = 0x20; + + switch (priv->id) { + case TDA18271HDC1: + regs[R_EB21] = 0x33; + break; + case TDA18271HDC2: + regs[R_EB21] = 0xb3; + break; + }; + + regs[R_EB22] = 0x48; + regs[R_EB23] = 0xb0; + + tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS); + + /* setup agc1 gain */ + regs[R_EB17] = 0x00; + tda18271_write_regs(fe, R_EB17, 1); + regs[R_EB17] = 0x03; + tda18271_write_regs(fe, R_EB17, 1); + regs[R_EB17] = 0x43; + tda18271_write_regs(fe, R_EB17, 1); + regs[R_EB17] = 0x4c; + tda18271_write_regs(fe, R_EB17, 1); + + /* setup agc2 gain */ + if ((priv->id) == TDA18271HDC1) { + regs[R_EB20] = 0xa0; + tda18271_write_regs(fe, R_EB20, 1); + regs[R_EB20] = 0xa7; + tda18271_write_regs(fe, R_EB20, 1); + regs[R_EB20] = 0xe7; + tda18271_write_regs(fe, R_EB20, 1); + regs[R_EB20] = 0xec; + tda18271_write_regs(fe, R_EB20, 1); + } + + /* image rejection calibration */ + + /* low-band */ + regs[R_EP3] = 0x1f; + regs[R_EP4] = 0x66; + regs[R_EP5] = 0x81; + regs[R_CPD] = 0xcc; + regs[R_CD1] = 0x6c; + regs[R_CD2] = 0x00; + regs[R_CD3] = 0x00; + regs[R_MPD] = 0xcd; + regs[R_MD1] = 0x77; + regs[R_MD2] = 0x08; + regs[R_MD3] = 0x00; + + tda18271_write_regs(fe, R_EP3, 11); + + if ((priv->id) == TDA18271HDC2) { + /* main pll cp source on */ + tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 1); + msleep(1); + + /* main pll cp source off */ + tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 0); + } + + msleep(5); /* pll locking */ + + /* launch detector */ + tda18271_write_regs(fe, R_EP1, 1); + msleep(5); /* wanted low measurement */ + + regs[R_EP5] = 0x85; + regs[R_CPD] = 0xcb; + regs[R_CD1] = 0x66; + regs[R_CD2] = 0x70; + + tda18271_write_regs(fe, R_EP3, 7); + msleep(5); /* pll locking */ + + /* launch optimization algorithm */ + tda18271_write_regs(fe, R_EP2, 1); + msleep(30); /* image low optimization completion */ + + /* mid-band */ + regs[R_EP5] = 0x82; + regs[R_CPD] = 0xa8; + regs[R_CD2] = 0x00; + regs[R_MPD] = 0xa9; + regs[R_MD1] = 0x73; + regs[R_MD2] = 0x1a; + + tda18271_write_regs(fe, R_EP3, 11); + msleep(5); /* pll locking */ + + /* launch detector */ + tda18271_write_regs(fe, R_EP1, 1); + msleep(5); /* wanted mid measurement */ + + regs[R_EP5] = 0x86; + regs[R_CPD] = 0xa8; + regs[R_CD1] = 0x66; + regs[R_CD2] = 0xa0; + + tda18271_write_regs(fe, R_EP3, 7); + msleep(5); /* pll locking */ + + /* launch optimization algorithm */ + tda18271_write_regs(fe, R_EP2, 1); + msleep(30); /* image mid optimization completion */ + + /* high-band */ + regs[R_EP5] = 0x83; + regs[R_CPD] = 0x98; + regs[R_CD1] = 0x65; + regs[R_CD2] = 0x00; + regs[R_MPD] = 0x99; + regs[R_MD1] = 0x71; + regs[R_MD2] = 0xcd; + + tda18271_write_regs(fe, R_EP3, 11); + msleep(5); /* pll locking */ + + /* launch detector */ + tda18271_write_regs(fe, R_EP1, 1); + msleep(5); /* wanted high measurement */ + + regs[R_EP5] = 0x87; + regs[R_CD1] = 0x65; + regs[R_CD2] = 0x50; + + tda18271_write_regs(fe, R_EP3, 7); + msleep(5); /* pll locking */ + + /* launch optimization algorithm */ + tda18271_write_regs(fe, R_EP2, 1); + msleep(30); /* image high optimization completion */ + + /* return to normal mode */ + regs[R_EP4] = 0x64; + tda18271_write_regs(fe, R_EP4, 1); + + /* synchronize */ + tda18271_write_regs(fe, R_EP1, 1); + + return 0; +} + +/*---------------------------------------------------------------------*/ + +/* + * Standby modes, EP3 [7:5] + * + * | SM || SM_LT || SM_XT || mode description + * |=====\\=======\\=======\\=================================== + * | 0 || 0 || 0 || normal mode + * |-----||-------||-------||----------------------------------- + * | || || || standby mode w/ slave tuner output + * | 1 || 0 || 0 || & loop thru & xtal oscillator on + * |-----||-------||-------||----------------------------------- + * | 1 || 1 || 0 || standby mode w/ xtal oscillator on + * |-----||-------||-------||----------------------------------- + * | 1 || 1 || 1 || power off + * + */ + +int tda18271_set_standby_mode(struct dvb_frontend *fe, + int sm, int sm_lt, int sm_xt) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + + if (tda18271_debug & DBG_ADV) + tda_dbg("sm = %d, sm_lt = %d, sm_xt = %d\n", sm, sm_lt, sm_xt); + + regs[R_EP3] &= ~0xe0; /* clear sm, sm_lt, sm_xt */ + regs[R_EP3] |= (sm ? (1 << 7) : 0) | + (sm_lt ? (1 << 6) : 0) | + (sm_xt ? (1 << 5) : 0); + + return tda18271_write_regs(fe, R_EP3, 1); +} + +/*---------------------------------------------------------------------*/ + +int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq) +{ + /* sets main post divider & divider bytes, but does not write them */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 d, pd; + u32 div; + + int ret = tda18271_lookup_pll_map(fe, MAIN_PLL, &freq, &pd, &d); + if (tda_fail(ret)) + goto fail; + + regs[R_MPD] = (0x7f & pd); + + div = ((d * (freq / 1000)) << 7) / 125; + + regs[R_MD1] = 0x7f & (div >> 16); + regs[R_MD2] = 0xff & (div >> 8); + regs[R_MD3] = 0xff & div; +fail: + return ret; +} + +int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq) +{ + /* sets cal post divider & divider bytes, but does not write them */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 d, pd; + u32 div; + + int ret = tda18271_lookup_pll_map(fe, CAL_PLL, &freq, &pd, &d); + if (tda_fail(ret)) + goto fail; + + regs[R_CPD] = pd; + + div = ((d * (freq / 1000)) << 7) / 125; + + regs[R_CD1] = 0x7f & (div >> 16); + regs[R_CD2] = 0xff & (div >> 8); + regs[R_CD3] = 0xff & div; +fail: + return ret; +} + +/*---------------------------------------------------------------------*/ + +int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq) +{ + /* sets bp filter bits, but does not write them */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 val; + + int ret = tda18271_lookup_map(fe, BP_FILTER, freq, &val); + if (tda_fail(ret)) + goto fail; + + regs[R_EP1] &= ~0x07; /* clear bp filter bits */ + regs[R_EP1] |= (0x07 & val); +fail: + return ret; +} + +int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq) +{ + /* sets K & M bits, but does not write them */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 val; + + int ret = tda18271_lookup_map(fe, RF_CAL_KMCO, freq, &val); + if (tda_fail(ret)) + goto fail; + + regs[R_EB13] &= ~0x7c; /* clear k & m bits */ + regs[R_EB13] |= (0x7c & val); +fail: + return ret; +} + +int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq) +{ + /* sets rf band bits, but does not write them */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 val; + + int ret = tda18271_lookup_map(fe, RF_BAND, freq, &val); + if (tda_fail(ret)) + goto fail; + + regs[R_EP2] &= ~0xe0; /* clear rf band bits */ + regs[R_EP2] |= (0xe0 & (val << 5)); +fail: + return ret; +} + +int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq) +{ + /* sets gain taper bits, but does not write them */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 val; + + int ret = tda18271_lookup_map(fe, GAIN_TAPER, freq, &val); + if (tda_fail(ret)) + goto fail; + + regs[R_EP2] &= ~0x1f; /* clear gain taper bits */ + regs[R_EP2] |= (0x1f & val); +fail: + return ret; +} + +int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq) +{ + /* sets IR Meas bits, but does not write them */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 val; + + int ret = tda18271_lookup_map(fe, IR_MEASURE, freq, &val); + if (tda_fail(ret)) + goto fail; + + regs[R_EP5] &= ~0x07; + regs[R_EP5] |= (0x07 & val); +fail: + return ret; +} + +int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq) +{ + /* sets rf cal byte (RFC_Cprog), but does not write it */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 val; + + int ret = tda18271_lookup_map(fe, RF_CAL, freq, &val); + /* The TDA18271HD/C1 rf_cal map lookup is expected to go out of range + * for frequencies above 61.1 MHz. In these cases, the internal RF + * tracking filters calibration mechanism is used. + * + * There is no need to warn the user about this. + */ + if (ret < 0) + goto fail; + + regs[R_EB14] = val; +fail: + return ret; +} + +int _tda_printk(struct tda18271_priv *state, const char *level, + const char *func, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + int rtn; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + if (state) + rtn = printk("%s%s: [%d-%04x|%c] %pV", + level, func, i2c_adapter_id(state->i2c_props.adap), + state->i2c_props.addr, + (state->role == TDA18271_MASTER) ? 'M' : 'S', + &vaf); + else + rtn = printk("%s%s: %pV", level, func, &vaf); + + va_end(args); + + return rtn; +} diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c new file mode 100644 index 000000000000..2e67f4459904 --- /dev/null +++ b/drivers/media/tuners/tda18271-fe.c @@ -0,0 +1,1345 @@ +/* + tda18271-fe.c - driver for the Philips / NXP TDA18271 silicon tuner + + Copyright (C) 2007, 2008 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. +*/ + +#include +#include +#include "tda18271-priv.h" + +int tda18271_debug; +module_param_named(debug, tda18271_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debug level " + "(info=1, map=2, reg=4, adv=8, cal=16 (or-able))"); + +static int tda18271_cal_on_startup = -1; +module_param_named(cal, tda18271_cal_on_startup, int, 0644); +MODULE_PARM_DESC(cal, "perform RF tracking filter calibration on startup"); + +static DEFINE_MUTEX(tda18271_list_mutex); +static LIST_HEAD(hybrid_tuner_instance_list); + +/*---------------------------------------------------------------------*/ + +static int tda18271_toggle_output(struct dvb_frontend *fe, int standby) +{ + struct tda18271_priv *priv = fe->tuner_priv; + + int ret = tda18271_set_standby_mode(fe, standby ? 1 : 0, + priv->output_opt & TDA18271_OUTPUT_LT_OFF ? 1 : 0, + priv->output_opt & TDA18271_OUTPUT_XT_OFF ? 1 : 0); + + if (tda_fail(ret)) + goto fail; + + tda_dbg("%s mode: xtal oscillator %s, slave tuner loop thru %s\n", + standby ? "standby" : "active", + priv->output_opt & TDA18271_OUTPUT_XT_OFF ? "off" : "on", + priv->output_opt & TDA18271_OUTPUT_LT_OFF ? "off" : "on"); +fail: + return ret; +} + +/*---------------------------------------------------------------------*/ + +static inline int charge_pump_source(struct dvb_frontend *fe, int force) +{ + struct tda18271_priv *priv = fe->tuner_priv; + return tda18271_charge_pump_source(fe, + (priv->role == TDA18271_SLAVE) ? + TDA18271_CAL_PLL : + TDA18271_MAIN_PLL, force); +} + +static inline void tda18271_set_if_notch(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + + switch (priv->mode) { + case TDA18271_ANALOG: + regs[R_MPD] &= ~0x80; /* IF notch = 0 */ + break; + case TDA18271_DIGITAL: + regs[R_MPD] |= 0x80; /* IF notch = 1 */ + break; + } +} + +static int tda18271_channel_configuration(struct dvb_frontend *fe, + struct tda18271_std_map_item *map, + u32 freq, u32 bw) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + int ret; + u32 N; + + /* update TV broadcast parameters */ + + /* set standard */ + regs[R_EP3] &= ~0x1f; /* clear std bits */ + regs[R_EP3] |= (map->agc_mode << 3) | map->std; + + if (priv->id == TDA18271HDC2) { + /* set rfagc to high speed mode */ + regs[R_EP3] &= ~0x04; + } + + /* set cal mode to normal */ + regs[R_EP4] &= ~0x03; + + /* update IF output level */ + regs[R_EP4] &= ~0x1c; /* clear if level bits */ + regs[R_EP4] |= (map->if_lvl << 2); + + /* update FM_RFn */ + regs[R_EP4] &= ~0x80; + regs[R_EP4] |= map->fm_rfn << 7; + + /* update rf top / if top */ + regs[R_EB22] = 0x00; + regs[R_EB22] |= map->rfagc_top; + ret = tda18271_write_regs(fe, R_EB22, 1); + if (tda_fail(ret)) + goto fail; + + /* --------------------------------------------------------------- */ + + /* disable Power Level Indicator */ + regs[R_EP1] |= 0x40; + + /* make sure thermometer is off */ + regs[R_TM] &= ~0x10; + + /* frequency dependent parameters */ + + tda18271_calc_ir_measure(fe, &freq); + + tda18271_calc_bp_filter(fe, &freq); + + tda18271_calc_rf_band(fe, &freq); + + tda18271_calc_gain_taper(fe, &freq); + + /* --------------------------------------------------------------- */ + + /* dual tuner and agc1 extra configuration */ + + switch (priv->role) { + case TDA18271_MASTER: + regs[R_EB1] |= 0x04; /* main vco */ + break; + case TDA18271_SLAVE: + regs[R_EB1] &= ~0x04; /* cal vco */ + break; + } + + /* agc1 always active */ + regs[R_EB1] &= ~0x02; + + /* agc1 has priority on agc2 */ + regs[R_EB1] &= ~0x01; + + ret = tda18271_write_regs(fe, R_EB1, 1); + if (tda_fail(ret)) + goto fail; + + /* --------------------------------------------------------------- */ + + N = map->if_freq * 1000 + freq; + + switch (priv->role) { + case TDA18271_MASTER: + tda18271_calc_main_pll(fe, N); + tda18271_set_if_notch(fe); + tda18271_write_regs(fe, R_MPD, 4); + break; + case TDA18271_SLAVE: + tda18271_calc_cal_pll(fe, N); + tda18271_write_regs(fe, R_CPD, 4); + + regs[R_MPD] = regs[R_CPD] & 0x7f; + tda18271_set_if_notch(fe); + tda18271_write_regs(fe, R_MPD, 1); + break; + } + + ret = tda18271_write_regs(fe, R_TM, 7); + if (tda_fail(ret)) + goto fail; + + /* force charge pump source */ + charge_pump_source(fe, 1); + + msleep(1); + + /* return pll to normal operation */ + charge_pump_source(fe, 0); + + msleep(20); + + if (priv->id == TDA18271HDC2) { + /* set rfagc to normal speed mode */ + if (map->fm_rfn) + regs[R_EP3] &= ~0x04; + else + regs[R_EP3] |= 0x04; + ret = tda18271_write_regs(fe, R_EP3, 1); + } +fail: + return ret; +} + +static int tda18271_read_thermometer(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + int tm; + + /* switch thermometer on */ + regs[R_TM] |= 0x10; + tda18271_write_regs(fe, R_TM, 1); + + /* read thermometer info */ + tda18271_read_regs(fe); + + if ((((regs[R_TM] & 0x0f) == 0x00) && ((regs[R_TM] & 0x20) == 0x20)) || + (((regs[R_TM] & 0x0f) == 0x08) && ((regs[R_TM] & 0x20) == 0x00))) { + + if ((regs[R_TM] & 0x20) == 0x20) + regs[R_TM] &= ~0x20; + else + regs[R_TM] |= 0x20; + + tda18271_write_regs(fe, R_TM, 1); + + msleep(10); /* temperature sensing */ + + /* read thermometer info */ + tda18271_read_regs(fe); + } + + tm = tda18271_lookup_thermometer(fe); + + /* switch thermometer off */ + regs[R_TM] &= ~0x10; + tda18271_write_regs(fe, R_TM, 1); + + /* set CAL mode to normal */ + regs[R_EP4] &= ~0x03; + tda18271_write_regs(fe, R_EP4, 1); + + return tm; +} + +/* ------------------------------------------------------------------ */ + +static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, + u32 freq) +{ + struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; + unsigned char *regs = priv->tda18271_regs; + int i, ret; + u8 tm_current, dc_over_dt, rf_tab; + s32 rfcal_comp, approx; + + /* power up */ + ret = tda18271_set_standby_mode(fe, 0, 0, 0); + if (tda_fail(ret)) + goto fail; + + /* read die current temperature */ + tm_current = tda18271_read_thermometer(fe); + + /* frequency dependent parameters */ + + tda18271_calc_rf_cal(fe, &freq); + rf_tab = regs[R_EB14]; + + i = tda18271_lookup_rf_band(fe, &freq, NULL); + if (tda_fail(i)) + return i; + + if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) { + approx = map[i].rf_a1 * (s32)(freq / 1000 - map[i].rf1) + + map[i].rf_b1 + rf_tab; + } else { + approx = map[i].rf_a2 * (s32)(freq / 1000 - map[i].rf2) + + map[i].rf_b2 + rf_tab; + } + + if (approx < 0) + approx = 0; + if (approx > 255) + approx = 255; + + tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt); + + /* calculate temperature compensation */ + rfcal_comp = dc_over_dt * (s32)(tm_current - priv->tm_rfcal) / 1000; + + regs[R_EB14] = (unsigned char)(approx + rfcal_comp); + ret = tda18271_write_regs(fe, R_EB14, 1); +fail: + return ret; +} + +static int tda18271_por(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + int ret; + + /* power up detector 1 */ + regs[R_EB12] &= ~0x20; + ret = tda18271_write_regs(fe, R_EB12, 1); + if (tda_fail(ret)) + goto fail; + + regs[R_EB18] &= ~0x80; /* turn agc1 loop on */ + regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ + ret = tda18271_write_regs(fe, R_EB18, 1); + if (tda_fail(ret)) + goto fail; + + regs[R_EB21] |= 0x03; /* set agc2_gain to -6 dB */ + + /* POR mode */ + ret = tda18271_set_standby_mode(fe, 1, 0, 0); + if (tda_fail(ret)) + goto fail; + + /* disable 1.5 MHz low pass filter */ + regs[R_EB23] &= ~0x04; /* forcelp_fc2_en = 0 */ + regs[R_EB23] &= ~0x02; /* XXX: lp_fc[2] = 0 */ + ret = tda18271_write_regs(fe, R_EB21, 3); +fail: + return ret; +} + +static int tda18271_calibrate_rf(struct dvb_frontend *fe, u32 freq) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u32 N; + + /* set CAL mode to normal */ + regs[R_EP4] &= ~0x03; + tda18271_write_regs(fe, R_EP4, 1); + + /* switch off agc1 */ + regs[R_EP3] |= 0x40; /* sm_lt = 1 */ + + regs[R_EB18] |= 0x03; /* set agc1_gain to 15 dB */ + tda18271_write_regs(fe, R_EB18, 1); + + /* frequency dependent parameters */ + + tda18271_calc_bp_filter(fe, &freq); + tda18271_calc_gain_taper(fe, &freq); + tda18271_calc_rf_band(fe, &freq); + tda18271_calc_km(fe, &freq); + + tda18271_write_regs(fe, R_EP1, 3); + tda18271_write_regs(fe, R_EB13, 1); + + /* main pll charge pump source */ + tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 1); + + /* cal pll charge pump source */ + tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 1); + + /* force dcdc converter to 0 V */ + regs[R_EB14] = 0x00; + tda18271_write_regs(fe, R_EB14, 1); + + /* disable plls lock */ + regs[R_EB20] &= ~0x20; + tda18271_write_regs(fe, R_EB20, 1); + + /* set CAL mode to RF tracking filter calibration */ + regs[R_EP4] |= 0x03; + tda18271_write_regs(fe, R_EP4, 2); + + /* --------------------------------------------------------------- */ + + /* set the internal calibration signal */ + N = freq; + + tda18271_calc_cal_pll(fe, N); + tda18271_write_regs(fe, R_CPD, 4); + + /* downconvert internal calibration */ + N += 1000000; + + tda18271_calc_main_pll(fe, N); + tda18271_write_regs(fe, R_MPD, 4); + + msleep(5); + + tda18271_write_regs(fe, R_EP2, 1); + tda18271_write_regs(fe, R_EP1, 1); + tda18271_write_regs(fe, R_EP2, 1); + tda18271_write_regs(fe, R_EP1, 1); + + /* --------------------------------------------------------------- */ + + /* normal operation for the main pll */ + tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 0); + + /* normal operation for the cal pll */ + tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 0); + + msleep(10); /* plls locking */ + + /* launch the rf tracking filters calibration */ + regs[R_EB20] |= 0x20; + tda18271_write_regs(fe, R_EB20, 1); + + msleep(60); /* calibration */ + + /* --------------------------------------------------------------- */ + + /* set CAL mode to normal */ + regs[R_EP4] &= ~0x03; + + /* switch on agc1 */ + regs[R_EP3] &= ~0x40; /* sm_lt = 0 */ + + regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ + tda18271_write_regs(fe, R_EB18, 1); + + tda18271_write_regs(fe, R_EP3, 2); + + /* synchronization */ + tda18271_write_regs(fe, R_EP1, 1); + + /* get calibration result */ + tda18271_read_extended(fe); + + return regs[R_EB14]; +} + +static int tda18271_powerscan(struct dvb_frontend *fe, + u32 *freq_in, u32 *freq_out) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + int sgn, bcal, count, wait, ret; + u8 cid_target; + u16 count_limit; + u32 freq; + + freq = *freq_in; + + tda18271_calc_rf_band(fe, &freq); + tda18271_calc_rf_cal(fe, &freq); + tda18271_calc_gain_taper(fe, &freq); + tda18271_lookup_cid_target(fe, &freq, &cid_target, &count_limit); + + tda18271_write_regs(fe, R_EP2, 1); + tda18271_write_regs(fe, R_EB14, 1); + + /* downconvert frequency */ + freq += 1000000; + + tda18271_calc_main_pll(fe, freq); + tda18271_write_regs(fe, R_MPD, 4); + + msleep(5); /* pll locking */ + + /* detection mode */ + regs[R_EP4] &= ~0x03; + regs[R_EP4] |= 0x01; + tda18271_write_regs(fe, R_EP4, 1); + + /* launch power detection measurement */ + tda18271_write_regs(fe, R_EP2, 1); + + /* read power detection info, stored in EB10 */ + ret = tda18271_read_extended(fe); + if (tda_fail(ret)) + return ret; + + /* algorithm initialization */ + sgn = 1; + *freq_out = *freq_in; + bcal = 0; + count = 0; + wait = false; + + while ((regs[R_EB10] & 0x3f) < cid_target) { + /* downconvert updated freq to 1 MHz */ + freq = *freq_in + (sgn * count) + 1000000; + + tda18271_calc_main_pll(fe, freq); + tda18271_write_regs(fe, R_MPD, 4); + + if (wait) { + msleep(5); /* pll locking */ + wait = false; + } else + udelay(100); /* pll locking */ + + /* launch power detection measurement */ + tda18271_write_regs(fe, R_EP2, 1); + + /* read power detection info, stored in EB10 */ + ret = tda18271_read_extended(fe); + if (tda_fail(ret)) + return ret; + + count += 200; + + if (count <= count_limit) + continue; + + if (sgn <= 0) + break; + + sgn = -1 * sgn; + count = 200; + wait = true; + } + + if ((regs[R_EB10] & 0x3f) >= cid_target) { + bcal = 1; + *freq_out = freq - 1000000; + } else + bcal = 0; + + tda_cal("bcal = %d, freq_in = %d, freq_out = %d (freq = %d)\n", + bcal, *freq_in, *freq_out, freq); + + return bcal; +} + +static int tda18271_powerscan_init(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + int ret; + + /* set standard to digital */ + regs[R_EP3] &= ~0x1f; /* clear std bits */ + regs[R_EP3] |= 0x12; + + /* set cal mode to normal */ + regs[R_EP4] &= ~0x03; + + /* update IF output level */ + regs[R_EP4] &= ~0x1c; /* clear if level bits */ + + ret = tda18271_write_regs(fe, R_EP3, 2); + if (tda_fail(ret)) + goto fail; + + regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ + ret = tda18271_write_regs(fe, R_EB18, 1); + if (tda_fail(ret)) + goto fail; + + regs[R_EB21] &= ~0x03; /* set agc2_gain to -15 dB */ + + /* 1.5 MHz low pass filter */ + regs[R_EB23] |= 0x04; /* forcelp_fc2_en = 1 */ + regs[R_EB23] |= 0x02; /* lp_fc[2] = 1 */ + + ret = tda18271_write_regs(fe, R_EB21, 3); +fail: + return ret; +} + +static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) +{ + struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; + unsigned char *regs = priv->tda18271_regs; + int bcal, rf, i; + s32 divisor, dividend; +#define RF1 0 +#define RF2 1 +#define RF3 2 + u32 rf_default[3]; + u32 rf_freq[3]; + s32 prog_cal[3]; + s32 prog_tab[3]; + + i = tda18271_lookup_rf_band(fe, &freq, NULL); + + if (tda_fail(i)) + return i; + + rf_default[RF1] = 1000 * map[i].rf1_def; + rf_default[RF2] = 1000 * map[i].rf2_def; + rf_default[RF3] = 1000 * map[i].rf3_def; + + for (rf = RF1; rf <= RF3; rf++) { + if (0 == rf_default[rf]) + return 0; + tda_cal("freq = %d, rf = %d\n", freq, rf); + + /* look for optimized calibration frequency */ + bcal = tda18271_powerscan(fe, &rf_default[rf], &rf_freq[rf]); + if (tda_fail(bcal)) + return bcal; + + tda18271_calc_rf_cal(fe, &rf_freq[rf]); + prog_tab[rf] = (s32)regs[R_EB14]; + + if (1 == bcal) + prog_cal[rf] = + (s32)tda18271_calibrate_rf(fe, rf_freq[rf]); + else + prog_cal[rf] = prog_tab[rf]; + + switch (rf) { + case RF1: + map[i].rf_a1 = 0; + map[i].rf_b1 = (prog_cal[RF1] - prog_tab[RF1]); + map[i].rf1 = rf_freq[RF1] / 1000; + break; + case RF2: + dividend = (prog_cal[RF2] - prog_tab[RF2] - + prog_cal[RF1] + prog_tab[RF1]); + divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000; + map[i].rf_a1 = (dividend / divisor); + map[i].rf2 = rf_freq[RF2] / 1000; + break; + case RF3: + dividend = (prog_cal[RF3] - prog_tab[RF3] - + prog_cal[RF2] + prog_tab[RF2]); + divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000; + map[i].rf_a2 = (dividend / divisor); + map[i].rf_b2 = (prog_cal[RF2] - prog_tab[RF2]); + map[i].rf3 = rf_freq[RF3] / 1000; + break; + default: + BUG(); + } + } + + return 0; +} + +static int tda18271_calc_rf_filter_curve(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned int i; + int ret; + + tda_info("tda18271: performing RF tracking filter calibration\n"); + + /* wait for die temperature stabilization */ + msleep(200); + + ret = tda18271_powerscan_init(fe); + if (tda_fail(ret)) + goto fail; + + /* rf band calibration */ + for (i = 0; priv->rf_cal_state[i].rfmax != 0; i++) { + ret = + tda18271_rf_tracking_filters_init(fe, 1000 * + priv->rf_cal_state[i].rfmax); + if (tda_fail(ret)) + goto fail; + } + + priv->tm_rfcal = tda18271_read_thermometer(fe); +fail: + return ret; +} + +/* ------------------------------------------------------------------ */ + +static int tda18271c2_rf_cal_init(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + int ret; + + /* test RF_CAL_OK to see if we need init */ + if ((regs[R_EP1] & 0x10) == 0) + priv->cal_initialized = false; + + if (priv->cal_initialized) + return 0; + + ret = tda18271_calc_rf_filter_curve(fe); + if (tda_fail(ret)) + goto fail; + + ret = tda18271_por(fe); + if (tda_fail(ret)) + goto fail; + + tda_info("tda18271: RF tracking filter calibration complete\n"); + + priv->cal_initialized = true; + goto end; +fail: + tda_info("tda18271: RF tracking filter calibration failed!\n"); +end: + return ret; +} + +static int tda18271c1_rf_tracking_filter_calibration(struct dvb_frontend *fe, + u32 freq, u32 bw) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + int ret; + u32 N = 0; + + /* calculate bp filter */ + tda18271_calc_bp_filter(fe, &freq); + tda18271_write_regs(fe, R_EP1, 1); + + regs[R_EB4] &= 0x07; + regs[R_EB4] |= 0x60; + tda18271_write_regs(fe, R_EB4, 1); + + regs[R_EB7] = 0x60; + tda18271_write_regs(fe, R_EB7, 1); + + regs[R_EB14] = 0x00; + tda18271_write_regs(fe, R_EB14, 1); + + regs[R_EB20] = 0xcc; + tda18271_write_regs(fe, R_EB20, 1); + + /* set cal mode to RF tracking filter calibration */ + regs[R_EP4] |= 0x03; + + /* calculate cal pll */ + + switch (priv->mode) { + case TDA18271_ANALOG: + N = freq - 1250000; + break; + case TDA18271_DIGITAL: + N = freq + bw / 2; + break; + } + + tda18271_calc_cal_pll(fe, N); + + /* calculate main pll */ + + switch (priv->mode) { + case TDA18271_ANALOG: + N = freq - 250000; + break; + case TDA18271_DIGITAL: + N = freq + bw / 2 + 1000000; + break; + } + + tda18271_calc_main_pll(fe, N); + + ret = tda18271_write_regs(fe, R_EP3, 11); + if (tda_fail(ret)) + return ret; + + msleep(5); /* RF tracking filter calibration initialization */ + + /* search for K,M,CO for RF calibration */ + tda18271_calc_km(fe, &freq); + tda18271_write_regs(fe, R_EB13, 1); + + /* search for rf band */ + tda18271_calc_rf_band(fe, &freq); + + /* search for gain taper */ + tda18271_calc_gain_taper(fe, &freq); + + tda18271_write_regs(fe, R_EP2, 1); + tda18271_write_regs(fe, R_EP1, 1); + tda18271_write_regs(fe, R_EP2, 1); + tda18271_write_regs(fe, R_EP1, 1); + + regs[R_EB4] &= 0x07; + regs[R_EB4] |= 0x40; + tda18271_write_regs(fe, R_EB4, 1); + + regs[R_EB7] = 0x40; + tda18271_write_regs(fe, R_EB7, 1); + msleep(10); /* pll locking */ + + regs[R_EB20] = 0xec; + tda18271_write_regs(fe, R_EB20, 1); + msleep(60); /* RF tracking filter calibration completion */ + + regs[R_EP4] &= ~0x03; /* set cal mode to normal */ + tda18271_write_regs(fe, R_EP4, 1); + + tda18271_write_regs(fe, R_EP1, 1); + + /* RF tracking filter correction for VHF_Low band */ + if (0 == tda18271_calc_rf_cal(fe, &freq)) + tda18271_write_regs(fe, R_EB14, 1); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int tda18271_ir_cal_init(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + int ret; + + ret = tda18271_read_regs(fe); + if (tda_fail(ret)) + goto fail; + + /* test IR_CAL_OK to see if we need init */ + if ((regs[R_EP1] & 0x08) == 0) + ret = tda18271_init_regs(fe); +fail: + return ret; +} + +static int tda18271_init(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + int ret; + + mutex_lock(&priv->lock); + + /* full power up */ + ret = tda18271_set_standby_mode(fe, 0, 0, 0); + if (tda_fail(ret)) + goto fail; + + /* initialization */ + ret = tda18271_ir_cal_init(fe); + if (tda_fail(ret)) + goto fail; + + if (priv->id == TDA18271HDC2) + tda18271c2_rf_cal_init(fe); +fail: + mutex_unlock(&priv->lock); + + return ret; +} + +static int tda18271_sleep(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + int ret; + + mutex_lock(&priv->lock); + + /* enter standby mode, with required output features enabled */ + ret = tda18271_toggle_output(fe, 1); + + mutex_unlock(&priv->lock); + + return ret; +} + +/* ------------------------------------------------------------------ */ + +static int tda18271_agc(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + int ret = 0; + + switch (priv->config) { + case 0: + /* no external agc configuration required */ + if (tda18271_debug & DBG_ADV) + tda_dbg("no agc configuration provided\n"); + break; + case 3: + /* switch with GPIO of saa713x */ + tda_dbg("invoking callback\n"); + if (fe->callback) + ret = fe->callback(priv->i2c_props.adap->algo_data, + DVB_FRONTEND_COMPONENT_TUNER, + TDA18271_CALLBACK_CMD_AGC_ENABLE, + priv->mode); + break; + case 1: + case 2: + default: + /* n/a - currently not supported */ + tda_err("unsupported configuration: %d\n", priv->config); + ret = -EINVAL; + break; + } + return ret; +} + +static int tda18271_tune(struct dvb_frontend *fe, + struct tda18271_std_map_item *map, u32 freq, u32 bw) +{ + struct tda18271_priv *priv = fe->tuner_priv; + int ret; + + tda_dbg("freq = %d, ifc = %d, bw = %d, agc_mode = %d, std = %d\n", + freq, map->if_freq, bw, map->agc_mode, map->std); + + ret = tda18271_agc(fe); + if (tda_fail(ret)) + tda_warn("failed to configure agc\n"); + + ret = tda18271_init(fe); + if (tda_fail(ret)) + goto fail; + + mutex_lock(&priv->lock); + + switch (priv->id) { + case TDA18271HDC1: + tda18271c1_rf_tracking_filter_calibration(fe, freq, bw); + break; + case TDA18271HDC2: + tda18271c2_rf_tracking_filters_correction(fe, freq); + break; + } + ret = tda18271_channel_configuration(fe, map, freq, bw); + + mutex_unlock(&priv->lock); +fail: + return ret; +} + +/* ------------------------------------------------------------------ */ + +static int tda18271_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; + u32 freq = c->frequency; + struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_std_map *std_map = &priv->std; + struct tda18271_std_map_item *map; + int ret; + + priv->mode = TDA18271_DIGITAL; + + switch (delsys) { + case SYS_ATSC: + map = &std_map->atsc_6; + bw = 6000000; + break; + case SYS_ISDBT: + case SYS_DVBT: + case SYS_DVBT2: + if (bw <= 6000000) { + map = &std_map->dvbt_6; + } else if (bw <= 7000000) { + map = &std_map->dvbt_7; + } else { + map = &std_map->dvbt_8; + } + break; + case SYS_DVBC_ANNEX_B: + bw = 6000000; + /* falltrough */ + case SYS_DVBC_ANNEX_A: + case SYS_DVBC_ANNEX_C: + if (bw <= 6000000) { + map = &std_map->qam_6; + } else if (bw <= 7000000) { + map = &std_map->qam_7; + } else { + map = &std_map->qam_8; + } + break; + default: + tda_warn("modulation type not supported!\n"); + return -EINVAL; + } + + /* When tuning digital, the analog demod must be tri-stated */ + if (fe->ops.analog_ops.standby) + fe->ops.analog_ops.standby(fe); + + ret = tda18271_tune(fe, map, freq, bw); + + if (tda_fail(ret)) + goto fail; + + priv->if_freq = map->if_freq; + priv->frequency = freq; + priv->bandwidth = bw; +fail: + return ret; +} + +static int tda18271_set_analog_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_std_map *std_map = &priv->std; + struct tda18271_std_map_item *map; + char *mode; + int ret; + u32 freq = params->frequency * 125 * + ((params->mode == V4L2_TUNER_RADIO) ? 1 : 1000) / 2; + + priv->mode = TDA18271_ANALOG; + + if (params->mode == V4L2_TUNER_RADIO) { + map = &std_map->fm_radio; + mode = "fm"; + } else if (params->std & V4L2_STD_MN) { + map = &std_map->atv_mn; + mode = "MN"; + } else if (params->std & V4L2_STD_B) { + map = &std_map->atv_b; + mode = "B"; + } else if (params->std & V4L2_STD_GH) { + map = &std_map->atv_gh; + mode = "GH"; + } else if (params->std & V4L2_STD_PAL_I) { + map = &std_map->atv_i; + mode = "I"; + } else if (params->std & V4L2_STD_DK) { + map = &std_map->atv_dk; + mode = "DK"; + } else if (params->std & V4L2_STD_SECAM_L) { + map = &std_map->atv_l; + mode = "L"; + } else if (params->std & V4L2_STD_SECAM_LC) { + map = &std_map->atv_lc; + mode = "L'"; + } else { + map = &std_map->atv_i; + mode = "xx"; + } + + tda_dbg("setting tda18271 to system %s\n", mode); + + ret = tda18271_tune(fe, map, freq, 0); + + if (tda_fail(ret)) + goto fail; + + priv->if_freq = map->if_freq; + priv->frequency = freq; + priv->bandwidth = 0; +fail: + return ret; +} + +static int tda18271_release(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + + mutex_lock(&tda18271_list_mutex); + + if (priv) + hybrid_tuner_release_state(priv); + + mutex_unlock(&tda18271_list_mutex); + + fe->tuner_priv = NULL; + + return 0; +} + +static int tda18271_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tda18271_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int tda18271_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct tda18271_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +static int tda18271_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tda18271_priv *priv = fe->tuner_priv; + *frequency = (u32)priv->if_freq * 1000; + return 0; +} + +/* ------------------------------------------------------------------ */ + +#define tda18271_update_std(std_cfg, name) do { \ + if (map->std_cfg.if_freq + \ + map->std_cfg.agc_mode + map->std_cfg.std + \ + map->std_cfg.if_lvl + map->std_cfg.rfagc_top > 0) { \ + tda_dbg("Using custom std config for %s\n", name); \ + memcpy(&std->std_cfg, &map->std_cfg, \ + sizeof(struct tda18271_std_map_item)); \ + } } while (0) + +#define tda18271_dump_std_item(std_cfg, name) do { \ + tda_dbg("(%s) if_freq = %d, agc_mode = %d, std = %d, " \ + "if_lvl = %d, rfagc_top = 0x%02x\n", \ + name, std->std_cfg.if_freq, \ + std->std_cfg.agc_mode, std->std_cfg.std, \ + std->std_cfg.if_lvl, std->std_cfg.rfagc_top); \ + } while (0) + +static int tda18271_dump_std_map(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_std_map *std = &priv->std; + + tda_dbg("========== STANDARD MAP SETTINGS ==========\n"); + tda18271_dump_std_item(fm_radio, " fm "); + tda18271_dump_std_item(atv_b, "atv b "); + tda18271_dump_std_item(atv_dk, "atv dk"); + tda18271_dump_std_item(atv_gh, "atv gh"); + tda18271_dump_std_item(atv_i, "atv i "); + tda18271_dump_std_item(atv_l, "atv l "); + tda18271_dump_std_item(atv_lc, "atv l'"); + tda18271_dump_std_item(atv_mn, "atv mn"); + tda18271_dump_std_item(atsc_6, "atsc 6"); + tda18271_dump_std_item(dvbt_6, "dvbt 6"); + tda18271_dump_std_item(dvbt_7, "dvbt 7"); + tda18271_dump_std_item(dvbt_8, "dvbt 8"); + tda18271_dump_std_item(qam_6, "qam 6 "); + tda18271_dump_std_item(qam_8, "qam 8 "); + + return 0; +} + +static int tda18271_update_std_map(struct dvb_frontend *fe, + struct tda18271_std_map *map) +{ + struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_std_map *std = &priv->std; + + if (!map) + return -EINVAL; + + tda18271_update_std(fm_radio, "fm"); + tda18271_update_std(atv_b, "atv b"); + tda18271_update_std(atv_dk, "atv dk"); + tda18271_update_std(atv_gh, "atv gh"); + tda18271_update_std(atv_i, "atv i"); + tda18271_update_std(atv_l, "atv l"); + tda18271_update_std(atv_lc, "atv l'"); + tda18271_update_std(atv_mn, "atv mn"); + tda18271_update_std(atsc_6, "atsc 6"); + tda18271_update_std(dvbt_6, "dvbt 6"); + tda18271_update_std(dvbt_7, "dvbt 7"); + tda18271_update_std(dvbt_8, "dvbt 8"); + tda18271_update_std(qam_6, "qam 6"); + tda18271_update_std(qam_8, "qam 8"); + + return 0; +} + +static int tda18271_get_id(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + char *name; + + mutex_lock(&priv->lock); + tda18271_read_regs(fe); + mutex_unlock(&priv->lock); + + switch (regs[R_ID] & 0x7f) { + case 3: + name = "TDA18271HD/C1"; + priv->id = TDA18271HDC1; + break; + case 4: + name = "TDA18271HD/C2"; + priv->id = TDA18271HDC2; + break; + default: + tda_info("Unknown device (%i) detected @ %d-%04x, device not supported.\n", + regs[R_ID], i2c_adapter_id(priv->i2c_props.adap), + priv->i2c_props.addr); + return -EINVAL; + } + + tda_info("%s detected @ %d-%04x\n", name, + i2c_adapter_id(priv->i2c_props.adap), priv->i2c_props.addr); + + return 0; +} + +static int tda18271_setup_configuration(struct dvb_frontend *fe, + struct tda18271_config *cfg) +{ + struct tda18271_priv *priv = fe->tuner_priv; + + priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; + priv->role = (cfg) ? cfg->role : TDA18271_MASTER; + priv->config = (cfg) ? cfg->config : 0; + priv->small_i2c = (cfg) ? + cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT; + priv->output_opt = (cfg) ? + cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON; + + return 0; +} + +static inline int tda18271_need_cal_on_startup(struct tda18271_config *cfg) +{ + /* tda18271_cal_on_startup == -1 when cal module option is unset */ + return ((tda18271_cal_on_startup == -1) ? + /* honor configuration setting */ + ((cfg) && (cfg->rf_cal_on_startup)) : + /* module option overrides configuration setting */ + (tda18271_cal_on_startup)) ? 1 : 0; +} + +static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg) +{ + struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg; + + tda18271_setup_configuration(fe, cfg); + + if (tda18271_need_cal_on_startup(cfg)) + tda18271_init(fe); + + /* override default std map with values in config struct */ + if ((cfg) && (cfg->std_map)) + tda18271_update_std_map(fe, cfg->std_map); + + return 0; +} + +static const struct dvb_tuner_ops tda18271_tuner_ops = { + .info = { + .name = "NXP TDA18271HD", + .frequency_min = 45000000, + .frequency_max = 864000000, + .frequency_step = 62500 + }, + .init = tda18271_init, + .sleep = tda18271_sleep, + .set_params = tda18271_set_params, + .set_analog_params = tda18271_set_analog_params, + .release = tda18271_release, + .set_config = tda18271_set_config, + .get_frequency = tda18271_get_frequency, + .get_bandwidth = tda18271_get_bandwidth, + .get_if_frequency = tda18271_get_if_frequency, +}; + +struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, + struct i2c_adapter *i2c, + struct tda18271_config *cfg) +{ + struct tda18271_priv *priv = NULL; + int instance, ret; + + mutex_lock(&tda18271_list_mutex); + + instance = hybrid_tuner_request_state(struct tda18271_priv, priv, + hybrid_tuner_instance_list, + i2c, addr, "tda18271"); + switch (instance) { + case 0: + goto fail; + case 1: + /* new tuner instance */ + fe->tuner_priv = priv; + + tda18271_setup_configuration(fe, cfg); + + priv->cal_initialized = false; + mutex_init(&priv->lock); + + ret = tda18271_get_id(fe); + if (tda_fail(ret)) + goto fail; + + ret = tda18271_assign_map_layout(fe); + if (tda_fail(ret)) + goto fail; + + mutex_lock(&priv->lock); + tda18271_init_regs(fe); + + if ((tda18271_need_cal_on_startup(cfg)) && + (priv->id == TDA18271HDC2)) + tda18271c2_rf_cal_init(fe); + + mutex_unlock(&priv->lock); + break; + default: + /* existing tuner instance */ + fe->tuner_priv = priv; + + /* allow dvb driver to override configuration settings */ + if (cfg) { + if (cfg->gate != TDA18271_GATE_ANALOG) + priv->gate = cfg->gate; + if (cfg->role) + priv->role = cfg->role; + if (cfg->config) + priv->config = cfg->config; + if (cfg->small_i2c) + priv->small_i2c = cfg->small_i2c; + if (cfg->output_opt) + priv->output_opt = cfg->output_opt; + if (cfg->std_map) + tda18271_update_std_map(fe, cfg->std_map); + } + if (tda18271_need_cal_on_startup(cfg)) + tda18271_init(fe); + break; + } + + /* override default std map with values in config struct */ + if ((cfg) && (cfg->std_map)) + tda18271_update_std_map(fe, cfg->std_map); + + mutex_unlock(&tda18271_list_mutex); + + memcpy(&fe->ops.tuner_ops, &tda18271_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + if (tda18271_debug & (DBG_MAP | DBG_ADV)) + tda18271_dump_std_map(fe); + + return fe; +fail: + mutex_unlock(&tda18271_list_mutex); + + tda18271_release(fe); + return NULL; +} +EXPORT_SYMBOL_GPL(tda18271_attach); +MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver"); +MODULE_AUTHOR("Michael Krufky "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.4"); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/tuners/tda18271-maps.c b/drivers/media/tuners/tda18271-maps.c new file mode 100644 index 000000000000..fb881c667c94 --- /dev/null +++ b/drivers/media/tuners/tda18271-maps.c @@ -0,0 +1,1317 @@ +/* + tda18271-maps.c - driver for the Philips / NXP TDA18271 silicon tuner + + Copyright (C) 2007, 2008 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. +*/ + +#include "tda18271-priv.h" + +struct tda18271_pll_map { + u32 lomax; + u8 pd; /* post div */ + u8 d; /* div */ +}; + +struct tda18271_map { + u32 rfmax; + u8 val; +}; + +/*---------------------------------------------------------------------*/ + +static struct tda18271_pll_map tda18271c1_main_pll[] = { + { .lomax = 32000, .pd = 0x5f, .d = 0xf0 }, + { .lomax = 35000, .pd = 0x5e, .d = 0xe0 }, + { .lomax = 37000, .pd = 0x5d, .d = 0xd0 }, + { .lomax = 41000, .pd = 0x5c, .d = 0xc0 }, + { .lomax = 44000, .pd = 0x5b, .d = 0xb0 }, + { .lomax = 49000, .pd = 0x5a, .d = 0xa0 }, + { .lomax = 54000, .pd = 0x59, .d = 0x90 }, + { .lomax = 61000, .pd = 0x58, .d = 0x80 }, + { .lomax = 65000, .pd = 0x4f, .d = 0x78 }, + { .lomax = 70000, .pd = 0x4e, .d = 0x70 }, + { .lomax = 75000, .pd = 0x4d, .d = 0x68 }, + { .lomax = 82000, .pd = 0x4c, .d = 0x60 }, + { .lomax = 89000, .pd = 0x4b, .d = 0x58 }, + { .lomax = 98000, .pd = 0x4a, .d = 0x50 }, + { .lomax = 109000, .pd = 0x49, .d = 0x48 }, + { .lomax = 123000, .pd = 0x48, .d = 0x40 }, + { .lomax = 131000, .pd = 0x3f, .d = 0x3c }, + { .lomax = 141000, .pd = 0x3e, .d = 0x38 }, + { .lomax = 151000, .pd = 0x3d, .d = 0x34 }, + { .lomax = 164000, .pd = 0x3c, .d = 0x30 }, + { .lomax = 179000, .pd = 0x3b, .d = 0x2c }, + { .lomax = 197000, .pd = 0x3a, .d = 0x28 }, + { .lomax = 219000, .pd = 0x39, .d = 0x24 }, + { .lomax = 246000, .pd = 0x38, .d = 0x20 }, + { .lomax = 263000, .pd = 0x2f, .d = 0x1e }, + { .lomax = 282000, .pd = 0x2e, .d = 0x1c }, + { .lomax = 303000, .pd = 0x2d, .d = 0x1a }, + { .lomax = 329000, .pd = 0x2c, .d = 0x18 }, + { .lomax = 359000, .pd = 0x2b, .d = 0x16 }, + { .lomax = 395000, .pd = 0x2a, .d = 0x14 }, + { .lomax = 438000, .pd = 0x29, .d = 0x12 }, + { .lomax = 493000, .pd = 0x28, .d = 0x10 }, + { .lomax = 526000, .pd = 0x1f, .d = 0x0f }, + { .lomax = 564000, .pd = 0x1e, .d = 0x0e }, + { .lomax = 607000, .pd = 0x1d, .d = 0x0d }, + { .lomax = 658000, .pd = 0x1c, .d = 0x0c }, + { .lomax = 718000, .pd = 0x1b, .d = 0x0b }, + { .lomax = 790000, .pd = 0x1a, .d = 0x0a }, + { .lomax = 877000, .pd = 0x19, .d = 0x09 }, + { .lomax = 987000, .pd = 0x18, .d = 0x08 }, + { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ +}; + +static struct tda18271_pll_map tda18271c2_main_pll[] = { + { .lomax = 33125, .pd = 0x57, .d = 0xf0 }, + { .lomax = 35500, .pd = 0x56, .d = 0xe0 }, + { .lomax = 38188, .pd = 0x55, .d = 0xd0 }, + { .lomax = 41375, .pd = 0x54, .d = 0xc0 }, + { .lomax = 45125, .pd = 0x53, .d = 0xb0 }, + { .lomax = 49688, .pd = 0x52, .d = 0xa0 }, + { .lomax = 55188, .pd = 0x51, .d = 0x90 }, + { .lomax = 62125, .pd = 0x50, .d = 0x80 }, + { .lomax = 66250, .pd = 0x47, .d = 0x78 }, + { .lomax = 71000, .pd = 0x46, .d = 0x70 }, + { .lomax = 76375, .pd = 0x45, .d = 0x68 }, + { .lomax = 82750, .pd = 0x44, .d = 0x60 }, + { .lomax = 90250, .pd = 0x43, .d = 0x58 }, + { .lomax = 99375, .pd = 0x42, .d = 0x50 }, + { .lomax = 110375, .pd = 0x41, .d = 0x48 }, + { .lomax = 124250, .pd = 0x40, .d = 0x40 }, + { .lomax = 132500, .pd = 0x37, .d = 0x3c }, + { .lomax = 142000, .pd = 0x36, .d = 0x38 }, + { .lomax = 152750, .pd = 0x35, .d = 0x34 }, + { .lomax = 165500, .pd = 0x34, .d = 0x30 }, + { .lomax = 180500, .pd = 0x33, .d = 0x2c }, + { .lomax = 198750, .pd = 0x32, .d = 0x28 }, + { .lomax = 220750, .pd = 0x31, .d = 0x24 }, + { .lomax = 248500, .pd = 0x30, .d = 0x20 }, + { .lomax = 265000, .pd = 0x27, .d = 0x1e }, + { .lomax = 284000, .pd = 0x26, .d = 0x1c }, + { .lomax = 305500, .pd = 0x25, .d = 0x1a }, + { .lomax = 331000, .pd = 0x24, .d = 0x18 }, + { .lomax = 361000, .pd = 0x23, .d = 0x16 }, + { .lomax = 397500, .pd = 0x22, .d = 0x14 }, + { .lomax = 441500, .pd = 0x21, .d = 0x12 }, + { .lomax = 497000, .pd = 0x20, .d = 0x10 }, + { .lomax = 530000, .pd = 0x17, .d = 0x0f }, + { .lomax = 568000, .pd = 0x16, .d = 0x0e }, + { .lomax = 611000, .pd = 0x15, .d = 0x0d }, + { .lomax = 662000, .pd = 0x14, .d = 0x0c }, + { .lomax = 722000, .pd = 0x13, .d = 0x0b }, + { .lomax = 795000, .pd = 0x12, .d = 0x0a }, + { .lomax = 883000, .pd = 0x11, .d = 0x09 }, + { .lomax = 994000, .pd = 0x10, .d = 0x08 }, + { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ +}; + +static struct tda18271_pll_map tda18271c1_cal_pll[] = { + { .lomax = 33000, .pd = 0xdd, .d = 0xd0 }, + { .lomax = 36000, .pd = 0xdc, .d = 0xc0 }, + { .lomax = 40000, .pd = 0xdb, .d = 0xb0 }, + { .lomax = 44000, .pd = 0xda, .d = 0xa0 }, + { .lomax = 49000, .pd = 0xd9, .d = 0x90 }, + { .lomax = 55000, .pd = 0xd8, .d = 0x80 }, + { .lomax = 63000, .pd = 0xd3, .d = 0x70 }, + { .lomax = 67000, .pd = 0xcd, .d = 0x68 }, + { .lomax = 73000, .pd = 0xcc, .d = 0x60 }, + { .lomax = 80000, .pd = 0xcb, .d = 0x58 }, + { .lomax = 88000, .pd = 0xca, .d = 0x50 }, + { .lomax = 98000, .pd = 0xc9, .d = 0x48 }, + { .lomax = 110000, .pd = 0xc8, .d = 0x40 }, + { .lomax = 126000, .pd = 0xc3, .d = 0x38 }, + { .lomax = 135000, .pd = 0xbd, .d = 0x34 }, + { .lomax = 147000, .pd = 0xbc, .d = 0x30 }, + { .lomax = 160000, .pd = 0xbb, .d = 0x2c }, + { .lomax = 176000, .pd = 0xba, .d = 0x28 }, + { .lomax = 196000, .pd = 0xb9, .d = 0x24 }, + { .lomax = 220000, .pd = 0xb8, .d = 0x20 }, + { .lomax = 252000, .pd = 0xb3, .d = 0x1c }, + { .lomax = 271000, .pd = 0xad, .d = 0x1a }, + { .lomax = 294000, .pd = 0xac, .d = 0x18 }, + { .lomax = 321000, .pd = 0xab, .d = 0x16 }, + { .lomax = 353000, .pd = 0xaa, .d = 0x14 }, + { .lomax = 392000, .pd = 0xa9, .d = 0x12 }, + { .lomax = 441000, .pd = 0xa8, .d = 0x10 }, + { .lomax = 505000, .pd = 0xa3, .d = 0x0e }, + { .lomax = 543000, .pd = 0x9d, .d = 0x0d }, + { .lomax = 589000, .pd = 0x9c, .d = 0x0c }, + { .lomax = 642000, .pd = 0x9b, .d = 0x0b }, + { .lomax = 707000, .pd = 0x9a, .d = 0x0a }, + { .lomax = 785000, .pd = 0x99, .d = 0x09 }, + { .lomax = 883000, .pd = 0x98, .d = 0x08 }, + { .lomax = 1010000, .pd = 0x93, .d = 0x07 }, + { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ +}; + +static struct tda18271_pll_map tda18271c2_cal_pll[] = { + { .lomax = 33813, .pd = 0xdd, .d = 0xd0 }, + { .lomax = 36625, .pd = 0xdc, .d = 0xc0 }, + { .lomax = 39938, .pd = 0xdb, .d = 0xb0 }, + { .lomax = 43938, .pd = 0xda, .d = 0xa0 }, + { .lomax = 48813, .pd = 0xd9, .d = 0x90 }, + { .lomax = 54938, .pd = 0xd8, .d = 0x80 }, + { .lomax = 62813, .pd = 0xd3, .d = 0x70 }, + { .lomax = 67625, .pd = 0xcd, .d = 0x68 }, + { .lomax = 73250, .pd = 0xcc, .d = 0x60 }, + { .lomax = 79875, .pd = 0xcb, .d = 0x58 }, + { .lomax = 87875, .pd = 0xca, .d = 0x50 }, + { .lomax = 97625, .pd = 0xc9, .d = 0x48 }, + { .lomax = 109875, .pd = 0xc8, .d = 0x40 }, + { .lomax = 125625, .pd = 0xc3, .d = 0x38 }, + { .lomax = 135250, .pd = 0xbd, .d = 0x34 }, + { .lomax = 146500, .pd = 0xbc, .d = 0x30 }, + { .lomax = 159750, .pd = 0xbb, .d = 0x2c }, + { .lomax = 175750, .pd = 0xba, .d = 0x28 }, + { .lomax = 195250, .pd = 0xb9, .d = 0x24 }, + { .lomax = 219750, .pd = 0xb8, .d = 0x20 }, + { .lomax = 251250, .pd = 0xb3, .d = 0x1c }, + { .lomax = 270500, .pd = 0xad, .d = 0x1a }, + { .lomax = 293000, .pd = 0xac, .d = 0x18 }, + { .lomax = 319500, .pd = 0xab, .d = 0x16 }, + { .lomax = 351500, .pd = 0xaa, .d = 0x14 }, + { .lomax = 390500, .pd = 0xa9, .d = 0x12 }, + { .lomax = 439500, .pd = 0xa8, .d = 0x10 }, + { .lomax = 502500, .pd = 0xa3, .d = 0x0e }, + { .lomax = 541000, .pd = 0x9d, .d = 0x0d }, + { .lomax = 586000, .pd = 0x9c, .d = 0x0c }, + { .lomax = 639000, .pd = 0x9b, .d = 0x0b }, + { .lomax = 703000, .pd = 0x9a, .d = 0x0a }, + { .lomax = 781000, .pd = 0x99, .d = 0x09 }, + { .lomax = 879000, .pd = 0x98, .d = 0x08 }, + { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ +}; + +static struct tda18271_map tda18271_bp_filter[] = { + { .rfmax = 62000, .val = 0x00 }, + { .rfmax = 84000, .val = 0x01 }, + { .rfmax = 100000, .val = 0x02 }, + { .rfmax = 140000, .val = 0x03 }, + { .rfmax = 170000, .val = 0x04 }, + { .rfmax = 180000, .val = 0x05 }, + { .rfmax = 865000, .val = 0x06 }, + { .rfmax = 0, .val = 0x00 }, /* end */ +}; + +static struct tda18271_map tda18271c1_km[] = { + { .rfmax = 61100, .val = 0x74 }, + { .rfmax = 350000, .val = 0x40 }, + { .rfmax = 720000, .val = 0x30 }, + { .rfmax = 865000, .val = 0x40 }, + { .rfmax = 0, .val = 0x00 }, /* end */ +}; + +static struct tda18271_map tda18271c2_km[] = { + { .rfmax = 47900, .val = 0x38 }, + { .rfmax = 61100, .val = 0x44 }, + { .rfmax = 350000, .val = 0x30 }, + { .rfmax = 720000, .val = 0x24 }, + { .rfmax = 865000, .val = 0x3c }, + { .rfmax = 0, .val = 0x00 }, /* end */ +}; + +static struct tda18271_map tda18271_rf_band[] = { + { .rfmax = 47900, .val = 0x00 }, + { .rfmax = 61100, .val = 0x01 }, + { .rfmax = 152600, .val = 0x02 }, + { .rfmax = 164700, .val = 0x03 }, + { .rfmax = 203500, .val = 0x04 }, + { .rfmax = 457800, .val = 0x05 }, + { .rfmax = 865000, .val = 0x06 }, + { .rfmax = 0, .val = 0x00 }, /* end */ +}; + +static struct tda18271_map tda18271_gain_taper[] = { + { .rfmax = 45400, .val = 0x1f }, + { .rfmax = 45800, .val = 0x1e }, + { .rfmax = 46200, .val = 0x1d }, + { .rfmax = 46700, .val = 0x1c }, + { .rfmax = 47100, .val = 0x1b }, + { .rfmax = 47500, .val = 0x1a }, + { .rfmax = 47900, .val = 0x19 }, + { .rfmax = 49600, .val = 0x17 }, + { .rfmax = 51200, .val = 0x16 }, + { .rfmax = 52900, .val = 0x15 }, + { .rfmax = 54500, .val = 0x14 }, + { .rfmax = 56200, .val = 0x13 }, + { .rfmax = 57800, .val = 0x12 }, + { .rfmax = 59500, .val = 0x11 }, + { .rfmax = 61100, .val = 0x10 }, + { .rfmax = 67600, .val = 0x0d }, + { .rfmax = 74200, .val = 0x0c }, + { .rfmax = 80700, .val = 0x0b }, + { .rfmax = 87200, .val = 0x0a }, + { .rfmax = 93800, .val = 0x09 }, + { .rfmax = 100300, .val = 0x08 }, + { .rfmax = 106900, .val = 0x07 }, + { .rfmax = 113400, .val = 0x06 }, + { .rfmax = 119900, .val = 0x05 }, + { .rfmax = 126500, .val = 0x04 }, + { .rfmax = 133000, .val = 0x03 }, + { .rfmax = 139500, .val = 0x02 }, + { .rfmax = 146100, .val = 0x01 }, + { .rfmax = 152600, .val = 0x00 }, + { .rfmax = 154300, .val = 0x1f }, + { .rfmax = 156100, .val = 0x1e }, + { .rfmax = 157800, .val = 0x1d }, + { .rfmax = 159500, .val = 0x1c }, + { .rfmax = 161200, .val = 0x1b }, + { .rfmax = 163000, .val = 0x1a }, + { .rfmax = 164700, .val = 0x19 }, + { .rfmax = 170200, .val = 0x17 }, + { .rfmax = 175800, .val = 0x16 }, + { .rfmax = 181300, .val = 0x15 }, + { .rfmax = 186900, .val = 0x14 }, + { .rfmax = 192400, .val = 0x13 }, + { .rfmax = 198000, .val = 0x12 }, + { .rfmax = 203500, .val = 0x11 }, + { .rfmax = 216200, .val = 0x14 }, + { .rfmax = 228900, .val = 0x13 }, + { .rfmax = 241600, .val = 0x12 }, + { .rfmax = 254400, .val = 0x11 }, + { .rfmax = 267100, .val = 0x10 }, + { .rfmax = 279800, .val = 0x0f }, + { .rfmax = 292500, .val = 0x0e }, + { .rfmax = 305200, .val = 0x0d }, + { .rfmax = 317900, .val = 0x0c }, + { .rfmax = 330700, .val = 0x0b }, + { .rfmax = 343400, .val = 0x0a }, + { .rfmax = 356100, .val = 0x09 }, + { .rfmax = 368800, .val = 0x08 }, + { .rfmax = 381500, .val = 0x07 }, + { .rfmax = 394200, .val = 0x06 }, + { .rfmax = 406900, .val = 0x05 }, + { .rfmax = 419700, .val = 0x04 }, + { .rfmax = 432400, .val = 0x03 }, + { .rfmax = 445100, .val = 0x02 }, + { .rfmax = 457800, .val = 0x01 }, + { .rfmax = 476300, .val = 0x19 }, + { .rfmax = 494800, .val = 0x18 }, + { .rfmax = 513300, .val = 0x17 }, + { .rfmax = 531800, .val = 0x16 }, + { .rfmax = 550300, .val = 0x15 }, + { .rfmax = 568900, .val = 0x14 }, + { .rfmax = 587400, .val = 0x13 }, + { .rfmax = 605900, .val = 0x12 }, + { .rfmax = 624400, .val = 0x11 }, + { .rfmax = 642900, .val = 0x10 }, + { .rfmax = 661400, .val = 0x0f }, + { .rfmax = 679900, .val = 0x0e }, + { .rfmax = 698400, .val = 0x0d }, + { .rfmax = 716900, .val = 0x0c }, + { .rfmax = 735400, .val = 0x0b }, + { .rfmax = 753900, .val = 0x0a }, + { .rfmax = 772500, .val = 0x09 }, + { .rfmax = 791000, .val = 0x08 }, + { .rfmax = 809500, .val = 0x07 }, + { .rfmax = 828000, .val = 0x06 }, + { .rfmax = 846500, .val = 0x05 }, + { .rfmax = 865000, .val = 0x04 }, + { .rfmax = 0, .val = 0x00 }, /* end */ +}; + +static struct tda18271_map tda18271c1_rf_cal[] = { + { .rfmax = 41000, .val = 0x1e }, + { .rfmax = 43000, .val = 0x30 }, + { .rfmax = 45000, .val = 0x43 }, + { .rfmax = 46000, .val = 0x4d }, + { .rfmax = 47000, .val = 0x54 }, + { .rfmax = 47900, .val = 0x64 }, + { .rfmax = 49100, .val = 0x20 }, + { .rfmax = 50000, .val = 0x22 }, + { .rfmax = 51000, .val = 0x2a }, + { .rfmax = 53000, .val = 0x32 }, + { .rfmax = 55000, .val = 0x35 }, + { .rfmax = 56000, .val = 0x3c }, + { .rfmax = 57000, .val = 0x3f }, + { .rfmax = 58000, .val = 0x48 }, + { .rfmax = 59000, .val = 0x4d }, + { .rfmax = 60000, .val = 0x58 }, + { .rfmax = 61100, .val = 0x5f }, + { .rfmax = 0, .val = 0x00 }, /* end */ +}; + +static struct tda18271_map tda18271c2_rf_cal[] = { + { .rfmax = 41000, .val = 0x0f }, + { .rfmax = 43000, .val = 0x1c }, + { .rfmax = 45000, .val = 0x2f }, + { .rfmax = 46000, .val = 0x39 }, + { .rfmax = 47000, .val = 0x40 }, + { .rfmax = 47900, .val = 0x50 }, + { .rfmax = 49100, .val = 0x16 }, + { .rfmax = 50000, .val = 0x18 }, + { .rfmax = 51000, .val = 0x20 }, + { .rfmax = 53000, .val = 0x28 }, + { .rfmax = 55000, .val = 0x2b }, + { .rfmax = 56000, .val = 0x32 }, + { .rfmax = 57000, .val = 0x35 }, + { .rfmax = 58000, .val = 0x3e }, + { .rfmax = 59000, .val = 0x43 }, + { .rfmax = 60000, .val = 0x4e }, + { .rfmax = 61100, .val = 0x55 }, + { .rfmax = 63000, .val = 0x0f }, + { .rfmax = 64000, .val = 0x11 }, + { .rfmax = 65000, .val = 0x12 }, + { .rfmax = 66000, .val = 0x15 }, + { .rfmax = 67000, .val = 0x16 }, + { .rfmax = 68000, .val = 0x17 }, + { .rfmax = 70000, .val = 0x19 }, + { .rfmax = 71000, .val = 0x1c }, + { .rfmax = 72000, .val = 0x1d }, + { .rfmax = 73000, .val = 0x1f }, + { .rfmax = 74000, .val = 0x20 }, + { .rfmax = 75000, .val = 0x21 }, + { .rfmax = 76000, .val = 0x24 }, + { .rfmax = 77000, .val = 0x25 }, + { .rfmax = 78000, .val = 0x27 }, + { .rfmax = 80000, .val = 0x28 }, + { .rfmax = 81000, .val = 0x29 }, + { .rfmax = 82000, .val = 0x2d }, + { .rfmax = 83000, .val = 0x2e }, + { .rfmax = 84000, .val = 0x2f }, + { .rfmax = 85000, .val = 0x31 }, + { .rfmax = 86000, .val = 0x33 }, + { .rfmax = 87000, .val = 0x34 }, + { .rfmax = 88000, .val = 0x35 }, + { .rfmax = 89000, .val = 0x37 }, + { .rfmax = 90000, .val = 0x38 }, + { .rfmax = 91000, .val = 0x39 }, + { .rfmax = 93000, .val = 0x3c }, + { .rfmax = 94000, .val = 0x3e }, + { .rfmax = 95000, .val = 0x3f }, + { .rfmax = 96000, .val = 0x40 }, + { .rfmax = 97000, .val = 0x42 }, + { .rfmax = 99000, .val = 0x45 }, + { .rfmax = 100000, .val = 0x46 }, + { .rfmax = 102000, .val = 0x48 }, + { .rfmax = 103000, .val = 0x4a }, + { .rfmax = 105000, .val = 0x4d }, + { .rfmax = 106000, .val = 0x4e }, + { .rfmax = 107000, .val = 0x50 }, + { .rfmax = 108000, .val = 0x51 }, + { .rfmax = 110000, .val = 0x54 }, + { .rfmax = 111000, .val = 0x56 }, + { .rfmax = 112000, .val = 0x57 }, + { .rfmax = 113000, .val = 0x58 }, + { .rfmax = 114000, .val = 0x59 }, + { .rfmax = 115000, .val = 0x5c }, + { .rfmax = 116000, .val = 0x5d }, + { .rfmax = 117000, .val = 0x5f }, + { .rfmax = 119000, .val = 0x60 }, + { .rfmax = 120000, .val = 0x64 }, + { .rfmax = 121000, .val = 0x65 }, + { .rfmax = 122000, .val = 0x66 }, + { .rfmax = 123000, .val = 0x68 }, + { .rfmax = 124000, .val = 0x69 }, + { .rfmax = 125000, .val = 0x6c }, + { .rfmax = 126000, .val = 0x6d }, + { .rfmax = 127000, .val = 0x6e }, + { .rfmax = 128000, .val = 0x70 }, + { .rfmax = 129000, .val = 0x71 }, + { .rfmax = 130000, .val = 0x75 }, + { .rfmax = 131000, .val = 0x77 }, + { .rfmax = 132000, .val = 0x78 }, + { .rfmax = 133000, .val = 0x7b }, + { .rfmax = 134000, .val = 0x7e }, + { .rfmax = 135000, .val = 0x81 }, + { .rfmax = 136000, .val = 0x82 }, + { .rfmax = 137000, .val = 0x87 }, + { .rfmax = 138000, .val = 0x88 }, + { .rfmax = 139000, .val = 0x8d }, + { .rfmax = 140000, .val = 0x8e }, + { .rfmax = 141000, .val = 0x91 }, + { .rfmax = 142000, .val = 0x95 }, + { .rfmax = 143000, .val = 0x9a }, + { .rfmax = 144000, .val = 0x9d }, + { .rfmax = 145000, .val = 0xa1 }, + { .rfmax = 146000, .val = 0xa2 }, + { .rfmax = 147000, .val = 0xa4 }, + { .rfmax = 148000, .val = 0xa9 }, + { .rfmax = 149000, .val = 0xae }, + { .rfmax = 150000, .val = 0xb0 }, + { .rfmax = 151000, .val = 0xb1 }, + { .rfmax = 152000, .val = 0xb7 }, + { .rfmax = 152600, .val = 0xbd }, + { .rfmax = 154000, .val = 0x20 }, + { .rfmax = 155000, .val = 0x22 }, + { .rfmax = 156000, .val = 0x24 }, + { .rfmax = 157000, .val = 0x25 }, + { .rfmax = 158000, .val = 0x27 }, + { .rfmax = 159000, .val = 0x29 }, + { .rfmax = 160000, .val = 0x2c }, + { .rfmax = 161000, .val = 0x2d }, + { .rfmax = 163000, .val = 0x2e }, + { .rfmax = 164000, .val = 0x2f }, + { .rfmax = 164700, .val = 0x30 }, + { .rfmax = 166000, .val = 0x11 }, + { .rfmax = 167000, .val = 0x12 }, + { .rfmax = 168000, .val = 0x13 }, + { .rfmax = 169000, .val = 0x14 }, + { .rfmax = 170000, .val = 0x15 }, + { .rfmax = 172000, .val = 0x16 }, + { .rfmax = 173000, .val = 0x17 }, + { .rfmax = 174000, .val = 0x18 }, + { .rfmax = 175000, .val = 0x1a }, + { .rfmax = 176000, .val = 0x1b }, + { .rfmax = 178000, .val = 0x1d }, + { .rfmax = 179000, .val = 0x1e }, + { .rfmax = 180000, .val = 0x1f }, + { .rfmax = 181000, .val = 0x20 }, + { .rfmax = 182000, .val = 0x21 }, + { .rfmax = 183000, .val = 0x22 }, + { .rfmax = 184000, .val = 0x24 }, + { .rfmax = 185000, .val = 0x25 }, + { .rfmax = 186000, .val = 0x26 }, + { .rfmax = 187000, .val = 0x27 }, + { .rfmax = 188000, .val = 0x29 }, + { .rfmax = 189000, .val = 0x2a }, + { .rfmax = 190000, .val = 0x2c }, + { .rfmax = 191000, .val = 0x2d }, + { .rfmax = 192000, .val = 0x2e }, + { .rfmax = 193000, .val = 0x2f }, + { .rfmax = 194000, .val = 0x30 }, + { .rfmax = 195000, .val = 0x33 }, + { .rfmax = 196000, .val = 0x35 }, + { .rfmax = 198000, .val = 0x36 }, + { .rfmax = 200000, .val = 0x38 }, + { .rfmax = 201000, .val = 0x3c }, + { .rfmax = 202000, .val = 0x3d }, + { .rfmax = 203500, .val = 0x3e }, + { .rfmax = 206000, .val = 0x0e }, + { .rfmax = 208000, .val = 0x0f }, + { .rfmax = 212000, .val = 0x10 }, + { .rfmax = 216000, .val = 0x11 }, + { .rfmax = 217000, .val = 0x12 }, + { .rfmax = 218000, .val = 0x13 }, + { .rfmax = 220000, .val = 0x14 }, + { .rfmax = 222000, .val = 0x15 }, + { .rfmax = 225000, .val = 0x16 }, + { .rfmax = 228000, .val = 0x17 }, + { .rfmax = 231000, .val = 0x18 }, + { .rfmax = 234000, .val = 0x19 }, + { .rfmax = 235000, .val = 0x1a }, + { .rfmax = 236000, .val = 0x1b }, + { .rfmax = 237000, .val = 0x1c }, + { .rfmax = 240000, .val = 0x1d }, + { .rfmax = 242000, .val = 0x1e }, + { .rfmax = 244000, .val = 0x1f }, + { .rfmax = 247000, .val = 0x20 }, + { .rfmax = 249000, .val = 0x21 }, + { .rfmax = 252000, .val = 0x22 }, + { .rfmax = 253000, .val = 0x23 }, + { .rfmax = 254000, .val = 0x24 }, + { .rfmax = 256000, .val = 0x25 }, + { .rfmax = 259000, .val = 0x26 }, + { .rfmax = 262000, .val = 0x27 }, + { .rfmax = 264000, .val = 0x28 }, + { .rfmax = 267000, .val = 0x29 }, + { .rfmax = 269000, .val = 0x2a }, + { .rfmax = 271000, .val = 0x2b }, + { .rfmax = 273000, .val = 0x2c }, + { .rfmax = 275000, .val = 0x2d }, + { .rfmax = 277000, .val = 0x2e }, + { .rfmax = 279000, .val = 0x2f }, + { .rfmax = 282000, .val = 0x30 }, + { .rfmax = 284000, .val = 0x31 }, + { .rfmax = 286000, .val = 0x32 }, + { .rfmax = 287000, .val = 0x33 }, + { .rfmax = 290000, .val = 0x34 }, + { .rfmax = 293000, .val = 0x35 }, + { .rfmax = 295000, .val = 0x36 }, + { .rfmax = 297000, .val = 0x37 }, + { .rfmax = 300000, .val = 0x38 }, + { .rfmax = 303000, .val = 0x39 }, + { .rfmax = 305000, .val = 0x3a }, + { .rfmax = 306000, .val = 0x3b }, + { .rfmax = 307000, .val = 0x3c }, + { .rfmax = 310000, .val = 0x3d }, + { .rfmax = 312000, .val = 0x3e }, + { .rfmax = 315000, .val = 0x3f }, + { .rfmax = 318000, .val = 0x40 }, + { .rfmax = 320000, .val = 0x41 }, + { .rfmax = 323000, .val = 0x42 }, + { .rfmax = 324000, .val = 0x43 }, + { .rfmax = 325000, .val = 0x44 }, + { .rfmax = 327000, .val = 0x45 }, + { .rfmax = 331000, .val = 0x46 }, + { .rfmax = 334000, .val = 0x47 }, + { .rfmax = 337000, .val = 0x48 }, + { .rfmax = 339000, .val = 0x49 }, + { .rfmax = 340000, .val = 0x4a }, + { .rfmax = 341000, .val = 0x4b }, + { .rfmax = 343000, .val = 0x4c }, + { .rfmax = 345000, .val = 0x4d }, + { .rfmax = 349000, .val = 0x4e }, + { .rfmax = 352000, .val = 0x4f }, + { .rfmax = 353000, .val = 0x50 }, + { .rfmax = 355000, .val = 0x51 }, + { .rfmax = 357000, .val = 0x52 }, + { .rfmax = 359000, .val = 0x53 }, + { .rfmax = 361000, .val = 0x54 }, + { .rfmax = 362000, .val = 0x55 }, + { .rfmax = 364000, .val = 0x56 }, + { .rfmax = 368000, .val = 0x57 }, + { .rfmax = 370000, .val = 0x58 }, + { .rfmax = 372000, .val = 0x59 }, + { .rfmax = 375000, .val = 0x5a }, + { .rfmax = 376000, .val = 0x5b }, + { .rfmax = 377000, .val = 0x5c }, + { .rfmax = 379000, .val = 0x5d }, + { .rfmax = 382000, .val = 0x5e }, + { .rfmax = 384000, .val = 0x5f }, + { .rfmax = 385000, .val = 0x60 }, + { .rfmax = 386000, .val = 0x61 }, + { .rfmax = 388000, .val = 0x62 }, + { .rfmax = 390000, .val = 0x63 }, + { .rfmax = 393000, .val = 0x64 }, + { .rfmax = 394000, .val = 0x65 }, + { .rfmax = 396000, .val = 0x66 }, + { .rfmax = 397000, .val = 0x67 }, + { .rfmax = 398000, .val = 0x68 }, + { .rfmax = 400000, .val = 0x69 }, + { .rfmax = 402000, .val = 0x6a }, + { .rfmax = 403000, .val = 0x6b }, + { .rfmax = 407000, .val = 0x6c }, + { .rfmax = 408000, .val = 0x6d }, + { .rfmax = 409000, .val = 0x6e }, + { .rfmax = 410000, .val = 0x6f }, + { .rfmax = 411000, .val = 0x70 }, + { .rfmax = 412000, .val = 0x71 }, + { .rfmax = 413000, .val = 0x72 }, + { .rfmax = 414000, .val = 0x73 }, + { .rfmax = 417000, .val = 0x74 }, + { .rfmax = 418000, .val = 0x75 }, + { .rfmax = 420000, .val = 0x76 }, + { .rfmax = 422000, .val = 0x77 }, + { .rfmax = 423000, .val = 0x78 }, + { .rfmax = 424000, .val = 0x79 }, + { .rfmax = 427000, .val = 0x7a }, + { .rfmax = 428000, .val = 0x7b }, + { .rfmax = 429000, .val = 0x7d }, + { .rfmax = 432000, .val = 0x7f }, + { .rfmax = 434000, .val = 0x80 }, + { .rfmax = 435000, .val = 0x81 }, + { .rfmax = 436000, .val = 0x83 }, + { .rfmax = 437000, .val = 0x84 }, + { .rfmax = 438000, .val = 0x85 }, + { .rfmax = 439000, .val = 0x86 }, + { .rfmax = 440000, .val = 0x87 }, + { .rfmax = 441000, .val = 0x88 }, + { .rfmax = 442000, .val = 0x89 }, + { .rfmax = 445000, .val = 0x8a }, + { .rfmax = 446000, .val = 0x8b }, + { .rfmax = 447000, .val = 0x8c }, + { .rfmax = 448000, .val = 0x8e }, + { .rfmax = 449000, .val = 0x8f }, + { .rfmax = 450000, .val = 0x90 }, + { .rfmax = 452000, .val = 0x91 }, + { .rfmax = 453000, .val = 0x93 }, + { .rfmax = 454000, .val = 0x94 }, + { .rfmax = 456000, .val = 0x96 }, + { .rfmax = 457800, .val = 0x98 }, + { .rfmax = 461000, .val = 0x11 }, + { .rfmax = 468000, .val = 0x12 }, + { .rfmax = 472000, .val = 0x13 }, + { .rfmax = 473000, .val = 0x14 }, + { .rfmax = 474000, .val = 0x15 }, + { .rfmax = 481000, .val = 0x16 }, + { .rfmax = 486000, .val = 0x17 }, + { .rfmax = 491000, .val = 0x18 }, + { .rfmax = 498000, .val = 0x19 }, + { .rfmax = 499000, .val = 0x1a }, + { .rfmax = 501000, .val = 0x1b }, + { .rfmax = 506000, .val = 0x1c }, + { .rfmax = 511000, .val = 0x1d }, + { .rfmax = 516000, .val = 0x1e }, + { .rfmax = 520000, .val = 0x1f }, + { .rfmax = 521000, .val = 0x20 }, + { .rfmax = 525000, .val = 0x21 }, + { .rfmax = 529000, .val = 0x22 }, + { .rfmax = 533000, .val = 0x23 }, + { .rfmax = 539000, .val = 0x24 }, + { .rfmax = 541000, .val = 0x25 }, + { .rfmax = 547000, .val = 0x26 }, + { .rfmax = 549000, .val = 0x27 }, + { .rfmax = 551000, .val = 0x28 }, + { .rfmax = 556000, .val = 0x29 }, + { .rfmax = 561000, .val = 0x2a }, + { .rfmax = 563000, .val = 0x2b }, + { .rfmax = 565000, .val = 0x2c }, + { .rfmax = 569000, .val = 0x2d }, + { .rfmax = 571000, .val = 0x2e }, + { .rfmax = 577000, .val = 0x2f }, + { .rfmax = 580000, .val = 0x30 }, + { .rfmax = 582000, .val = 0x31 }, + { .rfmax = 584000, .val = 0x32 }, + { .rfmax = 588000, .val = 0x33 }, + { .rfmax = 591000, .val = 0x34 }, + { .rfmax = 596000, .val = 0x35 }, + { .rfmax = 598000, .val = 0x36 }, + { .rfmax = 603000, .val = 0x37 }, + { .rfmax = 604000, .val = 0x38 }, + { .rfmax = 606000, .val = 0x39 }, + { .rfmax = 612000, .val = 0x3a }, + { .rfmax = 615000, .val = 0x3b }, + { .rfmax = 617000, .val = 0x3c }, + { .rfmax = 621000, .val = 0x3d }, + { .rfmax = 622000, .val = 0x3e }, + { .rfmax = 625000, .val = 0x3f }, + { .rfmax = 632000, .val = 0x40 }, + { .rfmax = 633000, .val = 0x41 }, + { .rfmax = 634000, .val = 0x42 }, + { .rfmax = 642000, .val = 0x43 }, + { .rfmax = 643000, .val = 0x44 }, + { .rfmax = 647000, .val = 0x45 }, + { .rfmax = 650000, .val = 0x46 }, + { .rfmax = 652000, .val = 0x47 }, + { .rfmax = 657000, .val = 0x48 }, + { .rfmax = 661000, .val = 0x49 }, + { .rfmax = 662000, .val = 0x4a }, + { .rfmax = 665000, .val = 0x4b }, + { .rfmax = 667000, .val = 0x4c }, + { .rfmax = 670000, .val = 0x4d }, + { .rfmax = 673000, .val = 0x4e }, + { .rfmax = 676000, .val = 0x4f }, + { .rfmax = 677000, .val = 0x50 }, + { .rfmax = 681000, .val = 0x51 }, + { .rfmax = 683000, .val = 0x52 }, + { .rfmax = 686000, .val = 0x53 }, + { .rfmax = 688000, .val = 0x54 }, + { .rfmax = 689000, .val = 0x55 }, + { .rfmax = 691000, .val = 0x56 }, + { .rfmax = 695000, .val = 0x57 }, + { .rfmax = 698000, .val = 0x58 }, + { .rfmax = 703000, .val = 0x59 }, + { .rfmax = 704000, .val = 0x5a }, + { .rfmax = 705000, .val = 0x5b }, + { .rfmax = 707000, .val = 0x5c }, + { .rfmax = 710000, .val = 0x5d }, + { .rfmax = 712000, .val = 0x5e }, + { .rfmax = 717000, .val = 0x5f }, + { .rfmax = 718000, .val = 0x60 }, + { .rfmax = 721000, .val = 0x61 }, + { .rfmax = 722000, .val = 0x62 }, + { .rfmax = 723000, .val = 0x63 }, + { .rfmax = 725000, .val = 0x64 }, + { .rfmax = 727000, .val = 0x65 }, + { .rfmax = 730000, .val = 0x66 }, + { .rfmax = 732000, .val = 0x67 }, + { .rfmax = 735000, .val = 0x68 }, + { .rfmax = 740000, .val = 0x69 }, + { .rfmax = 741000, .val = 0x6a }, + { .rfmax = 742000, .val = 0x6b }, + { .rfmax = 743000, .val = 0x6c }, + { .rfmax = 745000, .val = 0x6d }, + { .rfmax = 747000, .val = 0x6e }, + { .rfmax = 748000, .val = 0x6f }, + { .rfmax = 750000, .val = 0x70 }, + { .rfmax = 752000, .val = 0x71 }, + { .rfmax = 754000, .val = 0x72 }, + { .rfmax = 757000, .val = 0x73 }, + { .rfmax = 758000, .val = 0x74 }, + { .rfmax = 760000, .val = 0x75 }, + { .rfmax = 763000, .val = 0x76 }, + { .rfmax = 764000, .val = 0x77 }, + { .rfmax = 766000, .val = 0x78 }, + { .rfmax = 767000, .val = 0x79 }, + { .rfmax = 768000, .val = 0x7a }, + { .rfmax = 773000, .val = 0x7b }, + { .rfmax = 774000, .val = 0x7c }, + { .rfmax = 776000, .val = 0x7d }, + { .rfmax = 777000, .val = 0x7e }, + { .rfmax = 778000, .val = 0x7f }, + { .rfmax = 779000, .val = 0x80 }, + { .rfmax = 781000, .val = 0x81 }, + { .rfmax = 783000, .val = 0x82 }, + { .rfmax = 784000, .val = 0x83 }, + { .rfmax = 785000, .val = 0x84 }, + { .rfmax = 786000, .val = 0x85 }, + { .rfmax = 793000, .val = 0x86 }, + { .rfmax = 794000, .val = 0x87 }, + { .rfmax = 795000, .val = 0x88 }, + { .rfmax = 797000, .val = 0x89 }, + { .rfmax = 799000, .val = 0x8a }, + { .rfmax = 801000, .val = 0x8b }, + { .rfmax = 802000, .val = 0x8c }, + { .rfmax = 803000, .val = 0x8d }, + { .rfmax = 804000, .val = 0x8e }, + { .rfmax = 810000, .val = 0x90 }, + { .rfmax = 811000, .val = 0x91 }, + { .rfmax = 812000, .val = 0x92 }, + { .rfmax = 814000, .val = 0x93 }, + { .rfmax = 816000, .val = 0x94 }, + { .rfmax = 817000, .val = 0x96 }, + { .rfmax = 818000, .val = 0x97 }, + { .rfmax = 820000, .val = 0x98 }, + { .rfmax = 821000, .val = 0x99 }, + { .rfmax = 822000, .val = 0x9a }, + { .rfmax = 828000, .val = 0x9b }, + { .rfmax = 829000, .val = 0x9d }, + { .rfmax = 830000, .val = 0x9f }, + { .rfmax = 831000, .val = 0xa0 }, + { .rfmax = 833000, .val = 0xa1 }, + { .rfmax = 835000, .val = 0xa2 }, + { .rfmax = 836000, .val = 0xa3 }, + { .rfmax = 837000, .val = 0xa4 }, + { .rfmax = 838000, .val = 0xa6 }, + { .rfmax = 840000, .val = 0xa8 }, + { .rfmax = 842000, .val = 0xa9 }, + { .rfmax = 845000, .val = 0xaa }, + { .rfmax = 846000, .val = 0xab }, + { .rfmax = 847000, .val = 0xad }, + { .rfmax = 848000, .val = 0xae }, + { .rfmax = 852000, .val = 0xaf }, + { .rfmax = 853000, .val = 0xb0 }, + { .rfmax = 858000, .val = 0xb1 }, + { .rfmax = 860000, .val = 0xb2 }, + { .rfmax = 861000, .val = 0xb3 }, + { .rfmax = 862000, .val = 0xb4 }, + { .rfmax = 863000, .val = 0xb6 }, + { .rfmax = 864000, .val = 0xb8 }, + { .rfmax = 865000, .val = 0xb9 }, + { .rfmax = 0, .val = 0x00 }, /* end */ +}; + +static struct tda18271_map tda18271_ir_measure[] = { + { .rfmax = 30000, .val = 4 }, + { .rfmax = 200000, .val = 5 }, + { .rfmax = 600000, .val = 6 }, + { .rfmax = 865000, .val = 7 }, + { .rfmax = 0, .val = 0 }, /* end */ +}; + +static struct tda18271_map tda18271_rf_cal_dc_over_dt[] = { + { .rfmax = 47900, .val = 0x00 }, + { .rfmax = 55000, .val = 0x00 }, + { .rfmax = 61100, .val = 0x0a }, + { .rfmax = 64000, .val = 0x0a }, + { .rfmax = 82000, .val = 0x14 }, + { .rfmax = 84000, .val = 0x19 }, + { .rfmax = 119000, .val = 0x1c }, + { .rfmax = 124000, .val = 0x20 }, + { .rfmax = 129000, .val = 0x2a }, + { .rfmax = 134000, .val = 0x32 }, + { .rfmax = 139000, .val = 0x39 }, + { .rfmax = 144000, .val = 0x3e }, + { .rfmax = 149000, .val = 0x3f }, + { .rfmax = 152600, .val = 0x40 }, + { .rfmax = 154000, .val = 0x40 }, + { .rfmax = 164700, .val = 0x41 }, + { .rfmax = 203500, .val = 0x32 }, + { .rfmax = 353000, .val = 0x19 }, + { .rfmax = 356000, .val = 0x1a }, + { .rfmax = 359000, .val = 0x1b }, + { .rfmax = 363000, .val = 0x1c }, + { .rfmax = 366000, .val = 0x1d }, + { .rfmax = 369000, .val = 0x1e }, + { .rfmax = 373000, .val = 0x1f }, + { .rfmax = 376000, .val = 0x20 }, + { .rfmax = 379000, .val = 0x21 }, + { .rfmax = 383000, .val = 0x22 }, + { .rfmax = 386000, .val = 0x23 }, + { .rfmax = 389000, .val = 0x24 }, + { .rfmax = 393000, .val = 0x25 }, + { .rfmax = 396000, .val = 0x26 }, + { .rfmax = 399000, .val = 0x27 }, + { .rfmax = 402000, .val = 0x28 }, + { .rfmax = 404000, .val = 0x29 }, + { .rfmax = 407000, .val = 0x2a }, + { .rfmax = 409000, .val = 0x2b }, + { .rfmax = 412000, .val = 0x2c }, + { .rfmax = 414000, .val = 0x2d }, + { .rfmax = 417000, .val = 0x2e }, + { .rfmax = 419000, .val = 0x2f }, + { .rfmax = 422000, .val = 0x30 }, + { .rfmax = 424000, .val = 0x31 }, + { .rfmax = 427000, .val = 0x32 }, + { .rfmax = 429000, .val = 0x33 }, + { .rfmax = 432000, .val = 0x34 }, + { .rfmax = 434000, .val = 0x35 }, + { .rfmax = 437000, .val = 0x36 }, + { .rfmax = 439000, .val = 0x37 }, + { .rfmax = 442000, .val = 0x38 }, + { .rfmax = 444000, .val = 0x39 }, + { .rfmax = 447000, .val = 0x3a }, + { .rfmax = 449000, .val = 0x3b }, + { .rfmax = 457800, .val = 0x3c }, + { .rfmax = 465000, .val = 0x0f }, + { .rfmax = 477000, .val = 0x12 }, + { .rfmax = 483000, .val = 0x14 }, + { .rfmax = 502000, .val = 0x19 }, + { .rfmax = 508000, .val = 0x1b }, + { .rfmax = 519000, .val = 0x1c }, + { .rfmax = 522000, .val = 0x1d }, + { .rfmax = 524000, .val = 0x1e }, + { .rfmax = 534000, .val = 0x1f }, + { .rfmax = 549000, .val = 0x20 }, + { .rfmax = 554000, .val = 0x22 }, + { .rfmax = 584000, .val = 0x24 }, + { .rfmax = 589000, .val = 0x26 }, + { .rfmax = 658000, .val = 0x27 }, + { .rfmax = 664000, .val = 0x2c }, + { .rfmax = 669000, .val = 0x2d }, + { .rfmax = 699000, .val = 0x2e }, + { .rfmax = 704000, .val = 0x30 }, + { .rfmax = 709000, .val = 0x31 }, + { .rfmax = 714000, .val = 0x32 }, + { .rfmax = 724000, .val = 0x33 }, + { .rfmax = 729000, .val = 0x36 }, + { .rfmax = 739000, .val = 0x38 }, + { .rfmax = 744000, .val = 0x39 }, + { .rfmax = 749000, .val = 0x3b }, + { .rfmax = 754000, .val = 0x3c }, + { .rfmax = 759000, .val = 0x3d }, + { .rfmax = 764000, .val = 0x3e }, + { .rfmax = 769000, .val = 0x3f }, + { .rfmax = 774000, .val = 0x40 }, + { .rfmax = 779000, .val = 0x41 }, + { .rfmax = 784000, .val = 0x43 }, + { .rfmax = 789000, .val = 0x46 }, + { .rfmax = 794000, .val = 0x48 }, + { .rfmax = 799000, .val = 0x4b }, + { .rfmax = 804000, .val = 0x4f }, + { .rfmax = 809000, .val = 0x54 }, + { .rfmax = 814000, .val = 0x59 }, + { .rfmax = 819000, .val = 0x5d }, + { .rfmax = 824000, .val = 0x61 }, + { .rfmax = 829000, .val = 0x68 }, + { .rfmax = 834000, .val = 0x6e }, + { .rfmax = 839000, .val = 0x75 }, + { .rfmax = 844000, .val = 0x7e }, + { .rfmax = 849000, .val = 0x82 }, + { .rfmax = 854000, .val = 0x84 }, + { .rfmax = 859000, .val = 0x8f }, + { .rfmax = 865000, .val = 0x9a }, + { .rfmax = 0, .val = 0x00 }, /* end */ +}; + +/*---------------------------------------------------------------------*/ + +struct tda18271_thermo_map { + u8 d; + u8 r0; + u8 r1; +}; + +static struct tda18271_thermo_map tda18271_thermometer[] = { + { .d = 0x00, .r0 = 60, .r1 = 92 }, + { .d = 0x01, .r0 = 62, .r1 = 94 }, + { .d = 0x02, .r0 = 66, .r1 = 98 }, + { .d = 0x03, .r0 = 64, .r1 = 96 }, + { .d = 0x04, .r0 = 74, .r1 = 106 }, + { .d = 0x05, .r0 = 72, .r1 = 104 }, + { .d = 0x06, .r0 = 68, .r1 = 100 }, + { .d = 0x07, .r0 = 70, .r1 = 102 }, + { .d = 0x08, .r0 = 90, .r1 = 122 }, + { .d = 0x09, .r0 = 88, .r1 = 120 }, + { .d = 0x0a, .r0 = 84, .r1 = 116 }, + { .d = 0x0b, .r0 = 86, .r1 = 118 }, + { .d = 0x0c, .r0 = 76, .r1 = 108 }, + { .d = 0x0d, .r0 = 78, .r1 = 110 }, + { .d = 0x0e, .r0 = 82, .r1 = 114 }, + { .d = 0x0f, .r0 = 80, .r1 = 112 }, + { .d = 0x00, .r0 = 0, .r1 = 0 }, /* end */ +}; + +int tda18271_lookup_thermometer(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + int val, i = 0; + + while (tda18271_thermometer[i].d < (regs[R_TM] & 0x0f)) { + if (tda18271_thermometer[i + 1].d == 0) + break; + i++; + } + + if ((regs[R_TM] & 0x20) == 0x20) + val = tda18271_thermometer[i].r1; + else + val = tda18271_thermometer[i].r0; + + tda_map("(%d) tm = %d\n", i, val); + + return val; +} + +/*---------------------------------------------------------------------*/ + +struct tda18271_cid_target_map { + u32 rfmax; + u8 target; + u16 limit; +}; + +static struct tda18271_cid_target_map tda18271_cid_target[] = { + { .rfmax = 46000, .target = 0x04, .limit = 1800 }, + { .rfmax = 52200, .target = 0x0a, .limit = 1500 }, + { .rfmax = 70100, .target = 0x01, .limit = 4000 }, + { .rfmax = 136800, .target = 0x18, .limit = 4000 }, + { .rfmax = 156700, .target = 0x18, .limit = 4000 }, + { .rfmax = 186250, .target = 0x0a, .limit = 4000 }, + { .rfmax = 230000, .target = 0x0a, .limit = 4000 }, + { .rfmax = 345000, .target = 0x18, .limit = 4000 }, + { .rfmax = 426000, .target = 0x0e, .limit = 4000 }, + { .rfmax = 489500, .target = 0x1e, .limit = 4000 }, + { .rfmax = 697500, .target = 0x32, .limit = 4000 }, + { .rfmax = 842000, .target = 0x3a, .limit = 4000 }, + { .rfmax = 0, .target = 0x00, .limit = 0 }, /* end */ +}; + +int tda18271_lookup_cid_target(struct dvb_frontend *fe, + u32 *freq, u8 *cid_target, u16 *count_limit) +{ + struct tda18271_priv *priv = fe->tuner_priv; + int i = 0; + + while ((tda18271_cid_target[i].rfmax * 1000) < *freq) { + if (tda18271_cid_target[i + 1].rfmax == 0) + break; + i++; + } + *cid_target = tda18271_cid_target[i].target; + *count_limit = tda18271_cid_target[i].limit; + + tda_map("(%d) cid_target = %02x, count_limit = %d\n", i, + tda18271_cid_target[i].target, tda18271_cid_target[i].limit); + + return 0; +} + +/*---------------------------------------------------------------------*/ + +static struct tda18271_rf_tracking_filter_cal tda18271_rf_band_template[] = { + { .rfmax = 47900, .rfband = 0x00, + .rf1_def = 46000, .rf2_def = 0, .rf3_def = 0 }, + { .rfmax = 61100, .rfband = 0x01, + .rf1_def = 52200, .rf2_def = 0, .rf3_def = 0 }, + { .rfmax = 152600, .rfband = 0x02, + .rf1_def = 70100, .rf2_def = 136800, .rf3_def = 0 }, + { .rfmax = 164700, .rfband = 0x03, + .rf1_def = 156700, .rf2_def = 0, .rf3_def = 0 }, + { .rfmax = 203500, .rfband = 0x04, + .rf1_def = 186250, .rf2_def = 0, .rf3_def = 0 }, + { .rfmax = 457800, .rfband = 0x05, + .rf1_def = 230000, .rf2_def = 345000, .rf3_def = 426000 }, + { .rfmax = 865000, .rfband = 0x06, + .rf1_def = 489500, .rf2_def = 697500, .rf3_def = 842000 }, + { .rfmax = 0, .rfband = 0x00, + .rf1_def = 0, .rf2_def = 0, .rf3_def = 0 }, /* end */ +}; + +int tda18271_lookup_rf_band(struct dvb_frontend *fe, u32 *freq, u8 *rf_band) +{ + struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; + int i = 0; + + while ((map[i].rfmax * 1000) < *freq) { + if (tda18271_debug & DBG_ADV) + tda_map("(%d) rfmax = %d < freq = %d, " + "rf1_def = %d, rf2_def = %d, rf3_def = %d, " + "rf1 = %d, rf2 = %d, rf3 = %d, " + "rf_a1 = %d, rf_a2 = %d, " + "rf_b1 = %d, rf_b2 = %d\n", + i, map[i].rfmax * 1000, *freq, + map[i].rf1_def, map[i].rf2_def, map[i].rf3_def, + map[i].rf1, map[i].rf2, map[i].rf3, + map[i].rf_a1, map[i].rf_a2, + map[i].rf_b1, map[i].rf_b2); + if (map[i].rfmax == 0) + return -EINVAL; + i++; + } + if (rf_band) + *rf_band = map[i].rfband; + + tda_map("(%d) rf_band = %02x\n", i, map[i].rfband); + + return i; +} + +/*---------------------------------------------------------------------*/ + +struct tda18271_map_layout { + struct tda18271_pll_map *main_pll; + struct tda18271_pll_map *cal_pll; + + struct tda18271_map *rf_cal; + struct tda18271_map *rf_cal_kmco; + struct tda18271_map *rf_cal_dc_over_dt; + + struct tda18271_map *bp_filter; + struct tda18271_map *rf_band; + struct tda18271_map *gain_taper; + struct tda18271_map *ir_measure; +}; + +/*---------------------------------------------------------------------*/ + +int tda18271_lookup_pll_map(struct dvb_frontend *fe, + enum tda18271_map_type map_type, + u32 *freq, u8 *post_div, u8 *div) +{ + struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_pll_map *map = NULL; + unsigned int i = 0; + char *map_name; + int ret = 0; + + BUG_ON(!priv->maps); + + switch (map_type) { + case MAIN_PLL: + map = priv->maps->main_pll; + map_name = "main_pll"; + break; + case CAL_PLL: + map = priv->maps->cal_pll; + map_name = "cal_pll"; + break; + default: + /* we should never get here */ + map_name = "undefined"; + break; + } + + if (!map) { + tda_warn("%s map is not set!\n", map_name); + ret = -EINVAL; + goto fail; + } + + while ((map[i].lomax * 1000) < *freq) { + if (map[i + 1].lomax == 0) { + tda_map("%s: frequency (%d) out of range\n", + map_name, *freq); + ret = -ERANGE; + break; + } + i++; + } + *post_div = map[i].pd; + *div = map[i].d; + + tda_map("(%d) %s: post div = 0x%02x, div = 0x%02x\n", + i, map_name, *post_div, *div); +fail: + return ret; +} + +int tda18271_lookup_map(struct dvb_frontend *fe, + enum tda18271_map_type map_type, + u32 *freq, u8 *val) +{ + struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_map *map = NULL; + unsigned int i = 0; + char *map_name; + int ret = 0; + + BUG_ON(!priv->maps); + + switch (map_type) { + case BP_FILTER: + map = priv->maps->bp_filter; + map_name = "bp_filter"; + break; + case RF_CAL_KMCO: + map = priv->maps->rf_cal_kmco; + map_name = "km"; + break; + case RF_BAND: + map = priv->maps->rf_band; + map_name = "rf_band"; + break; + case GAIN_TAPER: + map = priv->maps->gain_taper; + map_name = "gain_taper"; + break; + case RF_CAL: + map = priv->maps->rf_cal; + map_name = "rf_cal"; + break; + case IR_MEASURE: + map = priv->maps->ir_measure; + map_name = "ir_measure"; + break; + case RF_CAL_DC_OVER_DT: + map = priv->maps->rf_cal_dc_over_dt; + map_name = "rf_cal_dc_over_dt"; + break; + default: + /* we should never get here */ + map_name = "undefined"; + break; + } + + if (!map) { + tda_warn("%s map is not set!\n", map_name); + ret = -EINVAL; + goto fail; + } + + while ((map[i].rfmax * 1000) < *freq) { + if (map[i + 1].rfmax == 0) { + tda_map("%s: frequency (%d) out of range\n", + map_name, *freq); + ret = -ERANGE; + break; + } + i++; + } + *val = map[i].val; + + tda_map("(%d) %s: 0x%02x\n", i, map_name, *val); +fail: + return ret; +} + +/*---------------------------------------------------------------------*/ + +static struct tda18271_std_map tda18271c1_std_map = { + .fm_radio = { .if_freq = 1250, .fm_rfn = 1, .agc_mode = 3, .std = 0, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x18 */ + .atv_b = { .if_freq = 6750, .fm_rfn = 0, .agc_mode = 1, .std = 6, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ + .atv_dk = { .if_freq = 7750, .fm_rfn = 0, .agc_mode = 1, .std = 7, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ + .atv_gh = { .if_freq = 7750, .fm_rfn = 0, .agc_mode = 1, .std = 7, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ + .atv_i = { .if_freq = 7750, .fm_rfn = 0, .agc_mode = 1, .std = 7, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ + .atv_l = { .if_freq = 7750, .fm_rfn = 0, .agc_mode = 1, .std = 7, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ + .atv_lc = { .if_freq = 1250, .fm_rfn = 0, .agc_mode = 1, .std = 7, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ + .atv_mn = { .if_freq = 5750, .fm_rfn = 0, .agc_mode = 1, .std = 5, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0d */ + .atsc_6 = { .if_freq = 3250, .fm_rfn = 0, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ + .dvbt_6 = { .if_freq = 3300, .fm_rfn = 0, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ + .dvbt_7 = { .if_freq = 3800, .fm_rfn = 0, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1d */ + .dvbt_8 = { .if_freq = 4300, .fm_rfn = 0, .agc_mode = 3, .std = 6, + .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1e */ + .qam_6 = { .if_freq = 4000, .fm_rfn = 0, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1d */ + .qam_7 = { .if_freq = 4500, .fm_rfn = 0, .agc_mode = 3, .std = 6, + .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1e */ + .qam_8 = { .if_freq = 5000, .fm_rfn = 0, .agc_mode = 3, .std = 7, + .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1f */ +}; + +static struct tda18271_std_map tda18271c2_std_map = { + .fm_radio = { .if_freq = 1250, .fm_rfn = 1, .agc_mode = 3, .std = 0, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x18 */ + .atv_b = { .if_freq = 6000, .fm_rfn = 0, .agc_mode = 1, .std = 5, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0d */ + .atv_dk = { .if_freq = 6900, .fm_rfn = 0, .agc_mode = 1, .std = 6, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ + .atv_gh = { .if_freq = 7100, .fm_rfn = 0, .agc_mode = 1, .std = 6, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ + .atv_i = { .if_freq = 7250, .fm_rfn = 0, .agc_mode = 1, .std = 6, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ + .atv_l = { .if_freq = 6900, .fm_rfn = 0, .agc_mode = 1, .std = 6, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ + .atv_lc = { .if_freq = 1250, .fm_rfn = 0, .agc_mode = 1, .std = 6, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ + .atv_mn = { .if_freq = 5400, .fm_rfn = 0, .agc_mode = 1, .std = 4, + .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0c */ + .atsc_6 = { .if_freq = 3250, .fm_rfn = 0, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ + .dvbt_6 = { .if_freq = 3300, .fm_rfn = 0, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ + .dvbt_7 = { .if_freq = 3500, .fm_rfn = 0, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ + .dvbt_8 = { .if_freq = 4000, .fm_rfn = 0, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1d */ + .qam_6 = { .if_freq = 4000, .fm_rfn = 0, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1d */ + .qam_7 = { .if_freq = 4500, .fm_rfn = 0, .agc_mode = 3, .std = 6, + .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1e */ + .qam_8 = { .if_freq = 5000, .fm_rfn = 0, .agc_mode = 3, .std = 7, + .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1f */ +}; + +/*---------------------------------------------------------------------*/ + +static struct tda18271_map_layout tda18271c1_map_layout = { + .main_pll = tda18271c1_main_pll, + .cal_pll = tda18271c1_cal_pll, + + .rf_cal = tda18271c1_rf_cal, + .rf_cal_kmco = tda18271c1_km, + + .bp_filter = tda18271_bp_filter, + .rf_band = tda18271_rf_band, + .gain_taper = tda18271_gain_taper, + .ir_measure = tda18271_ir_measure, +}; + +static struct tda18271_map_layout tda18271c2_map_layout = { + .main_pll = tda18271c2_main_pll, + .cal_pll = tda18271c2_cal_pll, + + .rf_cal = tda18271c2_rf_cal, + .rf_cal_kmco = tda18271c2_km, + + .rf_cal_dc_over_dt = tda18271_rf_cal_dc_over_dt, + + .bp_filter = tda18271_bp_filter, + .rf_band = tda18271_rf_band, + .gain_taper = tda18271_gain_taper, + .ir_measure = tda18271_ir_measure, +}; + +int tda18271_assign_map_layout(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + int ret = 0; + + switch (priv->id) { + case TDA18271HDC1: + priv->maps = &tda18271c1_map_layout; + memcpy(&priv->std, &tda18271c1_std_map, + sizeof(struct tda18271_std_map)); + break; + case TDA18271HDC2: + priv->maps = &tda18271c2_map_layout; + memcpy(&priv->std, &tda18271c2_std_map, + sizeof(struct tda18271_std_map)); + break; + default: + ret = -EINVAL; + break; + } + memcpy(priv->rf_cal_state, &tda18271_rf_band_template, + sizeof(tda18271_rf_band_template)); + + return ret; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/tuners/tda18271-priv.h b/drivers/media/tuners/tda18271-priv.h new file mode 100644 index 000000000000..454c152ccaa0 --- /dev/null +++ b/drivers/media/tuners/tda18271-priv.h @@ -0,0 +1,236 @@ +/* + tda18271-priv.h - private header for the NXP TDA18271 silicon tuner + + Copyright (C) 2007, 2008 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 __TDA18271_PRIV_H__ +#define __TDA18271_PRIV_H__ + +#include +#include +#include +#include "tuner-i2c.h" +#include "tda18271.h" + +#define R_ID 0x00 /* ID byte */ +#define R_TM 0x01 /* Thermo byte */ +#define R_PL 0x02 /* Power level byte */ +#define R_EP1 0x03 /* Easy Prog byte 1 */ +#define R_EP2 0x04 /* Easy Prog byte 2 */ +#define R_EP3 0x05 /* Easy Prog byte 3 */ +#define R_EP4 0x06 /* Easy Prog byte 4 */ +#define R_EP5 0x07 /* Easy Prog byte 5 */ +#define R_CPD 0x08 /* Cal Post-Divider byte */ +#define R_CD1 0x09 /* Cal Divider byte 1 */ +#define R_CD2 0x0a /* Cal Divider byte 2 */ +#define R_CD3 0x0b /* Cal Divider byte 3 */ +#define R_MPD 0x0c /* Main Post-Divider byte */ +#define R_MD1 0x0d /* Main Divider byte 1 */ +#define R_MD2 0x0e /* Main Divider byte 2 */ +#define R_MD3 0x0f /* Main Divider byte 3 */ +#define R_EB1 0x10 /* Extended byte 1 */ +#define R_EB2 0x11 /* Extended byte 2 */ +#define R_EB3 0x12 /* Extended byte 3 */ +#define R_EB4 0x13 /* Extended byte 4 */ +#define R_EB5 0x14 /* Extended byte 5 */ +#define R_EB6 0x15 /* Extended byte 6 */ +#define R_EB7 0x16 /* Extended byte 7 */ +#define R_EB8 0x17 /* Extended byte 8 */ +#define R_EB9 0x18 /* Extended byte 9 */ +#define R_EB10 0x19 /* Extended byte 10 */ +#define R_EB11 0x1a /* Extended byte 11 */ +#define R_EB12 0x1b /* Extended byte 12 */ +#define R_EB13 0x1c /* Extended byte 13 */ +#define R_EB14 0x1d /* Extended byte 14 */ +#define R_EB15 0x1e /* Extended byte 15 */ +#define R_EB16 0x1f /* Extended byte 16 */ +#define R_EB17 0x20 /* Extended byte 17 */ +#define R_EB18 0x21 /* Extended byte 18 */ +#define R_EB19 0x22 /* Extended byte 19 */ +#define R_EB20 0x23 /* Extended byte 20 */ +#define R_EB21 0x24 /* Extended byte 21 */ +#define R_EB22 0x25 /* Extended byte 22 */ +#define R_EB23 0x26 /* Extended byte 23 */ + +#define TDA18271_NUM_REGS 39 + +/*---------------------------------------------------------------------*/ + +struct tda18271_rf_tracking_filter_cal { + u32 rfmax; + u8 rfband; + u32 rf1_def; + u32 rf2_def; + u32 rf3_def; + u32 rf1; + u32 rf2; + u32 rf3; + s32 rf_a1; + s32 rf_b1; + s32 rf_a2; + s32 rf_b2; +}; + +enum tda18271_pll { + TDA18271_MAIN_PLL, + TDA18271_CAL_PLL, +}; + +struct tda18271_map_layout; + +enum tda18271_ver { + TDA18271HDC1, + TDA18271HDC2, +}; + +struct tda18271_priv { + unsigned char tda18271_regs[TDA18271_NUM_REGS]; + + struct list_head hybrid_tuner_instance_list; + struct tuner_i2c_props i2c_props; + + enum tda18271_mode mode; + enum tda18271_role role; + enum tda18271_i2c_gate gate; + enum tda18271_ver id; + enum tda18271_output_options output_opt; + enum tda18271_small_i2c small_i2c; + + unsigned int config; /* interface to saa713x / tda829x */ + unsigned int cal_initialized:1; + + u8 tm_rfcal; + + struct tda18271_map_layout *maps; + struct tda18271_std_map std; + struct tda18271_rf_tracking_filter_cal rf_cal_state[8]; + + struct mutex lock; + + u16 if_freq; + + u32 frequency; + u32 bandwidth; +}; + +/*---------------------------------------------------------------------*/ + +extern int tda18271_debug; + +#define DBG_INFO 1 +#define DBG_MAP 2 +#define DBG_REG 4 +#define DBG_ADV 8 +#define DBG_CAL 16 + +__attribute__((format(printf, 4, 5))) +int _tda_printk(struct tda18271_priv *state, const char *level, + const char *func, const char *fmt, ...); + +#define tda_printk(st, lvl, fmt, arg...) \ + _tda_printk(st, lvl, __func__, fmt, ##arg) + +#define tda_dprintk(st, lvl, fmt, arg...) \ +do { \ + if (tda18271_debug & lvl) \ + tda_printk(st, KERN_DEBUG, fmt, ##arg); \ +} while (0) + +#define tda_info(fmt, arg...) pr_info(fmt, ##arg) +#define tda_warn(fmt, arg...) tda_printk(priv, KERN_WARNING, fmt, ##arg) +#define tda_err(fmt, arg...) tda_printk(priv, KERN_ERR, fmt, ##arg) +#define tda_dbg(fmt, arg...) tda_dprintk(priv, DBG_INFO, fmt, ##arg) +#define tda_map(fmt, arg...) tda_dprintk(priv, DBG_MAP, fmt, ##arg) +#define tda_reg(fmt, arg...) tda_dprintk(priv, DBG_REG, fmt, ##arg) +#define tda_cal(fmt, arg...) tda_dprintk(priv, DBG_CAL, fmt, ##arg) + +#define tda_fail(ret) \ +({ \ + int __ret; \ + __ret = (ret < 0); \ + if (__ret) \ + tda_printk(priv, KERN_ERR, \ + "error %d on line %d\n", ret, __LINE__); \ + __ret; \ +}) + +/*---------------------------------------------------------------------*/ + +enum tda18271_map_type { + /* tda18271_pll_map */ + MAIN_PLL, + CAL_PLL, + /* tda18271_map */ + RF_CAL, + RF_CAL_KMCO, + RF_CAL_DC_OVER_DT, + BP_FILTER, + RF_BAND, + GAIN_TAPER, + IR_MEASURE, +}; + +extern int tda18271_lookup_pll_map(struct dvb_frontend *fe, + enum tda18271_map_type map_type, + u32 *freq, u8 *post_div, u8 *div); +extern int tda18271_lookup_map(struct dvb_frontend *fe, + enum tda18271_map_type map_type, + u32 *freq, u8 *val); + +extern int tda18271_lookup_thermometer(struct dvb_frontend *fe); + +extern int tda18271_lookup_rf_band(struct dvb_frontend *fe, + u32 *freq, u8 *rf_band); + +extern int tda18271_lookup_cid_target(struct dvb_frontend *fe, + u32 *freq, u8 *cid_target, + u16 *count_limit); + +extern int tda18271_assign_map_layout(struct dvb_frontend *fe); + +/*---------------------------------------------------------------------*/ + +extern int tda18271_read_regs(struct dvb_frontend *fe); +extern int tda18271_read_extended(struct dvb_frontend *fe); +extern int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len); +extern int tda18271_init_regs(struct dvb_frontend *fe); + +extern int tda18271_charge_pump_source(struct dvb_frontend *fe, + enum tda18271_pll pll, int force); +extern int tda18271_set_standby_mode(struct dvb_frontend *fe, + int sm, int sm_lt, int sm_xt); + +extern int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq); +extern int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq); + +extern int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq); +extern int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq); +extern int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq); +extern int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq); +extern int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq); +extern int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq); + +#endif /* __TDA18271_PRIV_H__ */ + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/tuners/tda18271.h b/drivers/media/tuners/tda18271.h new file mode 100644 index 000000000000..640bae4e6a5a --- /dev/null +++ b/drivers/media/tuners/tda18271.h @@ -0,0 +1,134 @@ +/* + tda18271.h - header for the Philips / NXP TDA18271 silicon tuner + + Copyright (C) 2007, 2008 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 __TDA18271_H__ +#define __TDA18271_H__ + +#include +#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/tuners/tda827x.c b/drivers/media/tuners/tda827x.c new file mode 100644 index 000000000000..a0d176267470 --- /dev/null +++ b/drivers/media/tuners/tda827x.c @@ -0,0 +1,917 @@ +/* + * + * (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. + */ + +#include +#include +#include +#include +#include + +#include "tda827x.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "tda827x: " args); \ + } while (0) + +struct tda827x_priv { + int i2c_addr; + struct i2c_adapter *i2c_adap; + struct tda827x_config *cfg; + + unsigned int sgIF; + unsigned char lpsel; + + u32 frequency; + u32 bandwidth; +}; + +static void tda827x_set_std(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct tda827x_priv *priv = fe->tuner_priv; + char *mode; + + priv->lpsel = 0; + if (params->std & V4L2_STD_MN) { + priv->sgIF = 92; + priv->lpsel = 1; + mode = "MN"; + } else if (params->std & V4L2_STD_B) { + priv->sgIF = 108; + mode = "B"; + } else if (params->std & V4L2_STD_GH) { + priv->sgIF = 124; + mode = "GH"; + } else if (params->std & V4L2_STD_PAL_I) { + priv->sgIF = 124; + mode = "I"; + } else if (params->std & V4L2_STD_DK) { + priv->sgIF = 124; + mode = "DK"; + } else if (params->std & V4L2_STD_SECAM_L) { + priv->sgIF = 124; + mode = "L"; + } else if (params->std & V4L2_STD_SECAM_LC) { + priv->sgIF = 20; + mode = "LC"; + } else { + priv->sgIF = 124; + mode = "xx"; + } + + if (params->mode == V4L2_TUNER_RADIO) { + priv->sgIF = 88; /* if frequency is 5.5 MHz */ + dprintk("setting tda827x to radio FM\n"); + } else + dprintk("setting tda827x to system %s\n", mode); +} + + +/* ------------------------------------------------------------------ */ + +struct tda827x_data { + u32 lomax; + u8 spd; + u8 bs; + u8 bp; + u8 cp; + u8 gc3; + u8 div1p5; +}; + +static const struct tda827x_data tda827x_table[] = { + { .lomax = 62000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, + { .lomax = 66000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, + { .lomax = 76000000, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, + { .lomax = 84000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, + { .lomax = 93000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 98000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 109000000, .spd = 3, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 123000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 133000000, .spd = 2, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 151000000, .spd = 2, .bs = 1, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 154000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 181000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 0, .div1p5 = 0}, + { .lomax = 185000000, .spd = 2, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 217000000, .spd = 2, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 244000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 265000000, .spd = 1, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 302000000, .spd = 1, .bs = 1, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 324000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 370000000, .spd = 1, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 454000000, .spd = 1, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 493000000, .spd = 0, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 530000000, .spd = 0, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 554000000, .spd = 0, .bs = 1, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 604000000, .spd = 0, .bs = 1, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, + { .lomax = 696000000, .spd = 0, .bs = 2, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, + { .lomax = 740000000, .spd = 0, .bs = 2, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, + { .lomax = 820000000, .spd = 0, .bs = 3, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, + { .lomax = 865000000, .spd = 0, .bs = 3, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, + { .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0} +}; + +static int tuner_transfer(struct dvb_frontend *fe, + struct i2c_msg *msg, + const int size) +{ + int rc; + struct tda827x_priv *priv = fe->tuner_priv; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + rc = i2c_transfer(priv->i2c_adap, msg, size); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (rc >= 0 && rc != size) + return -EIO; + + return rc; +} + +static int tda827xo_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct tda827x_priv *priv = fe->tuner_priv; + u8 buf[14]; + int rc; + + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + int i, tuner_freq, if_freq; + u32 N; + + dprintk("%s:\n", __func__); + if (c->bandwidth_hz == 0) { + if_freq = 5000000; + } else if (c->bandwidth_hz <= 6000000) { + if_freq = 4000000; + } else if (c->bandwidth_hz <= 7000000) { + if_freq = 4500000; + } else { /* 8 MHz */ + if_freq = 5000000; + } + tuner_freq = c->frequency; + + i = 0; + while (tda827x_table[i].lomax < tuner_freq) { + if (tda827x_table[i + 1].lomax == 0) + break; + i++; + } + + tuner_freq += if_freq; + + N = ((tuner_freq + 125000) / 250000) << (tda827x_table[i].spd + 2); + buf[0] = 0; + buf[1] = (N>>8) | 0x40; + buf[2] = N & 0xff; + buf[3] = 0; + buf[4] = 0x52; + buf[5] = (tda827x_table[i].spd << 6) + (tda827x_table[i].div1p5 << 5) + + (tda827x_table[i].bs << 3) + + tda827x_table[i].bp; + buf[6] = (tda827x_table[i].gc3 << 4) + 0x8f; + buf[7] = 0xbf; + buf[8] = 0x2a; + buf[9] = 0x05; + buf[10] = 0xff; + buf[11] = 0x00; + buf[12] = 0x00; + buf[13] = 0x40; + + msg.len = 14; + rc = tuner_transfer(fe, &msg, 1); + if (rc < 0) + goto err; + + msleep(500); + /* correct CP value */ + buf[0] = 0x30; + buf[1] = 0x50 + tda827x_table[i].cp; + msg.len = 2; + + rc = tuner_transfer(fe, &msg, 1); + if (rc < 0) + goto err; + + priv->frequency = c->frequency; + priv->bandwidth = c->bandwidth_hz; + + return 0; + +err: + printk(KERN_ERR "%s: could not write to tuner at addr: 0x%02x\n", + __func__, priv->i2c_addr << 1); + return rc; +} + +static int tda827xo_sleep(struct dvb_frontend *fe) +{ + struct tda827x_priv *priv = fe->tuner_priv; + static u8 buf[] = { 0x30, 0xd0 }; + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + + dprintk("%s:\n", __func__); + tuner_transfer(fe, &msg, 1); + + if (priv->cfg && priv->cfg->sleep) + priv->cfg->sleep(fe); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int tda827xo_set_analog_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + unsigned char tuner_reg[8]; + unsigned char reg2[2]; + u32 N; + int i; + struct tda827x_priv *priv = fe->tuner_priv; + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0 }; + unsigned int freq = params->frequency; + + tda827x_set_std(fe, params); + + if (params->mode == V4L2_TUNER_RADIO) + freq = freq / 1000; + + N = freq + priv->sgIF; + + i = 0; + while (tda827x_table[i].lomax < N * 62500) { + if (tda827x_table[i + 1].lomax == 0) + break; + i++; + } + + N = N << tda827x_table[i].spd; + + tuner_reg[0] = 0; + tuner_reg[1] = (unsigned char)(N>>8); + tuner_reg[2] = (unsigned char) N; + tuner_reg[3] = 0x40; + tuner_reg[4] = 0x52 + (priv->lpsel << 5); + tuner_reg[5] = (tda827x_table[i].spd << 6) + + (tda827x_table[i].div1p5 << 5) + + (tda827x_table[i].bs << 3) + tda827x_table[i].bp; + tuner_reg[6] = 0x8f + (tda827x_table[i].gc3 << 4); + tuner_reg[7] = 0x8f; + + msg.buf = tuner_reg; + msg.len = 8; + tuner_transfer(fe, &msg, 1); + + msg.buf = reg2; + msg.len = 2; + reg2[0] = 0x80; + reg2[1] = 0; + tuner_transfer(fe, &msg, 1); + + reg2[0] = 0x60; + reg2[1] = 0xbf; + tuner_transfer(fe, &msg, 1); + + reg2[0] = 0x30; + reg2[1] = tuner_reg[4] + 0x80; + tuner_transfer(fe, &msg, 1); + + msleep(1); + reg2[0] = 0x30; + reg2[1] = tuner_reg[4] + 4; + tuner_transfer(fe, &msg, 1); + + msleep(1); + reg2[0] = 0x30; + reg2[1] = tuner_reg[4]; + tuner_transfer(fe, &msg, 1); + + msleep(550); + reg2[0] = 0x30; + reg2[1] = (tuner_reg[4] & 0xfc) + tda827x_table[i].cp; + tuner_transfer(fe, &msg, 1); + + reg2[0] = 0x60; + reg2[1] = 0x3f; + tuner_transfer(fe, &msg, 1); + + reg2[0] = 0x80; + reg2[1] = 0x08; /* Vsync en */ + tuner_transfer(fe, &msg, 1); + + priv->frequency = params->frequency; + + return 0; +} + +static void tda827xo_agcf(struct dvb_frontend *fe) +{ + struct tda827x_priv *priv = fe->tuner_priv; + unsigned char data[] = { 0x80, 0x0c }; + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = data, .len = 2}; + + tuner_transfer(fe, &msg, 1); +} + +/* ------------------------------------------------------------------ */ + +struct tda827xa_data { + u32 lomax; + u8 svco; + u8 spd; + u8 scr; + u8 sbs; + u8 gc3; +}; + +static struct tda827xa_data tda827xa_dvbt[] = { + { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 1}, + { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, + { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, + { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, + { .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, + { .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, + { .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, + { .lomax = 290000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, + { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1}, + { .lomax = 550000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, + { .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, + { .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, + { .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, + { .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, + { .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, + { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0}, + { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} +}; + +static struct tda827xa_data tda827xa_dvbc[] = { + { .lomax = 50125000, .svco = 2, .spd = 4, .scr = 2, .sbs = 0, .gc3 = 3}, + { .lomax = 58500000, .svco = 3, .spd = 4, .scr = 2, .sbs = 0, .gc3 = 3}, + { .lomax = 69250000, .svco = 0, .spd = 3, .scr = 2, .sbs = 0, .gc3 = 3}, + { .lomax = 83625000, .svco = 1, .spd = 3, .scr = 2, .sbs = 0, .gc3 = 3}, + { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 2, .sbs = 0, .gc3 = 3}, + { .lomax = 100250000, .svco = 2, .spd = 3, .scr = 2, .sbs = 1, .gc3 = 1}, + { .lomax = 117000000, .svco = 3, .spd = 3, .scr = 2, .sbs = 1, .gc3 = 1}, + { .lomax = 138500000, .svco = 0, .spd = 2, .scr = 2, .sbs = 1, .gc3 = 1}, + { .lomax = 167250000, .svco = 1, .spd = 2, .scr = 2, .sbs = 1, .gc3 = 1}, + { .lomax = 187000000, .svco = 2, .spd = 2, .scr = 2, .sbs = 1, .gc3 = 1}, + { .lomax = 200500000, .svco = 2, .spd = 2, .scr = 2, .sbs = 2, .gc3 = 1}, + { .lomax = 234000000, .svco = 3, .spd = 2, .scr = 2, .sbs = 2, .gc3 = 3}, + { .lomax = 277000000, .svco = 0, .spd = 1, .scr = 2, .sbs = 2, .gc3 = 3}, + { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 2, .sbs = 2, .gc3 = 1}, + { .lomax = 334500000, .svco = 1, .spd = 1, .scr = 2, .sbs = 3, .gc3 = 3}, + { .lomax = 401000000, .svco = 2, .spd = 1, .scr = 2, .sbs = 3, .gc3 = 3}, + { .lomax = 468000000, .svco = 3, .spd = 1, .scr = 2, .sbs = 3, .gc3 = 1}, + { .lomax = 535000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1}, + { .lomax = 554000000, .svco = 0, .spd = 0, .scr = 2, .sbs = 3, .gc3 = 1}, + { .lomax = 638000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1}, + { .lomax = 669000000, .svco = 1, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 1}, + { .lomax = 720000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1}, + { .lomax = 802000000, .svco = 2, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 1}, + { .lomax = 835000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1}, + { .lomax = 885000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1}, + { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 1}, + { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} +}; + +static struct tda827xa_data tda827xa_analog[] = { + { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 3}, + { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3}, + { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3}, + { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3}, + { .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, + { .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 3}, + { .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 3}, + { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, + { .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 3}, + { .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 3}, + { .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1}, + { .lomax = 554000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, + { .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, + { .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, + { .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, + { .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, + { .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, + { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0}, + { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} +}; + +static int tda827xa_sleep(struct dvb_frontend *fe) +{ + struct tda827x_priv *priv = fe->tuner_priv; + static u8 buf[] = { 0x30, 0x90 }; + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + + dprintk("%s:\n", __func__); + + tuner_transfer(fe, &msg, 1); + + if (priv->cfg && priv->cfg->sleep) + priv->cfg->sleep(fe); + + return 0; +} + +static void tda827xa_lna_gain(struct dvb_frontend *fe, int high, + struct analog_parameters *params) +{ + struct tda827x_priv *priv = fe->tuner_priv; + unsigned char buf[] = {0x22, 0x01}; + int arg; + int gp_func; + struct i2c_msg msg = { .flags = 0, .buf = buf, .len = sizeof(buf) }; + + if (NULL == priv->cfg) { + dprintk("tda827x_config not defined, cannot set LNA gain!\n"); + return; + } + msg.addr = priv->cfg->switch_addr; + if (priv->cfg->config) { + if (high) + dprintk("setting LNA to high gain\n"); + else + dprintk("setting LNA to low gain\n"); + } + switch (priv->cfg->config) { + case 0: /* no LNA */ + break; + case 1: /* switch is GPIO 0 of tda8290 */ + case 2: + if (params == NULL) { + gp_func = 0; + arg = 0; + } else { + /* turn Vsync on */ + gp_func = 1; + if (params->std & V4L2_STD_MN) + arg = 1; + else + arg = 0; + } + if (fe->callback) + fe->callback(priv->i2c_adap->algo_data, + DVB_FRONTEND_COMPONENT_TUNER, + gp_func, arg); + buf[1] = high ? 0 : 1; + if (priv->cfg->config == 2) + buf[1] = high ? 1 : 0; + tuner_transfer(fe, &msg, 1); + break; + case 3: /* switch with GPIO of saa713x */ + if (fe->callback) + fe->callback(priv->i2c_adap->algo_data, + DVB_FRONTEND_COMPONENT_TUNER, 0, high); + break; + } +} + +static int tda827xa_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct tda827x_priv *priv = fe->tuner_priv; + struct tda827xa_data *frequency_map = tda827xa_dvbt; + u8 buf[11]; + + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + + int i, tuner_freq, if_freq, rc; + u32 N; + + dprintk("%s:\n", __func__); + + tda827xa_lna_gain(fe, 1, NULL); + msleep(20); + + if (c->bandwidth_hz == 0) { + if_freq = 5000000; + } else if (c->bandwidth_hz <= 6000000) { + if_freq = 4000000; + } else if (c->bandwidth_hz <= 7000000) { + if_freq = 4500000; + } else { /* 8 MHz */ + if_freq = 5000000; + } + tuner_freq = c->frequency; + + switch (c->delivery_system) { + case SYS_DVBC_ANNEX_A: + case SYS_DVBC_ANNEX_C: + dprintk("%s select tda827xa_dvbc\n", __func__); + frequency_map = tda827xa_dvbc; + break; + default: + break; + } + + i = 0; + while (frequency_map[i].lomax < tuner_freq) { + if (frequency_map[i + 1].lomax == 0) + break; + i++; + } + + tuner_freq += if_freq; + + N = ((tuner_freq + 31250) / 62500) << frequency_map[i].spd; + buf[0] = 0; // subaddress + buf[1] = N >> 8; + buf[2] = N & 0xff; + buf[3] = 0; + buf[4] = 0x16; + buf[5] = (frequency_map[i].spd << 5) + (frequency_map[i].svco << 3) + + frequency_map[i].sbs; + buf[6] = 0x4b + (frequency_map[i].gc3 << 4); + buf[7] = 0x1c; + buf[8] = 0x06; + buf[9] = 0x24; + buf[10] = 0x00; + msg.len = 11; + rc = tuner_transfer(fe, &msg, 1); + if (rc < 0) + goto err; + + buf[0] = 0x90; + buf[1] = 0xff; + buf[2] = 0x60; + buf[3] = 0x00; + buf[4] = 0x59; // lpsel, for 6MHz + 2 + msg.len = 5; + rc = tuner_transfer(fe, &msg, 1); + if (rc < 0) + goto err; + + buf[0] = 0xa0; + buf[1] = 0x40; + msg.len = 2; + rc = tuner_transfer(fe, &msg, 1); + if (rc < 0) + goto err; + + msleep(11); + msg.flags = I2C_M_RD; + rc = tuner_transfer(fe, &msg, 1); + if (rc < 0) + goto err; + msg.flags = 0; + + buf[1] >>= 4; + dprintk("tda8275a AGC2 gain is: %d\n", buf[1]); + if ((buf[1]) < 2) { + tda827xa_lna_gain(fe, 0, NULL); + buf[0] = 0x60; + buf[1] = 0x0c; + rc = tuner_transfer(fe, &msg, 1); + if (rc < 0) + goto err; + } + + buf[0] = 0xc0; + buf[1] = 0x99; // lpsel, for 6MHz + 2 + rc = tuner_transfer(fe, &msg, 1); + if (rc < 0) + goto err; + + buf[0] = 0x60; + buf[1] = 0x3c; + rc = tuner_transfer(fe, &msg, 1); + if (rc < 0) + goto err; + + /* correct CP value */ + buf[0] = 0x30; + buf[1] = 0x10 + frequency_map[i].scr; + rc = tuner_transfer(fe, &msg, 1); + if (rc < 0) + goto err; + + msleep(163); + buf[0] = 0xc0; + buf[1] = 0x39; // lpsel, for 6MHz + 2 + rc = tuner_transfer(fe, &msg, 1); + if (rc < 0) + goto err; + + msleep(3); + /* freeze AGC1 */ + buf[0] = 0x50; + buf[1] = 0x4f + (frequency_map[i].gc3 << 4); + rc = tuner_transfer(fe, &msg, 1); + if (rc < 0) + goto err; + + priv->frequency = c->frequency; + priv->bandwidth = c->bandwidth_hz; + + return 0; + +err: + printk(KERN_ERR "%s: could not write to tuner at addr: 0x%02x\n", + __func__, priv->i2c_addr << 1); + return rc; +} + + +static int tda827xa_set_analog_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + unsigned char tuner_reg[11]; + u32 N; + int i; + struct tda827x_priv *priv = fe->tuner_priv; + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = tuner_reg, .len = sizeof(tuner_reg) }; + unsigned int freq = params->frequency; + + tda827x_set_std(fe, params); + + tda827xa_lna_gain(fe, 1, params); + msleep(10); + + if (params->mode == V4L2_TUNER_RADIO) + freq = freq / 1000; + + N = freq + priv->sgIF; + + i = 0; + while (tda827xa_analog[i].lomax < N * 62500) { + if (tda827xa_analog[i + 1].lomax == 0) + break; + i++; + } + + N = N << tda827xa_analog[i].spd; + + tuner_reg[0] = 0; + tuner_reg[1] = (unsigned char)(N>>8); + tuner_reg[2] = (unsigned char) N; + tuner_reg[3] = 0; + tuner_reg[4] = 0x16; + tuner_reg[5] = (tda827xa_analog[i].spd << 5) + + (tda827xa_analog[i].svco << 3) + + tda827xa_analog[i].sbs; + tuner_reg[6] = 0x8b + (tda827xa_analog[i].gc3 << 4); + tuner_reg[7] = 0x1c; + tuner_reg[8] = 4; + tuner_reg[9] = 0x20; + tuner_reg[10] = 0x00; + msg.len = 11; + tuner_transfer(fe, &msg, 1); + + tuner_reg[0] = 0x90; + tuner_reg[1] = 0xff; + tuner_reg[2] = 0xe0; + tuner_reg[3] = 0; + tuner_reg[4] = 0x99 + (priv->lpsel << 1); + msg.len = 5; + tuner_transfer(fe, &msg, 1); + + tuner_reg[0] = 0xa0; + tuner_reg[1] = 0xc0; + msg.len = 2; + tuner_transfer(fe, &msg, 1); + + tuner_reg[0] = 0x30; + tuner_reg[1] = 0x10 + tda827xa_analog[i].scr; + tuner_transfer(fe, &msg, 1); + + msg.flags = I2C_M_RD; + tuner_transfer(fe, &msg, 1); + msg.flags = 0; + tuner_reg[1] >>= 4; + dprintk("AGC2 gain is: %d\n", tuner_reg[1]); + if (tuner_reg[1] < 1) + tda827xa_lna_gain(fe, 0, params); + + msleep(100); + tuner_reg[0] = 0x60; + tuner_reg[1] = 0x3c; + tuner_transfer(fe, &msg, 1); + + msleep(163); + tuner_reg[0] = 0x50; + tuner_reg[1] = 0x8f + (tda827xa_analog[i].gc3 << 4); + tuner_transfer(fe, &msg, 1); + + tuner_reg[0] = 0x80; + tuner_reg[1] = 0x28; + tuner_transfer(fe, &msg, 1); + + tuner_reg[0] = 0xb0; + tuner_reg[1] = 0x01; + tuner_transfer(fe, &msg, 1); + + tuner_reg[0] = 0xc0; + tuner_reg[1] = 0x19 + (priv->lpsel << 1); + tuner_transfer(fe, &msg, 1); + + priv->frequency = params->frequency; + + return 0; +} + +static void tda827xa_agcf(struct dvb_frontend *fe) +{ + struct tda827x_priv *priv = fe->tuner_priv; + unsigned char data[] = {0x80, 0x2c}; + struct i2c_msg msg = {.addr = priv->i2c_addr, .flags = 0, + .buf = data, .len = 2}; + tuner_transfer(fe, &msg, 1); +} + +/* ------------------------------------------------------------------ */ + +static int tda827x_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int tda827x_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tda827x_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int tda827x_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct tda827x_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +static int tda827x_init(struct dvb_frontend *fe) +{ + struct tda827x_priv *priv = fe->tuner_priv; + dprintk("%s:\n", __func__); + if (priv->cfg && priv->cfg->init) + priv->cfg->init(fe); + + return 0; +} + +static int tda827x_probe_version(struct dvb_frontend *fe); + +static int tda827x_initial_init(struct dvb_frontend *fe) +{ + int ret; + ret = tda827x_probe_version(fe); + if (ret) + return ret; + return fe->ops.tuner_ops.init(fe); +} + +static int tda827x_initial_sleep(struct dvb_frontend *fe) +{ + int ret; + ret = tda827x_probe_version(fe); + if (ret) + return ret; + return fe->ops.tuner_ops.sleep(fe); +} + +static struct dvb_tuner_ops tda827xo_tuner_ops = { + .info = { + .name = "Philips TDA827X", + .frequency_min = 55000000, + .frequency_max = 860000000, + .frequency_step = 250000 + }, + .release = tda827x_release, + .init = tda827x_initial_init, + .sleep = tda827x_initial_sleep, + .set_params = tda827xo_set_params, + .set_analog_params = tda827xo_set_analog_params, + .get_frequency = tda827x_get_frequency, + .get_bandwidth = tda827x_get_bandwidth, +}; + +static struct dvb_tuner_ops tda827xa_tuner_ops = { + .info = { + .name = "Philips TDA827XA", + .frequency_min = 44000000, + .frequency_max = 906000000, + .frequency_step = 62500 + }, + .release = tda827x_release, + .init = tda827x_init, + .sleep = tda827xa_sleep, + .set_params = tda827xa_set_params, + .set_analog_params = tda827xa_set_analog_params, + .get_frequency = tda827x_get_frequency, + .get_bandwidth = tda827x_get_bandwidth, +}; + +static int tda827x_probe_version(struct dvb_frontend *fe) +{ + u8 data; + int rc; + struct tda827x_priv *priv = fe->tuner_priv; + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = I2C_M_RD, + .buf = &data, .len = 1 }; + + rc = tuner_transfer(fe, &msg, 1); + + if (rc < 0) { + printk("%s: could not read from tuner at addr: 0x%02x\n", + __func__, msg.addr << 1); + return rc; + } + if ((data & 0x3c) == 0) { + dprintk("tda827x tuner found\n"); + fe->ops.tuner_ops.init = tda827x_init; + fe->ops.tuner_ops.sleep = tda827xo_sleep; + if (priv->cfg) + priv->cfg->agcf = tda827xo_agcf; + } else { + dprintk("tda827xa tuner found\n"); + memcpy(&fe->ops.tuner_ops, &tda827xa_tuner_ops, sizeof(struct dvb_tuner_ops)); + if (priv->cfg) + priv->cfg->agcf = tda827xa_agcf; + } + return 0; +} + +struct dvb_frontend *tda827x_attach(struct dvb_frontend *fe, int addr, + struct i2c_adapter *i2c, + struct tda827x_config *cfg) +{ + struct tda827x_priv *priv = NULL; + + dprintk("%s:\n", __func__); + priv = kzalloc(sizeof(struct tda827x_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c_addr = addr; + priv->i2c_adap = i2c; + priv->cfg = cfg; + memcpy(&fe->ops.tuner_ops, &tda827xo_tuner_ops, sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + + dprintk("type set to %s\n", fe->ops.tuner_ops.info.name); + + return fe; +} +EXPORT_SYMBOL_GPL(tda827x_attach); + +MODULE_DESCRIPTION("DVB TDA827x driver"); +MODULE_AUTHOR("Hartmut Hackmann "); +MODULE_AUTHOR("Michael Krufky "); +MODULE_LICENSE("GPL"); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/tuners/tda827x.h b/drivers/media/tuners/tda827x.h new file mode 100644 index 000000000000..7d72ce0a0c2d --- /dev/null +++ b/drivers/media/tuners/tda827x.h @@ -0,0 +1,68 @@ + /* + 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 +#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/tuners/tda8290.c b/drivers/media/tuners/tda8290.c new file mode 100644 index 000000000000..8c4852114eeb --- /dev/null +++ b/drivers/media/tuners/tda8290.c @@ -0,0 +1,874 @@ +/* + + i2c tv tuner chip device driver + controls the philips tda8290+75 tuner chip combo. + + 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 "tda8290" module was split apart from the original "tuner" module. +*/ + +#include +#include +#include +#include +#include "tuner-i2c.h" +#include "tda8290.h" +#include "tda827x.h" +#include "tda18271.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable verbose debug messages"); + +static int deemphasis_50; +module_param(deemphasis_50, int, 0644); +MODULE_PARM_DESC(deemphasis_50, "0 - 75us deemphasis; 1 - 50us deemphasis"); + +/* ---------------------------------------------------------------------- */ + +struct tda8290_priv { + struct tuner_i2c_props i2c_props; + + unsigned char tda8290_easy_mode; + + unsigned char tda827x_addr; + + unsigned char ver; +#define TDA8290 1 +#define TDA8295 2 +#define TDA8275 4 +#define TDA8275A 8 +#define TDA18271 16 + + struct tda827x_config cfg; +}; + +/*---------------------------------------------------------------------*/ + +static int tda8290_i2c_bridge(struct dvb_frontend *fe, int close) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + + unsigned char enable[2] = { 0x21, 0xC0 }; + unsigned char disable[2] = { 0x21, 0x00 }; + unsigned char *msg; + + if (close) { + msg = enable; + tuner_i2c_xfer_send(&priv->i2c_props, msg, 2); + /* let the bridge stabilize */ + msleep(20); + } else { + msg = disable; + tuner_i2c_xfer_send(&priv->i2c_props, msg, 2); + } + + return 0; +} + +static int tda8295_i2c_bridge(struct dvb_frontend *fe, int close) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + + unsigned char enable[2] = { 0x45, 0xc1 }; + unsigned char disable[2] = { 0x46, 0x00 }; + unsigned char buf[3] = { 0x45, 0x01, 0x00 }; + unsigned char *msg; + + if (close) { + msg = enable; + tuner_i2c_xfer_send(&priv->i2c_props, msg, 2); + /* let the bridge stabilize */ + msleep(20); + } else { + msg = disable; + tuner_i2c_xfer_send_recv(&priv->i2c_props, msg, 1, &msg[1], 1); + + buf[2] = msg[1]; + buf[2] &= ~0x04; + tuner_i2c_xfer_send(&priv->i2c_props, buf, 3); + msleep(5); + + msg[1] |= 0x04; + tuner_i2c_xfer_send(&priv->i2c_props, msg, 2); + } + + return 0; +} + +/*---------------------------------------------------------------------*/ + +static void set_audio(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + char* mode; + + if (params->std & V4L2_STD_MN) { + priv->tda8290_easy_mode = 0x01; + mode = "MN"; + } else if (params->std & V4L2_STD_B) { + priv->tda8290_easy_mode = 0x02; + mode = "B"; + } else if (params->std & V4L2_STD_GH) { + priv->tda8290_easy_mode = 0x04; + mode = "GH"; + } else if (params->std & V4L2_STD_PAL_I) { + priv->tda8290_easy_mode = 0x08; + mode = "I"; + } else if (params->std & V4L2_STD_DK) { + priv->tda8290_easy_mode = 0x10; + mode = "DK"; + } else if (params->std & V4L2_STD_SECAM_L) { + priv->tda8290_easy_mode = 0x20; + mode = "L"; + } else if (params->std & V4L2_STD_SECAM_LC) { + priv->tda8290_easy_mode = 0x40; + mode = "LC"; + } else { + priv->tda8290_easy_mode = 0x10; + mode = "xx"; + } + + if (params->mode == V4L2_TUNER_RADIO) { + /* Set TDA8295 to FM radio; Start TDA8290 with MN values */ + priv->tda8290_easy_mode = (priv->ver & TDA8295) ? 0x80 : 0x01; + tuner_dbg("setting to radio FM\n"); + } else { + tuner_dbg("setting tda829x to system %s\n", mode); + } +} + +static struct { + unsigned char seq[2]; +} fm_mode[] = { + { { 0x01, 0x81} }, /* Put device into expert mode */ + { { 0x03, 0x48} }, /* Disable NOTCH and VIDEO filters */ + { { 0x04, 0x04} }, /* Disable color carrier filter (SSIF) */ + { { 0x05, 0x04} }, /* ADC headroom */ + { { 0x06, 0x10} }, /* group delay flat */ + + { { 0x07, 0x00} }, /* use the same radio DTO values as a tda8295 */ + { { 0x08, 0x00} }, + { { 0x09, 0x80} }, + { { 0x0a, 0xda} }, + { { 0x0b, 0x4b} }, + { { 0x0c, 0x68} }, + + { { 0x0d, 0x00} }, /* PLL off, no video carrier detect */ + { { 0x14, 0x00} }, /* disable auto mute if no video */ +}; + +static void tda8290_set_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + + unsigned char soft_reset[] = { 0x00, 0x00 }; + unsigned char easy_mode[] = { 0x01, priv->tda8290_easy_mode }; + unsigned char expert_mode[] = { 0x01, 0x80 }; + unsigned char agc_out_on[] = { 0x02, 0x00 }; + unsigned char gainset_off[] = { 0x28, 0x14 }; + unsigned char if_agc_spd[] = { 0x0f, 0x88 }; + unsigned char adc_head_6[] = { 0x05, 0x04 }; + unsigned char adc_head_9[] = { 0x05, 0x02 }; + unsigned char adc_head_12[] = { 0x05, 0x01 }; + unsigned char pll_bw_nom[] = { 0x0d, 0x47 }; + unsigned char pll_bw_low[] = { 0x0d, 0x27 }; + unsigned char gainset_2[] = { 0x28, 0x64 }; + unsigned char agc_rst_on[] = { 0x0e, 0x0b }; + unsigned char agc_rst_off[] = { 0x0e, 0x09 }; + unsigned char if_agc_set[] = { 0x0f, 0x81 }; + unsigned char addr_adc_sat = 0x1a; + unsigned char addr_agc_stat = 0x1d; + unsigned char addr_pll_stat = 0x1b; + unsigned char adc_sat, agc_stat, + pll_stat; + int i; + + set_audio(fe, params); + + if (priv->cfg.config) + tuner_dbg("tda827xa config is 0x%02x\n", priv->cfg.config); + tuner_i2c_xfer_send(&priv->i2c_props, easy_mode, 2); + tuner_i2c_xfer_send(&priv->i2c_props, agc_out_on, 2); + tuner_i2c_xfer_send(&priv->i2c_props, soft_reset, 2); + msleep(1); + + if (params->mode == V4L2_TUNER_RADIO) { + unsigned char deemphasis[] = { 0x13, 1 }; + + /* FIXME: allow using a different deemphasis */ + + if (deemphasis_50) + deemphasis[1] = 2; + + for (i = 0; i < ARRAY_SIZE(fm_mode); i++) + tuner_i2c_xfer_send(&priv->i2c_props, fm_mode[i].seq, 2); + + tuner_i2c_xfer_send(&priv->i2c_props, deemphasis, 2); + } else { + expert_mode[1] = priv->tda8290_easy_mode + 0x80; + tuner_i2c_xfer_send(&priv->i2c_props, expert_mode, 2); + tuner_i2c_xfer_send(&priv->i2c_props, gainset_off, 2); + tuner_i2c_xfer_send(&priv->i2c_props, if_agc_spd, 2); + if (priv->tda8290_easy_mode & 0x60) + tuner_i2c_xfer_send(&priv->i2c_props, adc_head_9, 2); + else + tuner_i2c_xfer_send(&priv->i2c_props, adc_head_6, 2); + tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2); + } + + + tda8290_i2c_bridge(fe, 1); + + if (fe->ops.tuner_ops.set_analog_params) + fe->ops.tuner_ops.set_analog_params(fe, params); + + for (i = 0; i < 3; i++) { + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_pll_stat, 1, &pll_stat, 1); + if (pll_stat & 0x80) { + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_adc_sat, 1, + &adc_sat, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_agc_stat, 1, + &agc_stat, 1); + tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat); + break; + } else { + tuner_dbg("tda8290 not locked, no signal?\n"); + msleep(100); + } + } + /* adjust headroom resp. gain */ + if ((agc_stat > 115) || (!(pll_stat & 0x80) && (adc_sat < 20))) { + tuner_dbg("adjust gain, step 1. Agc: %d, ADC stat: %d, lock: %d\n", + agc_stat, adc_sat, pll_stat & 0x80); + tuner_i2c_xfer_send(&priv->i2c_props, gainset_2, 2); + msleep(100); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_agc_stat, 1, &agc_stat, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_pll_stat, 1, &pll_stat, 1); + if ((agc_stat > 115) || !(pll_stat & 0x80)) { + tuner_dbg("adjust gain, step 2. Agc: %d, lock: %d\n", + agc_stat, pll_stat & 0x80); + if (priv->cfg.agcf) + priv->cfg.agcf(fe); + msleep(100); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_agc_stat, 1, + &agc_stat, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_pll_stat, 1, + &pll_stat, 1); + if((agc_stat > 115) || !(pll_stat & 0x80)) { + tuner_dbg("adjust gain, step 3. Agc: %d\n", agc_stat); + tuner_i2c_xfer_send(&priv->i2c_props, adc_head_12, 2); + tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_low, 2); + msleep(100); + } + } + } + + /* l/ l' deadlock? */ + if(priv->tda8290_easy_mode & 0x60) { + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_adc_sat, 1, + &adc_sat, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_pll_stat, 1, + &pll_stat, 1); + if ((adc_sat > 20) || !(pll_stat & 0x80)) { + tuner_dbg("trying to resolve SECAM L deadlock\n"); + tuner_i2c_xfer_send(&priv->i2c_props, agc_rst_on, 2); + msleep(40); + tuner_i2c_xfer_send(&priv->i2c_props, agc_rst_off, 2); + } + } + + tda8290_i2c_bridge(fe, 0); + tuner_i2c_xfer_send(&priv->i2c_props, if_agc_set, 2); +} + +/*---------------------------------------------------------------------*/ + +static void tda8295_power(struct dvb_frontend *fe, int enable) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + unsigned char buf[] = { 0x30, 0x00 }; /* clb_stdbt */ + + tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1); + + if (enable) + buf[1] = 0x01; + else + buf[1] = 0x03; + + tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); +} + +static void tda8295_set_easy_mode(struct dvb_frontend *fe, int enable) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + unsigned char buf[] = { 0x01, 0x00 }; + + tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1); + + if (enable) + buf[1] = 0x01; /* rising edge sets regs 0x02 - 0x23 */ + else + buf[1] = 0x00; /* reset active bit */ + + tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); +} + +static void tda8295_set_video_std(struct dvb_frontend *fe) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + unsigned char buf[] = { 0x00, priv->tda8290_easy_mode }; + + tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); + + tda8295_set_easy_mode(fe, 1); + msleep(20); + tda8295_set_easy_mode(fe, 0); +} + +/*---------------------------------------------------------------------*/ + +static void tda8295_agc1_out(struct dvb_frontend *fe, int enable) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + unsigned char buf[] = { 0x02, 0x00 }; /* DIV_FUNC */ + + tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1); + + if (enable) + buf[1] &= ~0x40; + else + buf[1] |= 0x40; + + tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); +} + +static void tda8295_agc2_out(struct dvb_frontend *fe, int enable) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + unsigned char set_gpio_cf[] = { 0x44, 0x00 }; + unsigned char set_gpio_val[] = { 0x46, 0x00 }; + + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &set_gpio_cf[0], 1, &set_gpio_cf[1], 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &set_gpio_val[0], 1, &set_gpio_val[1], 1); + + set_gpio_cf[1] &= 0xf0; /* clear GPIO_0 bits 3-0 */ + + if (enable) { + set_gpio_cf[1] |= 0x01; /* config GPIO_0 as Open Drain Out */ + set_gpio_val[1] &= 0xfe; /* set GPIO_0 pin low */ + } + tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_cf, 2); + tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_val, 2); +} + +static int tda8295_has_signal(struct dvb_frontend *fe) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + + unsigned char hvpll_stat = 0x26; + unsigned char ret; + + tuner_i2c_xfer_send_recv(&priv->i2c_props, &hvpll_stat, 1, &ret, 1); + return (ret & 0x01) ? 65535 : 0; +} + +/*---------------------------------------------------------------------*/ + +static void tda8295_set_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + + unsigned char blanking_mode[] = { 0x1d, 0x00 }; + + set_audio(fe, params); + + tuner_dbg("%s: freq = %d\n", __func__, params->frequency); + + tda8295_power(fe, 1); + tda8295_agc1_out(fe, 1); + + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &blanking_mode[0], 1, &blanking_mode[1], 1); + + tda8295_set_video_std(fe); + + blanking_mode[1] = 0x03; + tuner_i2c_xfer_send(&priv->i2c_props, blanking_mode, 2); + msleep(20); + + tda8295_i2c_bridge(fe, 1); + + if (fe->ops.tuner_ops.set_analog_params) + fe->ops.tuner_ops.set_analog_params(fe, params); + + if (priv->cfg.agcf) + priv->cfg.agcf(fe); + + if (tda8295_has_signal(fe)) + tuner_dbg("tda8295 is locked\n"); + else + tuner_dbg("tda8295 not locked, no signal?\n"); + + tda8295_i2c_bridge(fe, 0); +} + +/*---------------------------------------------------------------------*/ + +static int tda8290_has_signal(struct dvb_frontend *fe) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + + unsigned char i2c_get_afc[1] = { 0x1B }; + unsigned char afc = 0; + + tuner_i2c_xfer_send_recv(&priv->i2c_props, + i2c_get_afc, ARRAY_SIZE(i2c_get_afc), &afc, 1); + return (afc & 0x80)? 65535:0; +} + +/*---------------------------------------------------------------------*/ + +static void tda8290_standby(struct dvb_frontend *fe) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + + unsigned char cb1[] = { 0x30, 0xD0 }; + unsigned char tda8290_standby[] = { 0x00, 0x02 }; + unsigned char tda8290_agc_tri[] = { 0x02, 0x20 }; + struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, .buf=cb1, .len = 2}; + + tda8290_i2c_bridge(fe, 1); + if (priv->ver & TDA8275A) + cb1[1] = 0x90; + i2c_transfer(priv->i2c_props.adap, &msg, 1); + tda8290_i2c_bridge(fe, 0); + tuner_i2c_xfer_send(&priv->i2c_props, tda8290_agc_tri, 2); + tuner_i2c_xfer_send(&priv->i2c_props, tda8290_standby, 2); +} + +static void tda8295_standby(struct dvb_frontend *fe) +{ + tda8295_agc1_out(fe, 0); /* Put AGC in tri-state */ + + tda8295_power(fe, 0); +} + +static void tda8290_init_if(struct dvb_frontend *fe) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + + unsigned char set_VS[] = { 0x30, 0x6F }; + unsigned char set_GP00_CF[] = { 0x20, 0x01 }; + unsigned char set_GP01_CF[] = { 0x20, 0x0B }; + + if ((priv->cfg.config == 1) || (priv->cfg.config == 2)) + tuner_i2c_xfer_send(&priv->i2c_props, set_GP00_CF, 2); + else + tuner_i2c_xfer_send(&priv->i2c_props, set_GP01_CF, 2); + tuner_i2c_xfer_send(&priv->i2c_props, set_VS, 2); +} + +static void tda8295_init_if(struct dvb_frontend *fe) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + + static unsigned char set_adc_ctl[] = { 0x33, 0x14 }; + static unsigned char set_adc_ctl2[] = { 0x34, 0x00 }; + static unsigned char set_pll_reg6[] = { 0x3e, 0x63 }; + static unsigned char set_pll_reg0[] = { 0x38, 0x23 }; + static unsigned char set_pll_reg7[] = { 0x3f, 0x01 }; + static unsigned char set_pll_reg10[] = { 0x42, 0x61 }; + static unsigned char set_gpio_reg0[] = { 0x44, 0x0b }; + + tda8295_power(fe, 1); + + tda8295_set_easy_mode(fe, 0); + tda8295_set_video_std(fe); + + tuner_i2c_xfer_send(&priv->i2c_props, set_adc_ctl, 2); + tuner_i2c_xfer_send(&priv->i2c_props, set_adc_ctl2, 2); + tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg6, 2); + tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg0, 2); + tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg7, 2); + tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg10, 2); + tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_reg0, 2); + + tda8295_agc1_out(fe, 0); + tda8295_agc2_out(fe, 0); +} + +static void tda8290_init_tuner(struct dvb_frontend *fe) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + unsigned char tda8275_init[] = { 0x00, 0x00, 0x00, 0x40, 0xdC, 0x04, 0xAf, + 0x3F, 0x2A, 0x04, 0xFF, 0x00, 0x00, 0x40 }; + unsigned char tda8275a_init[] = { 0x00, 0x00, 0x00, 0x00, 0xdC, 0x05, 0x8b, + 0x0c, 0x04, 0x20, 0xFF, 0x00, 0x00, 0x4b }; + struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, + .buf=tda8275_init, .len = 14}; + if (priv->ver & TDA8275A) + msg.buf = tda8275a_init; + + tda8290_i2c_bridge(fe, 1); + i2c_transfer(priv->i2c_props.adap, &msg, 1); + tda8290_i2c_bridge(fe, 0); +} + +/*---------------------------------------------------------------------*/ + +static void tda829x_release(struct dvb_frontend *fe) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + + /* only try to release the tuner if we've + * attached it from within this module */ + if (priv->ver & (TDA18271 | TDA8275 | TDA8275A)) + if (fe->ops.tuner_ops.release) + fe->ops.tuner_ops.release(fe); + + kfree(fe->analog_demod_priv); + fe->analog_demod_priv = NULL; +} + +static struct tda18271_config tda829x_tda18271_config = { + .gate = TDA18271_GATE_ANALOG, +}; + +static int tda829x_find_tuner(struct dvb_frontend *fe) +{ + struct tda8290_priv *priv = fe->analog_demod_priv; + struct analog_demod_ops *analog_ops = &fe->ops.analog_ops; + int i, ret, tuners_found; + u32 tuner_addrs; + u8 data; + struct i2c_msg msg = { .flags = I2C_M_RD, .buf = &data, .len = 1 }; + + if (!analog_ops->i2c_gate_ctrl) { + printk(KERN_ERR "tda8290: no gate control were provided!\n"); + + return -EINVAL; + } + + analog_ops->i2c_gate_ctrl(fe, 1); + + /* probe for tuner chip */ + tuners_found = 0; + tuner_addrs = 0; + for (i = 0x60; i <= 0x63; i++) { + msg.addr = i; + ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); + if (ret == 1) { + tuners_found++; + tuner_addrs = (tuner_addrs << 8) + i; + } + } + /* if there is more than one tuner, we expect the right one is + behind the bridge and we choose the highest address that doesn't + give a response now + */ + + analog_ops->i2c_gate_ctrl(fe, 0); + + if (tuners_found > 1) + for (i = 0; i < tuners_found; i++) { + msg.addr = tuner_addrs & 0xff; + ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); + if (ret == 1) + tuner_addrs = tuner_addrs >> 8; + else + break; + } + + if (tuner_addrs == 0) { + tuner_addrs = 0x60; + tuner_info("could not clearly identify tuner address, " + "defaulting to %x\n", tuner_addrs); + } else { + tuner_addrs = tuner_addrs & 0xff; + tuner_info("setting tuner address to %x\n", tuner_addrs); + } + priv->tda827x_addr = tuner_addrs; + msg.addr = tuner_addrs; + + analog_ops->i2c_gate_ctrl(fe, 1); + ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); + + if (ret != 1) { + tuner_warn("tuner access failed!\n"); + analog_ops->i2c_gate_ctrl(fe, 0); + return -EREMOTEIO; + } + + if ((data == 0x83) || (data == 0x84)) { + priv->ver |= TDA18271; + tda829x_tda18271_config.config = priv->cfg.config; + dvb_attach(tda18271_attach, fe, priv->tda827x_addr, + priv->i2c_props.adap, &tda829x_tda18271_config); + } else { + if ((data & 0x3c) == 0) + priv->ver |= TDA8275; + else + priv->ver |= TDA8275A; + + dvb_attach(tda827x_attach, fe, priv->tda827x_addr, + priv->i2c_props.adap, &priv->cfg); + priv->cfg.switch_addr = priv->i2c_props.addr; + } + if (fe->ops.tuner_ops.init) + fe->ops.tuner_ops.init(fe); + + if (fe->ops.tuner_ops.sleep) + fe->ops.tuner_ops.sleep(fe); + + analog_ops->i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int tda8290_probe(struct tuner_i2c_props *i2c_props) +{ +#define TDA8290_ID 0x89 + u8 reg = 0x1f, id; + struct i2c_msg msg_read[] = { + { .addr = i2c_props->addr, .flags = 0, .len = 1, .buf = ® }, + { .addr = i2c_props->addr, .flags = I2C_M_RD, .len = 1, .buf = &id }, + }; + + /* detect tda8290 */ + if (i2c_transfer(i2c_props->adap, msg_read, 2) != 2) { + printk(KERN_WARNING "%s: couldn't read register 0x%02x\n", + __func__, reg); + return -ENODEV; + } + + if (id == TDA8290_ID) { + if (debug) + printk(KERN_DEBUG "%s: tda8290 detected @ %d-%04x\n", + __func__, i2c_adapter_id(i2c_props->adap), + i2c_props->addr); + return 0; + } + return -ENODEV; +} + +static int tda8295_probe(struct tuner_i2c_props *i2c_props) +{ +#define TDA8295_ID 0x8a +#define TDA8295C2_ID 0x8b + u8 reg = 0x2f, id; + struct i2c_msg msg_read[] = { + { .addr = i2c_props->addr, .flags = 0, .len = 1, .buf = ® }, + { .addr = i2c_props->addr, .flags = I2C_M_RD, .len = 1, .buf = &id }, + }; + + /* detect tda8295 */ + if (i2c_transfer(i2c_props->adap, msg_read, 2) != 2) { + printk(KERN_WARNING "%s: couldn't read register 0x%02x\n", + __func__, reg); + return -ENODEV; + } + + if ((id & 0xfe) == TDA8295_ID) { + if (debug) + printk(KERN_DEBUG "%s: %s detected @ %d-%04x\n", + __func__, (id == TDA8295_ID) ? + "tda8295c1" : "tda8295c2", + i2c_adapter_id(i2c_props->adap), + i2c_props->addr); + return 0; + } + + return -ENODEV; +} + +static struct analog_demod_ops tda8290_ops = { + .set_params = tda8290_set_params, + .has_signal = tda8290_has_signal, + .standby = tda8290_standby, + .release = tda829x_release, + .i2c_gate_ctrl = tda8290_i2c_bridge, +}; + +static struct analog_demod_ops tda8295_ops = { + .set_params = tda8295_set_params, + .has_signal = tda8295_has_signal, + .standby = tda8295_standby, + .release = tda829x_release, + .i2c_gate_ctrl = tda8295_i2c_bridge, +}; + +struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c_adap, u8 i2c_addr, + struct tda829x_config *cfg) +{ + struct tda8290_priv *priv = NULL; + char *name; + + priv = kzalloc(sizeof(struct tda8290_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + fe->analog_demod_priv = priv; + + priv->i2c_props.addr = i2c_addr; + priv->i2c_props.adap = i2c_adap; + priv->i2c_props.name = "tda829x"; + if (cfg) + priv->cfg.config = cfg->lna_cfg; + + if (tda8290_probe(&priv->i2c_props) == 0) { + priv->ver = TDA8290; + memcpy(&fe->ops.analog_ops, &tda8290_ops, + sizeof(struct analog_demod_ops)); + } + + if (tda8295_probe(&priv->i2c_props) == 0) { + priv->ver = TDA8295; + memcpy(&fe->ops.analog_ops, &tda8295_ops, + sizeof(struct analog_demod_ops)); + } + + if (!(cfg) || (TDA829X_PROBE_TUNER == cfg->probe_tuner)) { + tda8295_power(fe, 1); + if (tda829x_find_tuner(fe) < 0) + goto fail; + } + + switch (priv->ver) { + case TDA8290: + name = "tda8290"; + break; + case TDA8295: + name = "tda8295"; + break; + case TDA8290 | TDA8275: + name = "tda8290+75"; + break; + case TDA8295 | TDA8275: + name = "tda8295+75"; + break; + case TDA8290 | TDA8275A: + name = "tda8290+75a"; + break; + case TDA8295 | TDA8275A: + name = "tda8295+75a"; + break; + case TDA8290 | TDA18271: + name = "tda8290+18271"; + break; + case TDA8295 | TDA18271: + name = "tda8295+18271"; + break; + default: + goto fail; + } + tuner_info("type set to %s\n", name); + + fe->ops.analog_ops.info.name = name; + + if (priv->ver & TDA8290) { + if (priv->ver & (TDA8275 | TDA8275A)) + tda8290_init_tuner(fe); + tda8290_init_if(fe); + } else if (priv->ver & TDA8295) + tda8295_init_if(fe); + + return fe; + +fail: + memset(&fe->ops.analog_ops, 0, sizeof(struct analog_demod_ops)); + + tda829x_release(fe); + return NULL; +} +EXPORT_SYMBOL_GPL(tda829x_attach); + +int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr) +{ + struct tuner_i2c_props i2c_props = { + .adap = i2c_adap, + .addr = i2c_addr, + }; + + unsigned char soft_reset[] = { 0x00, 0x00 }; + unsigned char easy_mode_b[] = { 0x01, 0x02 }; + unsigned char easy_mode_g[] = { 0x01, 0x04 }; + unsigned char restore_9886[] = { 0x00, 0xd6, 0x30 }; + unsigned char addr_dto_lsb = 0x07; + unsigned char data; +#define PROBE_BUFFER_SIZE 8 + unsigned char buf[PROBE_BUFFER_SIZE]; + int i; + + /* rule out tda9887, which would return the same byte repeatedly */ + tuner_i2c_xfer_send_recv(&i2c_props, + soft_reset, 1, buf, PROBE_BUFFER_SIZE); + for (i = 1; i < PROBE_BUFFER_SIZE; i++) { + if (buf[i] != buf[0]) + break; + } + + /* all bytes are equal, not a tda829x - probably a tda9887 */ + if (i == PROBE_BUFFER_SIZE) + return -ENODEV; + + if ((tda8290_probe(&i2c_props) == 0) || + (tda8295_probe(&i2c_props) == 0)) + return 0; + + /* fall back to old probing method */ + tuner_i2c_xfer_send(&i2c_props, easy_mode_b, 2); + tuner_i2c_xfer_send(&i2c_props, soft_reset, 2); + tuner_i2c_xfer_send_recv(&i2c_props, &addr_dto_lsb, 1, &data, 1); + if (data == 0) { + tuner_i2c_xfer_send(&i2c_props, easy_mode_g, 2); + tuner_i2c_xfer_send(&i2c_props, soft_reset, 2); + tuner_i2c_xfer_send_recv(&i2c_props, + &addr_dto_lsb, 1, &data, 1); + if (data == 0x7b) { + return 0; + } + } + tuner_i2c_xfer_send(&i2c_props, restore_9886, 3); + return -ENODEV; +} +EXPORT_SYMBOL_GPL(tda829x_probe); + +MODULE_DESCRIPTION("Philips/NXP TDA8290/TDA8295 analog IF demodulator driver"); +MODULE_AUTHOR("Gerd Knorr, Hartmut Hackmann, Michael Krufky"); +MODULE_LICENSE("GPL"); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/tuners/tda8290.h b/drivers/media/tuners/tda8290.h new file mode 100644 index 000000000000..7e288b26fcc3 --- /dev/null +++ b/drivers/media/tuners/tda8290.h @@ -0,0 +1,56 @@ +/* + 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 +#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/tuners/tda9887.c b/drivers/media/tuners/tda9887.c new file mode 100644 index 000000000000..cdb645d57438 --- /dev/null +++ b/drivers/media/tuners/tda9887.c @@ -0,0 +1,717 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tuner-i2c.h" +#include "tda9887.h" + + +/* Chips: + TDA9885 (PAL, NTSC) + TDA9886 (PAL, SECAM, NTSC) + TDA9887 (PAL, SECAM, NTSC, FM Radio) + + Used as part of several tuners +*/ + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable verbose debug messages"); + +static DEFINE_MUTEX(tda9887_list_mutex); +static LIST_HEAD(hybrid_tuner_instance_list); + +struct tda9887_priv { + struct tuner_i2c_props i2c_props; + struct list_head hybrid_tuner_instance_list; + + unsigned char data[4]; + unsigned int config; + unsigned int mode; + unsigned int audmode; + v4l2_std_id std; + + bool standby; +}; + +/* ---------------------------------------------------------------------- */ + +#define UNSET (-1U) + +struct tvnorm { + v4l2_std_id std; + char *name; + unsigned char b; + unsigned char c; + unsigned char e; +}; + +/* ---------------------------------------------------------------------- */ + +// +// TDA defines +// + +//// first reg (b) +#define cVideoTrapBypassOFF 0x00 // bit b0 +#define cVideoTrapBypassON 0x01 // bit b0 + +#define cAutoMuteFmInactive 0x00 // bit b1 +#define cAutoMuteFmActive 0x02 // bit b1 + +#define cIntercarrier 0x00 // bit b2 +#define cQSS 0x04 // bit b2 + +#define cPositiveAmTV 0x00 // bit b3:4 +#define cFmRadio 0x08 // bit b3:4 +#define cNegativeFmTV 0x10 // bit b3:4 + + +#define cForcedMuteAudioON 0x20 // bit b5 +#define cForcedMuteAudioOFF 0x00 // bit b5 + +#define cOutputPort1Active 0x00 // bit b6 +#define cOutputPort1Inactive 0x40 // bit b6 + +#define cOutputPort2Active 0x00 // bit b7 +#define cOutputPort2Inactive 0x80 // bit b7 + + +//// second reg (c) +#define cDeemphasisOFF 0x00 // bit c5 +#define cDeemphasisON 0x20 // bit c5 + +#define cDeemphasis75 0x00 // bit c6 +#define cDeemphasis50 0x40 // bit c6 + +#define cAudioGain0 0x00 // bit c7 +#define cAudioGain6 0x80 // bit c7 + +#define cTopMask 0x1f // bit c0:4 +#define cTopDefault 0x10 // bit c0:4 + +//// third reg (e) +#define cAudioIF_4_5 0x00 // bit e0:1 +#define cAudioIF_5_5 0x01 // bit e0:1 +#define cAudioIF_6_0 0x02 // bit e0:1 +#define cAudioIF_6_5 0x03 // bit e0:1 + + +#define cVideoIFMask 0x1c // bit e2:4 +/* Video IF selection in TV Mode (bit B3=0) */ +#define cVideoIF_58_75 0x00 // bit e2:4 +#define cVideoIF_45_75 0x04 // bit e2:4 +#define cVideoIF_38_90 0x08 // bit e2:4 +#define cVideoIF_38_00 0x0C // bit e2:4 +#define cVideoIF_33_90 0x10 // bit e2:4 +#define cVideoIF_33_40 0x14 // bit e2:4 +#define cRadioIF_45_75 0x18 // bit e2:4 +#define cRadioIF_38_90 0x1C // bit e2:4 + +/* IF1 selection in Radio Mode (bit B3=1) */ +#define cRadioIF_33_30 0x00 // bit e2,4 (also 0x10,0x14) +#define cRadioIF_41_30 0x04 // bit e2,4 + +/* Output of AFC pin in radio mode when bit E7=1 */ +#define cRadioAGC_SIF 0x00 // bit e3 +#define cRadioAGC_FM 0x08 // bit e3 + +#define cTunerGainNormal 0x00 // bit e5 +#define cTunerGainLow 0x20 // bit e5 + +#define cGating_18 0x00 // bit e6 +#define cGating_36 0x40 // bit e6 + +#define cAgcOutON 0x80 // bit e7 +#define cAgcOutOFF 0x00 // bit e7 + +/* ---------------------------------------------------------------------- */ + +static struct tvnorm tvnorms[] = { + { + .std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N, + .name = "PAL-BGHN", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis50 | + cTopDefault), + .e = ( cGating_36 | + cAudioIF_5_5 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_PAL_I, + .name = "PAL-I", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis50 | + cTopDefault), + .e = ( cGating_36 | + cAudioIF_6_0 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_PAL_DK, + .name = "PAL-DK", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis50 | + cTopDefault), + .e = ( cGating_36 | + cAudioIF_6_5 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc, + .name = "PAL-M/Nc", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis75 | + cTopDefault), + .e = ( cGating_36 | + cAudioIF_4_5 | + cVideoIF_45_75 ), + },{ + .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, + .name = "SECAM-BGH", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cTopDefault), + .e = ( cAudioIF_5_5 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_SECAM_L, + .name = "SECAM-L", + .b = ( cPositiveAmTV | + cQSS ), + .c = ( cTopDefault), + .e = ( cGating_36 | + cAudioIF_6_5 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_SECAM_LC, + .name = "SECAM-L'", + .b = ( cOutputPort2Inactive | + cPositiveAmTV | + cQSS ), + .c = ( cTopDefault), + .e = ( cGating_36 | + cAudioIF_6_5 | + cVideoIF_33_90 ), + },{ + .std = V4L2_STD_SECAM_DK, + .name = "SECAM-DK", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis50 | + cTopDefault), + .e = ( cGating_36 | + cAudioIF_6_5 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR, + .name = "NTSC-M", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis75 | + cTopDefault), + .e = ( cGating_36 | + cAudioIF_4_5 | + cVideoIF_45_75 ), + },{ + .std = V4L2_STD_NTSC_M_JP, + .name = "NTSC-M-JP", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis50 | + cTopDefault), + .e = ( cGating_36 | + cAudioIF_4_5 | + cVideoIF_58_75 ), + } +}; + +static struct tvnorm radio_stereo = { + .name = "Radio Stereo", + .b = ( cFmRadio | + cQSS ), + .c = ( cDeemphasisOFF | + cAudioGain6 | + cTopDefault), + .e = ( cTunerGainLow | + cAudioIF_5_5 | + cRadioIF_38_90 ), +}; + +static struct tvnorm radio_mono = { + .name = "Radio Mono", + .b = ( cFmRadio | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis75 | + cTopDefault), + .e = ( cTunerGainLow | + cAudioIF_5_5 | + cRadioIF_38_90 ), +}; + +/* ---------------------------------------------------------------------- */ + +static void dump_read_message(struct dvb_frontend *fe, unsigned char *buf) +{ + struct tda9887_priv *priv = fe->analog_demod_priv; + + static char *afc[16] = { + "- 12.5 kHz", + "- 37.5 kHz", + "- 62.5 kHz", + "- 87.5 kHz", + "-112.5 kHz", + "-137.5 kHz", + "-162.5 kHz", + "-187.5 kHz [min]", + "+187.5 kHz [max]", + "+162.5 kHz", + "+137.5 kHz", + "+112.5 kHz", + "+ 87.5 kHz", + "+ 62.5 kHz", + "+ 37.5 kHz", + "+ 12.5 kHz", + }; + tuner_info("read: 0x%2x\n", buf[0]); + tuner_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no"); + tuner_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]); + tuner_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low"); + tuner_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out"); + tuner_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low"); +} + +static void dump_write_message(struct dvb_frontend *fe, unsigned char *buf) +{ + struct tda9887_priv *priv = fe->analog_demod_priv; + + static char *sound[4] = { + "AM/TV", + "FM/radio", + "FM/TV", + "FM/radio" + }; + static char *adjust[32] = { + "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9", + "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1", + "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7", + "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15" + }; + static char *deemph[4] = { + "no", "no", "75", "50" + }; + static char *carrier[4] = { + "4.5 MHz", + "5.5 MHz", + "6.0 MHz", + "6.5 MHz / AM" + }; + static char *vif[8] = { + "58.75 MHz", + "45.75 MHz", + "38.9 MHz", + "38.0 MHz", + "33.9 MHz", + "33.4 MHz", + "45.75 MHz + pin13", + "38.9 MHz + pin13", + }; + static char *rif[4] = { + "44 MHz", + "52 MHz", + "52 MHz", + "44 MHz", + }; + + tuner_info("write: byte B 0x%02x\n", buf[1]); + tuner_info(" B0 video mode : %s\n", + (buf[1] & 0x01) ? "video trap" : "sound trap"); + tuner_info(" B1 auto mute fm : %s\n", + (buf[1] & 0x02) ? "yes" : "no"); + tuner_info(" B2 carrier mode : %s\n", + (buf[1] & 0x04) ? "QSS" : "Intercarrier"); + tuner_info(" B3-4 tv sound/radio : %s\n", + sound[(buf[1] & 0x18) >> 3]); + tuner_info(" B5 force mute audio: %s\n", + (buf[1] & 0x20) ? "yes" : "no"); + tuner_info(" B6 output port 1 : %s\n", + (buf[1] & 0x40) ? "high (inactive)" : "low (active)"); + tuner_info(" B7 output port 2 : %s\n", + (buf[1] & 0x80) ? "high (inactive)" : "low (active)"); + + tuner_info("write: byte C 0x%02x\n", buf[2]); + tuner_info(" C0-4 top adjustment : %s dB\n", + adjust[buf[2] & 0x1f]); + tuner_info(" C5-6 de-emphasis : %s\n", + deemph[(buf[2] & 0x60) >> 5]); + tuner_info(" C7 audio gain : %s\n", + (buf[2] & 0x80) ? "-6" : "0"); + + tuner_info("write: byte E 0x%02x\n", buf[3]); + tuner_info(" E0-1 sound carrier : %s\n", + carrier[(buf[3] & 0x03)]); + tuner_info(" E6 l pll gating : %s\n", + (buf[3] & 0x40) ? "36" : "13"); + + if (buf[1] & 0x08) { + /* radio */ + tuner_info(" E2-4 video if : %s\n", + rif[(buf[3] & 0x0c) >> 2]); + tuner_info(" E7 vif agc output : %s\n", + (buf[3] & 0x80) + ? ((buf[3] & 0x10) ? "fm-agc radio" : + "sif-agc radio") + : "fm radio carrier afc"); + } else { + /* video */ + tuner_info(" E2-4 video if : %s\n", + vif[(buf[3] & 0x1c) >> 2]); + tuner_info(" E5 tuner gain : %s\n", + (buf[3] & 0x80) + ? ((buf[3] & 0x20) ? "external" : "normal") + : ((buf[3] & 0x20) ? "minimum" : "normal")); + tuner_info(" E7 vif agc output : %s\n", + (buf[3] & 0x80) ? ((buf[3] & 0x20) + ? "pin3 port, pin22 vif agc out" + : "pin22 port, pin3 vif acg ext in") + : "pin3+pin22 port"); + } + tuner_info("--\n"); +} + +/* ---------------------------------------------------------------------- */ + +static int tda9887_set_tvnorm(struct dvb_frontend *fe) +{ + struct tda9887_priv *priv = fe->analog_demod_priv; + struct tvnorm *norm = NULL; + char *buf = priv->data; + int i; + + if (priv->mode == V4L2_TUNER_RADIO) { + if (priv->audmode == V4L2_TUNER_MODE_MONO) + norm = &radio_mono; + else + norm = &radio_stereo; + } else { + for (i = 0; i < ARRAY_SIZE(tvnorms); i++) { + if (tvnorms[i].std & priv->std) { + norm = tvnorms+i; + break; + } + } + } + if (NULL == norm) { + tuner_dbg("Unsupported tvnorm entry - audio muted\n"); + return -1; + } + + tuner_dbg("configure for: %s\n", norm->name); + buf[1] = norm->b; + buf[2] = norm->c; + buf[3] = norm->e; + return 0; +} + +static unsigned int port1 = UNSET; +static unsigned int port2 = UNSET; +static unsigned int qss = UNSET; +static unsigned int adjust = UNSET; + +module_param(port1, int, 0644); +module_param(port2, int, 0644); +module_param(qss, int, 0644); +module_param(adjust, int, 0644); + +static int tda9887_set_insmod(struct dvb_frontend *fe) +{ + struct tda9887_priv *priv = fe->analog_demod_priv; + char *buf = priv->data; + + if (UNSET != port1) { + if (port1) + buf[1] |= cOutputPort1Inactive; + else + buf[1] &= ~cOutputPort1Inactive; + } + if (UNSET != port2) { + if (port2) + buf[1] |= cOutputPort2Inactive; + else + buf[1] &= ~cOutputPort2Inactive; + } + + if (UNSET != qss) { + if (qss) + buf[1] |= cQSS; + else + buf[1] &= ~cQSS; + } + + if (adjust < 0x20) { + buf[2] &= ~cTopMask; + buf[2] |= adjust; + } + return 0; +} + +static int tda9887_do_config(struct dvb_frontend *fe) +{ + struct tda9887_priv *priv = fe->analog_demod_priv; + char *buf = priv->data; + + if (priv->config & TDA9887_PORT1_ACTIVE) + buf[1] &= ~cOutputPort1Inactive; + if (priv->config & TDA9887_PORT1_INACTIVE) + buf[1] |= cOutputPort1Inactive; + if (priv->config & TDA9887_PORT2_ACTIVE) + buf[1] &= ~cOutputPort2Inactive; + if (priv->config & TDA9887_PORT2_INACTIVE) + buf[1] |= cOutputPort2Inactive; + + if (priv->config & TDA9887_QSS) + buf[1] |= cQSS; + if (priv->config & TDA9887_INTERCARRIER) + buf[1] &= ~cQSS; + + if (priv->config & TDA9887_AUTOMUTE) + buf[1] |= cAutoMuteFmActive; + if (priv->config & TDA9887_DEEMPHASIS_MASK) { + buf[2] &= ~0x60; + switch (priv->config & TDA9887_DEEMPHASIS_MASK) { + case TDA9887_DEEMPHASIS_NONE: + buf[2] |= cDeemphasisOFF; + break; + case TDA9887_DEEMPHASIS_50: + buf[2] |= cDeemphasisON | cDeemphasis50; + break; + case TDA9887_DEEMPHASIS_75: + buf[2] |= cDeemphasisON | cDeemphasis75; + break; + } + } + if (priv->config & TDA9887_TOP_SET) { + buf[2] &= ~cTopMask; + buf[2] |= (priv->config >> 8) & cTopMask; + } + if ((priv->config & TDA9887_INTERCARRIER_NTSC) && + (priv->std & V4L2_STD_NTSC)) + buf[1] &= ~cQSS; + if (priv->config & TDA9887_GATING_18) + buf[3] &= ~cGating_36; + + if (priv->mode == V4L2_TUNER_RADIO) { + if (priv->config & TDA9887_RIF_41_3) { + buf[3] &= ~cVideoIFMask; + buf[3] |= cRadioIF_41_30; + } + if (priv->config & TDA9887_GAIN_NORMAL) + buf[3] &= ~cTunerGainLow; + } + + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static int tda9887_status(struct dvb_frontend *fe) +{ + struct tda9887_priv *priv = fe->analog_demod_priv; + unsigned char buf[1]; + int rc; + + memset(buf,0,sizeof(buf)); + if (1 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props,buf,1))) + tuner_info("i2c i/o error: rc == %d (should be 1)\n", rc); + dump_read_message(fe, buf); + return 0; +} + +static void tda9887_configure(struct dvb_frontend *fe) +{ + struct tda9887_priv *priv = fe->analog_demod_priv; + int rc; + + memset(priv->data,0,sizeof(priv->data)); + tda9887_set_tvnorm(fe); + + /* A note on the port settings: + These settings tend to depend on the specifics of the board. + By default they are set to inactive (bit value 1) by this driver, + overwriting any changes made by the tvnorm. This means that it + is the responsibility of the module using the tda9887 to set + these values in case of changes in the tvnorm. + In many cases port 2 should be made active (0) when selecting + SECAM-L, and port 2 should remain inactive (1) for SECAM-L'. + + For the other standards the tda9887 application note says that + the ports should be set to active (0), but, again, that may + differ depending on the precise hardware configuration. + */ + priv->data[1] |= cOutputPort1Inactive; + priv->data[1] |= cOutputPort2Inactive; + + tda9887_do_config(fe); + tda9887_set_insmod(fe); + + if (priv->standby) + priv->data[1] |= cForcedMuteAudioON; + + tuner_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n", + priv->data[1], priv->data[2], priv->data[3]); + if (debug > 1) + dump_write_message(fe, priv->data); + + if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,priv->data,4))) + tuner_info("i2c i/o error: rc == %d (should be 4)\n", rc); + + if (debug > 2) { + msleep_interruptible(1000); + tda9887_status(fe); + } +} + +/* ---------------------------------------------------------------------- */ + +static void tda9887_tuner_status(struct dvb_frontend *fe) +{ + struct tda9887_priv *priv = fe->analog_demod_priv; + tuner_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", + priv->data[1], priv->data[2], priv->data[3]); +} + +static int tda9887_get_afc(struct dvb_frontend *fe) +{ + struct tda9887_priv *priv = fe->analog_demod_priv; + static int AFC_BITS_2_kHz[] = { + -12500, -37500, -62500, -97500, + -112500, -137500, -162500, -187500, + 187500, 162500, 137500, 112500, + 97500 , 62500, 37500 , 12500 + }; + int afc=0; + __u8 reg = 0; + + if (1 == tuner_i2c_xfer_recv(&priv->i2c_props,®,1)) + afc = AFC_BITS_2_kHz[(reg>>1)&0x0f]; + + return afc; +} + +static void tda9887_standby(struct dvb_frontend *fe) +{ + struct tda9887_priv *priv = fe->analog_demod_priv; + + priv->standby = true; + + tda9887_configure(fe); +} + +static void tda9887_set_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct tda9887_priv *priv = fe->analog_demod_priv; + + priv->standby = false; + priv->mode = params->mode; + priv->audmode = params->audmode; + priv->std = params->std; + tda9887_configure(fe); +} + +static int tda9887_set_config(struct dvb_frontend *fe, void *priv_cfg) +{ + struct tda9887_priv *priv = fe->analog_demod_priv; + + priv->config = *(unsigned int *)priv_cfg; + tda9887_configure(fe); + + return 0; +} + +static void tda9887_release(struct dvb_frontend *fe) +{ + struct tda9887_priv *priv = fe->analog_demod_priv; + + mutex_lock(&tda9887_list_mutex); + + if (priv) + hybrid_tuner_release_state(priv); + + mutex_unlock(&tda9887_list_mutex); + + fe->analog_demod_priv = NULL; +} + +static struct analog_demod_ops tda9887_ops = { + .info = { + .name = "tda9887", + }, + .set_params = tda9887_set_params, + .standby = tda9887_standby, + .tuner_status = tda9887_tuner_status, + .get_afc = tda9887_get_afc, + .release = tda9887_release, + .set_config = tda9887_set_config, +}; + +struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c_adap, + u8 i2c_addr) +{ + struct tda9887_priv *priv = NULL; + int instance; + + mutex_lock(&tda9887_list_mutex); + + instance = hybrid_tuner_request_state(struct tda9887_priv, priv, + hybrid_tuner_instance_list, + i2c_adap, i2c_addr, "tda9887"); + switch (instance) { + case 0: + mutex_unlock(&tda9887_list_mutex); + return NULL; + case 1: + fe->analog_demod_priv = priv; + priv->standby = true; + tuner_info("tda988[5/6/7] found\n"); + break; + default: + fe->analog_demod_priv = priv; + break; + } + + mutex_unlock(&tda9887_list_mutex); + + memcpy(&fe->ops.analog_ops, &tda9887_ops, + sizeof(struct analog_demod_ops)); + + return fe; +} +EXPORT_SYMBOL_GPL(tda9887_attach); + +MODULE_LICENSE("GPL"); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/tuners/tda9887.h b/drivers/media/tuners/tda9887.h new file mode 100644 index 000000000000..acc419e8c4fc --- /dev/null +++ b/drivers/media/tuners/tda9887.h @@ -0,0 +1,38 @@ +/* + 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 +#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/tuners/tea5761.c b/drivers/media/tuners/tea5761.c new file mode 100644 index 000000000000..bf78cb9fc52c --- /dev/null +++ b/drivers/media/tuners/tea5761.c @@ -0,0 +1,348 @@ +/* + * For Philips TEA5761 FM Chip + * I2C address is allways 0x20 (0x10 at 7-bit mode). + * + * Copyright (c) 2005-2007 Mauro Carvalho Chehab (mchehab@infradead.org) + * This code is placed under the terms of the GNUv2 General Public License + * + */ + +#include +#include +#include +#include +#include +#include "tuner-i2c.h" +#include "tea5761.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable verbose debug messages"); + +struct tea5761_priv { + struct tuner_i2c_props i2c_props; + + u32 frequency; + bool standby; +}; + +/*****************************************************************************/ + +/*************************** + * TEA5761HN I2C registers * + ***************************/ + +/* INTREG - Read: bytes 0 and 1 / Write: byte 0 */ + + /* first byte for reading */ +#define TEA5761_INTREG_IFFLAG 0x10 +#define TEA5761_INTREG_LEVFLAG 0x8 +#define TEA5761_INTREG_FRRFLAG 0x2 +#define TEA5761_INTREG_BLFLAG 0x1 + + /* second byte for reading / byte for writing */ +#define TEA5761_INTREG_IFMSK 0x10 +#define TEA5761_INTREG_LEVMSK 0x8 +#define TEA5761_INTREG_FRMSK 0x2 +#define TEA5761_INTREG_BLMSK 0x1 + +/* FRQSET - Read: bytes 2 and 3 / Write: byte 1 and 2 */ + + /* First byte */ +#define TEA5761_FRQSET_SEARCH_UP 0x80 /* 1=Station search from botton to up */ +#define TEA5761_FRQSET_SEARCH_MODE 0x40 /* 1=Search mode */ + + /* Bits 0-5 for divider MSB */ + + /* Second byte */ + /* Bits 0-7 for divider LSB */ + +/* TNCTRL - Read: bytes 4 and 5 / Write: Bytes 3 and 4 */ + + /* first byte */ + +#define TEA5761_TNCTRL_PUPD_0 0x40 /* Power UP/Power Down MSB */ +#define TEA5761_TNCTRL_BLIM 0X20 /* 1= Japan Frequencies, 0= European frequencies */ +#define TEA5761_TNCTRL_SWPM 0x10 /* 1= software port is FRRFLAG */ +#define TEA5761_TNCTRL_IFCTC 0x08 /* 1= IF count time 15.02 ms, 0= IF count time 2.02 ms */ +#define TEA5761_TNCTRL_AFM 0x04 +#define TEA5761_TNCTRL_SMUTE 0x02 /* 1= Soft mute */ +#define TEA5761_TNCTRL_SNC 0x01 + + /* second byte */ + +#define TEA5761_TNCTRL_MU 0x80 /* 1=Hard mute */ +#define TEA5761_TNCTRL_SSL_1 0x40 +#define TEA5761_TNCTRL_SSL_0 0x20 +#define TEA5761_TNCTRL_HLSI 0x10 +#define TEA5761_TNCTRL_MST 0x08 /* 1 = mono */ +#define TEA5761_TNCTRL_SWP 0x04 +#define TEA5761_TNCTRL_DTC 0x02 /* 1 = deemphasis 50 us, 0 = deemphasis 75 us */ +#define TEA5761_TNCTRL_AHLSI 0x01 + +/* FRQCHECK - Read: bytes 6 and 7 */ + /* First byte */ + + /* Bits 0-5 for divider MSB */ + + /* Second byte */ + /* Bits 0-7 for divider LSB */ + +/* TUNCHECK - Read: bytes 8 and 9 */ + + /* First byte */ +#define TEA5761_TUNCHECK_IF_MASK 0x7e /* IF count */ +#define TEA5761_TUNCHECK_TUNTO 0x01 + + /* Second byte */ +#define TEA5761_TUNCHECK_LEV_MASK 0xf0 /* Level Count */ +#define TEA5761_TUNCHECK_LD 0x08 +#define TEA5761_TUNCHECK_STEREO 0x04 + +/* TESTREG - Read: bytes 10 and 11 / Write: bytes 5 and 6 */ + + /* All zero = no test mode */ + +/* MANID - Read: bytes 12 and 13 */ + + /* First byte - should be 0x10 */ +#define TEA5767_MANID_VERSION_MASK 0xf0 /* Version = 1 */ +#define TEA5767_MANID_ID_MSB_MASK 0x0f /* Manufacurer ID - should be 0 */ + + /* Second byte - Should be 0x2b */ + +#define TEA5767_MANID_ID_LSB_MASK 0xfe /* Manufacturer ID - should be 0x15 */ +#define TEA5767_MANID_IDAV 0x01 /* 1 = Chip has ID, 0 = Chip has no ID */ + +/* Chip ID - Read: bytes 14 and 15 */ + + /* First byte - should be 0x57 */ + + /* Second byte - should be 0x61 */ + +/*****************************************************************************/ + +#define FREQ_OFFSET 0 /* for TEA5767, it is 700 to give the right freq */ +static void tea5761_status_dump(unsigned char *buffer) +{ + unsigned int div, frq; + + div = ((buffer[2] & 0x3f) << 8) | buffer[3]; + + frq = 1000 * (div * 32768 / 1000 + FREQ_OFFSET + 225) / 4; /* Freq in KHz */ + + printk(KERN_INFO "tea5761: Frequency %d.%03d KHz (divider = 0x%04x)\n", + frq / 1000, frq % 1000, div); +} + +/* Freq should be specifyed at 62.5 Hz */ +static int __set_radio_freq(struct dvb_frontend *fe, + unsigned int freq, + bool mono) +{ + struct tea5761_priv *priv = fe->tuner_priv; + unsigned int frq = freq; + unsigned char buffer[7] = {0, 0, 0, 0, 0, 0, 0 }; + unsigned div; + int rc; + + tuner_dbg("radio freq counter %d\n", frq); + + if (priv->standby) { + tuner_dbg("TEA5761 set to standby mode\n"); + buffer[5] |= TEA5761_TNCTRL_MU; + } else { + buffer[4] |= TEA5761_TNCTRL_PUPD_0; + } + + + if (mono) { + tuner_dbg("TEA5761 set to mono\n"); + buffer[5] |= TEA5761_TNCTRL_MST; + } else { + tuner_dbg("TEA5761 set to stereo\n"); + } + + div = (1000 * (frq * 4 / 16 + 700 + 225) ) >> 15; + buffer[1] = (div >> 8) & 0x3f; + buffer[2] = div & 0xff; + + if (debug) + tea5761_status_dump(buffer); + + if (7 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 7))) + tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); + + priv->frequency = frq * 125 / 2; + + return 0; +} + +static int set_radio_freq(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct tea5761_priv *priv = fe->analog_demod_priv; + + priv->standby = false; + + return __set_radio_freq(fe, params->frequency, + params->audmode == V4L2_TUNER_MODE_MONO); +} + +static int set_radio_sleep(struct dvb_frontend *fe) +{ + struct tea5761_priv *priv = fe->analog_demod_priv; + + priv->standby = true; + + return __set_radio_freq(fe, priv->frequency, false); +} + +static int tea5761_read_status(struct dvb_frontend *fe, char *buffer) +{ + struct tea5761_priv *priv = fe->tuner_priv; + int rc; + + memset(buffer, 0, 16); + if (16 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 16))) { + tuner_warn("i2c i/o error: rc == %d (should be 16)\n", rc); + return -EREMOTEIO; + } + + return 0; +} + +static inline int tea5761_signal(struct dvb_frontend *fe, const char *buffer) +{ + struct tea5761_priv *priv = fe->tuner_priv; + + int signal = ((buffer[9] & TEA5761_TUNCHECK_LEV_MASK) << (13 - 4)); + + tuner_dbg("Signal strength: %d\n", signal); + + return signal; +} + +static inline int tea5761_stereo(struct dvb_frontend *fe, const char *buffer) +{ + struct tea5761_priv *priv = fe->tuner_priv; + + int stereo = buffer[9] & TEA5761_TUNCHECK_STEREO; + + tuner_dbg("Radio ST GET = %02x\n", stereo); + + return (stereo ? V4L2_TUNER_SUB_STEREO : 0); +} + +static int tea5761_get_status(struct dvb_frontend *fe, u32 *status) +{ + unsigned char buffer[16]; + + *status = 0; + + if (0 == tea5761_read_status(fe, buffer)) { + if (tea5761_signal(fe, buffer)) + *status = TUNER_STATUS_LOCKED; + if (tea5761_stereo(fe, buffer)) + *status |= TUNER_STATUS_STEREO; + } + + return 0; +} + +static int tea5761_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ + unsigned char buffer[16]; + + *strength = 0; + + if (0 == tea5761_read_status(fe, buffer)) + *strength = tea5761_signal(fe, buffer); + + return 0; +} + +int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) +{ + unsigned char buffer[16]; + int rc; + struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr }; + + if (16 != (rc = tuner_i2c_xfer_recv(&i2c, buffer, 16))) { + printk(KERN_WARNING "it is not a TEA5761. Received %i chars.\n", rc); + return -EINVAL; + } + + if ((buffer[13] != 0x2b) || (buffer[14] != 0x57) || (buffer[15] != 0x061)) { + printk(KERN_WARNING "Manufacturer ID= 0x%02x, Chip ID = %02x%02x." + " It is not a TEA5761\n", + buffer[13], buffer[14], buffer[15]); + return -EINVAL; + } + printk(KERN_WARNING "tea5761: TEA%02x%02x detected. " + "Manufacturer ID= 0x%02x\n", + buffer[14], buffer[15], buffer[13]); + + return 0; +} + +static int tea5761_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + + return 0; +} + +static int tea5761_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tea5761_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static struct dvb_tuner_ops tea5761_tuner_ops = { + .info = { + .name = "tea5761", // Philips TEA5761HN FM Radio + }, + .set_analog_params = set_radio_freq, + .sleep = set_radio_sleep, + .release = tea5761_release, + .get_frequency = tea5761_get_frequency, + .get_status = tea5761_get_status, + .get_rf_strength = tea5761_get_rf_strength, +}; + +struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe, + struct i2c_adapter* i2c_adap, + u8 i2c_addr) +{ + struct tea5761_priv *priv = NULL; + + if (tea5761_autodetection(i2c_adap, i2c_addr) != 0) + return NULL; + + priv = kzalloc(sizeof(struct tea5761_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + fe->tuner_priv = priv; + + priv->i2c_props.addr = i2c_addr; + priv->i2c_props.adap = i2c_adap; + priv->i2c_props.name = "tea5761"; + + memcpy(&fe->ops.tuner_ops, &tea5761_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + tuner_info("type set to %s\n", "Philips TEA5761HN FM Radio"); + + return fe; +} + + +EXPORT_SYMBOL_GPL(tea5761_attach); +EXPORT_SYMBOL_GPL(tea5761_autodetection); + +MODULE_DESCRIPTION("Philips TEA5761 FM tuner driver"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/tea5761.h b/drivers/media/tuners/tea5761.h new file mode 100644 index 000000000000..2e2ff82c95a4 --- /dev/null +++ b/drivers/media/tuners/tea5761.h @@ -0,0 +1,47 @@ +/* + 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 +#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/tuners/tea5767.c b/drivers/media/tuners/tea5767.c new file mode 100644 index 000000000000..36e85d81acb2 --- /dev/null +++ b/drivers/media/tuners/tea5767.c @@ -0,0 +1,475 @@ +/* + * For Philips TEA5767 FM Chip used on some TV Cards like Prolink Pixelview + * I2C address is allways 0xC0. + * + * + * Copyright (c) 2005 Mauro Carvalho Chehab (mchehab@infradead.org) + * This code is placed under the terms of the GNU General Public License + * + * tea5767 autodetection thanks to Torsten Seeboth and Atsushi Nakagawa + * from their contributions on DScaler. + */ + +#include +#include +#include +#include +#include "tuner-i2c.h" +#include "tea5767.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable verbose debug messages"); + +/*****************************************************************************/ + +struct tea5767_priv { + struct tuner_i2c_props i2c_props; + u32 frequency; + struct tea5767_ctrl ctrl; +}; + +/*****************************************************************************/ + +/****************************** + * Write mode register values * + ******************************/ + +/* First register */ +#define TEA5767_MUTE 0x80 /* Mutes output */ +#define TEA5767_SEARCH 0x40 /* Activates station search */ +/* Bits 0-5 for divider MSB */ + +/* Second register */ +/* Bits 0-7 for divider LSB */ + +/* Third register */ + +/* Station search from botton to up */ +#define TEA5767_SEARCH_UP 0x80 + +/* Searches with ADC output = 10 */ +#define TEA5767_SRCH_HIGH_LVL 0x60 + +/* Searches with ADC output = 10 */ +#define TEA5767_SRCH_MID_LVL 0x40 + +/* Searches with ADC output = 5 */ +#define TEA5767_SRCH_LOW_LVL 0x20 + +/* if on, div=4*(Frf+Fif)/Fref otherwise, div=4*(Frf-Fif)/Freq) */ +#define TEA5767_HIGH_LO_INJECT 0x10 + +/* Disable stereo */ +#define TEA5767_MONO 0x08 + +/* Disable right channel and turns to mono */ +#define TEA5767_MUTE_RIGHT 0x04 + +/* Disable left channel and turns to mono */ +#define TEA5767_MUTE_LEFT 0x02 + +#define TEA5767_PORT1_HIGH 0x01 + +/* Fourth register */ +#define TEA5767_PORT2_HIGH 0x80 +/* Chips stops working. Only I2C bus remains on */ +#define TEA5767_STDBY 0x40 + +/* Japan freq (76-108 MHz. If disabled, 87.5-108 MHz */ +#define TEA5767_JAPAN_BAND 0x20 + +/* Unselected means 32.768 KHz freq as reference. Otherwise Xtal at 13 MHz */ +#define TEA5767_XTAL_32768 0x10 + +/* Cuts weak signals */ +#define TEA5767_SOFT_MUTE 0x08 + +/* Activates high cut control */ +#define TEA5767_HIGH_CUT_CTRL 0x04 + +/* Activates stereo noise control */ +#define TEA5767_ST_NOISE_CTL 0x02 + +/* If activate PORT 1 indicates SEARCH or else it is used as PORT1 */ +#define TEA5767_SRCH_IND 0x01 + +/* Fifth register */ + +/* By activating, it will use Xtal at 13 MHz as reference for divider */ +#define TEA5767_PLLREF_ENABLE 0x80 + +/* By activating, deemphasis=50, or else, deemphasis of 50us */ +#define TEA5767_DEEMPH_75 0X40 + +/***************************** + * Read mode register values * + *****************************/ + +/* First register */ +#define TEA5767_READY_FLAG_MASK 0x80 +#define TEA5767_BAND_LIMIT_MASK 0X40 +/* Bits 0-5 for divider MSB after search or preset */ + +/* Second register */ +/* Bits 0-7 for divider LSB after search or preset */ + +/* Third register */ +#define TEA5767_STEREO_MASK 0x80 +#define TEA5767_IF_CNTR_MASK 0x7f + +/* Fourth register */ +#define TEA5767_ADC_LEVEL_MASK 0xf0 + +/* should be 0 */ +#define TEA5767_CHIP_ID_MASK 0x0f + +/* Fifth register */ +/* Reserved for future extensions */ +#define TEA5767_RESERVED_MASK 0xff + +/*****************************************************************************/ + +static void tea5767_status_dump(struct tea5767_priv *priv, + unsigned char *buffer) +{ + unsigned int div, frq; + + if (TEA5767_READY_FLAG_MASK & buffer[0]) + tuner_info("Ready Flag ON\n"); + else + tuner_info("Ready Flag OFF\n"); + + if (TEA5767_BAND_LIMIT_MASK & buffer[0]) + tuner_info("Tuner at band limit\n"); + else + tuner_info("Tuner not at band limit\n"); + + div = ((buffer[0] & 0x3f) << 8) | buffer[1]; + + switch (priv->ctrl.xtal_freq) { + case TEA5767_HIGH_LO_13MHz: + frq = (div * 50000 - 700000 - 225000) / 4; /* Freq in KHz */ + break; + case TEA5767_LOW_LO_13MHz: + frq = (div * 50000 + 700000 + 225000) / 4; /* Freq in KHz */ + break; + case TEA5767_LOW_LO_32768: + frq = (div * 32768 + 700000 + 225000) / 4; /* Freq in KHz */ + break; + case TEA5767_HIGH_LO_32768: + default: + frq = (div * 32768 - 700000 - 225000) / 4; /* Freq in KHz */ + break; + } + buffer[0] = (div >> 8) & 0x3f; + buffer[1] = div & 0xff; + + tuner_info("Frequency %d.%03d KHz (divider = 0x%04x)\n", + frq / 1000, frq % 1000, div); + + if (TEA5767_STEREO_MASK & buffer[2]) + tuner_info("Stereo\n"); + else + tuner_info("Mono\n"); + + tuner_info("IF Counter = %d\n", buffer[2] & TEA5767_IF_CNTR_MASK); + + tuner_info("ADC Level = %d\n", + (buffer[3] & TEA5767_ADC_LEVEL_MASK) >> 4); + + tuner_info("Chip ID = %d\n", (buffer[3] & TEA5767_CHIP_ID_MASK)); + + tuner_info("Reserved = 0x%02x\n", + (buffer[4] & TEA5767_RESERVED_MASK)); +} + +/* Freq should be specifyed at 62.5 Hz */ +static int set_radio_freq(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct tea5767_priv *priv = fe->tuner_priv; + unsigned int frq = params->frequency; + unsigned char buffer[5]; + unsigned div; + int rc; + + tuner_dbg("radio freq = %d.%03d MHz\n", frq/16000,(frq/16)%1000); + + buffer[2] = 0; + + if (priv->ctrl.port1) + buffer[2] |= TEA5767_PORT1_HIGH; + + if (params->audmode == V4L2_TUNER_MODE_MONO) { + tuner_dbg("TEA5767 set to mono\n"); + buffer[2] |= TEA5767_MONO; + } else { + tuner_dbg("TEA5767 set to stereo\n"); + } + + + buffer[3] = 0; + + if (priv->ctrl.port2) + buffer[3] |= TEA5767_PORT2_HIGH; + + if (priv->ctrl.high_cut) + buffer[3] |= TEA5767_HIGH_CUT_CTRL; + + if (priv->ctrl.st_noise) + buffer[3] |= TEA5767_ST_NOISE_CTL; + + if (priv->ctrl.soft_mute) + buffer[3] |= TEA5767_SOFT_MUTE; + + if (priv->ctrl.japan_band) + buffer[3] |= TEA5767_JAPAN_BAND; + + buffer[4] = 0; + + if (priv->ctrl.deemph_75) + buffer[4] |= TEA5767_DEEMPH_75; + + if (priv->ctrl.pllref) + buffer[4] |= TEA5767_PLLREF_ENABLE; + + + /* Rounds freq to next decimal value - for 62.5 KHz step */ + /* frq = 20*(frq/16)+radio_frq[frq%16]; */ + + switch (priv->ctrl.xtal_freq) { + case TEA5767_HIGH_LO_13MHz: + tuner_dbg("radio HIGH LO inject xtal @ 13 MHz\n"); + buffer[2] |= TEA5767_HIGH_LO_INJECT; + div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000; + break; + case TEA5767_LOW_LO_13MHz: + tuner_dbg("radio LOW LO inject xtal @ 13 MHz\n"); + + div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000; + break; + case TEA5767_LOW_LO_32768: + tuner_dbg("radio LOW LO inject xtal @ 32,768 MHz\n"); + buffer[3] |= TEA5767_XTAL_32768; + /* const 700=4000*175 Khz - to adjust freq to right value */ + div = ((frq * (4000 / 16) - 700000 - 225000) + 16384) >> 15; + break; + case TEA5767_HIGH_LO_32768: + default: + tuner_dbg("radio HIGH LO inject xtal @ 32,768 MHz\n"); + + buffer[2] |= TEA5767_HIGH_LO_INJECT; + buffer[3] |= TEA5767_XTAL_32768; + div = ((frq * (4000 / 16) + 700000 + 225000) + 16384) >> 15; + break; + } + buffer[0] = (div >> 8) & 0x3f; + buffer[1] = div & 0xff; + + if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) + tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); + + if (debug) { + if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) + tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); + else + tea5767_status_dump(priv, buffer); + } + + priv->frequency = frq * 125 / 2; + + return 0; +} + +static int tea5767_read_status(struct dvb_frontend *fe, char *buffer) +{ + struct tea5767_priv *priv = fe->tuner_priv; + int rc; + + memset(buffer, 0, 5); + if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) { + tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); + return -EREMOTEIO; + } + + return 0; +} + +static inline int tea5767_signal(struct dvb_frontend *fe, const char *buffer) +{ + struct tea5767_priv *priv = fe->tuner_priv; + + int signal = ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8); + + tuner_dbg("Signal strength: %d\n", signal); + + return signal; +} + +static inline int tea5767_stereo(struct dvb_frontend *fe, const char *buffer) +{ + struct tea5767_priv *priv = fe->tuner_priv; + + int stereo = buffer[2] & TEA5767_STEREO_MASK; + + tuner_dbg("Radio ST GET = %02x\n", stereo); + + return (stereo ? V4L2_TUNER_SUB_STEREO : 0); +} + +static int tea5767_get_status(struct dvb_frontend *fe, u32 *status) +{ + unsigned char buffer[5]; + + *status = 0; + + if (0 == tea5767_read_status(fe, buffer)) { + if (tea5767_signal(fe, buffer)) + *status = TUNER_STATUS_LOCKED; + if (tea5767_stereo(fe, buffer)) + *status |= TUNER_STATUS_STEREO; + } + + return 0; +} + +static int tea5767_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ + unsigned char buffer[5]; + + *strength = 0; + + if (0 == tea5767_read_status(fe, buffer)) + *strength = tea5767_signal(fe, buffer); + + return 0; +} + +static int tea5767_standby(struct dvb_frontend *fe) +{ + unsigned char buffer[5]; + struct tea5767_priv *priv = fe->tuner_priv; + unsigned div, rc; + + div = (87500 * 4 + 700 + 225 + 25) / 50; /* Set frequency to 87.5 MHz */ + buffer[0] = (div >> 8) & 0x3f; + buffer[1] = div & 0xff; + buffer[2] = TEA5767_PORT1_HIGH; + buffer[3] = TEA5767_PORT2_HIGH | TEA5767_HIGH_CUT_CTRL | + TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND | TEA5767_STDBY; + buffer[4] = 0; + + if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) + tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); + + return 0; +} + +int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) +{ + struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr }; + unsigned char buffer[7] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + int rc; + + if ((rc = tuner_i2c_xfer_recv(&i2c, buffer, 7))< 5) { + printk(KERN_WARNING "It is not a TEA5767. Received %i bytes.\n", rc); + return -EINVAL; + } + + /* If all bytes are the same then it's a TV tuner and not a tea5767 */ + if (buffer[0] == buffer[1] && buffer[0] == buffer[2] && + buffer[0] == buffer[3] && buffer[0] == buffer[4]) { + printk(KERN_WARNING "All bytes are equal. It is not a TEA5767\n"); + return -EINVAL; + } + + /* Status bytes: + * Byte 4: bit 3:1 : CI (Chip Identification) == 0 + * bit 0 : internally set to 0 + * Byte 5: bit 7:0 : == 0 + */ + if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) { + printk(KERN_WARNING "Chip ID is not zero. It is not a TEA5767\n"); + return -EINVAL; + } + + + return 0; +} + +static int tea5767_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + + return 0; +} + +static int tea5767_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tea5767_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + + return 0; +} + +static int tea5767_set_config (struct dvb_frontend *fe, void *priv_cfg) +{ + struct tea5767_priv *priv = fe->tuner_priv; + + memcpy(&priv->ctrl, priv_cfg, sizeof(priv->ctrl)); + + return 0; +} + +static struct dvb_tuner_ops tea5767_tuner_ops = { + .info = { + .name = "tea5767", // Philips TEA5767HN FM Radio + }, + + .set_analog_params = set_radio_freq, + .set_config = tea5767_set_config, + .sleep = tea5767_standby, + .release = tea5767_release, + .get_frequency = tea5767_get_frequency, + .get_status = tea5767_get_status, + .get_rf_strength = tea5767_get_rf_strength, +}; + +struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, + struct i2c_adapter* i2c_adap, + u8 i2c_addr) +{ + struct tea5767_priv *priv = NULL; + + priv = kzalloc(sizeof(struct tea5767_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + fe->tuner_priv = priv; + + priv->i2c_props.addr = i2c_addr; + priv->i2c_props.adap = i2c_adap; + priv->i2c_props.name = "tea5767"; + + priv->ctrl.xtal_freq = TEA5767_HIGH_LO_32768; + priv->ctrl.port1 = 1; + priv->ctrl.port2 = 1; + priv->ctrl.high_cut = 1; + priv->ctrl.st_noise = 1; + priv->ctrl.japan_band = 1; + + memcpy(&fe->ops.tuner_ops, &tea5767_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + tuner_info("type set to %s\n", "Philips TEA5767HN FM Radio"); + + return fe; +} + +EXPORT_SYMBOL_GPL(tea5767_attach); +EXPORT_SYMBOL_GPL(tea5767_autodetection); + +MODULE_DESCRIPTION("Philips TEA5767 FM tuner driver"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/tea5767.h b/drivers/media/tuners/tea5767.h new file mode 100644 index 000000000000..d30ab1b483de --- /dev/null +++ b/drivers/media/tuners/tea5767.h @@ -0,0 +1,66 @@ +/* + 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 +#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/tuners/tua9001.c b/drivers/media/tuners/tua9001.c new file mode 100644 index 000000000000..de2607084672 --- /dev/null +++ b/drivers/media/tuners/tua9001.c @@ -0,0 +1,215 @@ +/* + * Infineon TUA 9001 silicon tuner driver + * + * Copyright (C) 2009 Antti Palosaari + * + * 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 "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/tua9001.h b/drivers/media/tuners/tua9001.h new file mode 100644 index 000000000000..38d6ae76b1d6 --- /dev/null +++ b/drivers/media/tuners/tua9001.h @@ -0,0 +1,46 @@ +/* + * Infineon TUA 9001 silicon tuner driver + * + * Copyright (C) 2009 Antti Palosaari + * + * 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/tuners/tua9001_priv.h b/drivers/media/tuners/tua9001_priv.h new file mode 100644 index 000000000000..73cc1ce0575c --- /dev/null +++ b/drivers/media/tuners/tua9001_priv.h @@ -0,0 +1,34 @@ +/* + * Infineon TUA 9001 silicon tuner driver + * + * Copyright (C) 2009 Antti Palosaari + * + * 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/tuners/tuner-i2c.h b/drivers/media/tuners/tuner-i2c.h new file mode 100644 index 000000000000..18f005634c67 --- /dev/null +++ b/drivers/media/tuners/tuner-i2c.h @@ -0,0 +1,182 @@ +/* + tuner-i2c.h - i2c interface for different tuners + + Copyright (C) 2007 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 __TUNER_I2C_H__ +#define __TUNER_I2C_H__ + +#include +#include + +struct tuner_i2c_props { + u8 addr; + struct i2c_adapter *adap; + + /* used for tuner instance management */ + int count; + char *name; +}; + +static inline int tuner_i2c_xfer_send(struct tuner_i2c_props *props, char *buf, int len) +{ + struct i2c_msg msg = { .addr = props->addr, .flags = 0, + .buf = buf, .len = len }; + int ret = i2c_transfer(props->adap, &msg, 1); + + return (ret == 1) ? len : ret; +} + +static inline int tuner_i2c_xfer_recv(struct tuner_i2c_props *props, char *buf, int len) +{ + struct i2c_msg msg = { .addr = props->addr, .flags = I2C_M_RD, + .buf = buf, .len = len }; + int ret = i2c_transfer(props->adap, &msg, 1); + + return (ret == 1) ? len : ret; +} + +static inline int tuner_i2c_xfer_send_recv(struct tuner_i2c_props *props, + char *obuf, int olen, + char *ibuf, int ilen) +{ + struct i2c_msg msg[2] = { { .addr = props->addr, .flags = 0, + .buf = obuf, .len = olen }, + { .addr = props->addr, .flags = I2C_M_RD, + .buf = ibuf, .len = ilen } }; + int ret = i2c_transfer(props->adap, msg, 2); + + return (ret == 2) ? ilen : ret; +} + +/* Callers must declare as a global for the module: + * + * static LIST_HEAD(hybrid_tuner_instance_list); + * + * hybrid_tuner_instance_list should be the third argument + * passed into hybrid_tuner_request_state(). + * + * state structure must contain the following: + * + * struct list_head hybrid_tuner_instance_list; + * struct tuner_i2c_props i2c_props; + * + * hybrid_tuner_instance_list (both within state structure and globally) + * is only required if the driver is using hybrid_tuner_request_state + * and hybrid_tuner_release_state to manage state sharing between + * multiple instances of hybrid tuners. + */ + +#define tuner_printk(kernlvl, i2cprops, fmt, arg...) do { \ + printk(kernlvl "%s %d-%04x: " fmt, i2cprops.name, \ + i2cprops.adap ? \ + i2c_adapter_id(i2cprops.adap) : -1, \ + i2cprops.addr, ##arg); \ + } while (0) + +/* TO DO: convert all callers of these macros to pass in + * struct tuner_i2c_props, then remove the macro wrappers */ + +#define __tuner_warn(i2cprops, fmt, arg...) do { \ + tuner_printk(KERN_WARNING, i2cprops, fmt, ##arg); \ + } while (0) + +#define __tuner_info(i2cprops, fmt, arg...) do { \ + tuner_printk(KERN_INFO, i2cprops, fmt, ##arg); \ + } while (0) + +#define __tuner_err(i2cprops, fmt, arg...) do { \ + tuner_printk(KERN_ERR, i2cprops, fmt, ##arg); \ + } while (0) + +#define __tuner_dbg(i2cprops, fmt, arg...) do { \ + if ((debug)) \ + tuner_printk(KERN_DEBUG, i2cprops, fmt, ##arg); \ + } while (0) + +#define tuner_warn(fmt, arg...) __tuner_warn(priv->i2c_props, fmt, ##arg) +#define tuner_info(fmt, arg...) __tuner_info(priv->i2c_props, fmt, ##arg) +#define tuner_err(fmt, arg...) __tuner_err(priv->i2c_props, fmt, ##arg) +#define tuner_dbg(fmt, arg...) __tuner_dbg(priv->i2c_props, fmt, ##arg) + +/****************************************************************************/ + +/* The return value of hybrid_tuner_request_state indicates the number of + * instances using this tuner object. + * + * 0 - no instances, indicates an error - kzalloc must have failed + * + * 1 - one instance, indicates that the tuner object was created successfully + * + * 2 (or more) instances, indicates that an existing tuner object was found + */ + +#define hybrid_tuner_request_state(type, state, list, i2cadap, i2caddr, devname)\ +({ \ + int __ret = 0; \ + list_for_each_entry(state, &list, hybrid_tuner_instance_list) { \ + if (((i2cadap) && (state->i2c_props.adap)) && \ + ((i2c_adapter_id(state->i2c_props.adap) == \ + i2c_adapter_id(i2cadap)) && \ + (i2caddr == state->i2c_props.addr))) { \ + __tuner_info(state->i2c_props, \ + "attaching existing instance\n"); \ + state->i2c_props.count++; \ + __ret = state->i2c_props.count; \ + break; \ + } \ + } \ + if (0 == __ret) { \ + state = kzalloc(sizeof(type), GFP_KERNEL); \ + if (NULL == state) \ + goto __fail; \ + state->i2c_props.addr = i2caddr; \ + state->i2c_props.adap = i2cadap; \ + state->i2c_props.name = devname; \ + __tuner_info(state->i2c_props, \ + "creating new instance\n"); \ + list_add_tail(&state->hybrid_tuner_instance_list, &list);\ + state->i2c_props.count++; \ + __ret = state->i2c_props.count; \ + } \ +__fail: \ + __ret; \ +}) + +#define hybrid_tuner_release_state(state) \ +({ \ + int __ret; \ + state->i2c_props.count--; \ + __ret = state->i2c_props.count; \ + if (!state->i2c_props.count) { \ + __tuner_info(state->i2c_props, "destroying instance\n");\ + list_del(&state->hybrid_tuner_instance_list); \ + kfree(state); \ + } \ + __ret; \ +}) + +#define hybrid_tuner_report_instance_count(state) \ +({ \ + int __ret = 0; \ + if (state) \ + __ret = state->i2c_props.count; \ + __ret; \ +}) + +#endif /* __TUNER_I2C_H__ */ diff --git a/drivers/media/tuners/tuner-simple.c b/drivers/media/tuners/tuner-simple.c new file mode 100644 index 000000000000..39e7e583c8c0 --- /dev/null +++ b/drivers/media/tuners/tuner-simple.c @@ -0,0 +1,1155 @@ +/* + * i2c tv tuner chip device driver + * controls all those simple 4-control-bytes style tuners. + * + * This "tuner-simple" module was split apart from the original "tuner" module. + */ +#include +#include +#include +#include +#include +#include +#include "tuner-i2c.h" +#include "tuner-simple.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable verbose debug messages"); + +#define TUNER_SIMPLE_MAX 64 +static unsigned int simple_devcount; + +static int offset; +module_param(offset, int, 0664); +MODULE_PARM_DESC(offset, "Allows to specify an offset for tuner"); + +static unsigned int atv_input[TUNER_SIMPLE_MAX] = \ + { [0 ... (TUNER_SIMPLE_MAX-1)] = 0 }; +static unsigned int dtv_input[TUNER_SIMPLE_MAX] = \ + { [0 ... (TUNER_SIMPLE_MAX-1)] = 0 }; +module_param_array(atv_input, int, NULL, 0644); +module_param_array(dtv_input, int, NULL, 0644); +MODULE_PARM_DESC(atv_input, "specify atv rf input, 0 for autoselect"); +MODULE_PARM_DESC(dtv_input, "specify dtv rf input, 0 for autoselect"); + +/* ---------------------------------------------------------------------- */ + +/* tv standard selection for Temic 4046 FM5 + this value takes the low bits of control byte 2 + from datasheet Rev.01, Feb.00 + standard BG I L L2 D + picture IF 38.9 38.9 38.9 33.95 38.9 + sound 1 33.4 32.9 32.4 40.45 32.4 + sound 2 33.16 + NICAM 33.05 32.348 33.05 33.05 + */ +#define TEMIC_SET_PAL_I 0x05 +#define TEMIC_SET_PAL_DK 0x09 +#define TEMIC_SET_PAL_L 0x0a /* SECAM ? */ +#define TEMIC_SET_PAL_L2 0x0b /* change IF ! */ +#define TEMIC_SET_PAL_BG 0x0c + +/* tv tuner system standard selection for Philips FQ1216ME + this value takes the low bits of control byte 2 + from datasheet "1999 Nov 16" (supersedes "1999 Mar 23") + standard BG DK I L L` + picture carrier 38.90 38.90 38.90 38.90 33.95 + colour 34.47 34.47 34.47 34.47 38.38 + sound 1 33.40 32.40 32.90 32.40 40.45 + sound 2 33.16 - - - - + NICAM 33.05 33.05 32.35 33.05 39.80 + */ +#define PHILIPS_SET_PAL_I 0x01 /* Bit 2 always zero !*/ +#define PHILIPS_SET_PAL_BGDK 0x09 +#define PHILIPS_SET_PAL_L2 0x0a +#define PHILIPS_SET_PAL_L 0x0b + +/* system switching for Philips FI1216MF MK2 + from datasheet "1996 Jul 09", + standard BG L L' + picture carrier 38.90 38.90 33.95 + colour 34.47 34.37 38.38 + sound 1 33.40 32.40 40.45 + sound 2 33.16 - - + NICAM 33.05 33.05 39.80 + */ +#define PHILIPS_MF_SET_STD_BG 0x01 /* Bit 2 must be zero, Bit 3 is system output */ +#define PHILIPS_MF_SET_STD_L 0x03 /* Used on Secam France */ +#define PHILIPS_MF_SET_STD_LC 0x02 /* Used on SECAM L' */ + +/* Control byte */ + +#define TUNER_RATIO_MASK 0x06 /* Bit cb1:cb2 */ +#define TUNER_RATIO_SELECT_50 0x00 +#define TUNER_RATIO_SELECT_32 0x02 +#define TUNER_RATIO_SELECT_166 0x04 +#define TUNER_RATIO_SELECT_62 0x06 + +#define TUNER_CHARGE_PUMP 0x40 /* Bit cb6 */ + +/* Status byte */ + +#define TUNER_POR 0x80 +#define TUNER_FL 0x40 +#define TUNER_MODE 0x38 +#define TUNER_AFC 0x07 +#define TUNER_SIGNAL 0x07 +#define TUNER_STEREO 0x10 + +#define TUNER_PLL_LOCKED 0x40 +#define TUNER_STEREO_MK3 0x04 + +static DEFINE_MUTEX(tuner_simple_list_mutex); +static LIST_HEAD(hybrid_tuner_instance_list); + +struct tuner_simple_priv { + unsigned int nr; + u16 last_div; + + struct tuner_i2c_props i2c_props; + struct list_head hybrid_tuner_instance_list; + + unsigned int type; + struct tunertype *tun; + + u32 frequency; + u32 bandwidth; +}; + +/* ---------------------------------------------------------------------- */ + +static int tuner_read_status(struct dvb_frontend *fe) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + unsigned char byte; + + if (1 != tuner_i2c_xfer_recv(&priv->i2c_props, &byte, 1)) + return 0; + + return byte; +} + +static inline int tuner_signal(const int status) +{ + return (status & TUNER_SIGNAL) << 13; +} + +static inline int tuner_stereo(const int type, const int status) +{ + switch (type) { + case TUNER_PHILIPS_FM1216ME_MK3: + case TUNER_PHILIPS_FM1236_MK3: + case TUNER_PHILIPS_FM1256_IH3: + case TUNER_LG_NTSC_TAPE: + case TUNER_TCL_MF02GIP_5N: + return ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3); + case TUNER_PHILIPS_FM1216MK5: + return status | TUNER_STEREO; + default: + return status & TUNER_STEREO; + } +} + +static inline int tuner_islocked(const int status) +{ + return (status & TUNER_FL); +} + +static inline int tuner_afcstatus(const int status) +{ + return (status & TUNER_AFC) - 2; +} + + +static int simple_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + int tuner_status; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + tuner_status = tuner_read_status(fe); + + *status = 0; + + if (tuner_islocked(tuner_status)) + *status = TUNER_STATUS_LOCKED; + if (tuner_stereo(priv->type, tuner_status)) + *status |= TUNER_STATUS_STEREO; + + tuner_dbg("AFC Status: %d\n", tuner_afcstatus(tuner_status)); + + return 0; +} + +static int simple_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + int signal; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + signal = tuner_signal(tuner_read_status(fe)); + + *strength = signal; + + tuner_dbg("Signal strength: %d\n", signal); + + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static inline char *tuner_param_name(enum param_type type) +{ + char *name; + + switch (type) { + case TUNER_PARAM_TYPE_RADIO: + name = "radio"; + break; + case TUNER_PARAM_TYPE_PAL: + name = "pal"; + break; + case TUNER_PARAM_TYPE_SECAM: + name = "secam"; + break; + case TUNER_PARAM_TYPE_NTSC: + name = "ntsc"; + break; + case TUNER_PARAM_TYPE_DIGITAL: + name = "digital"; + break; + default: + name = "unknown"; + break; + } + return name; +} + +static struct tuner_params *simple_tuner_params(struct dvb_frontend *fe, + enum param_type desired_type) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + struct tunertype *tun = priv->tun; + int i; + + for (i = 0; i < tun->count; i++) + if (desired_type == tun->params[i].type) + break; + + /* use default tuner params if desired_type not available */ + if (i == tun->count) { + tuner_dbg("desired params (%s) undefined for tuner %d\n", + tuner_param_name(desired_type), priv->type); + i = 0; + } + + tuner_dbg("using tuner params #%d (%s)\n", i, + tuner_param_name(tun->params[i].type)); + + return &tun->params[i]; +} + +static int simple_config_lookup(struct dvb_frontend *fe, + struct tuner_params *t_params, + unsigned *frequency, u8 *config, u8 *cb) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + int i; + + for (i = 0; i < t_params->count; i++) { + if (*frequency > t_params->ranges[i].limit) + continue; + break; + } + if (i == t_params->count) { + tuner_dbg("frequency out of range (%d > %d)\n", + *frequency, t_params->ranges[i - 1].limit); + *frequency = t_params->ranges[--i].limit; + } + *config = t_params->ranges[i].config; + *cb = t_params->ranges[i].cb; + + tuner_dbg("freq = %d.%02d (%d), range = %d, " + "config = 0x%02x, cb = 0x%02x\n", + *frequency / 16, *frequency % 16 * 100 / 16, *frequency, + i, *config, *cb); + + return i; +} + +/* ---------------------------------------------------------------------- */ + +static void simple_set_rf_input(struct dvb_frontend *fe, + u8 *config, u8 *cb, unsigned int rf) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + + switch (priv->type) { + case TUNER_PHILIPS_TUV1236D: + switch (rf) { + case 1: + *cb |= 0x08; + break; + default: + *cb &= ~0x08; + break; + } + break; + case TUNER_PHILIPS_FCV1236D: + switch (rf) { + case 1: + *cb |= 0x01; + break; + default: + *cb &= ~0x01; + break; + } + break; + default: + break; + } +} + +static int simple_std_setup(struct dvb_frontend *fe, + struct analog_parameters *params, + u8 *config, u8 *cb) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + int rc; + + /* tv norm specific stuff for multi-norm tuners */ + switch (priv->type) { + case TUNER_PHILIPS_SECAM: /* FI1216MF */ + /* 0x01 -> ??? no change ??? */ + /* 0x02 -> PAL BDGHI / SECAM L */ + /* 0x04 -> ??? PAL others / SECAM others ??? */ + *cb &= ~0x03; + if (params->std & V4L2_STD_SECAM_L) + /* also valid for V4L2_STD_SECAM */ + *cb |= PHILIPS_MF_SET_STD_L; + else if (params->std & V4L2_STD_SECAM_LC) + *cb |= PHILIPS_MF_SET_STD_LC; + else /* V4L2_STD_B|V4L2_STD_GH */ + *cb |= PHILIPS_MF_SET_STD_BG; + break; + + case TUNER_TEMIC_4046FM5: + *cb &= ~0x0f; + + if (params->std & V4L2_STD_PAL_BG) { + *cb |= TEMIC_SET_PAL_BG; + + } else if (params->std & V4L2_STD_PAL_I) { + *cb |= TEMIC_SET_PAL_I; + + } else if (params->std & V4L2_STD_PAL_DK) { + *cb |= TEMIC_SET_PAL_DK; + + } else if (params->std & V4L2_STD_SECAM_L) { + *cb |= TEMIC_SET_PAL_L; + + } + break; + + case TUNER_PHILIPS_FQ1216ME: + *cb &= ~0x0f; + + if (params->std & (V4L2_STD_PAL_BG|V4L2_STD_PAL_DK)) { + *cb |= PHILIPS_SET_PAL_BGDK; + + } else if (params->std & V4L2_STD_PAL_I) { + *cb |= PHILIPS_SET_PAL_I; + + } else if (params->std & V4L2_STD_SECAM_L) { + *cb |= PHILIPS_SET_PAL_L; + + } + break; + + case TUNER_PHILIPS_FCV1236D: + /* 0x00 -> ATSC antenna input 1 */ + /* 0x01 -> ATSC antenna input 2 */ + /* 0x02 -> NTSC antenna input 1 */ + /* 0x03 -> NTSC antenna input 2 */ + *cb &= ~0x03; + if (!(params->std & V4L2_STD_ATSC)) + *cb |= 2; + break; + + case TUNER_MICROTUNE_4042FI5: + /* Set the charge pump for fast tuning */ + *config |= TUNER_CHARGE_PUMP; + break; + + case TUNER_PHILIPS_TUV1236D: + { + struct tuner_i2c_props i2c = priv->i2c_props; + /* 0x40 -> ATSC antenna input 1 */ + /* 0x48 -> ATSC antenna input 2 */ + /* 0x00 -> NTSC antenna input 1 */ + /* 0x08 -> NTSC antenna input 2 */ + u8 buffer[4] = { 0x14, 0x00, 0x17, 0x00}; + *cb &= ~0x40; + if (params->std & V4L2_STD_ATSC) { + *cb |= 0x40; + buffer[1] = 0x04; + } + /* set to the correct mode (analog or digital) */ + i2c.addr = 0x0a; + rc = tuner_i2c_xfer_send(&i2c, &buffer[0], 2); + if (2 != rc) + tuner_warn("i2c i/o error: rc == %d " + "(should be 2)\n", rc); + rc = tuner_i2c_xfer_send(&i2c, &buffer[2], 2); + if (2 != rc) + tuner_warn("i2c i/o error: rc == %d " + "(should be 2)\n", rc); + break; + } + } + if (atv_input[priv->nr]) + simple_set_rf_input(fe, config, cb, atv_input[priv->nr]); + + return 0; +} + +static int simple_set_aux_byte(struct dvb_frontend *fe, u8 config, u8 aux) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + int rc; + u8 buffer[2]; + + buffer[0] = (config & ~0x38) | 0x18; + buffer[1] = aux; + + tuner_dbg("setting aux byte: 0x%02x 0x%02x\n", buffer[0], buffer[1]); + + rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 2); + if (2 != rc) + tuner_warn("i2c i/o error: rc == %d (should be 2)\n", rc); + + return rc == 2 ? 0 : rc; +} + +static int simple_post_tune(struct dvb_frontend *fe, u8 *buffer, + u16 div, u8 config, u8 cb) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + int rc; + + switch (priv->type) { + case TUNER_LG_TDVS_H06XF: + simple_set_aux_byte(fe, config, 0x20); + break; + case TUNER_PHILIPS_FQ1216LME_MK3: + simple_set_aux_byte(fe, config, 0x60); /* External AGC */ + break; + case TUNER_MICROTUNE_4042FI5: + { + /* FIXME - this may also work for other tuners */ + unsigned long timeout = jiffies + msecs_to_jiffies(1); + u8 status_byte = 0; + + /* Wait until the PLL locks */ + for (;;) { + if (time_after(jiffies, timeout)) + return 0; + rc = tuner_i2c_xfer_recv(&priv->i2c_props, + &status_byte, 1); + if (1 != rc) { + tuner_warn("i2c i/o read error: rc == %d " + "(should be 1)\n", rc); + break; + } + if (status_byte & TUNER_PLL_LOCKED) + break; + udelay(10); + } + + /* Set the charge pump for optimized phase noise figure */ + config &= ~TUNER_CHARGE_PUMP; + buffer[0] = (div>>8) & 0x7f; + buffer[1] = div & 0xff; + buffer[2] = config; + buffer[3] = cb; + tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n", + buffer[0], buffer[1], buffer[2], buffer[3]); + + rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); + if (4 != rc) + tuner_warn("i2c i/o error: rc == %d " + "(should be 4)\n", rc); + break; + } + } + + return 0; +} + +static int simple_radio_bandswitch(struct dvb_frontend *fe, u8 *buffer) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + + switch (priv->type) { + case TUNER_TENA_9533_DI: + case TUNER_YMEC_TVF_5533MF: + tuner_dbg("This tuner doesn't have FM. " + "Most cards have a TEA5767 for FM\n"); + return 0; + case TUNER_PHILIPS_FM1216ME_MK3: + case TUNER_PHILIPS_FM1236_MK3: + case TUNER_PHILIPS_FMD1216ME_MK3: + case TUNER_PHILIPS_FMD1216MEX_MK3: + case TUNER_LG_NTSC_TAPE: + case TUNER_PHILIPS_FM1256_IH3: + case TUNER_TCL_MF02GIP_5N: + buffer[3] = 0x19; + break; + case TUNER_PHILIPS_FM1216MK5: + buffer[2] = 0x88; + buffer[3] = 0x09; + break; + case TUNER_TNF_5335MF: + buffer[3] = 0x11; + break; + case TUNER_LG_PAL_FM: + buffer[3] = 0xa5; + break; + case TUNER_THOMSON_DTT761X: + buffer[3] = 0x39; + break; + case TUNER_PHILIPS_FQ1216LME_MK3: + case TUNER_PHILIPS_FQ1236_MK5: + tuner_err("This tuner doesn't have FM\n"); + /* Set the low band for sanity, since it covers 88-108 MHz */ + buffer[3] = 0x01; + break; + case TUNER_MICROTUNE_4049FM5: + default: + buffer[3] = 0xa4; + break; + } + + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static int simple_set_tv_freq(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + u8 config, cb; + u16 div; + u8 buffer[4]; + int rc, IFPCoff, i; + enum param_type desired_type; + struct tuner_params *t_params; + + /* IFPCoff = Video Intermediate Frequency - Vif: + 940 =16*58.75 NTSC/J (Japan) + 732 =16*45.75 M/N STD + 704 =16*44 ATSC (at DVB code) + 632 =16*39.50 I U.K. + 622.4=16*38.90 B/G D/K I, L STD + 592 =16*37.00 D China + 590 =16.36.875 B Australia + 543.2=16*33.95 L' STD + 171.2=16*10.70 FM Radio (at set_radio_freq) + */ + + if (params->std == V4L2_STD_NTSC_M_JP) { + IFPCoff = 940; + desired_type = TUNER_PARAM_TYPE_NTSC; + } else if ((params->std & V4L2_STD_MN) && + !(params->std & ~V4L2_STD_MN)) { + IFPCoff = 732; + desired_type = TUNER_PARAM_TYPE_NTSC; + } else if (params->std == V4L2_STD_SECAM_LC) { + IFPCoff = 543; + desired_type = TUNER_PARAM_TYPE_SECAM; + } else { + IFPCoff = 623; + desired_type = TUNER_PARAM_TYPE_PAL; + } + + t_params = simple_tuner_params(fe, desired_type); + + i = simple_config_lookup(fe, t_params, ¶ms->frequency, + &config, &cb); + + div = params->frequency + IFPCoff + offset; + + tuner_dbg("Freq= %d.%02d MHz, V_IF=%d.%02d MHz, " + "Offset=%d.%02d MHz, div=%0d\n", + params->frequency / 16, params->frequency % 16 * 100 / 16, + IFPCoff / 16, IFPCoff % 16 * 100 / 16, + offset / 16, offset % 16 * 100 / 16, div); + + /* tv norm specific stuff for multi-norm tuners */ + simple_std_setup(fe, params, &config, &cb); + + if (t_params->cb_first_if_lower_freq && div < priv->last_div) { + buffer[0] = config; + buffer[1] = cb; + buffer[2] = (div>>8) & 0x7f; + buffer[3] = div & 0xff; + } else { + buffer[0] = (div>>8) & 0x7f; + buffer[1] = div & 0xff; + buffer[2] = config; + buffer[3] = cb; + } + priv->last_div = div; + if (t_params->has_tda9887) { + struct v4l2_priv_tun_config tda9887_cfg; + int tda_config = 0; + int is_secam_l = (params->std & (V4L2_STD_SECAM_L | + V4L2_STD_SECAM_LC)) && + !(params->std & ~(V4L2_STD_SECAM_L | + V4L2_STD_SECAM_LC)); + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &tda_config; + + if (params->std == V4L2_STD_SECAM_LC) { + if (t_params->port1_active ^ t_params->port1_invert_for_secam_lc) + tda_config |= TDA9887_PORT1_ACTIVE; + if (t_params->port2_active ^ t_params->port2_invert_for_secam_lc) + tda_config |= TDA9887_PORT2_ACTIVE; + } else { + if (t_params->port1_active) + tda_config |= TDA9887_PORT1_ACTIVE; + if (t_params->port2_active) + tda_config |= TDA9887_PORT2_ACTIVE; + } + if (t_params->intercarrier_mode) + tda_config |= TDA9887_INTERCARRIER; + if (is_secam_l) { + if (i == 0 && t_params->default_top_secam_low) + tda_config |= TDA9887_TOP(t_params->default_top_secam_low); + else if (i == 1 && t_params->default_top_secam_mid) + tda_config |= TDA9887_TOP(t_params->default_top_secam_mid); + else if (t_params->default_top_secam_high) + tda_config |= TDA9887_TOP(t_params->default_top_secam_high); + } else { + if (i == 0 && t_params->default_top_low) + tda_config |= TDA9887_TOP(t_params->default_top_low); + else if (i == 1 && t_params->default_top_mid) + tda_config |= TDA9887_TOP(t_params->default_top_mid); + else if (t_params->default_top_high) + tda_config |= TDA9887_TOP(t_params->default_top_high); + } + if (t_params->default_pll_gating_18) + tda_config |= TDA9887_GATING_18; + i2c_clients_command(priv->i2c_props.adap, TUNER_SET_CONFIG, + &tda9887_cfg); + } + tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n", + buffer[0], buffer[1], buffer[2], buffer[3]); + + rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); + if (4 != rc) + tuner_warn("i2c i/o error: rc == %d (should be 4)\n", rc); + + simple_post_tune(fe, &buffer[0], div, config, cb); + + return 0; +} + +static int simple_set_radio_freq(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct tunertype *tun; + struct tuner_simple_priv *priv = fe->tuner_priv; + u8 buffer[4]; + u16 div; + int rc, j; + struct tuner_params *t_params; + unsigned int freq = params->frequency; + + tun = priv->tun; + + for (j = tun->count-1; j > 0; j--) + if (tun->params[j].type == TUNER_PARAM_TYPE_RADIO) + break; + /* default t_params (j=0) will be used if desired type wasn't found */ + t_params = &tun->params[j]; + + /* Select Radio 1st IF used */ + switch (t_params->radio_if) { + case 0: /* 10.7 MHz */ + freq += (unsigned int)(10.7*16000); + break; + case 1: /* 33.3 MHz */ + freq += (unsigned int)(33.3*16000); + break; + case 2: /* 41.3 MHz */ + freq += (unsigned int)(41.3*16000); + break; + default: + tuner_warn("Unsupported radio_if value %d\n", + t_params->radio_if); + return 0; + } + + buffer[2] = (t_params->ranges[0].config & ~TUNER_RATIO_MASK) | + TUNER_RATIO_SELECT_50; /* 50 kHz step */ + + /* Bandswitch byte */ + simple_radio_bandswitch(fe, &buffer[0]); + + /* Convert from 1/16 kHz V4L steps to 1/20 MHz (=50 kHz) PLL steps + freq * (1 Mhz / 16000 V4L steps) * (20 PLL steps / 1 MHz) = + freq * (1/800) */ + div = (freq + 400) / 800; + + if (t_params->cb_first_if_lower_freq && div < priv->last_div) { + buffer[0] = buffer[2]; + buffer[1] = buffer[3]; + buffer[2] = (div>>8) & 0x7f; + buffer[3] = div & 0xff; + } else { + buffer[0] = (div>>8) & 0x7f; + buffer[1] = div & 0xff; + } + + tuner_dbg("radio 0x%02x 0x%02x 0x%02x 0x%02x\n", + buffer[0], buffer[1], buffer[2], buffer[3]); + priv->last_div = div; + + if (t_params->has_tda9887) { + int config = 0; + struct v4l2_priv_tun_config tda9887_cfg; + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &config; + + if (t_params->port1_active && + !t_params->port1_fm_high_sensitivity) + config |= TDA9887_PORT1_ACTIVE; + if (t_params->port2_active && + !t_params->port2_fm_high_sensitivity) + config |= TDA9887_PORT2_ACTIVE; + if (t_params->intercarrier_mode) + config |= TDA9887_INTERCARRIER; +/* if (t_params->port1_set_for_fm_mono) + config &= ~TDA9887_PORT1_ACTIVE;*/ + if (t_params->fm_gain_normal) + config |= TDA9887_GAIN_NORMAL; + if (t_params->radio_if == 2) + config |= TDA9887_RIF_41_3; + i2c_clients_command(priv->i2c_props.adap, TUNER_SET_CONFIG, + &tda9887_cfg); + } + rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); + if (4 != rc) + tuner_warn("i2c i/o error: rc == %d (should be 4)\n", rc); + + /* Write AUX byte */ + switch (priv->type) { + case TUNER_PHILIPS_FM1216ME_MK3: + buffer[2] = 0x98; + buffer[3] = 0x20; /* set TOP AGC */ + rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); + if (4 != rc) + tuner_warn("i2c i/o error: rc == %d (should be 4)\n", rc); + break; + } + + return 0; +} + +static int simple_set_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + int ret = -EINVAL; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + switch (params->mode) { + case V4L2_TUNER_RADIO: + ret = simple_set_radio_freq(fe, params); + priv->frequency = params->frequency * 125 / 2; + break; + case V4L2_TUNER_ANALOG_TV: + case V4L2_TUNER_DIGITAL_TV: + ret = simple_set_tv_freq(fe, params); + priv->frequency = params->frequency * 62500; + break; + } + priv->bandwidth = 0; + + return ret; +} + +static void simple_set_dvb(struct dvb_frontend *fe, u8 *buf, + const u32 delsys, + const u32 frequency, + const u32 bandwidth) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + + switch (priv->type) { + case TUNER_PHILIPS_FMD1216ME_MK3: + case TUNER_PHILIPS_FMD1216MEX_MK3: + if (bandwidth == 8000000 && + frequency >= 158870000) + buf[3] |= 0x08; + break; + case TUNER_PHILIPS_TD1316: + /* determine band */ + buf[3] |= (frequency < 161000000) ? 1 : + (frequency < 444000000) ? 2 : 4; + + /* setup PLL filter */ + if (bandwidth == 8000000) + buf[3] |= 1 << 3; + break; + case TUNER_PHILIPS_TUV1236D: + case TUNER_PHILIPS_FCV1236D: + { + unsigned int new_rf; + + if (dtv_input[priv->nr]) + new_rf = dtv_input[priv->nr]; + else + switch (delsys) { + case SYS_DVBC_ANNEX_B: + new_rf = 1; + break; + case SYS_ATSC: + default: + new_rf = 0; + break; + } + simple_set_rf_input(fe, &buf[2], &buf[3], new_rf); + break; + } + default: + break; + } +} + +static u32 simple_dvb_configure(struct dvb_frontend *fe, u8 *buf, + const u32 delsys, + const u32 freq, + const u32 bw) +{ + /* This function returns the tuned frequency on success, 0 on error */ + struct tuner_simple_priv *priv = fe->tuner_priv; + struct tunertype *tun = priv->tun; + static struct tuner_params *t_params; + u8 config, cb; + u32 div; + int ret; + u32 frequency = freq / 62500; + + if (!tun->stepsize) { + /* tuner-core was loaded before the digital tuner was + * configured and somehow picked the wrong tuner type */ + tuner_err("attempt to treat tuner %d (%s) as digital tuner " + "without stepsize defined.\n", + priv->type, priv->tun->name); + return 0; /* failure */ + } + + t_params = simple_tuner_params(fe, TUNER_PARAM_TYPE_DIGITAL); + ret = simple_config_lookup(fe, t_params, &frequency, &config, &cb); + if (ret < 0) + return 0; /* failure */ + + div = ((frequency + t_params->iffreq) * 62500 + offset + + tun->stepsize/2) / tun->stepsize; + + buf[0] = div >> 8; + buf[1] = div & 0xff; + buf[2] = config; + buf[3] = cb; + + simple_set_dvb(fe, buf, delsys, freq, bw); + + tuner_dbg("%s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n", + tun->name, div, buf[0], buf[1], buf[2], buf[3]); + + /* calculate the frequency we set it to */ + return (div * tun->stepsize) - t_params->iffreq; +} + +static int simple_dvb_calc_regs(struct dvb_frontend *fe, + u8 *buf, int buf_len) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 delsys = c->delivery_system; + u32 bw = c->bandwidth_hz; + struct tuner_simple_priv *priv = fe->tuner_priv; + u32 frequency; + + if (buf_len < 5) + return -EINVAL; + + frequency = simple_dvb_configure(fe, buf+1, delsys, c->frequency, bw); + if (frequency == 0) + return -EINVAL; + + buf[0] = priv->i2c_props.addr; + + priv->frequency = frequency; + priv->bandwidth = c->bandwidth_hz; + + return 5; +} + +static int simple_dvb_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; + u32 freq = c->frequency; + struct tuner_simple_priv *priv = fe->tuner_priv; + u32 frequency; + u32 prev_freq, prev_bw; + int ret; + u8 buf[5]; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + prev_freq = priv->frequency; + prev_bw = priv->bandwidth; + + frequency = simple_dvb_configure(fe, buf+1, delsys, freq, bw); + if (frequency == 0) + return -EINVAL; + + buf[0] = priv->i2c_props.addr; + + priv->frequency = frequency; + priv->bandwidth = bw; + + /* put analog demod in standby when tuning digital */ + if (fe->ops.analog_ops.standby) + fe->ops.analog_ops.standby(fe); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* buf[0] contains the i2c address, but * + * we already have it in i2c_props.addr */ + ret = tuner_i2c_xfer_send(&priv->i2c_props, buf+1, 4); + if (ret != 4) + goto fail; + + return 0; +fail: + /* calc_regs sets frequency and bandwidth. if we failed, unset them */ + priv->frequency = prev_freq; + priv->bandwidth = prev_bw; + + return ret; +} + +static int simple_init(struct dvb_frontend *fe) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + if (priv->tun->initdata) { + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = tuner_i2c_xfer_send(&priv->i2c_props, + priv->tun->initdata + 1, + priv->tun->initdata[0]); + if (ret != priv->tun->initdata[0]) + return ret; + } + + return 0; +} + +static int simple_sleep(struct dvb_frontend *fe) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + if (priv->tun->sleepdata) { + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = tuner_i2c_xfer_send(&priv->i2c_props, + priv->tun->sleepdata + 1, + priv->tun->sleepdata[0]); + if (ret != priv->tun->sleepdata[0]) + return ret; + } + + return 0; +} + +static int simple_release(struct dvb_frontend *fe) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + + mutex_lock(&tuner_simple_list_mutex); + + if (priv) + hybrid_tuner_release_state(priv); + + mutex_unlock(&tuner_simple_list_mutex); + + fe->tuner_priv = NULL; + + return 0; +} + +static int simple_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int simple_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +static struct dvb_tuner_ops simple_tuner_ops = { + .init = simple_init, + .sleep = simple_sleep, + .set_analog_params = simple_set_params, + .set_params = simple_dvb_set_params, + .calc_regs = simple_dvb_calc_regs, + .release = simple_release, + .get_frequency = simple_get_frequency, + .get_bandwidth = simple_get_bandwidth, + .get_status = simple_get_status, + .get_rf_strength = simple_get_rf_strength, +}; + +struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c_adap, + u8 i2c_addr, + unsigned int type) +{ + struct tuner_simple_priv *priv = NULL; + int instance; + + if (type >= tuner_count) { + printk(KERN_WARNING "%s: invalid tuner type: %d (max: %d)\n", + __func__, type, tuner_count-1); + return NULL; + } + + /* If i2c_adap is set, check that the tuner is at the correct address. + * Otherwise, if i2c_adap is NULL, the tuner will be programmed directly + * by the digital demod via calc_regs. + */ + if (i2c_adap != NULL) { + u8 b[1]; + struct i2c_msg msg = { + .addr = i2c_addr, .flags = I2C_M_RD, + .buf = b, .len = 1, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (1 != i2c_transfer(i2c_adap, &msg, 1)) + printk(KERN_WARNING "tuner-simple %d-%04x: " + "unable to probe %s, proceeding anyway.", + i2c_adapter_id(i2c_adap), i2c_addr, + tuners[type].name); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + mutex_lock(&tuner_simple_list_mutex); + + instance = hybrid_tuner_request_state(struct tuner_simple_priv, priv, + hybrid_tuner_instance_list, + i2c_adap, i2c_addr, + "tuner-simple"); + switch (instance) { + case 0: + mutex_unlock(&tuner_simple_list_mutex); + return NULL; + case 1: + fe->tuner_priv = priv; + + priv->type = type; + priv->tun = &tuners[type]; + priv->nr = simple_devcount++; + break; + default: + fe->tuner_priv = priv; + break; + } + + mutex_unlock(&tuner_simple_list_mutex); + + memcpy(&fe->ops.tuner_ops, &simple_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + if (type != priv->type) + tuner_warn("couldn't set type to %d. Using %d (%s) instead\n", + type, priv->type, priv->tun->name); + else + tuner_info("type set to %d (%s)\n", + priv->type, priv->tun->name); + + if ((debug) || ((atv_input[priv->nr] > 0) || + (dtv_input[priv->nr] > 0))) { + if (0 == atv_input[priv->nr]) + tuner_info("tuner %d atv rf input will be " + "autoselected\n", priv->nr); + else + tuner_info("tuner %d atv rf input will be " + "set to input %d (insmod option)\n", + priv->nr, atv_input[priv->nr]); + if (0 == dtv_input[priv->nr]) + tuner_info("tuner %d dtv rf input will be " + "autoselected\n", priv->nr); + else + tuner_info("tuner %d dtv rf input will be " + "set to input %d (insmod option)\n", + priv->nr, dtv_input[priv->nr]); + } + + strlcpy(fe->ops.tuner_ops.info.name, priv->tun->name, + sizeof(fe->ops.tuner_ops.info.name)); + + return fe; +} +EXPORT_SYMBOL_GPL(simple_tuner_attach); + +MODULE_DESCRIPTION("Simple 4-control-bytes style tuner driver"); +MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); +MODULE_LICENSE("GPL"); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/tuners/tuner-simple.h b/drivers/media/tuners/tuner-simple.h new file mode 100644 index 000000000000..381fa5d35a9b --- /dev/null +++ b/drivers/media/tuners/tuner-simple.h @@ -0,0 +1,39 @@ +/* + 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 +#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/tuners/tuner-types.c b/drivers/media/tuners/tuner-types.c new file mode 100644 index 000000000000..2da4440c16ee --- /dev/null +++ b/drivers/media/tuners/tuner-types.c @@ -0,0 +1,1883 @@ +/* + * + * i2c tv tuner chip device type database. + * + */ + +#include +#include +#include +#include + +/* ---------------------------------------------------------------------- */ + +/* + * The floats in the tuner struct are computed at compile time + * by gcc and cast back to integers. Thus we don't violate the + * "no float in kernel" rule. + * + * A tuner_range may be referenced by multiple tuner_params structs. + * There are many duplicates in here. Reusing tuner_range structs, + * rather than defining new ones for each tuner, will cut down on + * memory usage, and is preferred when possible. + * + * Each tuner_params array may contain one or more elements, one + * for each video standard. + * + * FIXME: tuner_params struct contains an element, tda988x. We must + * set this for all tuners that contain a tda988x chip, and then we + * can remove this setting from the various card structs. + * + * FIXME: Right now, all tuners are using the first tuner_params[] + * array element for analog mode. In the future, we will be merging + * similar tuner definitions together, such that each tuner definition + * will have a tuner_params struct for each available video standard. + * At that point, the tuner_params[] array element will be chosen + * based on the video standard in use. + */ + +/* The following was taken from dvb-pll.c: */ + +/* Set AGC TOP value to 103 dBuV: + * 0x80 = Control Byte + * 0x40 = 250 uA charge pump (irrelevant) + * 0x18 = Aux Byte to follow + * 0x06 = 64.5 kHz divider (irrelevant) + * 0x01 = Disable Vt (aka sleep) + * + * 0x00 = AGC Time constant 2s Iagc = 300 nA (vs 0x80 = 9 nA) + * 0x50 = AGC Take over point = 103 dBuV + */ +static u8 tua603x_agc103[] = { 2, 0x80|0x40|0x18|0x06|0x01, 0x00|0x50 }; + +/* 0x04 = 166.67 kHz divider + * + * 0x80 = AGC Time constant 50ms Iagc = 9 uA + * 0x20 = AGC Take over point = 112 dBuV + */ +static u8 tua603x_agc112[] = { 2, 0x80|0x40|0x18|0x04|0x01, 0x80|0x20 }; + +/* 0-9 */ +/* ------------ TUNER_TEMIC_PAL - TEMIC PAL ------------ */ + +static struct tuner_range tuner_temic_pal_ranges[] = { + { 16 * 140.25 /*MHz*/, 0x8e, 0x02, }, + { 16 * 463.25 /*MHz*/, 0x8e, 0x04, }, + { 16 * 999.99 , 0x8e, 0x01, }, +}; + +static struct tuner_params tuner_temic_pal_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_pal_ranges), + }, +}; + +/* ------------ TUNER_PHILIPS_PAL_I - Philips PAL_I ------------ */ + +static struct tuner_range tuner_philips_pal_i_ranges[] = { + { 16 * 140.25 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 463.25 /*MHz*/, 0x8e, 0x90, }, + { 16 * 999.99 , 0x8e, 0x30, }, +}; + +static struct tuner_params tuner_philips_pal_i_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_pal_i_ranges, + .count = ARRAY_SIZE(tuner_philips_pal_i_ranges), + }, +}; + +/* ------------ TUNER_PHILIPS_NTSC - Philips NTSC ------------ */ + +static struct tuner_range tuner_philips_ntsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 451.25 /*MHz*/, 0x8e, 0x90, }, + { 16 * 999.99 , 0x8e, 0x30, }, +}; + +static struct tuner_params tuner_philips_ntsc_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_philips_ntsc_ranges, + .count = ARRAY_SIZE(tuner_philips_ntsc_ranges), + .cb_first_if_lower_freq = 1, + }, +}; + +/* ------------ TUNER_PHILIPS_SECAM - Philips SECAM ------------ */ + +static struct tuner_range tuner_philips_secam_ranges[] = { + { 16 * 168.25 /*MHz*/, 0x8e, 0xa7, }, + { 16 * 447.25 /*MHz*/, 0x8e, 0x97, }, + { 16 * 999.99 , 0x8e, 0x37, }, +}; + +static struct tuner_params tuner_philips_secam_params[] = { + { + .type = TUNER_PARAM_TYPE_SECAM, + .ranges = tuner_philips_secam_ranges, + .count = ARRAY_SIZE(tuner_philips_secam_ranges), + .cb_first_if_lower_freq = 1, + }, +}; + +/* ------------ TUNER_PHILIPS_PAL - Philips PAL ------------ */ + +static struct tuner_range tuner_philips_pal_ranges[] = { + { 16 * 168.25 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 447.25 /*MHz*/, 0x8e, 0x90, }, + { 16 * 999.99 , 0x8e, 0x30, }, +}; + +static struct tuner_params tuner_philips_pal_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_pal_ranges, + .count = ARRAY_SIZE(tuner_philips_pal_ranges), + .cb_first_if_lower_freq = 1, + }, +}; + +/* ------------ TUNER_TEMIC_NTSC - TEMIC NTSC ------------ */ + +static struct tuner_range tuner_temic_ntsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0x8e, 0x02, }, + { 16 * 463.25 /*MHz*/, 0x8e, 0x04, }, + { 16 * 999.99 , 0x8e, 0x01, }, +}; + +static struct tuner_params tuner_temic_ntsc_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_temic_ntsc_ranges, + .count = ARRAY_SIZE(tuner_temic_ntsc_ranges), + }, +}; + +/* ------------ TUNER_TEMIC_PAL_I - TEMIC PAL_I ------------ */ + +static struct tuner_range tuner_temic_pal_i_ranges[] = { + { 16 * 170.00 /*MHz*/, 0x8e, 0x02, }, + { 16 * 450.00 /*MHz*/, 0x8e, 0x04, }, + { 16 * 999.99 , 0x8e, 0x01, }, +}; + +static struct tuner_params tuner_temic_pal_i_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_pal_i_ranges, + .count = ARRAY_SIZE(tuner_temic_pal_i_ranges), + }, +}; + +/* ------------ TUNER_TEMIC_4036FY5_NTSC - TEMIC NTSC ------------ */ + +static struct tuner_range tuner_temic_4036fy5_ntsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 463.25 /*MHz*/, 0x8e, 0x90, }, + { 16 * 999.99 , 0x8e, 0x30, }, +}; + +static struct tuner_params tuner_temic_4036fy5_ntsc_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_temic_4036fy5_ntsc_ranges, + .count = ARRAY_SIZE(tuner_temic_4036fy5_ntsc_ranges), + }, +}; + +/* ------------ TUNER_ALPS_TSBH1_NTSC - TEMIC NTSC ------------ */ + +static struct tuner_range tuner_alps_tsb_1_ranges[] = { + { 16 * 137.25 /*MHz*/, 0x8e, 0x01, }, + { 16 * 385.25 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x08, }, +}; + +static struct tuner_params tuner_alps_tsbh1_ntsc_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_alps_tsb_1_ranges, + .count = ARRAY_SIZE(tuner_alps_tsb_1_ranges), + }, +}; + +/* 10-19 */ +/* ------------ TUNER_ALPS_TSBE1_PAL - TEMIC PAL ------------ */ + +static struct tuner_params tuner_alps_tsb_1_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_alps_tsb_1_ranges, + .count = ARRAY_SIZE(tuner_alps_tsb_1_ranges), + }, +}; + +/* ------------ TUNER_ALPS_TSBB5_PAL_I - Alps PAL_I ------------ */ + +static struct tuner_range tuner_alps_tsb_5_pal_ranges[] = { + { 16 * 133.25 /*MHz*/, 0x8e, 0x01, }, + { 16 * 351.25 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x08, }, +}; + +static struct tuner_params tuner_alps_tsbb5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_alps_tsb_5_pal_ranges, + .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges), + }, +}; + +/* ------------ TUNER_ALPS_TSBE5_PAL - Alps PAL ------------ */ + +static struct tuner_params tuner_alps_tsbe5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_alps_tsb_5_pal_ranges, + .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges), + }, +}; + +/* ------------ TUNER_ALPS_TSBC5_PAL - Alps PAL ------------ */ + +static struct tuner_params tuner_alps_tsbc5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_alps_tsb_5_pal_ranges, + .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges), + }, +}; + +/* ------------ TUNER_TEMIC_4006FH5_PAL - TEMIC PAL ------------ */ + +static struct tuner_range tuner_lg_pal_ranges[] = { + { 16 * 170.00 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 450.00 /*MHz*/, 0x8e, 0x90, }, + { 16 * 999.99 , 0x8e, 0x30, }, +}; + +static struct tuner_params tuner_temic_4006fh5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_pal_ranges, + .count = ARRAY_SIZE(tuner_lg_pal_ranges), + }, +}; + +/* ------------ TUNER_ALPS_TSHC6_NTSC - Alps NTSC ------------ */ + +static struct tuner_range tuner_alps_tshc6_ntsc_ranges[] = { + { 16 * 137.25 /*MHz*/, 0x8e, 0x14, }, + { 16 * 385.25 /*MHz*/, 0x8e, 0x12, }, + { 16 * 999.99 , 0x8e, 0x11, }, +}; + +static struct tuner_params tuner_alps_tshc6_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_alps_tshc6_ntsc_ranges, + .count = ARRAY_SIZE(tuner_alps_tshc6_ntsc_ranges), + }, +}; + +/* ------------ TUNER_TEMIC_PAL_DK - TEMIC PAL ------------ */ + +static struct tuner_range tuner_temic_pal_dk_ranges[] = { + { 16 * 168.25 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 456.25 /*MHz*/, 0x8e, 0x90, }, + { 16 * 999.99 , 0x8e, 0x30, }, +}; + +static struct tuner_params tuner_temic_pal_dk_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_pal_dk_ranges, + .count = ARRAY_SIZE(tuner_temic_pal_dk_ranges), + }, +}; + +/* ------------ TUNER_PHILIPS_NTSC_M - Philips NTSC ------------ */ + +static struct tuner_range tuner_philips_ntsc_m_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 454.00 /*MHz*/, 0x8e, 0x90, }, + { 16 * 999.99 , 0x8e, 0x30, }, +}; + +static struct tuner_params tuner_philips_ntsc_m_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_philips_ntsc_m_ranges, + .count = ARRAY_SIZE(tuner_philips_ntsc_m_ranges), + }, +}; + +/* ------------ TUNER_TEMIC_4066FY5_PAL_I - TEMIC PAL_I ------------ */ + +static struct tuner_range tuner_temic_40x6f_5_pal_ranges[] = { + { 16 * 169.00 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 454.00 /*MHz*/, 0x8e, 0x90, }, + { 16 * 999.99 , 0x8e, 0x30, }, +}; + +static struct tuner_params tuner_temic_4066fy5_pal_i_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_40x6f_5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_40x6f_5_pal_ranges), + }, +}; + +/* ------------ TUNER_TEMIC_4006FN5_MULTI_PAL - TEMIC PAL ------------ */ + +static struct tuner_params tuner_temic_4006fn5_multi_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_40x6f_5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_40x6f_5_pal_ranges), + }, +}; + +/* 20-29 */ +/* ------------ TUNER_TEMIC_4009FR5_PAL - TEMIC PAL ------------ */ + +static struct tuner_range tuner_temic_4009f_5_pal_ranges[] = { + { 16 * 141.00 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 464.00 /*MHz*/, 0x8e, 0x90, }, + { 16 * 999.99 , 0x8e, 0x30, }, +}; + +static struct tuner_params tuner_temic_4009f_5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_4009f_5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), + }, +}; + +/* ------------ TUNER_TEMIC_4039FR5_NTSC - TEMIC NTSC ------------ */ + +static struct tuner_range tuner_temic_4x3x_f_5_ntsc_ranges[] = { + { 16 * 158.00 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 453.00 /*MHz*/, 0x8e, 0x90, }, + { 16 * 999.99 , 0x8e, 0x30, }, +}; + +static struct tuner_params tuner_temic_4039fr5_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_temic_4x3x_f_5_ntsc_ranges, + .count = ARRAY_SIZE(tuner_temic_4x3x_f_5_ntsc_ranges), + }, +}; + +/* ------------ TUNER_TEMIC_4046FM5 - TEMIC PAL ------------ */ + +static struct tuner_params tuner_temic_4046fm5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_40x6f_5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_40x6f_5_pal_ranges), + }, +}; + +/* ------------ TUNER_PHILIPS_PAL_DK - Philips PAL ------------ */ + +static struct tuner_params tuner_philips_pal_dk_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_pal_ranges, + .count = ARRAY_SIZE(tuner_lg_pal_ranges), + }, +}; + +/* ------------ TUNER_PHILIPS_FQ1216ME - Philips PAL ------------ */ + +static struct tuner_params tuner_philips_fq1216me_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_pal_ranges, + .count = ARRAY_SIZE(tuner_lg_pal_ranges), + .has_tda9887 = 1, + .port1_active = 1, + .port2_active = 1, + .port2_invert_for_secam_lc = 1, + }, +}; + +/* ------------ TUNER_LG_PAL_I_FM - LGINNOTEK PAL_I ------------ */ + +static struct tuner_params tuner_lg_pal_i_fm_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_pal_ranges, + .count = ARRAY_SIZE(tuner_lg_pal_ranges), + }, +}; + +/* ------------ TUNER_LG_PAL_I - LGINNOTEK PAL_I ------------ */ + +static struct tuner_params tuner_lg_pal_i_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_pal_ranges, + .count = ARRAY_SIZE(tuner_lg_pal_ranges), + }, +}; + +/* ------------ TUNER_LG_NTSC_FM - LGINNOTEK NTSC ------------ */ + +static struct tuner_range tuner_lg_ntsc_fm_ranges[] = { + { 16 * 210.00 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 497.00 /*MHz*/, 0x8e, 0x90, }, + { 16 * 999.99 , 0x8e, 0x30, }, +}; + +static struct tuner_params tuner_lg_ntsc_fm_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_lg_ntsc_fm_ranges, + .count = ARRAY_SIZE(tuner_lg_ntsc_fm_ranges), + }, +}; + +/* ------------ TUNER_LG_PAL_FM - LGINNOTEK PAL ------------ */ + +static struct tuner_params tuner_lg_pal_fm_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_pal_ranges, + .count = ARRAY_SIZE(tuner_lg_pal_ranges), + }, +}; + +/* ------------ TUNER_LG_PAL - LGINNOTEK PAL ------------ */ + +static struct tuner_params tuner_lg_pal_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_pal_ranges, + .count = ARRAY_SIZE(tuner_lg_pal_ranges), + }, +}; + +/* 30-39 */ +/* ------------ TUNER_TEMIC_4009FN5_MULTI_PAL_FM - TEMIC PAL ------------ */ + +static struct tuner_params tuner_temic_4009_fn5_multi_pal_fm_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_4009f_5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), + }, +}; + +/* ------------ TUNER_SHARP_2U5JF5540_NTSC - SHARP NTSC ------------ */ + +static struct tuner_range tuner_sharp_2u5jf5540_ntsc_ranges[] = { + { 16 * 137.25 /*MHz*/, 0x8e, 0x01, }, + { 16 * 317.25 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x08, }, +}; + +static struct tuner_params tuner_sharp_2u5jf5540_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_sharp_2u5jf5540_ntsc_ranges, + .count = ARRAY_SIZE(tuner_sharp_2u5jf5540_ntsc_ranges), + }, +}; + +/* ------------ TUNER_Samsung_PAL_TCPM9091PD27 - Samsung PAL ------------ */ + +static struct tuner_range tuner_samsung_pal_tcpm9091pd27_ranges[] = { + { 16 * 169 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 464 /*MHz*/, 0x8e, 0x90, }, + { 16 * 999.99 , 0x8e, 0x30, }, +}; + +static struct tuner_params tuner_samsung_pal_tcpm9091pd27_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_samsung_pal_tcpm9091pd27_ranges, + .count = ARRAY_SIZE(tuner_samsung_pal_tcpm9091pd27_ranges), + }, +}; + +/* ------------ TUNER_TEMIC_4106FH5 - TEMIC PAL ------------ */ + +static struct tuner_params tuner_temic_4106fh5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_4009f_5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), + }, +}; + +/* ------------ TUNER_TEMIC_4012FY5 - TEMIC PAL ------------ */ + +static struct tuner_params tuner_temic_4012fy5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_pal_ranges), + }, +}; + +/* ------------ TUNER_TEMIC_4136FY5 - TEMIC NTSC ------------ */ + +static struct tuner_params tuner_temic_4136_fy5_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_temic_4x3x_f_5_ntsc_ranges, + .count = ARRAY_SIZE(tuner_temic_4x3x_f_5_ntsc_ranges), + }, +}; + +/* ------------ TUNER_LG_PAL_NEW_TAPC - LGINNOTEK PAL ------------ */ + +static struct tuner_range tuner_lg_new_tapc_ranges[] = { + { 16 * 170.00 /*MHz*/, 0x8e, 0x01, }, + { 16 * 450.00 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x08, }, +}; + +static struct tuner_params tuner_lg_pal_new_tapc_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_new_tapc_ranges, + .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges), + }, +}; + +/* ------------ TUNER_PHILIPS_FM1216ME_MK3 - Philips PAL ------------ */ + +static struct tuner_range tuner_fm1216me_mk3_pal_ranges[] = { + { 16 * 158.00 /*MHz*/, 0x8e, 0x01, }, + { 16 * 442.00 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x04, }, +}; + +static struct tuner_params tuner_fm1216me_mk3_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_fm1216me_mk3_pal_ranges, + .count = ARRAY_SIZE(tuner_fm1216me_mk3_pal_ranges), + .cb_first_if_lower_freq = 1, + .has_tda9887 = 1, + .port1_active = 1, + .port2_active = 1, + .port2_invert_for_secam_lc = 1, + .port1_fm_high_sensitivity = 1, + .default_top_mid = -2, + .default_top_secam_mid = -2, + .default_top_secam_high = -2, + }, +}; + +/* ------------ TUNER_PHILIPS_FM1216MK5 - Philips PAL ------------ */ + +static struct tuner_range tuner_fm1216mk5_pal_ranges[] = { + { 16 * 158.00 /*MHz*/, 0xce, 0x01, }, + { 16 * 441.00 /*MHz*/, 0xce, 0x02, }, + { 16 * 864.00 , 0xce, 0x04, }, +}; + +static struct tuner_params tuner_fm1216mk5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_fm1216mk5_pal_ranges, + .count = ARRAY_SIZE(tuner_fm1216mk5_pal_ranges), + .cb_first_if_lower_freq = 1, + .has_tda9887 = 1, + .port1_active = 1, + .port2_active = 1, + .port2_invert_for_secam_lc = 1, + .port1_fm_high_sensitivity = 1, + .default_top_mid = -2, + .default_top_secam_mid = -2, + .default_top_secam_high = -2, + }, +}; + +/* ------------ TUNER_LG_NTSC_NEW_TAPC - LGINNOTEK NTSC ------------ */ + +static struct tuner_params tuner_lg_ntsc_new_tapc_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_lg_new_tapc_ranges, + .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges), + }, +}; + +/* 40-49 */ +/* ------------ TUNER_HITACHI_NTSC - HITACHI NTSC ------------ */ + +static struct tuner_params tuner_hitachi_ntsc_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_lg_new_tapc_ranges, + .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges), + }, +}; + +/* ------------ TUNER_PHILIPS_PAL_MK - Philips PAL ------------ */ + +static struct tuner_range tuner_philips_pal_mk_pal_ranges[] = { + { 16 * 140.25 /*MHz*/, 0x8e, 0x01, }, + { 16 * 463.25 /*MHz*/, 0x8e, 0xc2, }, + { 16 * 999.99 , 0x8e, 0xcf, }, +}; + +static struct tuner_params tuner_philips_pal_mk_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_pal_mk_pal_ranges, + .count = ARRAY_SIZE(tuner_philips_pal_mk_pal_ranges), + }, +}; + +/* ---- TUNER_PHILIPS_FCV1236D - Philips FCV1236D (ATSC/NTSC) ---- */ + +static struct tuner_range tuner_philips_fcv1236d_ntsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0x8e, 0xa2, }, + { 16 * 451.25 /*MHz*/, 0x8e, 0x92, }, + { 16 * 999.99 , 0x8e, 0x32, }, +}; + +static struct tuner_range tuner_philips_fcv1236d_atsc_ranges[] = { + { 16 * 159.00 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 453.00 /*MHz*/, 0x8e, 0x90, }, + { 16 * 999.99 , 0x8e, 0x30, }, +}; + +static struct tuner_params tuner_philips_fcv1236d_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_philips_fcv1236d_ntsc_ranges, + .count = ARRAY_SIZE(tuner_philips_fcv1236d_ntsc_ranges), + }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_philips_fcv1236d_atsc_ranges, + .count = ARRAY_SIZE(tuner_philips_fcv1236d_atsc_ranges), + .iffreq = 16 * 44.00, + }, +}; + +/* ------------ TUNER_PHILIPS_FM1236_MK3 - Philips NTSC ------------ */ + +static struct tuner_range tuner_fm1236_mk3_ntsc_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x8e, 0x01, }, + { 16 * 442.00 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x04, }, +}; + +static struct tuner_params tuner_fm1236_mk3_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_fm1236_mk3_ntsc_ranges, + .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), + .cb_first_if_lower_freq = 1, + .has_tda9887 = 1, + .port1_active = 1, + .port2_active = 1, + .port1_fm_high_sensitivity = 1, + }, +}; + +/* ------------ TUNER_PHILIPS_4IN1 - Philips NTSC ------------ */ + +static struct tuner_params tuner_philips_4in1_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_fm1236_mk3_ntsc_ranges, + .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), + }, +}; + +/* ------------ TUNER_MICROTUNE_4049FM5 - Microtune PAL ------------ */ + +static struct tuner_params tuner_microtune_4049_fm5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_4009f_5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), + .has_tda9887 = 1, + .port1_invert_for_secam_lc = 1, + .default_pll_gating_18 = 1, + .fm_gain_normal=1, + .radio_if = 1, /* 33.3 MHz */ + }, +}; + +/* ------------ TUNER_PANASONIC_VP27 - Panasonic NTSC ------------ */ + +static struct tuner_range tuner_panasonic_vp27_ntsc_ranges[] = { + { 16 * 160.00 /*MHz*/, 0xce, 0x01, }, + { 16 * 454.00 /*MHz*/, 0xce, 0x02, }, + { 16 * 999.99 , 0xce, 0x08, }, +}; + +static struct tuner_params tuner_panasonic_vp27_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_panasonic_vp27_ntsc_ranges, + .count = ARRAY_SIZE(tuner_panasonic_vp27_ntsc_ranges), + .has_tda9887 = 1, + .intercarrier_mode = 1, + .default_top_low = -3, + .default_top_mid = -3, + .default_top_high = -3, + }, +}; + +/* ------------ TUNER_TNF_8831BGFF - Philips PAL ------------ */ + +static struct tuner_range tuner_tnf_8831bgff_pal_ranges[] = { + { 16 * 161.25 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 463.25 /*MHz*/, 0x8e, 0x90, }, + { 16 * 999.99 , 0x8e, 0x30, }, +}; + +static struct tuner_params tuner_tnf_8831bgff_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_tnf_8831bgff_pal_ranges, + .count = ARRAY_SIZE(tuner_tnf_8831bgff_pal_ranges), + }, +}; + +/* ------------ TUNER_MICROTUNE_4042FI5 - Microtune NTSC ------------ */ + +static struct tuner_range tuner_microtune_4042fi5_ntsc_ranges[] = { + { 16 * 162.00 /*MHz*/, 0x8e, 0xa2, }, + { 16 * 457.00 /*MHz*/, 0x8e, 0x94, }, + { 16 * 999.99 , 0x8e, 0x31, }, +}; + +static struct tuner_range tuner_microtune_4042fi5_atsc_ranges[] = { + { 16 * 162.00 /*MHz*/, 0x8e, 0xa1, }, + { 16 * 457.00 /*MHz*/, 0x8e, 0x91, }, + { 16 * 999.99 , 0x8e, 0x31, }, +}; + +static struct tuner_params tuner_microtune_4042fi5_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_microtune_4042fi5_ntsc_ranges, + .count = ARRAY_SIZE(tuner_microtune_4042fi5_ntsc_ranges), + }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_microtune_4042fi5_atsc_ranges, + .count = ARRAY_SIZE(tuner_microtune_4042fi5_atsc_ranges), + .iffreq = 16 * 44.00 /*MHz*/, + }, +}; + +/* 50-59 */ +/* ------------ TUNER_TCL_2002N - TCL NTSC ------------ */ + +static struct tuner_range tuner_tcl_2002n_ntsc_ranges[] = { + { 16 * 172.00 /*MHz*/, 0x8e, 0x01, }, + { 16 * 448.00 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x08, }, +}; + +static struct tuner_params tuner_tcl_2002n_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_tcl_2002n_ntsc_ranges, + .count = ARRAY_SIZE(tuner_tcl_2002n_ntsc_ranges), + .cb_first_if_lower_freq = 1, + }, +}; + +/* ------------ TUNER_PHILIPS_FM1256_IH3 - Philips PAL ------------ */ + +static struct tuner_params tuner_philips_fm1256_ih3_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_fm1236_mk3_ntsc_ranges, + .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), + .radio_if = 1, /* 33.3 MHz */ + }, +}; + +/* ------------ TUNER_THOMSON_DTT7610 - THOMSON ATSC ------------ */ + +/* single range used for both ntsc and atsc */ +static struct tuner_range tuner_thomson_dtt7610_ntsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0x8e, 0x39, }, + { 16 * 454.00 /*MHz*/, 0x8e, 0x3a, }, + { 16 * 999.99 , 0x8e, 0x3c, }, +}; + +static struct tuner_params tuner_thomson_dtt7610_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_thomson_dtt7610_ntsc_ranges, + .count = ARRAY_SIZE(tuner_thomson_dtt7610_ntsc_ranges), + }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_thomson_dtt7610_ntsc_ranges, + .count = ARRAY_SIZE(tuner_thomson_dtt7610_ntsc_ranges), + .iffreq = 16 * 44.00 /*MHz*/, + }, +}; + +/* ------------ TUNER_PHILIPS_FQ1286 - Philips NTSC ------------ */ + +static struct tuner_range tuner_philips_fq1286_ntsc_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x8e, 0x41, }, + { 16 * 454.00 /*MHz*/, 0x8e, 0x42, }, + { 16 * 999.99 , 0x8e, 0x04, }, +}; + +static struct tuner_params tuner_philips_fq1286_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_philips_fq1286_ntsc_ranges, + .count = ARRAY_SIZE(tuner_philips_fq1286_ntsc_ranges), + }, +}; + +/* ------------ TUNER_TCL_2002MB - TCL PAL ------------ */ + +static struct tuner_range tuner_tcl_2002mb_pal_ranges[] = { + { 16 * 170.00 /*MHz*/, 0xce, 0x01, }, + { 16 * 450.00 /*MHz*/, 0xce, 0x02, }, + { 16 * 999.99 , 0xce, 0x08, }, +}; + +static struct tuner_params tuner_tcl_2002mb_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_tcl_2002mb_pal_ranges, + .count = ARRAY_SIZE(tuner_tcl_2002mb_pal_ranges), + }, +}; + +/* ------------ TUNER_PHILIPS_FQ1216AME_MK4 - Philips PAL ------------ */ + +static struct tuner_range tuner_philips_fq12_6a___mk4_pal_ranges[] = { + { 16 * 160.00 /*MHz*/, 0xce, 0x01, }, + { 16 * 442.00 /*MHz*/, 0xce, 0x02, }, + { 16 * 999.99 , 0xce, 0x04, }, +}; + +static struct tuner_params tuner_philips_fq1216ame_mk4_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_fq12_6a___mk4_pal_ranges, + .count = ARRAY_SIZE(tuner_philips_fq12_6a___mk4_pal_ranges), + .has_tda9887 = 1, + .port1_active = 1, + .port2_invert_for_secam_lc = 1, + .default_top_mid = -2, + .default_top_secam_low = -2, + .default_top_secam_mid = -2, + .default_top_secam_high = -2, + }, +}; + +/* ------------ TUNER_PHILIPS_FQ1236A_MK4 - Philips NTSC ------------ */ + +static struct tuner_params tuner_philips_fq1236a_mk4_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_fm1236_mk3_ntsc_ranges, + .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), + }, +}; + +/* ------------ TUNER_YMEC_TVF_8531MF - Philips NTSC ------------ */ + +static struct tuner_params tuner_ymec_tvf_8531mf_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_philips_ntsc_m_ranges, + .count = ARRAY_SIZE(tuner_philips_ntsc_m_ranges), + }, +}; + +/* ------------ TUNER_YMEC_TVF_5533MF - Philips NTSC ------------ */ + +static struct tuner_range tuner_ymec_tvf_5533mf_ntsc_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x8e, 0x01, }, + { 16 * 454.00 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x04, }, +}; + +static struct tuner_params tuner_ymec_tvf_5533mf_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_ymec_tvf_5533mf_ntsc_ranges, + .count = ARRAY_SIZE(tuner_ymec_tvf_5533mf_ntsc_ranges), + }, +}; + +/* 60-69 */ +/* ------------ TUNER_THOMSON_DTT761X - THOMSON ATSC ------------ */ +/* DTT 7611 7611A 7612 7613 7613A 7614 7615 7615A */ + +static struct tuner_range tuner_thomson_dtt761x_ntsc_ranges[] = { + { 16 * 145.25 /*MHz*/, 0x8e, 0x39, }, + { 16 * 415.25 /*MHz*/, 0x8e, 0x3a, }, + { 16 * 999.99 , 0x8e, 0x3c, }, +}; + +static struct tuner_range tuner_thomson_dtt761x_atsc_ranges[] = { + { 16 * 147.00 /*MHz*/, 0x8e, 0x39, }, + { 16 * 417.00 /*MHz*/, 0x8e, 0x3a, }, + { 16 * 999.99 , 0x8e, 0x3c, }, +}; + +static struct tuner_params tuner_thomson_dtt761x_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_thomson_dtt761x_ntsc_ranges, + .count = ARRAY_SIZE(tuner_thomson_dtt761x_ntsc_ranges), + .has_tda9887 = 1, + .fm_gain_normal = 1, + .radio_if = 2, /* 41.3 MHz */ + }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_thomson_dtt761x_atsc_ranges, + .count = ARRAY_SIZE(tuner_thomson_dtt761x_atsc_ranges), + .iffreq = 16 * 44.00, /*MHz*/ + }, +}; + +/* ------------ TUNER_TENA_9533_DI - Philips PAL ------------ */ + +static struct tuner_range tuner_tena_9533_di_pal_ranges[] = { + { 16 * 160.25 /*MHz*/, 0x8e, 0x01, }, + { 16 * 464.25 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x04, }, +}; + +static struct tuner_params tuner_tena_9533_di_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_tena_9533_di_pal_ranges, + .count = ARRAY_SIZE(tuner_tena_9533_di_pal_ranges), + }, +}; + +/* ------------ TUNER_TENA_TNF_5337 - Tena tnf5337MFD STD M/N ------------ */ + +static struct tuner_range tuner_tena_tnf_5337_ntsc_ranges[] = { + { 16 * 166.25 /*MHz*/, 0x86, 0x01, }, + { 16 * 466.25 /*MHz*/, 0x86, 0x02, }, + { 16 * 999.99 , 0x86, 0x08, }, +}; + +static struct tuner_params tuner_tena_tnf_5337_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_tena_tnf_5337_ntsc_ranges, + .count = ARRAY_SIZE(tuner_tena_tnf_5337_ntsc_ranges), + }, +}; + +/* ------------ TUNER_PHILIPS_FMD1216ME(X)_MK3 - Philips PAL ------------ */ + +static struct tuner_range tuner_philips_fmd1216me_mk3_pal_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x86, 0x51, }, + { 16 * 442.00 /*MHz*/, 0x86, 0x52, }, + { 16 * 999.99 , 0x86, 0x54, }, +}; + +static struct tuner_range tuner_philips_fmd1216me_mk3_dvb_ranges[] = { + { 16 * 143.87 /*MHz*/, 0xbc, 0x41 }, + { 16 * 158.87 /*MHz*/, 0xf4, 0x41 }, + { 16 * 329.87 /*MHz*/, 0xbc, 0x42 }, + { 16 * 441.87 /*MHz*/, 0xf4, 0x42 }, + { 16 * 625.87 /*MHz*/, 0xbc, 0x44 }, + { 16 * 803.87 /*MHz*/, 0xf4, 0x44 }, + { 16 * 999.99 , 0xfc, 0x44 }, +}; + +static struct tuner_params tuner_philips_fmd1216me_mk3_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_fmd1216me_mk3_pal_ranges, + .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_pal_ranges), + .has_tda9887 = 1, + .port1_active = 1, + .port2_active = 1, + .port2_fm_high_sensitivity = 1, + .port2_invert_for_secam_lc = 1, + .port1_set_for_fm_mono = 1, + }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_philips_fmd1216me_mk3_dvb_ranges, + .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_dvb_ranges), + .iffreq = 16 * 36.125, /*MHz*/ + }, +}; + +static struct tuner_params tuner_philips_fmd1216mex_mk3_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_fmd1216me_mk3_pal_ranges, + .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_pal_ranges), + .has_tda9887 = 1, + .port1_active = 1, + .port2_active = 1, + .port2_fm_high_sensitivity = 1, + .port2_invert_for_secam_lc = 1, + .port1_set_for_fm_mono = 1, + .radio_if = 1, + .fm_gain_normal = 1, + }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_philips_fmd1216me_mk3_dvb_ranges, + .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_dvb_ranges), + .iffreq = 16 * 36.125, /*MHz*/ + }, +}; + +/* ------ TUNER_LG_TDVS_H06XF - LG INNOTEK / INFINEON ATSC ----- */ + +static struct tuner_range tuner_tua6034_ntsc_ranges[] = { + { 16 * 165.00 /*MHz*/, 0x8e, 0x01 }, + { 16 * 450.00 /*MHz*/, 0x8e, 0x02 }, + { 16 * 999.99 , 0x8e, 0x04 }, +}; + +static struct tuner_range tuner_tua6034_atsc_ranges[] = { + { 16 * 165.00 /*MHz*/, 0xce, 0x01 }, + { 16 * 450.00 /*MHz*/, 0xce, 0x02 }, + { 16 * 999.99 , 0xce, 0x04 }, +}; + +static struct tuner_params tuner_lg_tdvs_h06xf_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_tua6034_ntsc_ranges, + .count = ARRAY_SIZE(tuner_tua6034_ntsc_ranges), + }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_tua6034_atsc_ranges, + .count = ARRAY_SIZE(tuner_tua6034_atsc_ranges), + .iffreq = 16 * 44.00, + }, +}; + +/* ------------ TUNER_YMEC_TVF66T5_B_DFF - Philips PAL ------------ */ + +static struct tuner_range tuner_ymec_tvf66t5_b_dff_pal_ranges[] = { + { 16 * 160.25 /*MHz*/, 0x8e, 0x01, }, + { 16 * 464.25 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x08, }, +}; + +static struct tuner_params tuner_ymec_tvf66t5_b_dff_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_ymec_tvf66t5_b_dff_pal_ranges, + .count = ARRAY_SIZE(tuner_ymec_tvf66t5_b_dff_pal_ranges), + }, +}; + +/* ------------ TUNER_LG_NTSC_TALN_MINI - LGINNOTEK NTSC ------------ */ + +static struct tuner_range tuner_lg_taln_ntsc_ranges[] = { + { 16 * 137.25 /*MHz*/, 0x8e, 0x01, }, + { 16 * 373.25 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x08, }, +}; + +static struct tuner_range tuner_lg_taln_pal_secam_ranges[] = { + { 16 * 150.00 /*MHz*/, 0x8e, 0x01, }, + { 16 * 425.00 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x08, }, +}; + +static struct tuner_params tuner_lg_taln_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_lg_taln_ntsc_ranges, + .count = ARRAY_SIZE(tuner_lg_taln_ntsc_ranges), + },{ + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_taln_pal_secam_ranges, + .count = ARRAY_SIZE(tuner_lg_taln_pal_secam_ranges), + }, +}; + +/* ------------ TUNER_PHILIPS_TD1316 - Philips PAL ------------ */ + +static struct tuner_range tuner_philips_td1316_pal_ranges[] = { + { 16 * 160.00 /*MHz*/, 0xc8, 0xa1, }, + { 16 * 442.00 /*MHz*/, 0xc8, 0xa2, }, + { 16 * 999.99 , 0xc8, 0xa4, }, +}; + +static struct tuner_range tuner_philips_td1316_dvb_ranges[] = { + { 16 * 93.834 /*MHz*/, 0xca, 0x60, }, + { 16 * 123.834 /*MHz*/, 0xca, 0xa0, }, + { 16 * 163.834 /*MHz*/, 0xca, 0xc0, }, + { 16 * 253.834 /*MHz*/, 0xca, 0x60, }, + { 16 * 383.834 /*MHz*/, 0xca, 0xa0, }, + { 16 * 443.834 /*MHz*/, 0xca, 0xc0, }, + { 16 * 583.834 /*MHz*/, 0xca, 0x60, }, + { 16 * 793.834 /*MHz*/, 0xca, 0xa0, }, + { 16 * 999.999 , 0xca, 0xe0, }, +}; + +static struct tuner_params tuner_philips_td1316_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_td1316_pal_ranges, + .count = ARRAY_SIZE(tuner_philips_td1316_pal_ranges), + }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_philips_td1316_dvb_ranges, + .count = ARRAY_SIZE(tuner_philips_td1316_dvb_ranges), + .iffreq = 16 * 36.166667 /*MHz*/, + }, +}; + +/* ------------ TUNER_PHILIPS_TUV1236D - Philips ATSC ------------ */ + +static struct tuner_range tuner_tuv1236d_ntsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0xce, 0x01, }, + { 16 * 454.00 /*MHz*/, 0xce, 0x02, }, + { 16 * 999.99 , 0xce, 0x04, }, +}; + +static struct tuner_range tuner_tuv1236d_atsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0xc6, 0x41, }, + { 16 * 454.00 /*MHz*/, 0xc6, 0x42, }, + { 16 * 999.99 , 0xc6, 0x44, }, +}; + +static struct tuner_params tuner_tuv1236d_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_tuv1236d_ntsc_ranges, + .count = ARRAY_SIZE(tuner_tuv1236d_ntsc_ranges), + }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_tuv1236d_atsc_ranges, + .count = ARRAY_SIZE(tuner_tuv1236d_atsc_ranges), + .iffreq = 16 * 44.00, + }, +}; + +/* ------------ TUNER_TNF_xxx5 - Texas Instruments--------- */ +/* This is known to work with Tenna TVF58t5-MFF and TVF5835 MFF + * but it is expected to work also with other Tenna/Ymec + * models based on TI SN 761677 chip on both PAL and NTSC + */ + +static struct tuner_range tuner_tnf_5335_d_if_pal_ranges[] = { + { 16 * 168.25 /*MHz*/, 0x8e, 0x01, }, + { 16 * 471.25 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x08, }, +}; + +static struct tuner_range tuner_tnf_5335mf_ntsc_ranges[] = { + { 16 * 169.25 /*MHz*/, 0x8e, 0x01, }, + { 16 * 469.25 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x08, }, +}; + +static struct tuner_params tuner_tnf_5335mf_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_tnf_5335mf_ntsc_ranges, + .count = ARRAY_SIZE(tuner_tnf_5335mf_ntsc_ranges), + }, + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_tnf_5335_d_if_pal_ranges, + .count = ARRAY_SIZE(tuner_tnf_5335_d_if_pal_ranges), + }, +}; + +/* 70-79 */ +/* ------------ TUNER_SAMSUNG_TCPN_2121P30A - Samsung NTSC ------------ */ + +/* '+ 4' turns on the Low Noise Amplifier */ +static struct tuner_range tuner_samsung_tcpn_2121p30a_ntsc_ranges[] = { + { 16 * 130.00 /*MHz*/, 0xce, 0x01 + 4, }, + { 16 * 364.50 /*MHz*/, 0xce, 0x02 + 4, }, + { 16 * 999.99 , 0xce, 0x08 + 4, }, +}; + +static struct tuner_params tuner_samsung_tcpn_2121p30a_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_samsung_tcpn_2121p30a_ntsc_ranges, + .count = ARRAY_SIZE(tuner_samsung_tcpn_2121p30a_ntsc_ranges), + }, +}; + +/* ------------ TUNER_THOMSON_FE6600 - DViCO Hybrid PAL ------------ */ + +static struct tuner_range tuner_thomson_fe6600_pal_ranges[] = { + { 16 * 160.00 /*MHz*/, 0xfe, 0x11, }, + { 16 * 442.00 /*MHz*/, 0xf6, 0x12, }, + { 16 * 999.99 , 0xf6, 0x18, }, +}; + +static struct tuner_range tuner_thomson_fe6600_dvb_ranges[] = { + { 16 * 250.00 /*MHz*/, 0xb4, 0x12, }, + { 16 * 455.00 /*MHz*/, 0xfe, 0x11, }, + { 16 * 775.50 /*MHz*/, 0xbc, 0x18, }, + { 16 * 999.99 , 0xf4, 0x18, }, +}; + +static struct tuner_params tuner_thomson_fe6600_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_thomson_fe6600_pal_ranges, + .count = ARRAY_SIZE(tuner_thomson_fe6600_pal_ranges), + }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_thomson_fe6600_dvb_ranges, + .count = ARRAY_SIZE(tuner_thomson_fe6600_dvb_ranges), + .iffreq = 16 * 36.125 /*MHz*/, + }, +}; + +/* ------------ TUNER_SAMSUNG_TCPG_6121P30A - Samsung PAL ------------ */ + +/* '+ 4' turns on the Low Noise Amplifier */ +static struct tuner_range tuner_samsung_tcpg_6121p30a_pal_ranges[] = { + { 16 * 146.25 /*MHz*/, 0xce, 0x01 + 4, }, + { 16 * 428.50 /*MHz*/, 0xce, 0x02 + 4, }, + { 16 * 999.99 , 0xce, 0x08 + 4, }, +}; + +static struct tuner_params tuner_samsung_tcpg_6121p30a_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_samsung_tcpg_6121p30a_pal_ranges, + .count = ARRAY_SIZE(tuner_samsung_tcpg_6121p30a_pal_ranges), + .has_tda9887 = 1, + .port1_active = 1, + .port2_active = 1, + .port2_invert_for_secam_lc = 1, + }, +}; + +/* ------------ TUNER_TCL_MF02GIP-5N-E - TCL MF02GIP-5N ------------ */ + +static struct tuner_range tuner_tcl_mf02gip_5n_ntsc_ranges[] = { + { 16 * 172.00 /*MHz*/, 0x8e, 0x01, }, + { 16 * 448.00 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x04, }, +}; + +static struct tuner_params tuner_tcl_mf02gip_5n_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_tcl_mf02gip_5n_ntsc_ranges, + .count = ARRAY_SIZE(tuner_tcl_mf02gip_5n_ntsc_ranges), + .cb_first_if_lower_freq = 1, + }, +}; + +/* 80-89 */ +/* --------- TUNER_PHILIPS_FQ1216LME_MK3 -- active loopthrough, no FM ------- */ + +static struct tuner_params tuner_fq1216lme_mk3_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_fm1216me_mk3_pal_ranges, + .count = ARRAY_SIZE(tuner_fm1216me_mk3_pal_ranges), + .cb_first_if_lower_freq = 1, /* not specified, but safe to do */ + .has_tda9887 = 1, /* TDA9886 */ + .port1_active = 1, + .port2_active = 1, + .port2_invert_for_secam_lc = 1, + .default_top_low = 4, + .default_top_mid = 4, + .default_top_high = 4, + .default_top_secam_low = 4, + .default_top_secam_mid = 4, + .default_top_secam_high = 4, + }, +}; + +/* ----- TUNER_PARTSNIC_PTI_5NF05 - Partsnic (Daewoo) PTI-5NF05 NTSC ----- */ + +static struct tuner_range tuner_partsnic_pti_5nf05_ranges[] = { + /* The datasheet specified channel ranges and the bandswitch byte */ + /* The control byte value of 0x8e is just a guess */ + { 16 * 133.25 /*MHz*/, 0x8e, 0x01, }, /* Channels 2 - B */ + { 16 * 367.25 /*MHz*/, 0x8e, 0x02, }, /* Channels C - W+11 */ + { 16 * 999.99 , 0x8e, 0x08, }, /* Channels W+12 - 69 */ +}; + +static struct tuner_params tuner_partsnic_pti_5nf05_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_partsnic_pti_5nf05_ranges, + .count = ARRAY_SIZE(tuner_partsnic_pti_5nf05_ranges), + .cb_first_if_lower_freq = 1, /* not specified but safe to do */ + }, +}; + +/* --------- TUNER_PHILIPS_CU1216L - DVB-C NIM ------------------------- */ + +static struct tuner_range tuner_cu1216l_ranges[] = { + { 16 * 160.25 /*MHz*/, 0xce, 0x01 }, + { 16 * 444.25 /*MHz*/, 0xce, 0x02 }, + { 16 * 999.99 , 0xce, 0x04 }, +}; + +static struct tuner_params tuner_philips_cu1216l_params[] = { + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_cu1216l_ranges, + .count = ARRAY_SIZE(tuner_cu1216l_ranges), + .iffreq = 16 * 36.125, /*MHz*/ + }, +}; + +/* ---------------------- TUNER_SONY_BTF_PXN01Z ------------------------ */ + +static struct tuner_range tuner_sony_btf_pxn01z_ranges[] = { + { 16 * 137.25 /*MHz*/, 0x8e, 0x01, }, + { 16 * 367.25 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x04, }, +}; + +static struct tuner_params tuner_sony_btf_pxn01z_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_sony_btf_pxn01z_ranges, + .count = ARRAY_SIZE(tuner_sony_btf_pxn01z_ranges), + }, +}; + +/* ------------ TUNER_PHILIPS_FQ1236_MK5 - Philips NTSC ------------ */ + +static struct tuner_params tuner_philips_fq1236_mk5_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_fm1236_mk3_ntsc_ranges, + .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), + .has_tda9887 = 1, /* TDA9885, no FM radio */ + }, +}; + +/* --------------------------------------------------------------------- */ + +struct tunertype tuners[] = { + /* 0-9 */ + [TUNER_TEMIC_PAL] = { /* TEMIC PAL */ + .name = "Temic PAL (4002 FH5)", + .params = tuner_temic_pal_params, + .count = ARRAY_SIZE(tuner_temic_pal_params), + }, + [TUNER_PHILIPS_PAL_I] = { /* Philips PAL_I */ + .name = "Philips PAL_I (FI1246 and compatibles)", + .params = tuner_philips_pal_i_params, + .count = ARRAY_SIZE(tuner_philips_pal_i_params), + }, + [TUNER_PHILIPS_NTSC] = { /* Philips NTSC */ + .name = "Philips NTSC (FI1236,FM1236 and compatibles)", + .params = tuner_philips_ntsc_params, + .count = ARRAY_SIZE(tuner_philips_ntsc_params), + }, + [TUNER_PHILIPS_SECAM] = { /* Philips SECAM */ + .name = "Philips (SECAM+PAL_BG) (FI1216MF, FM1216MF, FR1216MF)", + .params = tuner_philips_secam_params, + .count = ARRAY_SIZE(tuner_philips_secam_params), + }, + [TUNER_ABSENT] = { /* Tuner Absent */ + .name = "NoTuner", + }, + [TUNER_PHILIPS_PAL] = { /* Philips PAL */ + .name = "Philips PAL_BG (FI1216 and compatibles)", + .params = tuner_philips_pal_params, + .count = ARRAY_SIZE(tuner_philips_pal_params), + }, + [TUNER_TEMIC_NTSC] = { /* TEMIC NTSC */ + .name = "Temic NTSC (4032 FY5)", + .params = tuner_temic_ntsc_params, + .count = ARRAY_SIZE(tuner_temic_ntsc_params), + }, + [TUNER_TEMIC_PAL_I] = { /* TEMIC PAL_I */ + .name = "Temic PAL_I (4062 FY5)", + .params = tuner_temic_pal_i_params, + .count = ARRAY_SIZE(tuner_temic_pal_i_params), + }, + [TUNER_TEMIC_4036FY5_NTSC] = { /* TEMIC NTSC */ + .name = "Temic NTSC (4036 FY5)", + .params = tuner_temic_4036fy5_ntsc_params, + .count = ARRAY_SIZE(tuner_temic_4036fy5_ntsc_params), + }, + [TUNER_ALPS_TSBH1_NTSC] = { /* TEMIC NTSC */ + .name = "Alps HSBH1", + .params = tuner_alps_tsbh1_ntsc_params, + .count = ARRAY_SIZE(tuner_alps_tsbh1_ntsc_params), + }, + + /* 10-19 */ + [TUNER_ALPS_TSBE1_PAL] = { /* TEMIC PAL */ + .name = "Alps TSBE1", + .params = tuner_alps_tsb_1_params, + .count = ARRAY_SIZE(tuner_alps_tsb_1_params), + }, + [TUNER_ALPS_TSBB5_PAL_I] = { /* Alps PAL_I */ + .name = "Alps TSBB5", + .params = tuner_alps_tsbb5_params, + .count = ARRAY_SIZE(tuner_alps_tsbb5_params), + }, + [TUNER_ALPS_TSBE5_PAL] = { /* Alps PAL */ + .name = "Alps TSBE5", + .params = tuner_alps_tsbe5_params, + .count = ARRAY_SIZE(tuner_alps_tsbe5_params), + }, + [TUNER_ALPS_TSBC5_PAL] = { /* Alps PAL */ + .name = "Alps TSBC5", + .params = tuner_alps_tsbc5_params, + .count = ARRAY_SIZE(tuner_alps_tsbc5_params), + }, + [TUNER_TEMIC_4006FH5_PAL] = { /* TEMIC PAL */ + .name = "Temic PAL_BG (4006FH5)", + .params = tuner_temic_4006fh5_params, + .count = ARRAY_SIZE(tuner_temic_4006fh5_params), + }, + [TUNER_ALPS_TSHC6_NTSC] = { /* Alps NTSC */ + .name = "Alps TSCH6", + .params = tuner_alps_tshc6_params, + .count = ARRAY_SIZE(tuner_alps_tshc6_params), + }, + [TUNER_TEMIC_PAL_DK] = { /* TEMIC PAL */ + .name = "Temic PAL_DK (4016 FY5)", + .params = tuner_temic_pal_dk_params, + .count = ARRAY_SIZE(tuner_temic_pal_dk_params), + }, + [TUNER_PHILIPS_NTSC_M] = { /* Philips NTSC */ + .name = "Philips NTSC_M (MK2)", + .params = tuner_philips_ntsc_m_params, + .count = ARRAY_SIZE(tuner_philips_ntsc_m_params), + }, + [TUNER_TEMIC_4066FY5_PAL_I] = { /* TEMIC PAL_I */ + .name = "Temic PAL_I (4066 FY5)", + .params = tuner_temic_4066fy5_pal_i_params, + .count = ARRAY_SIZE(tuner_temic_4066fy5_pal_i_params), + }, + [TUNER_TEMIC_4006FN5_MULTI_PAL] = { /* TEMIC PAL */ + .name = "Temic PAL* auto (4006 FN5)", + .params = tuner_temic_4006fn5_multi_params, + .count = ARRAY_SIZE(tuner_temic_4006fn5_multi_params), + }, + + /* 20-29 */ + [TUNER_TEMIC_4009FR5_PAL] = { /* TEMIC PAL */ + .name = "Temic PAL_BG (4009 FR5) or PAL_I (4069 FR5)", + .params = tuner_temic_4009f_5_params, + .count = ARRAY_SIZE(tuner_temic_4009f_5_params), + }, + [TUNER_TEMIC_4039FR5_NTSC] = { /* TEMIC NTSC */ + .name = "Temic NTSC (4039 FR5)", + .params = tuner_temic_4039fr5_params, + .count = ARRAY_SIZE(tuner_temic_4039fr5_params), + }, + [TUNER_TEMIC_4046FM5] = { /* TEMIC PAL */ + .name = "Temic PAL/SECAM multi (4046 FM5)", + .params = tuner_temic_4046fm5_params, + .count = ARRAY_SIZE(tuner_temic_4046fm5_params), + }, + [TUNER_PHILIPS_PAL_DK] = { /* Philips PAL */ + .name = "Philips PAL_DK (FI1256 and compatibles)", + .params = tuner_philips_pal_dk_params, + .count = ARRAY_SIZE(tuner_philips_pal_dk_params), + }, + [TUNER_PHILIPS_FQ1216ME] = { /* Philips PAL */ + .name = "Philips PAL/SECAM multi (FQ1216ME)", + .params = tuner_philips_fq1216me_params, + .count = ARRAY_SIZE(tuner_philips_fq1216me_params), + }, + [TUNER_LG_PAL_I_FM] = { /* LGINNOTEK PAL_I */ + .name = "LG PAL_I+FM (TAPC-I001D)", + .params = tuner_lg_pal_i_fm_params, + .count = ARRAY_SIZE(tuner_lg_pal_i_fm_params), + }, + [TUNER_LG_PAL_I] = { /* LGINNOTEK PAL_I */ + .name = "LG PAL_I (TAPC-I701D)", + .params = tuner_lg_pal_i_params, + .count = ARRAY_SIZE(tuner_lg_pal_i_params), + }, + [TUNER_LG_NTSC_FM] = { /* LGINNOTEK NTSC */ + .name = "LG NTSC+FM (TPI8NSR01F)", + .params = tuner_lg_ntsc_fm_params, + .count = ARRAY_SIZE(tuner_lg_ntsc_fm_params), + }, + [TUNER_LG_PAL_FM] = { /* LGINNOTEK PAL */ + .name = "LG PAL_BG+FM (TPI8PSB01D)", + .params = tuner_lg_pal_fm_params, + .count = ARRAY_SIZE(tuner_lg_pal_fm_params), + }, + [TUNER_LG_PAL] = { /* LGINNOTEK PAL */ + .name = "LG PAL_BG (TPI8PSB11D)", + .params = tuner_lg_pal_params, + .count = ARRAY_SIZE(tuner_lg_pal_params), + }, + + /* 30-39 */ + [TUNER_TEMIC_4009FN5_MULTI_PAL_FM] = { /* TEMIC PAL */ + .name = "Temic PAL* auto + FM (4009 FN5)", + .params = tuner_temic_4009_fn5_multi_pal_fm_params, + .count = ARRAY_SIZE(tuner_temic_4009_fn5_multi_pal_fm_params), + }, + [TUNER_SHARP_2U5JF5540_NTSC] = { /* SHARP NTSC */ + .name = "SHARP NTSC_JP (2U5JF5540)", + .params = tuner_sharp_2u5jf5540_params, + .count = ARRAY_SIZE(tuner_sharp_2u5jf5540_params), + }, + [TUNER_Samsung_PAL_TCPM9091PD27] = { /* Samsung PAL */ + .name = "Samsung PAL TCPM9091PD27", + .params = tuner_samsung_pal_tcpm9091pd27_params, + .count = ARRAY_SIZE(tuner_samsung_pal_tcpm9091pd27_params), + }, + [TUNER_MT2032] = { /* Microtune PAL|NTSC */ + .name = "MT20xx universal", + /* see mt20xx.c for details */ }, + [TUNER_TEMIC_4106FH5] = { /* TEMIC PAL */ + .name = "Temic PAL_BG (4106 FH5)", + .params = tuner_temic_4106fh5_params, + .count = ARRAY_SIZE(tuner_temic_4106fh5_params), + }, + [TUNER_TEMIC_4012FY5] = { /* TEMIC PAL */ + .name = "Temic PAL_DK/SECAM_L (4012 FY5)", + .params = tuner_temic_4012fy5_params, + .count = ARRAY_SIZE(tuner_temic_4012fy5_params), + }, + [TUNER_TEMIC_4136FY5] = { /* TEMIC NTSC */ + .name = "Temic NTSC (4136 FY5)", + .params = tuner_temic_4136_fy5_params, + .count = ARRAY_SIZE(tuner_temic_4136_fy5_params), + }, + [TUNER_LG_PAL_NEW_TAPC] = { /* LGINNOTEK PAL */ + .name = "LG PAL (newer TAPC series)", + .params = tuner_lg_pal_new_tapc_params, + .count = ARRAY_SIZE(tuner_lg_pal_new_tapc_params), + }, + [TUNER_PHILIPS_FM1216ME_MK3] = { /* Philips PAL */ + .name = "Philips PAL/SECAM multi (FM1216ME MK3)", + .params = tuner_fm1216me_mk3_params, + .count = ARRAY_SIZE(tuner_fm1216me_mk3_params), + }, + [TUNER_LG_NTSC_NEW_TAPC] = { /* LGINNOTEK NTSC */ + .name = "LG NTSC (newer TAPC series)", + .params = tuner_lg_ntsc_new_tapc_params, + .count = ARRAY_SIZE(tuner_lg_ntsc_new_tapc_params), + }, + + /* 40-49 */ + [TUNER_HITACHI_NTSC] = { /* HITACHI NTSC */ + .name = "HITACHI V7-J180AT", + .params = tuner_hitachi_ntsc_params, + .count = ARRAY_SIZE(tuner_hitachi_ntsc_params), + }, + [TUNER_PHILIPS_PAL_MK] = { /* Philips PAL */ + .name = "Philips PAL_MK (FI1216 MK)", + .params = tuner_philips_pal_mk_params, + .count = ARRAY_SIZE(tuner_philips_pal_mk_params), + }, + [TUNER_PHILIPS_FCV1236D] = { /* Philips ATSC */ + .name = "Philips FCV1236D ATSC/NTSC dual in", + .params = tuner_philips_fcv1236d_params, + .count = ARRAY_SIZE(tuner_philips_fcv1236d_params), + .min = 16 * 53.00, + .max = 16 * 803.00, + .stepsize = 62500, + }, + [TUNER_PHILIPS_FM1236_MK3] = { /* Philips NTSC */ + .name = "Philips NTSC MK3 (FM1236MK3 or FM1236/F)", + .params = tuner_fm1236_mk3_params, + .count = ARRAY_SIZE(tuner_fm1236_mk3_params), + }, + [TUNER_PHILIPS_4IN1] = { /* Philips NTSC */ + .name = "Philips 4 in 1 (ATI TV Wonder Pro/Conexant)", + .params = tuner_philips_4in1_params, + .count = ARRAY_SIZE(tuner_philips_4in1_params), + }, + [TUNER_MICROTUNE_4049FM5] = { /* Microtune PAL */ + .name = "Microtune 4049 FM5", + .params = tuner_microtune_4049_fm5_params, + .count = ARRAY_SIZE(tuner_microtune_4049_fm5_params), + }, + [TUNER_PANASONIC_VP27] = { /* Panasonic NTSC */ + .name = "Panasonic VP27s/ENGE4324D", + .params = tuner_panasonic_vp27_params, + .count = ARRAY_SIZE(tuner_panasonic_vp27_params), + }, + [TUNER_LG_NTSC_TAPE] = { /* LGINNOTEK NTSC */ + .name = "LG NTSC (TAPE series)", + .params = tuner_fm1236_mk3_params, + .count = ARRAY_SIZE(tuner_fm1236_mk3_params), + }, + [TUNER_TNF_8831BGFF] = { /* Philips PAL */ + .name = "Tenna TNF 8831 BGFF)", + .params = tuner_tnf_8831bgff_params, + .count = ARRAY_SIZE(tuner_tnf_8831bgff_params), + }, + [TUNER_MICROTUNE_4042FI5] = { /* Microtune NTSC */ + .name = "Microtune 4042 FI5 ATSC/NTSC dual in", + .params = tuner_microtune_4042fi5_params, + .count = ARRAY_SIZE(tuner_microtune_4042fi5_params), + .min = 16 * 57.00, + .max = 16 * 858.00, + .stepsize = 62500, + }, + + /* 50-59 */ + [TUNER_TCL_2002N] = { /* TCL NTSC */ + .name = "TCL 2002N", + .params = tuner_tcl_2002n_params, + .count = ARRAY_SIZE(tuner_tcl_2002n_params), + }, + [TUNER_PHILIPS_FM1256_IH3] = { /* Philips PAL */ + .name = "Philips PAL/SECAM_D (FM 1256 I-H3)", + .params = tuner_philips_fm1256_ih3_params, + .count = ARRAY_SIZE(tuner_philips_fm1256_ih3_params), + }, + [TUNER_THOMSON_DTT7610] = { /* THOMSON ATSC */ + .name = "Thomson DTT 7610 (ATSC/NTSC)", + .params = tuner_thomson_dtt7610_params, + .count = ARRAY_SIZE(tuner_thomson_dtt7610_params), + .min = 16 * 44.00, + .max = 16 * 958.00, + .stepsize = 62500, + }, + [TUNER_PHILIPS_FQ1286] = { /* Philips NTSC */ + .name = "Philips FQ1286", + .params = tuner_philips_fq1286_params, + .count = ARRAY_SIZE(tuner_philips_fq1286_params), + }, + [TUNER_PHILIPS_TDA8290] = { /* Philips PAL|NTSC */ + .name = "Philips/NXP TDA 8290/8295 + 8275/8275A/18271", + /* see tda8290.c for details */ }, + [TUNER_TCL_2002MB] = { /* TCL PAL */ + .name = "TCL 2002MB", + .params = tuner_tcl_2002mb_params, + .count = ARRAY_SIZE(tuner_tcl_2002mb_params), + }, + [TUNER_PHILIPS_FQ1216AME_MK4] = { /* Philips PAL */ + .name = "Philips PAL/SECAM multi (FQ1216AME MK4)", + .params = tuner_philips_fq1216ame_mk4_params, + .count = ARRAY_SIZE(tuner_philips_fq1216ame_mk4_params), + }, + [TUNER_PHILIPS_FQ1236A_MK4] = { /* Philips NTSC */ + .name = "Philips FQ1236A MK4", + .params = tuner_philips_fq1236a_mk4_params, + .count = ARRAY_SIZE(tuner_philips_fq1236a_mk4_params), + }, + [TUNER_YMEC_TVF_8531MF] = { /* Philips NTSC */ + .name = "Ymec TVision TVF-8531MF/8831MF/8731MF", + .params = tuner_ymec_tvf_8531mf_params, + .count = ARRAY_SIZE(tuner_ymec_tvf_8531mf_params), + }, + [TUNER_YMEC_TVF_5533MF] = { /* Philips NTSC */ + .name = "Ymec TVision TVF-5533MF", + .params = tuner_ymec_tvf_5533mf_params, + .count = ARRAY_SIZE(tuner_ymec_tvf_5533mf_params), + }, + + /* 60-69 */ + [TUNER_THOMSON_DTT761X] = { /* THOMSON ATSC */ + /* DTT 7611 7611A 7612 7613 7613A 7614 7615 7615A */ + .name = "Thomson DTT 761X (ATSC/NTSC)", + .params = tuner_thomson_dtt761x_params, + .count = ARRAY_SIZE(tuner_thomson_dtt761x_params), + .min = 16 * 57.00, + .max = 16 * 863.00, + .stepsize = 62500, + .initdata = tua603x_agc103, + }, + [TUNER_TENA_9533_DI] = { /* Philips PAL */ + .name = "Tena TNF9533-D/IF/TNF9533-B/DF", + .params = tuner_tena_9533_di_params, + .count = ARRAY_SIZE(tuner_tena_9533_di_params), + }, + [TUNER_TEA5767] = { /* Philips RADIO */ + .name = "Philips TEA5767HN FM Radio", + /* see tea5767.c for details */ + }, + [TUNER_PHILIPS_FMD1216ME_MK3] = { /* Philips PAL */ + .name = "Philips FMD1216ME MK3 Hybrid Tuner", + .params = tuner_philips_fmd1216me_mk3_params, + .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_params), + .min = 16 * 50.87, + .max = 16 * 858.00, + .stepsize = 166667, + .initdata = tua603x_agc112, + .sleepdata = (u8[]){ 4, 0x9c, 0x60, 0x85, 0x54 }, + }, + [TUNER_LG_TDVS_H06XF] = { /* LGINNOTEK ATSC */ + .name = "LG TDVS-H06xF", /* H061F, H062F & H064F */ + .params = tuner_lg_tdvs_h06xf_params, + .count = ARRAY_SIZE(tuner_lg_tdvs_h06xf_params), + .min = 16 * 54.00, + .max = 16 * 863.00, + .stepsize = 62500, + .initdata = tua603x_agc103, + }, + [TUNER_YMEC_TVF66T5_B_DFF] = { /* Philips PAL */ + .name = "Ymec TVF66T5-B/DFF", + .params = tuner_ymec_tvf66t5_b_dff_params, + .count = ARRAY_SIZE(tuner_ymec_tvf66t5_b_dff_params), + }, + [TUNER_LG_TALN] = { /* LGINNOTEK NTSC / PAL / SECAM */ + .name = "LG TALN series", + .params = tuner_lg_taln_params, + .count = ARRAY_SIZE(tuner_lg_taln_params), + }, + [TUNER_PHILIPS_TD1316] = { /* Philips PAL */ + .name = "Philips TD1316 Hybrid Tuner", + .params = tuner_philips_td1316_params, + .count = ARRAY_SIZE(tuner_philips_td1316_params), + .min = 16 * 87.00, + .max = 16 * 895.00, + .stepsize = 166667, + }, + [TUNER_PHILIPS_TUV1236D] = { /* Philips ATSC */ + .name = "Philips TUV1236D ATSC/NTSC dual in", + .params = tuner_tuv1236d_params, + .count = ARRAY_SIZE(tuner_tuv1236d_params), + .min = 16 * 54.00, + .max = 16 * 864.00, + .stepsize = 62500, + }, + [TUNER_TNF_5335MF] = { /* Tenna PAL/NTSC */ + .name = "Tena TNF 5335 and similar models", + .params = tuner_tnf_5335mf_params, + .count = ARRAY_SIZE(tuner_tnf_5335mf_params), + }, + + /* 70-79 */ + [TUNER_SAMSUNG_TCPN_2121P30A] = { /* Samsung NTSC */ + .name = "Samsung TCPN 2121P30A", + .params = tuner_samsung_tcpn_2121p30a_params, + .count = ARRAY_SIZE(tuner_samsung_tcpn_2121p30a_params), + }, + [TUNER_XC2028] = { /* Xceive 2028 */ + .name = "Xceive xc2028/xc3028 tuner", + /* see tuner-xc2028.c for details */ + }, + [TUNER_THOMSON_FE6600] = { /* Thomson PAL / DVB-T */ + .name = "Thomson FE6600", + .params = tuner_thomson_fe6600_params, + .count = ARRAY_SIZE(tuner_thomson_fe6600_params), + .min = 16 * 44.25, + .max = 16 * 858.00, + .stepsize = 166667, + }, + [TUNER_SAMSUNG_TCPG_6121P30A] = { /* Samsung PAL */ + .name = "Samsung TCPG 6121P30A", + .params = tuner_samsung_tcpg_6121p30a_params, + .count = ARRAY_SIZE(tuner_samsung_tcpg_6121p30a_params), + }, + [TUNER_TDA9887] = { /* Philips TDA 9887 IF PLL Demodulator. + This chip is part of some modern tuners */ + .name = "Philips TDA988[5,6,7] IF PLL Demodulator", + /* see tda9887.c for details */ + }, + [TUNER_TEA5761] = { /* Philips RADIO */ + .name = "Philips TEA5761 FM Radio", + /* see tea5767.c for details */ + }, + [TUNER_XC5000] = { /* Xceive 5000 */ + .name = "Xceive 5000 tuner", + /* see xc5000.c for details */ + }, + [TUNER_XC4000] = { /* Xceive 4000 */ + .name = "Xceive 4000 tuner", + /* see xc4000.c for details */ + }, + [TUNER_TCL_MF02GIP_5N] = { /* TCL tuner MF02GIP-5N-E */ + .name = "TCL tuner MF02GIP-5N-E", + .params = tuner_tcl_mf02gip_5n_params, + .count = ARRAY_SIZE(tuner_tcl_mf02gip_5n_params), + }, + [TUNER_PHILIPS_FMD1216MEX_MK3] = { /* Philips PAL */ + .name = "Philips FMD1216MEX MK3 Hybrid Tuner", + .params = tuner_philips_fmd1216mex_mk3_params, + .count = ARRAY_SIZE(tuner_philips_fmd1216mex_mk3_params), + .min = 16 * 50.87, + .max = 16 * 858.00, + .stepsize = 166667, + .initdata = tua603x_agc112, + .sleepdata = (u8[]){ 4, 0x9c, 0x60, 0x85, 0x54 }, + }, + [TUNER_PHILIPS_FM1216MK5] = { /* Philips PAL */ + .name = "Philips PAL/SECAM multi (FM1216 MK5)", + .params = tuner_fm1216mk5_params, + .count = ARRAY_SIZE(tuner_fm1216mk5_params), + }, + + /* 80-89 */ + [TUNER_PHILIPS_FQ1216LME_MK3] = { /* PAL/SECAM, Loop-thru, no FM */ + .name = "Philips FQ1216LME MK3 PAL/SECAM w/active loopthrough", + .params = tuner_fq1216lme_mk3_params, + .count = ARRAY_SIZE(tuner_fq1216lme_mk3_params), + }, + + [TUNER_PARTSNIC_PTI_5NF05] = { + .name = "Partsnic (Daewoo) PTI-5NF05", + .params = tuner_partsnic_pti_5nf05_params, + .count = ARRAY_SIZE(tuner_partsnic_pti_5nf05_params), + }, + [TUNER_PHILIPS_CU1216L] = { + .name = "Philips CU1216L", + .params = tuner_philips_cu1216l_params, + .count = ARRAY_SIZE(tuner_philips_cu1216l_params), + .stepsize = 62500, + }, + [TUNER_NXP_TDA18271] = { + .name = "NXP TDA18271", + /* see tda18271-fe.c for details */ + }, + [TUNER_SONY_BTF_PXN01Z] = { + .name = "Sony BTF-Pxn01Z", + .params = tuner_sony_btf_pxn01z_params, + .count = ARRAY_SIZE(tuner_sony_btf_pxn01z_params), + }, + [TUNER_PHILIPS_FQ1236_MK5] = { /* NTSC, TDA9885, no FM radio */ + .name = "Philips FQ1236 MK5", + .params = tuner_philips_fq1236_mk5_params, + .count = ARRAY_SIZE(tuner_philips_fq1236_mk5_params), + }, + [TUNER_TENA_TNF_5337] = { /* Tena 5337 MFD */ + .name = "Tena TNF5337 MFD", + .params = tuner_tena_tnf_5337_params, + .count = ARRAY_SIZE(tuner_tena_tnf_5337_params), + }, + [TUNER_XC5000C] = { /* Xceive 5000C */ + .name = "Xceive 5000C tuner", + /* see xc5000.c for details */ + }, +}; +EXPORT_SYMBOL(tuners); + +unsigned const int tuner_count = ARRAY_SIZE(tuners); +EXPORT_SYMBOL(tuner_count); + +MODULE_DESCRIPTION("Simple tuner device type database"); +MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/tuner-xc2028-types.h b/drivers/media/tuners/tuner-xc2028-types.h new file mode 100644 index 000000000000..74dc46a71f64 --- /dev/null +++ b/drivers/media/tuners/tuner-xc2028-types.h @@ -0,0 +1,141 @@ +/* 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/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c new file mode 100644 index 000000000000..7bcb6b0ff1df --- /dev/null +++ b/drivers/media/tuners/tuner-xc2028.c @@ -0,0 +1,1509 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "tuner-i2c.h" +#include "tuner-xc2028.h" +#include "tuner-xc2028-types.h" + +#include +#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; +}; + +enum xc2028_state { + XC2028_NO_FIRMWARE = 0, + XC2028_WAITING_FIRMWARE, + XC2028_ACTIVE, + XC2028_SLEEP, + XC2028_NODEV, +}; + +struct xc2028_data { + struct list_head hybrid_tuner_instance_list; + struct tuner_i2c_props i2c_props; + __u32 frequency; + + enum xc2028_state state; + const char *fname; + + 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 int check_device_status(struct xc2028_data *priv) +{ + switch (priv->state) { + case XC2028_NO_FIRMWARE: + case XC2028_WAITING_FIRMWARE: + return -EAGAIN; + case XC2028_ACTIVE: + case XC2028_SLEEP: + return 0; + case XC2028_NODEV: + return -ENODEV; + } + 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; + priv->state = XC2028_NO_FIRMWARE; + + memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); +} + +static int load_all_firmwares(struct dvb_frontend *fe, + const struct firmware *fw) +{ + struct xc2028_data *priv = fe->tuner_priv; + const unsigned char *p, *endp; + int rc = 0; + int n, n_array; + char name[33]; + + tuner_dbg("%s called\n", __func__); + + 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", + priv->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, priv->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: + if (rc == 0) + tuner_dbg("Firmware files loaded.\n"); + else + priv->state = XC2028_NODEV; + + 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, retry_count = 0; + u16 version, hwmodel; + v4l2_std_id std0; + + tuner_dbg("%s called\n", __func__); + + rc = check_device_status(priv); + 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 and if the tuner + * is not at sleep mode + */ + if ((priv->state == XC2028_ACTIVE) && + (((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; + priv->state = XC2028_ACTIVE; + + return 0; + +fail: + priv->state = XC2028_SLEEP; + + 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, i; + + tuner_dbg("%s called\n", __func__); + + rc = check_device_status(priv); + if (rc < 0) + return rc; + + mutex_lock(&priv->lock); + + /* Sync Lock Indicator */ + for (i = 0; i < 3; i++) { + rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock); + if (rc < 0) + goto ret; + + if (frq_lock) + break; + msleep(6); + } + + /* Frequency didn't lock */ + if (frq_lock == 2) + goto ret; + + /* Get SNR of the video signal */ + rc = xc2028_get_reg(priv, XREG_SNR, &signal); + if (rc < 0) + goto ret; + + /* Signal level is 3 bits only */ + + signal = ((1 << 12) - 1) | ((signal & 0x07) << 12); + +ret: + mutex_unlock(&priv->lock); + + *strength = signal; + + tuner_dbg("signal strength is %d\n", signal); + + return rc; +} + +static int xc2028_get_afc(struct dvb_frontend *fe, s32 *afc) +{ + struct xc2028_data *priv = fe->tuner_priv; + int i, rc; + u16 frq_lock = 0; + s16 afc_reg = 0; + + rc = check_device_status(priv); + if (rc < 0) + return rc; + + mutex_lock(&priv->lock); + + /* Sync Lock Indicator */ + for (i = 0; i < 3; i++) { + rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock); + if (rc < 0) + goto ret; + + if (frq_lock) + break; + msleep(6); + } + + /* Frequency didn't lock */ + if (frq_lock == 2) + goto ret; + + /* Get AFC */ + rc = xc2028_get_reg(priv, XREG_FREQ_ERROR, &afc_reg); + if (rc < 0) + goto ret; + + *afc = afc_reg * 15625; /* Hz */ + + tuner_dbg("AFC is %d Hz\n", *afc); + +ret: + mutex_unlock(&priv->lock); + + 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 + */ + +#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= %*ph (freq=%d.%03d)\n", 4, buf, + 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; + int rc; + unsigned int type = 0; + u16 demod = 0; + + tuner_dbg("%s called\n", __func__); + + rc = check_device_status(priv); + if (rc < 0) + return rc; + + 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 + */ + 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 + */ + } + + 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; + + rc = check_device_status(priv); + if (rc < 0) + return rc; + + /* 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->state = XC2028_SLEEP; + + 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) { + free_firmware(priv); + kfree(priv->ctrl.fname); + priv->ctrl.fname = NULL; + } + + 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; + int rc; + + tuner_dbg("%s called\n", __func__); + + rc = check_device_status(priv); + if (rc < 0) + return rc; + + *frequency = priv->frequency; + + return 0; +} + +static void load_firmware_cb(const struct firmware *fw, + void *context) +{ + struct dvb_frontend *fe = context; + struct xc2028_data *priv = fe->tuner_priv; + int rc; + + tuner_dbg("request_firmware_nowait(): %s\n", fw ? "OK" : "error"); + if (!fw) { + tuner_err("Could not load firmware %s.\n", priv->fname); + priv->state = XC2028_NODEV; + return; + } + + rc = load_all_firmwares(fe, fw); + + release_firmware(fw); + + if (rc < 0) + return; + priv->state = XC2028_SLEEP; +} + +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); + + /* + * Copy the config data. + * For the firmware name, keep a local copy of the string, + * in order to avoid troubles during device release. + */ + if (priv->ctrl.fname) + kfree(priv->ctrl.fname); + memcpy(&priv->ctrl, p, sizeof(priv->ctrl)); + if (p->fname) { + priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL); + if (priv->ctrl.fname == NULL) + rc = -ENOMEM; + } + + /* + * If firmware name changed, frees firmware. As free_firmware will + * reset the status to NO_FIRMWARE, this forces a new request_firmware + */ + if (!firmware_name[0] && p->fname && + priv->fname && strcmp(p->fname, priv->fname)) + free_firmware(priv); + + if (priv->ctrl.max_len < 9) + priv->ctrl.max_len = 13; + + if (priv->state == XC2028_NO_FIRMWARE) { + if (!firmware_name[0]) + priv->fname = priv->ctrl.fname; + else + priv->fname = firmware_name; + + rc = request_firmware_nowait(THIS_MODULE, 1, + priv->fname, + priv->i2c_props.adap->dev.parent, + GFP_KERNEL, + fe, load_firmware_cb); + if (rc < 0) { + tuner_err("Failed to request firmware %s\n", + priv->fname); + priv->state = XC2028_NODEV; + } else + priv->state = XC2028_WAITING_FIRMWARE; + } + 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, + .get_afc = xc2028_get_afc, + .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 "); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE); +MODULE_FIRMWARE(XC3028L_DEFAULT_FIRMWARE); diff --git a/drivers/media/tuners/tuner-xc2028.h b/drivers/media/tuners/tuner-xc2028.h new file mode 100644 index 000000000000..9ebfb2d0ff14 --- /dev/null +++ b/drivers/media/tuners/tuner-xc2028.h @@ -0,0 +1,72 @@ +/* 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/tuners/xc4000.c b/drivers/media/tuners/xc4000.c new file mode 100644 index 000000000000..4937712278f6 --- /dev/null +++ b/drivers/media/tuners/xc4000.c @@ -0,0 +1,1757 @@ +/* + * Driver for Xceive XC4000 "QAM/8VSB single chip tuner" + * + * Copyright (c) 2007 Xceive Corporation + * Copyright (c) 2007 Steven Toth + * Copyright (c) 2009 Devin Heitmueller + * Copyright (c) 2009 Davide Ferri + * Copyright (c) 2010 Istvan Varga + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" + +#include "xc4000.h" +#include "tuner-i2c.h" +#include "tuner-xc2028-types.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debugging level (0 to 2, default: 0 (off))."); + +static int no_poweroff; +module_param(no_poweroff, int, 0644); +MODULE_PARM_DESC(no_poweroff, "Power management (1: disabled, 2: enabled, " + "0 (default): use device-specific default mode)."); + +static int audio_std; +module_param(audio_std, int, 0644); +MODULE_PARM_DESC(audio_std, "Audio standard. XC4000 audio decoder explicitly " + "needs to know what audio standard is needed for some video standards " + "with audio A2 or NICAM. The valid settings are a sum of:\n" + " 1: use NICAM/B or A2/B instead of NICAM/A or A2/A\n" + " 2: use A2 instead of NICAM or BTSC\n" + " 4: use SECAM/K3 instead of K1\n" + " 8: use PAL-D/K audio for SECAM-D/K\n" + "16: use FM radio input 1 instead of input 2\n" + "32: use mono audio (the lower three bits are ignored)"); + +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."); + +static DEFINE_MUTEX(xc4000_list_mutex); +static LIST_HEAD(hybrid_tuner_instance_list); + +#define dprintk(level, fmt, arg...) if (debug >= level) \ + printk(KERN_INFO "%s: " fmt, "xc4000", ## arg) + +/* 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 xc4000_priv { + struct tuner_i2c_props i2c_props; + struct list_head hybrid_tuner_instance_list; + struct firmware_description *firm; + int firm_size; + u32 if_khz; + u32 freq_hz; + u32 bandwidth; + u8 video_standard; + u8 rf_mode; + u8 default_pm; + u8 dvb_amplitude; + u8 set_smoothedcvbs; + u8 ignore_i2c_write_errors; + __u16 firm_version; + struct firmware_properties cur_fw; + __u16 hwmodel; + __u16 hwvers; + struct mutex lock; +}; + +#define XC4000_AUDIO_STD_B 1 +#define XC4000_AUDIO_STD_A2 2 +#define XC4000_AUDIO_STD_K3 4 +#define XC4000_AUDIO_STD_L 8 +#define XC4000_AUDIO_STD_INPUT1 16 +#define XC4000_AUDIO_STD_MONO 32 + +#define XC4000_DEFAULT_FIRMWARE "dvb-fe-xc4000-1.4.fw" + +/* Misc Defines */ +#define MAX_TV_STANDARD 24 +#define XC_MAX_I2C_WRITE_LENGTH 64 +#define XC_POWERED_DOWN 0x80000000U + +/* Signal Types */ +#define XC_RF_MODE_AIR 0 +#define XC_RF_MODE_CABLE 1 + +/* Product id */ +#define XC_PRODUCT_ID_FW_NOT_LOADED 0x2000 +#define XC_PRODUCT_ID_XC4000 0x0FA0 +#define XC_PRODUCT_ID_XC4100 0x1004 + +/* Registers (Write-only) */ +#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_DIRECTSITTING_MODE 0x05 +#define XREG_SEEK_MODE 0x06 +#define XREG_POWER_DOWN 0x08 +#define XREG_SIGNALSOURCE 0x0A +#define XREG_SMOOTHEDCVBS 0x0E +#define XREG_AMPLITUDE 0x10 + +/* Registers (Read-only) */ +#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_SIGNAL_LEVEL 0x0A +#define XREG_NOISE_LEVEL 0x0B + +/* + 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 { + const char *Name; + u16 audio_mode; + u16 video_mode; + u16 int_freq; +}; + +/* Tuner standards */ +#define XC4000_MN_NTSC_PAL_BTSC 0 +#define XC4000_MN_NTSC_PAL_A2 1 +#define XC4000_MN_NTSC_PAL_EIAJ 2 +#define XC4000_MN_NTSC_PAL_Mono 3 +#define XC4000_BG_PAL_A2 4 +#define XC4000_BG_PAL_NICAM 5 +#define XC4000_BG_PAL_MONO 6 +#define XC4000_I_PAL_NICAM 7 +#define XC4000_I_PAL_NICAM_MONO 8 +#define XC4000_DK_PAL_A2 9 +#define XC4000_DK_PAL_NICAM 10 +#define XC4000_DK_PAL_MONO 11 +#define XC4000_DK_SECAM_A2DK1 12 +#define XC4000_DK_SECAM_A2LDK3 13 +#define XC4000_DK_SECAM_A2MONO 14 +#define XC4000_DK_SECAM_NICAM 15 +#define XC4000_L_SECAM_NICAM 16 +#define XC4000_LC_SECAM_NICAM 17 +#define XC4000_DTV6 18 +#define XC4000_DTV8 19 +#define XC4000_DTV7_8 20 +#define XC4000_DTV7 21 +#define XC4000_FM_Radio_INPUT2 22 +#define XC4000_FM_Radio_INPUT1 23 + +static struct XC_TV_STANDARD xc4000_standard[MAX_TV_STANDARD] = { + {"M/N-NTSC/PAL-BTSC", 0x0000, 0x80A0, 4500}, + {"M/N-NTSC/PAL-A2", 0x0000, 0x80A0, 4600}, + {"M/N-NTSC/PAL-EIAJ", 0x0040, 0x80A0, 4500}, + {"M/N-NTSC/PAL-Mono", 0x0078, 0x80A0, 4500}, + {"B/G-PAL-A2", 0x0000, 0x8159, 5640}, + {"B/G-PAL-NICAM", 0x0004, 0x8159, 5740}, + {"B/G-PAL-MONO", 0x0078, 0x8159, 5500}, + {"I-PAL-NICAM", 0x0080, 0x8049, 6240}, + {"I-PAL-NICAM-MONO", 0x0078, 0x8049, 6000}, + {"D/K-PAL-A2", 0x0000, 0x8049, 6380}, + {"D/K-PAL-NICAM", 0x0080, 0x8049, 6200}, + {"D/K-PAL-MONO", 0x0078, 0x8049, 6500}, + {"D/K-SECAM-A2 DK1", 0x0000, 0x8049, 6340}, + {"D/K-SECAM-A2 L/DK3", 0x0000, 0x8049, 6000}, + {"D/K-SECAM-A2 MONO", 0x0078, 0x8049, 6500}, + {"D/K-SECAM-NICAM", 0x0080, 0x8049, 6200}, + {"L-SECAM-NICAM", 0x8080, 0x0009, 6200}, + {"L'-SECAM-NICAM", 0x8080, 0x4009, 6200}, + {"DTV6", 0x00C0, 0x8002, 0}, + {"DTV8", 0x00C0, 0x800B, 0}, + {"DTV7/8", 0x00C0, 0x801B, 0}, + {"DTV7", 0x00C0, 0x8007, 0}, + {"FM Radio-INPUT2", 0x0008, 0x9800, 10700}, + {"FM Radio-INPUT1", 0x0008, 0x9000, 10700} +}; + +static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val); +static int xc4000_tuner_reset(struct dvb_frontend *fe); +static void xc_debug_dump(struct xc4000_priv *priv); + +static int xc_send_i2c_data(struct xc4000_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) { + if (priv->ignore_i2c_write_errors == 0) { + printk(KERN_ERR "xc4000: I2C write failed (len=%i)\n", + len); + if (len == 4) { + printk(KERN_ERR "bytes %*ph\n", 4, buf); + } + return -EREMOTEIO; + } + } + return 0; +} + +static int xc4000_tuner_reset(struct dvb_frontend *fe) +{ + struct xc4000_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, + XC4000_TUNER_RESET, 0); + if (ret) { + printk(KERN_ERR "xc4000: reset failed\n"); + return -EREMOTEIO; + } + } else { + printk(KERN_ERR "xc4000: no tuner reset callback function, " + "fatal\n"); + return -EINVAL; + } + return 0; +} + +static int xc_write_reg(struct xc4000_priv *priv, u16 regAddr, u16 i2cData) +{ + u8 buf[4]; + 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); + + return result; +} + +static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence) +{ + struct xc4000_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 */ + /* NOTE: this is ignored, as the reset callback was */ + /* already called by check_firmware() */ + index += 2; + } else if (len & 0x8000) { + /* WAIT command */ + msleep(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 != 0) + return result; + + pos += nbytes_to_send - 2; + } + index += len; + } + } + return 0; +} + +static int xc_set_tv_standard(struct xc4000_priv *priv, + u16 video_mode, u16 audio_mode) +{ + int ret; + dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, video_mode, audio_mode); + dprintk(1, "%s() Standard = %s\n", + __func__, + xc4000_standard[priv->video_standard].Name); + + /* Don't complain when the request fails because of i2c stretching */ + priv->ignore_i2c_write_errors = 1; + + ret = xc_write_reg(priv, XREG_VIDEO_MODE, video_mode); + if (ret == 0) + ret = xc_write_reg(priv, XREG_AUDIO_MODE, audio_mode); + + priv->ignore_i2c_write_errors = 0; + + return ret; +} + +static int xc_set_signal_source(struct xc4000_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 xc4000_tuner_ops; + +static int xc_set_rf_frequency(struct xc4000_priv *priv, u32 freq_hz) +{ + u16 freq_code; + + dprintk(1, "%s(%u)\n", __func__, freq_hz); + + if ((freq_hz > xc4000_tuner_ops.info.frequency_max) || + (freq_hz < xc4000_tuner_ops.info.frequency_min)) + return -EINVAL; + + freq_code = (u16)(freq_hz / 15625); + + /* WAS: 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) */ + /* WAS: XREG_FINERFREQ */ + return xc_write_reg(priv, XREG_RF_FREQ, freq_code); +} + +static int xc_get_adc_envelope(struct xc4000_priv *priv, u16 *adc_envelope) +{ + return xc4000_readreg(priv, XREG_ADC_ENV, adc_envelope); +} + +static int xc_get_frequency_error(struct xc4000_priv *priv, u32 *freq_error_hz) +{ + int result; + u16 regData; + u32 tmp; + + result = xc4000_readreg(priv, XREG_FREQ_ERROR, ®Data); + if (result != 0) + return result; + + tmp = (u32)regData & 0xFFFFU; + tmp = (tmp < 0x8000U ? tmp : 0x10000U - tmp); + (*freq_error_hz) = tmp * 15625; + return result; +} + +static int xc_get_lock_status(struct xc4000_priv *priv, u16 *lock_status) +{ + return xc4000_readreg(priv, XREG_LOCK, lock_status); +} + +static int xc_get_version(struct xc4000_priv *priv, + u8 *hw_majorversion, u8 *hw_minorversion, + u8 *fw_majorversion, u8 *fw_minorversion) +{ + u16 data; + int result; + + result = xc4000_readreg(priv, XREG_VERSION, &data); + if (result != 0) + 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_hsync_freq(struct xc4000_priv *priv, u32 *hsync_freq_hz) +{ + u16 regData; + int result; + + result = xc4000_readreg(priv, XREG_HSYNC_FREQ, ®Data); + if (result != 0) + return result; + + (*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100; + return result; +} + +static int xc_get_frame_lines(struct xc4000_priv *priv, u16 *frame_lines) +{ + return xc4000_readreg(priv, XREG_FRAME_LINES, frame_lines); +} + +static int xc_get_quality(struct xc4000_priv *priv, u16 *quality) +{ + return xc4000_readreg(priv, XREG_QUALITY, quality); +} + +static int xc_get_signal_level(struct xc4000_priv *priv, u16 *signal) +{ + return xc4000_readreg(priv, XREG_SIGNAL_LEVEL, signal); +} + +static int xc_get_noise_level(struct xc4000_priv *priv, u16 *noise) +{ + return xc4000_readreg(priv, XREG_NOISE_LEVEL, noise); +} + +static u16 xc_wait_for_lock(struct xc4000_priv *priv) +{ + u16 lock_state = 0; + int watchdog_count = 40; + + while ((lock_state == 0) && (watchdog_count > 0)) { + xc_get_lock_status(priv, &lock_state); + if (lock_state != 1) { + msleep(5); + watchdog_count--; + } + } + return lock_state; +} + +static int xc_tune_channel(struct xc4000_priv *priv, u32 freq_hz) +{ + int found = 1; + int result; + + dprintk(1, "%s(%u)\n", __func__, freq_hz); + + /* Don't complain when the request fails because of i2c stretching */ + priv->ignore_i2c_write_errors = 1; + result = xc_set_rf_frequency(priv, freq_hz); + priv->ignore_i2c_write_errors = 0; + + if (result != 0) + return 0; + + /* wait for lock only in analog TV mode */ + if ((priv->cur_fw.type & (FM | DTV6 | DTV7 | DTV78 | DTV8)) == 0) { + if (xc_wait_for_lock(priv) != 1) + found = 0; + } + + /* Wait for stats to stabilize. + * Frame Lines needs two frame times after initial lock + * before it is valid. + */ + msleep(debug ? 100 : 10); + + if (debug) + xc_debug_dump(priv); + + return found; +} + +static int xc4000_readreg(struct xc4000_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_ERR "xc4000: I2C read failed\n"); + return -EREMOTEIO; + } + + *val = (bval[0] << 8) | bval[1]; + 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(KERN_CONT "BASE "); + if (type & INIT1) + printk(KERN_CONT "INIT1 "); + if (type & F8MHZ) + printk(KERN_CONT "F8MHZ "); + if (type & MTS) + printk(KERN_CONT "MTS "); + if (type & D2620) + printk(KERN_CONT "D2620 "); + if (type & D2633) + printk(KERN_CONT "D2633 "); + if (type & DTV6) + printk(KERN_CONT "DTV6 "); + if (type & QAM) + printk(KERN_CONT "QAM "); + if (type & DTV7) + printk(KERN_CONT "DTV7 "); + if (type & DTV78) + printk(KERN_CONT "DTV78 "); + if (type & DTV8) + printk(KERN_CONT "DTV8 "); + if (type & FM) + printk(KERN_CONT "FM "); + if (type & INPUT1) + printk(KERN_CONT "INPUT1 "); + if (type & LCD) + printk(KERN_CONT "LCD "); + if (type & NOGD) + printk(KERN_CONT "NOGD "); + if (type & MONO) + printk(KERN_CONT "MONO "); + if (type & ATSC) + printk(KERN_CONT "ATSC "); + if (type & IF) + printk(KERN_CONT "IF "); + if (type & LG60) + printk(KERN_CONT "LG60 "); + if (type & ATI638) + printk(KERN_CONT "ATI638 "); + if (type & OREN538) + printk(KERN_CONT "OREN538 "); + if (type & OREN36) + printk(KERN_CONT "OREN36 "); + if (type & TOYOTA388) + printk(KERN_CONT "TOYOTA388 "); + if (type & TOYOTA794) + printk(KERN_CONT "TOYOTA794 "); + if (type & DIBCOM52) + printk(KERN_CONT "DIBCOM52 "); + if (type & ZARLINK456) + printk(KERN_CONT "ZARLINK456 "); + if (type & CHINA) + printk(KERN_CONT "CHINA "); + if (type & F6MHZ) + printk(KERN_CONT "F6MHZ "); + if (type & INPUT2) + printk(KERN_CONT "INPUT2 "); + if (type & SCODE) + printk(KERN_CONT "SCODE "); + if (type & HAS_IF) + printk(KERN_CONT "HAS_IF_%d ", int_freq); +} + +static int seek_firmware(struct dvb_frontend *fe, unsigned int type, + v4l2_std_id *id) +{ + struct xc4000_priv *priv = fe->tuner_priv; + int i, best_i = -1; + unsigned int best_nr_diffs = 255U; + + if (!priv->firm) { + printk(KERN_ERR "Error! firmware not loaded\n"); + return -EINVAL; + } + + if (((type & ~SCODE) == 0) && (*id == 0)) + *id = V4L2_STD_PAL; + + /* Seek for generic video standard match */ + for (i = 0; i < priv->firm_size; i++) { + v4l2_std_id id_diff_mask = + (priv->firm[i].id ^ (*id)) & (*id); + unsigned int type_diff_mask = + (priv->firm[i].type ^ type) + & (BASE_TYPES | DTV_TYPES | LCD | NOGD | MONO | SCODE); + unsigned int nr_diffs; + + if (type_diff_mask + & (BASE | INIT1 | FM | DTV6 | DTV7 | DTV78 | DTV8 | SCODE)) + continue; + + nr_diffs = hweight64(id_diff_mask) + hweight32(type_diff_mask); + if (!nr_diffs) /* Supports all the requested standards */ + goto found; + + if (nr_diffs < best_nr_diffs) { + best_nr_diffs = nr_diffs; + best_i = i; + } + } + + /* FIXME: Would make sense to seek for type "hint" match ? */ + if (best_i < 0) { + i = -ENOENT; + goto ret; + } + + if (best_nr_diffs > 0U) { + printk(KERN_WARNING + "Selecting best matching firmware (%u bits differ) for " + "type=(%x), id %016llx:\n", + best_nr_diffs, type, (unsigned long long)*id); + i = best_i; + } + +found: + *id = priv->firm[i].id; + +ret: + if (debug) { + printk(KERN_DEBUG "%s firmware for type=", + (i < 0) ? "Can't find" : "Found"); + dump_firm_type(type); + printk(KERN_DEBUG "(%x), id %016llx.\n", type, (unsigned long long)*id); + } + return i; +} + +static int load_firmware(struct dvb_frontend *fe, unsigned int type, + v4l2_std_id *id) +{ + struct xc4000_priv *priv = fe->tuner_priv; + int pos, rc; + unsigned char *p; + + pos = seek_firmware(fe, type, id); + if (pos < 0) + return pos; + + p = priv->firm[pos].ptr; + + /* Don't complain when the request fails because of i2c stretching */ + priv->ignore_i2c_write_errors = 1; + + rc = xc_load_i2c_sequence(fe, p); + + priv->ignore_i2c_write_errors = 0; + + return rc; +} + +static int xc4000_fwupload(struct dvb_frontend *fe) +{ + struct xc4000_priv *priv = fe->tuner_priv; + const struct firmware *fw = NULL; + const unsigned char *p, *endp; + int rc = 0; + int n, n_array; + char name[33]; + const char *fname; + + if (firmware_name[0] != '\0') + fname = firmware_name; + else + fname = XC4000_DEFAULT_FIRMWARE; + + dprintk(1, "Reading firmware %s\n", fname); + rc = request_firmware(&fw, fname, priv->i2c_props.adap->dev.parent); + if (rc < 0) { + if (rc == -ENOENT) + printk(KERN_ERR "Error: firmware %s not found.\n", fname); + else + printk(KERN_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) { + printk(KERN_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; + + dprintk(1, "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) { + printk(KERN_ERR "Not enough memory to load firmware file.\n"); + rc = -ENOMEM; + goto done; + } + 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) { + printk(KERN_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) { + printk(KERN_ERR "Firmware type (%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) { + printk(KERN_ERR "Not enough memory to load firmware file.\n"); + rc = -ENOMEM; + goto done; + } + + if (debug) { + printk(KERN_DEBUG "Reading firmware type "); + dump_firm_type_and_int_freq(type, int_freq); + printk(KERN_DEBUG "(%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) { + printk(KERN_ERR "Firmware file is incomplete!\n"); + goto corrupt; + } + + goto done; + +header: + printk(KERN_ERR "Firmware header is incomplete!\n"); +corrupt: + rc = -EINVAL; + printk(KERN_ERR "Error: firmware file is corrupted!\n"); + +done: + release_firmware(fw); + if (rc == 0) + dprintk(1, "Firmware files loaded.\n"); + + return rc; +} + +static int load_scode(struct dvb_frontend *fe, unsigned int type, + v4l2_std_id *id, __u16 int_freq, int scode) +{ + struct xc4000_priv *priv = fe->tuner_priv; + int pos, rc; + unsigned char *p; + u8 scode_buf[13]; + u8 indirect_mode[5]; + + dprintk(1, "%s called int_freq=%d\n", __func__, int_freq); + + 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].size != 12 * 16 || scode >= 16) + return -EINVAL; + p += 12 * scode; + + if (debug) { + tuner_info("Loading SCODE for type="); + dump_firm_type_and_int_freq(priv->firm[pos].type, + priv->firm[pos].int_freq); + printk(KERN_CONT "(%x), id %016llx.\n", priv->firm[pos].type, + (unsigned long long)*id); + } + + scode_buf[0] = 0x00; + memcpy(&scode_buf[1], p, 12); + + /* Enter direct-mode */ + rc = xc_write_reg(priv, XREG_DIRECTSITTING_MODE, 0); + if (rc < 0) { + printk(KERN_ERR "failed to put device into direct mode!\n"); + return -EIO; + } + + rc = xc_send_i2c_data(priv, scode_buf, 13); + if (rc != 0) { + /* Even if the send failed, make sure we set back to indirect + mode */ + printk(KERN_ERR "Failed to set scode %d\n", rc); + } + + /* Switch back to indirect-mode */ + memset(indirect_mode, 0, sizeof(indirect_mode)); + indirect_mode[4] = 0x88; + xc_send_i2c_data(priv, indirect_mode, sizeof(indirect_mode)); + msleep(10); + + return 0; +} + +static int check_firmware(struct dvb_frontend *fe, unsigned int type, + v4l2_std_id std, __u16 int_freq) +{ + struct xc4000_priv *priv = fe->tuner_priv; + struct firmware_properties new_fw; + int rc = 0, is_retry = 0; + u16 hwmodel; + v4l2_std_id std0; + u8 hw_major, hw_minor, fw_major, fw_minor; + + dprintk(1, "%s called\n", __func__); + + if (!priv->firm) { + rc = xc4000_fwupload(fe); + if (rc < 0) + return rc; + } + +retry: + new_fw.type = type; + new_fw.id = std; + new_fw.std_req = std; + new_fw.scode_table = SCODE; + new_fw.scode_nr = 0; + new_fw.int_freq = int_freq; + + dprintk(1, "checking firmware, user requested type="); + if (debug) { + dump_firm_type(new_fw.type); + printk(KERN_CONT "(%x), id %016llx, ", new_fw.type, + (unsigned long long)new_fw.std_req); + if (!int_freq) + printk(KERN_CONT "scode_tbl "); + else + printk(KERN_CONT "int_freq %d, ", new_fw.int_freq); + printk(KERN_CONT "scode_nr %d\n", new_fw.scode_nr); + } + + /* No need to reload base firmware if it matches */ + if (priv->cur_fw.type & BASE) { + dprintk(1, "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 = xc4000_tuner_reset(fe); + if (rc < 0) + goto fail; + + /* BASE firmwares are all std0 */ + std0 = 0; + rc = load_firmware(fe, BASE, &std0); + if (rc < 0) { + printk(KERN_ERR "Error %d while loading base firmware\n", rc); + goto fail; + } + + /* Load INIT1, if needed */ + dprintk(1, "Load init1 firmware, if exists\n"); + + rc = load_firmware(fe, BASE | INIT1, &std0); + if (rc == -ENOENT) + rc = load_firmware(fe, BASE | INIT1, &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) { + dprintk(1, "Std-specific firmware already loaded.\n"); + goto skip_std_specific; + } + + /* Reloading std-specific firmware forces a SCODE update */ + priv->cur_fw.scode_table = 0; + + /* Load the standard firmware */ + rc = load_firmware(fe, new_fw.type, &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) { + dprintk(1, "SCODE firmware already loaded.\n"); + goto check_device; + } + + /* Load SCODE firmware, if exists */ + rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id, + new_fw.int_freq, new_fw.scode_nr); + if (rc != 0) + dprintk(1, "load scode failed %d\n", rc); + +check_device: + rc = xc4000_readreg(priv, XREG_PRODUCT_ID, &hwmodel); + + if (xc_get_version(priv, &hw_major, &hw_minor, &fw_major, + &fw_minor) != 0) { + printk(KERN_ERR "Unable to read tuner registers.\n"); + goto fail; + } + + dprintk(1, "Device is Xceive %d version %d.%d, " + "firmware version %d.%d\n", + hwmodel, hw_major, hw_minor, fw_major, fw_minor); + + /* Check firmware version against what we downloaded. */ + if (priv->firm_version != ((fw_major << 8) | fw_minor)) { + printk(KERN_WARNING + "Incorrect readback of firmware version %d.%d.\n", + fw_major, fw_minor); + goto fail; + } + + /* Check that the tuner hardware model remains consistent over time. */ + if (priv->hwmodel == 0 && + (hwmodel == XC_PRODUCT_ID_XC4000 || + hwmodel == XC_PRODUCT_ID_XC4100)) { + priv->hwmodel = hwmodel; + priv->hwvers = (hw_major << 8) | hw_minor; + } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel || + priv->hwvers != ((hw_major << 8) | hw_minor)) { + printk(KERN_WARNING + "Read invalid device hardware information - tuner " + "hung?\n"); + goto fail; + } + + 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 (!is_retry) { + msleep(50); + is_retry = 1; + dprintk(1, "Retrying firmware load\n"); + goto retry; + } + + if (rc == -ENOENT) + rc = -EINVAL; + return rc; +} + +static void xc_debug_dump(struct xc4000_priv *priv) +{ + u16 adc_envelope; + u32 freq_error_hz = 0; + u16 lock_status; + u32 hsync_freq_hz = 0; + u16 frame_lines; + u16 quality; + u16 signal = 0; + u16 noise = 0; + u8 hw_majorversion = 0, hw_minorversion = 0; + u8 fw_majorversion = 0, fw_minorversion = 0; + + 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); + dprintk(1, "*** HW: V%02x.%02x, FW: V%02x.%02x\n", + hw_majorversion, hw_minorversion, + fw_majorversion, fw_minorversion); + + if (priv->video_standard < XC4000_DTV6) { + 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); + + xc_get_signal_level(priv, &signal); + dprintk(1, "*** Signal level = -%ddB (%d)\n", signal >> 8, signal); + + xc_get_noise_level(priv, &noise); + dprintk(1, "*** Noise level = %ddB (%d)\n", noise >> 8, noise); +} + +static int xc4000_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 xc4000_priv *priv = fe->tuner_priv; + unsigned int type; + int ret = -EREMOTEIO; + + dprintk(1, "%s() frequency=%d (Hz)\n", __func__, c->frequency); + + mutex_lock(&priv->lock); + + switch (delsys) { + case SYS_ATSC: + dprintk(1, "%s() VSB modulation\n", __func__); + priv->rf_mode = XC_RF_MODE_AIR; + priv->freq_hz = c->frequency - 1750000; + priv->video_standard = XC4000_DTV6; + type = DTV6; + break; + case SYS_DVBC_ANNEX_B: + dprintk(1, "%s() QAM modulation\n", __func__); + priv->rf_mode = XC_RF_MODE_CABLE; + priv->freq_hz = c->frequency - 1750000; + priv->video_standard = XC4000_DTV6; + type = DTV6; + break; + case SYS_DVBT: + case SYS_DVBT2: + dprintk(1, "%s() OFDM\n", __func__); + if (bw == 0) { + if (c->frequency < 400000000) { + priv->freq_hz = c->frequency - 2250000; + } else { + priv->freq_hz = c->frequency - 2750000; + } + priv->video_standard = XC4000_DTV7_8; + type = DTV78; + } else if (bw <= 6000000) { + priv->video_standard = XC4000_DTV6; + priv->freq_hz = c->frequency - 1750000; + type = DTV6; + } else if (bw <= 7000000) { + priv->video_standard = XC4000_DTV7; + priv->freq_hz = c->frequency - 2250000; + type = DTV7; + } else { + priv->video_standard = XC4000_DTV8; + priv->freq_hz = c->frequency - 2750000; + type = DTV8; + } + priv->rf_mode = XC_RF_MODE_AIR; + break; + default: + printk(KERN_ERR "xc4000 delivery system not supported!\n"); + ret = -EINVAL; + goto fail; + } + + dprintk(1, "%s() frequency=%d (compensated)\n", + __func__, priv->freq_hz); + + /* Make sure the correct firmware type is loaded */ + if (check_firmware(fe, type, 0, priv->if_khz) != 0) + goto fail; + + priv->bandwidth = c->bandwidth_hz; + + ret = xc_set_signal_source(priv, priv->rf_mode); + if (ret != 0) { + printk(KERN_ERR "xc4000: xc_set_signal_source(%d) failed\n", + priv->rf_mode); + goto fail; + } else { + u16 video_mode, audio_mode; + video_mode = xc4000_standard[priv->video_standard].video_mode; + audio_mode = xc4000_standard[priv->video_standard].audio_mode; + if (type == DTV6 && priv->firm_version != 0x0102) + video_mode |= 0x0001; + ret = xc_set_tv_standard(priv, video_mode, audio_mode); + if (ret != 0) { + printk(KERN_ERR "xc4000: xc_set_tv_standard failed\n"); + /* DJH - do not return when it fails... */ + /* goto fail; */ + } + } + + if (xc_write_reg(priv, XREG_D_CODE, 0) == 0) + ret = 0; + if (priv->dvb_amplitude != 0) { + if (xc_write_reg(priv, XREG_AMPLITUDE, + (priv->firm_version != 0x0102 || + priv->dvb_amplitude != 134 ? + priv->dvb_amplitude : 132)) != 0) + ret = -EREMOTEIO; + } + if (priv->set_smoothedcvbs != 0) { + if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0) + ret = -EREMOTEIO; + } + if (ret != 0) { + printk(KERN_ERR "xc4000: setting registers failed\n"); + /* goto fail; */ + } + + xc_tune_channel(priv, priv->freq_hz); + + ret = 0; + +fail: + mutex_unlock(&priv->lock); + + return ret; +} + +static int xc4000_set_analog_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct xc4000_priv *priv = fe->tuner_priv; + unsigned int type = 0; + int ret = -EREMOTEIO; + + if (params->mode == V4L2_TUNER_RADIO) { + dprintk(1, "%s() frequency=%d (in units of 62.5Hz)\n", + __func__, params->frequency); + + mutex_lock(&priv->lock); + + params->std = 0; + priv->freq_hz = params->frequency * 125L / 2; + + if (audio_std & XC4000_AUDIO_STD_INPUT1) { + priv->video_standard = XC4000_FM_Radio_INPUT1; + type = FM | INPUT1; + } else { + priv->video_standard = XC4000_FM_Radio_INPUT2; + type = FM | INPUT2; + } + + goto tune_channel; + } + + dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n", + __func__, params->frequency); + + mutex_lock(&priv->lock); + + /* params->frequency is in units of 62.5khz */ + priv->freq_hz = params->frequency * 62500; + + params->std &= V4L2_STD_ALL; + /* if std is not defined, choose one */ + if (!params->std) + params->std = V4L2_STD_PAL_BG; + + if (audio_std & XC4000_AUDIO_STD_MONO) + type = MONO; + + if (params->std & V4L2_STD_MN) { + params->std = V4L2_STD_MN; + if (audio_std & XC4000_AUDIO_STD_MONO) { + priv->video_standard = XC4000_MN_NTSC_PAL_Mono; + } else if (audio_std & XC4000_AUDIO_STD_A2) { + params->std |= V4L2_STD_A2; + priv->video_standard = XC4000_MN_NTSC_PAL_A2; + } else { + params->std |= V4L2_STD_BTSC; + priv->video_standard = XC4000_MN_NTSC_PAL_BTSC; + } + goto tune_channel; + } + + if (params->std & V4L2_STD_PAL_BG) { + params->std = V4L2_STD_PAL_BG; + if (audio_std & XC4000_AUDIO_STD_MONO) { + priv->video_standard = XC4000_BG_PAL_MONO; + } else if (!(audio_std & XC4000_AUDIO_STD_A2)) { + if (!(audio_std & XC4000_AUDIO_STD_B)) { + params->std |= V4L2_STD_NICAM_A; + priv->video_standard = XC4000_BG_PAL_NICAM; + } else { + params->std |= V4L2_STD_NICAM_B; + priv->video_standard = XC4000_BG_PAL_NICAM; + } + } else { + if (!(audio_std & XC4000_AUDIO_STD_B)) { + params->std |= V4L2_STD_A2_A; + priv->video_standard = XC4000_BG_PAL_A2; + } else { + params->std |= V4L2_STD_A2_B; + priv->video_standard = XC4000_BG_PAL_A2; + } + } + goto tune_channel; + } + + if (params->std & V4L2_STD_PAL_I) { + /* default to NICAM audio standard */ + params->std = V4L2_STD_PAL_I | V4L2_STD_NICAM; + if (audio_std & XC4000_AUDIO_STD_MONO) + priv->video_standard = XC4000_I_PAL_NICAM_MONO; + else + priv->video_standard = XC4000_I_PAL_NICAM; + goto tune_channel; + } + + if (params->std & V4L2_STD_PAL_DK) { + params->std = V4L2_STD_PAL_DK; + if (audio_std & XC4000_AUDIO_STD_MONO) { + priv->video_standard = XC4000_DK_PAL_MONO; + } else if (audio_std & XC4000_AUDIO_STD_A2) { + params->std |= V4L2_STD_A2; + priv->video_standard = XC4000_DK_PAL_A2; + } else { + params->std |= V4L2_STD_NICAM; + priv->video_standard = XC4000_DK_PAL_NICAM; + } + goto tune_channel; + } + + if (params->std & V4L2_STD_SECAM_DK) { + /* default to A2 audio standard */ + params->std = V4L2_STD_SECAM_DK | V4L2_STD_A2; + if (audio_std & XC4000_AUDIO_STD_L) { + type = 0; + priv->video_standard = XC4000_DK_SECAM_NICAM; + } else if (audio_std & XC4000_AUDIO_STD_MONO) { + priv->video_standard = XC4000_DK_SECAM_A2MONO; + } else if (audio_std & XC4000_AUDIO_STD_K3) { + params->std |= V4L2_STD_SECAM_K3; + priv->video_standard = XC4000_DK_SECAM_A2LDK3; + } else { + priv->video_standard = XC4000_DK_SECAM_A2DK1; + } + goto tune_channel; + } + + if (params->std & V4L2_STD_SECAM_L) { + /* default to NICAM audio standard */ + type = 0; + params->std = V4L2_STD_SECAM_L | V4L2_STD_NICAM; + priv->video_standard = XC4000_L_SECAM_NICAM; + goto tune_channel; + } + + if (params->std & V4L2_STD_SECAM_LC) { + /* default to NICAM audio standard */ + type = 0; + params->std = V4L2_STD_SECAM_LC | V4L2_STD_NICAM; + priv->video_standard = XC4000_LC_SECAM_NICAM; + goto tune_channel; + } + +tune_channel: + /* FIXME: it could be air. */ + priv->rf_mode = XC_RF_MODE_CABLE; + + if (check_firmware(fe, type, params->std, + xc4000_standard[priv->video_standard].int_freq) != 0) + goto fail; + + ret = xc_set_signal_source(priv, priv->rf_mode); + if (ret != 0) { + printk(KERN_ERR + "xc4000: xc_set_signal_source(%d) failed\n", + priv->rf_mode); + goto fail; + } else { + u16 video_mode, audio_mode; + video_mode = xc4000_standard[priv->video_standard].video_mode; + audio_mode = xc4000_standard[priv->video_standard].audio_mode; + if (priv->video_standard < XC4000_BG_PAL_A2) { + if (type & NOGD) + video_mode &= 0xFF7F; + } else if (priv->video_standard < XC4000_I_PAL_NICAM) { + if (priv->firm_version == 0x0102) + video_mode &= 0xFEFF; + if (audio_std & XC4000_AUDIO_STD_B) + video_mode |= 0x0080; + } + ret = xc_set_tv_standard(priv, video_mode, audio_mode); + if (ret != 0) { + printk(KERN_ERR "xc4000: xc_set_tv_standard failed\n"); + goto fail; + } + } + + if (xc_write_reg(priv, XREG_D_CODE, 0) == 0) + ret = 0; + if (xc_write_reg(priv, XREG_AMPLITUDE, 1) != 0) + ret = -EREMOTEIO; + if (priv->set_smoothedcvbs != 0) { + if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0) + ret = -EREMOTEIO; + } + if (ret != 0) { + printk(KERN_ERR "xc4000: setting registers failed\n"); + goto fail; + } + + xc_tune_channel(priv, priv->freq_hz); + + ret = 0; + +fail: + mutex_unlock(&priv->lock); + + return ret; +} + +static int xc4000_get_signal(struct dvb_frontend *fe, u16 *strength) +{ + struct xc4000_priv *priv = fe->tuner_priv; + u16 value = 0; + int rc; + + mutex_lock(&priv->lock); + rc = xc4000_readreg(priv, XREG_SIGNAL_LEVEL, &value); + mutex_unlock(&priv->lock); + + if (rc < 0) + goto ret; + + /* Informations from real testing of DVB-T and radio part, + coeficient for one dB is 0xff. + */ + tuner_dbg("Signal strength: -%ddB (%05d)\n", value >> 8, value); + + /* all known digital modes */ + if ((priv->video_standard == XC4000_DTV6) || + (priv->video_standard == XC4000_DTV7) || + (priv->video_standard == XC4000_DTV7_8) || + (priv->video_standard == XC4000_DTV8)) + goto digital; + + /* Analog mode has NOISE LEVEL important, signal + depends only on gain of antenna and amplifiers, + but it doesn't tell anything about real quality + of reception. + */ + mutex_lock(&priv->lock); + rc = xc4000_readreg(priv, XREG_NOISE_LEVEL, &value); + mutex_unlock(&priv->lock); + + tuner_dbg("Noise level: %ddB (%05d)\n", value >> 8, value); + + /* highest noise level: 32dB */ + if (value >= 0x2000) { + value = 0; + } else { + value = ~value << 3; + } + + goto ret; + + /* Digital mode has SIGNAL LEVEL important and real + noise level is stored in demodulator registers. + */ +digital: + /* best signal: -50dB */ + if (value <= 0x3200) { + value = 0xffff; + /* minimum: -114dB - should be 0x7200 but real zero is 0x713A */ + } else if (value >= 0x713A) { + value = 0; + } else { + value = ~(value - 0x3200) << 2; + } + +ret: + *strength = value; + + return rc; +} + +static int xc4000_get_frequency(struct dvb_frontend *fe, u32 *freq) +{ + struct xc4000_priv *priv = fe->tuner_priv; + + *freq = priv->freq_hz; + + if (debug) { + mutex_lock(&priv->lock); + if ((priv->cur_fw.type + & (BASE | FM | DTV6 | DTV7 | DTV78 | DTV8)) == BASE) { + u16 snr = 0; + if (xc4000_readreg(priv, XREG_SNR, &snr) == 0) { + mutex_unlock(&priv->lock); + dprintk(1, "%s() freq = %u, SNR = %d\n", + __func__, *freq, snr); + return 0; + } + } + mutex_unlock(&priv->lock); + } + + dprintk(1, "%s()\n", __func__); + + return 0; +} + +static int xc4000_get_bandwidth(struct dvb_frontend *fe, u32 *bw) +{ + struct xc4000_priv *priv = fe->tuner_priv; + dprintk(1, "%s()\n", __func__); + + *bw = priv->bandwidth; + return 0; +} + +static int xc4000_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct xc4000_priv *priv = fe->tuner_priv; + u16 lock_status = 0; + + mutex_lock(&priv->lock); + + if (priv->cur_fw.type & BASE) + xc_get_lock_status(priv, &lock_status); + + *status = (lock_status == 1 ? + TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO : 0); + if (priv->cur_fw.type & (DTV6 | DTV7 | DTV78 | DTV8)) + *status &= (~TUNER_STATUS_STEREO); + + mutex_unlock(&priv->lock); + + dprintk(2, "%s() lock_status = %d\n", __func__, lock_status); + + return 0; +} + +static int xc4000_sleep(struct dvb_frontend *fe) +{ + struct xc4000_priv *priv = fe->tuner_priv; + int ret = 0; + + dprintk(1, "%s()\n", __func__); + + mutex_lock(&priv->lock); + + /* Avoid firmware reload on slow devices */ + if ((no_poweroff == 2 || + (no_poweroff == 0 && priv->default_pm != 0)) && + (priv->cur_fw.type & BASE) != 0) { + /* force reset and firmware reload */ + priv->cur_fw.type = XC_POWERED_DOWN; + + if (xc_write_reg(priv, XREG_POWER_DOWN, 0) != 0) { + printk(KERN_ERR + "xc4000: %s() unable to shutdown tuner\n", + __func__); + ret = -EREMOTEIO; + } + msleep(20); + } + + mutex_unlock(&priv->lock); + + return ret; +} + +static int xc4000_init(struct dvb_frontend *fe) +{ + dprintk(1, "%s()\n", __func__); + + return 0; +} + +static int xc4000_release(struct dvb_frontend *fe) +{ + struct xc4000_priv *priv = fe->tuner_priv; + + dprintk(1, "%s()\n", __func__); + + mutex_lock(&xc4000_list_mutex); + + if (priv) + hybrid_tuner_release_state(priv); + + mutex_unlock(&xc4000_list_mutex); + + fe->tuner_priv = NULL; + + return 0; +} + +static const struct dvb_tuner_ops xc4000_tuner_ops = { + .info = { + .name = "Xceive XC4000", + .frequency_min = 1000000, + .frequency_max = 1023000000, + .frequency_step = 50000, + }, + + .release = xc4000_release, + .init = xc4000_init, + .sleep = xc4000_sleep, + + .set_params = xc4000_set_params, + .set_analog_params = xc4000_set_analog_params, + .get_frequency = xc4000_get_frequency, + .get_rf_strength = xc4000_get_signal, + .get_bandwidth = xc4000_get_bandwidth, + .get_status = xc4000_get_status +}; + +struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct xc4000_config *cfg) +{ + struct xc4000_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(&xc4000_list_mutex); + + instance = hybrid_tuner_request_state(struct xc4000_priv, priv, + hybrid_tuner_instance_list, + i2c, cfg->i2c_address, "xc4000"); + switch (instance) { + case 0: + goto fail; + break; + case 1: + /* new tuner instance */ + priv->bandwidth = 6000000; + /* set default configuration */ + priv->if_khz = 4560; + priv->default_pm = 0; + priv->dvb_amplitude = 134; + priv->set_smoothedcvbs = 1; + mutex_init(&priv->lock); + fe->tuner_priv = priv; + break; + default: + /* existing tuner instance */ + fe->tuner_priv = priv; + break; + } + + if (cfg->if_khz != 0) { + /* copy configuration if provided by the caller */ + priv->if_khz = cfg->if_khz; + priv->default_pm = cfg->default_pm; + priv->dvb_amplitude = cfg->dvb_amplitude; + priv->set_smoothedcvbs = cfg->set_smoothedcvbs; + } + + /* Check if firmware has been loaded. It is possible that another + instance of the driver has loaded the firmware. + */ + + if (instance == 1) { + if (xc4000_readreg(priv, XREG_PRODUCT_ID, &id) != 0) + goto fail; + } else { + id = ((priv->cur_fw.type & BASE) != 0 ? + priv->hwmodel : XC_PRODUCT_ID_FW_NOT_LOADED); + } + + switch (id) { + case XC_PRODUCT_ID_XC4000: + case XC_PRODUCT_ID_XC4100: + printk(KERN_INFO + "xc4000: Successfully identified at address 0x%02x\n", + cfg->i2c_address); + printk(KERN_INFO + "xc4000: Firmware has been loaded previously\n"); + break; + case XC_PRODUCT_ID_FW_NOT_LOADED: + printk(KERN_INFO + "xc4000: Successfully identified at address 0x%02x\n", + cfg->i2c_address); + printk(KERN_INFO + "xc4000: Firmware has not been loaded previously\n"); + break; + default: + printk(KERN_ERR + "xc4000: Device not found at addr 0x%02x (0x%x)\n", + cfg->i2c_address, id); + goto fail; + } + + mutex_unlock(&xc4000_list_mutex); + + memcpy(&fe->ops.tuner_ops, &xc4000_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + if (instance == 1) { + int ret; + mutex_lock(&priv->lock); + ret = xc4000_fwupload(fe); + mutex_unlock(&priv->lock); + if (ret != 0) + goto fail2; + } + + return fe; +fail: + mutex_unlock(&xc4000_list_mutex); +fail2: + xc4000_release(fe); + return NULL; +} +EXPORT_SYMBOL(xc4000_attach); + +MODULE_AUTHOR("Steven Toth, Davide Ferri"); +MODULE_DESCRIPTION("Xceive xc4000 silicon tuner driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/xc4000.h b/drivers/media/tuners/xc4000.h new file mode 100644 index 000000000000..e6a44d151cbd --- /dev/null +++ b/drivers/media/tuners/xc4000.h @@ -0,0 +1,67 @@ +/* + * Driver for Xceive XC4000 "QAM/8VSB single chip tuner" + * + * Copyright (c) 2007 Steven Toth + * + * 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 + +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/tuners/xc5000.c b/drivers/media/tuners/xc5000.c new file mode 100644 index 000000000000..dc93cf338f36 --- /dev/null +++ b/drivers/media/tuners/xc5000.c @@ -0,0 +1,1366 @@ +/* + * Driver for Xceive XC5000 "QAM/8VSB single chip tuner" + * + * Copyright (c) 2007 Xceive Corporation + * Copyright (c) 2007 Steven Toth + * Copyright (c) 2009 Devin Heitmueller + * + * 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 +#include +#include +#include +#include +#include + +#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; + u16 pll_register_no; + u8 init_status_supported; + u8 fw_checksum_supported; +}; + +/* 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 +#define XREG_TOTALGAIN 0x0F +#define XREG_FW_CHECKSUM 0x12 +#define XREG_INIT_STATUS 0x13 + +/* + 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; + u16 pll_reg; + u8 init_status_supported; + u8 fw_checksum_supported; +}; + +#define XC5000A_FIRMWARE "dvb-fe-xc5000-1.6.114.fw" +static const struct xc5000_fw_cfg xc5000a_1_6_114 = { + .name = XC5000A_FIRMWARE, + .size = 12401, + .pll_reg = 0x806c, +}; + +#define XC5000C_FIRMWARE "dvb-fe-xc5000c-4.1.30.7.fw" +static const struct xc5000_fw_cfg xc5000c_41_024_5 = { + .name = XC5000C_FIRMWARE, + .size = 16497, + .pll_reg = 0x13, + .init_status_supported = 1, + .fw_checksum_supported = 1, +}; + +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, int force); +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, ®Data); + 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, ®Data); + 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 int xc_get_analogsnr(struct xc5000_priv *priv, u16 *snr) +{ + return xc5000_readreg(priv, XREG_SNR, snr); +} + +static int xc_get_totalgain(struct xc5000_priv *priv, u16 *totalgain) +{ + return xc5000_readreg(priv, XREG_TOTALGAIN, totalgain); +} + +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); + priv->pll_register_no = desired_fw->pll_reg; + priv->init_status_supported = desired_fw->init_status_supported; + priv->fw_checksum_supported = desired_fw->fw_checksum_supported; + + /* 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; + u16 snr; + u16 totalgain; + u8 hw_majorversion = 0, hw_minorversion = 0; + u8 fw_majorversion = 0, fw_minorversion = 0; + u16 fw_buildversion = 0; + u16 regval; + + /* 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%d.%d, FW: V %d.%d.%d\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 & 0x07); + + xc_get_analogsnr(priv, &snr); + dprintk(1, "*** Unweighted analog SNR = %d dB\n", snr & 0x3f); + + xc_get_totalgain(priv, &totalgain); + dprintk(1, "*** Total gain = %d.%d dB\n", totalgain / 256, + (totalgain % 256) * 100 / 256); + + if (priv->pll_register_no) { + xc5000_readreg(priv, priv->pll_register_no, ®val); + dprintk(1, "*** PLL lock status = 0x%04x\n", regval); + } +} + +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 (xc_load_fw_and_init_tuner(fe, 0) != 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_ISDBT: + /* All ISDB-T are currently for 6 MHz bw */ + if (!bw) + bw = 6000000; + /* fall to OFDM handling */ + case SYS_DMBTH: + 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; + u16 pll_lock_status; + 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); + + if (priv->pll_register_no != 0) { + msleep(20); + xc5000_readreg(priv, priv->pll_register_no, &pll_lock_status); + if (pll_lock_status > 63) { + /* PLL is unlocked, force reload of the firmware */ + dprintk(1, "xc5000: PLL not locked (0x%x). Reloading...\n", + pll_lock_status); + if (xc_load_fw_and_init_tuner(fe, 1) != XC_RESULT_SUCCESS) { + printk(KERN_ERR "xc5000: Unable to reload fw\n"); + return -EREMOTEIO; + } + goto tune_channel; + } + } + + 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 (xc_load_fw_and_init_tuner(fe, 0) != 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, int force) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret = XC_RESULT_SUCCESS; + u16 pll_lock_status; + u16 fw_ck; + + if (force || xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { + +fw_retry: + + ret = xc5000_fwupload(fe); + if (ret != XC_RESULT_SUCCESS) + return ret; + + msleep(20); + + if (priv->fw_checksum_supported) { + if (xc5000_readreg(priv, XREG_FW_CHECKSUM, &fw_ck) + != XC_RESULT_SUCCESS) { + dprintk(1, "%s() FW checksum reading failed.\n", + __func__); + goto fw_retry; + } + + if (fw_ck == 0) { + dprintk(1, "%s() FW checksum failed = 0x%04x\n", + __func__, fw_ck); + goto fw_retry; + } + } + + /* Start the tuner self-calibration process */ + ret |= xc_initialize(priv); + + if (ret != XC_RESULT_SUCCESS) + goto fw_retry; + + /* 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); + + if (priv->init_status_supported) { + if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck) != XC_RESULT_SUCCESS) { + dprintk(1, "%s() FW failed reading init status.\n", + __func__); + goto fw_retry; + } + + if (fw_ck == 0) { + dprintk(1, "%s() FW init status failed = 0x%04x\n", __func__, fw_ck); + goto fw_retry; + } + } + + if (priv->pll_register_no) { + xc5000_readreg(priv, priv->pll_register_no, + &pll_lock_status); + if (pll_lock_status > 63) { + /* PLL is unlocked, force reload of the firmware */ + printk(KERN_ERR "xc5000: PLL not running after fwload.\n"); + goto fw_retry; + } + } + + /* 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, 0) != 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"); +MODULE_FIRMWARE(XC5000A_FIRMWARE); +MODULE_FIRMWARE(XC5000C_FIRMWARE); diff --git a/drivers/media/tuners/xc5000.h b/drivers/media/tuners/xc5000.h new file mode 100644 index 000000000000..b1a547494625 --- /dev/null +++ b/drivers/media/tuners/xc5000.h @@ -0,0 +1,74 @@ +/* + * Driver for Xceive XC5000 "QAM/8VSB single chip tuner" + * + * Copyright (c) 2007 Steven Toth + * + * 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 + +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/usb/b2c2/Makefile b/drivers/media/usb/b2c2/Makefile index 9eaf208bfa43..2f7ee5cc5b29 100644 --- a/drivers/media/usb/b2c2/Makefile +++ b/drivers/media/usb/b2c2/Makefile @@ -3,5 +3,5 @@ obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o ccflags-y += -Idrivers/media/dvb-core/ ccflags-y += -Idrivers/media/dvb-frontends/ -ccflags-y += -Idrivers/media/common/tuners/ +ccflags-y += -Idrivers/media/tuners/ ccflags-y += -Idrivers/media/common/b2c2/ diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile index 24248b3acd4f..62568430418b 100644 --- a/drivers/media/usb/dvb-usb-v2/Makefile +++ b/drivers/media/usb/dvb-usb-v2/Makefile @@ -44,5 +44,5 @@ obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends -ccflags-y += -I$(srctree)/drivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/tuners diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile index 94bdf4d51712..d3ab1e74647d 100644 --- a/drivers/media/usb/dvb-usb/Makefile +++ b/drivers/media/usb/dvb-usb/Makefile @@ -78,5 +78,5 @@ obj-$(CONFIG_DVB_USB_TECHNISAT_USB2) += dvb-usb-technisat-usb2.o ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends/ # due to tuner-xc3028 -ccflags-y += -I$(srctree)/drivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/tuners ccflags-y += -I$(srctree)/drivers/media/pci/ttpci diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index 74b65ea00f63..c0e90bc23692 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -31,5 +31,5 @@ obj-$(CONFIG_VIDEOBUF2_DMA_SG) += videobuf2-dma-sg.o ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends -ccflags-y += -I$(srctree)/drivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/tuners diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 98160ce77d8d..9dff3e2ec613 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -192,4 +192,4 @@ obj-$(CONFIG_ARCH_OMAP) += omap/ ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends -ccflags-y += -I$(srctree)/drivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/tuners diff --git a/drivers/media/video/au0828/Makefile b/drivers/media/video/au0828/Makefile index 61b69e68d950..98cc20cc0ffb 100644 --- a/drivers/media/video/au0828/Makefile +++ b/drivers/media/video/au0828/Makefile @@ -2,7 +2,7 @@ au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-vid obj-$(CONFIG_VIDEO_AU0828) += au0828.o -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/video/bt8xx/Makefile b/drivers/media/video/bt8xx/Makefile index 4cba4eff36d8..f6351a25c267 100644 --- a/drivers/media/video/bt8xx/Makefile +++ b/drivers/media/video/bt8xx/Makefile @@ -9,5 +9,5 @@ bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ obj-$(CONFIG_VIDEO_BT848) += bttv.o ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core diff --git a/drivers/media/video/cx18/Makefile b/drivers/media/video/cx18/Makefile index db5ab12d84a3..d3ff1545c2c5 100644 --- a/drivers/media/video/cx18/Makefile +++ b/drivers/media/video/cx18/Makefile @@ -10,4 +10,4 @@ obj-$(CONFIG_VIDEO_CX18_ALSA) += cx18-alsa.o ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/video/cx231xx/Makefile b/drivers/media/video/cx231xx/Makefile index 9f2d4a9df3f2..1d40fce77601 100644 --- a/drivers/media/video/cx231xx/Makefile +++ b/drivers/media/video/cx231xx/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-alsa.o obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends ccflags-y += -Idrivers/media/usb/dvb-usb diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile index 8f82e01a6675..f92cc4c14f0c 100644 --- a/drivers/media/video/cx23885/Makefile +++ b/drivers/media/video/cx23885/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885.o obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/video/cx25821/Makefile b/drivers/media/video/cx25821/Makefile index af23e0c77e16..1434e8094803 100644 --- a/drivers/media/video/cx25821/Makefile +++ b/drivers/media/video/cx25821/Makefile @@ -8,6 +8,6 @@ obj-$(CONFIG_VIDEO_CX25821) += cx25821.o obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o ccflags-y := -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile index 5c4d3064d494..884b4cdd8ff0 100644 --- a/drivers/media/video/cx88/Makefile +++ b/drivers/media/video/cx88/Makefile @@ -11,6 +11,6 @@ obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile index f4118d23a70b..65c7c29e4161 100644 --- a/drivers/media/video/em28xx/Makefile +++ b/drivers/media/video/em28xx/Makefile @@ -10,6 +10,6 @@ obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o obj-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-rc.o ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile index 0015bd46f667..80b4ec18475d 100644 --- a/drivers/media/video/ivtv/Makefile +++ b/drivers/media/video/ivtv/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_VIDEO_IVTV) += ivtv.o obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o ccflags-y += -I$(srctree)/drivers/media/video -ccflags-y += -I$(srctree)/drivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/tuners ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile index 1458797bafd2..bc716db797e3 100644 --- a/drivers/media/video/pvrusb2/Makefile +++ b/drivers/media/video/pvrusb2/Makefile @@ -17,6 +17,6 @@ pvrusb2-objs := pvrusb2-i2c-core.o \ obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile index 7af78a868b19..aba50088dcdc 100644 --- a/drivers/media/video/saa7134/Makefile +++ b/drivers/media/video/saa7134/Makefile @@ -11,6 +11,6 @@ obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o ccflags-y += -I$(srctree)/drivers/media/video -ccflags-y += -I$(srctree)/drivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/tuners ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends diff --git a/drivers/media/video/saa7164/Makefile b/drivers/media/video/saa7164/Makefile index d8ed33d6b853..847110c2e14c 100644 --- a/drivers/media/video/saa7164/Makefile +++ b/drivers/media/video/saa7164/Makefile @@ -5,7 +5,7 @@ saa7164-objs := saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \ obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o ccflags-y += -I$(srctree)/drivers/media/video -ccflags-y += -I$(srctree)/drivers/media/common/tuners +ccflags-y += -I$(srctree)/drivers/media/tuners ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends diff --git a/drivers/media/video/tlg2300/Makefile b/drivers/media/video/tlg2300/Makefile index 268d8251f0e9..4d660879999f 100644 --- a/drivers/media/video/tlg2300/Makefile +++ b/drivers/media/video/tlg2300/Makefile @@ -3,7 +3,7 @@ poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/video/tm6000/Makefile b/drivers/media/video/tm6000/Makefile index 56cbcba778f6..1feb8c9c816c 100644 --- a/drivers/media/video/tm6000/Makefile +++ b/drivers/media/video/tm6000/Makefile @@ -10,6 +10,6 @@ obj-$(CONFIG_VIDEO_TM6000_ALSA) += tm6000-alsa.o obj-$(CONFIG_VIDEO_TM6000_DVB) += tm6000-dvb.o ccflags-y := -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/video/usbvision/Makefile b/drivers/media/video/usbvision/Makefile index aea1e3b5f06b..d55c6bd97a35 100644 --- a/drivers/media/video/usbvision/Makefile +++ b/drivers/media/video/usbvision/Makefile @@ -3,4 +3,4 @@ usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision- obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o ccflags-y += -Idrivers/media/video -ccflags-y += -Idrivers/media/common/tuners +ccflags-y += -Idrivers/media/tuners diff --git a/drivers/staging/media/cxd2099/Makefile b/drivers/staging/media/cxd2099/Makefile index eb6bc595f1ac..b2905e65057c 100644 --- a/drivers/staging/media/cxd2099/Makefile +++ b/drivers/staging/media/cxd2099/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_DVB_CXD2099) += cxd2099.o ccflags-y += -Idrivers/media/dvb-core/ ccflags-y += -Idrivers/media/dvb-frontends/ -ccflags-y += -Idrivers/media/common/tuners/ +ccflags-y += -Idrivers/media/tuners/ -- cgit v1.2.3 From ed0c8b5465d6cec5458d9a3041a5167d83f40fdb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 14 Jun 2012 16:36:00 -0300 Subject: [media] saa7146: Move it to its own directory In order to better organize the directory tree, move the saa7146 common driver to its own directory. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/Kconfig | 11 +- drivers/media/common/Makefile | 7 +- drivers/media/common/saa7146/Kconfig | 9 + drivers/media/common/saa7146/Makefile | 5 + drivers/media/common/saa7146/saa7146_core.c | 592 ++++++++++++ drivers/media/common/saa7146/saa7146_fops.c | 664 +++++++++++++ drivers/media/common/saa7146/saa7146_hlp.c | 1048 ++++++++++++++++++++ drivers/media/common/saa7146/saa7146_i2c.c | 423 ++++++++ drivers/media/common/saa7146/saa7146_vbi.c | 498 ++++++++++ drivers/media/common/saa7146/saa7146_video.c | 1332 ++++++++++++++++++++++++++ drivers/media/common/saa7146_core.c | 592 ------------ drivers/media/common/saa7146_fops.c | 664 ------------- drivers/media/common/saa7146_hlp.c | 1048 -------------------- drivers/media/common/saa7146_i2c.c | 423 -------- drivers/media/common/saa7146_vbi.c | 498 ---------- drivers/media/common/saa7146_video.c | 1332 -------------------------- 16 files changed, 4573 insertions(+), 4573 deletions(-) create mode 100644 drivers/media/common/saa7146/Kconfig create mode 100644 drivers/media/common/saa7146/Makefile create mode 100644 drivers/media/common/saa7146/saa7146_core.c create mode 100644 drivers/media/common/saa7146/saa7146_fops.c create mode 100644 drivers/media/common/saa7146/saa7146_hlp.c create mode 100644 drivers/media/common/saa7146/saa7146_i2c.c create mode 100644 drivers/media/common/saa7146/saa7146_vbi.c create mode 100644 drivers/media/common/saa7146/saa7146_video.c delete mode 100644 drivers/media/common/saa7146_core.c delete mode 100644 drivers/media/common/saa7146_fops.c delete mode 100644 drivers/media/common/saa7146_hlp.c delete mode 100644 drivers/media/common/saa7146_i2c.c delete mode 100644 drivers/media/common/saa7146_vbi.c delete mode 100644 drivers/media/common/saa7146_video.c (limited to 'drivers/media/common/Makefile') diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index 4672f7d82f67..157f191ef646 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -1,11 +1,2 @@ -config VIDEO_SAA7146 - tristate - depends on I2C && PCI - -config VIDEO_SAA7146_VV - tristate - depends on VIDEO_V4L2 - select VIDEOBUF_DMA_SG - select VIDEO_SAA7146 - source "drivers/media/common/b2c2/Kconfig" +source "drivers/media/common/saa7146/Kconfig" diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index a471242c46a7..f3afd83843e0 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,6 +1 @@ -saa7146-objs := saa7146_i2c.o saa7146_core.o -saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o - -obj-y += b2c2/ -obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o -obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o +obj-y += b2c2/ saa7146/ diff --git a/drivers/media/common/saa7146/Kconfig b/drivers/media/common/saa7146/Kconfig new file mode 100644 index 000000000000..769c6f8142d2 --- /dev/null +++ b/drivers/media/common/saa7146/Kconfig @@ -0,0 +1,9 @@ +config VIDEO_SAA7146 + tristate + depends on I2C && PCI + +config VIDEO_SAA7146_VV + tristate + depends on VIDEO_V4L2 + select VIDEOBUF_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..3219b00a8771 --- /dev/null +++ b/drivers/media/common/saa7146/Makefile @@ -0,0 +1,5 @@ +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/saa7146_core.c b/drivers/media/common/saa7146/saa7146_core.c new file mode 100644 index 000000000000..bb6ee5191eb1 --- /dev/null +++ b/drivers/media/common/saa7146/saa7146_core.c @@ -0,0 +1,592 @@ +/* + saa7146.o - driver for generic saa7146-based hardware + + Copyright (C) 1998-2003 Michael Hunold + + 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 +#include + +static int saa7146_num; + +unsigned int saa7146_debug; + +module_param(saa7146_debug, uint, 0644); +MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)"); + +#if 0 +static void dump_registers(struct saa7146_dev* dev) +{ + int i = 0; + + pr_info(" @ %li jiffies:\n", jiffies); + for (i = 0; i <= 0x148; i += 4) + pr_info("0x%03x: 0x%08x\n", i, saa7146_read(dev, i)); +} +#endif + +/**************************************************************************** + * gpio and debi helper functions + ****************************************************************************/ + +void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data) +{ + u32 value = 0; + + BUG_ON(port > 3); + + value = saa7146_read(dev, GPIO_CTRL); + value &= ~(0xff << (8*port)); + value |= (data << (8*port)); + saa7146_write(dev, GPIO_CTRL, value); +} + +/* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */ +static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev, + unsigned long us1, unsigned long us2) +{ + unsigned long timeout; + int err; + + /* wait for registers to be programmed */ + timeout = jiffies + usecs_to_jiffies(us1); + while (1) { + err = time_after(jiffies, timeout); + if (saa7146_read(dev, MC2) & 2) + break; + if (err) { + pr_err("%s: %s timed out while waiting for registers getting programmed\n", + dev->name, __func__); + return -ETIMEDOUT; + } + msleep(1); + } + + /* wait for transfer to complete */ + timeout = jiffies + usecs_to_jiffies(us2); + while (1) { + err = time_after(jiffies, timeout); + if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S)) + break; + saa7146_read(dev, MC2); + if (err) { + DEB_S("%s: %s timed out while waiting for transfer completion\n", + dev->name, __func__); + return -ETIMEDOUT; + } + msleep(1); + } + + return 0; +} + +static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev, + unsigned long us1, unsigned long us2) +{ + unsigned long loops; + + /* wait for registers to be programmed */ + loops = us1; + while (1) { + if (saa7146_read(dev, MC2) & 2) + break; + if (!loops--) { + pr_err("%s: %s timed out while waiting for registers getting programmed\n", + dev->name, __func__); + return -ETIMEDOUT; + } + udelay(1); + } + + /* wait for transfer to complete */ + loops = us2 / 5; + while (1) { + if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S)) + break; + saa7146_read(dev, MC2); + if (!loops--) { + DEB_S("%s: %s timed out while waiting for transfer completion\n", + dev->name, __func__); + return -ETIMEDOUT; + } + udelay(5); + } + + return 0; +} + +int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop) +{ + if (nobusyloop) + return saa7146_wait_for_debi_done_sleep(dev, 50000, 250000); + else + return saa7146_wait_for_debi_done_busyloop(dev, 50000, 250000); +} + +/**************************************************************************** + * general helper functions + ****************************************************************************/ + +/* 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 */ +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); + if (NULL == sglist) + return NULL; + sg_init_table(sglist, nr_pages); + for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) { + pg = vmalloc_to_page(virt); + if (NULL == pg) + goto err; + BUG_ON(PageHighMem(pg)); + sg_set_page(&sglist[i], pg, PAGE_SIZE, 0); + } + return sglist; + + err: + kfree(sglist); + return NULL; +} + +/********************************************************************************/ +/* common page table functions */ + +void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt) +{ + int pages = (length+PAGE_SIZE-1)/PAGE_SIZE; + void *mem = vmalloc_32(length); + int slen = 0; + + if (NULL == mem) + goto err_null; + + if (!(pt->slist = vmalloc_to_sg(mem, pages))) + goto err_free_mem; + + if (saa7146_pgtable_alloc(pci, pt)) + goto err_free_slist; + + pt->nents = pages; + slen = pci_map_sg(pci,pt->slist,pt->nents,PCI_DMA_FROMDEVICE); + if (0 == slen) + goto err_free_pgtable; + + if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen)) + goto err_unmap_sg; + + return mem; + +err_unmap_sg: + pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE); +err_free_pgtable: + saa7146_pgtable_free(pci, pt); +err_free_slist: + kfree(pt->slist); + pt->slist = NULL; +err_free_mem: + vfree(mem); +err_null: + return 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); + saa7146_pgtable_free(pci, pt); + kfree(pt->slist); + pt->slist = NULL; + vfree(mem); +} + +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); + pt->cpu = NULL; +} + +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); + if (NULL == cpu) { + return -ENOMEM; + } + pt->size = PAGE_SIZE; + pt->cpu = cpu; + pt->dma = dma_addr; + + return 0; +} + +int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, + struct scatterlist *list, int sglen ) +{ + __le32 *ptr, fill; + int nr_pages = 0; + int i,p; + + BUG_ON(0 == sglen); + BUG_ON(list->offset > PAGE_SIZE); + + /* 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++; + } + } + + + /* safety; fill the page table up with the last valid page */ + fill = *(ptr-1); + 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; +} + +/********************************************************************************/ +/* interrupt handler */ +static irqreturn_t interrupt_hw(int irq, void *dev_id) +{ + struct saa7146_dev *dev = dev_id; + u32 isr; + u32 ack_isr; + + /* read out the interrupt status register */ + ack_isr = isr = saa7146_read(dev, ISR); + + /* is this our interrupt? */ + if ( 0 == isr ) { + /* nope, some other device */ + return IRQ_NONE; + } + + if (dev->ext) { + if (dev->ext->irq_mask & isr) { + if (dev->ext->irq_func) + dev->ext->irq_func(dev, &isr); + isr &= ~dev->ext->irq_mask; + } + } + if (0 != (isr & (MASK_27))) { + DEB_INT("irq: RPS0 (0x%08x)\n", isr); + if (dev->vv_data && dev->vv_callback) + dev->vv_callback(dev,isr); + isr &= ~MASK_27; + } + if (0 != (isr & (MASK_28))) { + if (dev->vv_data && dev->vv_callback) + dev->vv_callback(dev,isr); + isr &= ~MASK_28; + } + if (0 != (isr & (MASK_16|MASK_17))) { + SAA7146_IER_DISABLE(dev, MASK_16|MASK_17); + /* only wake up if we expect something */ + if (0 != dev->i2c_op) { + dev->i2c_op = 0; + wake_up(&dev->i2c_wq); + } else { + u32 psr = saa7146_read(dev, PSR); + u32 ssr = saa7146_read(dev, SSR); + pr_warn("%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n", + dev->name, isr, psr, ssr); + } + isr &= ~(MASK_16|MASK_17); + } + if( 0 != isr ) { + ERR("warning: interrupt enabled, but not handled properly.(0x%08x)\n", + isr); + ERR("disabling interrupt source(s)!\n"); + SAA7146_IER_DISABLE(dev,isr); + } + saa7146_write(dev, ISR, ack_isr); + return IRQ_HANDLED; +} + +/*********************************************************************************/ +/* configuration-functions */ + +static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent) +{ + struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data; + struct saa7146_extension *ext = pci_ext->ext; + struct saa7146_dev *dev; + int err = -ENOMEM; + + /* clear out mem for sure */ + dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL); + if (!dev) { + ERR("out of memory\n"); + goto out; + } + + DEB_EE("pci:%p\n", pci); + + err = pci_enable_device(pci); + if (err < 0) { + ERR("pci_enable_device() failed\n"); + goto err_free; + } + + /* enable bus-mastering */ + pci_set_master(pci); + + dev->pci = pci; + + /* get chip-revision; this is needed to enable bug-fixes */ + dev->revision = pci->revision; + + /* remap the memory from virtual to physical address */ + + err = pci_request_region(pci, 0, "saa7146"); + if (err < 0) + goto err_disable; + + dev->mem = ioremap(pci_resource_start(pci, 0), + pci_resource_len(pci, 0)); + if (!dev->mem) { + ERR("ioremap() failed\n"); + err = -ENODEV; + goto err_release; + } + + /* we don't do a master reset here anymore, it screws up + some boards that don't have an i2c-eeprom for configuration + values */ +/* + saa7146_write(dev, MC1, MASK_31); +*/ + + /* disable all irqs */ + saa7146_write(dev, IER, 0); + + /* shut down all dma transfers and rps tasks */ + saa7146_write(dev, MC1, 0x30ff0000); + + /* clear out any rps-signals pending */ + saa7146_write(dev, MC2, 0xf8000000); + + /* request an interrupt for the saa7146 */ + err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED | IRQF_DISABLED, + dev->name, dev); + if (err < 0) { + ERR("request_irq() failed\n"); + goto err_unmap; + } + + err = -ENOMEM; + + /* get memory for various stuff */ + dev->d_rps0.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM, + &dev->d_rps0.dma_handle); + 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); + 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); + 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); + dev->ext = ext; + + mutex_init(&dev->v4l2_lock); + spin_lock_init(&dev->int_slock); + spin_lock_init(&dev->slock); + + mutex_init(&dev->i2c_lock); + + dev->module = THIS_MODULE; + init_waitqueue_head(&dev->i2c_wq); + + /* set some sane pci arbitrition values */ + saa7146_write(dev, PCI_BT_V1, 0x1c00101f); + + /* TODO: use the status code of the callback */ + + err = -ENODEV; + + if (ext->probe && ext->probe(dev)) { + DEB_D("ext->probe() failed for %p. skipping device.\n", dev); + goto err_free_i2c; + } + + if (ext->attach(dev, pci_ext)) { + DEB_D("ext->attach() failed for %p. skipping device.\n", dev); + goto err_free_i2c; + } + /* V4L extensions will set the pci drvdata to the v4l2_device in the + attach() above. So for those cards that do not use V4L we have to + set it explicitly. */ + pci_set_drvdata(pci, &dev->v4l2_dev); + + saa7146_num++; + + err = 0; +out: + return err; + +err_free_i2c: + pci_free_consistent(pci, 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); +err_free_rps0: + pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, + dev->d_rps0.dma_handle); +err_free_irq: + free_irq(pci->irq, (void *)dev); +err_unmap: + iounmap(dev->mem); +err_release: + pci_release_region(pci, 0); +err_disable: + pci_disable_device(pci); +err_free: + kfree(dev); + goto out; +} + +static void saa7146_remove_one(struct pci_dev *pdev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev); + struct { + void *addr; + dma_addr_t dma; + } dev_map[] = { + { dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle }, + { dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle }, + { dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle }, + { NULL, 0 } + }, *p; + + 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); + + /* disable all irqs, release irq-routine */ + saa7146_write(dev, IER, 0); + + free_irq(pdev->irq, dev); + + for (p = dev_map; p->addr; p++) + pci_free_consistent(pdev, SAA7146_RPS_MEM, p->addr, p->dma); + + iounmap(dev->mem); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + kfree(dev); + + saa7146_num--; +} + +/*********************************************************************************/ +/* extension handling functions */ + +int saa7146_register_extension(struct saa7146_extension* ext) +{ + DEB_EE("ext:%p\n", ext); + + ext->driver.name = ext->name; + ext->driver.id_table = ext->pci_tbl; + ext->driver.probe = saa7146_init_one; + ext->driver.remove = saa7146_remove_one; + + pr_info("register extension '%s'\n", ext->name); + return pci_register_driver(&ext->driver); +} + +int saa7146_unregister_extension(struct saa7146_extension* ext) +{ + DEB_EE("ext:%p\n", ext); + pr_info("unregister extension '%s'\n", ext->name); + pci_unregister_driver(&ext->driver); + return 0; +} + +EXPORT_SYMBOL_GPL(saa7146_register_extension); +EXPORT_SYMBOL_GPL(saa7146_unregister_extension); + +/* misc functions used by extension modules */ +EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc); +EXPORT_SYMBOL_GPL(saa7146_pgtable_free); +EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single); +EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable); +EXPORT_SYMBOL_GPL(saa7146_vfree_destroy_pgtable); +EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done); + +EXPORT_SYMBOL_GPL(saa7146_setgpio); + +EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare); + +EXPORT_SYMBOL_GPL(saa7146_debug); + +MODULE_AUTHOR("Michael Hunold "); +MODULE_DESCRIPTION("driver for generic saa7146-based hardware"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c new file mode 100644 index 000000000000..b3890bd49df6 --- /dev/null +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -0,0 +1,664 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include + +/****************************************************************************/ +/* 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(vdev->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(vdev->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(vdev->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(vdev->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; + int res; + + 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; + } + + if (mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + res = videobuf_mmap_mapper(q, vma); + mutex_unlock(vdev->lock); + return res; +} + +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 unsigned int fops_poll(struct file *file, struct poll_table_struct *wait) +{ + struct video_device *vdev = video_devdata(file); + unsigned int res; + + mutex_lock(vdev->lock); + res = __fops_poll(file, wait); + mutex_unlock(vdev->lock); + 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; + int ret; + + 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) { + if (mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + ret = saa7146_vbi_uops.read(file, data, count, ppos); + mutex_unlock(vdev->lock); + return ret; + } + 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; + int ret; + + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: + return -EINVAL; + case VFL_TYPE_VBI: + if (fh->dev->ext_vv_data->vbi_fops.write) { + if (mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + ret = fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos); + mutex_unlock(vdev->lock); + return ret; + } + 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; + 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 "); +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..be746d1aee9a --- /dev/null +++ b/drivers/media/common/saa7146/saa7146_hlp.c @@ -0,0 +1,1048 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +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/saa7146_i2c.c b/drivers/media/common/saa7146/saa7146_i2c.c new file mode 100644 index 000000000000..22027198129d --- /dev/null +++ b/drivers/media/common/saa7146/saa7146_i2c.c @@ -0,0 +1,423 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include + +static u32 saa7146_i2c_func(struct i2c_adapter *adapter) +{ + /* DEB_I2C("'%s'\n", adapter->name); */ + + return I2C_FUNC_I2C + | I2C_FUNC_SMBUS_QUICK + | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE + | I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA; +} + +/* this function returns the status-register of our i2c-device */ +static inline u32 saa7146_i2c_status(struct saa7146_dev *dev) +{ + u32 iicsta = saa7146_read(dev, I2C_STATUS); + /* DEB_I2C("status: 0x%08x\n", iicsta); */ + return iicsta; +} + +/* this function runs through the i2c-messages and prepares the data to be + sent through the saa7146. have a look at the specifications p. 122 ff + to understand this. it returns the number of u32s to send, or -1 + in case of an error. */ +static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op) +{ + int h1, h2; + int i, j, addr; + int mem = 0, op_count = 0; + + /* first determine size of needed memory */ + for(i = 0; i < num; i++) { + mem += m[i].len + 1; + } + + /* worst case: we need one u32 for three bytes to be send + plus one extra byte to address the device */ + mem = 1 + ((mem-1) / 3); + + /* we assume that op points to a memory of at least + * SAA7146_I2C_MEM bytes size. if we exceed this limit... + */ + if ((4 * mem) > SAA7146_I2C_MEM) { + /* DEB_I2C("cannot prepare i2c-message\n"); */ + return -ENOMEM; + } + + /* be careful: clear out the i2c-mem first */ + memset(op,0,sizeof(__le32)*mem); + + /* 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); + 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)); + op_count++; + + /* loop through all bytes of message i */ + for(j = 0; j < m[i].len; j++) { + /* insert the data bytes */ + h1 = op_count/3; h2 = op_count%3; + op[h1] |= cpu_to_le32( (u32)((u8)m[i].buf[j]) << ((3-h2)*8)); + op[h1] |= cpu_to_le32( SAA7146_I2C_CONT << ((3-h2)*2)); + op_count++; + } + + } + + /* have a look at the last byte inserted: + if it was: ...CONT change it to ...STOP */ + h1 = (op_count-1)/3; h2 = (op_count-1)%3; + if ( SAA7146_I2C_CONT == (0x3 & (le32_to_cpu(op[h1]) >> ((3-h2)*2))) ) { + op[h1] &= ~cpu_to_le32(0x2 << ((3-h2)*2)); + op[h1] |= cpu_to_le32(SAA7146_I2C_STOP << ((3-h2)*2)); + } + + /* return the number of u32s to send */ + return mem; +} + +/* this functions loops through all i2c-messages. normally, it should determine + which bytes were read through the adapter and write them back to the corresponding + i2c-message. but instead, we simply write back all bytes. + fixme: this could be improved. */ +static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, __le32 *op) +{ + int i, j; + int op_count = 0; + + /* loop through all messages */ + for(i = 0; i < num; i++) { + + op_count++; + + /* loop through all bytes of message i */ + for(j = 0; j < m[i].len; j++) { + /* write back all bytes that could have been read */ + m[i].buf[j] = (le32_to_cpu(op[op_count/3]) >> ((3-(op_count%3))*8)); + op_count++; + } + } + + return 0; +} + +/* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */ +static int saa7146_i2c_reset(struct saa7146_dev *dev) +{ + /* get current status */ + u32 status = saa7146_i2c_status(dev); + + /* clear registers for sure */ + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, I2C_TRANSFER, 0); + + /* check if any operation is still in progress */ + if ( 0 != ( status & SAA7146_I2C_BUSY) ) { + + /* yes, kill ongoing operation */ + DEB_I2C("busy_state detected\n"); + + /* set "ABORT-OPERATION"-bit (bit 7)*/ + saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + + /* clear all error-bits pending; this is needed because p.123, note 1 */ + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + } + + /* check if any error is (still) present. (this can be necessary because p.123, note 1) */ + status = saa7146_i2c_status(dev); + + if ( dev->i2c_bitrate != status ) { + + DEB_I2C("error_state detected. status:0x%08x\n", status); + + /* Repeat the abort operation. This seems to be necessary + after serious protocol errors caused by e.g. the SAA7740 */ + saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + + /* clear all error-bits pending */ + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + + /* the data sheet says it might be necessary to clear the status + twice after an abort */ + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + } + + /* if any error is still present, a fatal error has occurred ... */ + status = saa7146_i2c_status(dev); + if ( dev->i2c_bitrate != status ) { + DEB_I2C("fatal error. status:0x%08x\n", status); + return -1; + } + + return 0; +} + +/* this functions writes out the data-byte 'dword' to the i2c-device. + it returns 0 if ok, -1 if the transfer failed, -2 if the transfer + failed badly (e.g. address error) */ +static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int short_delay) +{ + u32 status = 0, mc2 = 0; + int trial = 0; + unsigned long timeout; + + /* write out i2c-command */ + DEB_I2C("before: 0x%08x (status: 0x%08x), %d\n", + *dword, saa7146_read(dev, I2C_STATUS), dev->i2c_op); + + if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) { + + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword)); + + dev->i2c_op = 1; + SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17); + SAA7146_IER_ENABLE(dev, MASK_16|MASK_17); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + + timeout = HZ/100 + 1; /* 10ms */ + timeout = wait_event_interruptible_timeout(dev->i2c_wq, dev->i2c_op == 0, timeout); + if (timeout == -ERESTARTSYS || dev->i2c_op) { + SAA7146_IER_DISABLE(dev, MASK_16|MASK_17); + SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17); + if (timeout == -ERESTARTSYS) + /* a signal arrived */ + return -ERESTARTSYS; + + pr_warn("%s %s [irq]: timed out waiting for end of xfer\n", + dev->name, __func__); + return -EIO; + } + status = saa7146_read(dev, I2C_STATUS); + } else { + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword)); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + + /* do not poll for i2c-status before upload is complete */ + timeout = jiffies + HZ/100 + 1; /* 10ms */ + while(1) { + mc2 = (saa7146_read(dev, MC2) & 0x1); + if( 0 != mc2 ) { + break; + } + if (time_after(jiffies,timeout)) { + pr_warn("%s %s: timed out waiting for MC2\n", + dev->name, __func__); + return -EIO; + } + } + /* wait until we get a transfer done or error */ + timeout = jiffies + HZ/100 + 1; /* 10ms */ + /* first read usually delivers bogus results... */ + saa7146_i2c_status(dev); + while(1) { + status = saa7146_i2c_status(dev); + if ((status & 0x3) != 1) + break; + if (time_after(jiffies,timeout)) { + /* this is normal when probing the bus + * (no answer from nonexisistant device...) + */ + pr_warn("%s %s [poll]: timed out waiting for end of xfer\n", + dev->name, __func__); + return -EIO; + } + if (++trial < 50 && short_delay) + udelay(10); + else + msleep(1); + } + } + + /* give a detailed status report */ + if ( 0 != (status & (SAA7146_I2C_SPERR | SAA7146_I2C_APERR | + SAA7146_I2C_DTERR | SAA7146_I2C_DRERR | + SAA7146_I2C_AL | SAA7146_I2C_ERR | + SAA7146_I2C_BUSY)) ) { + + if ( 0 == (status & SAA7146_I2C_ERR) || + 0 == (status & SAA7146_I2C_BUSY) ) { + /* it may take some time until ERR goes high - ignore */ + DEB_I2C("unexpected i2c status %04x\n", status); + } + if( 0 != (status & SAA7146_I2C_SPERR) ) { + DEB_I2C("error due to invalid start/stop condition\n"); + } + if( 0 != (status & SAA7146_I2C_DTERR) ) { + DEB_I2C("error in data transmission\n"); + } + if( 0 != (status & SAA7146_I2C_DRERR) ) { + DEB_I2C("error when receiving data\n"); + } + if( 0 != (status & SAA7146_I2C_AL) ) { + DEB_I2C("error because arbitration lost\n"); + } + + /* we handle address-errors here */ + if( 0 != (status & SAA7146_I2C_APERR) ) { + DEB_I2C("error in address phase\n"); + return -EREMOTEIO; + } + + return -EIO; + } + + /* read back data, just in case we were reading ... */ + *dword = cpu_to_le32(saa7146_read(dev, I2C_TRANSFER)); + + DEB_I2C("after: 0x%08x\n", *dword); + return 0; +} + +static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries) +{ + int i = 0, count = 0; + __le32 *buffer = dev->d_i2c.cpu_addr; + int err = 0; + int short_delay = 0; + + if (mutex_lock_interruptible(&dev->i2c_lock)) + return -ERESTARTSYS; + + for(i=0;i count ) { + err = -1; + goto out; + } + + if ( count > 3 || 0 != (SAA7146_I2C_SHORT_DELAY & dev->ext->flags) ) + short_delay = 1; + + do { + /* reset the i2c-device if necessary */ + err = saa7146_i2c_reset(dev); + if ( 0 > err ) { + DEB_I2C("could not reset i2c-device\n"); + goto out; + } + + /* write out the u32s one after another */ + for(i = 0; i < count; i++) { + err = saa7146_i2c_writeout(dev, &buffer[i], short_delay); + if ( 0 != err) { + /* this one is unsatisfying: some i2c slaves on some + dvb cards don't acknowledge correctly, so the saa7146 + thinks that an address error occurred. in that case, the + transaction should be retrying, even if an address error + occurred. analog saa7146 based cards extensively rely on + i2c address probing, however, and address errors indicate that a + device is really *not* there. retrying in that case + increases the time the device needs to probe greatly, so + it should be avoided. So we bail out in irq mode after an + address error and trust the saa7146 address error detection. */ + if (-EREMOTEIO == err && 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) + goto out; + DEB_I2C("error while sending message(s). starting again\n"); + break; + } + } + if( 0 == err ) { + err = num; + break; + } + + /* delay a bit before retrying */ + msleep(10); + + } while (err != num && retries--); + + /* quit if any error occurred */ + if (err != num) + goto out; + + /* 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; + goto out; + } + + /* return the number of delivered messages */ + DEB_I2C("transmission successful. (msg:%d)\n", err); +out: + /* another bug in revision 0: the i2c-registers get uploaded randomly by other + uploads, so we better clear them out before continuing */ + if( 0 == dev->revision ) { + __le32 zero = 0; + saa7146_i2c_reset(dev); + if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) { + pr_info("revision 0 error. this should never happen\n"); + } + } + + mutex_unlock(&dev->i2c_lock); + return err; +} + +/* utility functions */ +static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num) +{ + struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter); + struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev); + + /* use helper function to transfer data */ + return saa7146_i2c_transfer(dev, msg, num, adapter->retries); +} + + +/*****************************************************************************/ +/* i2c-adapter helper functions */ + +/* exported algorithm data */ +static struct i2c_algorithm saa7146_algo = { + .master_xfer = saa7146_i2c_xfer, + .functionality = saa7146_i2c_func, +}; + +int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate) +{ + DEB_EE("bitrate: 0x%08x\n", bitrate); + + /* enable i2c-port pins */ + saa7146_write(dev, MC1, (MASK_08 | MASK_24)); + + dev->i2c_bitrate = bitrate; + saa7146_i2c_reset(dev); + + if (i2c_adapter) { + i2c_set_adapdata(i2c_adapter, &dev->v4l2_dev); + i2c_adapter->dev.parent = &dev->pci->dev; + i2c_adapter->algo = &saa7146_algo; + i2c_adapter->algo_data = NULL; + i2c_adapter->timeout = SAA7146_I2C_TIMEOUT; + i2c_adapter->retries = SAA7146_I2C_RETRIES; + } + + return 0; +} diff --git a/drivers/media/common/saa7146/saa7146_vbi.c b/drivers/media/common/saa7146/saa7146_vbi.c new file mode 100644 index 000000000000..1e71e374bbfe --- /dev/null +++ b/drivers/media/common/saa7146/saa7146_vbi.c @@ -0,0 +1,498 @@ +#include + +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/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c new file mode 100644 index 000000000000..6d14785d4747 --- /dev/null +++ b/drivers/media/common/saa7146/saa7146_video.c @@ -0,0 +1,1332 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +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/saa7146_core.c b/drivers/media/common/saa7146_core.c deleted file mode 100644 index bb6ee5191eb1..000000000000 --- a/drivers/media/common/saa7146_core.c +++ /dev/null @@ -1,592 +0,0 @@ -/* - saa7146.o - driver for generic saa7146-based hardware - - Copyright (C) 1998-2003 Michael Hunold - - 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 -#include - -static int saa7146_num; - -unsigned int saa7146_debug; - -module_param(saa7146_debug, uint, 0644); -MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)"); - -#if 0 -static void dump_registers(struct saa7146_dev* dev) -{ - int i = 0; - - pr_info(" @ %li jiffies:\n", jiffies); - for (i = 0; i <= 0x148; i += 4) - pr_info("0x%03x: 0x%08x\n", i, saa7146_read(dev, i)); -} -#endif - -/**************************************************************************** - * gpio and debi helper functions - ****************************************************************************/ - -void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data) -{ - u32 value = 0; - - BUG_ON(port > 3); - - value = saa7146_read(dev, GPIO_CTRL); - value &= ~(0xff << (8*port)); - value |= (data << (8*port)); - saa7146_write(dev, GPIO_CTRL, value); -} - -/* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */ -static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev, - unsigned long us1, unsigned long us2) -{ - unsigned long timeout; - int err; - - /* wait for registers to be programmed */ - timeout = jiffies + usecs_to_jiffies(us1); - while (1) { - err = time_after(jiffies, timeout); - if (saa7146_read(dev, MC2) & 2) - break; - if (err) { - pr_err("%s: %s timed out while waiting for registers getting programmed\n", - dev->name, __func__); - return -ETIMEDOUT; - } - msleep(1); - } - - /* wait for transfer to complete */ - timeout = jiffies + usecs_to_jiffies(us2); - while (1) { - err = time_after(jiffies, timeout); - if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S)) - break; - saa7146_read(dev, MC2); - if (err) { - DEB_S("%s: %s timed out while waiting for transfer completion\n", - dev->name, __func__); - return -ETIMEDOUT; - } - msleep(1); - } - - return 0; -} - -static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev, - unsigned long us1, unsigned long us2) -{ - unsigned long loops; - - /* wait for registers to be programmed */ - loops = us1; - while (1) { - if (saa7146_read(dev, MC2) & 2) - break; - if (!loops--) { - pr_err("%s: %s timed out while waiting for registers getting programmed\n", - dev->name, __func__); - return -ETIMEDOUT; - } - udelay(1); - } - - /* wait for transfer to complete */ - loops = us2 / 5; - while (1) { - if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S)) - break; - saa7146_read(dev, MC2); - if (!loops--) { - DEB_S("%s: %s timed out while waiting for transfer completion\n", - dev->name, __func__); - return -ETIMEDOUT; - } - udelay(5); - } - - return 0; -} - -int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop) -{ - if (nobusyloop) - return saa7146_wait_for_debi_done_sleep(dev, 50000, 250000); - else - return saa7146_wait_for_debi_done_busyloop(dev, 50000, 250000); -} - -/**************************************************************************** - * general helper functions - ****************************************************************************/ - -/* 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 */ -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); - if (NULL == sglist) - return NULL; - sg_init_table(sglist, nr_pages); - for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) { - pg = vmalloc_to_page(virt); - if (NULL == pg) - goto err; - BUG_ON(PageHighMem(pg)); - sg_set_page(&sglist[i], pg, PAGE_SIZE, 0); - } - return sglist; - - err: - kfree(sglist); - return NULL; -} - -/********************************************************************************/ -/* common page table functions */ - -void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt) -{ - int pages = (length+PAGE_SIZE-1)/PAGE_SIZE; - void *mem = vmalloc_32(length); - int slen = 0; - - if (NULL == mem) - goto err_null; - - if (!(pt->slist = vmalloc_to_sg(mem, pages))) - goto err_free_mem; - - if (saa7146_pgtable_alloc(pci, pt)) - goto err_free_slist; - - pt->nents = pages; - slen = pci_map_sg(pci,pt->slist,pt->nents,PCI_DMA_FROMDEVICE); - if (0 == slen) - goto err_free_pgtable; - - if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen)) - goto err_unmap_sg; - - return mem; - -err_unmap_sg: - pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE); -err_free_pgtable: - saa7146_pgtable_free(pci, pt); -err_free_slist: - kfree(pt->slist); - pt->slist = NULL; -err_free_mem: - vfree(mem); -err_null: - return 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); - saa7146_pgtable_free(pci, pt); - kfree(pt->slist); - pt->slist = NULL; - vfree(mem); -} - -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); - pt->cpu = NULL; -} - -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); - if (NULL == cpu) { - return -ENOMEM; - } - pt->size = PAGE_SIZE; - pt->cpu = cpu; - pt->dma = dma_addr; - - return 0; -} - -int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, - struct scatterlist *list, int sglen ) -{ - __le32 *ptr, fill; - int nr_pages = 0; - int i,p; - - BUG_ON(0 == sglen); - BUG_ON(list->offset > PAGE_SIZE); - - /* 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++; - } - } - - - /* safety; fill the page table up with the last valid page */ - fill = *(ptr-1); - 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; -} - -/********************************************************************************/ -/* interrupt handler */ -static irqreturn_t interrupt_hw(int irq, void *dev_id) -{ - struct saa7146_dev *dev = dev_id; - u32 isr; - u32 ack_isr; - - /* read out the interrupt status register */ - ack_isr = isr = saa7146_read(dev, ISR); - - /* is this our interrupt? */ - if ( 0 == isr ) { - /* nope, some other device */ - return IRQ_NONE; - } - - if (dev->ext) { - if (dev->ext->irq_mask & isr) { - if (dev->ext->irq_func) - dev->ext->irq_func(dev, &isr); - isr &= ~dev->ext->irq_mask; - } - } - if (0 != (isr & (MASK_27))) { - DEB_INT("irq: RPS0 (0x%08x)\n", isr); - if (dev->vv_data && dev->vv_callback) - dev->vv_callback(dev,isr); - isr &= ~MASK_27; - } - if (0 != (isr & (MASK_28))) { - if (dev->vv_data && dev->vv_callback) - dev->vv_callback(dev,isr); - isr &= ~MASK_28; - } - if (0 != (isr & (MASK_16|MASK_17))) { - SAA7146_IER_DISABLE(dev, MASK_16|MASK_17); - /* only wake up if we expect something */ - if (0 != dev->i2c_op) { - dev->i2c_op = 0; - wake_up(&dev->i2c_wq); - } else { - u32 psr = saa7146_read(dev, PSR); - u32 ssr = saa7146_read(dev, SSR); - pr_warn("%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n", - dev->name, isr, psr, ssr); - } - isr &= ~(MASK_16|MASK_17); - } - if( 0 != isr ) { - ERR("warning: interrupt enabled, but not handled properly.(0x%08x)\n", - isr); - ERR("disabling interrupt source(s)!\n"); - SAA7146_IER_DISABLE(dev,isr); - } - saa7146_write(dev, ISR, ack_isr); - return IRQ_HANDLED; -} - -/*********************************************************************************/ -/* configuration-functions */ - -static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent) -{ - struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data; - struct saa7146_extension *ext = pci_ext->ext; - struct saa7146_dev *dev; - int err = -ENOMEM; - - /* clear out mem for sure */ - dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL); - if (!dev) { - ERR("out of memory\n"); - goto out; - } - - DEB_EE("pci:%p\n", pci); - - err = pci_enable_device(pci); - if (err < 0) { - ERR("pci_enable_device() failed\n"); - goto err_free; - } - - /* enable bus-mastering */ - pci_set_master(pci); - - dev->pci = pci; - - /* get chip-revision; this is needed to enable bug-fixes */ - dev->revision = pci->revision; - - /* remap the memory from virtual to physical address */ - - err = pci_request_region(pci, 0, "saa7146"); - if (err < 0) - goto err_disable; - - dev->mem = ioremap(pci_resource_start(pci, 0), - pci_resource_len(pci, 0)); - if (!dev->mem) { - ERR("ioremap() failed\n"); - err = -ENODEV; - goto err_release; - } - - /* we don't do a master reset here anymore, it screws up - some boards that don't have an i2c-eeprom for configuration - values */ -/* - saa7146_write(dev, MC1, MASK_31); -*/ - - /* disable all irqs */ - saa7146_write(dev, IER, 0); - - /* shut down all dma transfers and rps tasks */ - saa7146_write(dev, MC1, 0x30ff0000); - - /* clear out any rps-signals pending */ - saa7146_write(dev, MC2, 0xf8000000); - - /* request an interrupt for the saa7146 */ - err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED | IRQF_DISABLED, - dev->name, dev); - if (err < 0) { - ERR("request_irq() failed\n"); - goto err_unmap; - } - - err = -ENOMEM; - - /* get memory for various stuff */ - dev->d_rps0.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM, - &dev->d_rps0.dma_handle); - 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); - 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); - 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); - dev->ext = ext; - - mutex_init(&dev->v4l2_lock); - spin_lock_init(&dev->int_slock); - spin_lock_init(&dev->slock); - - mutex_init(&dev->i2c_lock); - - dev->module = THIS_MODULE; - init_waitqueue_head(&dev->i2c_wq); - - /* set some sane pci arbitrition values */ - saa7146_write(dev, PCI_BT_V1, 0x1c00101f); - - /* TODO: use the status code of the callback */ - - err = -ENODEV; - - if (ext->probe && ext->probe(dev)) { - DEB_D("ext->probe() failed for %p. skipping device.\n", dev); - goto err_free_i2c; - } - - if (ext->attach(dev, pci_ext)) { - DEB_D("ext->attach() failed for %p. skipping device.\n", dev); - goto err_free_i2c; - } - /* V4L extensions will set the pci drvdata to the v4l2_device in the - attach() above. So for those cards that do not use V4L we have to - set it explicitly. */ - pci_set_drvdata(pci, &dev->v4l2_dev); - - saa7146_num++; - - err = 0; -out: - return err; - -err_free_i2c: - pci_free_consistent(pci, 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); -err_free_rps0: - pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, - dev->d_rps0.dma_handle); -err_free_irq: - free_irq(pci->irq, (void *)dev); -err_unmap: - iounmap(dev->mem); -err_release: - pci_release_region(pci, 0); -err_disable: - pci_disable_device(pci); -err_free: - kfree(dev); - goto out; -} - -static void saa7146_remove_one(struct pci_dev *pdev) -{ - struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); - struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev); - struct { - void *addr; - dma_addr_t dma; - } dev_map[] = { - { dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle }, - { dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle }, - { dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle }, - { NULL, 0 } - }, *p; - - 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); - - /* disable all irqs, release irq-routine */ - saa7146_write(dev, IER, 0); - - free_irq(pdev->irq, dev); - - for (p = dev_map; p->addr; p++) - pci_free_consistent(pdev, SAA7146_RPS_MEM, p->addr, p->dma); - - iounmap(dev->mem); - pci_release_region(pdev, 0); - pci_disable_device(pdev); - kfree(dev); - - saa7146_num--; -} - -/*********************************************************************************/ -/* extension handling functions */ - -int saa7146_register_extension(struct saa7146_extension* ext) -{ - DEB_EE("ext:%p\n", ext); - - ext->driver.name = ext->name; - ext->driver.id_table = ext->pci_tbl; - ext->driver.probe = saa7146_init_one; - ext->driver.remove = saa7146_remove_one; - - pr_info("register extension '%s'\n", ext->name); - return pci_register_driver(&ext->driver); -} - -int saa7146_unregister_extension(struct saa7146_extension* ext) -{ - DEB_EE("ext:%p\n", ext); - pr_info("unregister extension '%s'\n", ext->name); - pci_unregister_driver(&ext->driver); - return 0; -} - -EXPORT_SYMBOL_GPL(saa7146_register_extension); -EXPORT_SYMBOL_GPL(saa7146_unregister_extension); - -/* misc functions used by extension modules */ -EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc); -EXPORT_SYMBOL_GPL(saa7146_pgtable_free); -EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single); -EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable); -EXPORT_SYMBOL_GPL(saa7146_vfree_destroy_pgtable); -EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done); - -EXPORT_SYMBOL_GPL(saa7146_setgpio); - -EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare); - -EXPORT_SYMBOL_GPL(saa7146_debug); - -MODULE_AUTHOR("Michael Hunold "); -MODULE_DESCRIPTION("driver for generic saa7146-based hardware"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c deleted file mode 100644 index b3890bd49df6..000000000000 --- a/drivers/media/common/saa7146_fops.c +++ /dev/null @@ -1,664 +0,0 @@ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include - -/****************************************************************************/ -/* 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(vdev->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(vdev->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(vdev->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(vdev->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; - int res; - - 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; - } - - if (mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; - res = videobuf_mmap_mapper(q, vma); - mutex_unlock(vdev->lock); - return res; -} - -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 unsigned int fops_poll(struct file *file, struct poll_table_struct *wait) -{ - struct video_device *vdev = video_devdata(file); - unsigned int res; - - mutex_lock(vdev->lock); - res = __fops_poll(file, wait); - mutex_unlock(vdev->lock); - 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; - int ret; - - 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) { - if (mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; - ret = saa7146_vbi_uops.read(file, data, count, ppos); - mutex_unlock(vdev->lock); - return ret; - } - 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; - int ret; - - switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: - return -EINVAL; - case VFL_TYPE_VBI: - if (fh->dev->ext_vv_data->vbi_fops.write) { - if (mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; - ret = fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos); - mutex_unlock(vdev->lock); - return ret; - } - 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; - 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 "); -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 -#include -#include - -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_i2c.c b/drivers/media/common/saa7146_i2c.c deleted file mode 100644 index 22027198129d..000000000000 --- a/drivers/media/common/saa7146_i2c.c +++ /dev/null @@ -1,423 +0,0 @@ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include - -static u32 saa7146_i2c_func(struct i2c_adapter *adapter) -{ - /* DEB_I2C("'%s'\n", adapter->name); */ - - return I2C_FUNC_I2C - | I2C_FUNC_SMBUS_QUICK - | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE - | I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA; -} - -/* this function returns the status-register of our i2c-device */ -static inline u32 saa7146_i2c_status(struct saa7146_dev *dev) -{ - u32 iicsta = saa7146_read(dev, I2C_STATUS); - /* DEB_I2C("status: 0x%08x\n", iicsta); */ - return iicsta; -} - -/* this function runs through the i2c-messages and prepares the data to be - sent through the saa7146. have a look at the specifications p. 122 ff - to understand this. it returns the number of u32s to send, or -1 - in case of an error. */ -static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op) -{ - int h1, h2; - int i, j, addr; - int mem = 0, op_count = 0; - - /* first determine size of needed memory */ - for(i = 0; i < num; i++) { - mem += m[i].len + 1; - } - - /* worst case: we need one u32 for three bytes to be send - plus one extra byte to address the device */ - mem = 1 + ((mem-1) / 3); - - /* we assume that op points to a memory of at least - * SAA7146_I2C_MEM bytes size. if we exceed this limit... - */ - if ((4 * mem) > SAA7146_I2C_MEM) { - /* DEB_I2C("cannot prepare i2c-message\n"); */ - return -ENOMEM; - } - - /* be careful: clear out the i2c-mem first */ - memset(op,0,sizeof(__le32)*mem); - - /* 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); - 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)); - op_count++; - - /* loop through all bytes of message i */ - for(j = 0; j < m[i].len; j++) { - /* insert the data bytes */ - h1 = op_count/3; h2 = op_count%3; - op[h1] |= cpu_to_le32( (u32)((u8)m[i].buf[j]) << ((3-h2)*8)); - op[h1] |= cpu_to_le32( SAA7146_I2C_CONT << ((3-h2)*2)); - op_count++; - } - - } - - /* have a look at the last byte inserted: - if it was: ...CONT change it to ...STOP */ - h1 = (op_count-1)/3; h2 = (op_count-1)%3; - if ( SAA7146_I2C_CONT == (0x3 & (le32_to_cpu(op[h1]) >> ((3-h2)*2))) ) { - op[h1] &= ~cpu_to_le32(0x2 << ((3-h2)*2)); - op[h1] |= cpu_to_le32(SAA7146_I2C_STOP << ((3-h2)*2)); - } - - /* return the number of u32s to send */ - return mem; -} - -/* this functions loops through all i2c-messages. normally, it should determine - which bytes were read through the adapter and write them back to the corresponding - i2c-message. but instead, we simply write back all bytes. - fixme: this could be improved. */ -static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, __le32 *op) -{ - int i, j; - int op_count = 0; - - /* loop through all messages */ - for(i = 0; i < num; i++) { - - op_count++; - - /* loop through all bytes of message i */ - for(j = 0; j < m[i].len; j++) { - /* write back all bytes that could have been read */ - m[i].buf[j] = (le32_to_cpu(op[op_count/3]) >> ((3-(op_count%3))*8)); - op_count++; - } - } - - return 0; -} - -/* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */ -static int saa7146_i2c_reset(struct saa7146_dev *dev) -{ - /* get current status */ - u32 status = saa7146_i2c_status(dev); - - /* clear registers for sure */ - saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); - saa7146_write(dev, I2C_TRANSFER, 0); - - /* check if any operation is still in progress */ - if ( 0 != ( status & SAA7146_I2C_BUSY) ) { - - /* yes, kill ongoing operation */ - DEB_I2C("busy_state detected\n"); - - /* set "ABORT-OPERATION"-bit (bit 7)*/ - saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); - saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - msleep(SAA7146_I2C_DELAY); - - /* clear all error-bits pending; this is needed because p.123, note 1 */ - saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); - saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - msleep(SAA7146_I2C_DELAY); - } - - /* check if any error is (still) present. (this can be necessary because p.123, note 1) */ - status = saa7146_i2c_status(dev); - - if ( dev->i2c_bitrate != status ) { - - DEB_I2C("error_state detected. status:0x%08x\n", status); - - /* Repeat the abort operation. This seems to be necessary - after serious protocol errors caused by e.g. the SAA7740 */ - saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); - saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - msleep(SAA7146_I2C_DELAY); - - /* clear all error-bits pending */ - saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); - saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - msleep(SAA7146_I2C_DELAY); - - /* the data sheet says it might be necessary to clear the status - twice after an abort */ - saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); - saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - msleep(SAA7146_I2C_DELAY); - } - - /* if any error is still present, a fatal error has occurred ... */ - status = saa7146_i2c_status(dev); - if ( dev->i2c_bitrate != status ) { - DEB_I2C("fatal error. status:0x%08x\n", status); - return -1; - } - - return 0; -} - -/* this functions writes out the data-byte 'dword' to the i2c-device. - it returns 0 if ok, -1 if the transfer failed, -2 if the transfer - failed badly (e.g. address error) */ -static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int short_delay) -{ - u32 status = 0, mc2 = 0; - int trial = 0; - unsigned long timeout; - - /* write out i2c-command */ - DEB_I2C("before: 0x%08x (status: 0x%08x), %d\n", - *dword, saa7146_read(dev, I2C_STATUS), dev->i2c_op); - - if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) { - - saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); - saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword)); - - dev->i2c_op = 1; - SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17); - SAA7146_IER_ENABLE(dev, MASK_16|MASK_17); - saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - - timeout = HZ/100 + 1; /* 10ms */ - timeout = wait_event_interruptible_timeout(dev->i2c_wq, dev->i2c_op == 0, timeout); - if (timeout == -ERESTARTSYS || dev->i2c_op) { - SAA7146_IER_DISABLE(dev, MASK_16|MASK_17); - SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17); - if (timeout == -ERESTARTSYS) - /* a signal arrived */ - return -ERESTARTSYS; - - pr_warn("%s %s [irq]: timed out waiting for end of xfer\n", - dev->name, __func__); - return -EIO; - } - status = saa7146_read(dev, I2C_STATUS); - } else { - saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); - saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword)); - saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - - /* do not poll for i2c-status before upload is complete */ - timeout = jiffies + HZ/100 + 1; /* 10ms */ - while(1) { - mc2 = (saa7146_read(dev, MC2) & 0x1); - if( 0 != mc2 ) { - break; - } - if (time_after(jiffies,timeout)) { - pr_warn("%s %s: timed out waiting for MC2\n", - dev->name, __func__); - return -EIO; - } - } - /* wait until we get a transfer done or error */ - timeout = jiffies + HZ/100 + 1; /* 10ms */ - /* first read usually delivers bogus results... */ - saa7146_i2c_status(dev); - while(1) { - status = saa7146_i2c_status(dev); - if ((status & 0x3) != 1) - break; - if (time_after(jiffies,timeout)) { - /* this is normal when probing the bus - * (no answer from nonexisistant device...) - */ - pr_warn("%s %s [poll]: timed out waiting for end of xfer\n", - dev->name, __func__); - return -EIO; - } - if (++trial < 50 && short_delay) - udelay(10); - else - msleep(1); - } - } - - /* give a detailed status report */ - if ( 0 != (status & (SAA7146_I2C_SPERR | SAA7146_I2C_APERR | - SAA7146_I2C_DTERR | SAA7146_I2C_DRERR | - SAA7146_I2C_AL | SAA7146_I2C_ERR | - SAA7146_I2C_BUSY)) ) { - - if ( 0 == (status & SAA7146_I2C_ERR) || - 0 == (status & SAA7146_I2C_BUSY) ) { - /* it may take some time until ERR goes high - ignore */ - DEB_I2C("unexpected i2c status %04x\n", status); - } - if( 0 != (status & SAA7146_I2C_SPERR) ) { - DEB_I2C("error due to invalid start/stop condition\n"); - } - if( 0 != (status & SAA7146_I2C_DTERR) ) { - DEB_I2C("error in data transmission\n"); - } - if( 0 != (status & SAA7146_I2C_DRERR) ) { - DEB_I2C("error when receiving data\n"); - } - if( 0 != (status & SAA7146_I2C_AL) ) { - DEB_I2C("error because arbitration lost\n"); - } - - /* we handle address-errors here */ - if( 0 != (status & SAA7146_I2C_APERR) ) { - DEB_I2C("error in address phase\n"); - return -EREMOTEIO; - } - - return -EIO; - } - - /* read back data, just in case we were reading ... */ - *dword = cpu_to_le32(saa7146_read(dev, I2C_TRANSFER)); - - DEB_I2C("after: 0x%08x\n", *dword); - return 0; -} - -static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries) -{ - int i = 0, count = 0; - __le32 *buffer = dev->d_i2c.cpu_addr; - int err = 0; - int short_delay = 0; - - if (mutex_lock_interruptible(&dev->i2c_lock)) - return -ERESTARTSYS; - - for(i=0;i count ) { - err = -1; - goto out; - } - - if ( count > 3 || 0 != (SAA7146_I2C_SHORT_DELAY & dev->ext->flags) ) - short_delay = 1; - - do { - /* reset the i2c-device if necessary */ - err = saa7146_i2c_reset(dev); - if ( 0 > err ) { - DEB_I2C("could not reset i2c-device\n"); - goto out; - } - - /* write out the u32s one after another */ - for(i = 0; i < count; i++) { - err = saa7146_i2c_writeout(dev, &buffer[i], short_delay); - if ( 0 != err) { - /* this one is unsatisfying: some i2c slaves on some - dvb cards don't acknowledge correctly, so the saa7146 - thinks that an address error occurred. in that case, the - transaction should be retrying, even if an address error - occurred. analog saa7146 based cards extensively rely on - i2c address probing, however, and address errors indicate that a - device is really *not* there. retrying in that case - increases the time the device needs to probe greatly, so - it should be avoided. So we bail out in irq mode after an - address error and trust the saa7146 address error detection. */ - if (-EREMOTEIO == err && 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) - goto out; - DEB_I2C("error while sending message(s). starting again\n"); - break; - } - } - if( 0 == err ) { - err = num; - break; - } - - /* delay a bit before retrying */ - msleep(10); - - } while (err != num && retries--); - - /* quit if any error occurred */ - if (err != num) - goto out; - - /* 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; - goto out; - } - - /* return the number of delivered messages */ - DEB_I2C("transmission successful. (msg:%d)\n", err); -out: - /* another bug in revision 0: the i2c-registers get uploaded randomly by other - uploads, so we better clear them out before continuing */ - if( 0 == dev->revision ) { - __le32 zero = 0; - saa7146_i2c_reset(dev); - if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) { - pr_info("revision 0 error. this should never happen\n"); - } - } - - mutex_unlock(&dev->i2c_lock); - return err; -} - -/* utility functions */ -static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num) -{ - struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter); - struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev); - - /* use helper function to transfer data */ - return saa7146_i2c_transfer(dev, msg, num, adapter->retries); -} - - -/*****************************************************************************/ -/* i2c-adapter helper functions */ - -/* exported algorithm data */ -static struct i2c_algorithm saa7146_algo = { - .master_xfer = saa7146_i2c_xfer, - .functionality = saa7146_i2c_func, -}; - -int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate) -{ - DEB_EE("bitrate: 0x%08x\n", bitrate); - - /* enable i2c-port pins */ - saa7146_write(dev, MC1, (MASK_08 | MASK_24)); - - dev->i2c_bitrate = bitrate; - saa7146_i2c_reset(dev); - - if (i2c_adapter) { - i2c_set_adapdata(i2c_adapter, &dev->v4l2_dev); - i2c_adapter->dev.parent = &dev->pci->dev; - i2c_adapter->algo = &saa7146_algo; - i2c_adapter->algo_data = NULL; - i2c_adapter->timeout = SAA7146_I2C_TIMEOUT; - i2c_adapter->retries = SAA7146_I2C_RETRIES; - } - - return 0; -} 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 - -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 -#include -#include -#include -#include - -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, -}; -- cgit v1.2.3 From 0013ca8c52ba7bb1030ed75d6df7e58af0314018 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 14 Jun 2012 16:36:01 -0300 Subject: [media] siano: break it into common, mmc and usb siano is, in fact, 2 drivers: one for MMC and one for USB, plus a common bus-independent code. Break it accordingly. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/Kconfig | 1 + drivers/media/Makefile | 2 +- drivers/media/common/Kconfig | 1 + drivers/media/common/Makefile | 2 +- drivers/media/common/siano/Kconfig | 17 + drivers/media/common/siano/Makefile | 7 + drivers/media/common/siano/sms-cards.c | 311 ++++++ drivers/media/common/siano/sms-cards.h | 123 +++ drivers/media/common/siano/smscoreapi.c | 1637 +++++++++++++++++++++++++++++++ drivers/media/common/siano/smscoreapi.h | 775 +++++++++++++++ drivers/media/common/siano/smsdvb.c | 1078 ++++++++++++++++++++ drivers/media/common/siano/smsendian.c | 103 ++ drivers/media/common/siano/smsendian.h | 32 + drivers/media/common/siano/smsir.c | 114 +++ drivers/media/common/siano/smsir.h | 55 ++ drivers/media/mmc/Kconfig | 1 + drivers/media/mmc/Makefile | 1 + drivers/media/mmc/siano/Kconfig | 10 + drivers/media/mmc/siano/Makefile | 6 + drivers/media/mmc/siano/smssdio.c | 365 +++++++ drivers/media/usb/siano/Kconfig | 26 +- drivers/media/usb/siano/Makefile | 7 +- drivers/media/usb/siano/sms-cards.c | 311 ------ drivers/media/usb/siano/sms-cards.h | 123 --- drivers/media/usb/siano/smscoreapi.c | 1637 ------------------------------- drivers/media/usb/siano/smscoreapi.h | 775 --------------- drivers/media/usb/siano/smsdvb.c | 1078 -------------------- drivers/media/usb/siano/smsendian.c | 103 -- drivers/media/usb/siano/smsendian.h | 32 - drivers/media/usb/siano/smsir.c | 114 --- drivers/media/usb/siano/smsir.h | 55 -- drivers/media/usb/siano/smssdio.c | 365 ------- 32 files changed, 4641 insertions(+), 4626 deletions(-) create mode 100644 drivers/media/common/siano/Kconfig create mode 100644 drivers/media/common/siano/Makefile create mode 100644 drivers/media/common/siano/sms-cards.c create mode 100644 drivers/media/common/siano/sms-cards.h create mode 100644 drivers/media/common/siano/smscoreapi.c create mode 100644 drivers/media/common/siano/smscoreapi.h create mode 100644 drivers/media/common/siano/smsdvb.c create mode 100644 drivers/media/common/siano/smsendian.c create mode 100644 drivers/media/common/siano/smsendian.h create mode 100644 drivers/media/common/siano/smsir.c create mode 100644 drivers/media/common/siano/smsir.h create mode 100644 drivers/media/mmc/Kconfig create mode 100644 drivers/media/mmc/Makefile create mode 100644 drivers/media/mmc/siano/Kconfig create mode 100644 drivers/media/mmc/siano/Makefile create mode 100644 drivers/media/mmc/siano/smssdio.c delete mode 100644 drivers/media/usb/siano/sms-cards.c delete mode 100644 drivers/media/usb/siano/sms-cards.h delete mode 100644 drivers/media/usb/siano/smscoreapi.c delete mode 100644 drivers/media/usb/siano/smscoreapi.h delete mode 100644 drivers/media/usb/siano/smsdvb.c delete mode 100644 drivers/media/usb/siano/smsendian.c delete mode 100644 drivers/media/usb/siano/smsendian.h delete mode 100644 drivers/media/usb/siano/smsir.c delete mode 100644 drivers/media/usb/siano/smsir.h delete mode 100644 drivers/media/usb/siano/smssdio.c (limited to 'drivers/media/common/Makefile') diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index aae6fec3b9e1..7970c24512e9 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -163,6 +163,7 @@ source "drivers/media/radio/Kconfig" source "drivers/media/dvb-core/Kconfig" source "drivers/media/pci/Kconfig" source "drivers/media/usb/Kconfig" +source "drivers/media/mmc/Kconfig" comment "Supported FireWire (IEEE 1394) Adapters" depends on DVB_CORE && FIREWIRE diff --git a/drivers/media/Makefile b/drivers/media/Makefile index f89ccace746f..3265a9a216df 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -11,5 +11,5 @@ endif obj-y += v4l2-core/ tuners/ common/ rc/ video/ obj-$(CONFIG_VIDEO_DEV) += radio/ -obj-$(CONFIG_DVB_CORE) += dvb-core/ pci/ dvb-frontends/ usb/ +obj-$(CONFIG_DVB_CORE) += dvb-core/ pci/ dvb-frontends/ usb/ mmc/ obj-$(CONFIG_DVB_FIREDTV) += firewire/ diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index 157f191ef646..121b0110af3c 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -1,2 +1,3 @@ source "drivers/media/common/b2c2/Kconfig" source "drivers/media/common/saa7146/Kconfig" +source "drivers/media/common/siano/Kconfig" diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index f3afd83843e0..b8e2e3a33a31 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1 +1 @@ -obj-y += b2c2/ saa7146/ +obj-y += b2c2/ saa7146/ siano/ diff --git a/drivers/media/common/siano/Kconfig b/drivers/media/common/siano/Kconfig new file mode 100644 index 000000000000..425aeadfb49d --- /dev/null +++ b/drivers/media/common/siano/Kconfig @@ -0,0 +1,17 @@ +# +# Siano Mobile Silicon Digital TV device configuration +# + +config SMS_SIANO_MDTV + tristate + depends on DVB_CORE && RC_CORE && HAS_DMA + depends on SMS_USB_DRV || SMS_SDIO_DRV + default y + ---help--- + Choose Y or M here if you have MDTV receiver with a Siano chipset. + + To compile this driver as a module, choose M here + (The module will be called smsmdtv). + + Further documentation on this driver can be found on the WWW + at http://www.siano-ms.com/ diff --git a/drivers/media/common/siano/Makefile b/drivers/media/common/siano/Makefile new file mode 100644 index 000000000000..2a09279e0648 --- /dev/null +++ b/drivers/media/common/siano/Makefile @@ -0,0 +1,7 @@ +smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o smsir.o + +obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o + +ccflags-y += -Idrivers/media/dvb-core +ccflags-y += $(extra-cflags-y) $(extra-cflags-m) + diff --git a/drivers/media/common/siano/sms-cards.c b/drivers/media/common/siano/sms-cards.c new file mode 100644 index 000000000000..680c781c8dd6 --- /dev/null +++ b/drivers/media/common/siano/sms-cards.c @@ -0,0 +1,311 @@ +/* + * Card-specific functions for the Siano SMS1xxx USB dongle + * + * Copyright (c) 2008 Michael Krufky + * + * 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. + */ + +#include "sms-cards.h" +#include "smsir.h" +#include + +static int sms_dbg; +module_param_named(cards_dbg, sms_dbg, int, 0644); +MODULE_PARM_DESC(cards_dbg, "set debug level (info=1, adv=2 (or-able))"); + +static struct sms_board sms_boards[] = { + [SMS_BOARD_UNKNOWN] = { + .name = "Unknown board", + }, + [SMS1XXX_BOARD_SIANO_STELLAR] = { + .name = "Siano Stellar Digital Receiver", + .type = SMS_STELLAR, + }, + [SMS1XXX_BOARD_SIANO_NOVA_A] = { + .name = "Siano Nova A Digital Receiver", + .type = SMS_NOVA_A0, + }, + [SMS1XXX_BOARD_SIANO_NOVA_B] = { + .name = "Siano Nova B Digital Receiver", + .type = SMS_NOVA_B0, + }, + [SMS1XXX_BOARD_SIANO_VEGA] = { + .name = "Siano Vega Digital Receiver", + .type = SMS_VEGA, + }, + [SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT] = { + .name = "Hauppauge Catamount", + .type = SMS_STELLAR, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-stellar-dvbt-01.fw", + }, + [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A] = { + .name = "Hauppauge Okemo-A", + .type = SMS_NOVA_A0, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-a-dvbt-01.fw", + }, + [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B] = { + .name = "Hauppauge Okemo-B", + .type = SMS_NOVA_B0, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-b-dvbt-01.fw", + }, + [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = { + .name = "Hauppauge WinTV MiniStick", + .type = SMS_NOVA_B0, + .fw[DEVICE_MODE_ISDBT_BDA] = "sms1xxx-hcw-55xxx-isdbt-02.fw", + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", + .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] = "sms1xxx-hcw-55xxx-dvbt-02.fw", + .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", + .type = SMS_NOVA_B0, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", + .lna_ctrl = -1, + }, + [SMS1XXX_BOARD_SIANO_NICE] = { + /* 11 */ + .name = "Siano Nice Digital Receiver", + .type = SMS_NOVA_B0, + }, + [SMS1XXX_BOARD_SIANO_VENICE] = { + /* 12 */ + .name = "Siano Venice Digital Receiver", + .type = SMS_VEGA, + }, +}; + +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_gpio_config *pGpioConfig) { + pGpioConfig->Direction = SMS_GPIO_DIRECTION_OUTPUT; + pGpioConfig->InputCharacteristics = + SMS_GPIO_INPUT_CHARACTERISTICS_NORMAL; + pGpioConfig->OutputDriving = SMS_GPIO_OUTPUT_DRIVING_4mA; + pGpioConfig->OutputSlewRate = SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS; + pGpioConfig->PullUpDown = SMS_GPIO_PULL_UP_DOWN_NONE; +} + +int sms_board_event(struct smscore_device_t *coredev, + enum SMS_BOARD_EVENTS gevent) { + struct smscore_gpio_config MyGpioConfig; + + sms_gpio_assign_11xx_default_led_config(&MyGpioConfig); + + 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: + sms_err("Unknown SMS board event"); + 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_OUTPUTSLEWRATE_FAST, + .outputdriving = SMS_GPIO_OUTPUTDRIVING_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); + + /* dont 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); + + sms_debug("%s: LNA %s", __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) +{ + switch (id) { + case SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT: + case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A: + case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B: + case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: + case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: + case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: + request_module("smsdvb"); + break; + default: + /* do nothing */ + break; + } + return 0; +} +EXPORT_SYMBOL_GPL(sms_board_load_modules); diff --git a/drivers/media/common/siano/sms-cards.h b/drivers/media/common/siano/sms-cards.h new file mode 100644 index 000000000000..d8cdf756f7cf --- /dev/null +++ b/drivers/media/common/siano/sms-cards.h @@ -0,0 +1,123 @@ +/* + * Card-specific functions for the Siano SMS1xxx USB dongle + * + * Copyright (c) 2008 Michael Krufky + * + * 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 +#include "smscoreapi.h" +#include "smsir.h" + +#define SMS_BOARD_UNKNOWN 0 +#define SMS1XXX_BOARD_SIANO_STELLAR 1 +#define SMS1XXX_BOARD_SIANO_NOVA_A 2 +#define SMS1XXX_BOARD_SIANO_NOVA_B 3 +#define SMS1XXX_BOARD_SIANO_VEGA 4 +#define SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT 5 +#define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A 6 +#define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B 7 +#define SMS1XXX_BOARD_HAUPPAUGE_WINDHAM 8 +#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD 9 +#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 10 +#define SMS1XXX_BOARD_SIANO_NICE 11 +#define SMS1XXX_BOARD_SIANO_VENICE 12 + +struct sms_board_gpio_cfg { + int lna_vhf_exist; + int lna_vhf_ctrl; + int lna_uhf_exist; + int lna_uhf_ctrl; + int lna_uhf_d_ctrl; + int lna_sband_exist; + int lna_sband_ctrl; + int lna_sband_d_ctrl; + int foreign_lna0_ctrl; + int foreign_lna1_ctrl; + int foreign_lna2_ctrl; + int rf_switch_vhf; + int rf_switch_uhf; + int rf_switch_sband; + int leds_power; + int led0; + int led1; + int led2; + int led3; + int led4; + int ir; + int eeprom_wp; + int mrc_sense; + int mrc_pdn_resetn; + int mrc_gp0; /* mrcs spi int */ + int mrc_gp1; + int mrc_gp2; + int mrc_gp3; + int mrc_gp4; + int host_spi_gsp_ts_int; +}; + +struct sms_board { + enum sms_device_type_st type; + char *name, *fw[DEVICE_MODE_MAX]; + struct sms_board_gpio_cfg board_cfg; + char *rc_codes; /* Name of IR codes table */ + + /* gpios */ + int led_power, led_hi, led_lo, lna_ctrl, rf_switch; +}; + +struct sms_board *sms_get_board(unsigned id); + +extern struct smscore_device_t *coredev; + +enum SMS_BOARD_EVENTS { + BOARD_EVENT_POWER_INIT, + BOARD_EVENT_POWER_SUSPEND, + BOARD_EVENT_POWER_RESUME, + BOARD_EVENT_BIND, + BOARD_EVENT_SCAN_PROG, + BOARD_EVENT_SCAN_COMP, + BOARD_EVENT_EMERGENCY_WARNING_SIGNAL, + BOARD_EVENT_FE_LOCK, + BOARD_EVENT_FE_UNLOCK, + BOARD_EVENT_DEMOD_LOCK, + BOARD_EVENT_DEMOD_UNLOCK, + BOARD_EVENT_RECEPTION_MAX_4, + BOARD_EVENT_RECEPTION_3, + BOARD_EVENT_RECEPTION_2, + BOARD_EVENT_RECEPTION_1, + BOARD_EVENT_RECEPTION_LOST_0, + BOARD_EVENT_MULTIPLEX_OK, + BOARD_EVENT_MULTIPLEX_ERRORS +}; + +int sms_board_event(struct smscore_device_t *coredev, + enum SMS_BOARD_EVENTS gevent); + +int sms_board_setup(struct smscore_device_t *coredev); + +#define SMS_LED_OFF 0 +#define SMS_LED_LO 1 +#define SMS_LED_HI 2 +int sms_board_led_feedback(struct smscore_device_t *coredev, int led); +int sms_board_power(struct smscore_device_t *coredev, int onoff); +int sms_board_lna_control(struct smscore_device_t *coredev, int onoff); + +extern int sms_board_load_modules(int id); + +#endif /* __SMS_CARDS_H__ */ diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c new file mode 100644 index 000000000000..9cc55546cc30 --- /dev/null +++ b/drivers/media/common/siano/smscoreapi.c @@ -0,0 +1,1637 @@ +/* + * 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. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "smscoreapi.h" +#include "sms-cards.h" +#include "smsir.h" +#include "smsendian.h" + +static int sms_dbg; +module_param_named(debug, sms_dbg, int, 0644); +MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); + +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; +}; + +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 struct mutex g_smscore_deviceslock; + +static struct list_head g_smscore_registry; +static struct mutex g_smscore_registrylock; + +static int default_mode = 4; + +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; + + kmutex_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 (!strcmp(entry->devpath, devpath)) { + kmutex_unlock(&g_smscore_registrylock); + return entry; + } + } + entry = kmalloc(sizeof(struct smscore_registry_entry_t), GFP_KERNEL); + if (entry) { + entry->mode = default_mode; + strcpy(entry->devpath, devpath); + list_add(&entry->entry, &g_smscore_registry); + } else + sms_err("failed to create smscore_registry."); + kmutex_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 + sms_err("No registry found."); + + 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 + sms_err("No registry found."); + + return -1; +} + +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 + sms_err("No registry found."); +} + +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 + sms_err("No registry found."); +} + + +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; + + kmutex_lock(&g_smscore_deviceslock); + + notifyee = kmalloc(sizeof(struct smscore_device_notifyee_t), + 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(¬ifyee->entry, &g_smscore_notifyees); + } else + kfree(notifyee); + } else + rc = -ENOMEM; + + kmutex_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; + + kmutex_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(¬ifyee->entry); + kfree(notifyee); + } + } + + kmutex_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 = + kmalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL); + if (!cb) { + sms_info("kmalloc(...) failed"); + return NULL; + } + + cb->p = buffer; + cb->offset_in_common = buffer - (u8 *) common_buffer; + 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) +{ + struct smscore_device_t *dev; + u8 *buffer; + + dev = kzalloc(sizeof(struct smscore_device_t), GFP_KERNEL); + if (!dev) { + sms_info("kzalloc(...) failed"); + return -ENOMEM; + } + + /* 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->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; + dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size, + &dev->common_buffer_phys, + GFP_KERNEL | GFP_DMA); + if (!dev->common_buffer) { + smscore_unregister_device(dev); + return -ENOMEM; + } + + /* prepare dma buffers */ + for (buffer = dev->common_buffer; + dev->num_buffers < params->num_buffers; + dev->num_buffers++, buffer += params->buffer_size) { + struct smscore_buffer_t *cb = + smscore_createbuffer(buffer, dev->common_buffer, + dev->common_buffer_phys); + if (!cb) { + smscore_unregister_device(dev); + return -ENOMEM; + } + + smscore_putbuffer(dev, cb); + } + + sms_info("allocated %d buffers", dev->num_buffers); + + dev->mode = DEVICE_MODE_NONE; + dev->context = params->context; + dev->device = params->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; + strcpy(dev->devpath, params->devpath); + + smscore_registry_settype(dev->devpath, params->device_type); + + /* add device to devices list */ + kmutex_lock(&g_smscore_deviceslock); + list_add(&dev->entry, &g_smscore_devices); + kmutex_unlock(&g_smscore_deviceslock); + + *coredev = dev; + + sms_info("device %p created", 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 = coredev->sendrequest_handler(coredev->context, buffer, size); + if (rc < 0) { + sms_info("sendrequest returned error %d", 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 */ + sms_info("IR loading"); + rc = sms_ir_init(coredev); + + if (rc != 0) + sms_err("Error initialization DTV IR sub-module"); + else { + buffer = kmalloc(sizeof(struct SmsMsgData_ST2) + + SMS_DMA_ALIGNMENT, + GFP_KERNEL | GFP_DMA); + if (buffer) { + struct SmsMsgData_ST2 *msg = + (struct SmsMsgData_ST2 *) + SMS_ALIGN_ADDRESS(buffer); + + SMS_INIT_MSG(&msg->xMsgHeader, + MSG_SMS_START_IR_REQ, + sizeof(struct SmsMsgData_ST2)); + msg->msgData[0] = coredev->ir.controller; + msg->msgData[1] = coredev->ir.timeout; + + smsendian_handle_tx_message( + (struct SmsMsgHdr_ST2 *)msg); + rc = smscore_sendrequest_and_wait(coredev, msg, + msg->xMsgHeader. msgLength, + &coredev->ir_init_done); + + kfree(buffer); + } else + sms_err + ("Sending IR initialization message failed"); + } + } else + sms_info("IR port has not been detected"); + + 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 = smscore_set_device_mode( + coredev, smscore_registry_getmode(coredev->devpath)); + if (rc < 0) { + sms_info("set device mode faile , rc %d", rc); + return rc; + } + + kmutex_lock(&g_smscore_deviceslock); + + rc = smscore_notify_callbacks(coredev, coredev->device, 1); + smscore_init_ir(coredev); + + sms_info("device %p started, rc %d", coredev, rc); + + kmutex_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 SmsFirmware_ST *firmware = (struct SmsFirmware_ST *) buffer; + struct SmsMsgHdr_ST *msg; + u32 mem_address; + u8 *payload = firmware->Payload; + int rc = 0; + firmware->StartAddress = le32_to_cpu(firmware->StartAddress); + firmware->Length = le32_to_cpu(firmware->Length); + + mem_address = firmware->StartAddress; + + sms_info("loading FW to addr 0x%x size %d", + 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 | GFP_DMA); + if (!msg) + return -ENOMEM; + + if (coredev->mode != DEVICE_MODE_NONE) { + sms_debug("sending reload command."); + SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ, + sizeof(struct SmsMsgHdr_ST)); + rc = smscore_sendrequest_and_wait(coredev, msg, + msg->msgLength, + &coredev->reload_start_done); + mem_address = *(u32 *) &payload[20]; + } + + while (size && rc >= 0) { + struct SmsDataDownload_ST *DataMsg = + (struct SmsDataDownload_ST *) msg; + int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE); + + SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ, + (u16)(sizeof(struct SmsMsgHdr_ST) + + sizeof(u32) + payload_size)); + + DataMsg->MemAddr = mem_address; + memcpy(DataMsg->Payload, payload, payload_size); + + if ((coredev->device_flags & SMS_ROM_NO_RESPONSE) && + (coredev->mode == DEVICE_MODE_NONE)) + rc = coredev->sendrequest_handler( + coredev->context, DataMsg, + DataMsg->xMsgHeader.msgLength); + else + rc = smscore_sendrequest_and_wait( + coredev, DataMsg, + DataMsg->xMsgHeader.msgLength, + &coredev->data_download_done); + + payload += payload_size; + size -= payload_size; + mem_address += payload_size; + } + + if (rc >= 0) { + if (coredev->mode == DEVICE_MODE_NONE) { + struct SmsMsgData_ST *TriggerMsg = + (struct SmsMsgData_ST *) msg; + + SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ, + sizeof(struct SmsMsgHdr_ST) + + sizeof(u32) * 5); + + TriggerMsg->msgData[0] = firmware->StartAddress; + /* Entry point */ + TriggerMsg->msgData[1] = 5; /* Priority */ + TriggerMsg->msgData[2] = 0x200; /* Stack size */ + TriggerMsg->msgData[3] = 0; /* Parameter */ + TriggerMsg->msgData[4] = 4; /* Task ID */ + + if (coredev->device_flags & SMS_ROM_NO_RESPONSE) { + rc = coredev->sendrequest_handler( + coredev->context, TriggerMsg, + TriggerMsg->xMsgHeader.msgLength); + msleep(100); + } else + rc = smscore_sendrequest_and_wait( + coredev, TriggerMsg, + TriggerMsg->xMsgHeader.msgLength, + &coredev->trigger_done); + } else { + SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ, + sizeof(struct SmsMsgHdr_ST)); + + rc = coredev->sendrequest_handler(coredev->context, + msg, msg->msgLength); + } + msleep(500); + } + + sms_debug("rc=%d, postload=%p ", rc, + coredev->postload_handler); + + kfree(msg); + + return ((rc >= 0) && coredev->postload_handler) ? + coredev->postload_handler(coredev->context) : + rc; +} + +/** + * 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, + char *filename, + loadfirmware_t loadfirmware_handler) +{ + int rc = -ENOENT; + const struct firmware *fw; + u8 *fw_buffer; + + if (loadfirmware_handler == NULL && !(coredev->device_flags & + SMS_DEVICE_FAMILY2)) + return -EINVAL; + + rc = request_firmware(&fw, filename, coredev->device); + if (rc < 0) { + sms_info("failed to open \"%s\"", filename); + return rc; + } + sms_info("read FW %s, size=%zd", filename, fw->size); + fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT), + GFP_KERNEL | GFP_DMA); + if (fw_buffer) { + memcpy(fw_buffer, fw->data, fw->size); + + rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ? + smscore_load_firmware_family2(coredev, + fw_buffer, + fw->size) : + loadfirmware_handler(coredev->context, + fw_buffer, fw->size); + + kfree(fw_buffer); + } else { + sms_info("failed to allocate firmware buffer"); + rc = -ENOMEM; + } + + 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; + + kmutex_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) { + sms_info("exiting although " + "not all buffers released."); + break; + } + + sms_info("waiting for %d buffer(s)", + coredev->num_buffers - num_buffers); + msleep(100); + } + + sms_info("freed %d buffers", num_buffers); + + if (coredev->common_buffer) + dma_free_coherent(NULL, coredev->common_buffer_size, + coredev->common_buffer, coredev->common_buffer_phys); + + if (coredev->fw_buf != NULL) + kfree(coredev->fw_buf); + + list_del(&coredev->entry); + kfree(coredev); + + kmutex_unlock(&g_smscore_deviceslock); + + sms_info("device %p destroyed", coredev); +} +EXPORT_SYMBOL_GPL(smscore_unregister_device); + +static int smscore_detect_mode(struct smscore_device_t *coredev) +{ + void *buffer = kmalloc(sizeof(struct SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT, + GFP_KERNEL | GFP_DMA); + struct SmsMsgHdr_ST *msg = + (struct SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer); + int rc; + + if (!buffer) + return -ENOMEM; + + SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ, + sizeof(struct SmsMsgHdr_ST)); + + rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, + &coredev->version_ex_done); + if (rc == -ETIME) { + sms_err("MSG_SMS_GET_VERSION_EX_REQ failed first try"); + + if (wait_for_completion_timeout(&coredev->resume_done, + msecs_to_jiffies(5000))) { + rc = smscore_sendrequest_and_wait( + coredev, msg, msg->msgLength, + &coredev->version_ex_done); + if (rc < 0) + sms_err("MSG_SMS_GET_VERSION_EX_REQ failed " + "second try, rc %d", rc); + } else + rc = -ETIME; + } + + kfree(buffer); + + return rc; +} + +static char *smscore_fw_lkup[][SMS_NUM_OF_DEVICE_TYPES] = { + /*Stellar NOVA A0 Nova B0 VEGA*/ + /*DVBT*/ + {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, + /*DVBH*/ + {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, + /*TDMB*/ + {"none", "tdmb_nova_12mhz.inp", "tdmb_nova_12mhz_b0.inp", "none"}, + /*DABIP*/ + {"none", "none", "none", "none"}, + /*BDA*/ + {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, + /*ISDBT*/ + {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, + /*ISDBTBDA*/ + {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, + /*CMMB*/ + {"none", "none", "none", "cmmb_vega_12mhz.inp"} +}; + +static inline char *sms_get_fw_name(struct smscore_device_t *coredev, + int mode, enum sms_device_type_st type) +{ + char **fw = sms_get_board(smscore_get_board_id(coredev))->fw; + return (fw && fw[mode]) ? fw[mode] : smscore_fw_lkup[mode][type]; +} + +/** + * 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) +{ + void *buffer; + int rc = 0; + enum sms_device_type_st type; + + sms_debug("set device mode to %d", mode); + if (coredev->device_flags & SMS_DEVICE_FAMILY2) { + if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) { + sms_err("invalid mode specified %d", 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) { + sms_err("mode detect failed %d", rc); + return rc; + } + } + + if (coredev->mode == mode) { + sms_info("device mode %d already set", mode); + return 0; + } + + if (!(coredev->modes_supported & (1 << mode))) { + char *fw_filename; + + type = smscore_registry_gettype(coredev->devpath); + fw_filename = sms_get_fw_name(coredev, mode, type); + + rc = smscore_load_firmware_from_file(coredev, + fw_filename, NULL); + if (rc < 0) { + sms_warn("error %d loading firmware: %s, " + "trying again with default firmware", + rc, fw_filename); + + /* try again with the default firmware */ + fw_filename = smscore_fw_lkup[mode][type]; + rc = smscore_load_firmware_from_file(coredev, + fw_filename, NULL); + + if (rc < 0) { + sms_warn("error %d loading " + "firmware: %s", rc, + fw_filename); + return rc; + } + } + sms_log("firmware download success: %s", fw_filename); + } else + sms_info("mode %d supported by running " + "firmware", mode); + + buffer = kmalloc(sizeof(struct SmsMsgData_ST) + + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); + if (buffer) { + struct SmsMsgData_ST *msg = + (struct SmsMsgData_ST *) + SMS_ALIGN_ADDRESS(buffer); + + SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ, + sizeof(struct SmsMsgData_ST)); + msg->msgData[0] = mode; + + rc = smscore_sendrequest_and_wait( + coredev, msg, msg->xMsgHeader.msgLength, + &coredev->init_device_done); + + kfree(buffer); + } else { + sms_err("Could not allocate buffer for " + "init device message."); + rc = -ENOMEM; + } + } else { + if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) { + sms_err("invalid mode specified %d", 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) { + coredev->mode = mode; + coredev->device_flags &= ~SMS_DEVICE_NOT_READY; + } + + if (rc < 0) + sms_err("return error code %d.", rc); + 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 SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) ((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) { + sms_debug("\ndata rate %d bytes/secs", + (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->msgType == MSG_SMS_HO_PER_SLICES_IND) || + (phdr->msgType == MSG_SMS_TRANSMISSION_IND)) { + if (coredev->mode == DEVICE_MODE_DVBT_BDA) + phdr->msgDstId = DVBT_BDA_CONTROL_MSG_ID; + } + + + client = smscore_find_client(coredev, phdr->msgType, phdr->msgDstId); + + /* 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->msgType) { + case MSG_SMS_GET_VERSION_EX_RES: + { + struct SmsVersionRes_ST *ver = + (struct SmsVersionRes_ST *) phdr; + sms_debug("MSG_SMS_GET_VERSION_EX_RES " + "id %d prots 0x%x ver %d.%d", + ver->FirmwareId, ver->SupportedProtocols, + ver->RomVersionMajor, ver->RomVersionMinor); + + coredev->mode = ver->FirmwareId == 255 ? + DEVICE_MODE_NONE : ver->FirmwareId; + coredev->modes_supported = ver->SupportedProtocols; + + complete(&coredev->version_ex_done); + break; + } + case MSG_SMS_INIT_DEVICE_RES: + sms_debug("MSG_SMS_INIT_DEVICE_RES"); + complete(&coredev->init_device_done); + break; + case MSG_SW_RELOAD_START_RES: + sms_debug("MSG_SW_RELOAD_START_RES"); + complete(&coredev->reload_start_done); + break; + case MSG_SMS_DATA_DOWNLOAD_RES: + complete(&coredev->data_download_done); + break; + case MSG_SW_RELOAD_EXEC_RES: + sms_debug("MSG_SW_RELOAD_EXEC_RES"); + break; + case MSG_SMS_SWDOWNLOAD_TRIGGER_RES: + sms_debug("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: + sms_debug("MSG_SMS_GPIO_CONFIG_EX_RES"); + complete(&coredev->gpio_configuration_done); + break; + case MSG_SMS_GPIO_SET_LEVEL_RES: + sms_debug("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]; + sms_debug("MSG_SMS_GPIO_GET_LEVEL_RES gpio level %d", + 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 SmsMsgHdr_ST)), + (int)phdr->msgLength + - sizeof(struct SmsMsgHdr_ST)); + break; + + default: + 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. + */ + +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) { + sms_err("bad parameter."); + return -EINVAL; + } + registered_client = smscore_find_client(coredev, data_type, id); + if (registered_client == client) + return 0; + + if (registered_client) { + sms_err("The msg ID already registered to another client."); + return -EEXIST; + } + listentry = kzalloc(sizeof(struct smscore_idlist_t), GFP_KERNEL); + if (!listentry) { + sms_err("Can't allocate memory for client id."); + 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)) { + sms_err("Client already exist."); + return -EEXIST; + } + + newclient = kzalloc(sizeof(struct smscore_client_t), GFP_KERNEL); + if (!newclient) { + sms_err("Failed to allocate memory for client."); + 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; + sms_debug("%p %d %d", 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); + } + + sms_info("%p", 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 SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) buffer; + int rc; + + if (client == NULL) { + sms_err("Got NULL client"); + return -EINVAL; + } + + coredev = client->coredev; + + /* check that no other channel with same id exists */ + if (coredev == NULL) { + sms_err("Got NULL coredev"); + return -EINVAL; + } + + rc = smscore_validate_client(client->coredev, client, 0, + phdr->msgSrcId); + 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 SmsMsgHdr_ST hdr; + u32 data[6]; + } msg; + + if (coredev->device_flags & SMS_DEVICE_FAMILY2) { + msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + msg.hdr.msgDstId = HIF_TASK; + msg.hdr.msgFlags = 0; + msg.hdr.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; + msg.hdr.msgLength = 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_16mA: + msg.data[3] = 7; /* Nova - 16mA */ + break; + case SMS_GPIO_OUTPUTDRIVING_12mA: + msg.data[3] = 5; /* Nova - 11mA */ + break; + case SMS_GPIO_OUTPUTDRIVING_8mA: + msg.data[3] = 3; /* Nova - 7mA */ + break; + case SMS_GPIO_OUTPUTDRIVING_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 SmsMsgHdr_ST hdr; + u32 data[3]; + } msg; + + if (pin > MAX_GPIO_PIN_NUMBER) + return -EINVAL; + + msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + msg.hdr.msgDstId = HIF_TASK; + msg.hdr.msgFlags = 0; + msg.hdr.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; + msg.hdr.msgLength = 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 GetGpioPinParams(u32 PinNum, u32 *pTranslatedPinNum, + u32 *pGroupNum, u32 *pGroupCfg) { + + *pGroupCfg = 1; + + if (PinNum <= 1) { + *pTranslatedPinNum = 0; + *pGroupNum = 9; + *pGroupCfg = 2; + } else if (PinNum >= 2 && PinNum <= 6) { + *pTranslatedPinNum = 2; + *pGroupNum = 0; + *pGroupCfg = 2; + } else if (PinNum >= 7 && PinNum <= 11) { + *pTranslatedPinNum = 7; + *pGroupNum = 1; + } else if (PinNum >= 12 && PinNum <= 15) { + *pTranslatedPinNum = 12; + *pGroupNum = 2; + *pGroupCfg = 3; + } else if (PinNum == 16) { + *pTranslatedPinNum = 16; + *pGroupNum = 23; + } else if (PinNum >= 17 && PinNum <= 24) { + *pTranslatedPinNum = 17; + *pGroupNum = 3; + } else if (PinNum == 25) { + *pTranslatedPinNum = 25; + *pGroupNum = 6; + } else if (PinNum >= 26 && PinNum <= 28) { + *pTranslatedPinNum = 26; + *pGroupNum = 4; + } else if (PinNum == 29) { + *pTranslatedPinNum = 29; + *pGroupNum = 5; + *pGroupCfg = 2; + } else if (PinNum == 30) { + *pTranslatedPinNum = 30; + *pGroupNum = 8; + } else if (PinNum == 31) { + *pTranslatedPinNum = 31; + *pGroupNum = 17; + } else + return -1; + + *pGroupCfg <<= 24; + + return 0; +} + +int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, + struct smscore_gpio_config *pGpioConfig) { + + u32 totalLen; + u32 TranslatedPinNum = 0; + u32 GroupNum = 0; + u32 ElectricChar; + u32 groupCfg; + void *buffer; + int rc; + + struct SetGpioMsg { + struct SmsMsgHdr_ST xMsgHeader; + u32 msgData[6]; + } *pMsg; + + + if (PinNum > MAX_GPIO_PIN_NUMBER) + return -EINVAL; + + if (pGpioConfig == NULL) + return -EINVAL; + + totalLen = sizeof(struct SmsMsgHdr_ST) + (sizeof(u32) * 6); + + buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, + GFP_KERNEL | GFP_DMA); + if (!buffer) + return -ENOMEM; + + pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); + + pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + pMsg->xMsgHeader.msgDstId = HIF_TASK; + pMsg->xMsgHeader.msgFlags = 0; + pMsg->xMsgHeader.msgLength = (u16) totalLen; + pMsg->msgData[0] = PinNum; + + if (!(coredev->device_flags & SMS_DEVICE_FAMILY2)) { + pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_REQ; + if (GetGpioPinParams(PinNum, &TranslatedPinNum, &GroupNum, + &groupCfg) != 0) { + rc = -EINVAL; + goto free; + } + + pMsg->msgData[1] = TranslatedPinNum; + pMsg->msgData[2] = GroupNum; + ElectricChar = (pGpioConfig->PullUpDown) + | (pGpioConfig->InputCharacteristics << 2) + | (pGpioConfig->OutputSlewRate << 3) + | (pGpioConfig->OutputDriving << 4); + pMsg->msgData[3] = ElectricChar; + pMsg->msgData[4] = pGpioConfig->Direction; + pMsg->msgData[5] = groupCfg; + } else { + pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; + pMsg->msgData[1] = pGpioConfig->PullUpDown; + pMsg->msgData[2] = pGpioConfig->OutputSlewRate; + pMsg->msgData[3] = pGpioConfig->OutputDriving; + pMsg->msgData[4] = pGpioConfig->Direction; + pMsg->msgData[5] = 0; + } + + smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); + rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, + &coredev->gpio_configuration_done); + + if (rc != 0) { + if (rc == -ETIME) + sms_err("smscore_gpio_configure timeout"); + else + sms_err("smscore_gpio_configure error"); + } +free: + kfree(buffer); + + return rc; +} + +int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, + u8 NewLevel) { + + u32 totalLen; + int rc; + void *buffer; + + struct SetGpioMsg { + struct SmsMsgHdr_ST xMsgHeader; + u32 msgData[3]; /* keep it 3 ! */ + } *pMsg; + + if ((NewLevel > 1) || (PinNum > MAX_GPIO_PIN_NUMBER)) + return -EINVAL; + + totalLen = sizeof(struct SmsMsgHdr_ST) + + (3 * sizeof(u32)); /* keep it 3 ! */ + + buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, + GFP_KERNEL | GFP_DMA); + if (!buffer) + return -ENOMEM; + + pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); + + pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + pMsg->xMsgHeader.msgDstId = HIF_TASK; + pMsg->xMsgHeader.msgFlags = 0; + pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; + pMsg->xMsgHeader.msgLength = (u16) totalLen; + pMsg->msgData[0] = PinNum; + pMsg->msgData[1] = NewLevel; + + /* Send message to SMS */ + smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); + rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, + &coredev->gpio_set_level_done); + + if (rc != 0) { + if (rc == -ETIME) + sms_err("smscore_gpio_set_level timeout"); + else + sms_err("smscore_gpio_set_level error"); + } + kfree(buffer); + + return rc; +} + +int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum, + u8 *level) { + + u32 totalLen; + int rc; + void *buffer; + + struct SetGpioMsg { + struct SmsMsgHdr_ST xMsgHeader; + u32 msgData[2]; + } *pMsg; + + + if (PinNum > MAX_GPIO_PIN_NUMBER) + return -EINVAL; + + totalLen = sizeof(struct SmsMsgHdr_ST) + (2 * sizeof(u32)); + + buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, + GFP_KERNEL | GFP_DMA); + if (!buffer) + return -ENOMEM; + + pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); + + pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + pMsg->xMsgHeader.msgDstId = HIF_TASK; + pMsg->xMsgHeader.msgFlags = 0; + pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_GET_LEVEL_REQ; + pMsg->xMsgHeader.msgLength = (u16) totalLen; + pMsg->msgData[0] = PinNum; + pMsg->msgData[1] = 0; + + /* Send message to SMS */ + smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); + rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, + &coredev->gpio_get_level_done); + + if (rc != 0) { + if (rc == -ETIME) + sms_err("smscore_gpio_get_level timeout"); + else + sms_err("smscore_gpio_get_level error"); + } + 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) +{ + int rc = 0; + + INIT_LIST_HEAD(&g_smscore_notifyees); + INIT_LIST_HEAD(&g_smscore_devices); + kmutex_init(&g_smscore_deviceslock); + + INIT_LIST_HEAD(&g_smscore_registry); + kmutex_init(&g_smscore_registrylock); + + return rc; +} + +static void __exit smscore_module_exit(void) +{ + kmutex_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(¬ifyee->entry); + kfree(notifyee); + } + kmutex_unlock(&g_smscore_deviceslock); + + kmutex_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); + } + kmutex_unlock(&g_smscore_registrylock); + + sms_debug(""); +} + +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"); diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h new file mode 100644 index 000000000000..c592ae090397 --- /dev/null +++ b/drivers/media/common/siano/smscoreapi.h @@ -0,0 +1,775 @@ +/**************************************************************** + +Siano Mobile Silicon, Inc. +MDTV receiver kernel modules. +Copyright (C) 2006-2008, Uri Shkolnik, Anatoly Greenblat + +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, see . + +****************************************************************/ + +#ifndef __SMS_CORE_API_H__ +#define __SMS_CORE_API_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "smsir.h" + +#define kmutex_init(_p_) mutex_init(_p_) +#define kmutex_lock(_p_) mutex_lock(_p_) +#define kmutex_trylock(_p_) mutex_trylock(_p_) +#define kmutex_unlock(_p_) mutex_unlock(_p_) + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#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_FAMILY2 1 +#define SMS_ROM_NO_RESPONSE 2 +#define SMS_DEVICE_NOT_READY 0x8000000 + +enum sms_device_type_st { + SMS_STELLAR = 0, + SMS_NOVA_A0, + SMS_NOVA_B0, + SMS_VEGA, + SMS_NUM_OF_DEVICE_TYPES +}; + +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 (*loadfirmware_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; + + 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; + + 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; + + /* host <--> device messages */ + struct completion version_ex_done, data_download_done, trigger_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; + + /* Infrared (IR) */ + struct ir_t ir; + + int led_state; +}; + +/* GPIO definitions for antenna frequency domain control (SMS8021) */ +#define SMS_ANTENNA_GPIO_0 1 +#define SMS_ANTENNA_GPIO_1 0 + +#define BW_8_MHZ 0 +#define BW_7_MHZ 1 +#define BW_6_MHZ 2 +#define BW_5_MHZ 3 +#define BW_ISDBT_1SEG 4 +#define BW_ISDBT_3SEG 5 + +#define MSG_HDR_FLAG_SPLIT_MSG 4 + +#define MAX_GPIO_PIN_NUMBER 31 + +#define HIF_TASK 11 +#define SMS_HOST_LIB 150 +#define DVBT_BDA_CONTROL_MSG_ID 201 + +#define SMS_MAX_PAYLOAD_SIZE 240 +#define SMS_TUNE_TIMEOUT 500 + +#define MSG_SMS_GPIO_CONFIG_REQ 507 +#define MSG_SMS_GPIO_CONFIG_RES 508 +#define MSG_SMS_GPIO_SET_LEVEL_REQ 509 +#define MSG_SMS_GPIO_SET_LEVEL_RES 510 +#define MSG_SMS_GPIO_GET_LEVEL_REQ 511 +#define MSG_SMS_GPIO_GET_LEVEL_RES 512 +#define MSG_SMS_RF_TUNE_REQ 561 +#define MSG_SMS_RF_TUNE_RES 562 +#define MSG_SMS_INIT_DEVICE_REQ 578 +#define MSG_SMS_INIT_DEVICE_RES 579 +#define MSG_SMS_ADD_PID_FILTER_REQ 601 +#define MSG_SMS_ADD_PID_FILTER_RES 602 +#define MSG_SMS_REMOVE_PID_FILTER_REQ 603 +#define MSG_SMS_REMOVE_PID_FILTER_RES 604 +#define MSG_SMS_DAB_CHANNEL 607 +#define MSG_SMS_GET_PID_FILTER_LIST_REQ 608 +#define MSG_SMS_GET_PID_FILTER_LIST_RES 609 +#define MSG_SMS_GET_STATISTICS_RES 616 +#define MSG_SMS_GET_STATISTICS_REQ 615 +#define MSG_SMS_HO_PER_SLICES_IND 630 +#define MSG_SMS_SET_ANTENNA_CONFIG_REQ 651 +#define MSG_SMS_SET_ANTENNA_CONFIG_RES 652 +#define MSG_SMS_SLEEP_RESUME_COMP_IND 655 +#define MSG_SMS_DATA_DOWNLOAD_REQ 660 +#define MSG_SMS_DATA_DOWNLOAD_RES 661 +#define MSG_SMS_SWDOWNLOAD_TRIGGER_REQ 664 +#define MSG_SMS_SWDOWNLOAD_TRIGGER_RES 665 +#define MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ 666 +#define MSG_SMS_SWDOWNLOAD_BACKDOOR_RES 667 +#define MSG_SMS_GET_VERSION_EX_REQ 668 +#define MSG_SMS_GET_VERSION_EX_RES 669 +#define MSG_SMS_SET_CLOCK_OUTPUT_REQ 670 +#define MSG_SMS_I2C_SET_FREQ_REQ 685 +#define MSG_SMS_GENERIC_I2C_REQ 687 +#define MSG_SMS_GENERIC_I2C_RES 688 +#define MSG_SMS_DVBT_BDA_DATA 693 +#define MSG_SW_RELOAD_REQ 697 +#define MSG_SMS_DATA_MSG 699 +#define MSG_SW_RELOAD_START_REQ 702 +#define MSG_SW_RELOAD_START_RES 703 +#define MSG_SW_RELOAD_EXEC_REQ 704 +#define MSG_SW_RELOAD_EXEC_RES 705 +#define MSG_SMS_SPI_INT_LINE_SET_REQ 710 +#define MSG_SMS_GPIO_CONFIG_EX_REQ 712 +#define MSG_SMS_GPIO_CONFIG_EX_RES 713 +#define MSG_SMS_ISDBT_TUNE_REQ 776 +#define MSG_SMS_ISDBT_TUNE_RES 777 +#define MSG_SMS_TRANSMISSION_IND 782 +#define MSG_SMS_START_IR_REQ 800 +#define MSG_SMS_START_IR_RES 801 +#define MSG_SMS_IR_SAMPLES_IND 802 +#define MSG_SMS_SIGNAL_DETECTED_IND 827 +#define MSG_SMS_NO_SIGNAL_IND 828 + +#define SMS_INIT_MSG_EX(ptr, type, src, dst, len) do { \ + (ptr)->msgType = type; (ptr)->msgSrcId = src; (ptr)->msgDstId = dst; \ + (ptr)->msgLength = len; (ptr)->msgFlags = 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_MAX, +}; + +struct SmsMsgHdr_ST { + u16 msgType; + u8 msgSrcId; + u8 msgDstId; + u16 msgLength; /* Length of entire message, including header */ + u16 msgFlags; +}; + +struct SmsMsgData_ST { + struct SmsMsgHdr_ST xMsgHeader; + u32 msgData[1]; +}; + +struct SmsMsgData_ST2 { + struct SmsMsgHdr_ST xMsgHeader; + u32 msgData[2]; +}; + +struct SmsDataDownload_ST { + struct SmsMsgHdr_ST xMsgHeader; + u32 MemAddr; + u8 Payload[SMS_MAX_PAYLOAD_SIZE]; +}; + +struct SmsVersionRes_ST { + struct SmsMsgHdr_ST xMsgHeader; + + u16 ChipModel; /* e.g. 0x1102 for SMS-1102 "Nova" */ + u8 Step; /* 0 - Step A */ + u8 MetalFix; /* 0 - Metal 0 */ + + /* FirmwareId 0xFF if ROM, otherwise the + * value indicated by SMSHOSTLIB_DEVICE_MODES_E */ + u8 FirmwareId; + /* SupportedProtocols Bitwise OR combination of + * supported protocols */ + u8 SupportedProtocols; + + u8 VersionMajor; + u8 VersionMinor; + u8 VersionPatch; + u8 VersionFieldPatch; + + u8 RomVersionMajor; + u8 RomVersionMinor; + u8 RomVersionPatch; + u8 RomVersionFieldPatch; + + u8 TextLabel[34]; +}; + +struct SmsFirmware_ST { + u32 CheckSum; + u32 Length; + u32 StartAddress; + u8 Payload[1]; +}; + +/* Statistics information returned as response for + * SmsHostApiGetStatistics_Req */ +struct SMSHOSTLIB_STATISTICS_ST { + u32 Reserved; /* Reserved */ + + /* Common parameters */ + u32 IsRfLocked; /* 0 - not locked, 1 - locked */ + u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ + u32 IsExternalLNAOn; /* 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 InBandPwr; /* In band power in dBM */ + s32 CarrierOffset; /* Carrier Offset in bin/1024 */ + + /* Transmission parameters */ + u32 Frequency; /* Frequency in Hz */ + u32 Bandwidth; /* Bandwidth in MHz, valid only for DVB-T/H */ + u32 TransmissionMode; /* Transmission Mode, for DAB modes 1-4, + for DVB-T/H FFT mode carriers in Kilos */ + u32 ModemState; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET, + valid only for DVB-T/H */ + u32 GuardInterval; /* Guard Interval from + SMSHOSTLIB_GUARD_INTERVALS_ET, valid only for DVB-T/H */ + u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET, + valid only for DVB-T/H */ + u32 LPCodeRate; /* 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 BurstSize; /* Current burst size in bytes, + valid only for DVB-H */ + u32 BurstDuration; /* Current burst duration in mSec, + valid only for DVB-H */ + u32 BurstCycleTime; /* Current burst cycle time in mSec, + valid only for DVB-H */ + u32 CalculatedBurstCycleTime;/* Current burst cycle time in mSec, + as calculated by demodulator, valid only for DVB-H */ + u32 NumOfRows; /* Number of rows in MPE table, + valid only for DVB-H */ + u32 NumOfPaddCols; /* Number of padding columns in MPE table, + valid only for DVB-H */ + u32 NumOfPunctCols; /* Number of puncturing columns in MPE table, + valid only for DVB-H */ + u32 ErrorTSPackets; /* Number of erroneous + transport-stream packets */ + u32 TotalTSPackets; /* Total number of transport-stream packets */ + u32 NumOfValidMpeTlbs; /* Number of MPE tables which do not include + errors after MPE RS decoding */ + u32 NumOfInvalidMpeTlbs;/* Number of MPE tables which include errors + after MPE RS decoding */ + u32 NumOfCorrectedMpeTlbs;/* Number of MPE tables which were + corrected by MPE RS decoding */ + /* Common params */ + u32 BERErrorCount; /* Number of errornous SYNC bits. */ + u32 BERBitCount; /* Total number of SYNC bits. */ + + /* Interface information */ + u32 SmsToHostTxErrors; /* Total number of transmission errors. */ + + /* DAB/T-DMB */ + u32 PreBER; /* DAB/T-DMB only: Pre Viterbi BER [1E-5] */ + + /* DVB-H TPS parameters */ + u32 CellId; /* TPS Cell ID in bits 15..0, bits 31..16 zero; + if set to 0xFFFFFFFF cell_id not yet recovered */ + u32 DvbhSrvIndHP; /* DVB-H service indication info, bit 1 - + Time Slicing indicator, bit 0 - MPE-FEC indicator */ + u32 DvbhSrvIndLP; /* DVB-H service indication info, bit 1 - + Time Slicing indicator, bit 0 - MPE-FEC indicator */ + + u32 NumMPEReceived; /* DVB-H, Num MPE section received */ + + u32 ReservedFields[10]; /* Reserved */ +}; + +struct SmsMsgStatisticsInfo_ST { + u32 RequestResult; + + struct SMSHOSTLIB_STATISTICS_ST Stat; + + /* Split the calc of the SNR in DAB */ + u32 Signal; /* dB */ + u32 Noise; /* dB */ + +}; + +struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST { + /* Per-layer information */ + u32 CodeRate; /* 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 BERErrorCount; /* Post Viterbi Error Bits Count */ + u32 BERBitCount; /* Post Viterbi Total Bits Count */ + u32 PreBER; /* Pre Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A */ + u32 TS_PER; /* Transport stream PER [%], 0xFFFFFFFF indicate N/A */ + u32 ErrorTSPackets; /* Number of erroneous transport-stream packets */ + u32 TotalTSPackets; /* Total number of transport-stream packets */ + u32 TILdepthI; /* Time interleaver depth I parameter, + * 255 means layer does not exist */ + u32 NumberOfSegments; /* Number of segments in layer A, + * 255 means layer does not exist */ + u32 TMCCErrors; /* TMCC errors */ +}; + +struct SMSHOSTLIB_STATISTICS_ISDBT_ST { + u32 StatisticsType; /* 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 FullSize; /* Total size of the structure returned by the modem. + * If the size requested by the host is smaller than + * FullSize, the struct will be truncated */ + + /* Common parameters */ + u32 IsRfLocked; /* 0 - not locked, 1 - locked */ + u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ + u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ + + /* Reception quality */ + s32 SNR; /* dB */ + s32 RSSI; /* dBm */ + s32 InBandPwr; /* In band power in dBM */ + s32 CarrierOffset; /* Carrier Offset in Hz */ + + /* Transmission parameters */ + u32 Frequency; /* Frequency in Hz */ + u32 Bandwidth; /* Bandwidth in MHz */ + u32 TransmissionMode; /* ISDB-T transmission mode */ + u32 ModemState; /* 0 - Acquisition, 1 - Locked */ + u32 GuardInterval; /* Guard Interval, 1 divided by value */ + u32 SystemType; /* ISDB-T system type (ISDB-T / ISDB-Tsb) */ + u32 PartialReception; /* TRUE - partial reception, FALSE otherwise */ + u32 NumOfLayers; /* Number of ISDB-T layers in the network */ + + /* Per-layer information */ + /* Layers A, B and C */ + struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST LayerInfo[3]; + /* Per-layer statistics, see SMSHOSTLIB_ISDBT_LAYER_STAT_ST */ + + /* Interface information */ + u32 SmsToHostTxErrors; /* Total number of transmission errors. */ +}; + +struct PID_STATISTICS_DATA_S { + 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 PID_DATA_S { + u32 pid; + u32 num_rows; + struct PID_STATISTICS_DATA_S 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.TransmissionMode == 0) \ + _stat.TransmissionMode = 2; \ + else if (_stat.TransmissionMode == 1) \ + _stat.TransmissionMode = 8; \ + else \ + _stat.TransmissionMode = 4; + +struct TRANSMISSION_STATISTICS_S { + u32 Frequency; /* Frequency in Hz */ + u32 Bandwidth; /* Bandwidth in MHz */ + u32 TransmissionMode; /* FFT mode carriers in Kilos */ + u32 GuardInterval; /* Guard Interval from + SMSHOSTLIB_GUARD_INTERVALS_ET */ + u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET */ + u32 LPCodeRate; /* 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 CellId; /* TPS Cell ID in bits 15..0, bits 31..16 zero; + if set to 0xFFFFFFFF cell_id not yet recovered */ + u32 DvbhSrvIndHP; /* DVB-H service indication info, bit 1 - + Time Slicing indicator, bit 0 - MPE-FEC indicator */ + u32 DvbhSrvIndLP; /* DVB-H service indication info, bit 1 - + Time Slicing indicator, bit 0 - MPE-FEC indicator */ + u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ +}; + +struct RECEPTION_STATISTICS_S { + u32 IsRfLocked; /* 0 - not locked, 1 - locked */ + u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ + u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ + + u32 ModemState; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */ + s32 SNR; /* dB */ + u32 BER; /* Post Viterbi BER [1E-5] */ + u32 BERErrorCount; /* Number of erronous SYNC bits. */ + u32 BERBitCount; /* 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 InBandPwr; /* In band power in dBM */ + s32 CarrierOffset; /* Carrier Offset in bin/1024 */ + u32 ErrorTSPackets; /* Number of erroneous + transport-stream packets */ + u32 TotalTSPackets; /* Total number of transport-stream packets */ + + s32 MRC_SNR; /* dB */ + s32 MRC_RSSI; /* dBm */ + s32 MRC_InBandPwr; /* In band power in dBM */ +}; + + +/* Statistics information returned as response for + * SmsHostApiGetStatisticsEx_Req for DVB applications, SMS1100 and up */ +struct SMSHOSTLIB_STATISTICS_DVB_S { + /* Reception */ + struct RECEPTION_STATISTICS_S ReceptionData; + + /* Transmission parameters */ + struct TRANSMISSION_STATISTICS_S TransmissionData; + + /* Burst parameters, valid only for DVB-H */ +#define SRVM_MAX_PID_FILTERS 8 + struct PID_DATA_S PidData[SRVM_MAX_PID_FILTERS]; +}; + +struct SRVM_SIGNAL_STATUS_S { + u32 result; + u32 snr; + u32 tsPackets; + u32 etsPackets; + u32 constellation; + u32 hpCode; + u32 tpsSrvIndLP; + u32 tpsSrvIndHP; + u32 cellId; + u32 reason; + + s32 inBandPower; + u32 requestId; +}; + +struct SMSHOSTLIB_I2C_REQ_ST { + u32 DeviceAddress; /* I2c device address */ + u32 WriteCount; /* number of bytes to write */ + u32 ReadCount; /* number of bytes to read */ + u8 Data[1]; +}; + +struct SMSHOSTLIB_I2C_RES_ST { + u32 Status; /* non-zero value in case of failure */ + u32 ReadCount; /* number of bytes read */ + u8 Data[1]; +}; + + +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; + +#define SMS_GPIO_OUTPUTSLEWRATE_FAST 0 +#define SMS_GPIO_OUTPUTSLEWRATE_SLOW 1 + u8 outputslewrate; + +#define SMS_GPIO_OUTPUTDRIVING_4mA 0 +#define SMS_GPIO_OUTPUTDRIVING_8mA 1 +#define SMS_GPIO_OUTPUTDRIVING_12mA 2 +#define SMS_GPIO_OUTPUTDRIVING_16mA 3 + u8 outputdriving; +}; + +struct smscore_gpio_config { +#define SMS_GPIO_DIRECTION_INPUT 0 +#define SMS_GPIO_DIRECTION_OUTPUT 1 + u8 Direction; + +#define SMS_GPIO_PULL_UP_DOWN_NONE 0 +#define SMS_GPIO_PULL_UP_DOWN_PULLDOWN 1 +#define SMS_GPIO_PULL_UP_DOWN_PULLUP 2 +#define SMS_GPIO_PULL_UP_DOWN_KEEPER 3 + u8 PullUpDown; + +#define SMS_GPIO_INPUT_CHARACTERISTICS_NORMAL 0 +#define SMS_GPIO_INPUT_CHARACTERISTICS_SCHMITT 1 + u8 InputCharacteristics; + +#define SMS_GPIO_OUTPUT_SLEW_RATE_SLOW 1 /* 10xx */ +#define SMS_GPIO_OUTPUT_SLEW_RATE_FAST 0 /* 10xx */ + + +#define SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS 0 /* 11xx */ +#define SMS_GPIO_OUTPUT_SLEW_RATE_0_9_V_NS 1 /* 11xx */ +#define SMS_GPIO_OUTPUT_SLEW_RATE_1_7_V_NS 2 /* 11xx */ +#define SMS_GPIO_OUTPUT_SLEW_RATE_3_3_V_NS 3 /* 11xx */ + u8 OutputSlewRate; + +#define SMS_GPIO_OUTPUT_DRIVING_S_4mA 0 /* 10xx */ +#define SMS_GPIO_OUTPUT_DRIVING_S_8mA 1 /* 10xx */ +#define SMS_GPIO_OUTPUT_DRIVING_S_12mA 2 /* 10xx */ +#define SMS_GPIO_OUTPUT_DRIVING_S_16mA 3 /* 10xx */ + +#define SMS_GPIO_OUTPUT_DRIVING_1_5mA 0 /* 11xx */ +#define SMS_GPIO_OUTPUT_DRIVING_2_8mA 1 /* 11xx */ +#define SMS_GPIO_OUTPUT_DRIVING_4mA 2 /* 11xx */ +#define SMS_GPIO_OUTPUT_DRIVING_7mA 3 /* 11xx */ +#define SMS_GPIO_OUTPUT_DRIVING_10mA 4 /* 11xx */ +#define SMS_GPIO_OUTPUT_DRIVING_11mA 5 /* 11xx */ +#define SMS_GPIO_OUTPUT_DRIVING_14mA 6 /* 11xx */ +#define SMS_GPIO_OUTPUT_DRIVING_16mA 7 /* 11xx */ + u8 OutputDriving; +}; + +extern void smscore_registry_setmode(char *devpath, int mode); +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); +extern void smscore_unregister_device(struct smscore_device_t *coredev); + +extern int smscore_start_device(struct smscore_device_t *coredev); +extern int smscore_load_firmware(struct smscore_device_t *coredev, + char *filename, + loadfirmware_t loadfirmware_handler); + +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 int smscore_get_common_buffer_size(struct smscore_device_t *coredev); +extern int smscore_map_common_buffer(struct smscore_device_t *coredev, + struct vm_area_struct *vma); +extern int smscore_get_fw_filename(struct smscore_device_t *coredev, + int mode, char *filename); +extern int smscore_send_fw_file(struct smscore_device_t *coredev, + u8 *ufwbuf, int size); + +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 PinNum, + struct smscore_gpio_config *pGpioConfig); +extern int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, + u8 NewLevel); +extern int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum, + 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); + + +/* ------------------------------------------------------------------------ */ + +#define DBG_INFO 1 +#define DBG_ADV 2 + +#define sms_printk(kern, fmt, arg...) \ + printk(kern "%s: " fmt "\n", __func__, ##arg) + +#define dprintk(kern, lvl, fmt, arg...) do {\ + if (sms_dbg & lvl) \ + sms_printk(kern, fmt, ##arg); } while (0) + +#define sms_log(fmt, arg...) sms_printk(KERN_INFO, fmt, ##arg) +#define sms_err(fmt, arg...) \ + sms_printk(KERN_ERR, "line: %d: " fmt, __LINE__, ##arg) +#define sms_warn(fmt, arg...) sms_printk(KERN_WARNING, fmt, ##arg) +#define sms_info(fmt, arg...) \ + dprintk(KERN_INFO, DBG_INFO, fmt, ##arg) +#define sms_debug(fmt, arg...) \ + dprintk(KERN_DEBUG, DBG_ADV, fmt, ##arg) + + +#endif /* __SMS_CORE_API_H__ */ diff --git a/drivers/media/common/siano/smsdvb.c b/drivers/media/common/siano/smsdvb.c new file mode 100644 index 000000000000..aa77e54a8fae --- /dev/null +++ b/drivers/media/common/siano/smsdvb.c @@ -0,0 +1,1078 @@ +/**************************************************************** + +Siano Mobile Silicon, Inc. +MDTV receiver kernel modules. +Copyright (C) 2006-2008, Uri Shkolnik + +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, see . + +****************************************************************/ + +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" + +#include "smscoreapi.h" +#include "smsendian.h" +#include "sms-cards.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +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; + + fe_status_t fe_status; + + struct completion tune_done; + + struct SMSHOSTLIB_STATISTICS_DVB_S sms_stat_dvb; + int event_fe_state; + int event_unc_state; +}; + +static struct list_head g_smsdvb_clients; +static struct mutex g_smsdvb_clientslock; + +static int sms_dbg; +module_param_named(debug, sms_dbg, int, 0644); +MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); + +/* 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: + sms_debug("DVB3_EVENT_INIT"); + sms_board_event(coredev, BOARD_EVENT_BIND); + break; + case DVB3_EVENT_SLEEP: + sms_debug("DVB3_EVENT_SLEEP"); + sms_board_event(coredev, BOARD_EVENT_POWER_SUSPEND); + break; + case DVB3_EVENT_HOTPLUG: + sms_debug("DVB3_EVENT_HOTPLUG"); + 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; + sms_debug("DVB3_EVENT_FE_LOCK"); + 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; + sms_debug("DVB3_EVENT_FE_UNLOCK"); + 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; + sms_debug("DVB3_EVENT_UNC_OK"); + 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; + sms_debug("DVB3_EVENT_UNC_ERR"); + sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_ERRORS); + } + break; + + default: + sms_err("Unknown dvb3 api event"); + break; + } +} + + +static void smsdvb_update_dvb_stats(struct RECEPTION_STATISTICS_S *pReceptionData, + struct SMSHOSTLIB_STATISTICS_ST *p) +{ + if (sms_dbg & 2) { + printk(KERN_DEBUG "Reserved = %d", p->Reserved); + printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); + printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); + printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); + printk(KERN_DEBUG "SNR = %d", p->SNR); + printk(KERN_DEBUG "BER = %d", p->BER); + printk(KERN_DEBUG "FIB_CRC = %d", p->FIB_CRC); + printk(KERN_DEBUG "TS_PER = %d", p->TS_PER); + printk(KERN_DEBUG "MFER = %d", p->MFER); + printk(KERN_DEBUG "RSSI = %d", p->RSSI); + printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); + printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); + printk(KERN_DEBUG "Frequency = %d", p->Frequency); + printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); + printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); + printk(KERN_DEBUG "ModemState = %d", p->ModemState); + printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); + printk(KERN_DEBUG "CodeRate = %d", p->CodeRate); + printk(KERN_DEBUG "LPCodeRate = %d", p->LPCodeRate); + printk(KERN_DEBUG "Hierarchy = %d", p->Hierarchy); + printk(KERN_DEBUG "Constellation = %d", p->Constellation); + printk(KERN_DEBUG "BurstSize = %d", p->BurstSize); + printk(KERN_DEBUG "BurstDuration = %d", p->BurstDuration); + printk(KERN_DEBUG "BurstCycleTime = %d", p->BurstCycleTime); + printk(KERN_DEBUG "CalculatedBurstCycleTime = %d", p->CalculatedBurstCycleTime); + printk(KERN_DEBUG "NumOfRows = %d", p->NumOfRows); + printk(KERN_DEBUG "NumOfPaddCols = %d", p->NumOfPaddCols); + printk(KERN_DEBUG "NumOfPunctCols = %d", p->NumOfPunctCols); + printk(KERN_DEBUG "ErrorTSPackets = %d", p->ErrorTSPackets); + printk(KERN_DEBUG "TotalTSPackets = %d", p->TotalTSPackets); + printk(KERN_DEBUG "NumOfValidMpeTlbs = %d", p->NumOfValidMpeTlbs); + printk(KERN_DEBUG "NumOfInvalidMpeTlbs = %d", p->NumOfInvalidMpeTlbs); + printk(KERN_DEBUG "NumOfCorrectedMpeTlbs = %d", p->NumOfCorrectedMpeTlbs); + printk(KERN_DEBUG "BERErrorCount = %d", p->BERErrorCount); + printk(KERN_DEBUG "BERBitCount = %d", p->BERBitCount); + printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); + printk(KERN_DEBUG "PreBER = %d", p->PreBER); + printk(KERN_DEBUG "CellId = %d", p->CellId); + printk(KERN_DEBUG "DvbhSrvIndHP = %d", p->DvbhSrvIndHP); + printk(KERN_DEBUG "DvbhSrvIndLP = %d", p->DvbhSrvIndLP); + printk(KERN_DEBUG "NumMPEReceived = %d", p->NumMPEReceived); + } + + pReceptionData->IsDemodLocked = p->IsDemodLocked; + + pReceptionData->SNR = p->SNR; + pReceptionData->BER = p->BER; + pReceptionData->BERErrorCount = p->BERErrorCount; + pReceptionData->InBandPwr = p->InBandPwr; + pReceptionData->ErrorTSPackets = p->ErrorTSPackets; +}; + + +static void smsdvb_update_isdbt_stats(struct RECEPTION_STATISTICS_S *pReceptionData, + struct SMSHOSTLIB_STATISTICS_ISDBT_ST *p) +{ + int i; + + if (sms_dbg & 2) { + printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); + printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); + printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); + printk(KERN_DEBUG "SNR = %d", p->SNR); + printk(KERN_DEBUG "RSSI = %d", p->RSSI); + printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); + printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); + printk(KERN_DEBUG "Frequency = %d", p->Frequency); + printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); + printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); + printk(KERN_DEBUG "ModemState = %d", p->ModemState); + printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); + printk(KERN_DEBUG "SystemType = %d", p->SystemType); + printk(KERN_DEBUG "PartialReception = %d", p->PartialReception); + printk(KERN_DEBUG "NumOfLayers = %d", p->NumOfLayers); + printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); + + for (i = 0; i < 3; i++) { + printk(KERN_DEBUG "%d: CodeRate = %d", i, p->LayerInfo[i].CodeRate); + printk(KERN_DEBUG "%d: Constellation = %d", i, p->LayerInfo[i].Constellation); + printk(KERN_DEBUG "%d: BER = %d", i, p->LayerInfo[i].BER); + printk(KERN_DEBUG "%d: BERErrorCount = %d", i, p->LayerInfo[i].BERErrorCount); + printk(KERN_DEBUG "%d: BERBitCount = %d", i, p->LayerInfo[i].BERBitCount); + printk(KERN_DEBUG "%d: PreBER = %d", i, p->LayerInfo[i].PreBER); + printk(KERN_DEBUG "%d: TS_PER = %d", i, p->LayerInfo[i].TS_PER); + printk(KERN_DEBUG "%d: ErrorTSPackets = %d", i, p->LayerInfo[i].ErrorTSPackets); + printk(KERN_DEBUG "%d: TotalTSPackets = %d", i, p->LayerInfo[i].TotalTSPackets); + printk(KERN_DEBUG "%d: TILdepthI = %d", i, p->LayerInfo[i].TILdepthI); + printk(KERN_DEBUG "%d: NumberOfSegments = %d", i, p->LayerInfo[i].NumberOfSegments); + printk(KERN_DEBUG "%d: TMCCErrors = %d", i, p->LayerInfo[i].TMCCErrors); + } + } + + pReceptionData->IsDemodLocked = p->IsDemodLocked; + + pReceptionData->SNR = p->SNR; + pReceptionData->InBandPwr = p->InBandPwr; + + pReceptionData->ErrorTSPackets = 0; + pReceptionData->BER = 0; + pReceptionData->BERErrorCount = 0; + for (i = 0; i < 3; i++) { + pReceptionData->BER += p->LayerInfo[i].BER; + pReceptionData->BERErrorCount += p->LayerInfo[i].BERErrorCount; + pReceptionData->ErrorTSPackets += p->LayerInfo[i].ErrorTSPackets; + } +} + +static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) +{ + struct smsdvb_client_t *client = (struct smsdvb_client_t *) context; + struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) (((u8 *) cb->p) + + cb->offset); + u32 *pMsgData = (u32 *) phdr + 1; + /*u32 MsgDataLen = phdr->msgLength - sizeof(struct SmsMsgHdr_ST);*/ + bool is_status_update = false; + + smsendian_handle_rx_message((struct SmsMsgData_ST *) phdr); + + switch (phdr->msgType) { + case MSG_SMS_DVBT_BDA_DATA: + dvb_dmx_swfilter(&client->demux, (u8 *)(phdr + 1), + cb->size - sizeof(struct SmsMsgHdr_ST)); + break; + + case MSG_SMS_RF_TUNE_RES: + case MSG_SMS_ISDBT_TUNE_RES: + complete(&client->tune_done); + break; + + case MSG_SMS_SIGNAL_DETECTED_IND: + sms_info("MSG_SMS_SIGNAL_DETECTED_IND"); + client->sms_stat_dvb.TransmissionData.IsDemodLocked = true; + is_status_update = true; + break; + + case MSG_SMS_NO_SIGNAL_IND: + sms_info("MSG_SMS_NO_SIGNAL_IND"); + client->sms_stat_dvb.TransmissionData.IsDemodLocked = false; + is_status_update = true; + break; + + case MSG_SMS_TRANSMISSION_IND: { + sms_info("MSG_SMS_TRANSMISSION_IND"); + + pMsgData++; + memcpy(&client->sms_stat_dvb.TransmissionData, pMsgData, + sizeof(struct TRANSMISSION_STATISTICS_S)); + + /* Mo need to correct guard interval + * (as opposed to old statistics message). + */ + CORRECT_STAT_BANDWIDTH(client->sms_stat_dvb.TransmissionData); + CORRECT_STAT_TRANSMISSON_MODE( + client->sms_stat_dvb.TransmissionData); + is_status_update = true; + break; + } + case MSG_SMS_HO_PER_SLICES_IND: { + struct RECEPTION_STATISTICS_S *pReceptionData = + &client->sms_stat_dvb.ReceptionData; + struct SRVM_SIGNAL_STATUS_S SignalStatusData; + + /*sms_info("MSG_SMS_HO_PER_SLICES_IND");*/ + pMsgData++; + SignalStatusData.result = pMsgData[0]; + SignalStatusData.snr = pMsgData[1]; + SignalStatusData.inBandPower = (s32) pMsgData[2]; + SignalStatusData.tsPackets = pMsgData[3]; + SignalStatusData.etsPackets = pMsgData[4]; + SignalStatusData.constellation = pMsgData[5]; + SignalStatusData.hpCode = pMsgData[6]; + SignalStatusData.tpsSrvIndLP = pMsgData[7] & 0x03; + SignalStatusData.tpsSrvIndHP = pMsgData[8] & 0x03; + SignalStatusData.cellId = pMsgData[9] & 0xFFFF; + SignalStatusData.reason = pMsgData[10]; + SignalStatusData.requestId = pMsgData[11]; + pReceptionData->IsRfLocked = pMsgData[16]; + pReceptionData->IsDemodLocked = pMsgData[17]; + pReceptionData->ModemState = pMsgData[12]; + pReceptionData->SNR = pMsgData[1]; + pReceptionData->BER = pMsgData[13]; + pReceptionData->RSSI = pMsgData[14]; + CORRECT_STAT_RSSI(client->sms_stat_dvb.ReceptionData); + + pReceptionData->InBandPwr = (s32) pMsgData[2]; + pReceptionData->CarrierOffset = (s32) pMsgData[15]; + pReceptionData->TotalTSPackets = pMsgData[3]; + pReceptionData->ErrorTSPackets = pMsgData[4]; + + /* TS PER */ + if ((SignalStatusData.tsPackets + SignalStatusData.etsPackets) + > 0) { + pReceptionData->TS_PER = (SignalStatusData.etsPackets + * 100) / (SignalStatusData.tsPackets + + SignalStatusData.etsPackets); + } else { + pReceptionData->TS_PER = 0; + } + + pReceptionData->BERBitCount = pMsgData[18]; + pReceptionData->BERErrorCount = pMsgData[19]; + + pReceptionData->MRC_SNR = pMsgData[20]; + pReceptionData->MRC_InBandPwr = pMsgData[21]; + pReceptionData->MRC_RSSI = pMsgData[22]; + + is_status_update = true; + break; + } + case MSG_SMS_GET_STATISTICS_RES: { + union { + struct SMSHOSTLIB_STATISTICS_ISDBT_ST isdbt; + struct SmsMsgStatisticsInfo_ST dvb; + } *p = (void *) (phdr + 1); + struct RECEPTION_STATISTICS_S *pReceptionData = + &client->sms_stat_dvb.ReceptionData; + + sms_info("MSG_SMS_GET_STATISTICS_RES"); + + is_status_update = true; + + switch (smscore_get_device_mode(client->coredev)) { + case DEVICE_MODE_ISDBT: + case DEVICE_MODE_ISDBT_BDA: + smsdvb_update_isdbt_stats(pReceptionData, &p->isdbt); + break; + default: + smsdvb_update_dvb_stats(pReceptionData, &p->dvb.Stat); + } + if (!pReceptionData->IsDemodLocked) { + pReceptionData->SNR = 0; + pReceptionData->BER = 0; + pReceptionData->BERErrorCount = 0; + pReceptionData->InBandPwr = 0; + pReceptionData->ErrorTSPackets = 0; + } + + complete(&client->tune_done); + break; + } + default: + sms_info("Unhandled message %d", phdr->msgType); + + } + smscore_putbuffer(client->coredev, cb); + + if (is_status_update) { + if (client->sms_stat_dvb.ReceptionData.IsDemodLocked) { + client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER + | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK); + if (client->sms_stat_dvb.ReceptionData.ErrorTSPackets + == 0) + sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK); + else + sms_board_dvb3_event(client, + DVB3_EVENT_UNC_ERR); + + } else { + if (client->sms_stat_dvb.ReceptionData.IsRfLocked) + client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + else + client->fe_status = 0; + sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK); + } + } + + return 0; +} + +static void smsdvb_unregister_client(struct smsdvb_client_t *client) +{ + /* must be called under clientslock */ + + list_del(&client->entry); + + smscore_unregister_client(client->smsclient); + dvb_unregister_frontend(&client->frontend); + dvb_dmxdev_release(&client->dmxdev); + dvb_dmx_release(&client->demux); + dvb_unregister_adapter(&client->adapter); + kfree(client); +} + +static void smsdvb_onremove(void *context) +{ + kmutex_lock(&g_smsdvb_clientslock); + + smsdvb_unregister_client((struct smsdvb_client_t *) context); + + kmutex_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 SmsMsgData_ST PidMsg; + + sms_debug("add pid %d(%x)", + feed->pid, feed->pid); + + PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + PidMsg.xMsgHeader.msgDstId = HIF_TASK; + PidMsg.xMsgHeader.msgFlags = 0; + PidMsg.xMsgHeader.msgType = MSG_SMS_ADD_PID_FILTER_REQ; + PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); + PidMsg.msgData[0] = feed->pid; + + smsendian_handle_tx_message((struct SmsMsgHdr_ST *)&PidMsg); + return smsclient_sendrequest(client->smsclient, + &PidMsg, sizeof(PidMsg)); +} + +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 SmsMsgData_ST PidMsg; + + sms_debug("remove pid %d(%x)", + feed->pid, feed->pid); + + PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + PidMsg.xMsgHeader.msgDstId = HIF_TASK; + PidMsg.xMsgHeader.msgFlags = 0; + PidMsg.xMsgHeader.msgType = MSG_SMS_REMOVE_PID_FILTER_REQ; + PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); + PidMsg.msgData[0] = feed->pid; + + smsendian_handle_tx_message((struct SmsMsgHdr_ST *)&PidMsg); + return smsclient_sendrequest(client->smsclient, + &PidMsg, sizeof(PidMsg)); +} + +static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client, + void *buffer, size_t size, + struct completion *completion) +{ + int rc; + + smsendian_handle_tx_message((struct SmsMsgHdr_ST *)buffer); + 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 SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ, + DVBT_BDA_CONTROL_MSG_ID, + HIF_TASK, + sizeof(struct SmsMsgHdr_ST), 0 }; + + rc = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), + &client->tune_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, + (client->sms_stat_dvb.ReceptionData.BER + == 0) ? SMS_LED_HI : SMS_LED_LO); + else + return sms_board_led_feedback(client->coredev, SMS_LED_OFF); +} + +static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *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->sms_stat_dvb.ReceptionData.BER; + + led_feedback(client); + + return rc; +} + +static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + int rc; + + struct smsdvb_client_t *client; + client = container_of(fe, struct smsdvb_client_t, frontend); + + rc = smsdvb_send_statistics_request(client); + + if (client->sms_stat_dvb.ReceptionData.InBandPwr < -95) + *strength = 0; + else if (client->sms_stat_dvb.ReceptionData.InBandPwr > -29) + *strength = 100; + else + *strength = + (client->sms_stat_dvb.ReceptionData.InBandPwr + + 95) * 3 / 2; + + led_feedback(client); + + return rc; +} + +static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + int rc; + struct smsdvb_client_t *client; + client = container_of(fe, struct smsdvb_client_t, frontend); + + rc = smsdvb_send_statistics_request(client); + + *snr = client->sms_stat_dvb.ReceptionData.SNR; + + led_feedback(client); + + return rc; +} + +static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + int rc; + struct smsdvb_client_t *client; + client = container_of(fe, struct smsdvb_client_t, frontend); + + rc = smsdvb_send_statistics_request(client); + + *ucblocks = client->sms_stat_dvb.ReceptionData.ErrorTSPackets; + + led_feedback(client); + + return rc; +} + +static int smsdvb_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + sms_debug(""); + + 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 SmsMsgHdr_ST Msg; + u32 Data[3]; + } Msg; + + int ret; + + client->fe_status = FE_HAS_SIGNAL; + client->event_fe_state = -1; + client->event_unc_state = -1; + fe->dtv_property_cache.delivery_system = SYS_DVBT; + + Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + Msg.Msg.msgDstId = HIF_TASK; + Msg.Msg.msgFlags = 0; + Msg.Msg.msgType = MSG_SMS_RF_TUNE_REQ; + Msg.Msg.msgLength = sizeof(Msg); + Msg.Data[0] = c->frequency; + Msg.Data[2] = 12000000; + + sms_info("%s: freq %d band %d", __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) { + fe_status_t 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); + + struct { + struct SmsMsgHdr_ST Msg; + u32 Data[4]; + } Msg; + + fe->dtv_property_cache.delivery_system = SYS_ISDBT; + + Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + Msg.Msg.msgDstId = HIF_TASK; + Msg.Msg.msgFlags = 0; + Msg.Msg.msgType = MSG_SMS_ISDBT_TUNE_REQ; + Msg.Msg.msgLength = sizeof(Msg); + + if (c->isdbt_sb_segment_idx == -1) + c->isdbt_sb_segment_idx = 0; + + switch (c->isdbt_sb_segment_count) { + case 3: + Msg.Data[1] = BW_ISDBT_3SEG; + break; + case 1: + Msg.Data[1] = BW_ISDBT_1SEG; + break; + case 0: /* AUTO */ + switch (c->bandwidth_hz / 1000000) { + case 8: + case 7: + c->isdbt_sb_segment_count = 3; + Msg.Data[1] = BW_ISDBT_3SEG; + break; + case 6: + c->isdbt_sb_segment_count = 1; + Msg.Data[1] = BW_ISDBT_1SEG; + break; + default: /* Assumes 6 MHZ bw */ + c->isdbt_sb_segment_count = 1; + c->bandwidth_hz = 6000; + Msg.Data[1] = BW_ISDBT_1SEG; + break; + } + break; + default: + sms_info("Segment count %d not supported", c->isdbt_sb_segment_count); + return -EINVAL; + } + + Msg.Data[0] = c->frequency; + Msg.Data[2] = 12000000; + Msg.Data[3] = c->isdbt_sb_segment_idx; + + sms_info("%s: freq %d segwidth %d segindex %d\n", __func__, + c->frequency, c->isdbt_sb_segment_count, + c->isdbt_sb_segment_idx); + + return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), + &client->tune_done); +} + +static int smsdvb_set_frontend(struct dvb_frontend *fe) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + struct smscore_device_t *coredev = client->coredev; + + 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_get_frontend(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *fep = &fe->dtv_property_cache; + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + struct smscore_device_t *coredev = client->coredev; + struct TRANSMISSION_STATISTICS_S *td = + &client->sms_stat_dvb.TransmissionData; + + switch (smscore_get_device_mode(coredev)) { + case DEVICE_MODE_DVBT: + case DEVICE_MODE_DVBT_BDA: + fep->frequency = td->Frequency; + + switch (td->Bandwidth) { + case 6: + fep->bandwidth_hz = 6000000; + break; + case 7: + fep->bandwidth_hz = 7000000; + break; + case 8: + fep->bandwidth_hz = 8000000; + break; + } + + switch (td->TransmissionMode) { + case 2: + fep->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 8: + fep->transmission_mode = TRANSMISSION_MODE_8K; + } + + switch (td->GuardInterval) { + case 0: + fep->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + fep->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + fep->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + fep->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + switch (td->CodeRate) { + case 0: + fep->code_rate_HP = FEC_1_2; + break; + case 1: + fep->code_rate_HP = FEC_2_3; + break; + case 2: + fep->code_rate_HP = FEC_3_4; + break; + case 3: + fep->code_rate_HP = FEC_5_6; + break; + case 4: + fep->code_rate_HP = FEC_7_8; + break; + } + + switch (td->LPCodeRate) { + case 0: + fep->code_rate_LP = FEC_1_2; + break; + case 1: + fep->code_rate_LP = FEC_2_3; + break; + case 2: + fep->code_rate_LP = FEC_3_4; + break; + case 3: + fep->code_rate_LP = FEC_5_6; + break; + case 4: + fep->code_rate_LP = FEC_7_8; + break; + } + + switch (td->Constellation) { + case 0: + fep->modulation = QPSK; + break; + case 1: + fep->modulation = QAM_16; + break; + case 2: + fep->modulation = QAM_64; + break; + } + + switch (td->Hierarchy) { + case 0: + fep->hierarchy = HIERARCHY_NONE; + break; + case 1: + fep->hierarchy = HIERARCHY_1; + break; + case 2: + fep->hierarchy = HIERARCHY_2; + break; + case 3: + fep->hierarchy = HIERARCHY_4; + break; + } + + fep->inversion = INVERSION_AUTO; + break; + case DEVICE_MODE_ISDBT: + case DEVICE_MODE_ISDBT_BDA: + fep->frequency = td->Frequency; + fep->bandwidth_hz = 6000000; + /* todo: retrive the other parameters */ + break; + default: + return -EINVAL; + } + + return 0; +} + +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 struct dvb_frontend_ops smsdvb_fe_ops = { + .info = { + .name = "Siano Mobile Digital MDTV Receiver", + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 250000, + .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_frontend = smsdvb_get_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) { + sms_err("kmalloc() failed"); + 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) { + sms_err("dvb_register_adapter() failed %d", rc); + goto adapter_error; + } + + /* 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) { + sms_err("dvb_dmx_init failed %d", 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) { + sms_err("dvb_dmxdev_init failed %d", 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) { + sms_err("frontend registration failed %d", 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, ¶ms, &client->smsclient); + if (rc < 0) { + sms_err("smscore_register_client() failed %d", rc); + goto client_error; + } + + client->coredev = coredev; + + init_completion(&client->tune_done); + + kmutex_lock(&g_smsdvb_clientslock); + + list_add(&client->entry, &g_smsdvb_clients); + + kmutex_unlock(&g_smsdvb_clientslock); + + client->event_fe_state = -1; + client->event_unc_state = -1; + sms_board_dvb3_event(client, DVB3_EVENT_HOTPLUG); + + sms_info("success"); + sms_board_setup(coredev); + + return 0; + +client_error: + dvb_unregister_frontend(&client->frontend); + +frontend_error: + dvb_dmxdev_release(&client->dmxdev); + +dmxdev_error: + dvb_dmx_release(&client->demux); + +dvbdmx_error: + dvb_unregister_adapter(&client->adapter); + +adapter_error: + kfree(client); + return rc; +} + +static int __init smsdvb_module_init(void) +{ + int rc; + + INIT_LIST_HEAD(&g_smsdvb_clients); + kmutex_init(&g_smsdvb_clientslock); + + rc = smscore_register_hotplug(smsdvb_hotplug); + + sms_debug(""); + + return rc; +} + +static void __exit smsdvb_module_exit(void) +{ + smscore_unregister_hotplug(smsdvb_hotplug); + + kmutex_lock(&g_smsdvb_clientslock); + + while (!list_empty(&g_smsdvb_clients)) + smsdvb_unregister_client( + (struct smsdvb_client_t *) g_smsdvb_clients.next); + + kmutex_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/smsendian.c b/drivers/media/common/siano/smsendian.c new file mode 100644 index 000000000000..e2657c2f0109 --- /dev/null +++ b/drivers/media/common/siano/smsendian.c @@ -0,0 +1,103 @@ +/**************************************************************** + + Siano Mobile Silicon, Inc. + MDTV receiver kernel modules. + Copyright (C) 2006-2009, Uri Shkolnik + + 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, see . + + ****************************************************************/ + +#include +#include + +#include "smsendian.h" +#include "smscoreapi.h" + +void smsendian_handle_tx_message(void *buffer) +{ +#ifdef __BIG_ENDIAN + struct SmsMsgData_ST *msg = (struct SmsMsgData_ST *)buffer; + int i; + int msgWords; + + switch (msg->xMsgHeader.msgType) { + case MSG_SMS_DATA_DOWNLOAD_REQ: + { + msg->msgData[0] = le32_to_cpu(msg->msgData[0]); + break; + } + + default: + msgWords = (msg->xMsgHeader.msgLength - + sizeof(struct SmsMsgHdr_ST))/4; + + for (i = 0; i < msgWords; i++) + msg->msgData[i] = le32_to_cpu(msg->msgData[i]); + + break; + } +#endif /* __BIG_ENDIAN */ +} +EXPORT_SYMBOL_GPL(smsendian_handle_tx_message); + +void smsendian_handle_rx_message(void *buffer) +{ +#ifdef __BIG_ENDIAN + struct SmsMsgData_ST *msg = (struct SmsMsgData_ST *)buffer; + int i; + int msgWords; + + switch (msg->xMsgHeader.msgType) { + case MSG_SMS_GET_VERSION_EX_RES: + { + struct SmsVersionRes_ST *ver = + (struct SmsVersionRes_ST *) msg; + ver->ChipModel = le16_to_cpu(ver->ChipModel); + break; + } + + case MSG_SMS_DVBT_BDA_DATA: + case MSG_SMS_DAB_CHANNEL: + case MSG_SMS_DATA_MSG: + { + break; + } + + default: + { + msgWords = (msg->xMsgHeader.msgLength - + sizeof(struct SmsMsgHdr_ST))/4; + + for (i = 0; i < msgWords; i++) + msg->msgData[i] = le32_to_cpu(msg->msgData[i]); + + break; + } + } +#endif /* __BIG_ENDIAN */ +} +EXPORT_SYMBOL_GPL(smsendian_handle_rx_message); + +void smsendian_handle_message_header(void *msg) +{ +#ifdef __BIG_ENDIAN + struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *)msg; + + phdr->msgType = le16_to_cpu(phdr->msgType); + phdr->msgLength = le16_to_cpu(phdr->msgLength); + phdr->msgFlags = le16_to_cpu(phdr->msgFlags); +#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..1624d6fd367b --- /dev/null +++ b/drivers/media/common/siano/smsendian.h @@ -0,0 +1,32 @@ +/**************************************************************** + +Siano Mobile Silicon, Inc. +MDTV receiver kernel modules. +Copyright (C) 2006-2009, Uri Shkolnik + +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, see . + +****************************************************************/ + +#ifndef __SMS_ENDIAN_H__ +#define __SMS_ENDIAN_H__ + +#include + +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..37bc5c4b8ad8 --- /dev/null +++ b/drivers/media/common/siano/smsir.c @@ -0,0 +1,114 @@ +/**************************************************************** + + 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 + + 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, see . + + ****************************************************************/ + + +#include +#include + +#include "smscoreapi.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++) { + DEFINE_IR_RAW_EVENT(ev); + + ev.duration = abs(samples[i]) * 1000; /* Convert to ns */ + ev.pulse = (samples[i] > 0) ? false : true; + + 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; + + sms_log("Allocating rc device"); + dev = rc_allocate_device(); + if (!dev) { + sms_err("Not enough memory"); + return -ENOMEM; + } + + coredev->ir.controller = 0; /* Todo: vega/nova SPI number */ + coredev->ir.timeout = IR_DEFAULT_TIMEOUT; + sms_log("IR port %d, timeout %d ms", + coredev->ir.controller, coredev->ir.timeout); + + snprintf(coredev->ir.name, sizeof(coredev->ir.name), + "SMS IR (%s)", sms_get_board(board_id)->name); + + strlcpy(coredev->ir.phys, coredev->devpath, sizeof(coredev->ir.phys)); + strlcat(coredev->ir.phys, "/ir0", sizeof(coredev->ir.phys)); + + dev->input_name = coredev->ir.name; + dev->input_phys = coredev->ir.phys; + dev->dev.parent = coredev->device; + +#if 0 + /* TODO: properly initialize the parameters bellow */ + 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->driver_type = RC_DRIVER_IR_RAW; + dev->allowed_protos = RC_TYPE_ALL; + dev->map_name = sms_get_board(board_id)->rc_codes; + dev->driver_name = MODULE_NAME; + + sms_log("Input device (IR) %s is set for key events", dev->input_name); + + err = rc_register_device(dev); + if (err < 0) { + sms_err("Failed to register device"); + rc_free_device(dev); + return err; + } + + coredev->ir.dev = dev; + return 0; +} + +void sms_ir_exit(struct smscore_device_t *coredev) +{ + if (coredev->ir.dev) + rc_unregister_device(coredev->ir.dev); + + sms_log(""); +} diff --git a/drivers/media/common/siano/smsir.h b/drivers/media/common/siano/smsir.h new file mode 100644 index 000000000000..ae92b3a8587e --- /dev/null +++ b/drivers/media/common/siano/smsir.h @@ -0,0 +1,55 @@ +/**************************************************************** + +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 + +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, see . + +****************************************************************/ + +#ifndef __SMS_IR_H__ +#define __SMS_IR_H__ + +#include +#include + +#define IR_DEFAULT_TIMEOUT 100 + +struct smscore_device_t; + +struct ir_t { + struct rc_dev *dev; + char name[40]; + char phys[32]; + + char *rc_codes; + u64 protocol; + + u32 timeout; + u32 controller; +}; + +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); + +#endif /* __SMS_IR_H__ */ + diff --git a/drivers/media/mmc/Kconfig b/drivers/media/mmc/Kconfig new file mode 100644 index 000000000000..0f2a9570eb01 --- /dev/null +++ b/drivers/media/mmc/Kconfig @@ -0,0 +1 @@ +source "drivers/media/mmc/siano/Kconfig" diff --git a/drivers/media/mmc/Makefile b/drivers/media/mmc/Makefile new file mode 100644 index 000000000000..dacd3cbb80d6 --- /dev/null +++ b/drivers/media/mmc/Makefile @@ -0,0 +1 @@ +obj-y := siano/ diff --git a/drivers/media/mmc/siano/Kconfig b/drivers/media/mmc/siano/Kconfig new file mode 100644 index 000000000000..fa62475be3bf --- /dev/null +++ b/drivers/media/mmc/siano/Kconfig @@ -0,0 +1,10 @@ +# +# Siano Mobile Silicon Digital TV device configuration +# + +config SMS_SDIO_DRV + tristate "Siano SMS1xxx based MDTV via SDIO interface" + depends on DVB_CORE && RC_CORE && HAS_DMA + depends on MMC + ---help--- + Choose if you would like to have Siano's support for SDIO interface diff --git a/drivers/media/mmc/siano/Makefile b/drivers/media/mmc/siano/Makefile new file mode 100644 index 000000000000..0e01f973db6b --- /dev/null +++ b/drivers/media/mmc/siano/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_SMS_SDIO_DRV) += smssdio.o + +ccflags-y += -Idrivers/media/dvb-core +ccflags-y += -Idrivers/media/common/siano +ccflags-y += $(extra-cflags-y) $(extra-cflags-m) + diff --git a/drivers/media/mmc/siano/smssdio.c b/drivers/media/mmc/siano/smssdio.c new file mode 100644 index 000000000000..d6f3f100699a --- /dev/null +++ b/drivers/media/mmc/siano/smssdio.c @@ -0,0 +1,365 @@ +/* + * smssdio.c - Siano 1xxx SDIO interface driver + * + * Copyright 2008 Pierre Ossman + * + * Based on code by Siano Mobile Silicon, Inc., + * Copyright (C) 2006-2008, Uri Shkolnik + * + * 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 hardware is a bit odd in that all transfers should be done + * to/from the SMSSDIO_DATA register, yet the "increase address" bit + * always needs to be set. + * + * Also, buffers from the card are always aligned to 128 byte + * boundaries. + */ + +/* + * General cleanup notes: + * + * - only typedefs should be name *_t + * + * - use ERR_PTR and friends for smscore_register_device() + * + * - smscore_getbuffer should zero fields + * + * Fix stop command + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smscoreapi.h" +#include "sms-cards.h" + +/* Registers */ + +#define SMSSDIO_DATA 0x00 +#define SMSSDIO_INT 0x04 +#define SMSSDIO_BLOCK_SIZE 128 + +static const struct sdio_device_id smssdio_ids[] __devinitconst = { + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR), + .driver_data = SMS1XXX_BOARD_SIANO_STELLAR}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0), + .driver_data = SMS1XXX_BOARD_SIANO_NOVA_A}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0), + .driver_data = SMS1XXX_BOARD_SIANO_NOVA_B}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0), + .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE), + .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, + { /* end: all zeroes */ }, +}; + +MODULE_DEVICE_TABLE(sdio, smssdio_ids); + +struct smssdio_device { + struct sdio_func *func; + + struct smscore_device_t *coredev; + + struct smscore_buffer_t *split_cb; +}; + +/*******************************************************************/ +/* Siano core callbacks */ +/*******************************************************************/ + +static int smssdio_sendrequest(void *context, void *buffer, size_t size) +{ + int ret = 0; + struct smssdio_device *smsdev; + + smsdev = context; + + sdio_claim_host(smsdev->func); + + while (size >= smsdev->func->cur_blksize) { + ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, + buffer, smsdev->func->cur_blksize); + if (ret) + goto out; + + buffer += smsdev->func->cur_blksize; + size -= smsdev->func->cur_blksize; + } + + if (size) { + ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, + buffer, size); + } + +out: + sdio_release_host(smsdev->func); + + return ret; +} + +/*******************************************************************/ +/* SDIO callbacks */ +/*******************************************************************/ + +static void smssdio_interrupt(struct sdio_func *func) +{ + int ret; + + struct smssdio_device *smsdev; + struct smscore_buffer_t *cb; + struct SmsMsgHdr_ST *hdr; + size_t size; + + smsdev = sdio_get_drvdata(func); + + /* + * The interrupt register has no defined meaning. It is just + * a way of turning of the level triggered interrupt. + */ + (void)sdio_readb(func, SMSSDIO_INT, &ret); + if (ret) { + sms_err("Unable to read interrupt register!\n"); + return; + } + + if (smsdev->split_cb == NULL) { + cb = smscore_getbuffer(smsdev->coredev); + if (!cb) { + sms_err("Unable to allocate data buffer!\n"); + return; + } + + ret = sdio_memcpy_fromio(smsdev->func, + cb->p, + SMSSDIO_DATA, + SMSSDIO_BLOCK_SIZE); + if (ret) { + sms_err("Error %d reading initial block!\n", ret); + return; + } + + hdr = cb->p; + + if (hdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG) { + smsdev->split_cb = cb; + return; + } + + if (hdr->msgLength > smsdev->func->cur_blksize) + size = hdr->msgLength - smsdev->func->cur_blksize; + else + size = 0; + } else { + cb = smsdev->split_cb; + hdr = cb->p; + + size = hdr->msgLength - sizeof(struct SmsMsgHdr_ST); + + smsdev->split_cb = NULL; + } + + if (size) { + void *buffer; + + buffer = cb->p + (hdr->msgLength - size); + size = ALIGN(size, SMSSDIO_BLOCK_SIZE); + + BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE); + + /* + * First attempt to transfer all of it in one go... + */ + ret = sdio_memcpy_fromio(smsdev->func, + buffer, + SMSSDIO_DATA, + size); + if (ret && ret != -EINVAL) { + smscore_putbuffer(smsdev->coredev, cb); + sms_err("Error %d reading data from card!\n", ret); + return; + } + + /* + * ..then fall back to one block at a time if that is + * not possible... + * + * (we have to do this manually because of the + * problem with the "increase address" bit) + */ + if (ret == -EINVAL) { + while (size) { + ret = sdio_memcpy_fromio(smsdev->func, + buffer, SMSSDIO_DATA, + smsdev->func->cur_blksize); + if (ret) { + smscore_putbuffer(smsdev->coredev, cb); + sms_err("Error %d reading " + "data from card!\n", ret); + return; + } + + buffer += smsdev->func->cur_blksize; + if (size > smsdev->func->cur_blksize) + size -= smsdev->func->cur_blksize; + else + size = 0; + } + } + } + + cb->size = hdr->msgLength; + cb->offset = 0; + + smscore_onresponse(smsdev->coredev, cb); +} + +static int __devinit smssdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + int ret; + + int board_id; + struct smssdio_device *smsdev; + struct smsdevice_params_t params; + + board_id = id->driver_data; + + smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL); + if (!smsdev) + return -ENOMEM; + + smsdev->func = func; + + memset(¶ms, 0, sizeof(struct smsdevice_params_t)); + + params.device = &func->dev; + params.buffer_size = 0x5000; /* ?? */ + params.num_buffers = 22; /* ?? */ + params.context = smsdev; + + snprintf(params.devpath, sizeof(params.devpath), + "sdio\\%s", sdio_func_id(func)); + + params.sendrequest_handler = smssdio_sendrequest; + + params.device_type = sms_get_board(board_id)->type; + + if (params.device_type != SMS_STELLAR) + params.flags |= SMS_DEVICE_FAMILY2; + else { + /* + * FIXME: Stellar needs special handling... + */ + ret = -ENODEV; + goto free; + } + + ret = smscore_register_device(¶ms, &smsdev->coredev); + if (ret < 0) + goto free; + + smscore_set_board_id(smsdev->coredev, board_id); + + sdio_claim_host(func); + + ret = sdio_enable_func(func); + if (ret) + goto release; + + ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE); + if (ret) + goto disable; + + ret = sdio_claim_irq(func, smssdio_interrupt); + if (ret) + goto disable; + + sdio_set_drvdata(func, smsdev); + + sdio_release_host(func); + + ret = smscore_start_device(smsdev->coredev); + if (ret < 0) + goto reclaim; + + return 0; + +reclaim: + sdio_claim_host(func); + sdio_release_irq(func); +disable: + sdio_disable_func(func); +release: + sdio_release_host(func); + smscore_unregister_device(smsdev->coredev); +free: + kfree(smsdev); + + return ret; +} + +static void smssdio_remove(struct sdio_func *func) +{ + struct smssdio_device *smsdev; + + smsdev = sdio_get_drvdata(func); + + /* FIXME: racy! */ + if (smsdev->split_cb) + smscore_putbuffer(smsdev->coredev, smsdev->split_cb); + + smscore_unregister_device(smsdev->coredev); + + sdio_claim_host(func); + sdio_release_irq(func); + sdio_disable_func(func); + sdio_release_host(func); + + kfree(smsdev); +} + +static struct sdio_driver smssdio_driver = { + .name = "smssdio", + .id_table = smssdio_ids, + .probe = smssdio_probe, + .remove = smssdio_remove, +}; + +/*******************************************************************/ +/* Module functions */ +/*******************************************************************/ + +static int __init smssdio_module_init(void) +{ + int ret = 0; + + printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n"); + printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n"); + + ret = sdio_register_driver(&smssdio_driver); + + return ret; +} + +static void __exit smssdio_module_exit(void) +{ + sdio_unregister_driver(&smssdio_driver); +} + +module_init(smssdio_module_init); +module_exit(smssdio_module_exit); + +MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver"); +MODULE_AUTHOR("Pierre Ossman"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/siano/Kconfig b/drivers/media/usb/siano/Kconfig index bc6456eb2c4f..3c76e62d820d 100644 --- a/drivers/media/usb/siano/Kconfig +++ b/drivers/media/usb/siano/Kconfig @@ -2,33 +2,9 @@ # Siano Mobile Silicon Digital TV device configuration # -config SMS_SIANO_MDTV +config SMS_USB_DRV tristate "Siano SMS1xxx based MDTV receiver" depends on DVB_CORE && RC_CORE && HAS_DMA - ---help--- - Choose Y or M here if you have MDTV receiver with a Siano chipset. - - To compile this driver as a module, choose M here - (The module will be called smsmdtv). - - Further documentation on this driver can be found on the WWW - at http://www.siano-ms.com/ - -if SMS_SIANO_MDTV -menu "Siano module components" - -# Hardware interfaces support - -config SMS_USB_DRV - tristate "USB interface support" - depends on DVB_CORE && USB ---help--- Choose if you would like to have Siano's support for USB interface -config SMS_SDIO_DRV - tristate "SDIO interface support" - depends on DVB_CORE && MMC - ---help--- - Choose if you would like to have Siano's support for SDIO interface -endmenu -endif # SMS_SIANO_MDTV diff --git a/drivers/media/usb/siano/Makefile b/drivers/media/usb/siano/Makefile index 14756bdb6eaa..758b6a090c59 100644 --- a/drivers/media/usb/siano/Makefile +++ b/drivers/media/usb/siano/Makefile @@ -1,11 +1,6 @@ - -smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o smsir.o - -obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o obj-$(CONFIG_SMS_USB_DRV) += smsusb.o -obj-$(CONFIG_SMS_SDIO_DRV) += smssdio.o ccflags-y += -Idrivers/media/dvb-core - +ccflags-y += -Idrivers/media/common/siano ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/usb/siano/sms-cards.c b/drivers/media/usb/siano/sms-cards.c deleted file mode 100644 index 680c781c8dd6..000000000000 --- a/drivers/media/usb/siano/sms-cards.c +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Card-specific functions for the Siano SMS1xxx USB dongle - * - * Copyright (c) 2008 Michael Krufky - * - * 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. - */ - -#include "sms-cards.h" -#include "smsir.h" -#include - -static int sms_dbg; -module_param_named(cards_dbg, sms_dbg, int, 0644); -MODULE_PARM_DESC(cards_dbg, "set debug level (info=1, adv=2 (or-able))"); - -static struct sms_board sms_boards[] = { - [SMS_BOARD_UNKNOWN] = { - .name = "Unknown board", - }, - [SMS1XXX_BOARD_SIANO_STELLAR] = { - .name = "Siano Stellar Digital Receiver", - .type = SMS_STELLAR, - }, - [SMS1XXX_BOARD_SIANO_NOVA_A] = { - .name = "Siano Nova A Digital Receiver", - .type = SMS_NOVA_A0, - }, - [SMS1XXX_BOARD_SIANO_NOVA_B] = { - .name = "Siano Nova B Digital Receiver", - .type = SMS_NOVA_B0, - }, - [SMS1XXX_BOARD_SIANO_VEGA] = { - .name = "Siano Vega Digital Receiver", - .type = SMS_VEGA, - }, - [SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT] = { - .name = "Hauppauge Catamount", - .type = SMS_STELLAR, - .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-stellar-dvbt-01.fw", - }, - [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A] = { - .name = "Hauppauge Okemo-A", - .type = SMS_NOVA_A0, - .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-a-dvbt-01.fw", - }, - [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B] = { - .name = "Hauppauge Okemo-B", - .type = SMS_NOVA_B0, - .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-b-dvbt-01.fw", - }, - [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = { - .name = "Hauppauge WinTV MiniStick", - .type = SMS_NOVA_B0, - .fw[DEVICE_MODE_ISDBT_BDA] = "sms1xxx-hcw-55xxx-isdbt-02.fw", - .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", - .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] = "sms1xxx-hcw-55xxx-dvbt-02.fw", - .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", - .type = SMS_NOVA_B0, - .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", - .lna_ctrl = -1, - }, - [SMS1XXX_BOARD_SIANO_NICE] = { - /* 11 */ - .name = "Siano Nice Digital Receiver", - .type = SMS_NOVA_B0, - }, - [SMS1XXX_BOARD_SIANO_VENICE] = { - /* 12 */ - .name = "Siano Venice Digital Receiver", - .type = SMS_VEGA, - }, -}; - -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_gpio_config *pGpioConfig) { - pGpioConfig->Direction = SMS_GPIO_DIRECTION_OUTPUT; - pGpioConfig->InputCharacteristics = - SMS_GPIO_INPUT_CHARACTERISTICS_NORMAL; - pGpioConfig->OutputDriving = SMS_GPIO_OUTPUT_DRIVING_4mA; - pGpioConfig->OutputSlewRate = SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS; - pGpioConfig->PullUpDown = SMS_GPIO_PULL_UP_DOWN_NONE; -} - -int sms_board_event(struct smscore_device_t *coredev, - enum SMS_BOARD_EVENTS gevent) { - struct smscore_gpio_config MyGpioConfig; - - sms_gpio_assign_11xx_default_led_config(&MyGpioConfig); - - 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: - sms_err("Unknown SMS board event"); - 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_OUTPUTSLEWRATE_FAST, - .outputdriving = SMS_GPIO_OUTPUTDRIVING_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); - - /* dont 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); - - sms_debug("%s: LNA %s", __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) -{ - switch (id) { - case SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT: - case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A: - case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B: - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: - request_module("smsdvb"); - break; - default: - /* do nothing */ - break; - } - return 0; -} -EXPORT_SYMBOL_GPL(sms_board_load_modules); diff --git a/drivers/media/usb/siano/sms-cards.h b/drivers/media/usb/siano/sms-cards.h deleted file mode 100644 index d8cdf756f7cf..000000000000 --- a/drivers/media/usb/siano/sms-cards.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Card-specific functions for the Siano SMS1xxx USB dongle - * - * Copyright (c) 2008 Michael Krufky - * - * 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 -#include "smscoreapi.h" -#include "smsir.h" - -#define SMS_BOARD_UNKNOWN 0 -#define SMS1XXX_BOARD_SIANO_STELLAR 1 -#define SMS1XXX_BOARD_SIANO_NOVA_A 2 -#define SMS1XXX_BOARD_SIANO_NOVA_B 3 -#define SMS1XXX_BOARD_SIANO_VEGA 4 -#define SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT 5 -#define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A 6 -#define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B 7 -#define SMS1XXX_BOARD_HAUPPAUGE_WINDHAM 8 -#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD 9 -#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 10 -#define SMS1XXX_BOARD_SIANO_NICE 11 -#define SMS1XXX_BOARD_SIANO_VENICE 12 - -struct sms_board_gpio_cfg { - int lna_vhf_exist; - int lna_vhf_ctrl; - int lna_uhf_exist; - int lna_uhf_ctrl; - int lna_uhf_d_ctrl; - int lna_sband_exist; - int lna_sband_ctrl; - int lna_sband_d_ctrl; - int foreign_lna0_ctrl; - int foreign_lna1_ctrl; - int foreign_lna2_ctrl; - int rf_switch_vhf; - int rf_switch_uhf; - int rf_switch_sband; - int leds_power; - int led0; - int led1; - int led2; - int led3; - int led4; - int ir; - int eeprom_wp; - int mrc_sense; - int mrc_pdn_resetn; - int mrc_gp0; /* mrcs spi int */ - int mrc_gp1; - int mrc_gp2; - int mrc_gp3; - int mrc_gp4; - int host_spi_gsp_ts_int; -}; - -struct sms_board { - enum sms_device_type_st type; - char *name, *fw[DEVICE_MODE_MAX]; - struct sms_board_gpio_cfg board_cfg; - char *rc_codes; /* Name of IR codes table */ - - /* gpios */ - int led_power, led_hi, led_lo, lna_ctrl, rf_switch; -}; - -struct sms_board *sms_get_board(unsigned id); - -extern struct smscore_device_t *coredev; - -enum SMS_BOARD_EVENTS { - BOARD_EVENT_POWER_INIT, - BOARD_EVENT_POWER_SUSPEND, - BOARD_EVENT_POWER_RESUME, - BOARD_EVENT_BIND, - BOARD_EVENT_SCAN_PROG, - BOARD_EVENT_SCAN_COMP, - BOARD_EVENT_EMERGENCY_WARNING_SIGNAL, - BOARD_EVENT_FE_LOCK, - BOARD_EVENT_FE_UNLOCK, - BOARD_EVENT_DEMOD_LOCK, - BOARD_EVENT_DEMOD_UNLOCK, - BOARD_EVENT_RECEPTION_MAX_4, - BOARD_EVENT_RECEPTION_3, - BOARD_EVENT_RECEPTION_2, - BOARD_EVENT_RECEPTION_1, - BOARD_EVENT_RECEPTION_LOST_0, - BOARD_EVENT_MULTIPLEX_OK, - BOARD_EVENT_MULTIPLEX_ERRORS -}; - -int sms_board_event(struct smscore_device_t *coredev, - enum SMS_BOARD_EVENTS gevent); - -int sms_board_setup(struct smscore_device_t *coredev); - -#define SMS_LED_OFF 0 -#define SMS_LED_LO 1 -#define SMS_LED_HI 2 -int sms_board_led_feedback(struct smscore_device_t *coredev, int led); -int sms_board_power(struct smscore_device_t *coredev, int onoff); -int sms_board_lna_control(struct smscore_device_t *coredev, int onoff); - -extern int sms_board_load_modules(int id); - -#endif /* __SMS_CARDS_H__ */ diff --git a/drivers/media/usb/siano/smscoreapi.c b/drivers/media/usb/siano/smscoreapi.c deleted file mode 100644 index 9cc55546cc30..000000000000 --- a/drivers/media/usb/siano/smscoreapi.c +++ /dev/null @@ -1,1637 +0,0 @@ -/* - * 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. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "smscoreapi.h" -#include "sms-cards.h" -#include "smsir.h" -#include "smsendian.h" - -static int sms_dbg; -module_param_named(debug, sms_dbg, int, 0644); -MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); - -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; -}; - -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 struct mutex g_smscore_deviceslock; - -static struct list_head g_smscore_registry; -static struct mutex g_smscore_registrylock; - -static int default_mode = 4; - -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; - - kmutex_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 (!strcmp(entry->devpath, devpath)) { - kmutex_unlock(&g_smscore_registrylock); - return entry; - } - } - entry = kmalloc(sizeof(struct smscore_registry_entry_t), GFP_KERNEL); - if (entry) { - entry->mode = default_mode; - strcpy(entry->devpath, devpath); - list_add(&entry->entry, &g_smscore_registry); - } else - sms_err("failed to create smscore_registry."); - kmutex_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 - sms_err("No registry found."); - - 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 - sms_err("No registry found."); - - return -1; -} - -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 - sms_err("No registry found."); -} - -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 - sms_err("No registry found."); -} - - -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; - - kmutex_lock(&g_smscore_deviceslock); - - notifyee = kmalloc(sizeof(struct smscore_device_notifyee_t), - 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(¬ifyee->entry, &g_smscore_notifyees); - } else - kfree(notifyee); - } else - rc = -ENOMEM; - - kmutex_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; - - kmutex_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(¬ifyee->entry); - kfree(notifyee); - } - } - - kmutex_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 = - kmalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL); - if (!cb) { - sms_info("kmalloc(...) failed"); - return NULL; - } - - cb->p = buffer; - cb->offset_in_common = buffer - (u8 *) common_buffer; - 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) -{ - struct smscore_device_t *dev; - u8 *buffer; - - dev = kzalloc(sizeof(struct smscore_device_t), GFP_KERNEL); - if (!dev) { - sms_info("kzalloc(...) failed"); - return -ENOMEM; - } - - /* 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->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; - dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size, - &dev->common_buffer_phys, - GFP_KERNEL | GFP_DMA); - if (!dev->common_buffer) { - smscore_unregister_device(dev); - return -ENOMEM; - } - - /* prepare dma buffers */ - for (buffer = dev->common_buffer; - dev->num_buffers < params->num_buffers; - dev->num_buffers++, buffer += params->buffer_size) { - struct smscore_buffer_t *cb = - smscore_createbuffer(buffer, dev->common_buffer, - dev->common_buffer_phys); - if (!cb) { - smscore_unregister_device(dev); - return -ENOMEM; - } - - smscore_putbuffer(dev, cb); - } - - sms_info("allocated %d buffers", dev->num_buffers); - - dev->mode = DEVICE_MODE_NONE; - dev->context = params->context; - dev->device = params->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; - strcpy(dev->devpath, params->devpath); - - smscore_registry_settype(dev->devpath, params->device_type); - - /* add device to devices list */ - kmutex_lock(&g_smscore_deviceslock); - list_add(&dev->entry, &g_smscore_devices); - kmutex_unlock(&g_smscore_deviceslock); - - *coredev = dev; - - sms_info("device %p created", 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 = coredev->sendrequest_handler(coredev->context, buffer, size); - if (rc < 0) { - sms_info("sendrequest returned error %d", 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 */ - sms_info("IR loading"); - rc = sms_ir_init(coredev); - - if (rc != 0) - sms_err("Error initialization DTV IR sub-module"); - else { - buffer = kmalloc(sizeof(struct SmsMsgData_ST2) + - SMS_DMA_ALIGNMENT, - GFP_KERNEL | GFP_DMA); - if (buffer) { - struct SmsMsgData_ST2 *msg = - (struct SmsMsgData_ST2 *) - SMS_ALIGN_ADDRESS(buffer); - - SMS_INIT_MSG(&msg->xMsgHeader, - MSG_SMS_START_IR_REQ, - sizeof(struct SmsMsgData_ST2)); - msg->msgData[0] = coredev->ir.controller; - msg->msgData[1] = coredev->ir.timeout; - - smsendian_handle_tx_message( - (struct SmsMsgHdr_ST2 *)msg); - rc = smscore_sendrequest_and_wait(coredev, msg, - msg->xMsgHeader. msgLength, - &coredev->ir_init_done); - - kfree(buffer); - } else - sms_err - ("Sending IR initialization message failed"); - } - } else - sms_info("IR port has not been detected"); - - 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 = smscore_set_device_mode( - coredev, smscore_registry_getmode(coredev->devpath)); - if (rc < 0) { - sms_info("set device mode faile , rc %d", rc); - return rc; - } - - kmutex_lock(&g_smscore_deviceslock); - - rc = smscore_notify_callbacks(coredev, coredev->device, 1); - smscore_init_ir(coredev); - - sms_info("device %p started, rc %d", coredev, rc); - - kmutex_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 SmsFirmware_ST *firmware = (struct SmsFirmware_ST *) buffer; - struct SmsMsgHdr_ST *msg; - u32 mem_address; - u8 *payload = firmware->Payload; - int rc = 0; - firmware->StartAddress = le32_to_cpu(firmware->StartAddress); - firmware->Length = le32_to_cpu(firmware->Length); - - mem_address = firmware->StartAddress; - - sms_info("loading FW to addr 0x%x size %d", - 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 | GFP_DMA); - if (!msg) - return -ENOMEM; - - if (coredev->mode != DEVICE_MODE_NONE) { - sms_debug("sending reload command."); - SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ, - sizeof(struct SmsMsgHdr_ST)); - rc = smscore_sendrequest_and_wait(coredev, msg, - msg->msgLength, - &coredev->reload_start_done); - mem_address = *(u32 *) &payload[20]; - } - - while (size && rc >= 0) { - struct SmsDataDownload_ST *DataMsg = - (struct SmsDataDownload_ST *) msg; - int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE); - - SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ, - (u16)(sizeof(struct SmsMsgHdr_ST) + - sizeof(u32) + payload_size)); - - DataMsg->MemAddr = mem_address; - memcpy(DataMsg->Payload, payload, payload_size); - - if ((coredev->device_flags & SMS_ROM_NO_RESPONSE) && - (coredev->mode == DEVICE_MODE_NONE)) - rc = coredev->sendrequest_handler( - coredev->context, DataMsg, - DataMsg->xMsgHeader.msgLength); - else - rc = smscore_sendrequest_and_wait( - coredev, DataMsg, - DataMsg->xMsgHeader.msgLength, - &coredev->data_download_done); - - payload += payload_size; - size -= payload_size; - mem_address += payload_size; - } - - if (rc >= 0) { - if (coredev->mode == DEVICE_MODE_NONE) { - struct SmsMsgData_ST *TriggerMsg = - (struct SmsMsgData_ST *) msg; - - SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ, - sizeof(struct SmsMsgHdr_ST) + - sizeof(u32) * 5); - - TriggerMsg->msgData[0] = firmware->StartAddress; - /* Entry point */ - TriggerMsg->msgData[1] = 5; /* Priority */ - TriggerMsg->msgData[2] = 0x200; /* Stack size */ - TriggerMsg->msgData[3] = 0; /* Parameter */ - TriggerMsg->msgData[4] = 4; /* Task ID */ - - if (coredev->device_flags & SMS_ROM_NO_RESPONSE) { - rc = coredev->sendrequest_handler( - coredev->context, TriggerMsg, - TriggerMsg->xMsgHeader.msgLength); - msleep(100); - } else - rc = smscore_sendrequest_and_wait( - coredev, TriggerMsg, - TriggerMsg->xMsgHeader.msgLength, - &coredev->trigger_done); - } else { - SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ, - sizeof(struct SmsMsgHdr_ST)); - - rc = coredev->sendrequest_handler(coredev->context, - msg, msg->msgLength); - } - msleep(500); - } - - sms_debug("rc=%d, postload=%p ", rc, - coredev->postload_handler); - - kfree(msg); - - return ((rc >= 0) && coredev->postload_handler) ? - coredev->postload_handler(coredev->context) : - rc; -} - -/** - * 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, - char *filename, - loadfirmware_t loadfirmware_handler) -{ - int rc = -ENOENT; - const struct firmware *fw; - u8 *fw_buffer; - - if (loadfirmware_handler == NULL && !(coredev->device_flags & - SMS_DEVICE_FAMILY2)) - return -EINVAL; - - rc = request_firmware(&fw, filename, coredev->device); - if (rc < 0) { - sms_info("failed to open \"%s\"", filename); - return rc; - } - sms_info("read FW %s, size=%zd", filename, fw->size); - fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT), - GFP_KERNEL | GFP_DMA); - if (fw_buffer) { - memcpy(fw_buffer, fw->data, fw->size); - - rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ? - smscore_load_firmware_family2(coredev, - fw_buffer, - fw->size) : - loadfirmware_handler(coredev->context, - fw_buffer, fw->size); - - kfree(fw_buffer); - } else { - sms_info("failed to allocate firmware buffer"); - rc = -ENOMEM; - } - - 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; - - kmutex_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) { - sms_info("exiting although " - "not all buffers released."); - break; - } - - sms_info("waiting for %d buffer(s)", - coredev->num_buffers - num_buffers); - msleep(100); - } - - sms_info("freed %d buffers", num_buffers); - - if (coredev->common_buffer) - dma_free_coherent(NULL, coredev->common_buffer_size, - coredev->common_buffer, coredev->common_buffer_phys); - - if (coredev->fw_buf != NULL) - kfree(coredev->fw_buf); - - list_del(&coredev->entry); - kfree(coredev); - - kmutex_unlock(&g_smscore_deviceslock); - - sms_info("device %p destroyed", coredev); -} -EXPORT_SYMBOL_GPL(smscore_unregister_device); - -static int smscore_detect_mode(struct smscore_device_t *coredev) -{ - void *buffer = kmalloc(sizeof(struct SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT, - GFP_KERNEL | GFP_DMA); - struct SmsMsgHdr_ST *msg = - (struct SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer); - int rc; - - if (!buffer) - return -ENOMEM; - - SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ, - sizeof(struct SmsMsgHdr_ST)); - - rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, - &coredev->version_ex_done); - if (rc == -ETIME) { - sms_err("MSG_SMS_GET_VERSION_EX_REQ failed first try"); - - if (wait_for_completion_timeout(&coredev->resume_done, - msecs_to_jiffies(5000))) { - rc = smscore_sendrequest_and_wait( - coredev, msg, msg->msgLength, - &coredev->version_ex_done); - if (rc < 0) - sms_err("MSG_SMS_GET_VERSION_EX_REQ failed " - "second try, rc %d", rc); - } else - rc = -ETIME; - } - - kfree(buffer); - - return rc; -} - -static char *smscore_fw_lkup[][SMS_NUM_OF_DEVICE_TYPES] = { - /*Stellar NOVA A0 Nova B0 VEGA*/ - /*DVBT*/ - {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, - /*DVBH*/ - {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, - /*TDMB*/ - {"none", "tdmb_nova_12mhz.inp", "tdmb_nova_12mhz_b0.inp", "none"}, - /*DABIP*/ - {"none", "none", "none", "none"}, - /*BDA*/ - {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, - /*ISDBT*/ - {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, - /*ISDBTBDA*/ - {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, - /*CMMB*/ - {"none", "none", "none", "cmmb_vega_12mhz.inp"} -}; - -static inline char *sms_get_fw_name(struct smscore_device_t *coredev, - int mode, enum sms_device_type_st type) -{ - char **fw = sms_get_board(smscore_get_board_id(coredev))->fw; - return (fw && fw[mode]) ? fw[mode] : smscore_fw_lkup[mode][type]; -} - -/** - * 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) -{ - void *buffer; - int rc = 0; - enum sms_device_type_st type; - - sms_debug("set device mode to %d", mode); - if (coredev->device_flags & SMS_DEVICE_FAMILY2) { - if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) { - sms_err("invalid mode specified %d", 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) { - sms_err("mode detect failed %d", rc); - return rc; - } - } - - if (coredev->mode == mode) { - sms_info("device mode %d already set", mode); - return 0; - } - - if (!(coredev->modes_supported & (1 << mode))) { - char *fw_filename; - - type = smscore_registry_gettype(coredev->devpath); - fw_filename = sms_get_fw_name(coredev, mode, type); - - rc = smscore_load_firmware_from_file(coredev, - fw_filename, NULL); - if (rc < 0) { - sms_warn("error %d loading firmware: %s, " - "trying again with default firmware", - rc, fw_filename); - - /* try again with the default firmware */ - fw_filename = smscore_fw_lkup[mode][type]; - rc = smscore_load_firmware_from_file(coredev, - fw_filename, NULL); - - if (rc < 0) { - sms_warn("error %d loading " - "firmware: %s", rc, - fw_filename); - return rc; - } - } - sms_log("firmware download success: %s", fw_filename); - } else - sms_info("mode %d supported by running " - "firmware", mode); - - buffer = kmalloc(sizeof(struct SmsMsgData_ST) + - SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); - if (buffer) { - struct SmsMsgData_ST *msg = - (struct SmsMsgData_ST *) - SMS_ALIGN_ADDRESS(buffer); - - SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ, - sizeof(struct SmsMsgData_ST)); - msg->msgData[0] = mode; - - rc = smscore_sendrequest_and_wait( - coredev, msg, msg->xMsgHeader.msgLength, - &coredev->init_device_done); - - kfree(buffer); - } else { - sms_err("Could not allocate buffer for " - "init device message."); - rc = -ENOMEM; - } - } else { - if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) { - sms_err("invalid mode specified %d", 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) { - coredev->mode = mode; - coredev->device_flags &= ~SMS_DEVICE_NOT_READY; - } - - if (rc < 0) - sms_err("return error code %d.", rc); - 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 SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) ((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) { - sms_debug("\ndata rate %d bytes/secs", - (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->msgType == MSG_SMS_HO_PER_SLICES_IND) || - (phdr->msgType == MSG_SMS_TRANSMISSION_IND)) { - if (coredev->mode == DEVICE_MODE_DVBT_BDA) - phdr->msgDstId = DVBT_BDA_CONTROL_MSG_ID; - } - - - client = smscore_find_client(coredev, phdr->msgType, phdr->msgDstId); - - /* 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->msgType) { - case MSG_SMS_GET_VERSION_EX_RES: - { - struct SmsVersionRes_ST *ver = - (struct SmsVersionRes_ST *) phdr; - sms_debug("MSG_SMS_GET_VERSION_EX_RES " - "id %d prots 0x%x ver %d.%d", - ver->FirmwareId, ver->SupportedProtocols, - ver->RomVersionMajor, ver->RomVersionMinor); - - coredev->mode = ver->FirmwareId == 255 ? - DEVICE_MODE_NONE : ver->FirmwareId; - coredev->modes_supported = ver->SupportedProtocols; - - complete(&coredev->version_ex_done); - break; - } - case MSG_SMS_INIT_DEVICE_RES: - sms_debug("MSG_SMS_INIT_DEVICE_RES"); - complete(&coredev->init_device_done); - break; - case MSG_SW_RELOAD_START_RES: - sms_debug("MSG_SW_RELOAD_START_RES"); - complete(&coredev->reload_start_done); - break; - case MSG_SMS_DATA_DOWNLOAD_RES: - complete(&coredev->data_download_done); - break; - case MSG_SW_RELOAD_EXEC_RES: - sms_debug("MSG_SW_RELOAD_EXEC_RES"); - break; - case MSG_SMS_SWDOWNLOAD_TRIGGER_RES: - sms_debug("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: - sms_debug("MSG_SMS_GPIO_CONFIG_EX_RES"); - complete(&coredev->gpio_configuration_done); - break; - case MSG_SMS_GPIO_SET_LEVEL_RES: - sms_debug("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]; - sms_debug("MSG_SMS_GPIO_GET_LEVEL_RES gpio level %d", - 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 SmsMsgHdr_ST)), - (int)phdr->msgLength - - sizeof(struct SmsMsgHdr_ST)); - break; - - default: - 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. - */ - -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) { - sms_err("bad parameter."); - return -EINVAL; - } - registered_client = smscore_find_client(coredev, data_type, id); - if (registered_client == client) - return 0; - - if (registered_client) { - sms_err("The msg ID already registered to another client."); - return -EEXIST; - } - listentry = kzalloc(sizeof(struct smscore_idlist_t), GFP_KERNEL); - if (!listentry) { - sms_err("Can't allocate memory for client id."); - 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)) { - sms_err("Client already exist."); - return -EEXIST; - } - - newclient = kzalloc(sizeof(struct smscore_client_t), GFP_KERNEL); - if (!newclient) { - sms_err("Failed to allocate memory for client."); - 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; - sms_debug("%p %d %d", 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); - } - - sms_info("%p", 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 SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) buffer; - int rc; - - if (client == NULL) { - sms_err("Got NULL client"); - return -EINVAL; - } - - coredev = client->coredev; - - /* check that no other channel with same id exists */ - if (coredev == NULL) { - sms_err("Got NULL coredev"); - return -EINVAL; - } - - rc = smscore_validate_client(client->coredev, client, 0, - phdr->msgSrcId); - 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 SmsMsgHdr_ST hdr; - u32 data[6]; - } msg; - - if (coredev->device_flags & SMS_DEVICE_FAMILY2) { - msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - msg.hdr.msgDstId = HIF_TASK; - msg.hdr.msgFlags = 0; - msg.hdr.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; - msg.hdr.msgLength = 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_16mA: - msg.data[3] = 7; /* Nova - 16mA */ - break; - case SMS_GPIO_OUTPUTDRIVING_12mA: - msg.data[3] = 5; /* Nova - 11mA */ - break; - case SMS_GPIO_OUTPUTDRIVING_8mA: - msg.data[3] = 3; /* Nova - 7mA */ - break; - case SMS_GPIO_OUTPUTDRIVING_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 SmsMsgHdr_ST hdr; - u32 data[3]; - } msg; - - if (pin > MAX_GPIO_PIN_NUMBER) - return -EINVAL; - - msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - msg.hdr.msgDstId = HIF_TASK; - msg.hdr.msgFlags = 0; - msg.hdr.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; - msg.hdr.msgLength = 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 GetGpioPinParams(u32 PinNum, u32 *pTranslatedPinNum, - u32 *pGroupNum, u32 *pGroupCfg) { - - *pGroupCfg = 1; - - if (PinNum <= 1) { - *pTranslatedPinNum = 0; - *pGroupNum = 9; - *pGroupCfg = 2; - } else if (PinNum >= 2 && PinNum <= 6) { - *pTranslatedPinNum = 2; - *pGroupNum = 0; - *pGroupCfg = 2; - } else if (PinNum >= 7 && PinNum <= 11) { - *pTranslatedPinNum = 7; - *pGroupNum = 1; - } else if (PinNum >= 12 && PinNum <= 15) { - *pTranslatedPinNum = 12; - *pGroupNum = 2; - *pGroupCfg = 3; - } else if (PinNum == 16) { - *pTranslatedPinNum = 16; - *pGroupNum = 23; - } else if (PinNum >= 17 && PinNum <= 24) { - *pTranslatedPinNum = 17; - *pGroupNum = 3; - } else if (PinNum == 25) { - *pTranslatedPinNum = 25; - *pGroupNum = 6; - } else if (PinNum >= 26 && PinNum <= 28) { - *pTranslatedPinNum = 26; - *pGroupNum = 4; - } else if (PinNum == 29) { - *pTranslatedPinNum = 29; - *pGroupNum = 5; - *pGroupCfg = 2; - } else if (PinNum == 30) { - *pTranslatedPinNum = 30; - *pGroupNum = 8; - } else if (PinNum == 31) { - *pTranslatedPinNum = 31; - *pGroupNum = 17; - } else - return -1; - - *pGroupCfg <<= 24; - - return 0; -} - -int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, - struct smscore_gpio_config *pGpioConfig) { - - u32 totalLen; - u32 TranslatedPinNum = 0; - u32 GroupNum = 0; - u32 ElectricChar; - u32 groupCfg; - void *buffer; - int rc; - - struct SetGpioMsg { - struct SmsMsgHdr_ST xMsgHeader; - u32 msgData[6]; - } *pMsg; - - - if (PinNum > MAX_GPIO_PIN_NUMBER) - return -EINVAL; - - if (pGpioConfig == NULL) - return -EINVAL; - - totalLen = sizeof(struct SmsMsgHdr_ST) + (sizeof(u32) * 6); - - buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, - GFP_KERNEL | GFP_DMA); - if (!buffer) - return -ENOMEM; - - pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); - - pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - pMsg->xMsgHeader.msgDstId = HIF_TASK; - pMsg->xMsgHeader.msgFlags = 0; - pMsg->xMsgHeader.msgLength = (u16) totalLen; - pMsg->msgData[0] = PinNum; - - if (!(coredev->device_flags & SMS_DEVICE_FAMILY2)) { - pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_REQ; - if (GetGpioPinParams(PinNum, &TranslatedPinNum, &GroupNum, - &groupCfg) != 0) { - rc = -EINVAL; - goto free; - } - - pMsg->msgData[1] = TranslatedPinNum; - pMsg->msgData[2] = GroupNum; - ElectricChar = (pGpioConfig->PullUpDown) - | (pGpioConfig->InputCharacteristics << 2) - | (pGpioConfig->OutputSlewRate << 3) - | (pGpioConfig->OutputDriving << 4); - pMsg->msgData[3] = ElectricChar; - pMsg->msgData[4] = pGpioConfig->Direction; - pMsg->msgData[5] = groupCfg; - } else { - pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; - pMsg->msgData[1] = pGpioConfig->PullUpDown; - pMsg->msgData[2] = pGpioConfig->OutputSlewRate; - pMsg->msgData[3] = pGpioConfig->OutputDriving; - pMsg->msgData[4] = pGpioConfig->Direction; - pMsg->msgData[5] = 0; - } - - smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); - rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, - &coredev->gpio_configuration_done); - - if (rc != 0) { - if (rc == -ETIME) - sms_err("smscore_gpio_configure timeout"); - else - sms_err("smscore_gpio_configure error"); - } -free: - kfree(buffer); - - return rc; -} - -int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, - u8 NewLevel) { - - u32 totalLen; - int rc; - void *buffer; - - struct SetGpioMsg { - struct SmsMsgHdr_ST xMsgHeader; - u32 msgData[3]; /* keep it 3 ! */ - } *pMsg; - - if ((NewLevel > 1) || (PinNum > MAX_GPIO_PIN_NUMBER)) - return -EINVAL; - - totalLen = sizeof(struct SmsMsgHdr_ST) + - (3 * sizeof(u32)); /* keep it 3 ! */ - - buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, - GFP_KERNEL | GFP_DMA); - if (!buffer) - return -ENOMEM; - - pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); - - pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - pMsg->xMsgHeader.msgDstId = HIF_TASK; - pMsg->xMsgHeader.msgFlags = 0; - pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; - pMsg->xMsgHeader.msgLength = (u16) totalLen; - pMsg->msgData[0] = PinNum; - pMsg->msgData[1] = NewLevel; - - /* Send message to SMS */ - smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); - rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, - &coredev->gpio_set_level_done); - - if (rc != 0) { - if (rc == -ETIME) - sms_err("smscore_gpio_set_level timeout"); - else - sms_err("smscore_gpio_set_level error"); - } - kfree(buffer); - - return rc; -} - -int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum, - u8 *level) { - - u32 totalLen; - int rc; - void *buffer; - - struct SetGpioMsg { - struct SmsMsgHdr_ST xMsgHeader; - u32 msgData[2]; - } *pMsg; - - - if (PinNum > MAX_GPIO_PIN_NUMBER) - return -EINVAL; - - totalLen = sizeof(struct SmsMsgHdr_ST) + (2 * sizeof(u32)); - - buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, - GFP_KERNEL | GFP_DMA); - if (!buffer) - return -ENOMEM; - - pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); - - pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - pMsg->xMsgHeader.msgDstId = HIF_TASK; - pMsg->xMsgHeader.msgFlags = 0; - pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_GET_LEVEL_REQ; - pMsg->xMsgHeader.msgLength = (u16) totalLen; - pMsg->msgData[0] = PinNum; - pMsg->msgData[1] = 0; - - /* Send message to SMS */ - smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); - rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, - &coredev->gpio_get_level_done); - - if (rc != 0) { - if (rc == -ETIME) - sms_err("smscore_gpio_get_level timeout"); - else - sms_err("smscore_gpio_get_level error"); - } - 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) -{ - int rc = 0; - - INIT_LIST_HEAD(&g_smscore_notifyees); - INIT_LIST_HEAD(&g_smscore_devices); - kmutex_init(&g_smscore_deviceslock); - - INIT_LIST_HEAD(&g_smscore_registry); - kmutex_init(&g_smscore_registrylock); - - return rc; -} - -static void __exit smscore_module_exit(void) -{ - kmutex_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(¬ifyee->entry); - kfree(notifyee); - } - kmutex_unlock(&g_smscore_deviceslock); - - kmutex_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); - } - kmutex_unlock(&g_smscore_registrylock); - - sms_debug(""); -} - -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"); diff --git a/drivers/media/usb/siano/smscoreapi.h b/drivers/media/usb/siano/smscoreapi.h deleted file mode 100644 index c592ae090397..000000000000 --- a/drivers/media/usb/siano/smscoreapi.h +++ /dev/null @@ -1,775 +0,0 @@ -/**************************************************************** - -Siano Mobile Silicon, Inc. -MDTV receiver kernel modules. -Copyright (C) 2006-2008, Uri Shkolnik, Anatoly Greenblat - -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, see . - -****************************************************************/ - -#ifndef __SMS_CORE_API_H__ -#define __SMS_CORE_API_H__ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "smsir.h" - -#define kmutex_init(_p_) mutex_init(_p_) -#define kmutex_lock(_p_) mutex_lock(_p_) -#define kmutex_trylock(_p_) mutex_trylock(_p_) -#define kmutex_unlock(_p_) mutex_unlock(_p_) - -#ifndef min -#define min(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -#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_FAMILY2 1 -#define SMS_ROM_NO_RESPONSE 2 -#define SMS_DEVICE_NOT_READY 0x8000000 - -enum sms_device_type_st { - SMS_STELLAR = 0, - SMS_NOVA_A0, - SMS_NOVA_B0, - SMS_VEGA, - SMS_NUM_OF_DEVICE_TYPES -}; - -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 (*loadfirmware_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; - - 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; - - 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; - - /* host <--> device messages */ - struct completion version_ex_done, data_download_done, trigger_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; - - /* Infrared (IR) */ - struct ir_t ir; - - int led_state; -}; - -/* GPIO definitions for antenna frequency domain control (SMS8021) */ -#define SMS_ANTENNA_GPIO_0 1 -#define SMS_ANTENNA_GPIO_1 0 - -#define BW_8_MHZ 0 -#define BW_7_MHZ 1 -#define BW_6_MHZ 2 -#define BW_5_MHZ 3 -#define BW_ISDBT_1SEG 4 -#define BW_ISDBT_3SEG 5 - -#define MSG_HDR_FLAG_SPLIT_MSG 4 - -#define MAX_GPIO_PIN_NUMBER 31 - -#define HIF_TASK 11 -#define SMS_HOST_LIB 150 -#define DVBT_BDA_CONTROL_MSG_ID 201 - -#define SMS_MAX_PAYLOAD_SIZE 240 -#define SMS_TUNE_TIMEOUT 500 - -#define MSG_SMS_GPIO_CONFIG_REQ 507 -#define MSG_SMS_GPIO_CONFIG_RES 508 -#define MSG_SMS_GPIO_SET_LEVEL_REQ 509 -#define MSG_SMS_GPIO_SET_LEVEL_RES 510 -#define MSG_SMS_GPIO_GET_LEVEL_REQ 511 -#define MSG_SMS_GPIO_GET_LEVEL_RES 512 -#define MSG_SMS_RF_TUNE_REQ 561 -#define MSG_SMS_RF_TUNE_RES 562 -#define MSG_SMS_INIT_DEVICE_REQ 578 -#define MSG_SMS_INIT_DEVICE_RES 579 -#define MSG_SMS_ADD_PID_FILTER_REQ 601 -#define MSG_SMS_ADD_PID_FILTER_RES 602 -#define MSG_SMS_REMOVE_PID_FILTER_REQ 603 -#define MSG_SMS_REMOVE_PID_FILTER_RES 604 -#define MSG_SMS_DAB_CHANNEL 607 -#define MSG_SMS_GET_PID_FILTER_LIST_REQ 608 -#define MSG_SMS_GET_PID_FILTER_LIST_RES 609 -#define MSG_SMS_GET_STATISTICS_RES 616 -#define MSG_SMS_GET_STATISTICS_REQ 615 -#define MSG_SMS_HO_PER_SLICES_IND 630 -#define MSG_SMS_SET_ANTENNA_CONFIG_REQ 651 -#define MSG_SMS_SET_ANTENNA_CONFIG_RES 652 -#define MSG_SMS_SLEEP_RESUME_COMP_IND 655 -#define MSG_SMS_DATA_DOWNLOAD_REQ 660 -#define MSG_SMS_DATA_DOWNLOAD_RES 661 -#define MSG_SMS_SWDOWNLOAD_TRIGGER_REQ 664 -#define MSG_SMS_SWDOWNLOAD_TRIGGER_RES 665 -#define MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ 666 -#define MSG_SMS_SWDOWNLOAD_BACKDOOR_RES 667 -#define MSG_SMS_GET_VERSION_EX_REQ 668 -#define MSG_SMS_GET_VERSION_EX_RES 669 -#define MSG_SMS_SET_CLOCK_OUTPUT_REQ 670 -#define MSG_SMS_I2C_SET_FREQ_REQ 685 -#define MSG_SMS_GENERIC_I2C_REQ 687 -#define MSG_SMS_GENERIC_I2C_RES 688 -#define MSG_SMS_DVBT_BDA_DATA 693 -#define MSG_SW_RELOAD_REQ 697 -#define MSG_SMS_DATA_MSG 699 -#define MSG_SW_RELOAD_START_REQ 702 -#define MSG_SW_RELOAD_START_RES 703 -#define MSG_SW_RELOAD_EXEC_REQ 704 -#define MSG_SW_RELOAD_EXEC_RES 705 -#define MSG_SMS_SPI_INT_LINE_SET_REQ 710 -#define MSG_SMS_GPIO_CONFIG_EX_REQ 712 -#define MSG_SMS_GPIO_CONFIG_EX_RES 713 -#define MSG_SMS_ISDBT_TUNE_REQ 776 -#define MSG_SMS_ISDBT_TUNE_RES 777 -#define MSG_SMS_TRANSMISSION_IND 782 -#define MSG_SMS_START_IR_REQ 800 -#define MSG_SMS_START_IR_RES 801 -#define MSG_SMS_IR_SAMPLES_IND 802 -#define MSG_SMS_SIGNAL_DETECTED_IND 827 -#define MSG_SMS_NO_SIGNAL_IND 828 - -#define SMS_INIT_MSG_EX(ptr, type, src, dst, len) do { \ - (ptr)->msgType = type; (ptr)->msgSrcId = src; (ptr)->msgDstId = dst; \ - (ptr)->msgLength = len; (ptr)->msgFlags = 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_MAX, -}; - -struct SmsMsgHdr_ST { - u16 msgType; - u8 msgSrcId; - u8 msgDstId; - u16 msgLength; /* Length of entire message, including header */ - u16 msgFlags; -}; - -struct SmsMsgData_ST { - struct SmsMsgHdr_ST xMsgHeader; - u32 msgData[1]; -}; - -struct SmsMsgData_ST2 { - struct SmsMsgHdr_ST xMsgHeader; - u32 msgData[2]; -}; - -struct SmsDataDownload_ST { - struct SmsMsgHdr_ST xMsgHeader; - u32 MemAddr; - u8 Payload[SMS_MAX_PAYLOAD_SIZE]; -}; - -struct SmsVersionRes_ST { - struct SmsMsgHdr_ST xMsgHeader; - - u16 ChipModel; /* e.g. 0x1102 for SMS-1102 "Nova" */ - u8 Step; /* 0 - Step A */ - u8 MetalFix; /* 0 - Metal 0 */ - - /* FirmwareId 0xFF if ROM, otherwise the - * value indicated by SMSHOSTLIB_DEVICE_MODES_E */ - u8 FirmwareId; - /* SupportedProtocols Bitwise OR combination of - * supported protocols */ - u8 SupportedProtocols; - - u8 VersionMajor; - u8 VersionMinor; - u8 VersionPatch; - u8 VersionFieldPatch; - - u8 RomVersionMajor; - u8 RomVersionMinor; - u8 RomVersionPatch; - u8 RomVersionFieldPatch; - - u8 TextLabel[34]; -}; - -struct SmsFirmware_ST { - u32 CheckSum; - u32 Length; - u32 StartAddress; - u8 Payload[1]; -}; - -/* Statistics information returned as response for - * SmsHostApiGetStatistics_Req */ -struct SMSHOSTLIB_STATISTICS_ST { - u32 Reserved; /* Reserved */ - - /* Common parameters */ - u32 IsRfLocked; /* 0 - not locked, 1 - locked */ - u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ - u32 IsExternalLNAOn; /* 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 InBandPwr; /* In band power in dBM */ - s32 CarrierOffset; /* Carrier Offset in bin/1024 */ - - /* Transmission parameters */ - u32 Frequency; /* Frequency in Hz */ - u32 Bandwidth; /* Bandwidth in MHz, valid only for DVB-T/H */ - u32 TransmissionMode; /* Transmission Mode, for DAB modes 1-4, - for DVB-T/H FFT mode carriers in Kilos */ - u32 ModemState; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET, - valid only for DVB-T/H */ - u32 GuardInterval; /* Guard Interval from - SMSHOSTLIB_GUARD_INTERVALS_ET, valid only for DVB-T/H */ - u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET, - valid only for DVB-T/H */ - u32 LPCodeRate; /* 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 BurstSize; /* Current burst size in bytes, - valid only for DVB-H */ - u32 BurstDuration; /* Current burst duration in mSec, - valid only for DVB-H */ - u32 BurstCycleTime; /* Current burst cycle time in mSec, - valid only for DVB-H */ - u32 CalculatedBurstCycleTime;/* Current burst cycle time in mSec, - as calculated by demodulator, valid only for DVB-H */ - u32 NumOfRows; /* Number of rows in MPE table, - valid only for DVB-H */ - u32 NumOfPaddCols; /* Number of padding columns in MPE table, - valid only for DVB-H */ - u32 NumOfPunctCols; /* Number of puncturing columns in MPE table, - valid only for DVB-H */ - u32 ErrorTSPackets; /* Number of erroneous - transport-stream packets */ - u32 TotalTSPackets; /* Total number of transport-stream packets */ - u32 NumOfValidMpeTlbs; /* Number of MPE tables which do not include - errors after MPE RS decoding */ - u32 NumOfInvalidMpeTlbs;/* Number of MPE tables which include errors - after MPE RS decoding */ - u32 NumOfCorrectedMpeTlbs;/* Number of MPE tables which were - corrected by MPE RS decoding */ - /* Common params */ - u32 BERErrorCount; /* Number of errornous SYNC bits. */ - u32 BERBitCount; /* Total number of SYNC bits. */ - - /* Interface information */ - u32 SmsToHostTxErrors; /* Total number of transmission errors. */ - - /* DAB/T-DMB */ - u32 PreBER; /* DAB/T-DMB only: Pre Viterbi BER [1E-5] */ - - /* DVB-H TPS parameters */ - u32 CellId; /* TPS Cell ID in bits 15..0, bits 31..16 zero; - if set to 0xFFFFFFFF cell_id not yet recovered */ - u32 DvbhSrvIndHP; /* DVB-H service indication info, bit 1 - - Time Slicing indicator, bit 0 - MPE-FEC indicator */ - u32 DvbhSrvIndLP; /* DVB-H service indication info, bit 1 - - Time Slicing indicator, bit 0 - MPE-FEC indicator */ - - u32 NumMPEReceived; /* DVB-H, Num MPE section received */ - - u32 ReservedFields[10]; /* Reserved */ -}; - -struct SmsMsgStatisticsInfo_ST { - u32 RequestResult; - - struct SMSHOSTLIB_STATISTICS_ST Stat; - - /* Split the calc of the SNR in DAB */ - u32 Signal; /* dB */ - u32 Noise; /* dB */ - -}; - -struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST { - /* Per-layer information */ - u32 CodeRate; /* 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 BERErrorCount; /* Post Viterbi Error Bits Count */ - u32 BERBitCount; /* Post Viterbi Total Bits Count */ - u32 PreBER; /* Pre Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A */ - u32 TS_PER; /* Transport stream PER [%], 0xFFFFFFFF indicate N/A */ - u32 ErrorTSPackets; /* Number of erroneous transport-stream packets */ - u32 TotalTSPackets; /* Total number of transport-stream packets */ - u32 TILdepthI; /* Time interleaver depth I parameter, - * 255 means layer does not exist */ - u32 NumberOfSegments; /* Number of segments in layer A, - * 255 means layer does not exist */ - u32 TMCCErrors; /* TMCC errors */ -}; - -struct SMSHOSTLIB_STATISTICS_ISDBT_ST { - u32 StatisticsType; /* 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 FullSize; /* Total size of the structure returned by the modem. - * If the size requested by the host is smaller than - * FullSize, the struct will be truncated */ - - /* Common parameters */ - u32 IsRfLocked; /* 0 - not locked, 1 - locked */ - u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ - u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ - - /* Reception quality */ - s32 SNR; /* dB */ - s32 RSSI; /* dBm */ - s32 InBandPwr; /* In band power in dBM */ - s32 CarrierOffset; /* Carrier Offset in Hz */ - - /* Transmission parameters */ - u32 Frequency; /* Frequency in Hz */ - u32 Bandwidth; /* Bandwidth in MHz */ - u32 TransmissionMode; /* ISDB-T transmission mode */ - u32 ModemState; /* 0 - Acquisition, 1 - Locked */ - u32 GuardInterval; /* Guard Interval, 1 divided by value */ - u32 SystemType; /* ISDB-T system type (ISDB-T / ISDB-Tsb) */ - u32 PartialReception; /* TRUE - partial reception, FALSE otherwise */ - u32 NumOfLayers; /* Number of ISDB-T layers in the network */ - - /* Per-layer information */ - /* Layers A, B and C */ - struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST LayerInfo[3]; - /* Per-layer statistics, see SMSHOSTLIB_ISDBT_LAYER_STAT_ST */ - - /* Interface information */ - u32 SmsToHostTxErrors; /* Total number of transmission errors. */ -}; - -struct PID_STATISTICS_DATA_S { - 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 PID_DATA_S { - u32 pid; - u32 num_rows; - struct PID_STATISTICS_DATA_S 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.TransmissionMode == 0) \ - _stat.TransmissionMode = 2; \ - else if (_stat.TransmissionMode == 1) \ - _stat.TransmissionMode = 8; \ - else \ - _stat.TransmissionMode = 4; - -struct TRANSMISSION_STATISTICS_S { - u32 Frequency; /* Frequency in Hz */ - u32 Bandwidth; /* Bandwidth in MHz */ - u32 TransmissionMode; /* FFT mode carriers in Kilos */ - u32 GuardInterval; /* Guard Interval from - SMSHOSTLIB_GUARD_INTERVALS_ET */ - u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET */ - u32 LPCodeRate; /* 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 CellId; /* TPS Cell ID in bits 15..0, bits 31..16 zero; - if set to 0xFFFFFFFF cell_id not yet recovered */ - u32 DvbhSrvIndHP; /* DVB-H service indication info, bit 1 - - Time Slicing indicator, bit 0 - MPE-FEC indicator */ - u32 DvbhSrvIndLP; /* DVB-H service indication info, bit 1 - - Time Slicing indicator, bit 0 - MPE-FEC indicator */ - u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ -}; - -struct RECEPTION_STATISTICS_S { - u32 IsRfLocked; /* 0 - not locked, 1 - locked */ - u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ - u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ - - u32 ModemState; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */ - s32 SNR; /* dB */ - u32 BER; /* Post Viterbi BER [1E-5] */ - u32 BERErrorCount; /* Number of erronous SYNC bits. */ - u32 BERBitCount; /* 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 InBandPwr; /* In band power in dBM */ - s32 CarrierOffset; /* Carrier Offset in bin/1024 */ - u32 ErrorTSPackets; /* Number of erroneous - transport-stream packets */ - u32 TotalTSPackets; /* Total number of transport-stream packets */ - - s32 MRC_SNR; /* dB */ - s32 MRC_RSSI; /* dBm */ - s32 MRC_InBandPwr; /* In band power in dBM */ -}; - - -/* Statistics information returned as response for - * SmsHostApiGetStatisticsEx_Req for DVB applications, SMS1100 and up */ -struct SMSHOSTLIB_STATISTICS_DVB_S { - /* Reception */ - struct RECEPTION_STATISTICS_S ReceptionData; - - /* Transmission parameters */ - struct TRANSMISSION_STATISTICS_S TransmissionData; - - /* Burst parameters, valid only for DVB-H */ -#define SRVM_MAX_PID_FILTERS 8 - struct PID_DATA_S PidData[SRVM_MAX_PID_FILTERS]; -}; - -struct SRVM_SIGNAL_STATUS_S { - u32 result; - u32 snr; - u32 tsPackets; - u32 etsPackets; - u32 constellation; - u32 hpCode; - u32 tpsSrvIndLP; - u32 tpsSrvIndHP; - u32 cellId; - u32 reason; - - s32 inBandPower; - u32 requestId; -}; - -struct SMSHOSTLIB_I2C_REQ_ST { - u32 DeviceAddress; /* I2c device address */ - u32 WriteCount; /* number of bytes to write */ - u32 ReadCount; /* number of bytes to read */ - u8 Data[1]; -}; - -struct SMSHOSTLIB_I2C_RES_ST { - u32 Status; /* non-zero value in case of failure */ - u32 ReadCount; /* number of bytes read */ - u8 Data[1]; -}; - - -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; - -#define SMS_GPIO_OUTPUTSLEWRATE_FAST 0 -#define SMS_GPIO_OUTPUTSLEWRATE_SLOW 1 - u8 outputslewrate; - -#define SMS_GPIO_OUTPUTDRIVING_4mA 0 -#define SMS_GPIO_OUTPUTDRIVING_8mA 1 -#define SMS_GPIO_OUTPUTDRIVING_12mA 2 -#define SMS_GPIO_OUTPUTDRIVING_16mA 3 - u8 outputdriving; -}; - -struct smscore_gpio_config { -#define SMS_GPIO_DIRECTION_INPUT 0 -#define SMS_GPIO_DIRECTION_OUTPUT 1 - u8 Direction; - -#define SMS_GPIO_PULL_UP_DOWN_NONE 0 -#define SMS_GPIO_PULL_UP_DOWN_PULLDOWN 1 -#define SMS_GPIO_PULL_UP_DOWN_PULLUP 2 -#define SMS_GPIO_PULL_UP_DOWN_KEEPER 3 - u8 PullUpDown; - -#define SMS_GPIO_INPUT_CHARACTERISTICS_NORMAL 0 -#define SMS_GPIO_INPUT_CHARACTERISTICS_SCHMITT 1 - u8 InputCharacteristics; - -#define SMS_GPIO_OUTPUT_SLEW_RATE_SLOW 1 /* 10xx */ -#define SMS_GPIO_OUTPUT_SLEW_RATE_FAST 0 /* 10xx */ - - -#define SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS 0 /* 11xx */ -#define SMS_GPIO_OUTPUT_SLEW_RATE_0_9_V_NS 1 /* 11xx */ -#define SMS_GPIO_OUTPUT_SLEW_RATE_1_7_V_NS 2 /* 11xx */ -#define SMS_GPIO_OUTPUT_SLEW_RATE_3_3_V_NS 3 /* 11xx */ - u8 OutputSlewRate; - -#define SMS_GPIO_OUTPUT_DRIVING_S_4mA 0 /* 10xx */ -#define SMS_GPIO_OUTPUT_DRIVING_S_8mA 1 /* 10xx */ -#define SMS_GPIO_OUTPUT_DRIVING_S_12mA 2 /* 10xx */ -#define SMS_GPIO_OUTPUT_DRIVING_S_16mA 3 /* 10xx */ - -#define SMS_GPIO_OUTPUT_DRIVING_1_5mA 0 /* 11xx */ -#define SMS_GPIO_OUTPUT_DRIVING_2_8mA 1 /* 11xx */ -#define SMS_GPIO_OUTPUT_DRIVING_4mA 2 /* 11xx */ -#define SMS_GPIO_OUTPUT_DRIVING_7mA 3 /* 11xx */ -#define SMS_GPIO_OUTPUT_DRIVING_10mA 4 /* 11xx */ -#define SMS_GPIO_OUTPUT_DRIVING_11mA 5 /* 11xx */ -#define SMS_GPIO_OUTPUT_DRIVING_14mA 6 /* 11xx */ -#define SMS_GPIO_OUTPUT_DRIVING_16mA 7 /* 11xx */ - u8 OutputDriving; -}; - -extern void smscore_registry_setmode(char *devpath, int mode); -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); -extern void smscore_unregister_device(struct smscore_device_t *coredev); - -extern int smscore_start_device(struct smscore_device_t *coredev); -extern int smscore_load_firmware(struct smscore_device_t *coredev, - char *filename, - loadfirmware_t loadfirmware_handler); - -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 int smscore_get_common_buffer_size(struct smscore_device_t *coredev); -extern int smscore_map_common_buffer(struct smscore_device_t *coredev, - struct vm_area_struct *vma); -extern int smscore_get_fw_filename(struct smscore_device_t *coredev, - int mode, char *filename); -extern int smscore_send_fw_file(struct smscore_device_t *coredev, - u8 *ufwbuf, int size); - -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 PinNum, - struct smscore_gpio_config *pGpioConfig); -extern int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, - u8 NewLevel); -extern int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum, - 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); - - -/* ------------------------------------------------------------------------ */ - -#define DBG_INFO 1 -#define DBG_ADV 2 - -#define sms_printk(kern, fmt, arg...) \ - printk(kern "%s: " fmt "\n", __func__, ##arg) - -#define dprintk(kern, lvl, fmt, arg...) do {\ - if (sms_dbg & lvl) \ - sms_printk(kern, fmt, ##arg); } while (0) - -#define sms_log(fmt, arg...) sms_printk(KERN_INFO, fmt, ##arg) -#define sms_err(fmt, arg...) \ - sms_printk(KERN_ERR, "line: %d: " fmt, __LINE__, ##arg) -#define sms_warn(fmt, arg...) sms_printk(KERN_WARNING, fmt, ##arg) -#define sms_info(fmt, arg...) \ - dprintk(KERN_INFO, DBG_INFO, fmt, ##arg) -#define sms_debug(fmt, arg...) \ - dprintk(KERN_DEBUG, DBG_ADV, fmt, ##arg) - - -#endif /* __SMS_CORE_API_H__ */ diff --git a/drivers/media/usb/siano/smsdvb.c b/drivers/media/usb/siano/smsdvb.c deleted file mode 100644 index aa77e54a8fae..000000000000 --- a/drivers/media/usb/siano/smsdvb.c +++ /dev/null @@ -1,1078 +0,0 @@ -/**************************************************************** - -Siano Mobile Silicon, Inc. -MDTV receiver kernel modules. -Copyright (C) 2006-2008, Uri Shkolnik - -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, see . - -****************************************************************/ - -#include -#include -#include - -#include "dmxdev.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dvb_frontend.h" - -#include "smscoreapi.h" -#include "smsendian.h" -#include "sms-cards.h" - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -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; - - fe_status_t fe_status; - - struct completion tune_done; - - struct SMSHOSTLIB_STATISTICS_DVB_S sms_stat_dvb; - int event_fe_state; - int event_unc_state; -}; - -static struct list_head g_smsdvb_clients; -static struct mutex g_smsdvb_clientslock; - -static int sms_dbg; -module_param_named(debug, sms_dbg, int, 0644); -MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); - -/* 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: - sms_debug("DVB3_EVENT_INIT"); - sms_board_event(coredev, BOARD_EVENT_BIND); - break; - case DVB3_EVENT_SLEEP: - sms_debug("DVB3_EVENT_SLEEP"); - sms_board_event(coredev, BOARD_EVENT_POWER_SUSPEND); - break; - case DVB3_EVENT_HOTPLUG: - sms_debug("DVB3_EVENT_HOTPLUG"); - 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; - sms_debug("DVB3_EVENT_FE_LOCK"); - 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; - sms_debug("DVB3_EVENT_FE_UNLOCK"); - 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; - sms_debug("DVB3_EVENT_UNC_OK"); - 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; - sms_debug("DVB3_EVENT_UNC_ERR"); - sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_ERRORS); - } - break; - - default: - sms_err("Unknown dvb3 api event"); - break; - } -} - - -static void smsdvb_update_dvb_stats(struct RECEPTION_STATISTICS_S *pReceptionData, - struct SMSHOSTLIB_STATISTICS_ST *p) -{ - if (sms_dbg & 2) { - printk(KERN_DEBUG "Reserved = %d", p->Reserved); - printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); - printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); - printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); - printk(KERN_DEBUG "SNR = %d", p->SNR); - printk(KERN_DEBUG "BER = %d", p->BER); - printk(KERN_DEBUG "FIB_CRC = %d", p->FIB_CRC); - printk(KERN_DEBUG "TS_PER = %d", p->TS_PER); - printk(KERN_DEBUG "MFER = %d", p->MFER); - printk(KERN_DEBUG "RSSI = %d", p->RSSI); - printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); - printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); - printk(KERN_DEBUG "Frequency = %d", p->Frequency); - printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); - printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); - printk(KERN_DEBUG "ModemState = %d", p->ModemState); - printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); - printk(KERN_DEBUG "CodeRate = %d", p->CodeRate); - printk(KERN_DEBUG "LPCodeRate = %d", p->LPCodeRate); - printk(KERN_DEBUG "Hierarchy = %d", p->Hierarchy); - printk(KERN_DEBUG "Constellation = %d", p->Constellation); - printk(KERN_DEBUG "BurstSize = %d", p->BurstSize); - printk(KERN_DEBUG "BurstDuration = %d", p->BurstDuration); - printk(KERN_DEBUG "BurstCycleTime = %d", p->BurstCycleTime); - printk(KERN_DEBUG "CalculatedBurstCycleTime = %d", p->CalculatedBurstCycleTime); - printk(KERN_DEBUG "NumOfRows = %d", p->NumOfRows); - printk(KERN_DEBUG "NumOfPaddCols = %d", p->NumOfPaddCols); - printk(KERN_DEBUG "NumOfPunctCols = %d", p->NumOfPunctCols); - printk(KERN_DEBUG "ErrorTSPackets = %d", p->ErrorTSPackets); - printk(KERN_DEBUG "TotalTSPackets = %d", p->TotalTSPackets); - printk(KERN_DEBUG "NumOfValidMpeTlbs = %d", p->NumOfValidMpeTlbs); - printk(KERN_DEBUG "NumOfInvalidMpeTlbs = %d", p->NumOfInvalidMpeTlbs); - printk(KERN_DEBUG "NumOfCorrectedMpeTlbs = %d", p->NumOfCorrectedMpeTlbs); - printk(KERN_DEBUG "BERErrorCount = %d", p->BERErrorCount); - printk(KERN_DEBUG "BERBitCount = %d", p->BERBitCount); - printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); - printk(KERN_DEBUG "PreBER = %d", p->PreBER); - printk(KERN_DEBUG "CellId = %d", p->CellId); - printk(KERN_DEBUG "DvbhSrvIndHP = %d", p->DvbhSrvIndHP); - printk(KERN_DEBUG "DvbhSrvIndLP = %d", p->DvbhSrvIndLP); - printk(KERN_DEBUG "NumMPEReceived = %d", p->NumMPEReceived); - } - - pReceptionData->IsDemodLocked = p->IsDemodLocked; - - pReceptionData->SNR = p->SNR; - pReceptionData->BER = p->BER; - pReceptionData->BERErrorCount = p->BERErrorCount; - pReceptionData->InBandPwr = p->InBandPwr; - pReceptionData->ErrorTSPackets = p->ErrorTSPackets; -}; - - -static void smsdvb_update_isdbt_stats(struct RECEPTION_STATISTICS_S *pReceptionData, - struct SMSHOSTLIB_STATISTICS_ISDBT_ST *p) -{ - int i; - - if (sms_dbg & 2) { - printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); - printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); - printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); - printk(KERN_DEBUG "SNR = %d", p->SNR); - printk(KERN_DEBUG "RSSI = %d", p->RSSI); - printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); - printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); - printk(KERN_DEBUG "Frequency = %d", p->Frequency); - printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); - printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); - printk(KERN_DEBUG "ModemState = %d", p->ModemState); - printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); - printk(KERN_DEBUG "SystemType = %d", p->SystemType); - printk(KERN_DEBUG "PartialReception = %d", p->PartialReception); - printk(KERN_DEBUG "NumOfLayers = %d", p->NumOfLayers); - printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); - - for (i = 0; i < 3; i++) { - printk(KERN_DEBUG "%d: CodeRate = %d", i, p->LayerInfo[i].CodeRate); - printk(KERN_DEBUG "%d: Constellation = %d", i, p->LayerInfo[i].Constellation); - printk(KERN_DEBUG "%d: BER = %d", i, p->LayerInfo[i].BER); - printk(KERN_DEBUG "%d: BERErrorCount = %d", i, p->LayerInfo[i].BERErrorCount); - printk(KERN_DEBUG "%d: BERBitCount = %d", i, p->LayerInfo[i].BERBitCount); - printk(KERN_DEBUG "%d: PreBER = %d", i, p->LayerInfo[i].PreBER); - printk(KERN_DEBUG "%d: TS_PER = %d", i, p->LayerInfo[i].TS_PER); - printk(KERN_DEBUG "%d: ErrorTSPackets = %d", i, p->LayerInfo[i].ErrorTSPackets); - printk(KERN_DEBUG "%d: TotalTSPackets = %d", i, p->LayerInfo[i].TotalTSPackets); - printk(KERN_DEBUG "%d: TILdepthI = %d", i, p->LayerInfo[i].TILdepthI); - printk(KERN_DEBUG "%d: NumberOfSegments = %d", i, p->LayerInfo[i].NumberOfSegments); - printk(KERN_DEBUG "%d: TMCCErrors = %d", i, p->LayerInfo[i].TMCCErrors); - } - } - - pReceptionData->IsDemodLocked = p->IsDemodLocked; - - pReceptionData->SNR = p->SNR; - pReceptionData->InBandPwr = p->InBandPwr; - - pReceptionData->ErrorTSPackets = 0; - pReceptionData->BER = 0; - pReceptionData->BERErrorCount = 0; - for (i = 0; i < 3; i++) { - pReceptionData->BER += p->LayerInfo[i].BER; - pReceptionData->BERErrorCount += p->LayerInfo[i].BERErrorCount; - pReceptionData->ErrorTSPackets += p->LayerInfo[i].ErrorTSPackets; - } -} - -static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) -{ - struct smsdvb_client_t *client = (struct smsdvb_client_t *) context; - struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) (((u8 *) cb->p) - + cb->offset); - u32 *pMsgData = (u32 *) phdr + 1; - /*u32 MsgDataLen = phdr->msgLength - sizeof(struct SmsMsgHdr_ST);*/ - bool is_status_update = false; - - smsendian_handle_rx_message((struct SmsMsgData_ST *) phdr); - - switch (phdr->msgType) { - case MSG_SMS_DVBT_BDA_DATA: - dvb_dmx_swfilter(&client->demux, (u8 *)(phdr + 1), - cb->size - sizeof(struct SmsMsgHdr_ST)); - break; - - case MSG_SMS_RF_TUNE_RES: - case MSG_SMS_ISDBT_TUNE_RES: - complete(&client->tune_done); - break; - - case MSG_SMS_SIGNAL_DETECTED_IND: - sms_info("MSG_SMS_SIGNAL_DETECTED_IND"); - client->sms_stat_dvb.TransmissionData.IsDemodLocked = true; - is_status_update = true; - break; - - case MSG_SMS_NO_SIGNAL_IND: - sms_info("MSG_SMS_NO_SIGNAL_IND"); - client->sms_stat_dvb.TransmissionData.IsDemodLocked = false; - is_status_update = true; - break; - - case MSG_SMS_TRANSMISSION_IND: { - sms_info("MSG_SMS_TRANSMISSION_IND"); - - pMsgData++; - memcpy(&client->sms_stat_dvb.TransmissionData, pMsgData, - sizeof(struct TRANSMISSION_STATISTICS_S)); - - /* Mo need to correct guard interval - * (as opposed to old statistics message). - */ - CORRECT_STAT_BANDWIDTH(client->sms_stat_dvb.TransmissionData); - CORRECT_STAT_TRANSMISSON_MODE( - client->sms_stat_dvb.TransmissionData); - is_status_update = true; - break; - } - case MSG_SMS_HO_PER_SLICES_IND: { - struct RECEPTION_STATISTICS_S *pReceptionData = - &client->sms_stat_dvb.ReceptionData; - struct SRVM_SIGNAL_STATUS_S SignalStatusData; - - /*sms_info("MSG_SMS_HO_PER_SLICES_IND");*/ - pMsgData++; - SignalStatusData.result = pMsgData[0]; - SignalStatusData.snr = pMsgData[1]; - SignalStatusData.inBandPower = (s32) pMsgData[2]; - SignalStatusData.tsPackets = pMsgData[3]; - SignalStatusData.etsPackets = pMsgData[4]; - SignalStatusData.constellation = pMsgData[5]; - SignalStatusData.hpCode = pMsgData[6]; - SignalStatusData.tpsSrvIndLP = pMsgData[7] & 0x03; - SignalStatusData.tpsSrvIndHP = pMsgData[8] & 0x03; - SignalStatusData.cellId = pMsgData[9] & 0xFFFF; - SignalStatusData.reason = pMsgData[10]; - SignalStatusData.requestId = pMsgData[11]; - pReceptionData->IsRfLocked = pMsgData[16]; - pReceptionData->IsDemodLocked = pMsgData[17]; - pReceptionData->ModemState = pMsgData[12]; - pReceptionData->SNR = pMsgData[1]; - pReceptionData->BER = pMsgData[13]; - pReceptionData->RSSI = pMsgData[14]; - CORRECT_STAT_RSSI(client->sms_stat_dvb.ReceptionData); - - pReceptionData->InBandPwr = (s32) pMsgData[2]; - pReceptionData->CarrierOffset = (s32) pMsgData[15]; - pReceptionData->TotalTSPackets = pMsgData[3]; - pReceptionData->ErrorTSPackets = pMsgData[4]; - - /* TS PER */ - if ((SignalStatusData.tsPackets + SignalStatusData.etsPackets) - > 0) { - pReceptionData->TS_PER = (SignalStatusData.etsPackets - * 100) / (SignalStatusData.tsPackets - + SignalStatusData.etsPackets); - } else { - pReceptionData->TS_PER = 0; - } - - pReceptionData->BERBitCount = pMsgData[18]; - pReceptionData->BERErrorCount = pMsgData[19]; - - pReceptionData->MRC_SNR = pMsgData[20]; - pReceptionData->MRC_InBandPwr = pMsgData[21]; - pReceptionData->MRC_RSSI = pMsgData[22]; - - is_status_update = true; - break; - } - case MSG_SMS_GET_STATISTICS_RES: { - union { - struct SMSHOSTLIB_STATISTICS_ISDBT_ST isdbt; - struct SmsMsgStatisticsInfo_ST dvb; - } *p = (void *) (phdr + 1); - struct RECEPTION_STATISTICS_S *pReceptionData = - &client->sms_stat_dvb.ReceptionData; - - sms_info("MSG_SMS_GET_STATISTICS_RES"); - - is_status_update = true; - - switch (smscore_get_device_mode(client->coredev)) { - case DEVICE_MODE_ISDBT: - case DEVICE_MODE_ISDBT_BDA: - smsdvb_update_isdbt_stats(pReceptionData, &p->isdbt); - break; - default: - smsdvb_update_dvb_stats(pReceptionData, &p->dvb.Stat); - } - if (!pReceptionData->IsDemodLocked) { - pReceptionData->SNR = 0; - pReceptionData->BER = 0; - pReceptionData->BERErrorCount = 0; - pReceptionData->InBandPwr = 0; - pReceptionData->ErrorTSPackets = 0; - } - - complete(&client->tune_done); - break; - } - default: - sms_info("Unhandled message %d", phdr->msgType); - - } - smscore_putbuffer(client->coredev, cb); - - if (is_status_update) { - if (client->sms_stat_dvb.ReceptionData.IsDemodLocked) { - client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER - | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK); - if (client->sms_stat_dvb.ReceptionData.ErrorTSPackets - == 0) - sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK); - else - sms_board_dvb3_event(client, - DVB3_EVENT_UNC_ERR); - - } else { - if (client->sms_stat_dvb.ReceptionData.IsRfLocked) - client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER; - else - client->fe_status = 0; - sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK); - } - } - - return 0; -} - -static void smsdvb_unregister_client(struct smsdvb_client_t *client) -{ - /* must be called under clientslock */ - - list_del(&client->entry); - - smscore_unregister_client(client->smsclient); - dvb_unregister_frontend(&client->frontend); - dvb_dmxdev_release(&client->dmxdev); - dvb_dmx_release(&client->demux); - dvb_unregister_adapter(&client->adapter); - kfree(client); -} - -static void smsdvb_onremove(void *context) -{ - kmutex_lock(&g_smsdvb_clientslock); - - smsdvb_unregister_client((struct smsdvb_client_t *) context); - - kmutex_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 SmsMsgData_ST PidMsg; - - sms_debug("add pid %d(%x)", - feed->pid, feed->pid); - - PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - PidMsg.xMsgHeader.msgDstId = HIF_TASK; - PidMsg.xMsgHeader.msgFlags = 0; - PidMsg.xMsgHeader.msgType = MSG_SMS_ADD_PID_FILTER_REQ; - PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); - PidMsg.msgData[0] = feed->pid; - - smsendian_handle_tx_message((struct SmsMsgHdr_ST *)&PidMsg); - return smsclient_sendrequest(client->smsclient, - &PidMsg, sizeof(PidMsg)); -} - -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 SmsMsgData_ST PidMsg; - - sms_debug("remove pid %d(%x)", - feed->pid, feed->pid); - - PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - PidMsg.xMsgHeader.msgDstId = HIF_TASK; - PidMsg.xMsgHeader.msgFlags = 0; - PidMsg.xMsgHeader.msgType = MSG_SMS_REMOVE_PID_FILTER_REQ; - PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); - PidMsg.msgData[0] = feed->pid; - - smsendian_handle_tx_message((struct SmsMsgHdr_ST *)&PidMsg); - return smsclient_sendrequest(client->smsclient, - &PidMsg, sizeof(PidMsg)); -} - -static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client, - void *buffer, size_t size, - struct completion *completion) -{ - int rc; - - smsendian_handle_tx_message((struct SmsMsgHdr_ST *)buffer); - 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 SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ, - DVBT_BDA_CONTROL_MSG_ID, - HIF_TASK, - sizeof(struct SmsMsgHdr_ST), 0 }; - - rc = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), - &client->tune_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, - (client->sms_stat_dvb.ReceptionData.BER - == 0) ? SMS_LED_HI : SMS_LED_LO); - else - return sms_board_led_feedback(client->coredev, SMS_LED_OFF); -} - -static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *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->sms_stat_dvb.ReceptionData.BER; - - led_feedback(client); - - return rc; -} - -static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) -{ - int rc; - - struct smsdvb_client_t *client; - client = container_of(fe, struct smsdvb_client_t, frontend); - - rc = smsdvb_send_statistics_request(client); - - if (client->sms_stat_dvb.ReceptionData.InBandPwr < -95) - *strength = 0; - else if (client->sms_stat_dvb.ReceptionData.InBandPwr > -29) - *strength = 100; - else - *strength = - (client->sms_stat_dvb.ReceptionData.InBandPwr - + 95) * 3 / 2; - - led_feedback(client); - - return rc; -} - -static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - int rc; - struct smsdvb_client_t *client; - client = container_of(fe, struct smsdvb_client_t, frontend); - - rc = smsdvb_send_statistics_request(client); - - *snr = client->sms_stat_dvb.ReceptionData.SNR; - - led_feedback(client); - - return rc; -} - -static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) -{ - int rc; - struct smsdvb_client_t *client; - client = container_of(fe, struct smsdvb_client_t, frontend); - - rc = smsdvb_send_statistics_request(client); - - *ucblocks = client->sms_stat_dvb.ReceptionData.ErrorTSPackets; - - led_feedback(client); - - return rc; -} - -static int smsdvb_get_tune_settings(struct dvb_frontend *fe, - struct dvb_frontend_tune_settings *tune) -{ - sms_debug(""); - - 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 SmsMsgHdr_ST Msg; - u32 Data[3]; - } Msg; - - int ret; - - client->fe_status = FE_HAS_SIGNAL; - client->event_fe_state = -1; - client->event_unc_state = -1; - fe->dtv_property_cache.delivery_system = SYS_DVBT; - - Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - Msg.Msg.msgDstId = HIF_TASK; - Msg.Msg.msgFlags = 0; - Msg.Msg.msgType = MSG_SMS_RF_TUNE_REQ; - Msg.Msg.msgLength = sizeof(Msg); - Msg.Data[0] = c->frequency; - Msg.Data[2] = 12000000; - - sms_info("%s: freq %d band %d", __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) { - fe_status_t 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); - - struct { - struct SmsMsgHdr_ST Msg; - u32 Data[4]; - } Msg; - - fe->dtv_property_cache.delivery_system = SYS_ISDBT; - - Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; - Msg.Msg.msgDstId = HIF_TASK; - Msg.Msg.msgFlags = 0; - Msg.Msg.msgType = MSG_SMS_ISDBT_TUNE_REQ; - Msg.Msg.msgLength = sizeof(Msg); - - if (c->isdbt_sb_segment_idx == -1) - c->isdbt_sb_segment_idx = 0; - - switch (c->isdbt_sb_segment_count) { - case 3: - Msg.Data[1] = BW_ISDBT_3SEG; - break; - case 1: - Msg.Data[1] = BW_ISDBT_1SEG; - break; - case 0: /* AUTO */ - switch (c->bandwidth_hz / 1000000) { - case 8: - case 7: - c->isdbt_sb_segment_count = 3; - Msg.Data[1] = BW_ISDBT_3SEG; - break; - case 6: - c->isdbt_sb_segment_count = 1; - Msg.Data[1] = BW_ISDBT_1SEG; - break; - default: /* Assumes 6 MHZ bw */ - c->isdbt_sb_segment_count = 1; - c->bandwidth_hz = 6000; - Msg.Data[1] = BW_ISDBT_1SEG; - break; - } - break; - default: - sms_info("Segment count %d not supported", c->isdbt_sb_segment_count); - return -EINVAL; - } - - Msg.Data[0] = c->frequency; - Msg.Data[2] = 12000000; - Msg.Data[3] = c->isdbt_sb_segment_idx; - - sms_info("%s: freq %d segwidth %d segindex %d\n", __func__, - c->frequency, c->isdbt_sb_segment_count, - c->isdbt_sb_segment_idx); - - return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), - &client->tune_done); -} - -static int smsdvb_set_frontend(struct dvb_frontend *fe) -{ - struct smsdvb_client_t *client = - container_of(fe, struct smsdvb_client_t, frontend); - struct smscore_device_t *coredev = client->coredev; - - 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_get_frontend(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *fep = &fe->dtv_property_cache; - struct smsdvb_client_t *client = - container_of(fe, struct smsdvb_client_t, frontend); - struct smscore_device_t *coredev = client->coredev; - struct TRANSMISSION_STATISTICS_S *td = - &client->sms_stat_dvb.TransmissionData; - - switch (smscore_get_device_mode(coredev)) { - case DEVICE_MODE_DVBT: - case DEVICE_MODE_DVBT_BDA: - fep->frequency = td->Frequency; - - switch (td->Bandwidth) { - case 6: - fep->bandwidth_hz = 6000000; - break; - case 7: - fep->bandwidth_hz = 7000000; - break; - case 8: - fep->bandwidth_hz = 8000000; - break; - } - - switch (td->TransmissionMode) { - case 2: - fep->transmission_mode = TRANSMISSION_MODE_2K; - break; - case 8: - fep->transmission_mode = TRANSMISSION_MODE_8K; - } - - switch (td->GuardInterval) { - case 0: - fep->guard_interval = GUARD_INTERVAL_1_32; - break; - case 1: - fep->guard_interval = GUARD_INTERVAL_1_16; - break; - case 2: - fep->guard_interval = GUARD_INTERVAL_1_8; - break; - case 3: - fep->guard_interval = GUARD_INTERVAL_1_4; - break; - } - - switch (td->CodeRate) { - case 0: - fep->code_rate_HP = FEC_1_2; - break; - case 1: - fep->code_rate_HP = FEC_2_3; - break; - case 2: - fep->code_rate_HP = FEC_3_4; - break; - case 3: - fep->code_rate_HP = FEC_5_6; - break; - case 4: - fep->code_rate_HP = FEC_7_8; - break; - } - - switch (td->LPCodeRate) { - case 0: - fep->code_rate_LP = FEC_1_2; - break; - case 1: - fep->code_rate_LP = FEC_2_3; - break; - case 2: - fep->code_rate_LP = FEC_3_4; - break; - case 3: - fep->code_rate_LP = FEC_5_6; - break; - case 4: - fep->code_rate_LP = FEC_7_8; - break; - } - - switch (td->Constellation) { - case 0: - fep->modulation = QPSK; - break; - case 1: - fep->modulation = QAM_16; - break; - case 2: - fep->modulation = QAM_64; - break; - } - - switch (td->Hierarchy) { - case 0: - fep->hierarchy = HIERARCHY_NONE; - break; - case 1: - fep->hierarchy = HIERARCHY_1; - break; - case 2: - fep->hierarchy = HIERARCHY_2; - break; - case 3: - fep->hierarchy = HIERARCHY_4; - break; - } - - fep->inversion = INVERSION_AUTO; - break; - case DEVICE_MODE_ISDBT: - case DEVICE_MODE_ISDBT_BDA: - fep->frequency = td->Frequency; - fep->bandwidth_hz = 6000000; - /* todo: retrive the other parameters */ - break; - default: - return -EINVAL; - } - - return 0; -} - -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 struct dvb_frontend_ops smsdvb_fe_ops = { - .info = { - .name = "Siano Mobile Digital MDTV Receiver", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 250000, - .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_frontend = smsdvb_get_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) { - sms_err("kmalloc() failed"); - 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) { - sms_err("dvb_register_adapter() failed %d", rc); - goto adapter_error; - } - - /* 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) { - sms_err("dvb_dmx_init failed %d", 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) { - sms_err("dvb_dmxdev_init failed %d", 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) { - sms_err("frontend registration failed %d", 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, ¶ms, &client->smsclient); - if (rc < 0) { - sms_err("smscore_register_client() failed %d", rc); - goto client_error; - } - - client->coredev = coredev; - - init_completion(&client->tune_done); - - kmutex_lock(&g_smsdvb_clientslock); - - list_add(&client->entry, &g_smsdvb_clients); - - kmutex_unlock(&g_smsdvb_clientslock); - - client->event_fe_state = -1; - client->event_unc_state = -1; - sms_board_dvb3_event(client, DVB3_EVENT_HOTPLUG); - - sms_info("success"); - sms_board_setup(coredev); - - return 0; - -client_error: - dvb_unregister_frontend(&client->frontend); - -frontend_error: - dvb_dmxdev_release(&client->dmxdev); - -dmxdev_error: - dvb_dmx_release(&client->demux); - -dvbdmx_error: - dvb_unregister_adapter(&client->adapter); - -adapter_error: - kfree(client); - return rc; -} - -static int __init smsdvb_module_init(void) -{ - int rc; - - INIT_LIST_HEAD(&g_smsdvb_clients); - kmutex_init(&g_smsdvb_clientslock); - - rc = smscore_register_hotplug(smsdvb_hotplug); - - sms_debug(""); - - return rc; -} - -static void __exit smsdvb_module_exit(void) -{ - smscore_unregister_hotplug(smsdvb_hotplug); - - kmutex_lock(&g_smsdvb_clientslock); - - while (!list_empty(&g_smsdvb_clients)) - smsdvb_unregister_client( - (struct smsdvb_client_t *) g_smsdvb_clients.next); - - kmutex_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/usb/siano/smsendian.c b/drivers/media/usb/siano/smsendian.c deleted file mode 100644 index e2657c2f0109..000000000000 --- a/drivers/media/usb/siano/smsendian.c +++ /dev/null @@ -1,103 +0,0 @@ -/**************************************************************** - - Siano Mobile Silicon, Inc. - MDTV receiver kernel modules. - Copyright (C) 2006-2009, Uri Shkolnik - - 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, see . - - ****************************************************************/ - -#include -#include - -#include "smsendian.h" -#include "smscoreapi.h" - -void smsendian_handle_tx_message(void *buffer) -{ -#ifdef __BIG_ENDIAN - struct SmsMsgData_ST *msg = (struct SmsMsgData_ST *)buffer; - int i; - int msgWords; - - switch (msg->xMsgHeader.msgType) { - case MSG_SMS_DATA_DOWNLOAD_REQ: - { - msg->msgData[0] = le32_to_cpu(msg->msgData[0]); - break; - } - - default: - msgWords = (msg->xMsgHeader.msgLength - - sizeof(struct SmsMsgHdr_ST))/4; - - for (i = 0; i < msgWords; i++) - msg->msgData[i] = le32_to_cpu(msg->msgData[i]); - - break; - } -#endif /* __BIG_ENDIAN */ -} -EXPORT_SYMBOL_GPL(smsendian_handle_tx_message); - -void smsendian_handle_rx_message(void *buffer) -{ -#ifdef __BIG_ENDIAN - struct SmsMsgData_ST *msg = (struct SmsMsgData_ST *)buffer; - int i; - int msgWords; - - switch (msg->xMsgHeader.msgType) { - case MSG_SMS_GET_VERSION_EX_RES: - { - struct SmsVersionRes_ST *ver = - (struct SmsVersionRes_ST *) msg; - ver->ChipModel = le16_to_cpu(ver->ChipModel); - break; - } - - case MSG_SMS_DVBT_BDA_DATA: - case MSG_SMS_DAB_CHANNEL: - case MSG_SMS_DATA_MSG: - { - break; - } - - default: - { - msgWords = (msg->xMsgHeader.msgLength - - sizeof(struct SmsMsgHdr_ST))/4; - - for (i = 0; i < msgWords; i++) - msg->msgData[i] = le32_to_cpu(msg->msgData[i]); - - break; - } - } -#endif /* __BIG_ENDIAN */ -} -EXPORT_SYMBOL_GPL(smsendian_handle_rx_message); - -void smsendian_handle_message_header(void *msg) -{ -#ifdef __BIG_ENDIAN - struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *)msg; - - phdr->msgType = le16_to_cpu(phdr->msgType); - phdr->msgLength = le16_to_cpu(phdr->msgLength); - phdr->msgFlags = le16_to_cpu(phdr->msgFlags); -#endif /* __BIG_ENDIAN */ -} -EXPORT_SYMBOL_GPL(smsendian_handle_message_header); diff --git a/drivers/media/usb/siano/smsendian.h b/drivers/media/usb/siano/smsendian.h deleted file mode 100644 index 1624d6fd367b..000000000000 --- a/drivers/media/usb/siano/smsendian.h +++ /dev/null @@ -1,32 +0,0 @@ -/**************************************************************** - -Siano Mobile Silicon, Inc. -MDTV receiver kernel modules. -Copyright (C) 2006-2009, Uri Shkolnik - -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, see . - -****************************************************************/ - -#ifndef __SMS_ENDIAN_H__ -#define __SMS_ENDIAN_H__ - -#include - -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/usb/siano/smsir.c b/drivers/media/usb/siano/smsir.c deleted file mode 100644 index 37bc5c4b8ad8..000000000000 --- a/drivers/media/usb/siano/smsir.c +++ /dev/null @@ -1,114 +0,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 - - 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, see . - - ****************************************************************/ - - -#include -#include - -#include "smscoreapi.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++) { - DEFINE_IR_RAW_EVENT(ev); - - ev.duration = abs(samples[i]) * 1000; /* Convert to ns */ - ev.pulse = (samples[i] > 0) ? false : true; - - 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; - - sms_log("Allocating rc device"); - dev = rc_allocate_device(); - if (!dev) { - sms_err("Not enough memory"); - return -ENOMEM; - } - - coredev->ir.controller = 0; /* Todo: vega/nova SPI number */ - coredev->ir.timeout = IR_DEFAULT_TIMEOUT; - sms_log("IR port %d, timeout %d ms", - coredev->ir.controller, coredev->ir.timeout); - - snprintf(coredev->ir.name, sizeof(coredev->ir.name), - "SMS IR (%s)", sms_get_board(board_id)->name); - - strlcpy(coredev->ir.phys, coredev->devpath, sizeof(coredev->ir.phys)); - strlcat(coredev->ir.phys, "/ir0", sizeof(coredev->ir.phys)); - - dev->input_name = coredev->ir.name; - dev->input_phys = coredev->ir.phys; - dev->dev.parent = coredev->device; - -#if 0 - /* TODO: properly initialize the parameters bellow */ - 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->driver_type = RC_DRIVER_IR_RAW; - dev->allowed_protos = RC_TYPE_ALL; - dev->map_name = sms_get_board(board_id)->rc_codes; - dev->driver_name = MODULE_NAME; - - sms_log("Input device (IR) %s is set for key events", dev->input_name); - - err = rc_register_device(dev); - if (err < 0) { - sms_err("Failed to register device"); - rc_free_device(dev); - return err; - } - - coredev->ir.dev = dev; - return 0; -} - -void sms_ir_exit(struct smscore_device_t *coredev) -{ - if (coredev->ir.dev) - rc_unregister_device(coredev->ir.dev); - - sms_log(""); -} diff --git a/drivers/media/usb/siano/smsir.h b/drivers/media/usb/siano/smsir.h deleted file mode 100644 index ae92b3a8587e..000000000000 --- a/drivers/media/usb/siano/smsir.h +++ /dev/null @@ -1,55 +0,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 - -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, see . - -****************************************************************/ - -#ifndef __SMS_IR_H__ -#define __SMS_IR_H__ - -#include -#include - -#define IR_DEFAULT_TIMEOUT 100 - -struct smscore_device_t; - -struct ir_t { - struct rc_dev *dev; - char name[40]; - char phys[32]; - - char *rc_codes; - u64 protocol; - - u32 timeout; - u32 controller; -}; - -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); - -#endif /* __SMS_IR_H__ */ - diff --git a/drivers/media/usb/siano/smssdio.c b/drivers/media/usb/siano/smssdio.c deleted file mode 100644 index d6f3f100699a..000000000000 --- a/drivers/media/usb/siano/smssdio.c +++ /dev/null @@ -1,365 +0,0 @@ -/* - * smssdio.c - Siano 1xxx SDIO interface driver - * - * Copyright 2008 Pierre Ossman - * - * Based on code by Siano Mobile Silicon, Inc., - * Copyright (C) 2006-2008, Uri Shkolnik - * - * 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 hardware is a bit odd in that all transfers should be done - * to/from the SMSSDIO_DATA register, yet the "increase address" bit - * always needs to be set. - * - * Also, buffers from the card are always aligned to 128 byte - * boundaries. - */ - -/* - * General cleanup notes: - * - * - only typedefs should be name *_t - * - * - use ERR_PTR and friends for smscore_register_device() - * - * - smscore_getbuffer should zero fields - * - * Fix stop command - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "smscoreapi.h" -#include "sms-cards.h" - -/* Registers */ - -#define SMSSDIO_DATA 0x00 -#define SMSSDIO_INT 0x04 -#define SMSSDIO_BLOCK_SIZE 128 - -static const struct sdio_device_id smssdio_ids[] __devinitconst = { - {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR), - .driver_data = SMS1XXX_BOARD_SIANO_STELLAR}, - {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0), - .driver_data = SMS1XXX_BOARD_SIANO_NOVA_A}, - {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0), - .driver_data = SMS1XXX_BOARD_SIANO_NOVA_B}, - {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0), - .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, - {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE), - .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, - { /* end: all zeroes */ }, -}; - -MODULE_DEVICE_TABLE(sdio, smssdio_ids); - -struct smssdio_device { - struct sdio_func *func; - - struct smscore_device_t *coredev; - - struct smscore_buffer_t *split_cb; -}; - -/*******************************************************************/ -/* Siano core callbacks */ -/*******************************************************************/ - -static int smssdio_sendrequest(void *context, void *buffer, size_t size) -{ - int ret = 0; - struct smssdio_device *smsdev; - - smsdev = context; - - sdio_claim_host(smsdev->func); - - while (size >= smsdev->func->cur_blksize) { - ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, - buffer, smsdev->func->cur_blksize); - if (ret) - goto out; - - buffer += smsdev->func->cur_blksize; - size -= smsdev->func->cur_blksize; - } - - if (size) { - ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, - buffer, size); - } - -out: - sdio_release_host(smsdev->func); - - return ret; -} - -/*******************************************************************/ -/* SDIO callbacks */ -/*******************************************************************/ - -static void smssdio_interrupt(struct sdio_func *func) -{ - int ret; - - struct smssdio_device *smsdev; - struct smscore_buffer_t *cb; - struct SmsMsgHdr_ST *hdr; - size_t size; - - smsdev = sdio_get_drvdata(func); - - /* - * The interrupt register has no defined meaning. It is just - * a way of turning of the level triggered interrupt. - */ - (void)sdio_readb(func, SMSSDIO_INT, &ret); - if (ret) { - sms_err("Unable to read interrupt register!\n"); - return; - } - - if (smsdev->split_cb == NULL) { - cb = smscore_getbuffer(smsdev->coredev); - if (!cb) { - sms_err("Unable to allocate data buffer!\n"); - return; - } - - ret = sdio_memcpy_fromio(smsdev->func, - cb->p, - SMSSDIO_DATA, - SMSSDIO_BLOCK_SIZE); - if (ret) { - sms_err("Error %d reading initial block!\n", ret); - return; - } - - hdr = cb->p; - - if (hdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG) { - smsdev->split_cb = cb; - return; - } - - if (hdr->msgLength > smsdev->func->cur_blksize) - size = hdr->msgLength - smsdev->func->cur_blksize; - else - size = 0; - } else { - cb = smsdev->split_cb; - hdr = cb->p; - - size = hdr->msgLength - sizeof(struct SmsMsgHdr_ST); - - smsdev->split_cb = NULL; - } - - if (size) { - void *buffer; - - buffer = cb->p + (hdr->msgLength - size); - size = ALIGN(size, SMSSDIO_BLOCK_SIZE); - - BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE); - - /* - * First attempt to transfer all of it in one go... - */ - ret = sdio_memcpy_fromio(smsdev->func, - buffer, - SMSSDIO_DATA, - size); - if (ret && ret != -EINVAL) { - smscore_putbuffer(smsdev->coredev, cb); - sms_err("Error %d reading data from card!\n", ret); - return; - } - - /* - * ..then fall back to one block at a time if that is - * not possible... - * - * (we have to do this manually because of the - * problem with the "increase address" bit) - */ - if (ret == -EINVAL) { - while (size) { - ret = sdio_memcpy_fromio(smsdev->func, - buffer, SMSSDIO_DATA, - smsdev->func->cur_blksize); - if (ret) { - smscore_putbuffer(smsdev->coredev, cb); - sms_err("Error %d reading " - "data from card!\n", ret); - return; - } - - buffer += smsdev->func->cur_blksize; - if (size > smsdev->func->cur_blksize) - size -= smsdev->func->cur_blksize; - else - size = 0; - } - } - } - - cb->size = hdr->msgLength; - cb->offset = 0; - - smscore_onresponse(smsdev->coredev, cb); -} - -static int __devinit smssdio_probe(struct sdio_func *func, - const struct sdio_device_id *id) -{ - int ret; - - int board_id; - struct smssdio_device *smsdev; - struct smsdevice_params_t params; - - board_id = id->driver_data; - - smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL); - if (!smsdev) - return -ENOMEM; - - smsdev->func = func; - - memset(¶ms, 0, sizeof(struct smsdevice_params_t)); - - params.device = &func->dev; - params.buffer_size = 0x5000; /* ?? */ - params.num_buffers = 22; /* ?? */ - params.context = smsdev; - - snprintf(params.devpath, sizeof(params.devpath), - "sdio\\%s", sdio_func_id(func)); - - params.sendrequest_handler = smssdio_sendrequest; - - params.device_type = sms_get_board(board_id)->type; - - if (params.device_type != SMS_STELLAR) - params.flags |= SMS_DEVICE_FAMILY2; - else { - /* - * FIXME: Stellar needs special handling... - */ - ret = -ENODEV; - goto free; - } - - ret = smscore_register_device(¶ms, &smsdev->coredev); - if (ret < 0) - goto free; - - smscore_set_board_id(smsdev->coredev, board_id); - - sdio_claim_host(func); - - ret = sdio_enable_func(func); - if (ret) - goto release; - - ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE); - if (ret) - goto disable; - - ret = sdio_claim_irq(func, smssdio_interrupt); - if (ret) - goto disable; - - sdio_set_drvdata(func, smsdev); - - sdio_release_host(func); - - ret = smscore_start_device(smsdev->coredev); - if (ret < 0) - goto reclaim; - - return 0; - -reclaim: - sdio_claim_host(func); - sdio_release_irq(func); -disable: - sdio_disable_func(func); -release: - sdio_release_host(func); - smscore_unregister_device(smsdev->coredev); -free: - kfree(smsdev); - - return ret; -} - -static void smssdio_remove(struct sdio_func *func) -{ - struct smssdio_device *smsdev; - - smsdev = sdio_get_drvdata(func); - - /* FIXME: racy! */ - if (smsdev->split_cb) - smscore_putbuffer(smsdev->coredev, smsdev->split_cb); - - smscore_unregister_device(smsdev->coredev); - - sdio_claim_host(func); - sdio_release_irq(func); - sdio_disable_func(func); - sdio_release_host(func); - - kfree(smsdev); -} - -static struct sdio_driver smssdio_driver = { - .name = "smssdio", - .id_table = smssdio_ids, - .probe = smssdio_probe, - .remove = smssdio_remove, -}; - -/*******************************************************************/ -/* Module functions */ -/*******************************************************************/ - -static int __init smssdio_module_init(void) -{ - int ret = 0; - - printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n"); - printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n"); - - ret = sdio_register_driver(&smssdio_driver); - - return ret; -} - -static void __exit smssdio_module_exit(void) -{ - sdio_unregister_driver(&smssdio_driver); -} - -module_init(smssdio_module_init); -module_exit(smssdio_module_exit); - -MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver"); -MODULE_AUTHOR("Pierre Ossman"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3